💈

GitHub Actions To Amazon ECRをCDKで用意する

2023/06/06に公開

はじめに

GitHub ActionsでDocker Imageをビルドして、ECRにPushするやつないかな。と調べたら素晴らしい記事が出てきたので、あっという間に終わってしまった。

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

新たに書く必要もない気はしつつ、該当の記事はTerraformを前提に置かれていたため、AWS CDK派の人向けに読み替えた手順を公開する。

CDK

Github ActionsからECRへアクセスするために作成したいリソースは、以下だ。

  • IAMのOIDC Provider
  • Github Actionsに利用させるIAM Role
  • (任意)ECR Repository

これをCDKで表現するとこうなる。

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as iam from "aws-cdk-lib/aws-iam";
import * as ecr from "aws-cdk-lib/aws-ecr";

export class GhOidcStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const gitHubIdProvider = new iam.OpenIdConnectProvider(
      this,
      "GitHubIdProvider",
      {
        url: "https://token.actions.githubusercontent.com",
        clientIds: ["sts.amazonaws.com"],
      }
    );

    const ghUser: string = <GitHub User>;
    const ghRepo: string = <GitHub Repo>;
    const ghBranch: string = <GitHub Branch>;
    const ghUsercontentSub: string = `repo:${ghUser}/${ghRepo}:ref:refs/heads/${ghBranch}`;
    // const ghUsercontentSubAllBranch: string = `repo:${ghUser}/${ghRepo}:*`;
    const assumeRoleAction: string = "sts:AssumeRoleWithWebIdentity";

    const federatedPrincipal = new iam.FederatedPrincipal(
      gitHubIdProvider.openIdConnectProviderArn,
      {
        StringEquals: {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          "token.actions.githubusercontent.com:sub": ghUsercontentSub,
        },
	// どのブランチでも動かしたいとき
        // StringLike: {
        //   "token.actions.githubusercontent.com:sub": ghUsercontentSubAllBranch
        // }
      },
      assumeRoleAction
    );

    const oidcDeployRole = new iam.Role(this, "GitHubOidcRole", {
      roleName: "github-oidc-role",
      assumedBy: federatedPrincipal,
    });

    const repo = new ecr.Repository(this, "MyTempRepo", {
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteImages: true,
    });
    repo.grantPullPush(oidcDeployRole);
  }
}

CFn,Terraformとの差分

  • iam.OpenIdConnectProviderを用いることで、フィンガープリントなどを意識せず簡素に書ける。
  • repo.grantPullPushでほぼPolicy設計が終わるので、仮にCDKのスコープ外でもecr.Repository.fromRepositoryArnで取得できると便利。
CDKで作成されるIAM Policyの課題

ほぼ、と書いたのはCDKのgrantで作成すると、若干Actionが多いため。

割り切りで使っていい気もするが、厳密に設定したい場合は別途Policyを書くこと。

  • repo.grantPullPushで付与されるPolicy
"ecr:CompleteLayerUpload",
"ecr:UploadLayerPart",
"ecr:InitiateLayerUpload",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage"
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
  • 最小限のIAM Policy
    • 公式ドキュメント参照

https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-push.html

"ecr:CompleteLayerUpload",
"ecr:UploadLayerPart",
"ecr:InitiateLayerUpload",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage"

この課題もコンストラクト側にrepo.grantPushがあれば済む話なので、PullRequestを出している。

https://github.com/aws/aws-cdk/pull/25845

Dockerfile

特に何でもいいが、前回のRye記事が好評だったので調子に乗って作ったRyeが動くもの。

https://zenn.dev/watany/articles/f69db9e33d4427

Ryeを導入したDockerfile
# Use multi-stage builds
FROM ubuntu:jammy-20230522 AS build
# Set up non-root user
ARG UID=10001
RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/home/ryeuser" \
    --shell "/bin/bash" \
    --uid "${UID}" \
    ryeuser
# Update and install dependencies
RUN apt-get update && apt-get install -y \
    curl \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*
# Set working directory
USER ryeuser
WORKDIR /home/ryeuser
# Set Rye environment variables
ENV RYE_HOME="/home/ryeuser/.rye"
ENV PATH="$RYE_HOME/shims:$PATH"
# Install Rye
RUN curl -sSf https://rye-up.com/get | RYE_NO_AUTO_INSTALL=1 RYE_INSTALL_OPTION="--yes" bash
RUN rye init app && cd app \
    && rye add fastapi uvicorn[standard] \
    && rye sync
# Second stage to create a lean production image
FROM ubuntu:jammy-20230522
COPY --from=build /home/ryeuser /home/ryeuser
# Set up non-root user
ARG UID=10001
RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/nonexistent" \
    --shell "/sbin/nologin" \
    --no-create-home \
    --uid "${UID}" \
    ryeuser
# Switch to non-root user
USER ryeuser
WORKDIR /home/ryeuser/app
# Set Rye environment variables
ENV RYE_HOME="/home/ryeuser/.rye"
ENV PATH="$RYE_HOME/shims:$PATH"
ENTRYPOINT ["rye"]
CMD ["run", "python", "-c", "import fastapi"]

Github Actions

ほぼこれなので、割愛。
https://zenn.dev/kou_pg_0131/articles/gh-actions-ecr-push-image#5.-github-actions-ワークフローを作成する

現在はaws-actions/configure-aws-credentials@v2でも動くので書き換えて良さそう。

まとめ

GitHub Actions+AWSはかなり簡単なので、臆さず使っていきたい。用意するリソースもCDKでサッと作れてよかった。

Discussion