Thursday, April 22, 2010

Upgrading an EBS Instance

Update 20110323: If you are reading this article, you almost certainly should be reading my Migrating to pv-grub kernels for kernel upgrades. The process described here will still work for 10.04 images, but the process described there is ultimately much easier. If you are using 10.10, or 11.04 images, you do not need to do anything, simply 'apt-get update && apt-get dist-upgrade && reboot' to get a new kernel.

For the majority of the existence of EC2 there was no way to change the kernel that an instance was using. With the addition of EBS instances, that changed. I wanted to explain how you can take advantage of that EBS feature by upgrading a Ubuntu 10.04 LTS (Lucid Lynx) instance launched from a Beta-2 AMI to the Release Candidate. This same basic process should also allow you to upgrade across a release, perhaps from a 9.04 Alestic instance to Ubuntu 10.04 LTS.

In reality, if you're hoping to upgrade you're kernel and EBS instance, its because you already have one running and need to upgrade. But for the sake of this excercise, we'll launch a new instance based on the Beta-2 image in the us-east-1 region and then connect to it.

$ ec2-run-instances --key mykey ami-4be50b22
# wait a bit
$ ec2-describe-instances | awk '-F\t' '$1 == "INSTANCE" { print $4 }'
ec2-184-73-101-171.compute-1.amazonaws.com
$ ssh -i mykey.pem ubuntu@ec2-184-73-101-171.compute-1.amazonaws.com

Now, on the instance we'll go ahead and do the upgrade. Whenever I'm working on ec2, I like to use GNU screen to protect against lost network.
% screen -S upgrade

This is the same basic process as upgrading any Ubuntu system. First update and then upgrade. Here, I've run '--dry-run' to point out that there would be kernel upgrades.

Also, the ec2 images suffer from a bug where grub will be installed and prompt you for some information even though its of no value. Because I know what those prompts will be, I'm go ahead and set them here so you're not interrupted during the dist-upgrade.

% sudo apt-get update
% sudo apt-get dist-upgrade --dry-run | grep "linux.*ec2"
  libparted0debian1 linux-image-2.6.32-21-virtual linux-image-2.6.32-305-ec2
  libpolkit-gobject-1-0 libpython2.6 libss2 libudev0 linux-ec2 linux-firmware
  linux-image-ec2 linux-image-virtual linux-virtual locales module-init-tools
Inst linux-image-2.6.32-305-ec2 (2.6.32-305.9 Ubuntu:10.04/lucid)
Inst linux-ec2 [2.6.32.304.5] (2.6.32.305.6 Ubuntu:10.04/lucid) []
Inst linux-image-ec2 [2.6.32.304.5] (2.6.32.305.6 Ubuntu:10.04/lucid)
Conf linux-image-2.6.32-305-ec2 (2.6.32-305.9 Ubuntu:10.04/lucid)
Conf linux-image-ec2 (2.6.32.305.6 Ubuntu:10.04/lucid)
Conf linux-ec2 (2.6.32.305.6 Ubuntu:10.04/lucid)

% echo grub-pc grub2/linux_cmdline string | sudo debconf-set-selections
% echo grub-pc grub-pc/install_devices_empty boolean true | sudo debconf-set-selections

% sudo apt-get dist-upgrade
..
94 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.
Need to get 84.1MB of archives.
..

Above, If you were upgrading from a previous release to 10.04, then you would use 'do-release-upgrade' from the 'update-manager-core' package.

At this point we've got 2 kernels installed, the new one and the old one. Unsurprisingly, we're booted into the old one.

% dpkg-query --show | grep "linux.*ec2"
linux-ec2       2.6.32.305.6
linux-image-2.6.32-304-ec2      2.6.32-304.8
linux-image-2.6.32-305-ec2      2.6.32-305.9
linux-image-ec2 2.6.32.305.6
% uname -r
2.6.32-304-ec2

Above, we can see that the 2.6.32-305.9 version of the kernel is the newest one. Its installed locally, but to boot it we have to find the aki of the version that is published by Ubuntu to ec2. The Ubuntu kernels are registered in ec2 such that you can correlate the dpkg version to the registered aki. We're going to query all the images, save that output to a file and then search for results that are owned by the Canonical user, and match our version string and arch.

$ owner=099720109477; # this is the canonical user's id
$ ver=2.6.32-305.9; arch=i386
$ ec2-describe-images --all > /tmp/images.list
$ awk '-F\t' '$4 == o && $3 ~ v && $8 == a { print $2, $3 }' \
   a=${arch} "o=${owner}" "v=${ver}" /tmp/images.list
aki-1f02ec76 099720109477/ubuntu-kernels-milestone/ubuntu-lucid-i386-linux-image-2.6.32-305-ec2-v-2.6.32-305.9-kernel
aki-d324caba 099720109477/ubuntu-kernels-testing/ubuntu-lucid-i386-linux-image-2.6.32-305-ec2-v-2.6.32-305.9-kernel

That shows us that we have 2 kernels available matching that explicit version. One is "testing", and one is "milestone". These are actually the same thing. We label the kernels differently so the user easily knows what is testing and what is "released". The released kernel version will be labeled with 'ubuntu-kernels'. The RC kernel version gets labeled "ubuntu-kernels-milestone".

In order to change the kernel, we have to stop the instance, modify the 'kernel' attribute, and then start it up again.
$ ec2-stop-instances i-23453048
$ ec2-modify-instance-attribute --kernel aki-1f02ec76
kernel   i-23453048  aki-1f02ec76

$ ec2-start-instances i-23453048
# wait a bit
$ ec2-describe-instances | awk '-F\t' '$1 == "INSTANCE" { print $4 }'
ec2-184-73-116-205.compute-1.amazonaws.com

So, in theory, we should have booted into our nice and shiny-new kernel. Lets test that theory:
$ ssh ubuntu@ec2-184-73-116-205.compute-1.amazonaws.com 'uname -r'
2.6.32-305-ec2

There you have it! This process can be applied to upgrading the RC to Release (which likely won't have a kernel change), or, eventually to upgrading your 10.04 LTS instance to a Maverick one.