Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save dysinger/1614857 to your computer and use it in GitHub Desktop.

Select an option

Save dysinger/1614857 to your computer and use it in GitHub Desktop.

Revisions

  1. @ELLIOTTCABLE ELLIOTTCABLE revised this gist May 26, 2009. 1 changed file with 6 additions and 0 deletions.
    6 changes: 6 additions & 0 deletions configuring-archlinux-ami.sh
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,12 @@
    # 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
  2. @ELLIOTTCABLE ELLIOTTCABLE renamed this gist May 26, 2009. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  3. @ELLIOTTCABLE ELLIOTTCABLE created this gist May 26, 2009.
    399 changes: 399 additions & 0 deletions gistfile1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,399 @@
    #!/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.

    ### 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 root@ec2-67-202-10-136.compute-1.amazonaws.com \
    -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 root@ec2-67-202-10-136.compute-1.amazonaws.com \
    -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 root@ec2-67-202-10-136.compute-1.amazonaws.com \
    -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 \
    root@ec2-67-202-10-136.compute-1.amazonaws.com:~/.ec2/
    scp -i ~/.ec2/id_rsa-bundling-host \
    ~/.ec2/account_number \
    root@ec2-67-202-10-136.compute-1.amazonaws.com:~/.ec2/
    scp -i ~/.ec2/id_rsa-bundling-host \
    ~/.ec2/access_key \
    root@ec2-67-202-10-136.compute-1.amazonaws.com:~/.ec2/
    scp -i ~/.ec2/id_rsa-bundling-host \
    ~/.ec2/secret_key \
    root@ec2-67-202-10-136.compute-1.amazonaws.com:~/.ec2/

    # And back in again!
    ssh root@ec2-67-202-10-136.compute-1.amazonaws.com \
    -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.