【CI/CD】GitHub ActionsからECSにアプリケーションをデプロイする
はじめに
以前、以下の記事で、CloudFormationを使って自作のアプリケーションをECS上で動かすハンズオンを実施しました。
ここができたら、今度はデプロイまで自動化しちゃおうということで、GitHubにpushしたらECSにアプリケーションを自動でデプロイするパイプラインを作成することとしました。
構成
AWSの環境に関しては前回で構築が完了しているので、GitHub周りの設定をしていきます。

GitHub Acitonsとは
GitHubが提供するタスク自動化サービスとなります。GitHubのイベントをトリガーにしてワークフローを起動することができます。例えばmasterブランチにpushされた、Pull Requestが作成されたなどのイベントを検知して、deployを実行するなどのことができるようになります。
ハンズオン
GitHub Actionsの設定
-
まずはGitHubにログインし、リポジトリに移動し、そこの
Actionsタブをクリックします。

-
テンプレートが複数表示されます。

-
今回はDjangoのサンプルアプリケーションのDockerイメージを使うので、
Djangoを選択します。set up this workflowをクリックします。

-
デフォルトでファイルが出来上がるので任意のファイル名(ここでは
django.yml) を入力して、start commitを押下します。

これでworkflowが完成!簡単!
ワークフローファイルの変更(django.yml)
ローカルにPullしてdjango.ymlを変更していきます。
ファイルを以下のようにします。
name: Django CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
max-parallel: 4
matrix:
python-version: [3.8, 3.9]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Tests
run: |
python3 manage.py test
- name: Configure AWS Credentials
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: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPO_NAME }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ./aws/task-definition.json
container-name: django-app
image: ${{ steps.build-image.outputs.image }}
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: django-service
cluster: django-cluster
wait-for-service-stability: true

これをmasterブランチにpushすると、ローカルで編集したdjangoアプリケーションがそのままECRリポジトリにpushされて、さらに稼働中のアプリケーションに反映されます!
workflowファイルの詳細
上記で作成したファイルの詳細を見ていきましょう
以下はテンプレートですでに出来上がっている個所です。
nameはworkflowの名前なので任意で大丈夫です。onはworkflowの起動トリガーを表していて、今回の場合は、masterブランチへのpushと、masterブランチへのpull requestをトリガーにしてworkflowを起動することになります。
name: Django CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
以下に関してもテンプレートですでに出来上がっている個所です。
jobsが起動するジョブを記述するセクションです。
buildはjobsのIDを表していて実はbuildでなくても良かったりします。
strategyで、それぞれのジョブを実行する様々なバリエーションを定義できます。max-parallelでジョブの並列最大実行数を定義できますし、matrixでランタイムの選択をする子ができます。
runs-on: ubuntu-latestは、GitHub上でホストされているubuntuで処理が実行されることを意味しています。
stepsはジョブ内で実際に処理する内容をグルーピングします。
基本的にnameでワークフロー名を記述し、そこに処理したい内容をrunに記載していきます。useは、コミュニティで定義されたアクションを利用する際に記述します。uses: actions/checkout@v2はactions/checkout@v2という名前のコミュニティアクションのv2 を取得するようにジョブに指示するという意味になります。
withはアクションによって定義される入力パラメータのmapです。キーと値を持ちます。
jobs:
build:
runs-on: ubuntu-latest
strategy:
max-parallel: 4
matrix:
python-version: [3.8, 3.9]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Tests
run: |
python3 manage.py test
ここからは自分でアクションを記述していきます。
まずは、AWSのクレデンシャル情報を登録します。すでにコミュニティで準備している、aws-actions/configure-aws-credentials@v1を利用します。入力として、クレデンシャル情報を渡してあげる必要があります。
secretsで定義されている値AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEYを渡しています。secretsは、GitHubのレポジトリの画面で、[Setting] > [Secrets]から登録することができます。ちなみに、一度登録すると参照ができなくなるので値を忘れてしまった場合はUpdateする必要があります。
- name: Configure AWS Credentials
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
次に、ECRへのログイン処理と、ECRレポジトリへのコンテナイメージのPushを行います。
ECRへのログイン処理には、コミュニティアクションaws-actions/amazon-ecr-login@v1を利用します。また、ログイン処理の実行結果を利用するために、このアクションにはidでIDlogin-ecrを付与します。
そして、次のアクションで実際にコンテナイメージのbuildとpushを行います。ここでは、envを使って環境変数の設定を行います。
その中で、login-ecrの処理の結果取得されたRegistory情報を環境変数にセットしています。
IMAGE_TAGには、${{ github.sha }}をセットしていますが、これは実行時にそのワークフローの実行のトリガとなったコミットのハッシュ文字列に置き換えられます。
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPO_NAME }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
ここまででECRにコンテナイメージを行っているので、ここからは実際にECSタスクの更新処理を記述します。
まずはタスク定義の更新処理です。それにはコミュニティアクションのaws-actions/amazon-ecs-render-task-definition@v1を利用します。入力には、task-definition, container-image, imageを指定する必要があります。imageは上記でpushしたコンテナイメージを利用するため、build-imageの処理結果を取得して値をセットします。
task-definitionは実際にGitHubレポジトリにタスク定義ファイルを配置します。タスク定義ファイルはAWSのECSの画面から取得して加工したものを利用します。
最後に更新されたタスク定義をもとに、ECSにデプロイ処理を実行します。
こちらもコミュニティアクションaws-actions/amazon-ecs-deploy-task-definition@v1を利用します。入力には、task-definition, service, cluster, wait-for-service-stabilityを指定します。task-definitionにはtask-defで更新されたタスク定義を利用します。service, clusterには該当のECSサービス、ECSクラスター名を指定します。
wait-for-service-stabilityをtrueにすることで、ECSサービスの安定稼働まで実行完了を待つことができるようになります。
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ./aws/task-definition.json
container-name: django-app
image: ${{ steps.build-image.outputs.image }}
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: django-service
cluster: django-cluster
wait-for-service-stability: true
まとめ
今回はGitHub Actionsを利用して、GithubへファイルをPushすることで、コンテナイメージの作成からECSのデプロイまでのCICDパイプラインを作成しました。ほとんどがコミュニティアクションで構成することができるので簡単に構築することができました。
Discussion