Friday, October 8, 2010

How to Create a CentOS 5 AMI to run on EC2 or Eucalyptus

Building a CentOS 5 AMI

First of all, a bit of background and why I love the cloud. Previously, I have worked for large ISP's (UUNet, Level 3, British Telecom) and a couple of startups. In some cases, when I'd provision a new system, I'd go to the vendor website, spec out the box, order it, wait for it to arrive, rack and console it. Then, I'd provision switch ports, etc and through an OS and apps on it. Then, I'd unrack it, box it up and drive it to the data center. Re-rack, cable, test, switch port configure it, etc. Took a huge amount of effort. With ec2 and cloud computing, you run a few commands and you're up and running in like, oh.. 20 minutes or so. So, that's awesome, convenient, etc.

You can either run a public AMI, an image created by some random person, or you can build and run you're own. Here's how to do it my way.

First a couple of assumptions, you're running this sequence on an existing CentOS system. You have the fuse module loaded:
modprobe fuse && lsmod | grep fuse
Need that to do loopback mounts.

Ok, let's get started. Note that this whole thing can be scripted but I figured I'd give a little explanation on each step.

So, first let's create a directory to work in:

mkdir -p ~/ami/centos/ && cd ~/ami/

Now, we're going to create an empty disk image. If you know about anaconda, this is just like the downloading stage2 process (well, kinda). Mine is going to be 2G because I like to have a bunch of stuff in there. Change count to 1024 if you want it to be smaller.

dd if=/dev/zero of=centos.fs bs=1M count=2048

Now, create a file system on it:

mke2fs -F -j centos.fs

Good, now we're going to open it up to be written to by mounting it:

mount -o loop centos.fs ~/ami/centos/

Ok, now we're going to turn this thing into a Linux system you can boot up. The kernel likes to have this stuff. The directory ~/ami/centos/ is the root directory / on your new instance.

Make the /dev/ directory:
mkdir ~/ami/centos/dev
   /sbin/MAKEDEV -d ~/ami/centos/dev/ -x console
   /sbin/MAKEDEV -d ~/ami/centos/dev/ -x null
   /sbin/MAKEDEV -d ~/ami/centos/dev/ -x zero

Create /etc/

mkdir ~/ami/centos/etc/

Now, we're going to use yum - just the way the default installer does to add a bunch of software to your new system. We create a yum.conf on the local file system to use to install OS and software.

vi ~/ami/yum.conf

Add this to the file:

[main]
cachedir=/var/cache/yum
debuglevel=2
logfile=/var/log/yum.log
exclude=*-debuginfo
gpgcheck=0
obsoletes=1
pkgpolicy=newest
distroverpkg=redhat-release
tolerant=1
exactarch=1
reposdir=/dev/null
metadata_expire=1800
[base]
name=CentOS-5.5 Base
baseurl=http://mirror.centos.org/centos/5.5/os/x86_64/
gpgcheck=0
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5.5
priority=1
protect=1
#released updates
[update]
name=CentOS-5.5 Updates
baseurl=http://mirror.centos.org/centos/5.5/updates/x86_64/
gpgcheck=0
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5.5
priority=1
protect=1
#packages used/produced in the build but not released
[addons]
name=CentOS-5.5 Addons
baseurl=http://mirror.centos.org/centos/5.5/addons/x86_64/
gpgcheck=0
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5.5
priority=1
[extras]
name=CentOS 5.5 Extras $releasever $basearch
baseurl=http://mirror.centos.org/centos/5.5/extras/x86_64/
enabled=1


So that pulls stuff from the official CentOS mirror list. GPG checking is off because we don't have the keys installed.

Next, create the proc file system and mount it up. This is where the kernel keeps track of all the stuff it's doing.

mkdir ~/ami/centos/proc
   mount -t proc none ~/ami/centos/proc/

Ok, here's where the magic begins to happen. We're going to start loading os packages:

yum -c ~/ami/yum.conf --installroot=/root/ami/centos -y groupinstall Core

So using that command, you can install all the stuff you want. The following is my list, you probably don't need the group 'Development Tools'. That's a BUNCH of stuff you only really need if you're doing development. I like to have it on some instances, some it never gets used. So, you should probably ignore it. A good way to see what's available to install is to run:

yum grouplist | less

and that'll tell you what package groups exist. If you're creating a DNS server, you'll want to add the 'DNS Name Server' group. Pick through the list below and install what you want/need. Not everybody is going to want JDK for example.

yum -c ~/ami/yum.conf --installroot=/root/ami/centos -y groupinstall 'Text-based Internet'
   yum -c ~/ami/yum.conf --installroot=/root/ami/centos -y groupinstall Ruby
   yum -c ~/ami/yum.conf --installroot=/root/ami/centos -y groupinstall 'Web Server'
   yum -c ~/ami/yum.conf --installroot=/root/ami/centos -y groupinstall 'Development Tools'
   yum -c ~/ami/yum.conf --installroot=/root/ami/centos -y groupinstall 'Java'
   yum -c ~/ami/yum.conf --installroot=/root/ami/centos -y groupinstall 'MySQL Database'
   yum -c ~/ami/yum.conf --installroot=/root/ami/centos -y install curl wget rsync sudo mlocate lsof man tcpdump bc iptables


Once all your software is installed, configure sshd. Now, this is just an old sysadmin tip. Don't run sshd on port 22. It gets scanned 24x7x365 with all kinds of brute force attacks and everything else. I always, always, always run it on another port. You can do tcpwrappers and other tricks (iptables, etc) but just running it on some higher port saves you tons of problems.
So, in this example, I'm running on port 55000.

vi ~/ami/centos/etc/ssh/sshd_config

and add something like this:

Port 55000
Protocol 2
SyslogFacility AUTHPRIV

PermitRootLogin yes
MaxAuthTries 4
PasswordAuthentication no
ChallengeResponseAuthentication no

GSSAPIAuthentication yes
GSSAPICleanupCredentials yes
UsePAM yes

AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL
X11Forwarding yes

Subsystem sftp /usr/libexec/openssh/sftp-server


Create a resolv.conf file with valid name servers. I pretty much always use google's because it's fast and anycasted so it's going to be fast no matter where you are, and it's google.

vi ~/ami/centos/etc/resolv.conf

and add:
# Google's public DNS servers.
nameserver 8.8.8.8
nameserver 8.8.4.4


Now, configure you motd. This is the banner that gets displayed whenever anyone logs in:

vi ~/ami/centos/etc/motd

Put whatever you want in there.. here's an example:

________________________________________
/ Unauthorized users will be killed and  \
\ eaten.                                 /
 ----------------------------------------
        \   ^__^
         \  (xx)\_______
            (__)\       )\/\
             U  ||----w |
                ||     ||


This is optional too but I like to have ec2tools installed on my instances:

cd ~/ami/centos/ && wget http://s3.amazonaws.com/ec2-downloads/ec2-ami-tools.noarch.rpm*
   chroot /root/ami/centos rpm -Uvh ec2-ami-tools.noarch.rpm


vi /etc/profile.d/ec2tools.sh
export EC2_HOME=/opt/ec2-tools
   export PATH=$EC2_HOME/bin:$PATH

Now, configure DHCP for networking:

mkdir -p /etc/sysconfig/network-scripts/
   vi /etc/sysconfig/network-scripts/ifcfg-eth0

Enter:

DEVICE=eth0
BOOTPROTO=dhcp
ONBOOT=yes
TYPE=Ethernet
USERCTL=yes
PEERDNS=yes
IPV6INIT=no
PERSISTENT_DHCLIENT=yes

That PERSISTENT_DHCLIENT is very nice to have. It means that if for some reason the DHCP server bites the dust, keep the lease you have. Otherwise your instance could loose it's IP at which point, it's game over.

Turn on networking:
vi /etc/sysconfig/network

add:

NETWORKING=yes

Now, configure fstab to mount everything up:

vi /etc/fstab

Add the following:

/dev/sda1               /                       ext3    defaults 1 1
none                    /dev/pts                devpts  gid=5,mode=620 0 0
none                    /dev/shm                tmpfs   defaults 0 0
none                    /proc                   proc    defaults 0 0
none                    /sys                    sysfs   defaults 0 0
/dev/sdc1               /mnt                    ext3    defaults 0 0
/dev/sdc2               swap                    swap    defaults 0 0

Next, turn on the services you want.

chroot ~/ami/centos/ bash
chkconfig --level 345 sshd on
chkconfig --level 345 httpd on

exit 

Almost done. I also like to add a user account with a password and sudo access, so if for some reason I can't get in with my ssh key(s), I can just login as this user and sudo to root and figure out what's going on. This step is optional but very useful:

chroot ~/ami/centos/ bash
   useradd -g users mclovin
   passwd mclovin

Then add your user to the sudoers file:

visudo

find this line:
root    ALL=(ALL)       ALL

and add your new user:
mclovin    ALL=(ALL)       ALL

Then, exit the chroot'd env:
exit

umount ~/ami/centos/

That's pretty much it holmes. Once that's all done you can unmount the image file. Bundle, upload and run.

1 comment:

  1. Awesome, thanks a lot for writing this up. I'm eagerly awaiting the next post which elaborates on "Bundle, upload and run" :)

    This went smoothly, but there's a few hitches. Here's my notes:


    * Brackets in yum.conf not rendering in HTML.
    * second line has extra indentation in two-line code blocks
    * Note that ruby packages are required by ec2-ami-tools
    * chroot /root/ami/centos
    * move 'chroot ~/ami/centos/ bash' up before 'vi /etc/profile.d/ec2tools.sh'
    * remove 'exit' (just stay in chroot for useradd)
    * chkconfig --level 345 httpd on # only required if 'Web Server' group was installed
    * Error on 'passwd mclovin': 'passwd: root:system_r:unconfined_t:s0-s0:c0.c1023 is not authorized to change the password of mclovin'. Had to disable selinux by putting 0 in /selinux/enforce in chroot (and host?)
    * need to 'umount ~/ami/centos/proc' before 'umount ~/ami/centos/'
    * Grammar: lose 'loose', use 'lose' instead; possessive 'its' has no apostrophe

    ReplyDelete