Create your own CentOS AMI Image (S3 Backed)

Welcome to my walk through of the steps required to build a CentOS 6.2 AMI image for your EC2 instances.  Previously, I have focused on Fedora for my AMIs but some people would prefer the stability that Redhat Enterprise Linux or it's free counterpart CentOS provide.  CentOS is popular because it tracks the Redhat EL distributions.  With these instructions you can create your own CentOS6 AMI with exactly the software and scripts that you want it to contain (this is particularly useful if you want to create your own boot up scripts).

32 or 64 bit

Note: You can create either an i386 (32 bits) or x86_64 (64 bits) architecture AMI using these instructions.  Please look out for the architecture specific instructions.

Please choose a AMI to launch for creating your AMI, preferably a recent Centos or Fedora AMI so that you can create ext4 filesystems.  Please use an AMI with the SAME Architecture as the AMI you want to build (the yum install will fail otherwise!)

NOTE NOTE: Please consider doing all of this in the cloud.  EC2 instances have much faster network connection to Amazon S3 and the upload process will run much more quickly.

Step by Step: Creating your AMI

Setting up the Tools to create your AMI

The easiest way to create an AMI is to load the practicalclouds amitools profile which will install all of the necessary tools for you.  If you don't want to use my profile then the following instructions will help you prepare your PC or EC2 instance with the right tools to create your AMI.  The guide does assume that you are creating your AMI on a machine already running either CentOS or a Redhat compatible build (sorry Windows and Debian users; I'm sure that there's great guides out there for you).

Install the Amazon API and AMI Tools, e2fsprogs and set things up so you can create AMIs: -

yum -y install e2fsprogs ruby java-1.6.0-openjdk unzip MAKEDEV
mkdir -p /root/.ec2
cp amikey.pem /root/.ec2
cp amicert.pem /root/.ec2
mkdir -p /opt/EC2TOOLS /data /opt/EC2YUM
# Install the latest Amazon EC2 API TOOLS
curl -o /tmp/ec2-api-tools.zip http://s3.amazonaws.com/ec2-downloads/ec2-api-tools.zip
cd /tmp
unzip /tmp/ec2-api-tools.zip
cp -r /tmp/ec2-api-tools-*/* /opt/EC2TOOLS
# Install the latest Amazon EC2 AMI Tools
curl -o /tmp/ec2-ami-tools.zip http://s3.amazonaws.com/ec2-downloads/ec2-ami-tools.zip
cd /tmp
unzip ec2-ami-tools.zip
cp -r /tmp/ec2-ami-tools-*/* /opt/EC2TOOLS

Please supply your own EC2 certs for accessing the Amazon's APIs - I've saved them under /root/.ec2/amikey.pem and amicert.pem.  I've also decided to install the Amazon AWS tools under /opt/EC2TOOLS, but you can put them anywhere you like.

Set up your shell so that the EC2 API/AMI tools are available in your PATH: -

cat <<EOT >/root/.bashrc
export PATH=\$PATH:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/opt/EC2TOOLS/bin
export EC2_HOME=/opt/EC2TOOLS
export EC2_PRIVATE_KEY=~/.ec2/amikey.pem
export EC2_CERT=~/.ec2/amicert.pem
export JAVA_HOME=/usr
EOT

source ~/.bashrc

Set up some environment varibles to aid in allowing you to cut-and-paste my instructions: -

export RELEASE=6
export ARCH=`uname -i`

Create your image file and prepare the install

Create a disk image and filesystem, then mount it (if you are running this procedure from an old OS then you might not have the ability to create an EXT4 filesystem, in which case you should use EXT3).  For this guide, I am going to create my images under the directory /data and I mount the ephemeral disk to this location so I have enough space to work:-

mkfs.ext4 /dev/xvda2
mount /dev/xvda2 /data
dd if=/dev/zero of=/data/CentOS$RELEASE-$ARCH-base.img bs=1M count=10240
mkfs.ext4 -F -j /data/CentOS$RELEASE-$ARCH-base.img
mkdir /mnt/image-base
mount -o loop /data/CentOS$RELEASE-$ARCH-base.img /mnt/image-base/

Label the image so that we can use that to identify our volume: -

e2label /dev/loop0 root

You have your newly created image mounted to /mnt/image-base.  Create a structure ready for install: -

cd /mnt/image-base
mkdir -p proc etc dev var /var/cache /var/log /var/lock/rpm sys

Create the fstab file, which is in charge of specifying the filesystems to mount (an include the noatime and nodiratime options for performance): -

cat <<EOT >/mnt/image-base/etc/fstab
LABEL=root    /        ext4        defaults,noatime,nodiratime    1    1
EOT

Create yum repository definitions for the CentOS 6 repositories

mkdir -p /opt/EC2YUM
cat <<EOT >/opt/EC2YUM/yum-ami.conf
[base]
name=CentOS-$RELEASE - Base
mirrorlist=http://mirrorlist.centos.org/?release=$RELEASE&arch=$ARCH&repo=os
#baseurl=http://mirror.centos.org/centos/$RELEASE/os/$ARCH/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-$RELEASE

#released updates
[updates]
name=CentOS-$RELEASE - Updates
mirrorlist=http://mirrorlist.centos.org/?release=$RELEASE&arch=$ARCH&repo=updates
#baseurl=http://mirror.centos.org/centos/$RELEASE/updates/$ARCH/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-$RELEASE

#packages used/produced in the build but not released
[addons]
name=CentOS-$RELEASE - Addons
mirrorlist=http://mirrorlist.centos.org/?release=$RELEASE&arch=$ARCH&repo=addons
#baseurl=http://mirror.centos.org/centos/$RELEASE/addons/$ARCH/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-$RELEASE

#additional packages that may be useful
[extras]
name=CentOS-$RELEASE - Extras
mirrorlist=http://mirrorlist.centos.org/?release=$RELEASE&arch=$ARCH&repo=extras
#baseurl=http://mirror.centos.org/centos/$RELEASE/extras/$ARCH/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-$RELEASE

#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$RELEASE - Plus
mirrorlist=http://mirrorlist.centos.org/?release=$RELEASE&arch=$ARCH&repo=centosplus
#baseurl=http://mirror.centos.org/centos/$RELEASE/centosplus/$ARCH/
gpgcheck=1
enabled=0
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-$RELEASE

#contrib - packages by Centos Users
[contrib]
name=CentOS-$RELEASE - Contrib
mirrorlist=http://mirrorlist.centos.org/?release=$RELEASE&arch=$ARCH&repo=contrib
#baseurl=http://mirror.centos.org/centos/$RELEASE/contrib/$ARCH/
gpgcheck=1
enabled=0
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-$RELEASE
EOT

You may also wish to enable the "centosplus" and "contrib" repositories which are disabled by default.  We onlyrequire the "base" and "updates" repositories to make a basic AMI.

Install the core CentOS packages

Do the base install using yum

yum -c /opt/EC2YUM/yum-ami.conf --installroot=/mnt/image-base -y groupinstall Base

Prevent JAVA and Performance Issues

NOTE: There is an issue which affects old versions of Xen and 32 bit instances.  See this blog post for more details.  Add the following to the AMI in order to work around the issue:-

echo "hwcap 1 nosegneg" >/mnt/image-base/etc/ld.so.conf.d/libc6-xen.conf

Install and configure the core services and devices

WARNING WARNING: Make sure that you install the dhclient package as it is not installed as part of the 'Base' package group.  Without this, your EC2 instance will not be able to access the network.
yum -c /opt/EC2YUM/yum-ami.conf --installroot=/mnt/image-base -y install openssh-server yum-plugin-fastestmirror.noarch e2fsprogs dhclient
cat <<EOT >>/mnt/image-base/etc/ssh/sshd_config
UseDNS no
PermitRootLogin without-password
EOT

Create some devices so that we can still use the network when we chroot into our image directory (/mnt/image-base): -

MAKEDEV -d /mnt/image-base/dev -x console
MAKEDEV -d /mnt/image-base/dev -x null
MAKEDEV -d /mnt/image-base/dev -x zero

mount -o bind /dev /mnt/image-base/dev
mount -o bind /dev/pts /mnt/image-base/dev/pts
mount -o bind /dev/shm /mnt/image-base/dev/shm
mount -o bind /proc /mnt/image-base/proc
mount -o bind /sys /mnt/image-base/sys

Create network settings...

cat <<EOT >/mnt/image-base/etc/sysconfig/network
NETWORKING=yes
HOSTNAME=localhost.localdomain
EOT

cat <<EOT >/mnt/image-base/etc/sysconfig/network-scripts/ifcfg-eth0
ONBOOT=yes
DEVICE=eth0
BOOTPROTO=dhcp
NM_CONTROLLED=yes
EOT

Setting up GRUB

We need to set up GRUB so that the Amazon Kernel Image can boot into our new kernel and initial ram disk.  Configure Grub (version 1) with the details of your PAE kernel and add important kernel boot parameters.

32bit only

WARNING: You must add idle=halt to your kernel command line for 32 bit AMIs, otherwise you will experience crashes when booting your instances on older versions of Xen running within the Amazon EC2 cloud.

For more information about debugging crashes and kernel issues, please view this post in my blog.

cat <<EOL > /mnt/image-base/boot/grub/grub.conf
default=0
timeout=0
title CentOS$RELEASE
root (hd0)
kernel /boot/vmlinuz ro root=LABEL=root rd_NO_PLYMOUTH selinux=0 console=hvc0 loglvl=all sync_console console_to_ring earlyprintk=xen nomodeset idle=halt 
initrd /boot/initramfs
EOL
64bit only

NOTE: 64 bit AMIs (x86_64) do not need to add idle=halt to the kernel command line, but it is still a good idea to add the other Xen debugging parameters.

cat <<EOL > /mnt/image-base/boot/grub/grub.conf
default=0
timeout=0
title CentOS$RELEASE
root (hd0)
kernel /boot/vmlinuz ro root=LABEL=root rd_NO_PLYMOUTH selinux=0 console=hvc0 loglvl=all sync_console console_to_ring earlyprintk=xen nomodeset 
initrd /boot/initramfs
EOL

Now we need to finish setting up the boot loader so that our AMI can boot.

ln -s /boot/grub/grub.conf /mnt/image-base/boot/grub/menu.lst
kern=`ls /mnt/image-base/boot/vmlin*|awk -F/ '{print $NF}'`
kernver=${kern#vmlinuz-}
ird=`ls /mnt/image-base/boot/initramfs*.img|awk -F/ '{print $NF}'`
sed -ie "s/vmlinuz/$kern/" /mnt/image-base/boot/grub/grub.conf
sed -ie "s/initramfs/$ird/" /mnt/image-base/boot/grub/grub.conf

Add Puppet and MCollective

Please consider adding puppet to your AMI image, I am moving all of my scripts and profiles over to puppet.  Follow these steps to install.

Configure your boot: or use my rc.local scripts

NOTE NOTE: As a minimum you should configure root's ssh key so that you can login to your new instance via SSH, I recommend adding my more developed boot script.

You have finished your basic installation, but a bare install will not do very much of use and contains no integration with Amazon AWS itself.  You should now add my rc.local script which adds functionality which enables the EC2 instance booting from the AMI to set up ssh keys, system prompts, access files from S3 and load software profiles.  The rc.local has been updated and tested to work correctly with CentOS.

wget http://files001.practicalclouds.com/rc.local.sh
cp rc.local.sh /mnt/image-base/etc/rc.d/rc.local
chmod +x /mnt/image-base/etc/rc.d/rc.local

Install s3fs (Optional) - This does not work ATM because the version of FUSE is too old.

Install s3fs so that we can mount S3 buckets to our instances.  As we are compiling s3fs you want to do this from a machine with the same architecture and so I do this after I have created my basic AMI.  With my "amitools" profile I can boot an instance which will load all of the required AMI tools and unbundle my AMI ready for updating.  Here are the steps for installing s3fs (note, we are not installing gcc etc to the finished AMI, just the instance being used to edit it).

yum -y install "gcc*" make fuse-devel libcurl-devel libxml2-devel openssl-devel
wget http://s3fs.googlecode.com/files/s3fs-1.61.tar.gz
tar xvfpz s3fs-1.61.tar.gz
cd s3fs-1.61
./configure
make && make install
cp /usr/local/bin/s3fs /mnt/image-base/usr/local/bin
cp -rp /usr/local/share/man/man1/s3fs.1 /mnt/image-base/usr/local/share/man/man1

Install Python Boto (Optional: For Route53 Access)

wget http://boto.googlecode.com/files/boto-2.3.0.tar.gz
tar xvfpz boto-2.3.0.tar.gz
cd boto-2.3.0
python setup.py install --prefix=/mnt/image-base/usr

Finishing and uploading the AMI image to Amazon

Clean up the image: -

yum -c /opt/EC2YUM/yum-ami.conf --installroot=/mnt/image-base -y clean packages
rm -rf /mnt/image-base/root/.bash_history
rm -rf /mnt/image-base/var/cache/yum
rm -rf /mnt/image-base/var/lib/yum

We've now finished creating our Centos image, but it's not ready to use just yet.  First we must bundle our image which compresses, encrypts and then splits it ready for upload to S3.  We're going to bundle the default kernalid etc. with the image so that these are included in the manifest (and the registration process via the Amazon Web Console will work).  Performing a quick check for the available AKI's using the EC2 AMI Tools:-

[root@localhost bin]# /opt/EC2TOOLS/bin/ec2-describe-images -H --region eu-west-1 -x all|grep "pv-grub-hd0"
IMAGE    aki-4deec439    ec2-public-images-eu/pv-grub-hd0-V1.01-i386.gz.manifest.xml    amazon    available    public        i386    kernel                instance-store    paravirtual    xen
IMAGE    aki-4feec43b    ec2-public-images-eu/pv-grub-hd0-V1.01-x86_64.gz.manifest.xml    amazon    available    public        x86_64    kernel                instance-store    paravirtual    xen
IMAGE    aki-47eec433    ec2-public-images-eu/pv-grub-hd00-V1.01-i386.gz.manifest.xml    amazon    available    public        i386    kernel                instance-store    paravirtual    xen
IMAGE    aki-41eec435    ec2-public-images-eu/pv-grub-hd00-V1.01-x86_64.gz.manifest.xml    amazon    available    public        x86_64    kernel                instance-store    paravirtual    xen
IMAGE    aki-8a6657fe    ec2-public-images-eu/pv-grub-hd00_1.02-i386.gz.manifest.xml    amazon    available    public        i386    kernel                instance-store    paravirtual    xen
IMAGE    aki-60695814    ec2-public-images-eu/pv-grub-hd00_1.02-x86_64.gz.manifest.xml    amazon    available    public        x86_64    kernel                instance-store    paravirtual    xen
IMAGE    aki-64695810    ec2-public-images-eu/pv-grub-hd0_1.02-i386.gz.manifest.xml    amazon    available    public        i386    kernel                instance-store    paravirtual    xen
IMAGE    aki-62695816    ec2-public-images-eu/pv-grub-hd0_1.02-x86_64.gz.manifest.xml    amazon    available    public        x86_64    kernel                instance-store    paravirtual    xen

Use hd0 for S3 backed (hd00 is for EBS backed) and choose the latest version for your architecture.  You need to first bundle the image so that it is reduced in size, split into parts and encrypted for upload to S3.

/opt/EC2TOOLS/bin/ec2-bundle-image --image /data/CentOS$RELEASE-$ARCH-base.img --prefix ami-CentOS$RELEASE-$ARCH-base --cert ~/.ec2/amicert.pem --privatekey ~/.ec2/amikey.pem --user youruser# --destination /data --arch $ARCH
NOTE NOTE:  You can turn a uploaded bundle back into an image file by using the ec2-unbundle command, so you don't need to try and store the whole image file in S3. e.g.
ec2-unbundle --privatekey ~/.ec2/amikey.pem -m ami-CentOS$RELEASE-$ARCH-base.manifest.xml

Next, we upload to S3 (note this step took over an hour to complete for me)...

/opt/EC2TOOLS/bin/ec2-upload-bundle --manifest /data/ami-CentOS$RELEASE-$ARCH-base.manifest.xml --bucket practicalclouds-ami --access-key accesskeyhere --secret-key secretkeyhere

Registering the new Image

In order to register the new image you need to use the Amazon AWS console or you must install the EC2 CLI Tools (as instructed at the top of this page).

Using the command line tools:-

ec2-register -K ~/.ec2/amikey.pem -C ~/.ec2/amicert.pem practicalclouds-ami/ami-CentOS${RELEASE}-$ARCH-base.manifest.xml --description "CentOS $RELEASE AMI $ARCH" --name "CentOS-${RELEASE}-base-image-$ARCH" --architecture $ARCH --region eu-west-1 --kernel THE_CORRECT_AKI_FOR_YOUR_REGION_AND_ARCHITECTURE

You can of course change region as you wish, description, name, arch, etc. but remember the kernels & ramdisks are specific to each region, so you must use the correct one!

It's now time to test your new AMI... good luck!

Comments

M.D.'s picture

This is exactly what I have been looking for.
Will follow the steps and give feedback if all works as outlined.
Thanks!

Erik van der Arend's picture

Hi Dave,
Thanks for your blog. It was helpful.

For the creation of my AMI i ran into an error at yum-ami.conf

1. [update] should be [updates]
2. My configuration requires also a section for [extras] and other sections [contrib],[centosplus] mentioned in /etc/yum.repos.d/CentOs-Base.repo

Greetings,
Erik van der Arend
Looking4ward

Dave McCormick's picture

Thanks for your comments.  I hadn't included the other repositories because only the base and updates are required for my basic AMI, but I see that it would be a better to include the others and give people more choice, so I have updated my instructions.

Many thanks

Dave

jl's picture

assuming /var/loc/rpm should be /var/lock/rpm right?

mkdir -p proc etc dev var /var/cache /var/log /var/lock /var/loc/rpm sys

thanks for the write up

Dave McCormick's picture

Hi, thanks for pointing that out - yes was meant to be lock.

Dave

Alan Cabrera's picture

I had to remove [addons] from yum-ami.conf since it no longer seems to reside on the centos mirrors.