Elastic Beanstalk で Docker を動かしてできることまとめ

# 概要

Docker Container を動かすための環境として [AWS Elastic Beanstalk](https://aws.amazon.com/jp/elasticbeanstalk/) を使った場合、どんなことができるか、デプロイはどんな感じになるか、といったことを紹介します。
「どうすれば動かせるか？」ではなく「採用できるか？運用はどうなるか？」を想像できそうな情報をまとめることを目指しました。

例によって記載の情報に間違いがあったら指摘していただけると嬉しいです。


# Elastic Beanstalk とは

実用に寄った他の AWS のプロダクトとは一味違うメルヘンな名前(Beanstalk = 豆の木)を持つこのサービスは、 AWS の各プロダクトをつなぎ合わせることによって PaaS 的な実行環境を作り出すサービスです。
ボタンを押すだけで、ロードバランサ(ELB)、実行環境(EC2)、データベース(RDS)、設定(環境変数など)、可用性やスケールアウト(Auto Scaling)、監視(CloudWatch, SNS)、といった一般的なアプリケーションが動作するために必要な要素を一通り揃えた環境が作成され、その環境に対するデプロイや運用の支援ツールが提供されます。
Elastic Beanstalk(以降 EB) で生成される EC2 などの各リソースに関しては当然利用料金が発生しますが、 EB 自体は無料で利用できます。

リリース以降しばらくは PHP や Python, Java ...といったような特定のアプリケーションを動作させる環境を作成することができるツールでしたが、2014年4月に満を持して Docker への対応がおこなわれ、コンテナを動かすための基盤として使えるようになりました。
この一年で見ても、日本語対応が行われたり、任意のタイミングでのスケーリングを予約することができる機能が追加されたり、環境に入っているインスタンスの sar 的なものを一覧できるようになったりと、頻繁にアップデートが行われている印象です。


# 利用イメージ

と、どんなものかをざっと説明されても、いまいち掴みづらいのではないかと思います。
というわけで、 EB を使った場合どんな利用イメージになるのか、早速イメージ図を作成してみました。
以下は EB をウェブサーバ環境として利用した場合です。

<img width="804" alt="スクリーンショット 2015-12-14 21.39.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/11679/943292ad-6c4a-850e-214f-ef8812ac48ca.png">

細かい要素は省きましたが、灰色の枠内が EB が生成、管理してくれるリソース群です。
EB で環境を作成すると、設定に従って ELB や EC2 インスタンス、 AutoScaling グループなどの各種リソースが作成され、それらが EB によって一括管理されるようになります。
各インスタンスでは設定に従い指定したイメージからコンテナが起動され、各インスタンスで動作している nginx によってプロキシされ、ユーザと疎通されます。

EB で Docker を運用する場合のデプロイのサイクルは、大体の場合以下のような感じになるかと思います。

- 手元ないし CI ツールなどで Docker イメージをビルド
- どこか適当なリポジトリにビルドしたイメージを Push
- 今 Push したイメージを使うように設定した、新しいバージョンを EB にデプロイ
- EB から EC2 インスタンスにデプロイ指示が行き、それぞれがリポジトリから新イメージを Pull
- 新しいイメージを使ったコンテナが立ち上がる

このとおり、 EB の管理外に別途 Docker イメージを保存しておくリポジトリが必要になります。
ただ、こちらについては低コストでデプロイに組み込むことができる素敵な方法が公式のブログで紹介されていたので、後ほど紹介します。

EB へのデプロイ時にソースと Dockerfile を含んでおいて、各インスタンスで Docker イメージをビルドさせるといったことも一応可能ですが、実運用だとちょっと使い物にならないかと思われます。
(デプロイに途方もない時間がかかるし、なにより全台で同一イメージを使えるというメリットがなくなる)


# アプリケーションや環境を管理する

複数のアプリケーションや環境を管理するために、 EB は以下のような構造を持っています。

<img width="630" alt="スクリーンショット 2015-12-14 21.55.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/11679/2b93d596-5bb1-7a1c-b5b5-9715752efbda.png">

## アプリケーション(Application)

Web アプリケーションや、管理アプリケーションといったように、具体的な目的を分ける(デプロイするソースが変わる)階層が **アプリケーション** です。
以下のようなものがアプリケーションに紐付きます。

- 環境
- 環境の設定(テンプレート)
- デプロイされたバージョンごとの zip アーカイブ

## 環境(Environment)

開発環境や本番環境といった、同一アプリケーションを別環境で動かすといったことを実現するための階層が **環境** です。
環境の下に ELB や EC2 インスタンスといった実際のリソースが紐づいており、環境毎にインスタンスのクラスを変更したり、起動台数を制限したり、環境変数を設定したりすることが可能です。
以下のようなものが環境に紐付きます。

- 実リソース
- 設定
    - スケーリング設定
    - インスタンス設定(クラスとかセキュリティグループとか)
    - 通知設定
    - 環境変数
    - デプロイ設定
    - ヘルスチェック設定
    - ロードバランサ設定
    - VPC 設定
- ログ(アプリケーションログは自分で取る必要あり)
- モニタリング
- アラーム


# Docker を動かすための二種類の構成

環境の作成時に Docker を利用可能なタイプを選択することで、 Docker コンテナを EB で動かすことができるようになります。
ここで選択可能な構成には、 **Docker** と **Multi-container Docker** の二種類がありますが、それぞれの特徴について紹介します。

![スクリーンショット_2015-12-14_14_43_10.png](https://qiita-image-store.s3.amazonaws.com/0/11679/033da912-76ce-74f0-429f-be5c9ded6988.png)

## Docker

各インスタンスで、単一のコンテナを立ち上げるのに適した構成です。
以下のようにシンプルな Dockerrun.aws.json(v1) をデプロイすることで、稼働するコンテナを設定します。

```
{
  "AWSEBDockerrunVersion": "1",
  "Image": {
    "Name": "janedoe/image",
    "Update": "true"
  },
  "Ports": [
    {
      "ContainerPort": "1234"
    }
  ],
  "Volumes": [
    {
      "HostDirectory": "/var/app/mydb",
      "ContainerDirectory": "/etc/mysql"
    }
  ],
  "Logging": "/var/log/nginx"
}
```

([単一コンテナの Docker の設定](http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/create_deploy_docker_image.html)より例)

実際のところ、監視やログの収集などが必要で、単一のコンテナだけで稼働できるケースはそんなにないと思うので、通常は後者の Multi-container Docker を使用することになるかと思います。
(一応、こちらの形式でも後述の .ebextensions と呼ばれる設定ファイルを使うことで、無理やり複数コンテナを運用することは可能です)


## Multi-container Docker

EB が Docker 対応をしてから一年ほど、前者の単一コンテナをサポートする構成しかありませんでしたが、2015年4月にこちらの Multi-container Docker 構成が追加され、ついに複数コンテナがサポートされました。

[EC2 Container Service(ECS)](https://aws.amazon.com/jp/ecs/) を内包することによって、複数コンテナをサポートするようになっています。
なので、こちらの構成を選択した場合は、さらに ECS が EB によって管理されることになります。

Dockerrun.aws.json(v2) をデプロイすることで、稼働するコンテナを設定します。
ECS のタスクとして動作するようになっているので、それと合わせて構成も大幅に変更になっており、 Volume Container の設定や、コンテナに割り当てるリソースの設定、コンテナが落ちたらサービス不全とするかどうかなどが設定できるようになっています。

以下の設定例を見れば、どんなことができるか大体想像できるのではないかと思われます。

```
{
  "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"
        }
      ]
    }
  ]
}
```

([複数コンテナの Docker 設定](http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/create_deploy_docker_v2config.html)より)


# 便利な点

個人的に EB を利用して感じた利点を、いくつか羅列してみます。
(つい最近インフラやらバックエンドやらを触り始めた新参者のため、この界隈の経験が薄く、ほかのサービスやソリューションとあまり比較できていないですが。。)

## 自前でいろいろ用意する必要がない

すでに用意されている EB というサービスを使うだけでいいのがまず利点かなーと思います。
EB 自体の利用は無料であり、かつ EB 自体の運用は AWS がやってくれているので、自分で運用するサービスが増えなくていいのが魅力的です。

## GUI でほとんど操作できる

リソースの設定などは AWS マネジメントコンソールから大体操作することが可能です。
GUI で操作できるのはやっぱり安心感があっていいです。

ただし、たとえば ssh のポートを変えるなど、細かい設定をする場合は直接 API を叩かなくてはいけない場面があります。

## 可用性と拡張性が何もせずともそれなりに備わっている

AutoScaling が備わっているので、死んだら生き返るし、負荷が高くなったら横に増えるという状態が初めからできています。
厳しい運用条件が求められていなければ、少し数字をいじるだけでそれなりに実用に耐えられる気がします。

## 環境が簡単に保存/複製できる

環境変数やインスタンスのクラスなど、 **環境** に設定した内容は、 **アプリケーション** に保存しておくことができるので、気軽に同一の環境を立ち上げることができます。
気軽に開発環境を追加したり、ステージング環境を立てたり消したりできるのは、なかなか便利です。

# 不便な点

逆に EB を利用する場合に不便なことを、いくつか羅列してみます。

## デプロイ時何が起きているか分かりづらいことがある

一応環境構築時やデプロイ時のログなどをまとめて見ることはできますが、何がどのくらい進んでいるかなどが見られなかったりするため、結局 EC2 のインスタンスに直接入って状況を確認することがしばしばあります。
また、アプリケーションログは自分で回収する必要があるため、デプロイ時にアプリケーションログに落ちるような処理をさせる場合なども、自力でなんとかする必要があります。

## 痒いところに手が届かないことが結構ある

デプロイ時のこのタイミングでこれを実施ししたい、この時にこの値を参照できさえすれば。。。など、痒いところに手が届かないと思うシーンが結構あります。

ただ、デプロイ時に参照される設定である、後述の .ebextensions を使えば、任意のタイミングでコマンドやスクリプトを動かしたりすることが可能なので、少々扱いづらいものの大体の場合は工夫でなんとかなります。

(追記) また、 EB において設定可能な AutoScaling トリガーの小回りがきかない(トリガーを一つしか指定できず、監視期間などがスケールアウト/スケールイン共用)ため、インスタンスの性能を生かしきれないことがあるという点があります。
ECU 可変な t2 インスタンスなどとは若干相性が悪いかもしれません。

## デプロイまわりは自分で整える必要がある

CI 的な機能は備わっていないため、当然といえば当然ですが、デプロイまわりの環境は別途自分で整える必要があります。

また、これは Docker を使用する場合のデプロイ全般に言えることかもしれませんが、Docker イメージと EB にデプロイするバージョンには対応がないため、Docker のイメージタグなどを使ってその対応を自分で作ってやる必要もあります。

## (追記) 設定変更をコード管理するのが難しい

GUI のマネジメントコンソールからほとんど操作できるのは利点でもあると思うのですが、一方で変更履歴を追いにくいという問題があります。
また、事前に設定の変更をレビューすることが難しく、ヒューマンエラーが発生しうるのも難点です。

# そのほか導入を考える際に知っておきたい便利なこと

## URL Swapping でブルーグリーン・デプロイメント

特に Docker とは関連のない機能ですが、 EB には同一アプリケーションの二つの環境間の CNAME をボタンひとつ(API ひとつ)でスワップさせる機能が備わっています。
この機能を使うことで、ブルーグリーン・デプロイメントが簡単に実現できます。

<img width="744" alt="スクリーンショット 2015-12-14 21.40.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/11679/22ccdc3a-eeff-c4fe-8619-49dd34c3d3a1.png">

また、環境自体は保存済みの設定から手軽に復元可能なので、節約のために普段は片割れの環境を落としておき、デプロイ時に環境を立ち上げてスワップさせる。。といったこともできます。

## Local Docker Registry で低コストデプロイ

デプロイ時に Docker リポジトリを用意する必要がありますが、以下のブログで紹介されている方法を使って、ローカルに Docker Registry を立てる方法を使えば、認証方法に頭を悩ませる必要もないし、コストも安くて運用不要な S3 を使うだけで済むのでおすすめです。

- [Amazon S3をDockerプライベートレポジトリにしてAWS ElasticBeanstalk環境にデプロイ](http://aws.typepad.com/sajp/2014/06/eb-docker-private-repo.html)

ただし、上記で紹介されているスクリプトそのままですと、現在は環境変数が取得できなくなっているため、そのまま動作はしないのでご注意ください。
[ElasticBeanstalk で環境変数を利用する](http://qiita.com/lciel/items/1df53db1c0a63be10564) にて現在も外部スクリプトから環境変数を参照できる方法を紹介しているので、よろしければご参照ください。

難点を挙げるとすれば、デプロイ中に registry を起動するため、メモリをそれなりに食うことです。
十分なメモリがないインスタンスだとデプロイに失敗することがあります。

(まだ Tokyo リージョンには来ていないですが [ECR](https://aws.amazon.com/jp/ecr/) が使えるのであれば、このような工夫をする必要はなくなりそうです)

## .ebextensions

EB にデプロイするソースの中に Dockerrun.aws.json 以外に .ebestensions というディレクトリに設定ファイルを追加しておくことで、デプロイ時に様々な設定を行うことができます。
.ebextensions に関しては「[設定ファイル（.ebextensions）による高度な環境のカスタマイズ](http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/ebextensions.html)」 あたりにリファレンスがあります。

.ebextensions に定義されている各種設定値の変更を行えるほか、 commands キーを使って任意のコマンドの発行が可能なことや、files キーを使って任意の場所にスクリプトを置いて各種デプロイの[タイミング](http://qiita.com/lciel/items/71df8ab12ad921b422f9)にフックさせることが可能なため、デプロイ時にゴニョゴニョしたいという場合も大体なんとかなります。


# 最後に

Docker Advent Calendar 用に書きましたが、よく考えてみるとほとんど EB の話しかしていませんでした。
すみません :crying_cat_face: