LoginSignup
30
20

More than 3 years have passed since last update.

GitHub Actions+ecspressoでECSにデプロイする

Last updated at Posted at 2020-06-22

2020-10-16 追記)
GitHub Actionsでset-env, add-path が非推奨になった :scream:
https://qiita.com/shonansurvivors/items/f256a0443d346f09448e
こちらの記事を参考に適宜書き換えてください(手抜き ^^;

はじめに

サイドカー構成で動くECS(Fargate)へGitHubActions+ecspressoを使用してデプロイするCD設定手順を紹介します。
CDとはContinuousDevelopment,継続的デプロイメントのことです)

参考)CircleCI+ecspresso版の記事はこちら

ecspressoを使う理由

  • Goで作られている(ワンバイナリ)ので利用が簡単
  • 環境別にタスク定義設定を二重管理したくない
    • 設定ファイルにgo-configが使えるので環境ごとの差異を変数化して挿入できる
  • サイドカー構成のデプロイに対応している

前提

システム構成

  • ALB + ECS(Fargate)という構成のWebアプリをデプロイします
    • ALB+ECS部分の構築はこの記事の本質とズレるのでCloudFormationを使います
  • 本番/QA環境用にAWSアカウントが2つある前提です
    • マージするブランチによってデプロイ先環境を切り替えます
      • developブランチマージ -> QA環境へデプロイ
      • masterブランチマージ -> 本番環境へデプロイ
  • こちらのリポジトリForkしてください
    • (リクエストヘッダを表示するだけの雑なアプリです)
    • もちろん構成が同じであれば自作アプリでもOKです

ディレクトリ構成

デプロイするアプリのGitリポジトリ-ディレクトリ構成例です。
今回デプロイするアプリの名称は仮にfargate-exampleとします。

fargate-exampleリポジトリ
.
├── .github
│   └── workflows
│       └── deploy.yml     # GitHubActionsワークフロー定義ファイル(デプロイ用) *後述*
├── .gitignore
├── README.md
├── cfn-alb-ecs.yml        # ALB+ECSを作るCloudFormationテンプレート
├── deploy
│   ├── ecs-task-def.json
│   └── ecspresso.yml
├── docker-compose.yml
├── deploy
│   ├── ecs-task-def.json  # タスク定義JSON *後述*
│   └── ecspresso.yml      # ecspresso設定ファイル *後述*
├── nginx                  # nginxコンテナ定義(Goアプリにリクエスト中継するためのサイドカー)
│   ├── Dockerfile
│   ├── goapp.conf.tmpl
│   └── nginx.conf
└── goapp                  # goappコンテナ定義(nginx経由でリクエストを処理するGoアプリ)
    ├── Dockerfile
    ├── Makefile
    ├── go.mod
    ├── go.sum
    └── main.go

作業環境

  • ecspressoがインストール済みであること
    • macOSであれば brew install kayac/tap/ecspressoでインストールできます
  • dockerが動く環境があること
    • macOSであれば Docker for Macをインストールしてください
  • aws-cliが使えるようにQA環境/本番環境用のcredentials設定が済んでいること
    • 一部aws-cliでAWSリソースを作る例にしていますが、もちろんマネジメントコンソールで実施しても問題ありません

具体的な値について

この記事内では下記値で記載します。
手順を実施する際には環境に合わせて読み替えてください。

  • AWSアカウントID
    • QA環境:123456789012
    • 本番環境:234567890123
  • AWSリージョン:ap-northeast-1
  • アプリ名:fargate-example
    • GitHubリポジトリ名/ECSクラスタ・サービスの名称も同じ
    • 分かりやすさのため、ECRリポジトリ名/IAMロール名のprefixとしてもアプリ名を使用します(後述)

アプリ動作環境構築

ECRリポジトリ作成

aws-cliを使用して{アプリ名}-{コンテナ名}という名前でコンテナ数分作成します。
今回の例では nginx/goappの2つ必要です。

nginx用リポジトリ作成例
# aws-cliのプロファイル、およびリージョンを指定
export AWS_PROFILE={プロファイル名}
export AWS_REGION=ap-northeast-1

aws ecr create-repository --repository-name fargate-example-nginx
実行結果
{
    "repository": {
        "repositoryArn": "arn:aws:ecr:ap-northeast-1:123456789012:repository/fargate-example-nginx",
        "registryId": "123456789012",
        "repositoryName": "fargate-example-nginx",
        "repositoryUri": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/fargate-example-nginx",
        "createdAt": "2020-06-17T09:36:06+00:00",
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": false
        }
    }
}

あとで使うのでrepositoryUriを控えておくこと・
同様にgoappも実行してください。

ECRリポジトリへ仮コンテナイメージPush

ECSクラスタ構築及び動作確認で必要となるので、暫定でプレーンのnginxコンテナイメージをECRにPushしておきます。

# nginxコンテナイメージ取得
docker pull nginx:1.17-alpine

# aws-cliのプロファイル、およびリージョンを指定
export AWS_PROFILE={プロファイル名}
export AWS_REGION=ap-northeast-1

ACCOUNT_ID=$(aws sts get-caller-identity | jq -r .Account)

# コンテナイメージをQA環境のECRにPush
aws ecr get-login-password |\
docker login --username AWS --password-stdin https://${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com
docker tag nginx:1.17-alpine ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/fargate-example-nginx:latest
docker push ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/fargate-example-nginx:latest

プロファイル名を切り替えてQA/本番の2回実行する。

CloudFormationスタック作成

CloudFormationの画面にアクセスし、スタック作成を開始します。
cfn-alb-ecs.ymlを指定して、次へ

スクリーンショット 2020-06-18 15.44.26.png

下記の通りに指定して 次へ
スクリーンショット 2020-06-18 15.44.26.png

再度 次へ 押して
AWS CloudFormation によって IAM リソースがカスタム名で作成される場合があることを承認します。
のチェックボックスにチェック入れて スタックの作成 を押してしばらく待ちます

スタック一覧に CREATE_COMPLETEが表示されていれば完了です。(4,5分かかります)

スクリーンショット 2020-06-18 18.58.53.png

出力タブをクリックし、ALB URLに表示されているリンクをクリックしてみてください。
スクリーンショット 2020-06-18 19.02.21.png

すると下記のようなnginxの画面が表示されるはずです。
スクリーンショット 2020-06-18 19.04.24.png

上記の作業をQA/本番両環境で実施してください。

↑ここまでがアプリ動作環境の構築手順です。

↓ここから先がGitHubActionsを使ったCD設定の手順・解説です。

デプロイ用IAMユーザ作成

GitHubActionsからECSにデプロイするための権限を持ったアクセスキーが必要になるため、ここでIAMユーザを作成します。

まずIAMコンソールを開き、下記の通りに入力して次のステップボタンを3回押し最後にユーザーの作成ボタンを押します。
iam1.png

IAMアクセスキーとシークレットアクセスキーが表示されるので、控えておいてください。

次にIAMユーザー一覧から作成したユーザーを選択し、インラインポリシーの追加をクリックします。
スクリーンショット 2020-06-20 11.34.00.png

JSONタブをクリックし下記を入力

deployToECSポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "ecs:RegisterTaskDefinition",
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "ecs:UpdateService",
                "iam:PassRole",
                "ecs:DescribeServices"
            ],
            "Resource": [
                "arn:aws:ecs:ap-northeast-1:123456789012:service/fargate-example/fargate-example",
                "arn:aws:iam::123456789012:role/fargate-example-task-role",
                "arn:aws:iam::123456789012:role/ecsTaskExecutionRole"
            ]
        }
    ]
}

putImageToECR

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ecr:CompleteLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:InitiateLayerUpload",
                "ecr:BatchCheckLayerAvailability",
                "ecr:PutImage" 
            ],
            "Resource": [
                "arn:aws:ecr:ap-northeast-1:123456789012:repository/fargate-example-*" 
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "ecr:GetAuthorizationToken",
            "Resource": "*" 
        }
    ]
}

上記をQA環境/本番環境用の2回実施してください。(AWSアカウントIDの書き換えを忘れずに)

GitHubSecrets登録

GitHubのSettingsメニューからSecretsを選択し、シークレットを登録します。
登録した値は、GitHubActions内からセキュアに参照することができます。

スクリーンショッ2020-06-18 19.04.24.png

下記4つの設定を登録してください。

シークレット名 説明
AWS_ACCESS_KEY_ID_QA QA環境のアクセスキー
AWS_SECRET_ACCESS_KEY_QA QA環境のシークレットアクセスキー
AWS_ACCESS_KEY_ID 本番環境のアクセスキー
AWS_SECRET_ACCESS_KEY 本番環境のシークレットアクセスキー

自動デプロイ設定

GitHubActionsワークフローファイルをベースに解説、及び修正が必要な箇所について説明します。

fargate-example/github/workflows/deploy.yml
name: deploy
on:
  push:
    branches:
      - dummy
      #- master
      #- develop

jobs:

  deploy:
    name: deploy
    runs-on: ubuntu-latest

    steps:
    - name: set environment variables for QA
      if: github.ref == 'refs/heads/develop'
      env:
        AWS_ACCOUNT_ID: 123456789012
      run: |
        echo "::set-env name=AWS_ACCOUNT_ID::${AWS_ACCOUNT_ID}"

    - name: set environment variables for Production
      if: github.ref == 'refs/heads/master'
      env:
        AWS_ACCOUNT_ID: 234567890123
      run: |
        echo "::set-env name=AWS_ACCOUNT_ID::${AWS_ACCOUNT_ID}"

    - name: configure aws credentials for QA
      if: github.ref == 'refs/heads/develop'
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_QA }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_QA }}
        aws-region: ap-northeast-1

    - name: configure aws credentials for Production
      if: github.ref == 'refs/heads/master'
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ap-northeast-1

    - name: checkout
      uses: actions/checkout@v2

    - name: login to ecr
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1

    - name: build, tag, and push image to ecr
      id: build-image
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        IMAGE_TAG: ${{ github.sha }}
      run: |
        docker build -t fargate-example-nginx:latest nginx
        docker tag fargate-example-nginx:latest $ECR_REGISTRY/fargate-example-nginx:latest
        docker tag fargate-example-nginx:latest $ECR_REGISTRY/fargate-example-nginx:$IMAGE_TAG
        docker push $ECR_REGISTRY/fargate-example-nginx:latest
        docker push $ECR_REGISTRY/fargate-example-nginx:$IMAGE_TAG
        docker build -t fargate-example-goapp:latest goapp
        docker tag fargate-example-goapp:latest $ECR_REGISTRY/fargate-example-goapp:latest
        docker tag fargate-example-goapp:latest $ECR_REGISTRY/fargate-example-goapp:$IMAGE_TAG
        docker push $ECR_REGISTRY/fargate-example-goapp:latest
        docker push $ECR_REGISTRY/fargate-example-goapp:$IMAGE_TAG

    - name: download ecspresso
      env:
        ver: v0.13.6
      run: |
        curl -sLo ecspresso.zip \
          https://github.com/kayac/ecspresso/releases/download/$ver/ecspresso-$ver-linux-amd64.zip
        unzip ecspresso.zip
        mv $(ls -1 ecspresso-*-linux-amd64) ecspresso
      working-directory: ./deploy

    - name: deploy to ecs
      run: ./ecspresso deploy --config ecspresso.yml --tasks=-1
      working-directory: ./deploy

解説1)環境の分岐

    - name: set environment variables for QA
      if: github.ref == 'refs/heads/develop'
      env:
        AWS_ACCOUNT_ID: 123456789012
      run: |
        echo "::set-env name=AWS_ACCOUNT_ID::${AWS_ACCOUNT_ID}"

if: github.ref == 'refs/heads/develop' の部分でマージされたブランチを判定しています。
上記の場合は、マージされたブランチによって、環境変数AWS_ACCOUNT_IDの値を変えています。

解説2)aws-cliのcredentials設定

    - name: configure aws credentials for QA
      if: github.ref == 'refs/heads/develop'
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_QA }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_QA }}
        aws-region: ap-northeast-1

AWS公式のconfigure-aws-credentialsというActionsを使用して、アクセスキー認証情報を環境変数にセットします。
以降、その権限でaws-cliを使うことができるようになります。
ここでも前述の分岐が使われており、マージされたブランチによってQA/本番環境のどのAWSリソースにアクセスするかを切り替えています。

解説3)docker build,tag,push

    - name: build, tag, and push image to ecr
      id: build-image
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        IMAGE_TAG: ${{ github.sha }}
      run: |
        docker build -t fargate-example-nginx:latest nginx
        docker tag fargate-example-nginx:latest $ECR_REGISTRY/fargate-example-nginx:latest
        docker tag fargate-example-nginx:latest $ECR_REGISTRY/fargate-example-nginx:$IMAGE_TAG
        docker push $ECR_REGISTRY/fargate-example-nginx:latest
        docker push $ECR_REGISTRY/fargate-example-nginx:$IMAGE_TAG

docker buildしてtag付けしてECRにPushしています。
latest$IMAGE_TAGの2つタグ付け&Pushしている理由は、運用上個別に戻したい場合に備えて$IMAGE_TAGをつけています。
常に最新でよければlatestだけでも問題ないです。

解説4)ecspressoによるデプロイ

    - name: download ecspresso
      env:
        ver: v0.13.6
      run: |
        curl -sLo ecspresso.zip \
          https://github.com/kayac/ecspresso/releases/download/$ver/ecspresso-$ver-linux-amd64.zip
        unzip ecspresso.zip
        mv $(ls -1 ecspresso-*-linux-amd64) ecspresso
      working-directory: ./deploy

    - name: deploy to ecs
      run: ./ecspresso deploy --config ecspresso.yml --tasks=-1
      working-directory: ./deploy

download ecspressoの部分でecspressoバイナリをダウンロードしています。
deploy to ecsの部分でecspressoを実行しています。
--tasks=-1を指定することで、現在のタスク実行数を引き継いでくれます。(つまり、オートスケールに対応できます)

修正1)対象ブランチ指定

ブランチマージ時にワークフローが動く条件となるブランチを指定します。
下記差分の通りに修正します。

on:
  push:
    branches:
-      - dummy
-      #- master
-      #- develop
+      - master
+      - develop

これにより、develop/masterブランチへのマージでこのワークフローが動きます。

修正2)ECSに渡す環境変数を修正

ecspresso(+その先のコンテナ)に渡す環境変数を、環境(QA/本番)に応じて切り替えることができます。
ワークフローファイルの下記の部分を実際のAWSアカウントIDに修正します。
また、コンテナに渡す環境変数を追加したい場合は以下のGOAPP_TEST_VARを参考に追加してください。
Actionsでstepを跨いで有効な環境変数を設定する場合は ::set-env name=~を使います。 参考

    - name: set environment variables for QA
      if: github.ref == 'refs/heads/develop'
      env:
-       AWS_ACCOUNT_ID: 123456789012
+       AWS_ACCOUNT_ID: 123456789012
+       GOAPP_TEST_VAR: "test var in qa"
      run: |
        echo "::set-env name=AWS_ACCOUNT_ID::${AWS_ACCOUNT_ID}"
+       echo "::set-env name=GOAPP_TEST_VAR::${GOAPP_TEST_VAR}"

    - name: set environment variables for Production
      if: github.ref == 'refs/heads/master'
      env:
-       AWS_ACCOUNT_ID: 234567890123
+       AWS_ACCOUNT_ID: 234567890123
+       GOAPP_TEST_VAR: "test var in prod"
      run: |
        echo "::set-env name=AWS_ACCOUNT_ID::${AWS_ACCOUNT_ID}"
+       echo "::set-env name=GOAPP_TEST_VAR::${GOAPP_TEST_VAR}"

Actionsで指定された環境変数が、ecspresso実行時にタスク定義JSONに渡されるように修正します。

deploy/ecs-task-def.json
      "environment": [
        {
          "name": "GOAPP_HOST",
          "value": "127.0.0.1"
        },
+       {
+         "name": "GOAPP_TEST_VAR",
+         "value": "{{ must_env `GOAPP_TEST_VAR` }}"
+       },
        {
          "name": "TZ",
          "value": "Asia/Tokyo"
        }
      ],

上記のように、GitHubActionsのワークフローに、ブランチ(環境)ごとに環境変数を定義することで、ビルドコマンドやデプロイ先を切り替えたり、コンテナに渡す環境変数を切り替えることができます。

これらの修正が完了したら、developブランチにマージ(orプッシュ)してください。
GitHubサイト上のActionsタブを表示するとワークフローが動いていると思います。
下記は、デプロイまで正常修正した場合の表示です。(スキップされたステップ以外は緑の✔︎がつきます)

deploy.png

ALBのURLに再度アクセスしてみると、アプリが更新され表示が変わっている(リクエストヘッダ一覧が表示されている)と思います。
あとは developをmasterブランチにマージすると本番環境へのデプロイが動くと思います。

おまけ

既存ECSクラスタをCDする場合

今回は、ALB+ECSを作成するところからの手順でしたが、既存のECSクラスタをGitHubActionsのCDに対応させたい場合があります。
その場合は、ecspresso initを実行してください。既存環境の情報を吸い上げて設定ファイルを作成してくれます。

$ ecspresso init --region ap-northeast-1 --cluster fargate-example --service fargate-example --config ecspresso.yml
2020/06/22 17:47:39 fargate-example/fargate-example save service definition to ecs-service-def.json
2020/06/22 17:47:39 fargate-example/fargate-example save task definition to ecs-task-def.json
2020/06/22 17:47:39 fargate-example/fargate-example save config to ecspresso.yml

ecs-task-def.jsonに対して、go-configの形式で環境変数の埋め込めばOKです。
ecs-service-def.jsonは今回の使い方(タスク入れ替えのみ)であれば不要なので消して問題ありません。

デプロイに時間かかる場合

ALBのヘルスチェック閾値を調整するとecspressoがタスクを追加した時にターゲットグループへの追加が早くなります。
登録解除の遅延を短くした場合にも、デプロイが早くなります。
(運用上問題のない値にしてください)

まとめ

GitHubActionsは、環境変数の扱いなど少しクセありますが、慣れてしまえばCircleCIに劣らず使いやすいと思います。
またecspressoはシンプルでサイドカーにも対応していて使いやすいのでこれからも使っていこうと思います。

30
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
20