🐳
レンティオでのGitHub Actions/AWS連携の事例
エンジニアのKIMIです。
ちょっと更新頻度が落ちてしまいましたが… 自分のためにも定期的にアウトプットしていこうと思っています。
この記事では、GitHub ActionsでjemallocをインストールしたRubyイメージをビルドし、ECR Publicにプッシュするまでをご紹介します。
なぜ
レンティオではalpineを使っていますので、jemallocを別途make installする必要があります。
(slimにすれば?という声が聞こえてきそうですが、がんばります)
これをインストール済みイメージを使うことで、デプロイ時の数分の待ち時間をなくそうという魂胆です。
やることは次の通りで、
- OpenID Connectを使って、GitHub ActionsからAWSにアクセス
- GitHub Actionsでビルド
- ビルドが終わったらECRにプッシュ
1つひとつはドキュメントに書いてある通りなので難しいわけではありませんが、微妙にドキュメントとは違うこともやっていますので、そのあたり参考になればと思います😀
- AWS CDKで環境構築
(レンティオではAWS Copilotで作ったところ以外はAWS CDKで管理している) - マルチプラットフォームビルド
(ECSのARM化終わっていますが、ローカルのPCで若干古いモノが残っているので) - ECRはパブリックリポジトリを使う
(Rubyとjemallocを秘密に扱う必要がないのと、パブリックリポジトリは無料利用枠の恩恵がある)
それでは、用意する3つのファイル(Dockerfile, CDKコード, GitHub Actionsワークフローファイル)を見ていきます
Dockerfile
- サービスで必要なライブラリを入れたり
bundle install
しているところを除いてDockerfileを分離し、docker build
するファイルを用意する
Dockerfile
FROM public.ecr.aws/docker/library/ruby:3.3-alpine
RUN apk --update --no-cache add --virtual build-dependencies curl make gcc musl-dev \
&& cd /tmp \
&& curl -L -O https://github.com/jemalloc/jemalloc/releases/download/5.3.0/jemalloc-5.3.0.tar.bz2 \
&& tar -xjf jemalloc-5.3.0.tar.bz2 \
&& cd jemalloc-5.3.0 \
&& ./configure \
&& make \
&& make install \
&& rm -fr /tmp/jemalloc-5.3.0.tar.bz2 /tmp/jemalloc-5.3.0 \
&& apk del --purge build-dependencies
AWS CDK
- GitHubのドキュメントアマゾン ウェブ サービスでの OpenID Connect の構成にある通り、OIDCプロバイダとロールを作る
- ついでにリポジトリも作る
OIDCプロバイダ
const provider = new iam.OpenIdConnectProvider(this, 'MyProvider', {
url: 'https://token.actions.githubusercontent.com',
clientIds: ['sts.amazonaws.com'],
})
const federatedPrincipal = new iam.FederatedPrincipal(
provider.openIdConnectProviderArn,
{
StringEquals: {
'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com',
},
StringLike: {
'token.actions.githubusercontent.com:sub': 'repo:my-org/my-repo:*',
},
},
'sts:AssumeRoleWithWebIdentity',
)
ECRパブリックリポジトリ
const publicRepositories = [
new ecr.CfnPublicRepository(this, 'MyRepository', { repositoryName: 'ruby' }),
// レンティオでは他に、pg_bigmインストール済みPostgreSQLのイメージなどもつくっている
]
IAMロール
- 必要最低限のPermissionは、後で出てくるamazon-ecr-loginのREADMEに書いてある通り
const role = new iam.Role(this, 'MyRole', {
roleName: 'ecr-access-from-github',
assumedBy: federatedPrincipal,
})
role.addToPolicy(
new iam.PolicyStatement({
resources: ['*'],
actions: ['ecr-public:GetAuthorizationToken', 'sts:GetServiceBearerToken'],
}),
)
role.addToPolicy(
new iam.PolicyStatement({
resources: publicRepositories.map((repo) => repo.attrArn),
actions: [
'ecr-public:BatchCheckLayerAvailability',
'ecr-public:CompleteLayerUpload',
'ecr-public:InitiateLayerUpload',
'ecr-public:PutImage',
'ecr-public:UploadLayerPart',
],
}),
)
GitHub Actions
こちらも公式ドキュメントがありますので、DockerのGitHub Actionsのドキュメント に沿って書いていきます。
ただ、プッシュするのはECRなのでDocker HubにログインしているところをECRにログインするように変える必要があります。
aws-actions/amazon-ecr-login
- CDKで作ったOIDCプロバイダを使って認証し、ECRにログインする
- Amazon ECR "Login" Action のREADMEに書かれている通り
- uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: us-east-1
# CDKで作成したIAMロールのARN
role-to-assume: arn:aws:iam::xxx:role/my-github-actions-role
- uses: aws-actions/amazon-ecr-login@v2
id: login-ecr
with:
registry-type: public
docker/build-push-action
- amd64, arm64用のイメージをビルド & プッシュ
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
まとめ
完成ファイル3つ
ruby-3.3/Dockerfile
ruby-3.3/Dockerfile
FROM public.ecr.aws/docker/library/ruby:3.3-alpine
RUN apk --update --no-cache add --virtual build-dependencies curl make gcc musl-dev \
&& cd /tmp \
&& curl -L -O https://github.com/jemalloc/jemalloc/releases/download/5.3.0/jemalloc-5.3.0.tar.bz2 \
&& tar -xjf jemalloc-5.3.0.tar.bz2 \
&& cd jemalloc-5.3.0 \
&& ./configure \
&& make \
&& make install \
&& rm -fr /tmp/jemalloc-5.3.0.tar.bz2 /tmp/jemalloc-5.3.0 \
&& apk del --purge build-dependencies
lib/github-oidc-stack.ts
lib/github-oidc-stack.ts
import { Stack, StackProps } 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 OpenidConnectStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props)
const provider = new iam.OpenIdConnectProvider(this, 'MyProvider', {
url: 'https://token.actions.githubusercontent.com',
clientIds: ['sts.amazonaws.com'],
})
const federatedPrincipal = new iam.FederatedPrincipal(
provider.openIdConnectProviderArn,
{
StringEquals: {
'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com',
},
StringLike: {
'token.actions.githubusercontent.com:sub': 'repo:my-org/my-repo:*',
},
},
'sts:AssumeRoleWithWebIdentity',
)
const publicRepositories = [
new ecr.CfnPublicRepository(this, 'MyRepository', { repositoryName: 'ruby' }),
]
const role = new iam.Role(this, 'MyRole', {
roleName: 'my-github-actions-role',
assumedBy: federatedPrincipal,
})
role.addToPolicy(
new iam.PolicyStatement({
resources: ['*'],
actions: ['ecr-public:GetAuthorizationToken', 'sts:GetServiceBearerToken'],
}),
)
role.addToPolicy(
new iam.PolicyStatement({
resources: publicRepositories.map((repo) => repo.attrArn),
actions: [
'ecr-public:BatchCheckLayerAvailability',
'ecr-public:CompleteLayerUpload',
'ecr-public:InitiateLayerUpload',
'ecr-public:PutImage',
'ecr-public:UploadLayerPart',
],
}),
)
}
}
.github/workflows/main.yml
.github/workflows/main.yml
on:
push:
branches:
- main
jobs:
pushToECR:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: us-east-1
role-to-assume: arn:aws:iam::xxx:role/my-github-actions-role
- uses: aws-actions/amazon-ecr-login@v2
id: login-ecr
with:
registry-type: public
- uses: docker/build-push-action@v6
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REGISTRY_ALIAS: my-registry-alias
REPOSITORY: my-repository-name
with:
context: ruby-3.3
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ env.REGISTRY }}/${{ env.REGISTRY_ALIAS }}/${{ env.REPOSITORY }}:3.3.5-alpine3.20
採用情報
レンティオでは絶賛、フルスタックエンジニアを募集しています!
Discussion