とことんDevOps | 日本仮想化技術のDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けする「とことんDevOps」では、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果、TipsなどDevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、コンテナ開発、IaCなど
読者登録と各種SNSのフォローもよろしくお願いいたします。

Docker Composeで既存のDockerネットワークにアプリケーションを追加する

今回はちょっとした小ネタです。

最近お仕事でKubernetesよりDockerを多く使っている私です(KubernetesもDockerも好きです)。 これまで、普段Dockerは決めた構成で動かすということしかやってきていませんでした。

具体的にいうと「こういう構成で動かす」と決まってから、そのマニフェストを作って環境作成という感じです。 その場合は必要最低限のマニフェストを記述してdocker compose up -dを実行すれば、足りない情報はDockerが補って環境を構築してくれるので特にハマることはないです。

一方、もうすでに動いているものに新しいアプリを追加するときはそうはいきません。 ちょっとだけハマったので、その時の話をシェアしたいと思います。

まずはアプリセットを作成

例えば、マニフェストは次のようなディレクトリー構成であると仮定します。

% tree ~/test
/Users/ytooyama/test
├── add
│   └── docker-compose.yml
├── docker-compose.yml
└── web
    ├── Dockerfile
    └── nginx.conf

そして、まずはこんなマニフェストでアプリケーションを展開したとします。

services:
  web:
    build: ./web
    ports:
      - "80:80"
    networks:
      - app-network
    depends_on:
      - db
  db:
    image: postgres:16.6
    environment:
      POSTGRES_USER: pguser01      
      POSTGRES_PASSWORD: strongpg5656#
    ports:
      - "5432:5432"
    networks:
      - app-network
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:
networks:
  app-network:

起動してみます。問題なく起動しました。

% docker compose up -d
% docker compose ps
NAME         IMAGE           COMMAND                   SERVICE   CREATED          STATUS          PORTS
test-db-1    postgres:16.6   "docker-entrypoint.s…"   db        18 seconds ago   Up 17 seconds   0.0.0.0:5432->5432/tcp, :::5432->5432/tcp
test-web-1   test-web        "/docker-entrypoint.…"   web       18 seconds ago   Up 17 seconds   0.0.0.0:80->80/tcp, :::80->80/tcp

Docker Composeでは書かなかった内容は、適当なランダム名で新規作成されます。今回はボリュームとネットワークの名前は指定したので、その名前でDockerボリュームとDockerネットワークが作成されます。

アプリを既存のネットワークに追加

いよいよ本題です。

前に構築済みのアプリケーションと同じネットワークに、別のアプリケーションをデプロイすることを考えてみます。先ほど何も指定しなかった場合は「ランダム名で新規作成されます」と説明しましたが、もうすでに存在するネットワークに参加させるには、Dockerのドキュメントによるとnetworksに同じネットワークを記述して、external: trueを書けばいいそうです。

docs.docker.com

このexternal: trueが重要で、これが書かれていないと新しいネットワークを作ろうとします。結果的に指定したネットワークがすでに存在する場合は「ネットワークがすでにある」ようなエラーを吐いて、デプロイに失敗します。

services:
  yugabyte:
    image: docker.io/yugabytedb/yugabyte:2024.1.4.0-b108
    restart: always
    command: bin/yugabyted start --background=false
    ports:
      - "7000:7000"
      - "9000:9000"
      - "15433:15433"
      - "5433:5433"
      - "9042:9042"
    volumes:
      - yugabyte-store:/root/var/
    networks:
      - app-network

volumes:
  yugabyte-store:
networks:
  app-network:
    external: true

マニフェストを実行してみましょう。すると失敗するはずです。

% docker-compose -f 'add/docker-compose.yml' up -d --build 
network app-network declared as external, but could not be found

同じネットワークを指定したのになぜ? まず手元の環境にどのようなネットワークが存在するか確認してみましょう。

test_app-networkというネットワークが作成されています。

% docker network ls
NETWORK ID     NAME                                    DRIVER    SCOPE
2f1483868536   bridge                                  bridge    local
0c8447d22df8   host                                    host      local
ecd7dc18f74b   none                                    null      local
ff09beaaf7ba   test_app-network                        bridge    local

ネットワークをinspectしてみます。 実行すると、このネットワークを利用しているコンテナの情報が確認できます。

そのほかにもコンテナをinspectして利用しているネットワークとかネットワークIDから識別する方法もありますが、今回は以下の出力だけでわかるので詳細は割愛します。

% docker network inspect test_app-network
...
        "Containers": {
            "c6c79f16c581a94b771124f4252eea637ba614a9073185f46a1b98da2492336e": {
                "Name": "test-db-1",
                "EndpointID": "e56db74a71d7fcb3e3b861db638ef339428432f1b97388a6206c60e48b5bcfba",
                "MacAddress": "02:42:ac:16:00:02",
                "IPv4Address": "172.22.0.2/16",
                "IPv6Address": ""
            },
            "ef31624288fe2688da7c1f393cb36d4bb1339c5e6cf4ad18d32f36e854729494": {
                "Name": "test-web-1",
                "EndpointID": "cec18c9182dc6991916e9c1eecf5a5f59965c91b7ef0a7b11b291c317f154f1d",
                "MacAddress": "02:42:ac:16:00:03",
                "IPv4Address": "172.22.0.3/16",
                "IPv6Address": ""
            }
        },
...

コンテナ一覧を見ると、ここにも頭にtestが。

% docker container ls
 
CONTAINER ID   IMAGE           COMMAND                   CREATED          STATUS          PORTS                                       NAMES
ef31624288fe   test-web        "/docker-entrypoint.…"   13 minutes ago   Up 13 minutes   0.0.0.0:80->80/tcp, :::80->80/tcp           test-web-1
c6c79f16c581   postgres:16.6   "docker-entrypoint.s…"   13 minutes ago   Up 13 minutes   0.0.0.0:5432->5432/tcp, :::5432->5432/tcp   test-db-1

そうなんです。頭のtestはプロジェクトの上位ディレクトリー名が勝手につきます。 したがって、今回の場合はこう書かないとデプロイに失敗するということです。

services:
  yugabyte:
    image: docker.io/yugabytedb/yugabyte:2024.1.4.0-b108
    restart: always
    command: bin/yugabyted start --background=false
    ports:
      - "7000:7000"
      - "9000:9000"
      - "15433:15433"
      - "5433:5433"
      - "9042:9042"
    volumes:
      - yugabyte-store:/root/var/
    networks:
      - test_app-network

volumes:
  yugabyte-store:
networks:
  test_app-network:
    external: true

実行してみます。add-yugabyte-1というコンテナーが作られましたね。 頭のaddは上位のディレクトリー名がaddだからです。

% docker-compose -f 'add/docker-compose.yml' up -d --build 
% docker network inspect test_app-network
...
        "Containers": {
            "2a9bc7cf4a03d7a73dd9a9480fca0dc75496ea19a4bfd9e219c80b523f6fdfac": {
                "Name": "add-yugabyte-1",
                "EndpointID": "7c8a2b576d5809b867b30fc6d67a387fad34a47b41ce9c0686c30cd30a0d628f",
                "MacAddress": "02:42:ac:16:00:04",
                "IPv4Address": "172.22.0.4/16",
                "IPv6Address": ""
            },
            "c6c79f16c581a94b771124f4252eea637ba614a9073185f46a1b98da2492336e": {
                "Name": "test-db-1",
                "EndpointID": "e56db74a71d7fcb3e3b861db638ef339428432f1b97388a6206c60e48b5bcfba",
                "MacAddress": "02:42:ac:16:00:02",
                "IPv4Address": "172.22.0.2/16",
                "IPv6Address": ""
            },
            "ef31624288fe2688da7c1f393cb36d4bb1339c5e6cf4ad18d32f36e854729494": {
                "Name": "test-web-1",
                "EndpointID": "cec18c9182dc6991916e9c1eecf5a5f59965c91b7ef0a7b11b291c317f154f1d",
                "MacAddress": "02:42:ac:16:00:03",
                "IPv4Address": "172.22.0.3/16",
                "IPv6Address": ""
            }
        },
...

というわけでまとめると、

  • Docker composeでは、ネットワークの設定を記述していないと適当な名前で新規作成される
  • 既存のネットワーク名はアプリケーションのdocker-compose.ymlを参考にするのではなく、docker network lsで確認しよう
  • できたらdocker network inspectで該当のネットワークをどのコンテナが使っているか確認しよう
  • ディレクトリー名は適当につけるとハマったり恥ずかしい思いをするので注意
  • ちなみにこれはDev Containerでも起きるので注意

ということですね。勉強になりました。

ちなみに今回主にGoogleのGeminiに助けられました。

また、external: trueの話はDockerのドキュメントの有志による翻訳サイト様のおかげで理解が深まりました。

ありがとうございます。