Github Actionsでget-diff-actionを用いてDocker Build時間を削減する
こんにちは!アルダグラムの開発ユニット長の田中です!
突然ですが、少しでもCIの実行時間を削減したいと思いませんか?
今回は、GitHub Actionsとget-diff-actionを活用してDockerビルド時間を削減する方法についてご紹介します。
TL;DR
get-diff-actionを使用してライブラリの変更差分を検知します。
変更差分がない場合はライブラリインストール済みのDockerイメージを使用してBuild時間を短縮します。
サンプルコード
具体的な例として、Railsアプリケーションを使用します。
on:
pull_request:
types: [ closed ]
branches:
- 'develop'
name: Deploy to Amazon ECS
jobs:
ecr-push:
name: Deploy
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true || github.event.inputs.manually == 'yes'
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1-node16
with:
role-to-assume: arn:aws:iam::(AWSのACCOUNT_ID):role/GithubActionsRole
role-session-name: GitHubActions-${{ github.run_id }}
aws-region: ap-northeast-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
# Gemfile.lockに差分があるかどうか
- name: Diff Gemfile.lock
uses: technote-space/get-diff-action@v6
id: check-diff
with:
PATTERNS: |
Gemfile.lock
# Gemfile.lockに差分があった場合、baseイメージをECRにPUSH
- name: base image Build, tag, and push image to Amazon ECR
id: build-base-image
if: env.GIT_DIFF
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: base-rails
IMAGE_TAG: latest
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f base.Dockerfile .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
# baseイメージを元にrailsイメージをECRにプッシュ
- name: rails image Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: rails
IMAGE_TAG: latest
run: |
docker login -u AWS -p $(aws ecr get-login-password) https://(AWSのACCOUNT_ID).dkr.ecr.ap-northeast-1.amazonaws.com
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f production.Dockerfile
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
FROM ruby:3.2-alpine
RUN apk upgrade --no-cache && \
apk add --update && \
apk add --no-cache --virtual build-dependencies build-base && \
apk add --no-cache --update \
curl \
curl-dev \
libstdc++ \
gcompat \
libxml2-dev \
libxslt-dev \
linux-headers \
mariadb-dev \
mysql-client \
mysql-dev \
ruby-dev \
ruby-json \
tzdata \
yaml \
yaml-dev \
build-base \
zlib-dev
RUN rm -rf /var/cache/apk/*
RUN gem install bundler
WORKDIR /app
RUN bundle config build.nokogiri -- \
--use-system-libraries \
--with-xml2-config=/usr/bin/xml2-config \
--with-xslt-config=/usr/bin/xslt-config
COPY Gemfile Gemfile.lock ./
RUN CFLAGS="-Wno-cast-function-type" \
BUNDLE_FORCE_RUBY_PLATFORM=1 \
bundle install
FROM (AWSのACCOUNT_ID).dkr.ecr.ap-northeast-1.amazonaws.com/base:latest
COPY . /app
解説
1. ライブラリの差分を検知
# Gemfile.lockに差分があるかどうか
- name: Diff Gemfile.lock
uses: technote-space/get-diff-action@v6
id: check-diff
with:
PATTERNS: |
Gemfile.lock
get-diff-actionを用いて、Gemfile.lockに変更があるかを検知します。
差分があるかどうかは env.GIT_DIFF
でbooleanで取得できます。
2. 差分があったらbaseイメージを作成
- name: base image Build, tag, and push image to Amazon ECR
id: build-base-image
if: env.GIT_DIFF
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: base-rails
IMAGE_TAG: latest
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f base.Dockerfile .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
手順1の結果はenv.GIT_DIFF
を使用して取得できます。
もし差分があれば base.Dockerfile
を元にライブラリインストール済みのDockerイメージを作成します。これをbaseイメージ用のECRにPUSHしておきます。
差分がない場合は、このステップはスキップされます。つまり、ライブラリインストールの時間をまるごと短縮できます。
3. baseイメージを元にDocker Imageを作成
# baseイメージを元にrailsイメージをECRにプッシュ
- name: rails image Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: rails
IMAGE_TAG: latest
run: |
docker login -u AWS -p $(aws ecr get-login-password) https://(AWSのACCOUNT_ID).dkr.ecr.ap-northeast-1.amazonaws.com
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f production.Dockerfile
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
手順2で作成したbaseイメージを元にして、Dockerイメージを作成し、ECRにPUSHすれば完了です!
簡単ですね!
最後に
今回はget-diff-actionを利用してDocker Buildの時間を削減する方法を紹介しました。
私たちのRailsアプリケーションのCI実行時間も、14~15分かかっていたものがたった3分で完了するように改善されました。
この手法はRailsアプリケーションだけでなく、他のフレームワークにも簡単に適用できるため、ぜひお試しください!
例えば、npmを使用しているアプリケーションの場合は以下のようになります。
- name: Diff package-lock.json
uses: technote-space/get-diff-action@v6
id: check-diff
with:
PATTERNS: |
package-lock.json
もっとアルダグラムエンジニア組織を知りたい人、ぜひ下記の情報をチェックしてみてください!
株式会社アルダグラムのTech Blogです。 世界中のノンデスクワーク業界における現場の生産性アップを実現する現場DXサービス「KANNA」を開発しています。 採用情報はこちら herp.careers/v1/aldagram0508/
Discussion
github actionsのymlファイルの冒頭で
on.pull_request.paths
にGemfile.lockを指定するのはいかがでしょうか外部のactionsに依存せず、条件が発動しなければそもそもactionsが実行されず、また記述もより簡易的になるかなと思いました。
もしすでに検討されていたらすみません