😘

docker-compose.ymlを全自動でAWS ECSにデプロイ

2020/12/30に公開
2

概要

2020年7月にDocker社とAWSが協業を発表した。

DockerとAWSが協業 Docker DesktopとAmazon ECSが連係可能に - ITmedia NEWS

それから約半年後、ついにdockerコマンドでAWSに一発デプロイできるようになった

Docker ComposeとAmazon ECSの統合が一般提供に:CodeZine(コードジン)

ここでは、dockerコマンドを使ってfargateにデプロイする方法をメモする。dockerを使ってAWSにデプロイする、ということは数年前からできてたことで、それ自体は何も新鮮ではないのだが、ざっくりいうとAWSの設定を全く、ないしは極力せずにデプロイできるところが今回のアップデートの強みと言える。

  • 今までのAmazon ECS CLIである「ecs-cli」コマンドは使わない
  • AWS CloudFormationの設定不要
  • Docker ECS Pluginもインストール不要

環境

今回はMacからデプロイしてみる。

$ docker -v 
Docker version 20.10.0, build 7287ab3

用意するもの

  • AWSのアカウント

Step1 Public Dockerイメージでデプロイ

まずは構成のシンプルな、Docker Hubのコンテナイメージを使ってデプロイしてみる。

単体で動作して、「動いている感」があるWordPressをデプロイしてみる。

docker-compose.ymlの準備

まずはローカルに以下のdocker-compose.ymlを作成

version: '3'
services:
  db:
    image: mysql:8.0
    command: '--default-authentication-plugin=mysql_native_password'
    volumes:
      - db_data:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=somewordpress
      - MYSQL_DATABASE=wordpress
      - MYSQL_USER=wordpress
      - MYSQL_PASSWORD=wordpress
  wordpress:
    image: wordpress:latest
    ports:
      - 80:80
    environment:
      - WORDPRESS_DB_HOST=db
      - WORDPRESS_DB_USER=wordpress
      - WORDPRESS_DB_PASSWORD=wordpress
      - WORDPRESS_DB_NAME=wordpress
volumes:
  db_data:

この時点でローカルで docker-compose up して http://localhost/ にアクセスするとWordPressをセットアップできるようになるはず。

Amazon ECS用のcontext作成

新しくdockerコマンドでcontextというものを設定できるようになった。これを使ってAWSにデプロイできるようにAWS用のコンテキストを作成する。

$ docker context create ecs myecs

AWSの認証にあたって、すでにaws-cli等でAWSのProfileを使うこともできる。

確認

$ docker context ls
NAME                TYPE                DESCRIPTION                               DOCKER ENDPOINT               KUBERNETES ENDPOINT   ORCHESTRATOR
default *           moby                Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                         swarm
myecs               ecs                 credentials read from environment                                                             

作成したContextを使用する。今までの戻す場合は docker context use default

$ docker context use myecs

いざデプロイ docker-composeコマンドではないことに注意

# ハイフン不要
$ docker compose up

初期設定ではFargateの設定やLBの設定でなんかかんやで数分かかるので気長に待つ。

起動確認

$ docker compose ps
ID                                               NAME                REPLICAS            PORTS
wordpress-docker-DbService-QlT5cdzCpSAw          db                  1/1                 
wordpress-docker-WordpressService-Z6O7p5za0nTI   wordpress           1/1                 xxxx.ap-northeast-1.elb.amazonaws.com:80->80/http

するとLBのエンドポイントが表示されるので、ブラウザで http://xxxx.ap-northeast-1.elb.amazonaws.com にアクセスすると晴れてWordPressが起動している。

ログ表示

$ docker compose logs

削除 Fargate、LBすべての設定も削除される。最強

$ docker compose down

Step2 自分のDockerイメージでデプロイ

ここまで既存のdocker-compose.ymlとdockerコマンドだけで完結できてしまっているのが凄い。

が、実際にはWordPressが一発で起動できても何も嬉しくない。結局は自分のコンテナをデプロイしたいのである。

Step2ではAWSにイメージをpushしてデプロイをしてみる。

事前準備

自分のDockerイメージをAWSのDocker RegistryであるECRにpushする必要がある。

以下のサイトを参考にECRにpushできるようにしておくこと。

AWS ECRにDockerイメージをPrivateにPushする - 動かざることバグの如し

push

自分のレポジトリを作成し、以下のようなdocker-compose-build.ymlを作成

version: '3'
services:
  web:
    image: xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/hatena-blog-shibafu:latest
    build:
      context: .
      dockerfile: docker/Dockerfile.production

docker context use defaultでコンテキストを切り替えて、ビルド

docker-compose -f docker-compose-build.yml build

push

docker-compose -f docker-compose-build.yml push

ECSデプロイするためにコンテキスト切り替え docker context use myecs

以下のような docker-compose-production.yml 作成

version: '3'
services:
  web:
    image: xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/hatena-blog-shibafu:latest
    ports:
      - 80:80
    command: "bundle exec puma -p 80"

デプロイ

docker compose up -f docker-compose-production.yml

今回は自分のイメージとして、はてなブログの記事を投稿具合をGithub芝生で草生やすサイト(https://hatena.turai.work/)を使った。(zenn.dev版作るか

内部のコード説明は割愛するが、要はrubyで動いてポート80でアクセスできるWEBアプリである。Githubに構成ファイル含めてコードを公開している。

thr3a/hatena-blog-shibafu

その他

以下色々あったのでメモ

公式ドキュメントはどこ

過渡期なのかググってもなかなか出てこない。。

https://docs.docker.com/engine/context/ecs-integration/

published port can't be set to a distinct value than container portエラー

fargateの仕様上なのか、コンテナとホスト側のポートは一致してなければならない。

例えば、

ports:
  - 80:9292

とかにするエラーになる。

ローカルでテストさせろ

専用のContextが用意されている。以下のコマンドで作成

docker context create ecs --local-simulation ecslocal

ただ本番と必ずしもasisで動くわけでないので注意は必要。

詳細は公式ドキュメントの「local-simulation」の項目

なんかエラーになる

エラーメッセージは忘れたが、デプロイしたい対象のディレクトリにアンダーバー(_)が含まれているとデプロイできない

デプロイ先のリージョンを変更できない

先述したdockerコマンドとAWSの紐付け時に特定の条件になるとリージョンを変更できず必ずus-east-1になってしまうバグがあるらしい。

詳細は https://github.com/docker/compose-cli/issues/1056

自分の場合は

export AWS_ACCESS_KEY_ID=XXXX
export AWS_SECRET_ACCESS_KEY=XXXX

でキーを環境変数でセットして

「AWS environment variables」

を使用することで東京リージョンへデプロイ出来た。

既存のVPCへデプロイしたい

docker-compose.ymlの先頭に x-aws-vpcを付与

x-aws-vpc: "vpc-114514"

services:
  hoge:
    image: nginx
    ports:
      - 80:80

オートスケールしたい

x-aws-autoscaling を使う。

services:
  hoge:
    deploy:
      x-aws-autoscaling:
        min: 1
        max: 3

Docker HubのPrivateレポジトリを使いたい

アクセストークンを使うとDocker Hubのイメージでもデプロイできる。

まずアクセストークンを生成する。Docker Hubの設定→Securty項目より発行

次に適当な場所で

$ cat dockerhub-credentials.json 
{
  "username":"amanekey",
  "password":"xxxxxxxxxxxxxxxxxxxxx"
}

のようなJSONを作成

認証 pullpullは適当なので任意の名前に変更する

docker secret create pullpull dockerhub-credentials.json

すると 「arn:aws:secretsmanager:xxxxxxxxxxxx」となにかが表示されるので控えておく。

最後にデプロイしたいdocker-compose.ymlで

services:
  app:
    image: DockerHubID/privateimage
    x-aws-pull_credentials: arn:aws:secretsmanager:xxxxxxxxxxxx
    ports:
      - 80:80

のように変更して docker compose up でデプロイできる。

公式でいい感じの説明とかチュートリアルないの?

残念ながらチュートリアル記事はまだないっぽい。(2020年12月30日現在)が、Githubに上がっているドキュメントが例付きで、かなりわかりやすいと思う。

正直まだ未熟な部分もあるけど、今までの「面倒」な点が確実に改善されているので、これから良くなりつつ普及していくんじゃないでしょうか(願望

Discussion

toshizoooooootoshizooooooo

docker compose up-fのオプションなくないですか?

$ docker compose up -f docker-compose-production.yml
unknown shorthand flag: 'f' in -f
ken_CloudEngineerken_CloudEngineer

解決済みかもしれませんが…
記事内のコマンドの表記に少々誤りがあったようですね。

docker compose -f docker-compose-production.yml up

とすれば動くかと思います。
要は「docker compose up」に-fのオプションをつけるのではなく、「docker compose」に-fオプションをつける必要があったみたいです。