docker-composeで作成されるものの名前を明示的に指定する方法

## 背景

docker-composeを利用するときに、コンテナ・ホスト名・データボリューム・ネットワーク名などの名前を指定しないと、自動的に名前が生成される。
あくまでも個人的な見解だが、ルールを覚えない限りは以下のような面倒な点があると考える。

* 作成されたものの名前を確認する必要がある。
* 意図せずにデフォルトネットワークが作られる。
* コンテナ間通信するときのFQDNはコンテナ名がわからない。（これについては別ページで別途記載するが、サービス名でも通信できた）
* 自動生成されたデータボリュームがわからないので紐づけを確認する必要がある。<br>
  コンテナとデータボリュームの関係を確認するには以下の方法があるが、どちらもコマンド自体覚えるのが面倒。（他にもあるかもしれないし、alias作れと言われればその通りだが、ある程度覚えておかないと新しい環境で再度aliasの設定内容を調べないといけない） 
  * `docker inspect ${コンテナ名} | jq .[0].Mounts`
  * `docker ps --format "table {{.Names}}\t{{.Mounts}}"`

[docker-compose.ymlのバージョン3.5からネットワーク名も指定してできる](https://docs.docker.com/compose/compose-file/#name-1)ようになって、全部解消したのでここにまとめます。

## 目的（ゴール）

* 基本的に作成されるものの名前は全部、docker-compose.ymlで事前に定義できるようにする。
* docker-composeコマンド実行時に作成されていないデータボリュームやネットワークが自動生成されること。
* 意図しないデフォルトネットワークなどが作成されないこと。

## 前提とする環境

* DockerホストOS：CentOS7
* Docker version 18.09.6, build 481bc77156
* docker-compose version 1.24.0, build 0aa59064
* docker-compose.ymlのバージョン：3.5


## シンプルな定義で作成される名前

docker-composeを利用する場合に特に名前を指定しないと、以下のようになる。

* コンテナ名 => ${プロジェクト名}_${サービス名}_${連番}
* ホスト名   => CONTAINER ID
* データボリューム名 => 任意のID（たぶんuuidのハイフン無し版）
* ネットワーク名 => ${プロジェクト名}}_default（networksを定義しない場合）

※１．プロジェクト名とは、、、

* デフォルトではdockse-compose.ymlのカレントディレクトリ名
* `docker-compose -p ${プロジェクト名}`で上書きできる。
* 環境変数"COMPOSE_PROJECT_NAME"で上書きできる。

※参考：
- [docker-compose の -p オプション](https://docs.docker.com/compose/reference/#use--p-to-specify-a-project-name)
- [COMPOSE_PROJECT_NAME 環境変数](https://docs.docker.com/compose/environment-variables/envvars/#compose_project_name)

以下に違いをわかりやすくするために、シンプルなもので作成してみた結果を記載する。

```yaml:docker-compose.yml
version: '3.5'
services:
  web:
    image: nginx:stable-alpine

  db:
    image: postgres:alpine
```

作成結果を確認する。

```shell-session:結果
[docker-user@docker-host test]$ docker-compose up -d
Creating network "test_default" with the default driver
Creating test_web_1 ... done
Creating test_db_1  ... done
[docker-user@docker-host test]$ docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Networks}}\t{{.Mounts}}"
CONTAINER ID        NAMES               IMAGE                 NETWORKS            MOUNTS
3b4634c1b885        test_web_1          nginx:stable-alpine   test_default
09ab8eb433a8        test_db_1           postgres:alpine       test_default        4f6e06816684d8…
[docker-user@docker-host test]$ docker volume ls
DRIVER              VOLUME NAME
local               4f6e06816684d8e22994e30e7b4bbdce9a51a889e1b021c021f33a0326c36709
[docker-user@docker-host test]$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
18b99de55ca4        bridge              bridge              local
09fbaf5da94d        host                host                local
b5252395c879        none                null                local
ff1019402a0f        test_default        bridge              local
[docker-user@docker-host test]$
```

無いものを自動生成してくれるのはありがたいが、勝手に名前が付くと`docker`コマンドで作ったものなのか、自動生成されたものなか、何で使われているのかをいちいち調べるのが面倒！！

<font color="Red">**2023/11追記**</font>

と記載しましたが、名前を固定化することによるデメリットもあるので追記します。

名前を固定化すると、複数の開発案件を同時進行で開発をしたり、他の人が開発したものをレビューを兼ねて動作確認をするときに現在の環境を汚さずに動作確認をするために、プロジェクトを分けて別の環境（コンテナ郡）を同一ホストに用意したい場合には名前が重複してエラーになることがあります。

おそらくですが、こういった用途も想定して動的にプロジェクト名で名前が決まるような仕様になっていると思われるので、深く考えずに以下の名前を固定化する方法を採用すると複数のアプリバージョンの実行環境を作成したいときに問題になります。
名前を明示的に指定しつつ、複数環境を用意したい場合は、追加で用意する側ではdocker-compose.override.ymlなどを用意して必要な部分をオーバーライドして用意するとよいでしょう。

## すべての名前を明確に定義する方法（結論）

いろいろ過去の経緯を書こうと思ったが、面倒なので結論だけ書きます。
以下、サービス名とコンテナ名/ホスト名、docker-compose.yml上のボリューム識別子・ネットワーク識別子などは名前を同じにした方が理想的ですが、わかりやすくするためにあえて、別の名前にしています。

```yaml:docker-compose.yml
version: '3.5'
services:
  web:
    image: nginx:stable-alpine
    # コンテナ名を明示的に指定する
    container_name: web-container
    # ホスト名を明示的に指定する
    hostname: web-server
    ports:
      - 80:80
    restart: always
    # 明示的に所属するネットワークを指定する
    networks:
      # ここに指定するのは実際のネットワーク名ではなく、↓のnetworksの識別子
      - container-link

  db:
    image: postgres:alpine
    # コンテナ名を明示的に指定する
    container_name: db-container
    # ホスト名を明示的に指定する
    hostname: db-server
    ports:
      - 5432:5432
    restart: always
    # 明示的に所属するネットワークを指定する
    networks:
      # ここに指定するのは実際のネットワーク名ではなく、↓のnetworksの識別子
      - container-link
    volumes:
      # ここに指定するのは実際のボリューム名ではなく、↓のvolumesの識別子
      - postgres-data-volume:/var/lib/postgresql/data:rw

# ネットワーク定義
networks:
  # docker-composeで勝手にデフォルトネットワークが生成される予防。（自動で全コンテナが所属するbridgeを指定）
  default:
#    external:
#      name: bridge
#    => docker-composeのあるバージョンから「WARN[0000] network default: network.external.name is deprecated. Please set network.name with external: true 」というWARNINGが表示されるようになりました。以下にように書き換えてください。
    external: true
    name: bridge
  # コンテナ間通信用のネットワークセグメント
  container-link:
    # これが作成されるネットワーク名（同名がなければ自動生成される）
    name: docker.internal
    # 以下は書かなくてもよい（サンプルとして）
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: "172.20.100.0/24"

volumes:
  # postgresコンテナのデータ永続化用のデータボリューム（同名がなければ自動生成される）
  postgres-data-volume:
    name: postgres-data
    driver: local
```

作成結果を確認する。

起動する。

```shell-session
[docker-user@docker-host test]$ docker-compose up -d
Creating network "docker.internal" with driver "bridge"
Creating volume "postgres-data" with local driver
Creating web-container ... done
Creating db-container  ... done
```

コンテナ・ネットワーク・データボリュームが作られた。
コンテナを確認する。

```shell-session
[docker-user@docker-host test]$ docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Networks}}\t{{.Mounts}}"
CONTAINER ID        NAMES               IMAGE                 NETWORKS            MOUNTS
12749a07b048        web-container       nginx:stable-alpine   docker.internal
9e6356b75522        db-container        postgres:alpine       docker.internal     postgres-data
```

コンテナのホスト名を確認する。

```shell-session
[docker-user@docker-host test]$ docker exec web-container hostname
web-server
[docker-user@docker-host test]$ docker exec db-container hostname
db-server
```

データボリュームを確認する。

```shell-session
[docker-user@docker-host test]$ docker volume ls
DRIVER              VOLUME NAME
local               postgres-data
[docker-user@docker-host test]$ docker volume inspect postgres-data
[
    {
        "CreatedAt": "2019-05-23T13:04:36+09:00",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.project": "test",
            "com.docker.compose.version": "1.24.0",
            "com.docker.compose.volume": "postgres-data"
        },
        "Mountpoint": "/var/lib/docker/volumes/postgres-data/_data",
        "Name": "postgres-data",
        "Options": null,
        "Scope": "local"
    }
]
```

ネットワークを確認する。

```shell-session
[vagrant@docker-host test]$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
18b99de55ca4        bridge              bridge              local
60ecb2f64e55        docker.internal     bridge              local
09fbaf5da94d        host                host                local
b5252395c879        none                null                local
[vagrant@docker-host test]$ docker network inspect docker.internal | jq .[0].Containers
{
  "12749a07b048f8135e0f6e0d80269f04c5ed8f7f6652703566076d478d31ef37": {
    "Name": "web-container",
    "EndpointID": "c93123b6c2e1005857753f4508a7bdb40c5a73d5211932de9e0cc80854809f00",
    "MacAddress": "02:42:ac:14:64:03",
    "IPv4Address": "172.20.100.3/24",
    "IPv6Address": ""
  },
  "9e6356b75522ed423ec561a0d2c9dc4a1d3b5056c00f5f781800b1891d0e4a11": {
    "Name": "db-container",
    "EndpointID": "6c83fd8ec9b3080d928cc73cda6f26da639172be40116ef7118bc2806be19879",
    "MacAddress": "02:42:ac:14:64:02",
    "IPv4Address": "172.20.100.2/24",
    "IPv6Address": ""
  }
}
```

コンテナ間通信を確認する。

```shell-session
[vagrant@docker-host test]$ docker exec web-container ping -c 1 db-container.docker.internal
PING db-container.docker.internal (172.20.100.2): 56 data bytes
64 bytes from 172.20.100.2: seq=0 ttl=64 time=0.062 ms

--- db-container.docker.internal ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.062/0.062/0.062 ms
[vagrant@docker-host test]$ docker exec db-container ping -c 1 web-container.docker.internal
PING web-container.docker.internal (172.20.100.3): 56 data bytes
64 bytes from 172.20.100.3: seq=0 ttl=64 time=0.060 ms

--- web-container.docker.internal ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.060/0.060/0.060 ms
```