最初に

DockerfileはどういうPathに配置するべきなのか?

そもそも一箇所に集めることが正しいのか?

自分ではそこそこいい方法だと信じてるんだけど
そもそも集めることが良いことなのか? ということを含めてちょっと考えてる

問題と出発点

昨今、複数のサービスが同一のRepositoryに入っていることが珍しくない
最初は各関連するサービスの直下に配置する。
最初のうちは機能する

.
└── server
    └── api
        └── Dockerfile

これが

管理ツールが必要だという話になって

.
└── server
    ├── admin
    │   └── Dockerfile
    └── api
        └── Dockerfile

debug serverが欲しくなってきた

.
└── server
    ├── admin
    │   └── Dockerfile
    ├── api
    │   └── Dockerfile
    └── debug
        └── Dockerfile

こうなる

OK、これくらいの環境ならまだ全然戦いについていける
composeにも build contextを "./server/admin" とかにするだけ楽勝
各サービスの直下にそれを構成するDockerfileが配置されてる、美しい世界だ。

次にtool directoryでもdockerを使いたいと言う話になった

.
├── server
│   ├── admin
│   │   └── Dockerfile
│   ├── api
│   │   └── Dockerfile
│   └── debug
│       └── Dockerfile
└── tool
    └── Dockerfile

まだまだいける探すのが若干めんどくさくなったが
$ find ./ -name "Dockerfile" でOK

最初の破綻

serverだけじゃなくclientのcodeも同じrepositoryで管理したい
とても自然なことだ。

.
├── client
├── server
│   ├── admin
│   │   └── Dockerfile
│   ├── api
│   │   └── Dockerfile
│   └── debug
│       └── Dockerfile
└── tool
    └── Dockerfile

問題は

共通の設定ファイルをclient directoryもしくはrepository topのcommonに置きたい!

こういう話が出てきたとき

.
├── client
├── common
│   └── config.yml
├── server
│   ├── admin
│   │   └── Dockerfile
│   ├── api
│   │   └── Dockerfile
│   └── debug
│       └── Dockerfile
└── tool
    └── Dockerfile

さぁ困ったぞ

Docker buildは COPY ../../common . のようにcontext境界を超えることが出来ない
美しい世界の破滅だ

build出来ないのは困るからしょうがない男らしくrepository topに配置しよう(これはこれで解決の一つだと思ってる ※1)

.
├── Dockerfile.admin
├── Dockerfile.api
├── Dockerfile.debug
├── Dockerfile.tool
├── client
├── common
│   └── config.yml
├── server
│   ├── admin
│   ├── api
│   └── debug
└── tool

これは一つの回答だが

  • Dockerfileの位置を容易に変えられない
  • .dockerignore を書きづらい という問題点がある

ある日
Docker使わない人から何あのファイル群怖い...
と言われ始めた(別に言われてないけど)


自分の解答

Multi stage Buildで最初に必要なファイルを集めよう!!

commonなどの共通で使うファイル、そもそもrepositoryの必要なファイルをbuilder containerとして最初にすべて集める。

最終的なdirectory 構造はこう

.
├── Dockerfile
├── client
├── common
│   └── config.yml
├── dockerfiles
│   ├── Dockerfile.admin
│   ├── Dockerfile.api
│   ├── Dockerfile.debug
│   └── Dockerfile.tool
├── server
│   ├── admin
│   ├── api
│   └── debug
└── tool

Top directoryのDockerfileで最初に必要なファイルをかき集めて
preprocessとかが必要なら書いてしまう(一意なBuildIDをここで生成するとかするとカッコイイぞ)

# 実行用のファイルをこのcontainerからそれぞれの実行containerでmulti staging buildでCOPYする
FROM alpine

ENV HOME=/source
WORKDIR $HOME

COPY . $HOME

このDockerfileを tag collector としてbuildしよう

各serviceのDockerfileで利用する場合は以下のようになる

# 先程作った 必要なfileが詰まったcontainer
FROM collector as collector

# 本命の実行container
FROM ruby:alpine-2.4 

ENV HOME=/api
WORKDIR=$HOME

COPY --from=collector /source/common ./
COPY --from=collector /source/server/api ./

# do something...

そこそこ美しい世界が戻ってきた気がする

各dockerfileが共通処理をダラダラと書く必要がなく
各serviceのDockerfile群に明日dockerfiles directoryから立ち退き要求が来たとしても
pathに依存しないのですぐにでも引っ越しできる。

問題点があるとしたらRepositoryがでかすぎて

debugで必要なファイルがすごい容量だがapiではいらないみたいなケース

各Dockerfileが関心のないファイル群が大きすぎた場合にツライ という点かなと思ってる

この方法が絶対に正しいとは思ってないが一つの回答なのかなーと思っている
みんなどうしてんだろ?

GCCBとかAWS codebuildとかbuild service使って明確にbuildとruntime container分けたいってのはもちろんあるとして Dckerfileだけである程度完結したいという気持ちもある

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.