2017.02.27   |   AWS

Elastic Beanstalkを使ってDockerをデプロイする

Docker 化したアプリケーションを手軽に AWS で動かせるようにしたいと思い、ElasticBeanstalk(EB) の Docker 版を試してみました。

ElasticBeanstalkとは

AWS Elastic Beanstalk により、AWS クラウドのアプリケーションを迅速にデプロイし管理するのが開発者にとってより簡単になります。開発者は単にそのアプリケーションをアップロードするだけで、Elastic Beanstalk が自動的に容量のプロビジョニング、負荷分散、Auto-Scaling、およびアプリケーション状態モニタリングといったデプロイの詳細を処理します。
https://aws.amazon.com/jp/elasticbeanstalk/faqs/

要するに AWS のインスタンスやロードバランサーといったインフラ周りの面倒を全部見てくれるよ というのが ElasticBeanstalk の機能です。今話題のサーバーレス構成と違い、こちらは実際のインスタンスを立てて運用するので構成の自由が利くのが強みです。
(その代わりハマりどころも色々ありますが…)

EB は Docker を使ったデプロイにも対応しているので、今回はそれを試してみました。

Docker コンテナを使った ElasticBeanstalk

EB のDocker 対応は 2種類用意されていて、

  • 単一コンテナのDocker
  • 複数コンテナのDocker

という風に分かれています。

単一コンテナの方は Docker を run するだけで動くようなもの 、 複数コンテナの方は docker-compose を使って動かすような複数のサービスに依存して動くもの をデプロイしたいときに使います。
単一と複数、システム内部的に別物 ( EC2 か ECS か) なのでデプロイするときに必要なファイルが異なってきます。

単一コンテナの Docker

まずはシンプルな方から。

  • Dockerfile
  • Dockerrun.aws.json

単一コンテナ版の EB を使うには、最低限この2つを揃えてあげれば動きます。
AWS のコンソールからこれらを ZIP で固めてアップロードすれば、Dockerfile に従ってコンテナがビルドされ、Dockerrun.aws.json に従ってインスタンス上で実行されます。

ZIP のルートが Dockerfile のルートになるので、アプリケーションに必要なファイルを一緒に置いて固めてあげれば ビルド時に使うことが可能です。

例えば Java 系の言語で app.war という実行用のWARファイルを作ったとすると、

# Dockerfile

FROM jetty:9.3

ADD app.war /var/lib/jetty/webapps/ROOT.war

EXPOSE 8080
# Dockerrun.aws.json

{
  "AWSEBDockerrunVersion": "1",
  "Image": {
      "Update": "false"
  }
}

みたいな設定ファイルを書いて WAR ファイルと一緒にアーカイブしてあげれば準備完了です。

複数コンテナの Docker

続いて複数コンテナの方ですが、こちらは Docker のビルドは行われません
既存の Docker イメージを使うか、自分でどこかにビルドしたイメージをアップロードして使う必要があります。
AWSには Docker イメージをホストする Amazon EC2 Container Registry (ECR) というサービスがあるので、プライベートなシステムならそこを使いましょう。

複数コンテナの場合に必要な Dockerrun.aws.json ファイルは、基本 docker-compose.yml の焼き直しみたいな形をしています。

# Dockerrun.aws.jso

{
  "AWSEBDockerrunVersion": 2,
  "volumes": [
    {
      "name": "php-app",
      "host": {
        "sourcePath": "/var/app/current/php-app"
      }
    },
    {
      "name": "nginx-proxy-conf",
      "host": {
        "sourcePath": "/var/app/current/proxy/conf.d"
      }
    }
  ],
  "containerDefinitions": [
    {
      "name": "php-app",
      "image": "php:fpm",
      "environment": [
        {
          "name": "Container",
          "value": "PHP"
        }
      ],
      "essential": true,
      "memory": 128,
      "mountPoints": [
        {
          "sourceVolume": "php-app",
          "containerPath": "/var/www/html",
          "readOnly": true
        }
      ]
    },
    {
      "name": "nginx-proxy",
      "image": "nginx",
      "essential": true,
      "memory": 128,
      "portMappings": [
        {
          "hostPort": 80,
          "containerPort": 80
        }
      ],
      "links": [
        "php-app"
      ],
      "mountPoints": [
        {
          "sourceVolume": "php-app",
          "containerPath": "/var/www/html",
          "readOnly": true
        },
        {
          "sourceVolume": "nginx-proxy-conf",
          "containerPath": "/etc/nginx/conf.d",
          "readOnly": true
        },
        {
          "sourceVolume": "awseb-logs-nginx-proxy",
          "containerPath": "/var/log/nginx"
        }
      ]
    }
  ]
}

http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/create_deploy_docker_v2config.html

公式サンプルのファイルだとこんな形です。
Docker-compose を使ったことがあれば大体意味が分かるのではないでしょうか。

違うところといえば使うメモリ量を指定するあたりですかね。使うインスタンスのメモリ量を意識して、余裕をもたせつつ割り当ててあげます。ちなみに EB では複数インスタンスを立てる場合でも、全部のインスタンスで同じように Docker コンテナを載せて動かします。
このインスタンスは Nginx 用、こっちは アプリケーションと Redis 用みたいな事は出来ないので沢山コンテナを動かす場合は注意してください。インスタンスを細かく制御したいときは EB を諦めて ECS を使いましょう。

複数コンテナ版では Docker のビルドは出来ないですが、今度は任意のファイルを Docker にマウント出来るようになってます。
サンプルでは php-app というディレクトリの中に PHP のソースが入ってる前提ですね。
これらを ZIP に詰めてあげれば準備完了です。

EB へデプロイする

デプロイのパッケージができれば、あとはAWSのコンソールからデプロイしてあげるだけです。
手順に従ってZIPを上げたりポチポチしたりしていきましょう。
初回時は環境タイプを「単一インスタンス」で作って、無事動くか確かめるのをオススメします。
ロードバランサー込の構成だと立ち上げに時間がかかるので、もし設定ファイルに不備があって試行錯誤しなきゃいけない場合にものすごく時間を浪費します。

また、awsebcliという公式ツールを使えばコマンドラインからもデプロイ出来ます。
コマンドラインからだとZIP化しなくて済むので慣れたらこっちのほうが楽です。
CIなどから自動デプロイとかも出来るようになりますし。

デプロイがうまくいくと、
XXXXXXXX.ap-northeast-1.elasticbeanstalk.com
みたいなドメインが割り当てられてアプリケーションにアクセスできるようになります。

Dockerで実行環境を作ってある場合には、なかなかお手軽なデプロイ環境ではないでしょうか。