elastic beanstalkをimmutableに運用する

AWSのelastic beanstalkはURLスワップ機能やauto scalingによるインスタンス増減機能があるため、immutable infrastructureやblue-green deploymentの文脈で語られることが多いかと思いますが、実際に運用しようとすると考えなければいけないことがたくさんあります。

ちょうど今elastic beanstalkの運用を行なっていて安定してきたので、elastic beanstalkでimmutable infrastructureに運用するためにはどうすればいいのか?というのをまとめてみます。

目的

  • elastic beanstalkの環境構築とデプロイを自動化し、immutableに運用できる環境を作る

デプロイ

デプロイする方法はいくつかありますが、今回はebコマンドを利用します。

ebコマンドを使う理由は以下の通り。

  • 操作が簡単
  • gitのリポジトリが簡単にデプロイできる
  • optionsettingsebextensionsを利用すれば設定をコードとして管理できる

通常は対話式で入力していくことになるのですが、以下のような適当なシェススクリプトを作成するだけでコマンドによる自動化も簡単です。

GIT_BRANCH='master'
ENV_NAME=`date +"%Y%m%d-%H%M%S"`

# parse opts
while getopts b:e:r: OPT
do
  case $OPT in
    b) GIT_BRANCH=$OPTARG
      ;;
    e) ENV_NAME=$OPTARG
      ;;
  esac
done

# checkout
git checkout ${GIT_BRANCH}
git pull origin ${GIT_BRANCH}

# create new eb environment
expect -c "
  spawn eb branch

  expect \"Enter an AWS Elastic Beanstalk environment name (auto-generated value is *):\" {
    send \"${ENV_NAME}\n\"
  }

  expect \"Do you want to copy the settings from environment * for the new branch?\" {
    send \"y\n\"
  }

  expect \"Would you like to deploy the latest Git commit to your environment?\" {
    send \"y\n\"
  }
"

# deploy to aws
eb start && git aws.push --environment ${ENV_NAME}

# clean up
git reset --hard HEAD
git clean -f

以下、ebコマンドでのデプロイを前提に進めます。

URL

ebコマンドでenvironmentを作成すると、デフォルトで<environment名>-<ハッシュ>.elasticbeanstalk.comというURLがそれぞれのenvironmentに付与されます。

独自ドメインで運用する場合はこのURLに向けてCNAMEを張って、environmentのswapで切り替えていくことになるので、公開中のenvironmentであることがわかる名前がいいと思います。ちなみに自分の環境では本番用URLが割り当てられるenvironment名はproductionとしてあります。 (ちなみにルートドメインの場合はCNAMEを張れません。Route53の場合も同様で、AレコードのエイリアスにELBを設定するためURLは関係なくなります。)

また、公開中のURLに紐付かないenvironmentは継続的デプロイで自動作成してステージング相当の環境として利用すると思うので、

  • 機械的に採番できる
  • 推測しにくい
  • URLスワップしても意味がわかる

environment名が望ましいと思います。自分の環境では上のデプロイスクリプトにあるようにyyyymmdd-hhmissというenvironment名をデフォルトにしてます。

プロビジョニング

基本はoptionsettingsやebextensionsを利用してenvironmentやインスタンスの作成時に設定などを行う運用になると思います。

またこれらのファイルはeb initによって.gitignoreに追加されているのですが、gitの管理下に置くことで設定やリソースなどをコードとして管理することができます。

しかし、もともと遅いelastic beanstalkのセットアップに加えて追加のパッケージのインストールなどを都度行っているとさらに時間がかかってしまうため、Auto Scalingで頻繁にインスタンスが増加する場合には設定済みのAMI用意する必要が出てくると思います。

ちなみに自分の環境ではchef+berkshelf+packerでAMI作成を自動化していますが、少し前にelastic beanstalkがdockerコンテナに対応したので、dockerを使うのもアリだと思います。

DB

当たり前の話ですが、immutableに運用するためにはDBは外出ししなければいけません。

RDSを利用する場合、optionsettingsで紐付たりebextensionsから作成を行ってしまうとenvironment削除時にRDSのインスタンスも削除されてしまうので、作成は別に行う必要があります。

通知/監視

SNSを利用する場合は、optionsettingsのaws:elasticbeanstalk:sns:topicsで指定したトピックやebextensionsのType: AWS::SNS::Topicで作成したトピックは環境削除時に削除されてしまいます。 またSNSは、メール通知するアドレスの確認作業がトピック作成を行うたびに必要となってしまいます(メール以外の通知方法に関しては未調査)。

なのでメールアドレスの確認作業が自動化できない場合は、SNSのトピック作成をあらかじめ行うようにして、optionsettingsのaws:elasticbeanstalk:sns:topicsには指定しないようにしましょう。

CloudWatchを利用する場合は、(environment削除時にひもづいているアラームも一緒に削除されてしまうため)ebextensionsのType: AWS::CloudWatch::Alarmでenvironment毎に作成する必要があります。 アラームが削除されると計測した値も削除されてしまうので、継続的に計測したい場合は別途監視サーバが必要です。

ちなみにType: AWS::CloudWatch::AlarmAlarmActionsInsufficientDataActionsに指定したSNSトピックはenvironment削除時に削除されないので、CloudWatchのアラームはSNSを通して通知可能です。

CloudWatchのAlarm作成は以下が参考になります。

ログ

.elasticbeanstalk/optionsettingsにあるLogPublicationControlをオンにすることでアプリケーションやWebサーバのログを自動的にS3にアップロードしてくれます。

しかしS3に保存されるログファイルのパスは${environment-id}/${ホスト名}/${ログファイルのパス}.log-${YYYYMMDD}.gzとなっていてenvironmentやホストをたくさん作って捨てる運用方法だと参照がしにくいので、自分の環境ではfluentd(tdagent)を利用しています。

fluentdを利用する場合はauto scalingによってインスタンスが突然死する場合に備えてflush_at_shutdownとか設定しておくといい感じです。

まとめ

上に挙げた通りenvironmentの削除時に消えてしまうリソースがあるため、そこだけ気をつけていれば基本的には問題ないと思います。 みんなでimmutable infrastructureなelastic beanstalkを満喫しましょう!