Help us understand the problem. What is going on with this article?

複数のDockerイメージを単一リポジトリで管理する

More than 1 year has passed since last update.

この記事は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してくれるようにします。これによってリリース操作を抽象化します。

Makefileサンプル
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を工夫する必要があります。

kanga
うさんくさいインフラエンジニア
http://kangaat.hatenablog.com/
speee
株式会社Speeeは「解き尽くす。未来を引きよせる。」というミッションを実現すべく、中長期的な目線で企業価値を最大化させていくため、組織・事業のStyleを大切にした永続的な価値創造を目指しています。
https://www.speee.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away