Skip to content

Instantly share code, notes, and snippets.

@ELLIOTTCABLE
Created May 26, 2009 21:19
Show Gist options
  • Save ELLIOTTCABLE/118307 to your computer and use it in GitHub Desktop.
Save ELLIOTTCABLE/118307 to your computer and use it in GitHub Desktop.
Instructions for building an EC2 AMI for Arch Linux
#!/dev/null
# This 'script' is a set of instructions for preparing and bundling an Arch
# Linux AMI for Amazon's EC2. Bits are to be run on three different
# computers, and there is interaction required, so please follow along and
# run commands individually.
# PROTIP: THESE DON'T ACTUALLY WORK. That's why I'm pasting them here,
# attemping to get some input on what I'm doing wrong. When the instructions
# are ready for prime-time, I'll clean them up and post them to my blog. If
# you're really interested, watch for it there:
# http://blog.elliottcable.name/
### Preperation -------------------------------------------------------------
# First, we need the EC2 API tools. They require Java.
# It's worth noting that the OpenJDK package suggested by the Arch Linux
# folks isn't compatible with EC2's command-line API tools, so you've
# probably gotta install Sun's proprietary Java Runtime Environment:
yaourt -S jre
# The jre package will set the $JAVA_HOME environment variable for you, but
# you need to close and re-open your terminal.
exit
# Now let's download and install the EC2 stuff to ~/.ec2:
yaourt -S wget unzip
wget http://s3.amazonaws.com/ec2-downloads/ec2-api-tools.zip
unzip ec2-api-tools.zip
mv ec2-api-tools-* .ec2
# At this point, you need to visit the EC2 site, and get a few pieces of
# data.
# First, you'll download your X.509 certficiates into ~/.ec2/ (they
# should be two seperate .pem files).
# Second, you'll copy-paste your "Account Number", "Access Key ID", and
# "Secret Access Key" into ~/.ec2/ as files named account_number,
# access_key, and secret_key respectively.
links http://aws-portal.amazon.com/gp/aws/developer/account/index.html?actio\
n=access-key
# This bit, you'll have to run any time you open a new terminal; it sets
# environment variables relevant to the EC2 tools:
export PATH="$PATH:$HOME/.ec2/bin"
export EC2_HOME="$HOME/.ec2"
export EC2_CERT="$(/bin/ls $EC2_HOME/cert-*.pem)"
export EC2_PRIVATE_KEY="$(/bin/ls $EC2_HOME/pk-*.pem)"
### Bundling host launching -------------------------------------------------
# Now you've got all the data you'll need. Let's prepare ourselves to launch
# a bundling instance!
# We need a private key now, for the instance we're about to create:
ec2-add-keypair bundling-host > ~/.ec2/id_rsa-bundling-host
chmod 600 ~/.ec2/id_rsa-bundling-host
# We also need a security group that will allow us to SSH into our new
# bundling server.
ec2-add-group bundling -d "Systems dedicated to bundling other AMIs."
ec2-authorize bundling --protocol tcp --port-range 22
# Now let's select an instance to launch. There are no decent Arch Linux
# images available as of this writing, so we'll be using an Amazon-official
# Fedora Core image instead. Let's see what's available:
ec2-describe-images -o amazon
# I'm most interested in the v1.08 images that are the (as of this writing)
# most up-to-date.
ec2-describe-images -o amazon | grep 'v1.08'
# Since we'll be packaging an i686 image in this tutorial, we'll use an i686
# bundling host in this tutorial. While it's possible to bundle an i686
# system on an x86_64 one, we're not going to bother with that here.
# Of course, if you want to build an x86_64 system, select the x86_64 AMI
# instead.
# Take the AMI above (the string starting with ami-), and pass it to
# ec2-run-instances. You'll also need the group and keypair created above.
# Ensure that you pass the correct instance-type; "m1.small" if you're
# launching an i686 instance, and "m1.large" if you're launching an x86_64:
ec2-run-instances --instance-type m1.small --group bundling \
--key bundling-host ami-5647a33f
# The output from the above command will include the instance ID of the newly
# created instance, a string starting with i-. Pass that to the
# ec2-describe-instances tool repeatedly until it reports that the new
# instance is operational and "running".
while true; do date; ec2-describe-instances i-ad286fc4; done
# Once the instance is operational, we're ready to SSH in!
# The describe command will tell you the public address of the isntance when
# it's available.
ssh [email protected] \
-i ~/.ec2/id_rsa-bundling-host
### Bundling host setup -----------------------------------------------------
# Now that we're into our new host instance, let's repeat our EC2 tools
# setup, but also install the AMI tools this time around. Most of the
# description is available above, I'm just going to list the commands here.
yum update # We want everything up-to-date!
# First, we'll need Java for the EC2 tools. Unfortunately, Fedora seems to be
# missing any sort of sensible package manager package for Java, so we have
# to deal with it the messy way.
# This URL may not work for you. Copy the download URL for the Linux RPM
# package from this page: http://www.java.com/en/download/manual.jsp
# (Obviously, if you're running on an x86_64 bundling host, download the
# x86_64 Java RPM)
mkdir -p /usr/java
cd /usr/java
wget http://javadl.sun.com/webapps/download/AutoDL?BundleId=29209 \
-O jre-6u13-linux-i586-rpm.bin
chmod o+x !$
./!$
touch /etc/profile.d/java.sh
chmod +x !$
echo 'export JAVA_HOME="/usr/java/latest"' >> !$
echo 'export JAVA_PATH="$JAVA_HOME"' >> !$
echo 'export PATH="$PATH:$JAVA_HOME/bin"' >> !$
# Blah, I bet you can't wait 'till we get an arch box running now, right? d-:
# Now you need to leave and re-SSH-in to get those variables set.
exit
ssh [email protected] \
-i ~/.ec2/id_rsa-bundling-host
# Check that they're properly set. This should list a bunch of java
# directories.
ls $JAVA_HOME/bin
# Let's finally download and install the EC2 API tools.
cd ~/
wget http://s3.amazonaws.com/ec2-downloads/ec2-api-tools.zip
unzip ec2-api-tools.zip
mv ec2-api-tools-* .ec2
wget http://s3.amazonaws.com/ec2-downloads/ec2-ami-tools.zip
unzip ec2-ami-tools.zip
mv ec2-ami-tools-*/etc .ec2/etc
mv ec2-ami-tools-*/bin/* .ec2/bin/
mv ec2-ami-tools-*/lib/* .ec2/lib/
# We also need our new EC2 tools in the $PATH.
touch /etc/profile.d/ec2.sh
chmod +x !$
echo 'export PATH="$HOME/.ec2/bin:$PATH"' >> !$
echo 'export EC2_HOME="$HOME/.ec2"' >> !$
echo 'export EC2_CERT="$(/bin/ls $EC2_HOME/cert-*.pem)"' >> !$
echo 'export EC2_PRIVATE_KEY="$(/bin/ls $EC2_HOME/pk-*.pem)"' >> !$
# Again, you gotta close the SSH session and re-open it.
exit
ssh [email protected] \
-i ~/.ec2/id_rsa-bundling-host
# Now that we've got the tools, we need your certifications and stuff to be
# duplicated on your bundling host. Let's upload all of those. Quit your SSH
# session again and ship everything off to your new server with scp:
exit
scp -i ~/.ec2/id_rsa-bundling-host \
~/.ec2/*.pem \
[email protected]:~/.ec2/
scp -i ~/.ec2/id_rsa-bundling-host \
~/.ec2/account_number \
[email protected]:~/.ec2/
scp -i ~/.ec2/id_rsa-bundling-host \
~/.ec2/access_key \
[email protected]:~/.ec2/
scp -i ~/.ec2/id_rsa-bundling-host \
~/.ec2/secret_key \
[email protected]:~/.ec2/
# And back in again!
ssh [email protected] \
-i ~/.ec2/id_rsa-bundling-host
# Now we need to get pacman, to install Arch Linux's base packages with.
# Unfortunately, it'd be difficult to install all of its dependencies on
# this Fedora box, so we'll use a statically-built pacman instead.
# Of course, this version number may have changed since this writing;
# make sure to check the server for the exact URL of this file:
# http://repo.archlinux.fr/i686/
# Note: Also, if you're building on an x86_64 system, that's the way to go.
wget http://repo.archlinux.fr/i686/pacman-static-3.2.2-1.pkg.tar.gz
tar -xzvf ~/pacman-static-3.2.2-1.pkg.tar.gz -C /
# We also need a mirrorlist and configuration file for pacman. We'll get
# those from the actual pacman packages. We'll also run the mirrorlist
# through rankmirrors now.
# Note: The archlinux repository, while guaranteed (hence why I'm using it
# here), is very, very slow. If you know of another mirror, use it.
mkdir pacman
wget http://ftp.archlinux.org/core/os/i686/pacman-3.2.2-1-i686.pkg.tar.gz -O pacman.pkg.tar.gz
tar -xzvf pacman.pkg.tar.gz -C pacman
wget http://ftp.archlinux.org/core/os/i686/pacman-mirrorlist-20090509-1-i686.pkg.tar.gz -O mirrorlist.pkg.tar.gz
tar -xzvf mirrorlist.pkg.tar.gz -C pacman
mv {pacman,}/etc/pacman.conf
mv {pacman,}/usr/bin/rankmirrors
mkdir -p /etc/pacman.d/
cat pacman/etc/pacman.d/mirrorlist | sed 's/#Server/Server/' \
> /etc/pacman.d/mirrorlist.b4.rankmirrors
rankmirrors -v -n0 /etc/pacman.d/mirrorlist.b4.rankmirrors \
> /etc/pacman.d/mirrorlist
### Image preparation -------------------------------------------------------
# Now let's get ourselves an empty image to prepare. First, we'll create an
# empty file.
# Note: You may want to change the size; I'm using 10GBs here, because that's
# the maximum space provided by EC2 for the root filesystem.
# Note: You may want to change the name; I'm using the month that Arch was
# packaged and the architecture it was packaged for.
dd if=/dev/zero of=/mnt/archlinux-2009.05-i686.fs bs=1M count=10000
# Now let's stuff a filesystem onto it. I'm using a journaled ext3 filesystem
# with the default options, because it's a known quantity. Feel free to use
# ext4 or tweak the options.
# Note: The -F flag is necessary, becasue we're writing our filesystem to an
# arbitrary file instead of a verifiable block device.
mkfs.ext3 -F /mnt/archlinux-2009.05-i686.fs
# Now, to mount the loopback filesystem, we need the loop module. The AMI I
# chose to launch above had some useful kernel modules (such as loop) bundled
# with it, so we simply have to run the following.
# Note: If your bundling host doesn't have some modules bundled with it, they
# can be acquired from the official Amazon EC2 buckets. Google can tell you
# more, as can the EC2 forums.
# Another note: This is already loaded in the AMI I chose. Thus, this command is commented out.
#modprobe loop
# Now let's mount everything! We're going to mount up our new image, as well
# as some linux devices we'll need.
mkdir -p /mnt/archlinux-2009.05-i686
mount -o loop /mnt/archlinux-2009.05-i686{.fs,}
mkdir -p /mnt/archlinux-2009.05-i686/{dev,sys,proc}
# These may not be necessary. Not sure.
#mknod -m 600 console c 5 1
#mknod -m 666 null c 1 3
#mknod -m 666 zero c 1 5
mount -o bind {,/mnt/archlinux-2009.05-i686}/dev
mount -o bind {,/mnt/archlinux-2009.05-i686}/sys
mount -t proc none /mnt/archlinux-2009.05-i686/proc
### System installation -----------------------------------------------------
# Now! Time to install us some Arch Linux!
mkdir -p /mnt/archlinux-2009.05-i686/var/lib/pacman
pacman.static -r /mnt/archlinux-2009.05-i686 -Sy
pacman.static -r /mnt/archlinux-2009.05-i686 -S base base-devel
# We'll need that mirrorlist we already created for this image, we'll just
# copy it in. Accept if it wants to overwrite.
cp {,/mnt/archlinux-2009.05-i686}/etc/pacman.d/mirrorlist
### System configuration ----------------------------------------------------
# Is it just me, or was that section way to short? I love Arch <3
# Anyway, let's configure some stuff. We'll need to pretend we're actually in
# the new system, so go ahead and chroot in:
chroot /mnt/archlinux-2009.05-i686 /bin/bash
# For whatever reason, while chrooted, we can't acquire network access unless
# we run a new dhcpcd daemon under the chroot environment. Let's do that now.
# Note: We can't kill this new dhcpcd using the -k option without killing our
# bundling host's dhcpcd as well (thus losing access to all of our work
# above). Instead, we'll use lsof to kill it when we're done.
dhcpcd eth0
# This is your chance to install any extra software you want on the image.
# Simply pacman it in, and configure it as desired.
# We're going to install sshd (so we can SSH into our image), as well as
# the OpenNTP daemon, openntpd (so our system's date and time don't get
# confused - this would be especially fatal on EC2, as then our instance
# would no longer be allowed to talk to the EC2 API!)
pacman -S openssh
pacman -S openntpd
# We also want to make sure these run on startup. Edit /etc/rc.conf and add
# them to the DAEMONS array.
nano /etc/rc.conf
# We also need to ensure that SSH connections are allowed. Add the following
# line to /etc/hosts.allow:
#
# sshd: ALL
#
# This will allow connections to sshd from anybody, anywhere. Don't worry,
# this isn't a particular security hole - EC2 has its own firewalling system.
nano /etc/hosts.allow
# Now that you've made any changes you wish to your image, we're going to do
# some final preperation to make it play friendly with EC2. Specifically, we
# want to download the proper root public-key from the EC2 API when the
# image is instantiated (i.e., the first time it boots).
# Unfortunately, I don't know how to do this yet. TODO: Figure this out.
# Instead, we'll just hardcode a root password into our image. Protip: This
# isn't very secure. )-:
passwd
# Finally, we need to make sure the instance's network is configured on boot.
# This is managed with DHCP, so we just need to modify our rc.conf - edit
# /etc/rc.conf, and replace the eth0= line with the following:
#
# eth0="dhcp"
#
# This will force the instance to instantiate dhcpcd on boot, and connect it
# to eth0, EC2's virtual network port.
nano /etc/rc.conf
# Let's do a few more final preperatory things. Edit /etc/locale.gen, and
# uncomment any locales you're interested in.
nano /etc/locale.gen && locale-gen
# Finally, we'll prepare the fstab for EC2's virtual drive configuration.
# More information on how you should configure your drives for EC2 is
# available here:
# http://docs.amazonwebservices.com/AWSEC2/latest/DeveloperGuide/index.html?
# concepts-amis-and-instances.html
# Since there's two very different setups, I'll document both i686 and x86_64
# setup here. The second, commented set of settings should be uncommented and
# traded for the first set if you're building an x86_64 image.
mv /etc/fstab{,.old}
echo 'none /dev/pts devpts defaults 0 0' >> /etc/fstab
echo 'none /dev/shm tmpfs defaults 0 0' >> /etc/fstab
echo '# i686 (m1.small, c1.medium)' >> /etc/fstab
echo '/dev/sda1 / ext3 defaults 0 1' >> /etc/fstab
echo '/dev/sda2 /mnt ext3 defaults 0 2' >> /etc/fstab
echo '/dev/sda3 swap swap defaults 0 0' >> /etc/fstab
echo '# x86_64 (m1.large, m1.xlarge, c1.xlarge)' >> /etc/fstab
echo '#/dev/sda1 / ext3 defaults 0 1' >> /etc/fstab
echo '#/dev/sdb /mnt ext3 defaults 0 2' >> /etc/fstab
# Now that we're all done, let's exit our chroot environment and get ready to
# bundle this image up.
exit
# Unfortunately, that dhcpcd instance we booted up earlier is clingy, and
# doesn't want to let go of our chrooted /dev/null. To unmount our chrooted
# /dev (and subsequently our entire chroot), we have to get rid of that
# process. This implies some disturbing connotations, however: Killing that
# dhcpcd will cause it to bring the eth0 interface down; that will cause our
# instance to lose network access to EC2. Incidentally, that also loses us
# access to our carefully configured bundling host, and all of our work so
# far.
# That's bad. We're going to avoid that by simultaneously killing all network
# processes (both the host's dhclient and our chroot's dhcpcd) and then
# launching a new dhclient on our host.
# Use ps as below to get the PIDs of both the dhclient and dhcpcd instance.
# Then kill them both, and use a bash ; to immediately launch a new dhclient.
ps aux | grep -E '(dhc|eth0)'
kill -9 785 12120 ; dhclient
# Now we can unmount!
umount /mnt/archlinux-2009.05-i686/{dev,sys,proc,}
### Image packaging ---------------------------------------------------------
# This is it! Time to package our prepared image and prepare it for uploading
# to Amazon S3 (where images are stored for instantiation)!
mkdir -p /mnt/tmp
ec2-bundle-image --user "$(cat ~/.ec2/account_number)" \
--cert ~/.ec2/cert-*.pem --privatekey ~/.ec2/pk-*.pem \
--destination /mnt/tmp --arch i386 \
--block-device-mapping "ami=/dev/sda1,root=/dev/sda1,ephemeral0=/dev/sdb" \
--prefix "archlinux-2009.05-i686" \
--image /mnt/archlinux-2009.05-i686.fs
### Image uploading ---------------------------------------------------------
# We need an S3 bucket to upload to, so let's install the s3cmd package:
yum install s3cmd
# Let's configure s3cmd. Too bad it doesn't take configuration as command-
# line flags, right? Silly UI fail (-:
echo '[default]' >> ~/.s3cfg
echo "access_key = $(cat ~/.ec2/access_key)" >> ~/.s3cfg
echo "secret_key = $(cat ~/.ec2/secret_key)" >> ~/.s3cfg
# Now, let's make a bucket to put our AMIs in. This has to be globally
# unique. I suggest using your name or company or something.
s3cmd mb s3://elliottcable-amis
# And now for the uploading!
ec2-upload-bundle \
--access-key "$(cat ~/.ec2/access_key)" \
--secret-key "$(cat ~/.ec2/secret_key)" \
--bucket elliottcable-amis --acl public-read --location US \
--manifest /mnt/tmp/archlinux-2009.05-i686.manifest.xml
# The very last step is registering your image as an instantiable AMI! Let's
# do that. The ami- string returned by this command is your new AMI!
ec2-register --verbose --headers --show-empty-fields \
elliottcable-amis/archlinux-2009.05-i686.manifest.xml
# You're all done. Now you can exit the SSH to the bundling host...
exit
# ... and if you aren't going to bundle any more instances on that host, kill
# it...
ec2-terminate-instances i-ad286fc4
# ... and try out your new image!
# Note: You'll probably want to create new security groups and keys for this,
# but the ones we've been using for the bundling host will work for now.
ec2-run-instances --instance-type m1.small --group bundling \
--key bundling-host ami-7dd23414
ec2-describe-instances i-bde2a3d4
# ... and so on.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment