先日、AWS Elastic Beanstalk Supports Multi-Container Docker Environmentsで発表がございましたが、 1つのEC2インスタンスに複数のDockerコンテナを配置することが出来るようになりました。 こちらは、Amazon EC2 Container Serviceのテクノロジーがベースになっています。 今回は、このMulti-Container Dockerを使った環境の構築と、 ついでにebというElastic Beanstalk用のコマンドラインツールを使ったアプリケーションのデプロイを試してみたいと思います。 なお、本投稿は私が個人的に行ったものであり、所属する企業や団体における公式見解ではございません。 ■ Multi-container Dockerの設定で構築 Elastic Beanstalkの用語として以下のようなものが挙げられます。 ・Application: Environmentの集合体。フォルダのようなイメージ。(動くコードそのものではない) ・Version: デプロイするコード。S3をリポジトリにしてバージョン管理。 ・Environment: Versionがデプロイされた環境。複数Environmentにそれぞれ異なるVersionをデプロイすることも出来る。 なんとなくApplicationというと、動くコードそのものをイメージしてしまいそうになるのですが、 そうではないので念の為ご注意いただければと思いますmm で、実際の構築のやり方は Multicontainer Docker Environments with the AWS Management Console に書いてありますが、Multi-containerは上でも少し触れたように、ECS(EC2 Container Service)が元になっているので、そのAPIを叩けるようにしたり、 ログの格納用に使用するS3バケットへのputの設定をaws-elasticbeanstalk-ec2-role(EBで環境構築する際にデフォルトで作成されるIAM Role)に対して行います。 ↓のワーニングはそのことを言っています。 具体的にはIAMの画面でPolicyを作ってRoleの画面でAttache Policyの設定をします。 サンプルのアプリケーションで構築が完了すると↓のような画面になります。 払いだされたURLにアクセスすると↓こんな感じ。 試しに、プロビジョニングされたEC2インスタンスにSSHで入ってみると、 ↓のようにdockerやecsが動いてるような感じしますね。 dockr psを叩いてみると、以下のように、プロキシ用のNginx、PHPのアプリ、ESCのエージェントが動いているのが分かります。
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a5139cf480ab nginx:1 "nginx -g 'daemon of 3 hours ago Up 3 hours 443/tcp, 0.0.0.0:80->80/tcp ecs-awseb-multiDockerTest-env-upq3tpm3fh-2-nginx-proxy-b0d1f6d898ddea93fa01 c4acd0810b38 php:5-fpm "php-fpm" 3 hours ago Up 3 hours 9000/tcp ecs-awseb-multiDockerTest-env-upq3tpm3fh-2-php-app-e684be8ba8d7d5bbd001 b9ab2fda98d4 amazon/amazon-ecs-agent:latest "/agent" 3 hours ago Up 3 hours 127.0.0.1:51678->51678/tcp ecs-agent
上記がそれぞれどういう役割なのか?という構成に関しては、以前一緒のチームでソリューションアーキテクトとして働いてて、 今はUSでサービスチームの一員として活躍している安川さん(@thekentiest)の、 昨年のAWS Black Belt Tech Webinarの記事が分かりやすくてよく紹介させていただいているのですが、 ↓のような感じになっています。 [slideshare id=q9S6Bp0agJG2kY?startSlide=13&w=425&h=355&fb=0&mw=0&mh=0&style=border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&sc=no]
デプロイされたものは/var/appの中に格納されています。$ ls /var/app/current Dockerrun.aws.json cron.yaml php-app proxy $ cd php-app/
index.phpを見てみると
<body id="sample"> <div class="textColumn"> <h1>Congratulations!</h1> <p>Your Docker Container is now running in Elastic Beanstalk on your own dedicated environment in the AWS Cloud.</p> </div> <div class="linksColumn"> <h2>Video Tutorials</h2> <ul>
のようになっていて、悪ノリして↓こんなことすると、 hogehogeうぇーい的な感じになりました。 で、Multi-containerって事でECSにはTaskというメモリとかCPUの条件を決めたりする概念というか、 括りがあるのですが、言葉で説明するとアレですが絵にすると↓こんな感じです。 (Multicontainer Docker Environments に出てくる挿絵です) こちらはまた別の機会に深堀りしていきたいと思います。 ■ zipファイルを使ったデプロイ 上記のように直接SSHでサーバーに入ってファイルを書き直すというのは現実的ではないと思われますので、 ローカルで編集したものをElasticBeanstalkのマネージメントコンソールからデプロイしてみたいと思います。 現状動いているサンプルをダウンロードしてきたかったのですが、Elastic Beanstalkのコンソール上の リンクを押してもリンクでドキュメントのページに飛ばされるだけで、2015年4月18日現在、Multi-containerの サンプルのzipファイルが見当たらなかったので、、 実行環境上でtar.gzにして、、
$ sudo tar cvf current.tar.gz current/* current/Dockerrun.aws.json current/cron.yaml current/php-app/ current/php-app/static.html current/php-app/index.php current/php-app/scheduled.php current/proxy/ current/proxy/conf.d/ current/proxy/conf.d/default.conf
コレをS3に上げてから、ローカルにダウンロードしてもってきます。(なんとも古典的なw)
$ aws s3 cp current.tar.gz s3://justabucket2015 upload: ./current.tar.gz to s3://justabucket2015/current.tar.gz
ローカルでindex.phpファイルを一部書き換えて、
113 <li><a href="http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/index.html?concepts.html">AWS Elastic Beanstalk concepts</a></li> 114 <li>hogehoge</li> 115 <li>local_hogehoge</li> 116 </ul>
zipファイルに圧縮して、
$ zip -r deploy.zip . adding: cron.yaml (deflated 21%) adding: Dockerrun.aws.json (deflated 75%) adding: php-app/ (stored 0%) adding: php-app/index.php (deflated 60%) adding: php-app/scheduled.php (deflated 31%) adding: php-app/static.html (deflated 2%) adding: proxy/ (stored 0%) adding: proxy/conf.d/ (stored 0%) adding: proxy/conf.d/default.conf (deflated 45%)
マネージメントコンソールからデプロイすれば、 デプロイ出来ました。 但し、コレだと毎回zipファイル作らなければならないので大変面倒です…。 ■ ebコマンドを使ったデプロイ Elastic Beanstalkにはebというコマンドラインツールがあって、コレを使うと便利にデプロイ出来ます。 今までは2.6系が主流でgit aws.push〜とかっていうご案内をよくしていたのですが、 最近では↓に書かれているように3系に移行してきていて、少しコマンドの体系が変わってきてきますのでご注意ください http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-reference-eb.html 3系のebコマンドのインストールはpipで行います。 普通にやると $ sudo pip install awsebcli だけでよく、 サクッと↓のようにいくはずですが、
$ eb --version EB CLI 3.2.2 (Python 2.7.8)
私の場合はpipが古かったのかupgradeとかしても上手くいかず、↓この辺のフォーラムにお世話になりました。 https://forums.aws.amazon.com/thread.jspa?threadID=164348&tstart=25 https://forums.aws.amazon.com/thread.jspa?messageID=580745 で、eb initを叩くと、既に構築されたElastic BeanstalkのApplicationが出てきて、 ↓のように選択できます。
$ eb init Select an application to use 1) mulcon 2) multi docker test 3) docker 4) [ Create new Application ] (default is 4): 2
続いて、先ほどのdeploy.zipは削除してgit init .しておきます。 そうすると、.elasticbeanstalkと.gitと.gitignoreが出来ているかなと思います。
$ ls -la total 24 drwxr-xr-x 9 eshinoha 1896053708 306 4 18 12:00 . drwx------+ 585 eshinoha 1896053708 19890 4 18 11:17 .. drwxr-xr-x 4 eshinoha 1896053708 136 4 18 12:00 .elasticbeanstalk drwxr-xr-x 10 eshinoha 1896053708 340 4 18 11:21 .git -rw-r--r-- 1 eshinoha 1896053708 108 4 18 12:00 .gitignore -rw-r--r--@ 1 eshinoha 1896053708 1457 3 23 20:22 Dockerrun.aws.json -rw-r--r--@ 1 eshinoha 1896053708 90 3 23 20:22 cron.yaml drwxr-xr-x@ 5 eshinoha 1896053708 170 4 18 10:37 php-app drwxr-xr-x@ 3 eshinoha 1896053708 102 3 23 20:22 proxy
今回はまだEnvironmentを1つしか作っていませんが、実際に開発を進めていく中で、 Dev, Staging, Prodのような形になっていくのかなと思います。 その場合は↓のようにeb useというのを使ってどのEnvironment〜といったところを指定出来るようになります。
git checkout master eb use prod git checkout develop eb use dev
では、さっそくローカルのgitに1行変更を入れてコミットしてタグを打ってみます。 ebコマンドを使ってデプロイする場合は、gitのタグをVersionのラベルに出来ます。 (上でzipをアップロードした時に付けたVersion labelの事です)
$ git add . $ git commit -m "first commit" $ vim php-app/index.php 114 <li>hogehoge</li> 115 <li>local_hogehoge</li> 116 <li>git_local_hogehoge</li> 117 </ul> $ git add . $ git commit -m "added git_local_hogehoge" $ git tag -a v0.1 -m 'my version 0.1'
eb deploy コマンドでデプロイ用のアーカイブを作ってくれて、S3にアップロードしてくれて、 古いECSのTaskから新しいTaskへ〜という様子が見て取れます。 で、新しいVersionがデプロイされて、Environmentの更新が成功しましたよ、と。 (ECS用語とElastic Beanstalk用語をちゃんと抑えておかないと頭が混乱しそうなのでご注意ください…^^;)
$ eb deploy Creating application version archive "v0_1". Uploading multi docker test/v0_1.zip to S3. This may take a while. Upload Complete. INFO: Environment update is starting. INFO: Deploying new version to instance(s). INFO: Stopping ECS task arn:aws:ecs:ap-northeast-1:832164682569:task/4e859699-b267-4664-9b4d-9553dda03702. INFO: ECS task: arn:aws:ecs:ap-northeast-1:832164682569:task/4e859699-b267-4664-9b4d-9553dda03702 is DEAD. INFO: Starting new ECS task with awseb-multiDockerTest-env-upq3tpm3fh. INFO: ECS task: arn:aws:ecs:ap-northeast-1:832164682569:task/3aac1d84-03b2-402c-a170-bd57772f7162 is RUNNING. INFO: New application version was deployed to running EC2 instances. INFO: Environment update completed successfully.
でもって、S3のバケットは↓のようにアーカイブされたものが入っていました。 もちろんブラウザからも↓のように確認できました。 ■ 複数インスタンスに対するebのdeploy 実際の業務では一日に何回もデプロイがされる可能性があって、その度にサービスをダウン わけにはいかないものだと思います。 Elastic Beanstalkには"Batch"という名前で複数のEC2インスタンスやContainerにデプロイする仕掛けがあります。 詳細は↓に記載されておりますが、こちらを使ってやっていきたいと思います。 http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.rolling-version-deploy.html まずは、AutoScalingの設定で複数のインスタンスになるようにします。 PHPに多少負荷がかかるように↓のようなコードを入れて、
118 <? 119 date_default_timezone_set('UTC'); 120 echo date('l jS of F Y h:i:s A'); 121 for ($i = 1; $i <= 10000000; $i++) { 122 if ($i % 1000000 === 1) { 123 echo $i; 124 } 125 } 126 ?>
複数のターミナルの窓から↓こんな感じでトラフィックを流し続けるようにします。
$ for ((i=0;i<10000;i++)) ; do curl http://multidockertest-env.elasticbeanstalk.com/ 2>&1 >/dev/null ; done ;
分かりやすいようにBatchのタイプをFixedにして、必ず1台だけという設定にします。 上記のような状態で、4つのインスタンスが稼働している状態で、 eb deployしている間のELBの様子は以下のようになります。 1. 最初は4つ全てのインスタンスがELB配下にいます 2. 1つのインスタンスがELBから切り離されて3インスタンスになります。その間に裏でデプロイが行われます 3. 上記2. で切り離されたインスタンスが戻ってきます(Out Of Serviceなのはヘルスチェック中の為) 4. 再び別のインスタンスが切り離され、デプロイのプロセスに入ります 5. デプロイが終わったインスタンスは再びヘルスチェック後にELB配下に入ります 上記のような動きを繰り返して、1つ1つデプロイをしていくことで、サービスへの影響を最小限にします。 ■ その他 ・eb deployを使用した場合に.gitignoreの扱いについて 例えばeb deployでデプロイを行った時に .elasticbeanstalk の設定は持っていく必要が無いわけですが、 .gitignoreには以下の記載があります。
$ cat .gitignore # Elastic Beanstalk Files .elasticbeanstalk/* !.elasticbeanstalk/*.cfg.yml !.elasticbeanstalk/*.global.yml
eb sshを使用して1つのインスタンスにSSHでログイン後、
$ eb ssh Select an instance to ssh into 1) i-79ffa98a 2) i-64489991 3) i-cc489939 4) i-8ff4a27c (default is 1): 1
叩いてみたら存在しなかったので、ebコマンドはちゃんと.gitignoreをチェックしてそうです。
[ec2-user@ip-172-31-11-114 ~]$ cd /var/app/current [ec2-user@ip-172-31-11-114 current]$ ls -la 合計 28 drwxr-xr-x 4 root root 4096 4月 18 07:55 . drwxr-xr-x 3 root root 4096 4月 18 07:55 .. -rw-r--r-- 1 root root 108 4月 18 07:50 .gitignore -rw-r--r-- 1 root root 1459 4月 18 07:50 Dockerrun.aws.json -rw-r--r-- 1 root root 90 4月 18 07:50 cron.yaml drwxr-xr-x 2 root root 4096 4月 18 07:50 php-app drwxr-xr-x 3 root root 4096 4月 18 07:50 proxy
・hookを使ったデプロイのカスタマイズについて ↓は2013年の安川さんのWebinar資料ですが、ココにデプロイ前後のhookに関する記載があります。 [slideshare id=FR6BXJW9heoBv4?startSlide=58&w=425&h=355&fb=0&mw=0&mh=0&style=border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&sc=no]
通常、Elastic Beanstalkでプロビジョニングされる環境の構成変更は .ebextensions というディレクトリの中に yamlファイルを配置してそこに記載しますが、デプロイの過程の中で、"ココ!"っていうタイミングで処理をしたい場合が 出てくることもあるかと思います。 例えばデプロイ前であればデフォルトでは↓のようにディレクトリ内をalphabeticalな順番で処理がされていきますが、 01unzip.sh で解凍された後に、、的な事をやろうとした場合に関して、 ディレクトリ内に01x_afterunzip.shみたいな名前で配置してやれば良い、と。$ pwd /opt/elasticbeanstalk/hooks/appdeploy/pre [ec2-user@ip-172-31-11-114 pre]$ ls -l 合計 20 -rwxr-xr-x 1 root root 862 4月 8 17:29 00enable-eb-ecs.sh -rwxr-xr-x 1 root root 842 3月 10 23:44 00stop-task.sh -rwxr-xr-x 1 root root 2095 3月 13 00:52 01unzip.sh -rwxr-xr-x 1 root root 2992 4月 8 17:29 02update-credentials.sh -rwxr-xr-x 1 root root 782 3月 11 18:01 03start-task.sh
で、コレを1インスタンスずつ配置していたら日が暮れてしまうので、 .ebextensionsで〜的なやり方で行うと、よりBlack Beltな感じかなと思います。 こちらは、これまた安川さんがAWSのフォーラムに、Railsのアセットコンパイルを 事前に行ってS3に置いておいて、それをコピってくるだけで、デプロイの時間を短縮させましょう的な例を (英語で)ガッツリ記載していますので↓を一読されるとよろしいかと思います。 Customizing ElasticBeanstalk deployment hooks(https://forums.aws.amazon.com/thread.jspa?threadID=137136) .ebextensionsに関しては↓に記載されているように、細かく定義が出来て便利なのですが、 http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html あまりやり過ぎると後から辛い目に遭ったりする可能性もあるので、ご利用は計画的に。 (AWSにはOpsWorksというChefベースのプロビジョニング/デプロイサービスがございます)