【AWS CodeBuild】Docker Composeのコンテナに接続できない？ユーザ定義ネットワークで解決！

# 目的

AWS CodeBuild 上で Docker Compose を利用したテストケースを実施する際に、CodeBuild 実行環境から Compose で起動した検証用 Docker コンテナへ接続できない問題に直面することがあります。本稿では、この問題の原因と、ユーザ定義 Docker ネットワークを利用した解決手順を説明します。

# 対象読者

- AWS CodeBuild を利用して CI/CD パイプラインを構築している開発者
- Docker Compose をテスト環境構築に利用しているエンジニア
- CodeBuild 上でのコンテナ間通信に課題を感じている方
- 中級レベルのクラウド／インフラ知識を持つエンジニア

# 概要

AWS CodeBuild で Docker Compose を使って複数のコンテナを立ち上げ、統合テストや結合テストを行うケースがあります。しかし、標準的な bridge ネットワークのままでは、CodeBuild の実行環境から Compose で立ち上げたコンテナ内のサービスに直接アクセスすることができず、ビルド失敗の原因になります。
本稿では、Docker のユーザ定義ネットワーク（user-defined bridge network）を作成し、CodeBuild 実行環境と Compose で立ち上げたコンテナを同一ネットワークに所属させることで、この接続問題を解決する手順を詳しく解説します。

# 背景：なぜこの問題が起こるのか

AWS CodeBuild は内部的に ECS (on EC2) 上で実行されており、各ビルドは独立したコンテナとして動作します。一方、ビルド処理中に Docker Compose で検証用のコンテナを立ち上げる場合、CodeBuild コンテナ内の Docker エンジンを通じて起動されるため、同じホスト上ではあるものの、別のコンテナとして動作します。
そのため、CodeBuild 実行コンテナから `localhost` で Docker Compose の検証用コンテナにアクセスしようとしても、接続することができず、ビルドは失敗します。

```mermaid
flowchart TD
    subgraph ECS[CodeBuild（ECS on EC2）]
        CB[CodeBuild 実行コンテナ]
        COMPOSE[Docker Compose の検証用コンテナ]
    CB --> |localhost| CB
    CB .-x |localhostでは検証用コンテナに届かない！| COMPOSE
end
```

# 再現例：localhost 接続に失敗するテストケース

例えば、以下のような CodeBuild の `buildspec.yaml` を使用し、Docker Compose で検証用コンテナを立ち上げた後、localhost で検証用コンテナに接続するようなテストケースを実行します。

:::note warn
エラー再現用サンプルコードの詳細や実行方法は GitHub リポジトリを参照してください。
- [CodeBuild-demo-with-Docker-Compose (test/build-failure ブランチ)](https://github.com/ogi-iii/CodeBuild-demo-with-Docker-Compose/blob/test/build-failure/buildspec.yaml)
:::

```yaml
version: 0.2

env: # テストケース実行時の環境変数
  variables:
    AWS_ACCESS_KEY_ID: test
    AWS_SECRET_ACCESS_KEY: test

phases:
  install: # テストケースの実行に必要な環境を構築
    runtime-versions:
      python: 3.13
    commands:
      - python -m pip install --upgrade pip
      - pip install -r requirements.txt
  pre_build:
    commands: # 前回のビルドで立ち上げた検証用コンテナを再利用しないために環境をクリーンアップ
      - docker compose down
      - docker container prune -f
  build:
    commands:
      - docker compose up -d # Docker Compose で検証用コンテナを立ち上げる
      - docker container ls -a
      - pytest # Docker Compose で立ち上げた検証用コンテナに `localhost` で接続するテストケースを実行: 接続エラーでビルドが失敗する
  post_build:
    commands: # ビルド終了時に環境をクリーンアップ
      - docker compose down
      - docker container prune -f
```

実行結果としては、接続エラーによりテストケースが異常終了し、CodeBuild はビルド失敗となります。

:::note warn
接続エラーと判定され、テストケースが異常終了するまでに時間を要するため、CodeBuild のビルド失敗再現時は (成功時よりも) 少し時間がかかります。
:::

```bash:CodeBuild 実行ログのエラー発生箇所を抜粋
agent-1  | >           raise EndpointConnectionError(endpoint_url=request.url, error=e)
agent-1  | E           botocore.exceptions.EndpointConnectionError: Could not connect to the endpoint URL: "http://localhost:4566/"
agent-1  | 
agent-1  | /root/.pyenv/versions/3.13.1/lib/python3.13/site-packages/botocore/httpsession.py:493: EndpointConnectionError
agent-1  | =========================== short test summary info ============================
agent-1  | FAILED tests/secretsmanager/test_secrets_manager_driver.py::test_create_and_get_secret
agent-1  | FAILED tests/secretsmanager/test_secrets_manager_driver.py::test_get_secret_value_not_found
agent-1  | FAILED tests/secretsmanager/test_secrets_manager_driver.py::test_delete_secret
agent-1  | FAILED tests/secretsmanager/test_secrets_manager_driver.py::test_list_secrets
agent-1  | 4 failed in 26.46s
agent-1  | 
agent-1  | [Container] 2025/06/07 04:45:34.481251 Command did not exit successfully pytest exit status 1
agent-1  | [Container] 2025/06/07 04:45:34.531995 Phase complete: BUILD State: FAILED
agent-1  | [Container] 2025/06/07 04:45:34.532028 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: pytest. Reason: exit status 1
```

# 解決手順：ユーザ定義 Docker ネットワークでコンテナ間通信を可能にする

この問題を解決するには、CodeBuild のビルド処理中にユーザ定義 Docker ネットワークを作成し、コンテナ間通信ができるようにします。
具体的には、CodeBuild 実行コンテナと Docker Compose で立ち上げた検証用コンテナをユーザ定義 Docker ネットワークに所属させ、接続先ホスト名は <INS>`localhost` ではなく</INS> **`<コンテナ名>` を指定**します。

```mermaid
flowchart TD
    subgraph ECS[CodeBuild（ECS on EC2）]
        subgraph DOCKER[ユーザ定義 Docker ネットワーク]
            CB[CodeBuild 実行コンテナ]
            COMPOSE[Docker Compose の検証用コンテナ]
        end
    CB --> |<コンテナ名>を接続先ホスト名としてコンテナ間通信| COMPOSE
end
```

よって、CodeBuild の `buildspec.yaml` は、以下のように修正します。

:::note info
正常動作するサンプルコードの詳細や実行方法は GitHub リポジトリを参照してください。
- [CodeBuild-demo-with-Docker-Compose (main ブランチ)](https://github.com/ogi-iii/CodeBuild-demo-with-Docker-Compose/blob/main/buildspec.yaml)
:::

```diff_yaml
version: 0.2

env: # テストケース実行時の環境変数
  variables:
    AWS_ACCESS_KEY_ID: test
    AWS_SECRET_ACCESS_KEY: test

phases:
  install: # テストケースの実行に必要な環境を構築
    runtime-versions:
      python: 3.13
    commands:
      - python -m pip install --upgrade pip
      - pip install -r requirements.txt
  pre_build:
    commands: # 前回のビルドで立ち上げた検証用コンテナを再利用しないために環境をクリーンアップ
      - docker compose down
      - docker container prune -f
+     - docker network prune -f
  build:
    commands:
      - docker compose up -d # Docker Compose で検証用コンテナを立ち上げる
+     - CUSTOM_NETWORK_NAME=$(head /dev/urandom | sha256sum | cut -c-50)
+     - docker network create -d bridge $CUSTOM_NETWORK_NAME # ユーザ定義 Docker ネットワークを作成
+     - SECRETSMANAGER_CONTAINER_NAME=$(docker ps --filter "name=mock-secretsmanager-container" --format "{{.Names}}")
+     - docker network connect $CUSTOM_NETWORK_NAME $SECRETSMANAGER_CONTAINER_NAME # Docker Compose で立ち上げた検証用コンテナをユーザ定義 Docker ネットワークに所属させる
+     - CODEBUILD_CONTAINER_NAME=$(docker ps --filter "ancestor=public.ecr.aws/codebuild/amazonlinux-x86_64-standard:5.0" --format "{{.Names}}")
+     - docker network connect $CUSTOM_NETWORK_NAME $CODEBUILD_CONTAINER_NAME # CodeBuild 実行コンテナをユーザ定義 Docker ネットワークに所属させる
      - docker container ls -a
+     - docker network ls
+     - docker inspect $CUSTOM_NETWORK_NAME # ユーザ定義 Docker ネットワークに所属するコンテナ一覧を確認
+     - find . -type f -name config.ini -exec sed -i "s/localhost/$SECRETSMANAGER_CONTAINER_NAME/g" {} + # アプリ設定ファイルにおける接続先ホスト名の `localhost` を `Docker Compose の検証用コンテナ名` に置換する
-     - pytest # Docker Compose で立ち上げた検証用コンテナに `localhost` で接続するテストケースを実行: 接続エラーでビルドが失敗する
+     - pytest # Docker Compose で立ち上げた検証用コンテナに `<コンテナ名>` で接続するテストケースを実行: コンテナ間通信によりビルドが成功する
  post_build:
    commands: # ビルド終了時に環境をクリーンアップ
      - docker compose down
      - docker container prune -f
+     - docker network disconnect $CUSTOM_NETWORK_NAME $CODEBUILD_CONTAINER_NAME
+     - docker network prune -f
```

実行結果として、テストケースが正常終了し、CodeBuild のビルドに成功します。

```bash:CodeBuild 実行ログのテストケース実行箇所を抜粋
agent-1  | [Container] 2025/06/07 06:00:00.393983 Running command pytest
agent-1  | ....                                                                   [100%]
agent-1  | 4 passed in 0.51s
agent-1  | 
agent-1  | [Container] 2025/06/07 06:00:02.290982 Phase complete: BUILD State: SUCCEEDED
```

# まとめ

CodeBuild 上で Docker Compose を利用したテストケースを実施する際に、localhost で接続できない問題は、CodeBuild が内部的に ECS (on EC2) 上で実行されていること、Docker Compose の検証用コンテナが CodeBuild 実行環境とは別のコンテナとして立ち上がることに起因する、ネットワーク分離によるものでした。
この問題は、ユーザ定義ネットワークを活用し、同一ネットワーク内でコンテナ間通信を実現することで解決できます。
CodeBuild 上での統合テストやマイクロサービスの検証に Docker Compose を用いる場合、この構成を知っておくことで、不要なトラブルを避けることができます。

# 参考リンク

- 本稿で紹介したサンプルコードの格納先リポジトリ
    - [CodeBuild-demo-with-Docker-Compose - GitHub](https://github.com/ogi-iii/CodeBuild-demo-with-Docker-Compose)

# 補足情報

## 2025/06/12 追記

- 本稿で解説した (CodeBuild から Docker Compose を利用する) 構成のような、特定のコンテナ内からホストマシンのdockerを操作する手法のことを、dood (docker-outside-of-docker) と呼ぶそうです
    - [dind(docker-in-docker)とdood(docker-outside-of-docker)でコンテナを料理する](https://qiita.com/t_katsumura/items/d5be6afadd6ec6545a9d)