dely Tech Blog

クラシル・TRILLを運営するdely株式会社の開発ブログです

GitHub ActionsがAWSのクレデンシャルなしでAWSと連携できるようになったおかげでCIが改善した話

自己紹介

こんにちは、松岡です。

私はコマース事業部でインフラ兼バックエンドエンジニアをやっています。

器用貧乏に幅広くいろいろなことができることを売りにしてきましたが、本格派の方々が続々と加入しているため居場所がなくなりつつあります笑。

この記事はdelyアドベントカレンダー4日目の投稿です。

昨日は偉大なEMのtakaoさんの「delyで働くパパエンジニアの日常を紹介」でした。偉大なEMは偉大な父親でもありました。ぜひご覧ください。

tech.dely.jp

事業の紹介

最初に私が所属するコマース事業部を紹介させてください。

コマース事業部はクラシルデリバリーというサービスを運営しています。

「あらゆるスーパーマーケットで最短30分配送を実現するグロサリーデリバリーサービス」です。

詳細は次のプレスリリースをご覧ください。

prtimes.jp

クラシルデリバリーは大きく分類すると生鮮食品EC市場のサービスになります。

この生鮮食品EC市場は近年で最も熱い市場の1つではないでしょうか。

私たちもこの市場で刺激のある毎日を送っています。

こんな毎日にご興味がある方はぜひ次の採用情報をご覧ください。

https://delyjp.notion.site/dely-kurashiru-delivery-2c5199d383cd4ce8ac50942e65da2fb6delyjp.notion.site

CIを改善した話

それでは本題のCIについてお話しします。 *この投稿内でのCIの定義はコードをプッシュするとデプロイしてくれるやつをあらわします。

改善前の様子

コマース事業部はCIをAWSのCodePipeline、CodeBuild、CodeDeployで行っていました。以後、この3サービスをCode3兄弟と呼びます。

GitHubとAWS CodePipelineを連携してプッシュするブランチとデプロイする環境を1対1に関連づけます。具体的な例は次のとおりです。

  • mainブランチにプッシュするとプロダクション環境にコードをデプロイする。
  • test1ブランチにプッシュするとテスト環境その1にコードをデプロイする。
  • test2ブランチにプッシュするとテスト環境その2にコードをデプロイする。

開発の流れは次のとおりです。

  1. mainブランチからトピックブランチを作り、トピックブランチで開発する。
  2. 開発が終わったらtest1~test2のいずれかの1ブランチにトピックブランチをマージしてテスト環境にデプロイし、テスト環境でテストする。
  3. テストが終わったらmainブランチにトピックブランチをマージし、プロダクション環境にリリースする。

Code3兄弟は基本的には最高ですが、1つ不満がありました。

それは学習コストが高いことです(私は何度か心が折れました)。そしてそれによりCIを運用・改善するエンジニアが特定の人に限られていました。

CIには開発で行う様々なことを自動化できる力があるため、その開発を行っているアプリケーションエンジニア自身も運用・改善できたほうがよいと私は思っています。

そのほうがきっとよい改善が行われるでしょう。

ですが、、、学習コストの高さが壁になり、それが行いづらい状況でした。

GitHub Actionsは上記の不満を解決してくれるよい選択肢でしたが、私たちの事業部ではできる限りAWSのアクセスキーを発行しないという方針を持っているため、AWSと連携させていませんでした。

GitHub Actionsの大きなニュース

こんな状況でしたが今年の9月にうれしいニュースを知ります。AWSのアクセスキーなしでGitHub ActionsとAWSが連携できるようになりました。

GitHub ActionsとCode3兄弟の連携

さっそく私たちはこの方法でGitHub ActionsとCode3兄弟を連携します。

具体的なコードは次のとおりです。ECSにデプロイする例です。

GitHubとAWSの連携をいままではWebhookで行っていましたが、この機会にS3のバケット経由に変更しました。 こうするとWebhookに必要だったGitHubのアクセストークン等の管理も不要になります(ここも意外と大きな改善)。

GitHub Actions

on:
  push:
    branches:
      - 'main'

jobs:
  push-to-bucket:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Configure AWS
        uses: aws-actions/configure-aws-credentials@master
        with:
          aws-region: ap-northeast-1
          role-to-assume: arn:aws:iam::123456789012:role/github-actions
          role-session-name: <session-name>
      - run: aws sts get-caller-identity
      - uses: actions/checkout@v2
      - run: echo $GITHUB_SHA > github_sha
        env:
          GITHUB_SHA: ${{ github.sha }}
      - run: zip -r source.zip . -x *.git/*
      - run: aws s3api put-object --bucket bucket-to-deploy --key backend.zip --body source.zip
  • aws-actions/configure-aws-credentialsのバージョンは強気のmasterです。安定版をお勧めします。
  • echo $GITHUB_SHA > github_sha はDockerイメージのタグをコミットIDにしたいための手段です。 SourceをS3バケットにするとCodeBuildのCODEBUILD_RESOLVED_SOURCE_VERSIONがコミットIDではなく、S3オブジェクトのバージョンIDになります。

そこで上記のとおりgithub.shaをファイルに書き出してZIPファイルに格納しています。書き出したファイルは後続のCodeBuild内で行うDockerイメージのビルドに使います。

IAM ロールその他

すみません、Terraformな表現です。

resource "aws_iam_openid_connect_provider" "github_actions" {
  url = "https://token.actions.githubusercontent.com"
  client_id_list = [
    "sts.amazonaws.com"
  ]
  thumbprint_list = [
    "a031c46782e6e6c662c2c87c76da9aa62ccabd8e"
  ]
}

resource "aws_iam_role" "github_actions" {
  name = "github-actions"
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        "Sid" : "",
        Action = "sts:AssumeRoleWithWebIdentity",
        Principal = {
          Federated = aws_iam_openid_connect_provider.github_actions.arn
        }
        Condition = {
          StringLike = {
            "token.actions.githubusercontent.com:sub" = [
              "repo:dely-no/repository-dayo:*"
            ]
          }
        },
        Effect = "Allow"
      }
    ]
  })
}
  • thumbplint_lista031c46782e6e6c662c2c87c76da9aa62ccabd8eは公式サンプルの引用です。

  • 公式のサンプルもご覧ください。

github.com

Code3兄弟

CodePipeline

  • SourceはGitHub Actionsのワークフローで指定したS3バケットのオブジェクトです。
 - run: aws s3api put-object --bucket bucket-to-deploy --key backend.zip --body source.zip

CodeBuild

version: 0.2

phases:
  pre_build:
    commands:
      - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
      - echo $DOCKER_HUB_PASSWORD | docker login -u $DOCKER_HUB_USERNAME --password-stdin
      - COMMIT_ID=$(cat github_sha)
      - TAG=$ECR_REPOSITORY_URL:$COMMIT_ID
  build:
    commands:
      - docker build -f Dockerfile -t $TAG .
      - docker push $TAG

*出力アーティファクトに関する部分は省略しています。

連携方法は以上です。

結果発表〜〜〜

GitHub Actionsと連携した結果、CIの学習コストが下がったためか自分を含めアプリケーションエンジニアの方々がCIを運用・改善するようになりました(あっさり)。

アプリケーションエンジニアが作ったワークフローが爆誕し、運用されています。

いかがでしたでしょうか!

この投稿を最後まで読んでくださった皆様方に少しでも役に立てたらうれしいです!!!