GitLab CI/CDのECR/ECSデプロイ、実は公式イメージで超ラクできる話

サムネイル

はじめに

こんにちは、またお会いしましたね。24年度新卒のエンジニアリング部のお涼です。

4月には今年の新卒が入り、とうとう私も2年目ですが、今日も「ちょっとわかってきた気がする」という感覚を頼りに日々勉強しています。

先日、とあるプロジェクトでGitLab CI/CDを使って、ECSへのデプロイを自動化する設定を書きました。

その際、ちまちまとスクリプトを書いたんですが、結局ほとんどそれらは書く必要がなく、GitLabの公式イメージとして用意されていました。それを取り込むだけで良かったわけです。

使えるものは積極的に使おう。ということで、今回は、用意されていた公式イメージをちゃんと読まずに全部コマンド書いちゃった私が、GitLabの公式イメージを活用して、ECR/ECSへのデプロイを『楽して自動化』する技を紹介します。

「自動化したいけど書き方がわからない」「全部手書きでやってるけど、もっと楽できる?」そんな方のお役に立てたらうれしいです。

前提

1:この記事では、gilab-ci.ymlの書き方は解説しません。

2:ECS、ECR自体の説明や、以下のリソース準備の手順は解説しません。

  • ネットワークの設定
  • ECR
    • リポジトリ
  • ECS
    • クラスター
    • サービス
    • タスク定義

※解説を省略する代わりに、リソース準備の参考ドキュメントを貼っておきます。

3:今回、以下の環境変数を事前に設定しています。

キー
ROLE_ARNIAMロールのARN
AWS_DEFAULT_REGIONAWSのリージョン
CI_AWS_ECS_CLUSTERAWS ECSクラスター名
CI_AWS_ECS_SERVICEECSのサービス名
CI_AWS_ECS_TASK_DEFINITIONECSのタスク定義名
REPOSITORY_URLECRのリポジトリ名

さっそくイメージの説明

さて、早速ECSへのデプロイに使用できるイメージを紹介します。

AWSへのデプロイで使用可能なイメージは以下のcloud-deployリポジトリに公開されています。

https://gitlab.com/gitlab-org/cloud-deploy

AWSのベースイメージ

まずは先ほどのリポジトリの『ci』フォルダを確認してみましょう。jobの実行環境に使用できる、AWS CLIを含むイメージが提供されています。

ci/aws-base.gitlab-ci.yml

このaws-baseイメージで参照されている『”aws/base/Dockerfile”』を辿ってみましょう。

aws/base/Dockerfile

AWS CLIをインストールする処理が書かれていますね。

つまり私たちは、以下のようにジョブの実行環境のimageをaws-baseに指定することで、既にAWS CLIがインストールされたイメージを使用してジョブを実行できるのです。

例:

#アプリケーションのコンテナをビルドしECRにプッシュするjob

build:

  stage: build

  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest

  services:

    - docker:dind

  script:

    - aws ecr get-login-password | docker login --username AWS --password-stdin $REPOSITORY_URL

    - docker build --tag $REPOSITORY_URL:$CI_COMMIT_SHA .

    - docker push --quiet $REPOSITORY_URL:$CI_COMMIT_SHA

これで『pip install awscli』など書く必要がなくなります。aws コマンドを使用したいあらゆるジョブで活用できますね。

ECSデプロイ用のイメージ

続いて、先程のリポジトリの『ci』フォルダに戻ります。今度はaws-ecs.gitlab-ci.ymlを見てみましょう。これはECSへデプロイするジョブで使用できるイメージです。

ci/aws-ecs.gitlab-ci.yml

このaws-ecs で参照されている『”aws/ecs/Dockerfile”』を辿ってみましょう。

aws/ecs/Dockerfile

すると、aws/src/bin/ecs というファイルを参照しているため、さらに辿ってみます。

aws/src/bin/ecs

ECSタスク定義を更新するためのスクリプトですね〜〜〜。内容を見ていきましょう。

まず最初に目に入るupdate_task_definition関数にECSタスク定義を更新する処理が書いてあります。ほかにも色々な関数が定義されていて、一番最後にこれが。↓

option=$1

case $option in

  update-task-definition) update_task_definition ;

  stop-task) stop_task ;;

  get-task-hostname) get_task_hostname ;;

  *) exit 1 ;;

esac

このスクリプト、なかなかの働き者です。引数に応じて、ECSタスクに対して以下の3つの操作をこなしてくれます:

  1. update-task-definition(タスク定義の更新)
  2. stop-task(タスクの停止)
  3. get-task-hostname(ホスト名の取得)

たとえば、ジョブの中でECSのタスク定義を更新したいと思ったら、その処理は全部 update_task_definition 関数を使えば良いのです。

ジョブで ci/aws-ecs.gitlab-ci.yml イメージを使って、引数にupdate_task_definitionって渡してあげるだけで、ECSタスク定義の更新ができます。

私は最初、このリポジトリにおいてあるGitLabの公式イメージをちゃんと読み解かなかったので、処理を一から書きました。『最初から用意されてたの?もっと早く言ってよ!』と言いたくなりましたが、ちゃんと読んでなかった自分のせいです。

それでは、ECSデプロイのジョブが、公式イメージを使用することで、どれくらいスッキリしたかみてみましょう。

before

deploy_to_ecs:

  image: docker:latest

  script:

  - |

    aws ecs describe-task-definition --task-definition "$CI_AWS_ECS_TASK_DEFINITION" > current-task-def.json

    cat current-task-def.json | \

      jq 'del(.taskDefinition.revision, .taskDefinition.status, .taskDefinition.requiresAttributes, .taskDefinition.compatibilities, .taskDefinition.registeredAt, .taskDefinition.registeredBy, .taskDefinition.taskDefinitionArn) | .taskDefinition' | \

      jq --arg image "$REPOSITORY_URL:latest" '.containerDefinitions[0].image = $image' > new-task-def.json

    NEW_TASK_DEF_ARN=$(aws ecs register-task-definition --cli-input-json file://new-task-def.json | jq -r '.taskDefinition.taskDefinitionArn')

    echo "New task definition registered: $NEW_TASK_DEF_ARN"

    aws ecs update-service --cluster "$CI_AWS_ECS_CLUSTER" --service "$CI_AWS_ECS_SERVICE" --task-definition "$NEW_TASK_DEF_ARN"

after

deploy_to_ecs:

  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-ecs:latest

  script:

    - ecs update-task-definition

やった〜〜〜〜。こんなにイケメンになりました。脅威の15行→1行です。

さらに応用編として、これを『.deploy_to_ecs』として再利用可能なテンプレートジョブにすれば、複数環境へのデプロイジョブも、見通しよく書くことが可能です。

ちなみに$CI_TEMPLATE_REGISTRY_HOSTはGitLabで既に定義済みの変数です。値はCI/CDテンプレートが使用するレジストリのホストで、デフォルトはregistry.gitlab.com です。

公式ドキュメント:Predefined CI/CD variables reference | GitLab Docs 

全体像はこんな感じ

コンテナビルド〜デプロイまでの、公式イメージを活かした全体像はこんな感じでしょうか。

stages:

  - build

  - pre_deploy

  - deploy

  - post_deploy

variables:

  GITLAB_URI: https://gitlab.com

".retrieve-aws-credential": // OIDCで認証情報を取得するテンプレートジョブ

  before_script: &retrieve-aws-credential

      export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s"

      $(aws sts assume-role-with-web-identity --role-arn ${ROLE_ARN} --role-session-name

      "GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}" --web-identity-token ${GITLAB_OIDC_TOKEN}

      --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]'

      --output text));

".aws-base": // コンテナイメージをaws-baseに指定するテンプレートジョブ

  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest

  extends:

    - .retrieve-aws-credential

  id_tokens:

    GITLAB_OIDC_TOKEN:

      aud: $GITLAB_URI

.deploy_to_ecs: // ECSデプロイのテンプレートジョブ

  image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-ecs:latest

  extends:

    - .aws-base

  dependencies: []

  script:

    - ecs update-task-definition

build: // ビルド&ECRプッシュ

  stage: build

  extends: ".aws-base"

  services:

    - docker:dind

  variables:

    DOCKER_HOST: tcp://docker:2375/

    DOCKER_TLS_CERTDIR: ""

  script:

    - aws ecr get-login-password | docker login --username AWS --password-stdin $REPOSITORY_URL

    - docker build --tag $REPOSITORY_URL:$CI_COMMIT_SHA .

    - docker push --quiet $REPOSITORY_URL:$CI_COMMIT_SHA

deploy_dev: // 開発環境デプロイ

  stage: deploy

  environment:

    name: development

  extends:

    - ".deploy_to_ecs"

  rules:

    - if: $CI_COMMIT_BRANCH == "develop"

deploy_staging: // ステージング環境デプロイ

  stage: deploy

  environment:

    name: staging

  extends:

    - ".deploy_to_ecs"

  rules:

    - if: $CI_COMMIT_BRANCH =~ /^staging\//

    - if: $CI_COMMIT_BRANCH == "master"

    - if: $CI_COMMIT_BRANCH =~ /^hotfix\//

deploy_production: // 本番環境デプロイ

  stage: deploy

  environment:

    name: production

  extends:

    - ".deploy_to_ecs"

  rules:

    - if: $CI_COMMIT_TAG

おお〜〜〜っ。いい感じですね〜〜〜。

以前の私のように、公式イメージの存在を知らずにスクリプトが膨大になっている方。ぜひとも用意されたイメージを使用してみてください。

さいごに

ということで、今回はGitLabの公式イメージを使ってECR/ECSデプロイをラクにする方法をご紹介しました。

地味に面倒な処理も、公式イメージの活用やジョブの再利用でずいぶんカッコよくなります。そして純粋に、書くのラクです。

他にもGitLabでは、CICDパイプラインで使えるあらゆるジョブの公式テンプレートが以下のリポジトリにまとまっています。そちらもぜひ活用してみてください。

https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates

この記事が、どこかの yml 職人の肩こり軽減につながれば嬉しいです。ではまた・・・👋

ABOUT US
井出涼香エンジニア
蓄電池訪問販売で精神を削られ、23歳から専門学校に通いエンジニアに転身しました。お金より心を大事にする2024年新卒です。