AmazonLinuxでcloud-initを使う

  • このエントリーをはてなブックマークに追加

cloud-initについて

AmazonLinuxではcloud-initという機能があり、Launch WizardでUser dataに処理を記述することで、Launch直後に特定の処理を自動実行させることができます。

がんばれば起動直後にミドルウェアを一揃いセットアップし、LAMPとしての機能を備えた状態でLaunchさせることなどが可能なようです。

実行させる処理は、以下の様な独自の記法で記述します。

起動時にyum updateする例

repo_upgrade: all

シェルコマンドを実行する例

runcmd:
 - [ ls, -l, / ]
 - [ sh, -xc, "echo $(date) ': hello world!'" ]
 - [ sh, -c, echo "=========hello world'=========" ]
 - ls -l /root
 - [ wget, "http://slashdot.org", -O, /tmp/index.html ]

http://bazaar.launchpad.net/~cloud-init-dev/cloud-init/trunk/view/head:/doc/examples/cloud-config-run-cmds.txtから引用

ちょっと癖がある書き方でとっつきにくいのですが、実はシェルスクリプトも実行することができます。

今回は、cloud-initを使って、AmazonLinux立ち上げ時に定型的に実行する処理を自動化してみます。

 

AmazonLinux立ち上げ時の定型処理

AmazonLinuxを起動するときは、だいたい以下の処理を行うことが多いです。

  • yum update
  • jqをインストール(aws-cliの出力がJSONなのでパースするのに必要です)
  • Nameタグに入っている名前でhostnameを設定
  • タイムゾーンを修正する
  • ルートディスクにresize2fsコマンドを実行する

これらをシェルスクリプトに起こしたものが以下です。 スクリプト内に日本語を書くと実行に失敗するようなのでコメントは入れていません。

#!/bin/bash -ex

RETRYLIMIT=3
RETRYINTERVAL=2

\cp -f /usr/share/zoneinfo/Japan /etc/localtime

sed -i -e "s/ZONE=.*$/ZONE=\"Asia\/Tokyo\"/" /etc/sysconfig/clock
sed -i -e "s/UTC=.*$/UTC=false/" /etc/sysconfig/clock
echo 'ARC=false' >> /etc/sysconfig/clock
service crond restart

yum install jq -y
yum update -y

CMD='curl -s --connect-timeout 5 --max-time 10 http://169.254.169.254/latest/meta-data/placement/availability-zone/'
for i in $(seq 1 ${RETRYLIMIT})
do
RET=$(${CMD} 2>&1)
RESULT=$?
[ ${RESULT} -eq 0 ] && break
[ $i -ge ${RETRYLIMIT} ] && logger "get region failed." && exit 1
sleep ${RETRYINTERVAL}
done
REGION=$(echo ${RET} | sed -e "s/.$//")

CMD='curl -s --connect-timeout 5 --max-time 10 http://169.254.169.254/latest/meta-data/instance-id'
for i in $(seq 1 ${RETRYLIMIT})
do
RET=$(${CMD} 2>&1)
RESULT=$?
[ ${RESULT} -eq 0 ] && break
[ $i -ge ${RETRYLIMIT} ] && logger "get instance-id failed." && exit 1
sleep ${RETRYINTERVAL}
done
INSTANCEID=${RET}

CMD="aws ec2 describe-tags --region ${REGION} --filters "Name=resource-id,Values=${INSTANCEID}""
for i in $(seq 1 ${RETRYLIMIT})
do
RET=$(${CMD} 2>&1)
RESULT=$?
[ ${RESULT} -eq 0 ] && break
[ $i -ge ${RETRYLIMIT} ] && logger "get Name failed" && exit 1
sleep ${RETRYINTERVAL}
done
NAME=$(echo ${RET} | jq '.Tags[] | select(.Key == "Name") | .Value' | tr -d '"' | tr -d "\n")

hostname ${NAME}
sed -i -e "1s/$/ ${NAME}/" /etc/hosts
sed -i -e "s/HOSTNAME=.*$/HOSTNAME=${NAME}/" /etc/sysconfig/network
service rsyslog restart

resize2fs /dev/xvda1

ws-cli実行時にはいちいちリトライ処理を入れています。
これは今までの経験上、実行に失敗して何度も痛い目を見てきたためです。

ぐぐってもメタデータ取得やaws-cli実行に失敗することに言及している例は見たことがないのですが、結構な頻度で失敗すると感じています。どちらもHTTPなので、ネットワークの状況によっては失敗する前提で考えるのが当たり前なのかもしれませんが。。

注意点として、yumを使うためにEIPを割り当てるかLaunch Wizardで「Automatically assign a public IP address to your instances」にチェックを入れてグローバル IPを持たせてインターネットに出られるようにする必要があることと、IAMのポリシーで「ec2:DescribeTags」権限を付与しておく必要があります。

勢いで書いたものですが試しに実行したらとりあえず動いているようです。。