学生の頃、PHPでWEBサービスを作っていたときは、「デプロイ」といえばFTPでレンタルサーバにファイルをアップロードする作業でした。スクリプト言語などだとソースコードを差し替えるだけで済むので、「えいや!」でデプロイしてもなんとかなったりしました。
最近はJavaでアプリケーションを書くことが多いのですが、JavaとSpringで実装したアプリは起動するのに30秒から数分かかります。「えいや!」で更新してしまうと新しいプログラムが起動するまでの時間、サイトにアクセスできなくなってしまいます。
複数台のWEBサーバを順に更新する
ロードバランサなどを使って、複数のWEBサーバを立てた状態で、順に再起動をしていけば、常にアクセスできる状態を保つことができます。
AWSでサービスを運用する場合は、ロードバランサはAmazon ELBを使うことが多いと思います。ELBのヘルスチェックは一定の時間がかかりますので、「えいや!」でアプリケーションを更新すると、ヘルスチェックで停止判定がなされるまでの間にリクエストが流れてしまってエラーとなってしまいます。
ELBから外してデプロイ、起動したらELBに追加
そこで、デプロイする際は、まずELBから1台ずつインスタンスを外して、リクエストが流れないようにします。その後、アプリケーションを更新しアクセスできるようになってから、再びELBに追加していきます。台数が増えてくると手作業でやるには随分面倒な作業です。
これはシェルスクリプトなどにまとめることもできますが、ELBで管理しているサーバの台数が増えてくるとやはり面倒ですし、Amazon Autoscalingなどで対象のサーバが動的に変わっている場合などは更に面倒です。
EC2Deployを作りました。
そこで、この手順を自動化するスクリプトをまとめました。
katty0324/ec2deploy
名前が少し微妙ですが、以前に作っていたEC2Muninや、まだブログには書いてないですがこっそり作っていたEC2Nagiosと同じようなコンセプトなので、統一しました。
全てのEC2インスタンスを自動で監視するEC2Muninを作りました。
使い方
スクリプトは、GitHubからダウンロードしてください。サブモジュールとしてAWSのPHP SDKが含まれていますので、サブモジュールも取得するようにします。
git clone http://github.com/katty0324/ec2deploy.git
cd ec2deploy/
git submodule update --init
実行するときは、AWSのキーとシークレットと、ELBの名前、デプロイのシェルコマンドを渡します。
./ec2deploy -k 'AWS-ACCESS-KEY' -s 'AWS-ACCESS-SECRET' -n 'ELB-NAME' -c 'DEPLOY-COMMAND'
ELBの配下のインスタンスの一覧を取得し、順にELBから外し、デプロイコマンドを実行し、ELBに戻します。
2台以上のサーバが同時にアクセス不能にならないように、デプロイ前後に一定時間のスリープを入れたり、全てのサーバのヘルスチェックが正常な状態になっていることを確認しています。
使用するAWSのキーとシークレットは、IAMのパーミッション管理などでいくつかの権限を有効にしておく必要があります。
ec2:DescribeInstances elasticloadbalancing:DeregisterInstancesFromLoadBalancer elasticloadbalancing:DescribeInstanceHealth elasticloadbalancing:RegisterInstancesWithLoadBalancer
EC2Deployを試しで使う時に細かい権限の設定が面倒な場合は、EC2のFull Access権限を付けてしまえば動きます。
デプロイコマンドの書き方
デプロイコマンドには、EC2インスタンスごとの変数が使えるようになっています。
たとえば、次のようにSSH経由で、WEBサーバにアプリケーションを送信することでデプロイするとします。
scp /var/lib/war/application.war ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com:/var/lib/war/application.war
ここで、Public DNSの値は、EC2インスタンスごとに異なるので、変数を用いることができます。
./ec2deploy -k 'xxxxxxxxxxxxxxxx' -s 'yyyyyyyyyyyyyyyy' -n 'web-load-balancer' -c 'scp /var/lib/war/application.war ${dnsName}:/var/lib/war/application.war'
これ以外にも、以下のようなインスタンスごとの変数を利用できるようにしておきました。EC2インスタンスのタグの値も取得できるようになっていますので、何かタグ付けした情報を用いてデプロイ処理を変えることもできます。
instanceId imageId instanceState privateDnsName dnsName keyName instanceType launchTime availabilityZone kernelId subnetId vpcId privateIpAddress ipAddress tag.XXX (XXX is EC2 instance tag)
使えたら、使ってみてください。他にもっとスマートなデプロイの方法があったら教えてほしいです。