DMM.com #1 Advent Calendar 2017の11日目を担当させていただきます@sssinsi です。
前日の記事は @yu-kgr さんの良いモノを創る為にチームビルドを頑張る話でした。
それではMulti-container Dockerを使って環境構築したら、早い!簡単!便利!だった話をします。
経緯
開発環境でDocker composeを使っていたためDockerは身近な存在でしたが、言語別のAWS ElasticBeanstalk(以下、EB)を使うとなると専用のミドルウェア設定が必要となり、途端に開発環境との差異に苦しめられました。
EBでは、各言語の環境を構築すると自動でミドルウェアの設定も行ってくれます。例えばPHPでデプロイするとApacheを使った環境、JavascriptではNginxを使った環境を作ってくれます。
EBを使わずにカスタムAMIを使うことも検討しましたが、できるだけ開発環境で揃えた設定やミドルウェアを使いつつ、俊敏にEB環境を用意したいと考えた結果、Multi-container Dockerならば簡単にそれを実現できることがわかりました
その際行った試行錯誤を含めて共有したいと思います。
用語説明
Elasticbeanstalkとは?
EBとは、コードをデプロイするだけでプログラミング言語毎にアプリケーション環境を用意してくれるAWSの便利なサービスです。
PHP、Node、Goはもちろん、Docker(単体、複数)を使った構築も構築できます。
Multi-container Dockerとは?
Multi-container Docker環境を使うことで1つのEC2内に複数のDockerを立ち上げることができます。コンテナの管理はAmazon ECSで行われます。
docker composeのEB版と考えればわかりやすいでしょう。ただしデプロイ中にDockerfile
からイメージを作れないため、独自Dockerイメージを使いたい場合は別途Amazon ECR等を使って事前に作成する必要があります。
簡単なコードの説明
Multi-container Dockerを使ったサンプルコードを用意しました -> GitHub
EB環境設定
./.ebextensions/
├── 00.option.config
└── 01.env.config
.ebextensionsとはフォルダ配下にファイル(*.config
)を作成することによってLBポート設定、オートスケーリング設定、セキュリティ設定、ミドルウェア用の設定ファイル作成、コマンド実行などなど、EBの環境構築を制御できる場所です。
参考: ElasticBeanstalkで.ebextensionsを利用してnginx+php-fpmの構成にする
イメージ登録
独自DockerイメージをAmazon ECRへ登録するためのシェルです。コードを最新にする時に活用してください。非公開のモジュールを使っている場合、EC2からアクセスできない可能性があります。その時はDockerイメージを作成する段階でモジュールのインストールも行う必要があるので注意してください。
#!/bin/bash
set -xue
# login docker(プロフィールが必要な場合)
AWS_ECR_GET_LOGIN="aws ecr get-login --no-include-email --region ap-northeast-1 --profile ${AWSCLI_PROFILE}"
DOCKER_LOGIN=`eval ${AWS_ECR_GET_LOGIN}`
eval ${DOCKER_LOGIN}
# build image
docker build -t php-fpm:latest -f php-fpm.Dockerfile .
# store image
docker tag php-fpm:latest xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/php-fpm:latest
docker push xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/php-fpm:latest
環境変数
Dockerコンテナ内部で環境変数を使用したい時、EC2に設定された環境変数が使用できます。また、Laravelのような環境変数ファイル(.env
)を使用する場合はサービス起動直前に環境変数ファイルを変更します。サンプルではデプロイ時にconfigで作成した.env
ファイルをDockerrun.aws.json
でコンテナに取り込んでいますが、ParameterStore等を使った方がより安全だと思います。
files:
"/tmp/.env"
mode: "000644"
owner: "ec2-user"
group: "ec2-user"
content: |
# ここでは環境変数を直接記載しているが、
# commandsオプションを使ってParameter Storeから環境変数を取得して.envファイルを作成すると更に良い。
DB_CONNECTION=mysql
DB_DATABASE=staging-sample
DB_USERNAME=staging-user
DB_PASSWORD=staging-passowd
DB_ROOT_PASSWORD=staging-password
DB_EXTERNAL_PORT=3306
DB_PORT=3306
設定共通化
以下のように、開発環境(docker-compose.yaml)とEB環境(Dockerrun.aws.json)で同じミドルウェアと設定ファイル使うことで手間が省け、環境構築の高速化に繋がりました。
/nginx/nginx.conf
と/nginx/site.conf
は同じファイルを使用しており、docker imageも同じです。
...
services:
nginx:
image: nginx:alpine
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/site.conf:/etc/nginx/conf.d/default.conf
...
{
...
"volumes": [
{
"name": "nginx_conf",
"host": {
"sourcePath": "/var/app/current/nginx/nginx.conf"
}
},
{
"name": "nginx_site_conf",
"host": {
"sourcePath": "/var/app/current/nginx/site.conf"
}
}
],
"containerDefinitions":[
{
"image": "nginx:alpine",
"mountPoints": [
{
"sourceVolume": "nginx_conf",
"containerPath": "/etc/nginx/nginx.conf",
"readOnly": true
},
{
"sourceVolume": "nginx_site_conf",
"containerPath": "/etc/nginx/conf.d/default.conf",
"readOnly": true
}
],
}
]
...
}
デプロイ方法
準備
まずはeb init
で作りたい環境を指定します。この時プラットフォームをMulti-container Docker
にします。これによって.elasticbeanstalk/config.yml
が作成されます。そして、必要に応じてdockerイメージを登録しておきます。
デプロイ実施
eb create
を使ってALB環境を作ります。この時デプロイも同時に実施されます。
※EBでALB(Application Load Balancer)を作りたい場合、CLIからしか作れないようです。
$eb create sample-env \
--elb-type application \
--vpc.id vpc-aaaaaaaa \
--vpc.ec2subnets subnet-bbbbbbbb,subnet-cccccccc \
--vpc.elbsubnets subnet-dddddddd,subnet-eeeeeeee
※VPCの指定は必須ではありません。
ソースコードが変更された場合、再度dockerイメージを登録して、eb deploy
すれば環境へ反映されます
気がついたこと
今回の環境構築で気がついたことを、メリット/デメリットとしてまとめてみました。
- メリット:
- 開発環境とAWS環境が同じように作れる
- ミドルウェア設定が容易
- Dockerなので言語バージョンの追従が用意
- Dockerの方がデプロイが早い場合もある
- デメリット:
- AWS EB、Amazon ECS、Amazon ECRなどを使用できる権限が必要(制限されていたので、、、)
- AWS EBの起動スクリプトの順序に悩まされる
- インフラ運用にAWS ECSを使う必要がある