Amimoto を利用した Auto Scaling

AmimotoAMIではインスタンス単発で使用することを前提にしていますが、作成方法によっては AutoScaling に対応させることも可能です。
この記事では、AmimotoAMIを利用した AutoScaling 構成について解説します。

そもそも AutoScaling とは?

設定された情報に基づいて、EC2インスタンスの台数を増減させることができるシステムです。
EC2インスタンスは ELB (Elastic Load Balancing) の配下に置かれます。
特徴としては

  • 予測できないアクセス増減などでの負荷の増減にあわせて自動的にインスタンスを増減することができるためコストの効率化が可能
  • 配下のインスタンスのうちのどれかが突然死しても、設定された最小台数にあわせてインスタンス数を調節するため、自動復旧が可能

といったメリットがあります。
特に2つ目の特徴を活かせば1台構成の AutoScaling だとしても、EC2インスタンスが壊れた時に自動で復旧してくれるため便利です。
また、台数を自動的に調節してくれるので、あらかじめ入札額を下回ると廃棄されるスポットインスタンスを活用することでコストダウンを図ることもできます。

AutoScaling は便利な半面、すべてのインスタンスはいつ廃棄されるかわかりません。
従来のように一度設定したら、秘伝のタレを足しながらサーバをメンテナンスしていくという考え方ではうまく運用できないでしょう。
すべてのサーバの設定はコードで管理し、自動的に同じ状態になるようにする必要があります。

また、インスタンスが廃棄されても良いように可変する情報( WordPress でいうと MySQL に保存された記事データや、wp-content/uploads/ に保存されるメディアデータ )についてはインスタンス内に置かず、別のところに保存する必要があります。
WordPress で使用するときは、以下の点を注意すべきでしょう。

  • コア、プラグイン、テーマなどのソースは git などで管理し、インスタンス上のソースを直接修正しない
  • MySQL は RDS など Web サーバで使用している EC2 インスタンスとは別で管理し、EC2 インスタンスが廃棄されても良いようにする
  • WordPress ダッシュボードからアップロードされるメディアファイルについても同様に S3 などに配置するようにする
  • Nginx や php-fpm のログについても EC2 から CloudWatch Logs、fluentd などを使って外部で参照できるようにしておく

AutoScaling の設定は、従来は aws-cli などのコマンドラインツールでしかできませんでしたが、現在はマネージメントコンソールから設定することが可能になっています。
設定方法については、下記のURLが参考になるでしょう。
【AutoScalingをManagementConsoleから設定してみる】

ここでは、AmimotoAMIを元に設定する際に注意すべき点を解説していきます。

すべてのインスタンスで起動時に設定をあわせる

EC2 Launch Step 3

すべてのインスタンスで起動時に設定を合わせるには cloud-init が便利です。
これは、EC2 をローンチするときの「Step3: インスタンスの詳細の設定」で指定することができます。
下の方に「高度な詳細」-「ユーザーデータ」という欄がありますが、こちらにシェルスクリプトを記入するとインスタンス起動時に一度だけそのシェルスクリプトが実行されます。
例えば以下の様なシェルスクリプトを書いておけば s3 バケットから initialize.sh というファイルを取得してきて、それを起動時に実行することができます。

#!/bin/sh
region='ap-northeast-1'
s3_bucket="YOUR_S3_BUCKET_HERE"
aws="/usr/bin/aws --region=${region}"

cd /opt/local
${aws} s3 cp s3://${s3_bucket}/initialize.sh /opt/local/
/bin/chmod +x /opt/local/initialize.sh
/opt/local/initialize.sh

インスタンスから、aws-cli を使用して S3 にリクエストを投げられるように EC2 ローンチ時に IAM ロールを適切に設定しておきましょう。
よくわからない場合は、github 等に初期化スクリプトを置いておいて、そちらを実行するようにしても良いかもしれません。

また「ユーザーデータ」にはシェルスクリプトだけでなく、cloud-init ユーザーディレクティブを記入することも可能です。
こちらについては http://cloudinit.readthedocs.org/en/latest/index.html を参考にしてください。

例えば、初期化スクリプトで行うこととしては以下のことが考えられます。

  • WordPress ディレクトリ内のソースを git リポジトリから取得してきて最新にする
  • wp-config.php に RDS への接続情報を記入する
  • Nginx の設定ファイルをあわせる
  • yum update など

AmimotoAMIでは、起動時に /opt/local/provision というスクリプトを実行しています。これは chef-solo を用いて、サーバの設定を合わせるためのスクリプトです。
このスクリプト内で /opt/local/amimoto.json というファイルを参照しています。
MySQL は RDS を使用するのであれば、MySQL プロセスを起動する必要はないので、以下のような amimoto.json を配置してあげると MySQL が起動しないAmimotoAMIができあがります。

{
  "mysql"    : { "enabled": false },
  "run_list" : [ "recipe[amimoto]" ]
}

稼働中の AutoScaling グループ内のインスタンスに修正された WordPress ソースを配布する

AutoScaling グループ内のインスタンス一覧を取得できれば Ansible などの構成管理ツールを用いて、ソースの配布が可能になるでしょう。
以下の様なスクリプトで、特定の AutoScaling グループ内のインスタンスのプライベートアドレス一覧を取得して Ansible 用の hosts ファイルを作成することが可能です。
このシェルスクリプトでは aws-cli の他に jq コマンドも使用していますので、そちらもインストールしておく必要があります。

#!/bin/sh
ASGROUP='AUTOSCALING_GROUP_NAME_HERE'
ANSIBLE_WRK="${HOME}/ansible"
HOSTS_FILE="${ANSIBLE_WRK}/hosts"

echo "[${ASGROUP}]" > ${HOSTS_FILE}
INSTANCES=`aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names ${ASGROUP} | jq -r ".AutoScalingGroups[].Instances[].InstanceId"`
for INSTANCE in ${INSTANCES}; do
  PRIVATE_IP=`aws ec2 describe-instances --instance-ids ${INSTANCE} | jq -r ".Reservations[].Instances[].PrivateIpAddress"`
  echo ${PRIVATE_IP} >> ${HOSTS_FILE}
done

たとえば、git リポジトリの hook/post-update を以下のようにしておくと、master ブランチに push するだけで ansible が動作して全インスタンスにソースを配布することも可能になります。

#!/bin/sh
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-update".

BRANCH=$(git rev-parse --symbolic --abbrev-ref $1)

if [ "${BRANCH}" = "master" ]; then
  ASGROUP='AUTOSCALING_GROUP_NAME_HERE'
  ANSIBLE_WRK="${HOME}/ansible"
  HOSTS_FILE="${ANSIBLE_WRK}/hosts"

  echo "[${ASGROUP}]" > ${HOSTS_FILE}
  INSTANCES=`aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names ${ASGROUP} | jq -r ".AutoScalingGroups[].Instances[].InstanceId"`
  for INSTANCE in ${INSTANCES}; do
    PRIVATE_IP=`aws ec2 describe-instances --instance-ids ${INSTANCE} | jq -r ".Reservations[].Instances[].PrivateIpAddress"`
    echo ${PRIVATE_IP} >> ${HOSTS_FILE}
  done

  ansible-playbook -i ${HOSTS_FILE} ${ANSIBLE_WRK}/source-sync.yml
fi

以上駆け足でしたが、AmimotoAMIを利用した AutoScaling の設定方法でした。

なお、細かい設定についてはかなり省いています。AmimotoAMIを利用した AutoScaling について興味のある方はお気軽にご相談ください。
設定作業等承ります。