Disclaimer: these code segments have not been really “tested” verbatim. I assume anyone who is successfully bundling EC2 images (that run) will know enough about copying shell scripts off blogs to test for typos, etc! Oh, and, sorry for lack of indentation on these… I’m just not taking the time 🙂
As I’ve been searching for a way of using ec2 in a production environment. To keep it as simple as possible, but also eliminate the need for unnecessary (and extremely tedious) image building both during and after the development process (development of the AMI, not the service). This is what I’ve come up with.
Step 1: our repository
Create a subversion repository which is web accessible and password protected (of course) like so:
- ami/trunk/init.sh
- ami/trunk/files/
- ami/tags/bootstrap.sh
- ami/tags/
ami/tags/bootstrap.sh would read:
#!/bin/bash
BootLocation=”ami/trunk”
BootHost=”svnhost.com”
BootUser=”userame”
BootPass=”password”
BootProtocol=”http”## Prepare the bootstrap directory
echo -en “\tPreparing… ”
if [ -d /mnt/ami ]
then
rm -rf /mnt/ami
fi
mkdir -p /mnt/ami/
if [ $? -ne 0 ]; then exit $?; else echo “OK”; fi
## populating the bootstrap
echo -en “\tPopulating… ”
svn –force \
–username $BootUser \
–password $BootPass \
export \
$BootProtocol://$BootHost/$BootLocation/ \
/mnt/ami/ 1>/dev/null 2>/dev/null
if [ $? -ne 0 ]; then exit $?; else echo “OK”; fi
chmod a+x /mnt/ami/init.sh
## hand off
echo -e “\tHanding off to init script…”
/mnt/ami/init.sh
exit $?
ami/trunk/init.sh would read something like:
#!/bin/bash
## Filesystem Additions/Changes
echo -en “\t\tSynchronizing System Files… ”
cd /mnt/ami/files/
for i in $(find -type d)
do
mkdir -p “/$i”
done
echo -en “d”
for i in $(find -type f)
do
cp -f “$i” “/$i”
done
echo -en “f”
echo ” OK”
## Any Commands Go Here
## All Done!
exit 0
Step 2: configure your AMI
/etc/init.d/servicename should look something like:
#! /bin/sh
#
# chkconfig: – 85 15
# description: Ec2 Bootstrapping Process
#
RETVAL=0
case “$1” in
start)
/usr/bin/wget \
-o /dev/null -O /mnt/bootstrap.sh \
http://user:pass@svnhost/ami/tags/bootstrap.sh
/bin/bash /mnt/bootstrap.sh
RETVAL=$?
;;
stop)
exit 0
;;
restart)
$0 start
RETVAL=$?
;;
*)
echo “Usage: $0 {start|stop|restart}”
exit 1
;;
esac
exit $RETVAL
And now when the AMI boots itself up we hit 85 during runlevel 3 bootup (well after network initialization), servicename starts, and the bootstrapping begins. We’re then able, with our shell scripts, to make a great deal of changes to the system after the fact. These changes might be bugfixes, or they might be setup processes to reconstitute a database and download the latest code from a source control repository located elsewhere… They might be registration via a DNS API… anything at all.
The point is that some flexibility is needed, and this is one way to build that in!
I work on the SmartFrog configuration framework (Java based); our plan for EC2 is to bring up that daemon and use it for late binding configuration.
One of the best techniques we use for large clusters is not to have any preconfiguration in the host, but instead have it announce its presence to the cluster; the rest of the cluster decides what it will be. This lets you decouple 'provisioning' from configuring. Bring up 20 nodes; decide which will be app servers and which will be workers based on how many come up, and in what order.
The alternative to smartfrog that you may want to look at is 'puppet' – http://reductivelabs.com/projects/puppet/ – its a ruby based configuration languge, and again, you can bring up the daemon on startup for late configuration.
Intriguing. The production that I'm planning on puttng this is is (at the moment) only slated to need one AMI, so mainly I was interested in being able to create, insert, and subsequently improve scripts for Unpacking the site, populating the database from backup, and installing regular backup jobs.
I'll have to take a look at puppet. I expect that it's something I could easily bootstrap in (now) without changing my AMI at all. And it sounds like it will be a godsend to know when the time comes around to use more than one ami!
I could also probably replace puppet later with something else using this simple loader mechanism.
My own approach to late binding is simpler than any of these.
I write a tiny startup script that retrieves the user data for the instance and runs it as a shell script. This provides ultimate flexibility and gets me out of the AMI building business quickly.
Any fancy cluster binding can be built on top of this. Typically the user data has a small script that downloads the full scale configuration code and scripts which then do the late binding as necessary.