Skip to content

Commit

Permalink
Merge pull request #102182 from grahamc/backport-ami-improvements
Browse files Browse the repository at this point in the history
[20.09] Backport AMI Boot Time and Disk Improvements
  • Loading branch information
grahamc committed Oct 30, 2020
2 parents 6b2df01 + a3b7c6e commit 56e5f2a
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 27 deletions.
16 changes: 15 additions & 1 deletion nixos/lib/make-disk-image.nix
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
# partition of reasonable size is created in addition to the root partition.
# For "legacy", the msdos partition table is used and a single large root
# partition is created.
# For "legacy+gpt", the GPT partition table is used, a 1MiB no-fs partition for
# use by the bootloader is created, and a single large root partition is
# created.
# For "hybrid", the GPT partition table is used and a mandatory ESP
# partition of reasonable size is created in addition to the root partition.
# Also a legacy MBR will be present.
Expand All @@ -54,7 +57,7 @@
format ? "raw"
}:

assert partitionTableType == "legacy" || partitionTableType == "efi" || partitionTableType == "hybrid" || partitionTableType == "none";
assert partitionTableType == "legacy" || partitionTableType == "legacy+gpt" || partitionTableType == "efi" || partitionTableType == "hybrid" || partitionTableType == "none";
# We use -E offset=X below, which is only supported by e2fsprogs
assert partitionTableType != "none" -> fsType == "ext4";

Expand All @@ -75,6 +78,7 @@ let format' = format; in let

rootPartition = { # switch-case
legacy = "1";
"legacy+gpt" = "2";
efi = "2";
hybrid = "3";
}.${partitionTableType};
Expand All @@ -85,6 +89,16 @@ let format' = format; in let
mklabel msdos \
mkpart primary ext4 1MiB -1
'';
"legacy+gpt" = ''
parted --script $diskImage -- \
mklabel gpt \
mkpart no-fs 1MB 2MB \
set 1 bios_grub on \
align-check optimal 1 \
mkpart primary ext4 2MB -1 \
align-check optimal 2 \
print
'';
efi = ''
parted --script $diskImage -- \
mklabel gpt \
Expand Down
2 changes: 1 addition & 1 deletion nixos/maintainers/scripts/ec2/amazon-image.nix
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ in {
inherit (cfg) contents format name;
pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
partitionTableType = if config.ec2.efi then "efi"
else if config.ec2.hvm then "legacy"
else if config.ec2.hvm then "legacy+gpt"
else "none";
diskSize = cfg.sizeMB;
fsType = "ext4";
Expand Down
54 changes: 31 additions & 23 deletions nixos/maintainers/scripts/ec2/create-amis.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#!/usr/bin/env nix-shell
#!nix-shell -p awscli -p jq -p qemu -i bash
# shellcheck shell=bash

# Uploads and registers NixOS images built from the
# <nixos/release.nix> amazonImage attribute. Images are uploaded and
# registered via a home region, and then copied to other regions.

# The home region requires an s3 bucket, and a "vmimport" IAM role
# with access to the S3 bucket. Configuration of the vmimport role is
# documented in
# The home region requires an s3 bucket, and an IAM role named "vmimport"
# (by default) with access to the S3 bucket. The name can be
# configured with the "service_role_name" variable. Configuration of the
# vmimport role is documented in
# https://docs.aws.amazon.com/vm-import/latest/userguide/vmimport-image-import.html

# set -x
Expand All @@ -17,6 +19,7 @@ set -euo pipefail
state_dir=$HOME/amis/ec2-images
home_region=eu-west-1
bucket=nixos-amis
service_role_name=vmimport

regions=(eu-west-1 eu-west-2 eu-west-3 eu-central-1 eu-north-1
us-east-1 us-east-2 us-west-1 us-west-2
Expand Down Expand Up @@ -64,7 +67,7 @@ image_logical_bytes=$(read_image_info .logical_bytes)

# Derived attributes

image_logical_gigabytes=$((($image_logical_bytes-1)/1024/1024/1024+1)) # Round to the next GB
image_logical_gigabytes=$(((image_logical_bytes-1)/1024/1024/1024+1)) # Round to the next GB

case "$image_system" in
aarch64-linux)
Expand Down Expand Up @@ -100,7 +103,7 @@ write_state() {
local type=$2
local val=$3

mkdir -p $state_dir
mkdir -p "$state_dir"
echo "$val" > "$state_dir/$state_key.$type"
}

Expand All @@ -110,8 +113,8 @@ wait_for_import() {
local state snapshot_id
log "Waiting for import task $task_id to be completed"
while true; do
read state progress snapshot_id < <(
aws ec2 describe-import-snapshot-tasks --region $region --import-task-ids "$task_id" | \
read -r state progress snapshot_id < <(
aws ec2 describe-import-snapshot-tasks --region "$region" --import-task-ids "$task_id" | \
jq -r '.ImportSnapshotTasks[].SnapshotTaskDetail | "\(.Status) \(.Progress) \(.SnapshotId)"'
)
log " ... state=$state progress=$progress snapshot_id=$snapshot_id"
Expand All @@ -125,6 +128,8 @@ wait_for_import() {
;;
*)
log "Unexpected snapshot import state: '${state}'"
log "Full response: "
aws ec2 describe-import-snapshot-tasks --region "$region" --import-task-ids "$task_id" >&2
exit 1
;;
esac
Expand All @@ -138,8 +143,8 @@ wait_for_image() {
log "Waiting for image $ami_id to be available"

while true; do
read state < <(
aws ec2 describe-images --image-ids "$ami_id" --region $region | \
read -r state < <(
aws ec2 describe-images --image-ids "$ami_id" --region "$region" | \
jq -r ".Images[].State"
)
log " ... state=$state"
Expand All @@ -163,7 +168,7 @@ make_image_public() {
local region=$1
local ami_id=$2

wait_for_image $region "$ami_id"
wait_for_image "$region" "$ami_id"

log "Making image $ami_id public"

Expand All @@ -177,27 +182,30 @@ upload_image() {
local aws_path=${image_file#/}

local state_key="$region.$image_label.$image_system"
local task_id=$(read_state "$state_key" task_id)
local snapshot_id=$(read_state "$state_key" snapshot_id)
local ami_id=$(read_state "$state_key" ami_id)
local task_id
task_id=$(read_state "$state_key" task_id)
local snapshot_id
snapshot_id=$(read_state "$state_key" snapshot_id)
local ami_id
ami_id=$(read_state "$state_key" ami_id)

if [ -z "$task_id" ]; then
log "Checking for image on S3"
if ! aws s3 ls --region "$region" "s3://${bucket}/${aws_path}" >&2; then
log "Image missing from aws, uploading"
aws s3 cp --region $region "$image_file" "s3://${bucket}/${aws_path}" >&2
aws s3 cp --region "$region" "$image_file" "s3://${bucket}/${aws_path}" >&2
fi

log "Importing image from S3 path s3://$bucket/$aws_path"

task_id=$(aws ec2 import-snapshot --disk-container "{
task_id=$(aws ec2 import-snapshot --role-name "$service_role_name" --disk-container "{
\"Description\": \"nixos-image-${image_label}-${image_system}\",
\"Format\": \"vhd\",
\"UserBucket\": {
\"S3Bucket\": \"$bucket\",
\"S3Key\": \"$aws_path\"
}
}" --region $region | jq -r '.ImportTaskId')
}" --region "$region" | jq -r '.ImportTaskId')

write_state "$state_key" task_id "$task_id"
fi
Expand All @@ -221,16 +229,16 @@ upload_image() {
--virtualization-type hvm
)

block_device_mappings+=(DeviceName=/dev/sdb,VirtualName=ephemeral0)
block_device_mappings+=(DeviceName=/dev/sdc,VirtualName=ephemeral1)
block_device_mappings+=(DeviceName=/dev/sdd,VirtualName=ephemeral2)
block_device_mappings+=(DeviceName=/dev/sde,VirtualName=ephemeral3)
block_device_mappings+=("DeviceName=/dev/sdb,VirtualName=ephemeral0")
block_device_mappings+=("DeviceName=/dev/sdc,VirtualName=ephemeral1")
block_device_mappings+=("DeviceName=/dev/sdd,VirtualName=ephemeral2")
block_device_mappings+=("DeviceName=/dev/sde,VirtualName=ephemeral3")

ami_id=$(
aws ec2 register-image \
--name "$image_name" \
--description "$image_description" \
--region $region \
--region "$region" \
--architecture $amazon_arch \
--block-device-mappings "${block_device_mappings[@]}" \
"${extra_flags[@]}" \
Expand All @@ -240,7 +248,7 @@ upload_image() {
write_state "$state_key" ami_id "$ami_id"
fi

make_image_public $region "$ami_id"
make_image_public "$region" "$ami_id"

echo "$ami_id"
}
Expand Down Expand Up @@ -268,7 +276,7 @@ copy_to_region() {
write_state "$state_key" ami_id "$ami_id"
fi

make_image_public $region "$ami_id"
make_image_public "$region" "$ami_id"

echo "$ami_id"
}
Expand Down
3 changes: 2 additions & 1 deletion nixos/modules/system/boot/stage-1-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ eval "exec $logOutFd>&1 $logErrFd>&2"
if test -w /dev/kmsg; then
tee -i < /tmp/stage-1-init.log.fifo /proc/self/fd/"$logOutFd" | while read -r line; do
if test -n "$line"; then
echo "<7>stage-1-init: $line" > /dev/kmsg
echo "<7>stage-1-init: [$(date)] $line" > /dev/kmsg
fi
done &
else
Expand Down Expand Up @@ -356,6 +356,7 @@ mountFS() {
case $options in
*x-nixos.autoresize*)
if [ "$fsType" = ext2 -o "$fsType" = ext3 -o "$fsType" = ext4 ]; then
modprobe "$fsType"
echo "resizing $device..."
e2fsck -fp "$device"
resize2fs "$device"
Expand Down
2 changes: 1 addition & 1 deletion nixos/modules/virtualisation/amazon-image.nix
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ in
];
boot.initrd.kernelModules = [ "xen-blkfront" "xen-netfront" ];
boot.initrd.availableKernelModules = [ "ixgbevf" "ena" "nvme" ];
boot.kernelParams = mkIf cfg.hvm [ "console=ttyS0" ];
boot.kernelParams = mkIf cfg.hvm [ "console=ttyS0" "random.trust_cpu=on" ];

# Prevent the nouveau kernel module from being loaded, as it
# interferes with the nvidia/nvidia-uvm modules needed for CUDA.
Expand Down

0 comments on commit 56e5f2a

Please sign in to comment.