docker-compose で一向にビルドがはじまらない、もしくは起動しない。はたまた忘れたころに起動する。

> `docker-compose up` や `docker-compose build` しても **`building <サービス名>` と出たまま先に進まない。ハングしているような感じ**で固まる。

Docker Desktop の設定（`Preferences`）で [`Resources`]-[`File sharing`] には、該当ディレクトリの親ディレクトリは含まれています。そもそも、さっきまで正常に動いていました。それが急に動かなくなったのです。何もしていないのに。

## TL; DR <sub><sup>（今北産業）</sup></sub>

1. <font color="darkorange">**大量のファイルを、裏で `Docker daemon` が読み込んでいる**</font>可能性があります。
2. `docker-compose` を使わずに `docker build -t test:local .` と Docker でビルドしてみて永遠と転送していたらビンゴです。
3. **`.dockerignore` で、大量ファイルのあるディレクトリを除外**しておき、`docker-compose.json` 内で別途マウントしましょう。

:::note info
🐒 `docker-compose` と `docker compose` の違い

2023/01/24 現在、`docker-compose` は `docker compose` のように、`docker` のサブコマンドとしての利用が推奨されています。元々 `docker-compose` は Python 言語による実装だったのですが、`docker-compose` v2 と同等の機能を Go 言語で実装したものが [compose](https://github.com/docker/compose) です。

Windows や macOS などで **Docker Desktop を利用している場合はデフォルトで内包されています**。Linux の場合はパケージマネージャーで追加インストールするか、`$HOME/.docker/cli-plugins` にバイナリを設置する必要があります。

`compose` サブコマンドは、動作が速くなっただけでなく、別途 Python のインストールも不要になりました。つまり `docker-compose run` は `docker compose run` と同等です。しかし、この記事では `docker-compose` の記載のままにします。適宜 `docker compose` と置き換えてお読みください。
:::

## TS; DR <sub><sup>（わかれば簡単なことに気づくまでの細かいこと）</sup></sub>

さっきまで `docker-compose run --rm <name_service>` で動いていた、機械学習の写経用コンテナが起動しなくなりました。「おかしいな」と、`docker-compose build --no-cache` とビルドしなおしても同じです。

**起動しないと言うより、止まったまま**の状態が続いたのです。

```shell-session:buildの場合
$ docker-compose build --no-cache
Building sample_service1 ← ここでハングってる
```

そこで、`Ctrl+c` で処理を止め、写経と言えば坊主ということで `--verbose` を呼んでみました。
:bellhop: ﾁﾘﾝ

```shell-session:build時の詳細
$ docker-compose build --no-cache
Building sample_service1 ← ここでハングってる
^CERROR: Aborting.

$ docker-compose --verbose build --no-cache
compose.config.config.find: Using configuration files: ./docker-compose.yml
...省略...
compose.service.build: Building sample_service1
compose.cli.verbose_proxy.proxy_callable: docker build <- (path='~/Desktop/Sample', tag='samp
le:local', rm=True, forcerm=False, pull=False, nocache=True, dockerfile=None, cache_from=None
, labels=None, buildargs={}, network_mode=None, target=None, shmsize=None, extra_hosts=None,
 container_limits={'memory': None}, gzip=False, isolation=None, platform=None) ← ここで止まる
```

どうも、上記 `compose.cli.verbose_proxy.proxy_callable:` で `docker-compose` が `docker build` を呼び出した処理で止まったままです。

Docker がイメージを `pull` する際、たまにネットワークが重かったり `pull` 先が重い場合に反応が遅れることはあるのですが、体感的にいつも以上の時間がかかっています。

### 切り分け（やったこと）:

- `docker-compose build --no-cache` → <font color=red>NG</font>（現象同じ）
- `docker-compose down` からの `docker-compose up` → <font color=red>NG</font>（現象同じ）
- `docker-compose stop` からの `docker-compose up` → <font color=red>NG</font>（現象同じ）
- `docker-compose ps` → <font color=red>NG</font>（起動中コンテナなし）
- `docker container prune -f` `docker image prune -f` からの `docker-compose build --no-cache` → <font color=red>NG</font>（現象同じ）
- `docker-compose logs` → <font color=red>NG</font>（`Attaching to` が出るだけ）
- ホスト OS の再起動 → <font color=red>NG</font>（現象同じ）
- `docker run --rm hello-world:latest` → <font color=aquablue>OK</font>（起動する）
- [`docker-compose` の "Hello World!"](https://qiita.com/KEINOS/items/43d9415e351d80f78c8b) → <font color=aquablue>OK</font>（起動する）

以上の切り分けにより、`docker` や `docker-compose` 自体は生きているものの「現在の `Dockerfile` や `docker-compose.yml` に問題がありそう」と判断できそうです。

しかし、それらファイルはいじっていません。となると、別途マウントさせてるスクリプトやイメージなどに原因がありそうです。

そこで、`docker-compose` を通さず Docker から直接イメージを 1 つ 1 つビルドしてみました。

```shell-session
$ docker build -t test:local .
Sending build context to Docker daemon xxxMB
```

すると、なんとなんと！上記の `xxxMB` が永遠とカウントアップされていきます。何やら大量のファイルがコンテキストに送られているではありませんか！

「そうかっ！！学習用のデータ、かっ！！」

:::note info
🐒 &nbsp;<strong>コンテキストとは</strong>
<sub><code>context</code> は日本語で「文脈」「文の前後関係」「事情」「背景」「状況」などといった意味があります。しかし、プログラムの世界では「文脈」ではピンとこないケースがあります。
コンテキストは、<code>con-</code> と <code>text</code> で構成され、<code>con-</code> の接頭辞は「一緒に」とか「付いに」などの意味を持ち、<code>text</code> は 「組み上げる」「編む」「作る」「構成する」の <code>*teks-</code> から来ています。つまり <code>context</code>は、<font color=darkorange>何かを「実行」「判断」「作成」する際に添えられた裏付け情報</font>のような意味合いになります。
そのため、<code>Sending <strong>build context</strong> to ...</code> とは「（Docker イメージの）<strong>ビルドに必要な情報</strong>を ... に送信中」という意味になります。
</sub>
:::

実は、機械学習用に用意した大量のデータ・ボリューム（`data` ディレクトリ）をコンテナにマウントさせていたのですが、軽量のデータで動作確認したいと思い**ディレクトリ名をリネームしたのを失念していました**。

元々 `data` ディレクトリ内のデータ量が多いため `.dockerignore` で除外して、ビルド後にマウントさていました。しかし、`data` が `data2` にリネームされたことにより、ビルド時にデータ（コンテキスト）として `data2` がメモリに読み込まれていたのです。

`.dockerignore` に記載済みだった `data` 同様、問題の `data2` を追加していつもどおり動くようになりましたとさ。とほほ

```conf:.dockerignore
data
data2
```

### `docker` と `docker compose` の違い

> `docker` はコンテナを個別に操作するコマンドで、`docker compose`（旧 `docker-compose`） は**同じマシン上で仮想ネットワークを作成し、その中で複数のコンテナを操作するツール**です。
>
> その上位として、同じネットワーク（LAN）内の複数のマシン間で仮想ネットワークを作成し、その中で複数のコンテナを操作するツールに `docker swarm` があります。さらにハイ・レベルな、複数ネットワーク（WAN）の間で仮想ネットワークを作成し、その中で複数コンテナの操作をするツールに `kubernetes` などがあります。
> 
> これらの、複数のコンテナや仮想ネットワークの作成・操作を支援するツールを、[オーケストレーション](https://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%BC%E3%82%B1%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3_(%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF))・ツールと呼びます。つまり `docker compose` もオーケストレーション・ツールの 1 つであるため、オーケストレーションの入門としては打ってつけです。

`docker` は、コンテナを作成＆起動すると明示しない限り「`docker` ネットワーク」と呼ばれる仮想ネットワークにコンテナを設置します。つまり、仮想 LAN 内にサーバ（docker のコンテナ）だけが存在するネットワークを作るということです。

この仮想ネットワークは `docker network create <ネットワーク名>` で作成することもでき、コンテナを起動する際に `--network <ネットワーク名>` オプションを付ると、そのネットワークに参加させることができます。

これは、サーバ間通信が必要なものをだけを特定のネットワークに入れておき、隔離することで余計なセキュリティの心配を減らすことができます。

例えば、Web サーバと DB サーバを同じネットワークに置いておき、Web サーバのポート（例えば 80 番ポート）だけを外部に開放（ポートを公開）する、などです。この場合、外部からは直接 DB サーバにはアクセスできません。

しかし、この方法だとコンテナやネットワークを起動するたびにコマンドを打たないといけないので、シェル・スクリプトなどで一発で起動させたいと思うことでしょう。

そんな時に使うのが `docker compose` です。

`docker compose` は複数コンテナを起動・操作するためのコマンドです。`docker-compose.yml` という YAML 形式の設定ファイルに記載された条件でコンテナを起動してくれます。

**`docker compose` を使うと `docker-compose.yaml` に記載されたコンテナはデフォルトで同じネットワークに設置**されます。

そのため、複数コンテナを起動してお互いを連携させたい場合は `docker compose` の利用が便利です。詳しくは以下の記事を参照ください。

- [環境変数でデータを渡すことの考察](https://qiita.com/KEINOS/items/518610bc2fdf5999acf2#%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E3%81%A7%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E6%B8%A1%E3%81%99%E3%81%93%E3%81%A8%E3%81%AE%E8%80%83%E5%AF%9F) | Docker で環境変数をホストからコンテナに渡す方法（ホスト OS 側からゲスト OS に渡す方法各種） @ Qiita

## 参考文献

- [dockerでコンテナが立ち上がらないときやってみること](https://qiita.com/mom0tomo/items/35dfacb628df1bd3651e) @ Qiita

## 関連文献

- `.dockerignore` が効かない？
    - [.dockerignoreが効かない？.gitignoreとは書き方が違うよ！](https://qiita.com/yucatio/items/f5d23043228cc35fc763#%E5%8F%82%E8%80%83%E8%B3%87%E6%96%99%E3%83%AA%E3%83%B3%E3%82%AF) @ Qiita
- `.gitignore` と違うの？
    - [[Git] .gitignoreの仕様詳解](https://qiita.com/anqooqie/items/110957797b3d5280c44f) @ Qiita

## あわせて読みたい

- [[Git] .gitignoreの仕様詳解](https://qiita.com/anqooqie/items/110957797b3d5280c44f) @ Qiita
- [.dockerignoreが効かない？.gitignoreとは書き方が違うよ！](https://qiita.com/yucatio/items/f5d23043228cc35fc763#%E5%8F%82%E8%80%83%E8%B3%87%E6%96%99%E3%83%AA%E3%83%B3%E3%82%AF) @ Qiita