Gemcook Tech Blog
🐬

Github ActionsでECS on EC2へデプロイするハンズオン

2024/12/18に公開

こんにちは!株式会社Gemcookのバックエンドエンジニアのshunです。

突然ですが皆さんはECS上でコンテナを実行するインフラストラクチャはFargateを選択していますか?それともEC2を選択していますか?

最近はFargateのアップデートも進み、より便利にサーバレスで利用できるFargateが選ばれるケースも多いかと思いますが、プロジェクトの要件によってはEC2が選択されるプロジェクトももちろん多くあるかと思います!

ちなみにFargateはサーバレスで便利な反面、数年前まではセキュリティの観点からコンテナへログインがでない仕様だったため緊急時のトラブルシューティングが難しいという観点もあり、弊社でもEC2を選択しているプロジェクトがあります。そのため、今回は業務でも触る機会のあるECS on EC2を改めて自分で構築して理解を定着させたいと思います👍

※現在ではFargateのコンテナにもログインできるような仕様になっているため[1]、弊社でもECS上のコンテナ実行環境のファーストチョイスはFargateになりました。

前置き

このセクションではハンズオンを読み進めていただく上での前提条件や、完成したworkflowファイルを先に掲載していますので、ハンズオンを読み進めていただく前に是非ご確認ください。

ハンズオンの構成図

構成は以下です。大きな工程としてはmainブランチへマージ(もしくはプッシュ)すると、最新のGitHubのリモートリポジトリのイメージがビルドされECRにプッシュされます。その後、更新されたタスク定義が実行され、ECS内でコンテナがデプロイされる仕組みを構築していきます。

GitHub Actionsのworkflowテンプレートをベースにします

GitHub ActionsでECSへデプロイするworkflowファイルはGitHubページからテンプレートを取得できます。今回のハンズオンも公式のテンプレートをベースにしていますが、この記事では順を追って作成するので先にテンプレートをリポジトリに作成しておかなくてもOKです。テンプレートをベースに編集を加えていく場合は以下を参考にGitHubページからテンプレートを取得できますので良ければご参照ください。

ECS on EC2へデプロイするGitHub Actionsのworkflowテンプレート

Actions > New workflowボタン

ECS検索しConfigureボタン

Commit changes...ボタン

先にハンズオンの完成版のworkflowを見たい方へ

今回の完成版のworkflowを先に確認したい方はこちらでご確認いただけます。今回のハンズオンでは、AWSへの認証環境変数やタスク定義の取り扱いはGitHub Actionsのworkflowのテンプレートに準じていますので、もしご活用される際は適宜ご自身のプロジェクトに置き換えてください😌

完成版のGitHub Actionsのworkflow
name: Deploy to AWS

# トリガー条件
# このワークフローは、mainブランチにマージ(変更がプッシュ)されたときに実行されます。
on:
  push:
    branches: ["main"]

# 環境変数の設定
# 必要な情報を環境変数として指定。
# (ハンズオンでは、AWSリージョン、リポジトリ名、クラスター、サービス名、タスク定義ファイル名などの順次追加していきます)
env:
  AWS_REGION: ap-northeast-1
  ECR_REPOSITORY: testrepo
  ECS_CLUSTER: test-cluster
  ECS_SERVICE: test-service
  CONTAINER_NAME: testrepo
  ECS_TASK_DEFINITION: testrepo-task-revision1.json

# ジョブの構成
# deployという名前のジョブを設定し、Ubuntu環境でGitHub Actionsを実行します。
jobs:
  deploy:
    name: deploy
    runs-on: ubuntu-latest

    steps:
      # リポジトリのチェックアウト
      # actions/checkoutを使用して、GitHubリポジトリの内容をクローン。
      - name: Checkout
        uses: actions/checkout@v4

      # AWS認証情報の設定
      # aws-actions/configure-aws-credentialsを使用して、GitHubのシークレットに保存したAWSアクセスキーとシークレットキーを用いて認証。
      - 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: ${{ env.AWS_REGION }}

      # ECRへのログイン
      # aws-actions/amazon-ecr-loginを使用して、Amazon ECRにログイン。
      # (このステップで取得したレジストリ情報は後続のステップで使用されます)
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      # Dockerイメージのビルドとプッシュ
      # docker buildコマンドでDockerイメージをビルドし、ECRリポジトリにプッシュ。
      # 成功後、ビルドしたイメージのタグを出力として保存します。
      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        run: |
          docker build --no-cache -t $ECR_REGISTRY/$ECR_REPOSITORY:latest .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
          echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:latest" >> $GITHUB_OUTPUT

      # タスク定義の更新
      # aws-actions/amazon-ecs-render-task-definitionを使用して、新しいDockerイメージを指定したタスク定義ファイルを更新。
      # これにより、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: ${{ env.ECS_TASK_DEFINITION }}
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ steps.build-image.outputs.image }}

      # デプロイの実行
      # aws-actions/amazon-ecs-deploy-task-definitionを使用して、更新されたタスク定義を実行しECSにデプロイ。
      # この際、強制的に新しいデプロイを開始する設定を有効にしています。
      - 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: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: false
          force-new-deployment: true

AWSの認証に関する補足
セクションの冒頭で記載しましたが今回はGitHub Actionsのworkflowのテンプレートをベースにしているため、AWSのアクセスキーシークレットアクセスキーを活用する方式を採っています。

OpenID Connect (OIDC)方式で認証を行いたい方はこちらの記事でもわかりやすく紹介されているので是非ご参照ください。

https://zenn.dev/kou_pg_0131/articles/gh-actions-oidc-aws

※弊社でもセキュリティ強化の観点から順次OpenID Connect (OIDC)方式へ切り替えているため、このハンズオンも別の機会にブラシュアップしたいと思います!

どの端末、サービス上で操作するかの目印

セクションの冒頭でどの環境で操作するかを明記します。ハンズオンの際は参考にしてください。

例: AWSマネジメントコンソール上で操作の場合 ↓

【1】GitHub ActionsでAWSにログインしよう

では早速ハンズオンを開始します!
【1-*】では、まずはGitHub ActionsでAWSにログインするところまで進めます。

【1-1】AWSのアクセスキーとシークレットアクセスキーを作成

今回はGitHub ActionsからAWSへの認証にはAWSのアクセスキーシークレットアクセスキーを活用する方式を採用するため、AWSマネジメントコンソールで以下手順で作成していきます。

IAM > ユーザー画面で対象のユーザーを選択してください。

セキュリティ認証情報タブを選択し、アクセスキーを作成ボタンを押します。

画面を進めると、アクセスキーとシークレットアクセスキーを作成できます。画面にも表示されますが、あとでわからなくなると困るのでcsvでダウンロードしておくと良いでしょう。(この情報はセキュアなため外部に漏れないようにしてください)

【1-2】GitHubにアクセスキーとシークレットアクセスキーを保存

前の章でAWSの認証に必要な情報は作成できたので、GitHub ActionsでAWSに接続できるか確認してみましょう。

先程AWSマネジメントコンソールで作成したアクセスキーシークレットアクセスキーをGitHub側で管理(保存)します。

GitHub Actionsを実行するリポジトリのSettings > Secrets and variables > Actioins を選択後、New repository secretボタンを押してシークレットを登録できる画面に進んでください。

NameSecretを入力できる画面に遷移するので、以下の表を参考に入力し保存してください。2つの情報を保存するため保存作業は2回行います。

Name Secret
AWS_ACCESS_KEY_ID 先程作成したアクセスキーをペースト
AWS_SECRET_ACCESS_KEY 先程作成したシークレットアクセスキーをペースト

保存作業が完了し以下のような画面になればOKです!

【1-3】AWSへの認証をテスト

workflowファイルを作成

GitHub Actionsのworkflowsを実行するには、決められたディレクトリ構成でYAMLファイルを格納する必要があるので、まずは.github/workflows/ディレクトリを作成し任意のファイル名のYAMLファイルを作成します。今回はdeploy-aws.ymlというワークフロー用のファイルを作成しました。

ディレクトリ構成
+ ├── .github
+    └── workflows
+        └── deploy-aws.yml

セクションの冒頭に説明したように、このセクションではAWSにログインできるところまでのワークフローを作成します。(ECRへのイメージのプッシュや、ECSへのデプロイは【2】【3】セクションで行います)

deploy-aws.yml
+ name: Deploy to AWS
+ 
+ # トリガー条件
+ # このワークフローは、mainブランチにマージ(変更がプッシュ)されたときに実行されます。
+ on:
+   push:
+     branches: ["main"]
+ 
+ # 環境変数の設定
+ # 必要な情報を環境変数として指定。
+ # (ハンズオンでは、AWSリージョン、リポジトリ名、クラスター、サービス名、タスク定義ファイル名などの順次追加していきます)
+ env:
+   AWS_REGION: ap-northeast-1
+ 
+ # ジョブの構成
+ # deployという名前のジョブを設定し、Ubuntu環境でGitHub Actionsを実行します。
+ jobs:
+   deploy:
+     name: deploy
+     runs-on: ubuntu-latest
+ 
+     steps:
+       # AWS認証情報の設定
+       # aws-actions/configure-aws-credentialsを使用して、Githubのシークレットに保存したAWSアクセスキーとシークレットキーを用いて認証。
+       - 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: ${{ env.AWS_REGION }}
+ 
+       # IAMユーザー情報の取得
+       # aws iam get-userコマンドを使用して、現在使用しているAWS認証情報に紐付けられたIAMユーザーの詳細を取得します。
+       - name: execute AWS CLI
+         run: aws iam get-user

次の章に進む前に、上記のYAMLファイルを作成したらリモートリポジトリへプッシュしておきましょう。

workflowの動作確認

今回のworkflowはmainブランチにマージすることで実行されます。前回の章でローカルリポジトリで作成したworkflowファイルをプッシュしたと思うので、GitHub上でmainブランチへ向けたプルリクエストを作成しマージしてください。

mainブランチへマージしたらGitHub上のActionsタブを選択し、サイドメニューの実行したワークフローを選択しましょう。

ログを確認しworkflowファイルの最後に実行したaws iam get-userコマンドの出力結果が表示されていれば、無事GitHub ActioinsでAWSと認証できているのでOKです!

【2】ECRにイメージをプッシュしよう

AWS Elastic Container Registry (ECR) は、Dockerコンテナイメージを保存・管理するためのフルマネージドなサービスです。Docker HubのAWS版というイメージです。

【2-1】 ECRのリポジトリを作成

ECR > Repositoriesメニューを選択し、リポジトリを作成ボタンからプライベートリポジトリを作成します。今回はtestrepoというリポジトリを作成しました。

まだイメージのプッシュ前なので、testrepoの中身は空っぽでOKです。

【2-2】 ECRのリポジトリにイメージをプッシュ

Dockerfileを作成

ECRにはDockerイメージをプッシュするため、Dockerfileを追加します。

Dockerfile
FROM httpd:2.4
COPY test.txt /usr/local/apache2/htdocs/test.txt

また、このタイミングで最終的にECSへデプロイするtest.txtも一緒に作っておきましょう。

test.txt
Hello World!!
ディレクトリ構成
  ├── .github
  │   └── workflows
  │       └── deploy-aws.yml
+ ├── Dockerfile
+ └── test.txt

workflowファイルを修正

イメージをビルドし、ECRへプッシュする処理を追加します。また、【1-3】で記述したAWSとの認証ができているかテストするためのIAMユーザー情報を取得する処理は不要なので削除しておきます。

deploy-aws.yml
name: Deploy to AWS

# トリガー条件
# このワークフローは、mainブランチにマージ(変更がプッシュ)されたときに実行されます。
on:
  push:
    branches: ["main"]

# 環境変数の設定
# 必要な情報を環境変数として指定。
# (ハンズオンでは、AWSリージョン、リポジトリ名、クラスター、サービス名、タスク定義ファイル名などの順次追加していきます)
env:
  AWS_REGION: ap-northeast-1
+ ECR_REPOSITORY: testrepo

# ジョブの構成
# deployという名前のジョブを設定し、Ubuntu環境でGitHub Actionsを実行します。
jobs:
  deploy:
    name: deploy
    runs-on: ubuntu-latest

    steps:
+      # リポジトリのチェックアウト
+      # actions/checkoutを使用して、GitHubリポジトリの内容をクローン。
+      - name: Checkout
+        uses: actions/checkout@v4

      # AWS認証情報の設定
      # aws-actions/configure-aws-credentialsを使用して、Githubのシークレットに保存したAWSアクセスキーとシークレットキーを用いて認証。
      - 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: ${{ env.AWS_REGION }}

-      # IAMユーザー情報の取得
-      # aws iam get-userコマンドを使用して、現在使用しているAWS認証情報に紐付けられたIAMユーザーの詳細を取得します。
-      - name: execute AWS CLI
-       run: aws iam get-user
+      # ECRへのログイン
+      # aws-actions/amazon-ecr-loginを使用して、Amazon ECRにログイン。
+      # (このステップで取得したレジストリ情報は後続のステップで使用されます)
+      - name: Login to Amazon ECR
+        id: login-ecr
+        uses: aws-actions/amazon-ecr-login@v1
+
+      # Dockerイメージのビルドとプッシュ
+      # docker buildコマンドでDockerイメージをビルドし、ECRリポジトリにプッシュ。
+      - name: Build, tag, and push image to Amazon ECR
+        id: build-image
+        env:
+          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
+        run: |
+          docker build --no-cache -t $ECR_REGISTRY/$ECR_REPOSITORY:latest .
+          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest

上記のファイルを作成したらリモートリポジトリへプッシュしておきましょう。

GitHub ActionsでECRにプッシュできるか確認

GitHub上でmainブランチへ向けたプルリクエストを作成し、マージしてください。

先ほど空っぽだったECRのリポジトリにイメージがプッシュされていればOKです!

【3】ECS on EC2へデプロイしよう

ECSは、Amazon Elastic Container Service(Amazon ECS)の略で、AWS上でDockerコンテナのデプロイや運用管理を効率化するためのフルマネージドなコンテナオーケストレーションサービスです。

前のセクションでmainブランチへのマージをトリガーに、GitHub ActionsでECRにイメージをプッシュできました。このセクションではそのDockerイメージのコンテナの実行環境を作成していきます。

AWSマネジメントコンソール上でクラスタータスク定義サービスの順に作成し、その後、GitHub Actionsのworkflowファイルを修正する手順で進めます。

※以降のセクションではECS関連のAWSリソースは設定のみ掲載します。 クラスタータスク定義サービスに関する詳細は割愛するため事前に詳しく知りたい方は以下がおすすめです。

https://envader.plus/article/180

【3-1】クラスター作成

ECS > クラスターメニューを選択し、クラスターの作成ボタンから作成します。

設定

クラスター設定

項目 設定
クラスター名 test-cluster

インフラストラクチャ
Amazon EC2 インスタンスを選択してください。

項目 設定
プロビジョニングモデル オンデマンド
EC2 インスタンスタイプ t2.large (※1)
必要な容量 最小1~最大5 (※2)
SSHキーペア 今回は新しく作りました (※3)

Amazon EC2 インスタンスのネットワーク設定

項目 設定
パブリック IP の自動割り当て オンにする

補足

  • (※1)この後、タスク定義でCPU1Gとメモリ3Gのコンテナを起動することを指定します。GitHub Actionsでデプロイする際に一時的に2つのコンテナ(起動中のコンテナとこれから入れ替わるコンテナ)が共存するため、大きめのインスタンスタイプを指定しています。
  • (※2)最小1にしておかないとクラスター内のコンテナインスタンスが1つも起動しないので、最小は1にしておいてください。
  • (※3)あとでクラスターのコンテナインスタンス(ECS on EC2)にログインして、実行中のコンテナの中身を確認するのでSSH接続できるようにキーペアを指定してください。このハンズオンではキーペアのファイルはローカルの.ssh/に置いています。


クラスター作成後

【3-2】タスク定義

ECS > タスク定義メニューを選択し、新しいタスク定義の作成ボタンから作成します。

設定

タスク定義の設定

項目 設定
タスク定義ファミリー testrepo-task

インフラストラクチャの要件

項目 設定
起動タイプ Amazon EC2 インスタンス
ネットワークモード bridge
タスクロール ecsTaskExecutionRole

コンテナ

項目 設定
名前 testrepo
イメージURL {ご自身のアカウントID}.dkr.ecr.ap-northeast-1.amazonaws.com/testrepo:latest


タスク定義後

タスク定義のjsonファイルをダウンロード

後の工程で必要になるため、作成したタスク定義のjsonファイルをダウンロードしておいてください。

【3-3】サービス作成

ECS > クラスターメニューを選択し、【3-1】で作成したtest-clusterを選択した画面からサービスを作成します。

設定

環境

項目 設定
起動タイプ EC2

デプロイ設定

項目 設定
ファミリー testrepo-task
サービス名 test-service


サービス作成後

ECRのイメージがコンテナで実行されているか確認

サービスを作成して数分待つとタスクが実行されタスク定義で指定したECRのtestrepoリポジトリのイメージが、コンテナインスタンス(ECS on EC2)の中でコンテナ実行されているため、実際にコンテナインスタンス(ECS on EC2)へログインしてコンテナの中身を確認します。

それでは、まずはtest-clusterページのインフラストラクチャタブのコンテナインスタンスセクションから起動中のEC2を選択します。

コンテナインスタンス(ECS on EC2)の画面からパブリックIPをコピーします。今回は3.112.213.79でした。

ターミナルを開いたら【3-1】で指定したSSHキーがあるディレクトリへ移動し、コンテナインスタンス(ECS on EC2)へログインします。

# SSHキーがあるディレクトリへ移動
cd ~./ssh
# コンテナインスタンス(ECS on EC2)へ接続
ssh -i {SSHキーのファイル名}.pem ec2-user@3.112.213.79

コンテナインスタンス(ECS on EC2)へログインできたら、起動中のコンテナを確認します。実行のコンテナIDは4d72db491210でした。

[ec2-user@ip-10-0-0-58 ~]$ docker ps
CONTAINER ID   IMAGE                                                               COMMAND              CREATED          STATUS                 PORTS                                     NAMES
4d72db491210   640168413381.dkr.ecr.ap-northeast-1.amazonaws.com/testrepo:latest   "httpd-foreground"   41 minutes ago   Up 41 minutes          0.0.0.0:32780->80/tcp, :::32780->80/tcp   ecs-testrepo-task-7-testrepo-dea9b984a7cca8d37d00
c5af6e906148   amazon/amazon-ecs-agent:latest                                      "/agent"             4 hours ago      Up 4 hours (healthy)                                             ecs-agent
c5af6e906148のECSコンテナエージェントについて

ECSコンテナエージェント
一番下に表示されているc5af6e906148 amazon/amazon-ecs-agent:latestコンテナプロセスは、コンテナインスタンス(ECS on EC2)内でコンテナオーケストレーションを実現させるためのタスク管理などを行うシステムです。
そのため、タスク定義で起動するアプリケーションコンテナプロセス(今回は4d72db491210)とは別のシステムコンポーネントです。

【2-2】でECRにプッシュされたtestrepoリポジトリのイメージがコンテナ実行されているか確認するため、さらにコンテナに入り【2-2】のDockerFileで指定した位置のtest.txtの中身を確認してみます。

# コンテナに入りbashを起動
[ec2-user@ip-10-0-0-58 ~]$ docker exec -i -t 4d72db491210 bash

# コンテナ内でtest.txtの中身を表示
root@4d72db491210:/usr/local/apache2# cat /usr/local/apache2/htdocs/test.txt
Hello World!!

Hello World!!を確認できればOKです!

補足
※次の工程でもコンテナインスタンス(ECS on EC2)内でコンテナの状況を確認するため、一旦コンテナから出てコンテナインスタンス(ECS on EC2)に接続した状態にしておいてください。

root@4d72db491210:/usr/local/apache2# exit
exit

[ec2-user@ip-10-0-0-58 ~]$

【3-4】デプロイ

workflowファイルを修正

【3-2】でダウンロードしたタスク定義のjsonファイルをアップロードしてください。

ディレクトリ構成
  ├── .github
  │   └── workflows
  │       └── deploy-aws.yml
  ├── Dockerfile
  ├── test.txt
+ └── testrepo-task-revision1.json

workflowファイルにECSのクラスタータスク定義サービスの情報とECSクラスター内のコンテナインスタンス(ECS on EC2)にデプロイする処理を追加します。

deploy-aws.yml
name: Deploy to AWS

# トリガー条件
# このワークフローは、mainブランチにマージ(変更がプッシュ)されたときに実行されます。
on:
  push:
    branches: ["main"]

# 環境変数の設定
# 必要な情報を環境変数として指定。
# (ハンズオンでは、AWSリージョン、リポジトリ名、クラスター、サービス名、タスク定義ファイル名などの順次追加していきます)
env:
  AWS_REGION: ap-northeast-1
  ECR_REPOSITORY: testrepo
+  ECS_CLUSTER: test-cluster
+  ECS_SERVICE: test-service
+  CONTAINER_NAME: testrepo
+  ECS_TASK_DEFINITION: testrepo-task-revision1.json

# ジョブの構成
# deployという名前のジョブを設定し、Ubuntu環境でGitHub Actionsを実行します。
jobs:
  deploy:
    name: deploy
    runs-on: ubuntu-latest

    steps:
      # リポジトリのチェックアウト
      # actions/checkoutを使用して、GitHubリポジトリの内容をクローン。
      - name: Checkout
        uses: actions/checkout@v4

      # AWS認証情報の設定
      # aws-actions/configure-aws-credentialsを使用して、Githubのシークレットに保存したAWSアクセスキーとシークレットキーを用いて認証。
      - 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: ${{ env.AWS_REGION }}

      # ECRへのログイン
      # aws-actions/amazon-ecr-loginを使用して、Amazon ECRにログイン。
      # (このステップで取得したレジストリ情報は後続のステップで使用されます)
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      # Dockerイメージのビルドとプッシュ
      # docker buildコマンドでDockerイメージをビルドし、ECRリポジトリにプッシュ。
+     # 成功後、ビルドしたイメージのタグを出力として保存します。
      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        run: |
          docker build --no-cache -t $ECR_REGISTRY/$ECR_REPOSITORY:latest .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
+          echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:latest" >> $GITHUB_OUTPUT

+      # タスク定義の更新
+      # aws-actions/amazon-ecs-render-task-definitionを使用して、新しいDockerイメージを指定したタスク定義ファイルを更新。
+      # これにより、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: ${{ env.ECS_TASK_DEFINITION }}
+          container-name: ${{ env.CONTAINER_NAME }}
+          image: ${{ steps.build-image.outputs.image }}
+
+      # デプロイの実行
+      # aws-actions/amazon-ecs-deploy-task-definitionを使用して、更新されたタスク定義を実行しECSにデプロイ。
+      # この際、強制的に新しいデプロイを開始する設定を有効にしています。
+      - 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: ${{ env.ECS_SERVICE }}
+          cluster: ${{ env.ECS_CLUSTER }}
+          wait-for-service-stability: false
+          force-new-deployment: true

最後にtest.txtの中身を変更しておきます。

test.txt
- Hello World!!
+ Hello ECS!!

上記の対応が済んだらリモートリポジトリへプッシュしてください。

GitHub Actionsを実行

mainブランチへマージし、GitHub Actionsが実行します。(成功すると黄色からグリーンに変わります)

ECRに新しいイメージが追加されているか確認

ECRに新しいイメージがプッシュされていることを確認します。

コンテナインスタンス(ECS on EC2)内の実行中のコンテナが切り替わるか確認

先程コンテナインスタンス(ECS on EC2)へ接続したターミナルに戻り、コンテナの実行状況を確認します。【3-3】のサービス定義後に作成された4d72db491210コンテナに加え、新たにもう一つb6a9e293ee7dコンテナが起動しています。

[ec2-user@ip-10-0-0-58 ~]$ docker ps
CONTAINER ID   IMAGE                                                               COMMAND              CREATED             STATUS                 PORTS                                     NAMES
b6a9e293ee7d   640168413381.dkr.ecr.ap-northeast-1.amazonaws.com/testrepo:latest   "httpd-foreground"   38 seconds ago      Up 38 seconds          0.0.0.0:32781->80/tcp, :::32781->80/tcp   ecs-testrepo-task-8-testrepo-eaefba87c5f39dae5300
4d72db491210   4aa03472352c                                                        "httpd-foreground"   About an hour ago   Up About an hour       0.0.0.0:32780->80/tcp, :::32780->80/tcp   ecs-testrepo-task-7-testrepo-dea9b984a7cca8d37d00
c5af6e906148   amazon/amazon-ecs-agent:latest                                      "/agent"             4 hours ago         Up 4 hours (healthy)

しばらくして再度docker psすると起動中のコンテナから4d72db491210が消え、b6a9e293ee7dの1つのコンテナだけ実行していることがわかります。

[ec2-user@ip-10-0-0-58 ~]$ docker ps
CONTAINER ID   IMAGE                                                               COMMAND              CREATED          STATUS                 PORTS                                     NAMES
b6a9e293ee7d   640168413381.dkr.ecr.ap-northeast-1.amazonaws.com/testrepo:latest   "httpd-foreground"   58 seconds ago   Up 57 seconds          0.0.0.0:32781->80/tcp, :::32781->80/tcp   ecs-testrepo-task-8-testrepo-eaefba87c5f39dae5300
c5af6e906148   amazon/amazon-ecs-agent:latest                                      "/agent"             4 hours ago      Up 4 hours (healthy)

それでは、b6a9e293ee7dへ入りtest.txtの中身を確認してみましょう。

# コンテナへ入りBashを起動
[ec2-user@ip-10-0-0-58 ~]$ docker exec -i -t b6a9e293ee7d bash

#test.txtの中身を確認
root@b6a9e293ee7d:/usr/local/apache2# cat /usr/local/apache2/htdocs/test.txt
Hello ECS!!

test.txtの中身がHello World!!からHello ECS!!になり、無事GitHub Actionsを使って指定のECRのリポジトリの最新のイメージをデプロイできました!

動作確認後の後片付け

AWSリソース

無料利用枠はありますが以下は課金が発生するサービスのため適宜削除してください。

  • ECSのクラスター
  • ECRのリポジトリ
  • ユーザー (ハンズオン用に作成していた場合)

GitHub

【1-2】でGitHubに保存したアクセスキーシークレットアクセスキーは不要であれば削除してくおくと安心です。

まとめ

今回はGitHub ActionsのworkflowでECSへECRのリポジトリの最新のイメージをデプロイしました!GitHub Actionsは公式のテンプレートや連携するサービスのためのactionもある程度用意されているため使いやすい印象です。また、アプリケーションコードとデプロイ周りの処理を同じリポジトリで管理できるのは、管理コスト面でもアドバンテージがあると感じました。

今回はAWSマネジメントコンソールベースでリソースを作りましたが、今後はこの辺りをCDKで置き換えたり、簡単なGoのAPIを作りGitHub ActionsのworkflowにCIを追加したり、ELB立てて動的ポートマッピングに対して外部からの通信を受けれる構成にしたり、より実務に近い構成に育てていきたいと思っています!👍

脚注
  1. New – Amazon ECS Exec による AWS Fargate, Amazon EC2 上のコンテナへのアクセス ↩︎

Gemcook Tech Blog
Gemcook Tech Blog

Discussion