AWS Beanstalk(Multi-Container Docker)をPythonで試してみた ！ Web編その3 / Dockerrun.aws.json

第３回です。

今回はBeanstalkのmulti containerの要でもある、

Dockerrun.aws.jsonについてです。

## 連載一覧

- [その1 / 初期設定](https://qiita.com/i_matsui/items/63d9268531f97e348647)
- [その2 / 環境設定](https://qiita.com/i_matsui/items/f2c61015228303df9540)
- [その3 / Dockerrun.aws.json](https://qiita.com/i_matsui/items/042ef74d3f8cb6614d5d)
- [その4 / python-uwsgi環境作成](https://qiita.com/i_matsui/items/b6cee261d1c77d7064ae)
- [その5 / ebextensions](https://qiita.com/i_matsui/items/402808f11ce345b74999)

## 前提知識

この記事の前提知識としては

Dockerやdocker-composeの基礎を習得していること・・・となります。

Dockerrun.aws.jsonはdocker-compose.ymlのAWS版みたいなもので

docker-compose.ymlを理解していれば記載方法が少し違うくらいなので

docker-compose.ymlの理解が非常に重要です。

## 今回の説明ポイント

BeanstalkのMulti-Container Dockerでは

EC2インスタンス(以下ホスト)上で任意の数のDockerコンテナ(以下ゲスト)を

起動するという仕組みになります。

今回説明するDockerrun.aws.jsonでは以下がポイントとなります。

1. Volumeを使ってホストとゲストのディレクトリを共有する
- ログはVolume越しでホストに出力され、CloudWatchLogsでホストのログを同期
- python(uWSGI+flask)のイメージはDockerリポジトリに作らず配備する度に再構築

※3についてはECRなどのDockerリポジトリを
　最終的には利用すべきですが時間があればどこかの回で説明します。

## Dockerrun.aws.jsonの構成

```
{
    // multi containerの場合は「2」固定
    "AWSEBDockerrunVersion": 2,

    // ディレクトリ共有をするホスト側のPATHを名前をつけて設定
    "volumes": [],

    // ホストの中に立ち上げるコンテナ設定
    // 今回はここにNGINXとPython(uWSGI)のコンテナ設定を記載していきます。
    "containerDefinitions": []
}
```

ファイルの構成としてはこのようになります。

### volumes

まずは【volumes】の設定例を見て行きましょう。

```
    "volumes": [
        {
            "name": "host-timezone",
            "host": {
                "sourcePath": "/etc/localtime"
            }
        },
        {
            "name": "xxxxx-src",
            "host": {
                "sourcePath": "/var/app/current"
            }
        },
        {
            "name": "nginx-proxy-conf",
            "host": {
                "sourcePath": "/var/app/current/proxy/conf.d"
            }
        }
    ],
```

【host-timezone】
ホストのタイムゾーン設定をゲスト(コンテナ)にも反映させる為の設定になります。
ホストのEC2はデフォルトではUTCですので別途(次回以降)に
ebextensionsでJST(Asia/Tokyo)に変更します。

【xxxxx-src】
「eb create」コマンドを実行したカレントディレクトリの中身が
ホストの「/var/app/current」にアップロードされると
前回までの記事でお伝えしました。
今回はDockerリポジトリなどでミドルウェアやプログラムの設定・配備がされていない
状態から始めますのでホストに自動で置かれたデータを
ゲストに配備する為の共有設定です。

【nginx-proxy-conf】
NGINXへのリクエストをuWSGIにリバースプロキシする為の
nginxのconf設定ファイルが置かれているパスです。
eb createを実行するカレントディレクトリの
proxy/conf.d/default.confに配備されています。

```
server {
    listen 80;
    server_name localhost;

    proxy_set_header    Host    $host;
    proxy_set_header    X-Real-IP    $remote_addr;
    proxy_set_header    X-Forwarded-Host       $host;
    proxy_set_header    X-Forwarded-Server    $host;
    proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;

    location / {
        proxy_pass http://python-container:8080;
    }
}
```
中身はこんな感じです。

実運用時はもっと細かい設定が必要かと思われます。

また、リバプロ先のホスト名=python-containerは

Dockerrun.aws.jsonのcontainerDefinitionsの中で設定します。

### containerDefinitions

containerDefinitionsでは各コンテナの設定を記載しています。

まずは代表的な設定値の説明をします。

その後にサンプルを見て行きましょう。

**name**
これはコンテナの名前を表す任意の文字列です。

今回はpython-containerとnginx-proxyとしています。

**image**
Dockerのイメージファイルになります。

ECRやDockerHubなどを利用する場合はそのイメージ名を指定してください。

今回はDocker公式で公開されているpython:3.7とnginxを使います。

**essential**
コンテナの起動に失敗した場合にBenasalkのセットアップ全体を

停止(異常終了)とさせるか否か・・・の設定のようですが

試してません。

**memory**
コンテナに割り当てるメモリです。

memoryReservationも合わせて設定することにより

ホストのメモリに余裕があればmemoryの値を超えても

memoryReservationの値まではメモリを確保するようです。

**portMappings**
ホストのXXポートにアクセスがきたらコンテナのYYに繋ぐ・・・というような

ポートフォワードの設定です。

今回はNGINXは外部からホスト越しでアクセスされますが

python(uWSGI)はNGINX経由でのアクセスになるので

ホストの80番にきたらゲストの80番に繋ぐ・・・という設定のみをしています。

**links**
DockerではコンテナのプライベートIPアドレスは

コンテナ起動時に動的に割り当てられます。

NGINX⇨uWSGIにリバースプロキシをする場合にIPが動的では困る為、

NGINX側のコンテナ設定で「python-container」コンテナに対してlink設定することで

NGINXコンテナからはpython-containerというホスト名でuWSGIにアクセスができます。

**mountPoints**
volumesのセクションでホストのディレクトリ共有を設定しましたが

ここではその共有先をコンテナ側のどのディレクトリを共有させるかの

マッピング設定をします。

readOnly=trueに設定するとコンテナ側でファイルの追加・編集はできなくなります。

　

Beanstalkではvolumesに指定しなくても

【awseb-logs-{コンテナ名}】という名前で自動的にvolume設定されます。

その為、mountPointsで以下を指定してログファイルの共有をします。

awseb-logs-python-container
awseb-logs-nginx-proxy

**entryPoint**
Dockerコンテナが立ち上がった直後に実行するコマンドです。

ここで実行したコマンドが終了するとコンテナも停止してしまうので

デーモンなど、プロセスを起動しっぱなしにする必要があります。

今回はここでシェルスクリプトを起動して

uWSGIのインストールやプログラムモジュールのセットアップを行いますが

詳細は次回説明します。

**workingDirectory**
コンテナが起動した後にentryPointで指定したコマンドを実行する

カレントディレクトリになります。

**environment**
コンテナ内で利用できる環境変数を設定します。

今回はここでuWSGIの起動コマンドを定義しています。

```
{
    "AWSEBDockerrunVersion": 2,
    "volumes": [
        {
            "name": "host-timezone",
            "host": {
                "sourcePath": "/etc/localtime"
            }
        },
        {
            "name": "xxxxx-src",
            "host": {
                "sourcePath": "/var/app/current"
            }
        },
        {
            "name": "nginx-proxy-conf",
            "host": {
                "sourcePath": "/var/app/current/proxy/conf.d"
            }
        }
    ],
    "containerDefinitions": [
        {
            "name": "python-container",
            "image": "python:3.7",
            "essential": true,
            "memory": 512,
            "mountPoints": [
                {
                    "sourceVolume": "host-timezone",
                    "containerPath": "/etc/localtime",
                    "readOnly": true
                },
                {
                    "sourceVolume": "xxxxx-src",
                    "containerPath": "/var/app",
                    "readOnly": true
                },
                {
                    "sourceVolume": "awseb-logs-python-container",
                    "containerPath": "/var/log/xxxdemo"
                }
            ],
            "workingDirectory": "/var/app",
            "environment": [
                {
                    "name": "UWSGI_NUM_PROCESSES",
                    "value": "1"
                },
                {
                    "name": "UWSGI_NUM_THREADS",
                    "value": "15"
                },
                {
                    "name": "UWSGI_UID",
                    "value": "uwsgi"
                },
                {
                    "name": "UWSGI_GID",
                    "value": "uwsgi"
                },
                {
                    "name": "UWSGI_LOG_FILE",
                    "value": "/var/log/uwsgi/uwsgi.log"
                }
            ],
            "entryPoint": [
                "/bin/sh",
                "/var/app/start-docker.sh"
            ]
        },
        {
            "name": "nginx-proxy",
            "image": "nginx",
            "essential": true,
            "memory": 256,
            "portMappings": [
                {
                    "hostPort": 80,
                    "containerPort": 80
                }
            ],
            "links": [
                "python-container"
            ],
            "mountPoints": [
                {
                    "sourceVolume": "host-timezone",
                    "containerPath": "/etc/localtime",
                    "readOnly": true
                },
                {
                    "sourceVolume": "nginx-proxy-conf",
                    "containerPath": "/etc/nginx/conf.d",
                    "readOnly": true
                },
                {
                    "sourceVolume": "awseb-logs-nginx-proxy",
                    "containerPath": "/var/log/nginx"
                }
            ]
        }
    ]
}

```