Not Dead Head

Side A

Category Archives: Operations

Know your Kung-Fu: Bash scripting … dynamic variable names using arrays/hashes.

When you read in the bash manpage about arrays, you will find:

… Arrays are assigned to using compound assignments of the form name=(value1 … valuen), where each value is of the form [subscript]=string. Only string is required. If the optional brackets and subscript are supplied, that index is assigned to; otherwise the index of the element assigned is the last index assigned to by the statement plus one. Indexing starts at zero. …

Any element of an array may be referenced using ${name[subscript]}.  The braces are required to avoid conflicts with pathname expansion. …

so for me that sounds like in bash arrays are actually hashes!, with dynamic keys which default to integers starting at 0. Nice.

I must say, I don’t use hashes or arrays in bash very often (if you need complex data structures, you should use an appropriate programming language)  and actually I used them here to get something else … dynamic variable names. By the way, this is also known as indirect variable reference.

This is just a “Mami mami, look what I can do!” script, the correct way to go, will be to use an array of hashes and thus use only one CLUSTER array or you could store this information in json format somewhere and access it from there.

#!/bin/bash
set -e
usage()
{
cat<< EOF
usage: $0 options

This script creates mysql dumps from the given cluster's mysql server or
RDS instance and dumps it into the production database.

OPTIONS:
       -d Database to dump
       -h Show this message
       -r the region [cluster_1|cluster_2|cluster_3]
       -t database table
       -v Verbose
EOF
exit 0
}

while getopts “ht:r:p:v” OPTION
do
  case $OPTION in
       h)
         usage
         ;;
       d)
         DATABASE=$OPTARG
         ;;
       r)
         REGION=$OPTARG
         # internal hack because array names in bash can't contain '-'
         REGION=${REGION/-/_}
         ;;
       t)
         DB_TABLE=$OPTARG
         ;;
       v)
         set -x
         MYSQL_OPT="--verbose"
         ;;
       ?)
         usage
         ;;
  esac
done

if [[ -z $REGION ]] || [[ -z $DATABASE ]] || [[ -z $DB_TABLE ]]
then
  usage
fi

# define some stuff
DUMP_DIR="/tmp/$(DB_TABLE)_dumps"

# all the target location and credentials
declare -A cluster_1
declare -A cluster_2
declare -A cluster_3

#
# TODO: get the server names using the ec2-api-tools
cluster_1=(["DB_USER"]="some_user" \
           ["DB_PASSWD"]="p@ssw0rd" \
           ["DB_HOST_RO"]="My_Slave" \
           ["DB_HOST_RW"]="My_Master" )

cluster_2=(["DB_USER"]="some_user" \
           ["DB_PASSWD"]="p@ssw0rd" \
           ["DB_HOST_RO"]="My_Slave" \
           ["DB_HOST_RW"]="My_Master" )

cluster_3=(["DB_USER"]="some_user" \
           ["DB_PASSWD"]="p@ssw0rd" \
           ["DB_HOST_RO"]="My_Slave" \
           ["DB_HOST_RW"]="My_Master" )

#
# backup production data
# dyanmic variable names are set using bash's indirect variable reference
BCKP_DUMP_FILE="${DUMP_DIR}/bckp_${REGION}_$(DB_TABLE)_$(date +%Y%m%d%H%M).dump"
echo "Dumping \"core_translate\"-table from ${REGION} production database"
time /usr/bin/mysqldump --single-transaction $(MYSQL_OPT) \
                        -u$(eval echo "\${${REGION}[DB_USER]}") \
                        -p$(eval echo "\${${REGION}[DB_PASSWD]}") \
                        -h$(eval echo "\${${REGION}[DB_HOST_RO]}") \
                        $(DATABASE) $(DB_TABLE) > ${BCKP_DUMP_FILE}
Advertisements

Using Amazon EC2 … basics about custom AMIs.

Why do I want to create custom EC2 AMIs? Personally because I prefer “to know” (yes, this is relative) how the system was setup.  Start with a minimal installation and run the necessary Chef (or the CM system of your preference) recipes to configure the system as expected. Also because running pre-setup systems may cause a lot of trouble when trying to customize them.

Let’s setup a custom EC2 image with CentOS 5.

I put this document together from many sources, probably some of the ones you already read. This is my version.

What do you’ll need?

You need to have an AWS account, but if you’re reading this you probably have some experience already.

Before we start, you have to go through the following steps. Just go to the Amazon Web Console and

* Create a 32bit Linux instance (preferably CentOS or Fedora); you can use almost any of the AMIs out there.
* Create a 10 GB EBS volume to start with. You may (and probably will) change the size after you have done some trials.
* Attach the volume to the instance (I’ll attach it to /dev/sdi).

Partitioning

I’ll only use one partition for the root filesystem and one for the swap space.  The partition scheme will look like this:

   Device Boot      Start         End      Blocks   Id  System
/dev/sdi1               1        1156     9285538+  83  Linux
/dev/sdi2            1157        1305     1196842+  83  Linux

Amazon’s py-grub bootloader can’t boot off LVM partitions, so we need to use ext3, reiserFS or XFS. But do you really need LVM on a EC2 instance? I’ll go for  ext3 (keep it simple … at first) :

$ mkfs -t ext3 /dev/sdi1
$ mkswap /dev/sdi2
$ mount /dev/sdi1 /mnt
$ mkdir /mnt/{boot,tmp,dev,sys,proc,etc,var}
$ mount -t proc none /mnt/proc
$ mount -t sysfs none /mnt/sys
$ for i in console null zero ; do /sbin/MAKEDEV -d /mnt/dev -x $i ; done

You’ve now got a basic partition and directory scheme created/mounted in /mnt.

Installation and Configuration

Use yum to install CentOS in the image, which is straight-forward and easy. Make sure that it’s configured properly to use CentOS repositories only. If you’re working on a CentOS-based instance, you’ll only need to have /etc/yum.repos.d/CentOS-Base.repo in place, which for CentOS 5 (latest) 32bit will look like this:

[base]
name=CentOS-5 - Base
baseurl=http://mirror.centos.com/centos/5/os/i386/
failovermethod=priority
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

[updates]
name=CentOS-5 - Updates
baseurl=http://mirror.centos.com/centos/5/updates/i386/
failovermethod=priority
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#packages used/produced in the build but not released
[addons]
name=CentOS-5 - Addons
baseurl=http://mirror.centos.com/centos/5/addons/i386/
failovermethod=priority
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#additional packages that may be useful
[extras]
name=CentOS-5 - Extras
baseurl=http://mirror.centos.com/centos/5/extras/i386/
failovermethod=priority
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-5 - Plus
baseurl=http://mirror.centos.org/5/centosplus/i386/
failovermethod=priority
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#contrib - packages by Centos Users
[contrib]
name=CentOS-5 - Contrib
baseurl=http://mirror.centos.org/centos/5/contrib/i386/
failovermethod=priority
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

[epel]
name=Extra Packages for Enterprise Linux 5 - i386
baseurl=http://mirror.rightscale.com/epel/5/i386
failovermethod=priority
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL

Any other repositories, which may be defined in /etc/yum.repos.d, should either be removed entirely or have enabled=0 set. This way you start with a clean CentOS. You can always mess with it later.

Now you’re ready to install the Core package group into /mnt:

$ yum --installroot=/mnt -y groupinstall core

This will install about 180 packages. As the name suggests, it is core. All you need to start the image and do basic sysadmin tasks is in there. You won’t find much more than that, though. Your own customization will of course follow later.

Next, we need to create /mnt/etc/fstab:

/dev/sda1  /        ext3    defaults        0 0
/dev/sda2  swap      swap    defaults        0 0
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

This is a pre-requisite for successful kernel installation.

XEN Kernel

$ yum --installroot=/mnt install kernel-xen

It’s indeed CentOS’s XEN kernel, which we use here. Not an Amazon kernel, no wild custom hacking (at this point). 😉

So let’s install grub:

$ chroot /mnt grub-install /dev/sdi

This may throw an error (no suitable device found). That’s ok. We don’t need the boot sector written. We’re only interested in populating /boot/grub.

Now let’s chroot altogether for the initrd and bootloader work:

$ chroot /mnt
$ cd /boot

At the time of this writing, kernel version was 2.6.18-194.26.1.el5xen, as “ls -l” confirms. Make sure you get the name exactly right! Spelling errors at this stage will result in an image, which will never boot.

Now build a new initial RAM disk (initrd) with the XEN kernel modules:

$ mkinitrd --preload=xenblk --preload=xennet --fstab=/etc/fstab \
 -f initrd-2.6.18-194.26.1.el5xen.img 2.6.18-194.26.1.el5xen

Essentially we’re replacing the existing initrd with all required modules added.

I had big big trouble starting an instance without the reference to the fstab file. The kernel would refuse to boot because it isn’t able to resolve where fstab is located, otherwise. I will have to investigate that further, but for now the added reference to /etc/fstab wouldn’t do any harm or change anything else.

Other tutorials suggest a way more complicated way of rebuilding an initrd file (manually unpacking, tinkering with the init script, putting it all back together). There’s always a way to overcomplicate things. mkinitrd used with the right parameters does exactly what we need it to do.

Now edit /boot/grub/menu.lst to look like this:

default 0
timeout 1
title CentOS5.5
     root (hd0,0)
     kernel /boot/vmlinuz-2.6.18-194.26.1.el5xen root=/dev/sda1
     initrd /boot/initrd-2.6.18-194.26.1.el5xen.img

Kernel installation/configuration done. Amazon’s py-grub will now be able to find the menu.lst file in the right place, use the kernel from the first partition (requirement!), load our initrd file with the xen modules we added, and then run the startup off /dev/sda1, which is where / is located.

Leave the chroot environment:

$ exit

But we wouldn’t be able to connect to the image yet. We need networking!

Networking
AWS uses DHPC to manage the networking configuration:

/mnt/etc/sysconfig/network should contain:

NETWORKING=yes

/mnt/etc/sysconfig/network-scripts/ifcfg-eth0 should contain:

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

Then ensure that networking will be started:

$ chroot /mnt chkconfig --level 2345 network on

For simplicity, let’s allow root to log in with a password (automating use of EC2 keypairs etc is a bit beyond the purpose of this, although it’s definitely the preferred method!):

$ echo "PermitRootLogin yes" >> /mnt/etc/ssh/sshd_config

Now we’ve got to set the root password:

$ chroot /mnt pwconv
$ chroot /mnt passwd

It’s highly recommended to use the AWS keypairs and disallow password-based logins for both security and automation reasons. Read section Using AWS Keypairs below.

SELinux

Now, CentOS comes with SELinux set to enforcing mode per default. That is a good thing! However, what we just set up may or may not be labelled correctly, depending on the instance you used to create it. It’s better to assume for the first start that it is not labelled properly. So let’s make sure this will be sorted out on the very first start of the image:

$ touch /mnt/.autorelabel

If you can’t be bothered to deal with it right now, you could alternatively set this in
/mnt/etc/sysconfig/selinux:

SELINUX=permissive

or even

SELINUX=disabled

Build your AMI and start a new Instance from it

You need to install the EC2 API tools and EC2 AMI tools which you can get from the AWS Developer Tools site . They require Java to be installed.

You need to generate/copy a AWS Private Key and Certificate. Both can be generated through AWS’s Account Management.

You can install many more packages into your image before bundling. You can do that any time later. I suggest testing your image first.

I’ll leave out the mandatory -K (private key) and -C (cert) parameters, which you have to add to all of the ec2 commands below.

$ sync
$ ec2-create-snapshot --region eu-west-1 -d "CentOS Image" vol-xxxx

You may want to set the region according to your requirements. The vol-xxxx ID is the ID of your volume as reported by the AWS console.

The output will give you the snapshot ID snap-xxxx, which you need to register it, once the snapshot is completed:

$ ec2-register -n "centos-5.5-32bit" -d "CentOS 5.5 32bit EBS - local storage support" \
                    --root-device-name /dev/sda1 -b /dev/sda=snap-xxxxx:10:true -b /dev/sdb=ephemeral0 \
                    -b /dev/sdc=ephemeral1 --region eu-west-1 -a i386

You might stumble upon the description: _”…with local storage support”. Let’s rip this command apart and look specifically at the -b parameters:

  • -b /dev/sdb=ephemeral0 and -b /dev/sdc=ephemeral1 ensure that EC2 instances of type m1.small or bigger can use the local storage (which isn’t present in t1.micro instances as they only support EBS volumes); without these parameters, those devices wouldn’t be detected nor accessible
  • -b /dev/sda=snap-xxxxx:10:true maps the snapshot of your image (which at boot time will be converted into a EBS volume) to sda, assigns 10GB of size (this must not differ from the size you chose when creating the EBS volume, which is currently mounted to /mnt), and tells the instance to remove the EBS volume after termination (true). This copies the behaviour of m1.small and bigger instances, where the local storage is lost after termination of the instance. If you want to keep the volume, you must set this to false instead and remove litter (unattached volumes) after termination yourself.Th ec2-register command gives you back an ami-xxxx ID, which is what you were after! That is the ID of your shiny new CentOS image, which you can directly launch instances from!But before you fire up the first instance, you need to find Amazon’s py-grub kernel, which will initiate the boot process and look for our kernel in the right place and using the right architecture and volume layout (in your region!):
    $ ec2-describe-images -o amazon --region eu-west-1 | grep grub

    You’ll get a list of (currently) four kernels, two of them with hd00 identifier in it. Pick the 32bit version of that kernel. Currently in Europe it would be kernel aki-47eec433, but these IDs will certainly change over time.

    Now, with this kernel ID, we can fire up an instance with our image:

    $ ec2-run-instances --region eu-west-1 -g yoursecuritygroup -k yourkeypairname -n 1 \
         --kernel aki-47eec433 -t m1.small ami-xxxxx

    It will take a while to start up, so don’t panic when you can’t ping or log into that new instance immediately after the AWS console shows running. Especially when you decided to use SELinux according to this manual, the re-labelling will take about 2-3 minutes to finish, and that happens long before networking and SSH are started.

    Your new AMI is compatible with t1.micro, m1.small, m1.large, and probably even bigger instance types. Regardless of the presence of local storage, the instances will create an EBS volume from the snapshot you created and boot from that volume.

    Using AWS Key Pairs

    When starting instances with ec2-run-instances you can specify a key pair using –key/-k your_keypair_name. This method is recommended, because it allows easy automation of new instances with credentials that are kept and maintained elsewhere.

    In order to achieve that, you need to configure your image to pick up the public SSH key of your named key pair. That’s actually quite easy to do, if you use the AWS web service, which provides all this metadata.

    First, make sure curl is available in your image:

    $ yum --installroot=/mnt -y install curl

    Then, make the startup process pick up the key. The approach (one out of many ways to solve this), which I describe here, works like this:

    * hook into /etc/rc.d/rc.local to enforce execution after everything else is started
    * check if authorized_keys2 exists for root; leave if it does
    * if not, grab the public key from the AWS webservice and create said file

    First, create a script called /mnt/etc/rc.d/getkeys.sh with the following content, executable by root:

    #!/bin/bash
    
    # leave if /root/.ssh/authorized_keys2 already exists
    test -f /root/.ssh/authorized_keys2 && exit 1
    
    # leave if curl hasn't been installed
    test -x /usr/bin/curl || exit 1
    
    # fetch public key from meta data service and add to authorized_keys2
    
    KEY=`curl http://169.254.169.254/1.0/meta-data/public-keys/0/openssh-key`
    IDENTIFIER=`curl -s http://169.254.169.254/2009-04-04/meta-data/public-keys/ | sed 's/0=//'`
    test -d /root/.ssh || mkdir -m 750 /root/.ssh
    echo "$KEY" > /root/.ssh/authorized_keys2
    chmod 640 /root/.ssh/authorized_keys2
    
    echo "*** Public key of keypair $IDENTIFIER has been added to /root/.ssh/authorized_keys2 ***"
    logger "*** Public key of keypair $IDENTIFIER has been added to /root/.ssh/authorized_keys2 ***"

    # now the named key used in e2-run-instances would be in place for root login

    $ exit 0

    Then make sure this script will be executed at the end of the startup process and .getkeys exists in the raw image:

    $ echo "/etc/rc.d/getkeys.sh" >> /mnt/etc/rc.d/rc.local

    That’s all it takes to use the named SSH key pair. You would then log in with ssh -i /path/to/your/private/key.pem root@instance_name.

    After the first start of the image, you can change /etc/ssh/sshd_config:

    PermitRootLogin yes

    should be replaced with

    PermitRootLogin without-password

    If you’re brave, you can do that now, before the first start (remember that your image is in /mnt, so you’d change /mnt/etc/ssh/sshd_config of course.
    From now on, you absolutely must specify the -k when using ec2-run-instances. Otherwise you’ve got a superb image running, but won’t have any way to access it.

    If you’re not intending to share your AMI with others, you might want to change the scripts to create and use a named user instead of root, of course. I just wanted to give you a straight-forward and convenient method to inherit AWS key pairs at boot time.

    You can use the web service listening at 169.254.169.254 from any of your instances to gather other interesting data, which you might need to query at run-time, such as local or public IP addresses, device mappings etc. Very handy for example if your newly started instances need to register with a central management instance to pick up further configuration details. Or any other automation tasks you can think of…

    64bit or x86_64 Images

    Basically you can use exactly the same steps explained here for a 64bit image. However, you must use a 64bit system to create it. Simply start up a 64bit instance and follow exactly the same steps. The only differences are:

    * you need to replace all occurrences of i386 in the listed ec2-* commands with x86_64
    * you need to pick a matching 64bit kernel above, where you run ec2-describe-images -o amazon --region eu-west-1 | grep grub

    That’s all there is to it (despite many manuals out there, which say it’s not possible or at least difficult).
    Keep in mind that only a few instance types support 64bit, namely t1.micro and m1.large but not m1.small! More details regarding the architectures of instances can be found here.

    Modifying the Image for Local (ephemeral) Storage and S3

    If you intend to not only create an EBS snapshot based AMI, but one you could serve from S3, you will need to change some tiny details in your configuration as well as in the procedure of bundling your image. I’ll go through them here.

    Keep in mind that instance-type storage, also known as ephemeral or local, is required for this time of AMI, because it’s effectively unpacked onto the local storage before booting from it. It is not supported by t1.micro instances, which don’t have local storage.

    Boot Loader Configuration

    You have to change a tiny little detail in here (took me ages to spot that):

    default 0
    timeout 1
    title CentOS5.5
         root (hd0)
         kernel /boot/vmlinuz-2.6.18-194.26.1.el5xen root=/dev/sda1
         initrd /boot/initrd-2.6.18-194.26.1.el5xen.img

    Did you spot the difference? Its from root (hd0,0) to root (hd0). Very important change, because Amazon’s bootloader has slightly different expectations for AMIs which will be copied onto ephemeral storage before boot as opposed to run off EBS volumes.

    By the way, if you want to be really cheeky and have a configuration which you can create EBS or S3-backed AMIs from, you’d use this menu.lst file:

    default 0
    fallback 1
    timeout 1
    title CentOS5.5-s3
         root (hd0)
         kernel /boot/vmlinuz-2.6.18-194.26.1.el5xen root=/dev/sda1
         initrd /boot/initrd-2.6.18-194.26.1.el5xen.img
    
    title CentOS5.5-ebs
         root (hd0,0)
         kernel /boot/vmlinuz-2.6.18-194.26.1.el5xen root=/dev/sda1
         initrd /boot/initrd-2.6.18-194.26.1.el5xen.img

    The trick here is to use the first one as default and the second one as fallback boot kernel. As EBS can’t find anything in (hd0) it will fall back to the second one, whereas instances created with instance-store, will boot straight from the first entry. Clever, eh?

    Swap

    If you start an t1.micro instance, you have to use EBS as the root device (will be automatically used, even if you didn’t manually create/attach a volume). We’ve covered swap there already.

    But all other instance types come with local (so-called ephemeral) storage. They are pre-partitioned:

    * your image goes always into /dev/sda1
    * /dev/sda2 is usually a free volume
    * /dev/sda3 is prepared to become a swap volume

    Consequently, we need to make sure that /dev/sda3 gets a swap signature at boot time, and it has to be activated. Otherwise your system will run without swap, which bears certain risks as described earlier.

    In order to detect the presence of /dev/sda3 and the fact that it is supposed to hold swap, we can make use of the AWS meta data service again. And finally we can wrap that up into a snipped, which you can simply append to /mnt/etc/rc.d/rc.local:

    if [ "`curl -s http://169.254.169.254/2009-04-04/meta-data/block-device-mapping/swap`" == "sda3" ]; then
       mkswap /dev/sda3
       swapon -a
    fi

    If you want to have portable AMI configurations (working for both EBS and instance storage), you can create the swap on sda3 for your EBS image as well. Just leave out sda2 when partitioning the volume, and change /mnt/etc/ftsab accordingly.

    SELinux Auto-Relabelling with S3 AMI images

    When packing your bundle with ec2-bundle-vol you will lose all files prefixed with a dot in the filesystem root, which means that the .autorelabel file will be lost and the filesystem not relabeled during startup, most probably rendering useless if SELinux is running in enforced mode.

    Instead of putting the file into /, we can put it into /etc and change one startup script (you don’t need to worry about future updates, because you should never again be required to relabel that image entirely):

    $ sed -i 's/\/\.autorelabel/\/etc\/.autorelabel/' /mnt/etc/rc.d/rc.sysinit
    $ touch /mnt/etc/.autorelabel

    Getting your AMI onto the S3

    After everything is prepared in /mnt, let’s bundle it. You’ll need your AWS User ID (without dashes), your S3 Access Token and Secret here.

    First wrap it up:

    $ ec2-bundle-vol -c your_cert.pem -k your_pk.pem -u your_aws_user_id -r i386 -p centos-55-32bit \
         --no-inherit --fstab /mnt/etc/fstab --volume /mnt

    The files will be created in /tmp. You should end up with one fairly large image file and numerous 10MB slices of it together with a manifest file.
    You can set a different path with -d /your/path.

    Now let’s upload it to S3. You don’t need to have a bucket set up; if you don’t, it will be created for you.

    $ ec2-upload-bundle -a your_access_token -s your_secret --location EU -b your_bucket \
          -m /tmp/centos-55-32bit.manifest.xml

    Time to grab a huge mug of coffee and drink it.
    Now let’s register the bundle. That works very similar to registering and EBS-based AMI:

    $ ec2-register -n centos-55-32bit-instance_storage -d "CentOS 5.5 32bit" \
         -a i386 --region eu-west-1 your_bucket/centos-55-32bit.manifest.xml

    The difference is here that you reference an S3 bucket location rather than a snapshot. You get back an AMI ID, which you can run like you would run the previously created AMIs from above, except that you need to choose a slightly different py-grub kernel. Get the list of available py-grub kernels again:

    $ ec2-describe-images -o amazon --region eu-west-1 | grep grub

    This time you don’t go for the hd00 identifiers, but for hd0, and of course for the right architecture. In Europe that would currently be: aki-4deec439

    Now you are all set to run it, with the ami-xxxx ID from two steps above:

    $ ec2-run-instances --region eu-west-1 -g your_security_group -k your_keypair_name \
         -n 1 --kernel aki-4deec439 -t m1.small ami-xxxxx

    It takes a moment longer than EBS-based instances, and if you decided to go for SELinux and do the relabelling, you’ll probably have to wait about 3-4 minutes altogether.

    >>>>>   I need  to update this article for CentOS 6, but it was just too long on my drafts <<<<<<

Using Amazon EC2 … basics about setting up instances.

Todo: Keypair part is not right.

1 Amazon Elastic Compute Cloud (EC2)

1.1 Setup the environment

Step 0: have a linux or a mac box with java installed and setup … JAVA_HOME and stuff. You’re using Windows!? … sorry about that.

Step 1: Create your account at http://aws.amazon.com

  • To use the EC2 service you’ll need a Credit Card.
  • You need a telephone number. They (automated system) are going to call you to check for your existence.

Step 2: Install/Download the command line tools for working with EC2.

You can install the Ubuntu package or download them from the amazon site.

  • Ubunutu
$ sudo apt-get install ec2-api-tools
$ sudo apt-get install ec2-ami-tools
  • Download the tools from:
  1. Amazon EC2 API Tools
  2. Amazon EC2 AMI Tools

Step 3: Create a X.509 Certificate and download the private key and certificate to ~/.ec2

  • Go to “http://aws.amazon.com/” -> “Account2 -> “Security Credentials”
  • Under “Access Credentials” you can find three types of access credentials used to authenticate your requests to AWS services.
    • Go to “X.509 Certificates” and generate a new key/certificate pair.

This certificate is used to secure SOAP protocol requests to AWS service APIs.

Step 4: Setup environment variables

  • You can extend your .bashrc with this lines
#
# check for the right values

export EC2_HOME=`pwd`
export PATH=$PATH:$EC2_HOME/bin
export EC2_PRIVATE_KEY=`ls ~/.ec2/pk-*.pem`
export EC2_CERT=`ls ~/.ec2/cert-*.pem`

Make sure your JAVA_HOME environment variable is set. Don’t forget to source your new .bashrc!

1.2 AWS Regions and Availability Zones

Step 5: Test the installation

  • List the available regions using ec2-describe-regions
$ ec2-describe-regions
REGION	eu-west-1	ec2.eu-west-1.amazonaws.com
REGION	us-east-1	ec2.us-east-1.amazonaws.com
REGION	us-west-1	ec2.us-west-1.amazonaws.com
REGION	ap-southeast-1	ec2.ap-southeast-1.amazonaws.com

This is very important. Amazon has at this point in time 4 different locations for data centers spread around the world:

$ ec2-describe-regions
REGION	eu-west-1	ec2.eu-west-1.amazonaws.com
REGION	us-east-1	ec2.us-east-1.amazonaws.com
REGION	us-west-1	ec2.us-west-1.amazonaws.com
REGION	ap-southeast-1	ec2.ap-southeast-1.amazonaws.com

Amazon uses us-east-1 as default, unless you explicitly say which Regions and Availability Zones you want to use. To do this most commands have a parameter –region.

You can also set the default region using an environment variable:

export EC2_URL=https://ec2.eu-west-1.amazon.com

this will set the default region to EU for every command that is region aware.

1.3 Create an EC2 instance

Before we can do this some things need to be all setup. To be able to access an EC2 instance you’ll need:

  1. A key-pair for the SSH authentication.
  2. A set of firewall rules that allow you to connect into the instance.

Step 1: Create a Keypair.

This is a Keypair to use for ssh authentication. (pay attention to the –region)

$ ec2-add-keypair gsg-keypair > id_rsa-gsg-keypair
$ cat id_rsa-gsg-keypair
KEYPAIR	gsg-keypair	ee:27:58:80:a0:fc:ea:6f:94:ff:f1:fa:ce:e1:3c:12:27:2c:3e:89
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEArGmqlDdBs3vBovt6XMYoHLPmXDDlKvVDjuskoW09iJOk5TbKacn1mJC8+WPi
QNou5qzMac26pvTSLC7x2fg3z4+r1ZYnrvm8vKyPYfcU4YlEPHI4P1fhGk/uFtd+2t9Drly/Ja0Y
T09fxgVCsOGm/5FZQHD+n/q6mJRx0zVcnVKKnD8C3NM0q6KRsUGpelu+Egr+YffC0mkDr2ArhgJO
cveYVlITRZGA8VzTVPunKUQjLN1Ue+68SlerCXMnZ9Rvc0gk3T5t8QobQkYzsxbvSu+99AlPAs7G
1CGpd3OOiydRHTpj8Vj4NDYmJHZE8DaUBejo3ORpOAAMr3KVikE8FQIDAQABAoIBAHqiIiGQeBwI
/79ErlFE5Q0dbNHQOYDnuSjz2jcz81us4NHkue6rJxRghvnAcRL93fRXnoH3Qjc01jV1IrOOWWw6
XdfrGMjde8Cb4NSmoWSWdUGlYJq1kDhd+BSpLOLTgVmJQLC0wSDQbWf1H+2eY2FO6jPq60GxXn3e
wGOhhgGjot6DReIbbnSJBTwpP36JCcDArA5MGMrpG3ZCtlUlFof3NdwCmbrU4U16QeMi4yyW2coV
...
f/jQXG6iSl0V2jNdw6ERyUKNOVeWVmvIV/pjU4LTnL0yL/N87eWnpX5osFLbw/PssPXOpR2kVwOS
QFAD21AApIqF3z+Dy+cvcFqaQWraiMdIAG+rJmTw7U8xrl9Yp2Oklno/V725yPjvEPw=
-----END RSA PRIVATE KEY-----

You will need to take the private key that it creates, save it to a file called i.e. id_rsa-gsg-keypair, then set the right permissions (600 or rw- — —).

You can check which Keypairs are available. In this case I created a set of keys using the --region eu-west-1 parameter and one without it.

$ ec2-describe-keypairs --region eu-west-1
KEYPAIR	My_Key	ef:3f:eb:ac:8d:b9:be:61:5a:fe:92:c9:ff:43:0a:02:61:77:d1:1a
$ ec2-describe-keypairs --region us-west-1
$ ec2-describe-keypairs --region us-east-1
KEYPAIR	gsg-keypair	cf:31:1b:33:d5:5a:a1:c2:85:cc:f9:6c:64:22:2a:be:70:cb:9f:c1
$ ec2-describe-keypairs
KEYPAIR	gsg-keypair	cf:31:1b:33:d5:5a:a1:c2:85:cc:f9:6c:64:22:2a:be:70:cb:9f:c1

Notice that without the region parameter or the EC2_URL environment variable, the commands always default to the us-east-1 zone.  That means that you’ll never see the information of the eu-west-1 elements, if you don’t use the –region parameter or set the environment variable.

Step 2: Create an EC2 instance.

Let’s list some of the available AMIs (Amazon Machine Images) in eu-west-1:

$ ec2-describe-images --region eu-west-1 -a  | grep -ie "rightscale-eu.*centos_5.4_"
IMAGE	ami-efe4cf9b	rightscale-eu/CentOS_5.4_i386_v4.4.10.manifest.xml	411009282317	available	public		i386	machine	aki-7e0d250a	ari-7d0d2509		instance-store
IMAGE	ami-ddf8d3a9	rightscale-eu/CentOS_5.4_i386_v5.1.1_Alpha.manifest.xml	411009282317	available	public		i386	machine	aki-7e0d250a	ari-7d0d2509		instance-store
IMAGE	ami-ebe4cf9f	rightscale-eu/CentOS_5.4_x64_v4.4.10.manifest.xml	411009282317	available	public		x86_64	machine	aki-780d250c	ari-7f0d250b		instance-store
IMAGE	ami-37f8d343	rightscale-eu/CentOS_5.4_x64_v5.1.1_Alpha.manifest.xml	411009282317	available	public		x86_64	machine	aki-780d250c	ari-7f0d250b		instance-store

You can only create EC2 instances in a region using images available in that region.

Now you need to know what kind of machine you want to instantiate. There is list of profiles under http://aws.amazon.com/ec2/instance-types/. What you need to know is the API name.

Now lets create 2 EC2 instances:

  • using the rightscale-eu/CentOS_5.4_i386_v4.4.10.manifest.xml image
  • on a High-CPU Medium Instance
  • in Europe.
$ ec2-run-instances --instance-count 2 --region eu-west-1 --key My_Key --instance-type c1.medium  ami-efe4cf9b
RESERVATION	r-faf3de8d	975547918662	default
INSTANCE	i-fa10128d	ami-efe4cf9b	pending	My_Key	0	c1.medium	2010-12-23T14:55:13+0000	eu-west-1a	aki-7e0d250a	ari-7d0d2509	monitoring-disabled    instance-store
INSTANCE	i-f810128f	ami-efe4cf9b	pending	My_Key	1	c1.medium	2010-12-23T14:55:13+0000	eu-west-1a	aki-7e0d250a	ari-7d0d2509	monitoring-disabled    instance-store

Check that your machines where instantiated: (it will take a couple of minutes at most …)

$ ec2-describe-instances --region eu-west-1
RESERVATION	r-faf3de8d	975547918662	default
INSTANCE	i-fa10128d	ami-efe4cf9b	ec2-79-125-53-72.eu-west-1.compute.amazonaws.com	ip-10-85-214-22.eu-west-1.compute.internal	running	My_Key   0	c1.medium	2010-12-23T14:55:13+0000	eu-west-1a	aki-7e0d250a	ari-7d0d2509	monitoring-disabled	79.125.53.72	10.48.214.22	instance-store
INSTANCE	i-f810128f	ami-efe4cf9b	ec2-46-51-132-163.eu-west-1.compute.amazonaws.com	ip-10-85-193-229.eu-west-1.compute.internal	running	My_Key   1	c1.medium	2010-12-23T14:55:13+0000	eu-west-1a	aki-7e0d250a	ari-7d0d2509	monitoring-disabled	46.51.132.163	10.48.193.229	instance-store

1.4 Accessing the EC2 Instance

Now we have a couple of running instances. So how do we get to ride them! For this we need access to the instances.

The AWS system sets all instances behind a firewall and defines a set of rules which are hard bounded to the instances at creation time. These rules a called Security Groups. So to access an instance you need to define a security group which allows at least ssh to from a trusted source into the instance.

What do you mean by “hard bounded”? You cannot add Security Groups to an already created instance. You can only change the rule definition on the current Security Group.

You can define different Security Groups. If you don’t configure this at creation time all instances are bounded to the default security group.

$ ec2-describe-group  --region eu-west-1
GROUP	9755xx91862	default	default group
PERMISSION	9755xx91862	default	ALLOWS	all			FROM	USER	9755xx91862	GRPNAME	default

We need to define a rule that allows ssh (port 22) from a trusted host.

$ ec2-authorize --source-subnet xxx.xxx.xxx.xxx/32 --port-range 22 --protocol tcp --region eu-west-1 default
GROUP		default
PERMISSION		default	ALLOWS	tcp	22	22	FROM	CIDR	xxx.xxx.xxx.xxx/32
$ ec2-describe-group  --region eu-west-1
GROUP	9755xx91862	default	default group
PERMISSION	9755xx91862	default	ALLOWS	all			FROM	USER	9755xx91862	GRPNAME	default
PERMISSION	9755xx91862	default	ALLOWS	tcp	22	22	FROM	CIDR	xxx.xxx.xxx.xxx/32

Now we can access our instantiated EC2 servers:

$ ec2-describe-instances --region eu-west-1
...
INSTANCE	i-f81x128f	ami-efexcf9b	ec2-46-xx-132-163.eu-west-1.compute.amazonaws.com	ip-10-xx-193-229.eu-west-1.compute.internal	running	My_Key	1	c1.medium	2010-12-23T14:55:13+0000	eu-west-1a	aki-7e0dxx0a	ari-7d0dxx09	monitoring-disabled	46.xx.132.163	10.xx.193.229	instance-store

$ ssh -i key_pair/My_Key.pem root@ec2-46-xx-132-163.eu-west-1.compute.amazonaws.com
The authenticity of host 'ec2-46-xx-132-163.eu-west-1.compute.amazonaws.com (46.xx.132.163)' can't be established.
RSA key fingerprint is 24:fc:75:8a:...:7e:f3:11:e4:e9.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'ec2-46-xx-132-163.eu-west-1.compute.amazonaws.com,46.xx.132.163' (RSA) to the list of known hosts.
     ___   _        __   __   ____            __
    / _ \ (_)___ _ / /  / /_ / __/____ ___ _ / /___
   / , _// // _ `// _ \/ __/_\ \ / __// _ `// // -_)
  /_/|_|/_/ \_, //_//_/\__//___/ \__/ \_,_//_/ \__/
           /___/

Welcome to a public Amazon EC2 image brought to you by RightScale!

********************************************************************
********************************************************************
***       Your EC2 Instance is now operational.                  ***
***       All of the configuration has completed.                ***
***       Please check /var/log/install for details.             ***
********************************************************************
********************************************************************
[root@ip-10-xx-193-229 ~]#

1.5 Shutting down the instances

After you use your EC2 instances you can terminate them and stop the billing process.

$ ec2-describe-instances --region eu-west-1
RESERVATION	r-faf3de8d	9755xx91862	default
INSTANCE	i-fa10128d	ami-efexcf9b	ec2-79-xx-53-72.eu-west-1.compute.amazonaws.com	ip-10-xx-214-22.eu-west-1.compute.internal	running	My_Key	0		c1.medium	2010-12-23T14:55:13+0000	eu-west-1a	aki-7e0d250a	ari-7d0d2509		monitoring-disabled	79.xx.53.72	10.xx.214.22			instance-store
INSTANCE	i-f810128f	ami-efexf9b	ec2-46-xx-132-163.eu-west-1.compute.amazonaws.com	ip-10-xx-193-229.eu-west-1.compute.internal	running	My_Key	1		c1.medium	2010-12-23T14:55:13+0000	eu-west-1a	aki-7e0d250a	ari-7d0d2509		monitoring-disabled	46.xx.132.163	10.xx.193.229			instance-store	

$ ec2-terminate-instances --region eu-west-1 i-f810128f i-fa10128d
INSTANCE	i-f810128f	running	shutting-down
INSTANCE	i-fa10128d	running	shutting-down

$ ec2-describe-instances --region eu-west-1
RESERVATION	r-faf3de8d	975547918662	default
INSTANCE	i-fa10128d	 ami-efexcf9b	terminated	 My_Key	0	c1.medium	2010-12-23T14:55:13+0000	eu-west-1a	aki-7e0d250a	ari-7d0d2509	monitoring-disabled	instance-store
INSTANCE	i-f810128f	 ami-efexcf9b	terminated	 My_Key	1	c1.medium	2010-12-23T14:55:13+0000	eu-west-1a	aki-7e0d250a	ari-7d0d2509	monitoring-disabled	instance-store

Terminated instances will be deleted and you won’t need to pay anymore. This is different from stopped instances, they are kept.

%d bloggers like this: