この記事はDocker Advent Calendar 2018の10日目の記事です。
はじめに
インフラチームとして提供する共通利用のDockerfileはDockerfileごとにリポジトリを作ってると沢山リポジトリが増えて結構大変です。しかし単一のリポジトリに複数のDockerfileを纏めると今度はCI/CDでDockerイメージをビルドするのが難しくなります。
この記事では複数のDockerfileを単一のリポジトリにまとめた時に、差分のあったDockerfileだけをビルドする構成について紹介します。
ディレクトリ構成
今回のサンプルの構成はgithubに上げてます。
https://github.com/kanga333/dockerfiles-monolepo
プロジェクトのディレクトリ構成は以下のような感じです。
dockerfiles-monolepo
├── Makefile
├── README.md
├── dockerfiles
│ ├── proxy-nginx
│ │ ├── Dockerfile
│ │ ├── Makefile
│ │ └── files
│ │ ├── nginx.conf
│ │ └── service.conf
│ └── python-lambda-builder
│ ├── Dockerfile
│ ├── Makefile
│ └── README.md
└── scripts
└── git-diff-make.sh
dockerfiles
の下にDockerfileごとのディレクトリを切っていくような構造になります。
構成のポイント
DockerfileのビルドをMakefileによって抽象化する
Dockerfile用のディレクトリにMakefileを用意してmake release
コマンドを実行すればdocker build & push
してくれるようにします。これによってリリース操作を抽象化します。
NAME := python-lambda-builder
REVISION := $(shell git rev-parse --short HEAD)
ORIGIN := $(shell git remote get-url origin | sed -e 's/^.*@//g')
RELEASE_TAGS := 1.0.0 $(REVISION)
TAGS := $(REVISION)
REGISTRY := index.docker.io
USER := kanga333
.PHONY: build
build:
@docker build \
--build-arg GIT_REVISION=$(REVISION) \
--build-arg GIT_ORIGIN=$(ORIGIN) \
--build-arg IMAGE_NAME=$(REGISTRY)/$(USER)/$(NAME) \
$(addprefix -t $(REGISTRY)/$(USER)/$(NAME):,$(TAGS)) .
.PHONY: push
push:
@for TAG in $(TAGS); do\
docker push $(REGISTRY)/$(USER)/$(NAME):$$TAG; \
done
.PHONY: release
release:
@make build TAGS="$(RELEASE_TAGS)"
@make push TAGS="$(RELEASE_TAGS)"
ちなみに以前dockerをビルドする際のMakefileについて記事を書いたのでこちらも合わせて参照いただければと思います。
https://qiita.com/kanga/items/aba6928996cb57daa460
差分のあったディレクトリを検知するShellスクリプト
このshellスクリプトは前回のコミットとの差分を比較して更新があったディレクトリでのみmakeを実行するshellスクリプトです。
#!/bin/bash
set -eu
if [ $# -ne 1 ]; then
echo "Please specify the argument of make." 1>&2
exit 1
fi
TARGETS=`find dockerfiles -maxdepth 1 -mindepth 1 -type d`
for TARGET in $TARGETS
do
HAS_DIFF=`git diff --diff-filter=ACMR --name-only HEAD\^ HEAD --relative=$TARGET | head -1`
if [ ! -z $HAS_DIFF ]; then
( cd ${TARGET} && make $1 )
fi
done
git diff --diff-filter=ACMR --name-only HEAD\^ HEAD --relative=$TARGET | head -1
によって対象ディレクトリで差分があるか無いかを判断するフィルターを実現しています。
あとはCIでマスターにマージされた際にこのshellスクリプトを呼ぶことで差分のあったディレクトリのみでビルドが走るようになります。
sh scripts/git-diff-make.sh release
注釈
ただしこの方法だと前回のコミットとの比較なのでマージが動いた事が差分検知の前提になります。
PRごとにビルドをしたい場合はMasterにマージした後にsh scripts/git-diff-make.sh
を動かすようにCIを工夫する必要があります。