散在するDockerfileを一箇所に集めたい

# 最初に

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

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

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

# 問題と出発点

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

~~~shell
.
└── 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をここで生成するとかするとカッコイイぞ)

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

ENV HOME=/source
WORKDIR $HOME

COPY . $HOME
~~~

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

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

~~~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だけである程度完結したいという気持ちもある