Edited at

Docker イメージを作って AWS EC2 Container Registory (ECR) にプッシュし、ElasticBeanstalk でデプロイする

More than 3 years have passed since last update.

Amazon Web Service の EC2 Container Registory (ECR) は、フルマネージドな Docker コンテナリポジトリです。プライベートな Docker イメージをプッシュし、EC2にデプロイすることができます。


今回の構成


  • AWS EC2 Container Registory (ECR)

  • AWS ElasticBeanstalk

  • Docker

  • Python3

  • Django1.10

  • uwsgi

  • nginx

  • supervisor

  • Fabric

手元で、Python3 + Django の Docker イメージを作り、EC2 Container Registory (ECR) にプッシュし、ElasticBeanstalk (EB) でデプロイするまで書きます。


サンプルコード

こちらです。 https://github.com/ytyng/aws-eb-docker-django-skeleton


Fabric の準備

fabric は必須ではありませんが、私はコマンドをすぐ忘れてしまうため fabric のコマンド化してあります。

とはいえ、サーバに SSH でデプロイするわけではないので、fabric の本来の使い方ではないですね。(make や invoke なんかで良さそうです)

fabfile

fabric は、あらかじめローカル環境にインストールしておく必要があります。

おそらく、Python3 では動作しないため、ローカルの Python2 環境に

インストールする必要があります。

$ python

Python 2.7.10
...

$ sudo pip install fabric


Dockerイメージのビルド

$ fab build

(もしくは $ docker build -t aws-eb-docker-django-skeleton )


Dockerfile

FROM ubuntu:16.04

MAINTAINER ytyng

RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
python3-dev \
python3-setuptools \
nginx \
supervisor \
&& rm -rf /var/lib/apt/lists/*

# for pil
# apt-get install libjpeg-dev
# RUN ln -s /usr/lib/x86_64-linux-gnu/libjpeg.so /usr/lib/libjpeg.so

# for ipython
# apt-get install lib32ncurses5-dev

# set locale
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

# Python packages
COPY requirements/base.txt /tmp/requirements/base.txt
COPY requirements/production.txt /tmp/requirements/production.txt
RUN pip3 install -r /tmp/requirements/base.txt
RUN pip3 install -r /tmp/requirements/production.txt

# setup all the configfiles
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
COPY conf/nginx-app.conf /etc/nginx/sites-available/default
COPY conf/supervisor-app.conf /etc/supervisor/conf.d/

RUN mkdir /var/log/django

COPY . /var/django/aws-eb-docker-django-skeleton

EXPOSE 80
EXPOSE 443
CMD ["supervisord", "-n"]


Dockerfile


DBマイグレーション

$ fab manage:migrate

↑コマンドが何をしているかは fabfile を見てみてください


サーバ起動

$ fab up

(ポート 80, 443 をバインドします)


docker-compose.yml

version: '2'

services:
aws-eb-docker-django-skeleton:
build: .
image: aws-eb-docker-django-skeleton
container_name: aws-eb-docker-django-skeleton
ports:
- 80:80
- 443:443
volumes:
- /etc/localtime:/etc/localtime:ro
- .:/var/django/aws-eb-docker-django-skeleton
environment:
- UWSGI_PROCESSES=1
- UWSGI_THREADS=1
- APP_NAME=aws-eb-docker-django-skeleton
- DJANGO_APP_NAME=aws_eb_docker_django_skeleton
- DJANGO_SETTINGS_MODULE=aws_eb_docker_django_skeleton.settings.local

docker-compose.yml

$ docker-machine ls でIPアドレスを調べ(例: 192.168.99.100 )、

ブラウザで開くと起動ページが見れます

$ open "http://192.168.99.100/"

スクリーンショット 2016-10-03 18.06.14.png


Docker リポジトリを、AWS EC2コンテナサービス (ECS) に登録する

プライベートリポジトリとして登録できます。


1. ローカルPCの AWS CLI を設定 (既に行っていれば不要)

1-1. IAM より、ローカルコンピュータに保存するログインクレデンシャルを作っておきます。

既に作ってある場合は不要です。

IAMページ

ユーザー → 新規ユーザーの作成

ユーザー名 適当に (例: aws-cli )

スクリーンショット 2016-10-03 16.48.17.png

ユーザーのセキュリティ認証情報を表示 をクリックし、アクセスキー ID と シークレットアクセスキー を保存しておく。

もしくは「認証情報のダウンロード」を押して、CSVを保存しておく。

閉じる をクリック

スクリーンショット 2016-10-03 17.02.24.png

作成したユーザーを選択し、「ポリシーのアタッチ」

スクリーンショット 2016-10-03 17.02.42.png

AmazonEC2ContainerRegistryFullAccess をアタッチしておきましょう

スクリーンショット 2016-10-03 17.04.02.png

スクリーンショット 2016-10-03 17.04.19.png

1-2. aws cli のインストール

$ sudo pip install awscli --upgrade --ignore-installed six

1-3. aws を設定

$ aws configure

さきほどのクレデンシャル情報を入力しておく

AWS Access Key ID [None]: AK................

AWS Secret Access Key [None]: ********************************
Default region name [None]: ap-northeast-1
Default output format [None]:


2. AWS EC2 コンテナリポジトリの設定とイメージのプッシュ

2-1. AWS コンソールの ECSページを表示

ECSページ

初回起動時、チェックボックスが出ます。

Store container images securely with Amazon ECR にチェックON して Continue。

スクリーンショット 2016-10-03 16.39.16.png

Repository name にリポジトリ名を入力して、Next step。

すると、コンソールでの手順が表示されます。

スクリーンショット 2016-10-03 16.42.15.png

2-2. ローカルでコンテナ登録コマンド実行

表示されたコマンドにそって

$ aws ecr get-login --region ap-northeast-1

ターミナルから入力すべきコマンドが表示されます。つまり、やるべきことは

$ $(aws ecr get-login --region ap-northeast-1)

です。

$ $(aws ecr get-login --region ap-northeast-1)

Flag --email has been deprecated, will be removed in 1.13.
Login Succeeded

fab のコマンドも用意してあります。 $ fab login_ecr

引き続き、AWSのページの手順に沿って続けます。

$ docker tag aws-eb-docker-django-skeleton:latest 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/aws-eb-docker-django-skeleton:latest

$ docker push 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/aws-eb-docker-django-skeleton:latest

fab のコマンドも用意してあります $ fab push


3. プッシュしたイメージから ElasticBeanstalk でインスタンスを作成

3-1. EBを作成

EB のページを表示

プラットフォームの選択 は、Multi-container Docker にして、「今すぐ起動」

スクリーンショット 2016-10-03 17.19.59.png

(※起動する Docker コンテナは1つですが、単一Docker コンテナの設定ファイルと互換性が無いため、あとからコンテナを増やしたい場合等に困ります。マルチコンテナDockerで全く問題無いでしょう。(メモリ設定が必須で少し面倒なぐらい?))

そしたら、「初めての Elastic Beanstalk アプリケーション」という変なアプリケーションが

出来たので、これは無視して右上の「新しいアプリケーションの作成」をクリック

スクリーンショット 2016-10-03 17.23.29.png

アプリケーション名は適当に、例: aws-eb-docker-django-skeleton

→ 次へ → ウェブサーバーの作成 →

スクリーンショット 2016-10-03 17.24.35.png

プラットフォームの選択: Multi-container Docker

環境タイプ: 単一インスタンス

→ 次へ

送信元: 独自のアップロード

今回は、手作業で Json ファイルをアップロードします。

本来は、S3経由でコマンドでアップロードなどした方が良いと思うのですが、

ここの ベストプラクティスはまだ確立できていません。(ebコマンドでできるか等もまだ不明…)

EC2コンテナサービス (ECS) を使う場合、Dockerrun.aws.json だけアップロードすれば良いです。


Dockerrun.aws.json

{

"AWSEBDockerrunVersion": 2,
"containerDefinitions": [
{
"name": "aws-eb-docker-django-skeleton",
"image": "000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/aws-eb-docker-django-skeleton:latest",
"essential": true,
"environment": [
{
"name": "UWSGI_PROCESSES",
"value": "2"
},
{
"name": "UWSGI_THREADS",
"value": "2"
},
{
"name": "APP_NAME",
"value": "aws-eb-docker-django-skeleton"
},
{
"name": "DJANGO_APP_NAME",
"value": "aws_eb_docker_django_skeleton"
},
{
"name": "DJANGO_SETTINGS_MODULE",
"value": "aws_eb_docker_django_skeleton.settings.production"
}
],
"portMappings": [
{
"hostPort": 80,
"containerPort": 80
},
{
"hostPort": 443,
"containerPort": 443
}
],
"memory": 256,
"memoryReservation": 128
}
]
}

Dockerrun.aws.json

image の箇所を修正した json ファイルを、「独自のアップロード」のファイルに指定します。

環境名、環境URL は、例: aws-eb-docker-django-skeleton を入力

スクリーンショット 2016-10-03 17.30.59.png

その他のリソース は、今回は使わないのでチェック無しで「次へ」

スクリーンショット 2016-10-03 17.31.17.png

構成の詳細は適当に。今回はテストなので t1.micro で充分です

スクリーンショット 2016-10-03 17.32.26.png

環境タグ も不要なので入力せずに「次へ」

アクセス制限 も、そのままで『次へ」

スクリーンショット 2016-10-03 17.33.08.png

内容がプレビューされますので、「起動」

スクリーンショット 2016-10-03 17.33.38.png

しばらく待つとインスタンスが作られるのですが、そのままでは下記のようにエラーになります。

ECS task stopped due to: Essential container in task exited. (aws-eb-docker-django-skeleton: CannotPullContainerError: AccessDeniedException: User: arn:aws:sts::000000000000:assumed-role/aws-elasticbeanstalk-ec2-role/i-00000000 is not authorized to perform: ecr:GetAuthorizationToken on resource: * status code: 400, request id: 00000000-0000-0000)

スクリーンショット 2016-10-03 17.53.13.png

スクリーンショット 2016-10-03 17.53.01.png

3-2. ロールにポリシーを設定

エラーの原因は、EB のロールが ECS (コンテナレジストリ) の GetAuthorizationToken

権限が無いためです。付与しましょう。

IAM のロールページ

から、aws-elasticbeanstalk-ec2-role を選択し、「ポリシーのアタッチ」→ container で検索して、

AmazonEC2ContainerRegistryReadOnly をアタッチします。

スクリーンショット 2016-10-03 17.58.30.png

スクリーンショット 2016-10-03 17.59.16.png

3-3. 再デプロイ

EB のアプリケーションのページの、アップロードとデプロイ → アプリケーションバージョン ページ

スクリーンショット 2016-10-03 18.02.26.png

「最初のリリース」を選択して「デプロイ」

Environment update completed successfully. になりました。

スクリーンショット 2016-10-03 18.04.49.png


4. ページ確認

EB には1つの URL が割り当てられます。例: aws-eb-docker-django-skeleton.ap-northeast-1.elasticbeanstalk.com

ブラウザでEBのURLにアクセスするとページが表示されます。

スクリーンショット 2016-10-03 18.06.14.png


追記

EB でデプロイすると、遅いですしダウンタイムもあったので少し使いづらいですね。

複数台構成にしてローリングデプロイなんかで対応する必要がありそうですが、 uwsgi や gunicorn は無停止リロードができるのでせっかくなら活用したい所です。