LoginSignup
0
1

【Docker】複数コンテナにおける接続エラーを解決する

Last updated at Posted at 2023-12-11

概要

DBコンテナ(MySQL)とApplicationコンテナ(SpringBoot)の2つをdocker-compose.ymlにて管理しようとしました。ビルド実行すると、Applicationコンテナのビルドに失敗したため、以下のエラーについての解決方法を調査検討しました。

エラー内容

Caused by: java.net.UnknownHostException: db: Name or service not known

databaseのmigrationにFlywayを使っていたので、以下のようなエラーも出ていました。

Unable to obtain connection from database: Communications link failure
Error creating bean with name ‘flywayInitializer’ defined in class path resource

最終結果

いろいろ試したのですが、先に最終的に設定したフォルダ構成とコンテナ設定ファイルを載せます。

ソースコード
フォルダ構造(一部省略)
├── docker-compose.yml
├── mysql(DBコンテナ)
│   ├── Dockerfile
│   ├── data
│   └── settings
└── spring(Appコンテナ)
    ├── Dockerfile
    └── demo
mysql/Dockerfile
FROM mysql:8.0.28-debian
EXPOSE 3306
spring/Dockerfile
FROM openjdk:11
WORKDIR /app
EXPOSE 8080
COPY ./demo /app/
CMD ["sh", "-c", "mvn clean package && java -jar /app/target/demo-0.0.1-SNAPSHOT.jar"]
docker-compose.yml
version: '3.9'
services:
  db:
    container_name: db
    build:
      context: ./mysql
    ports:
      - 3306:3306
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - ./mysql/settings:/etc/mysql/conf.d/
      - ./mysql/data/:/var/lib/mysql/
    env_file:
      - .env
    healthcheck:
      test: ["CMD", "mysqladmin" ,"ping", "-h", "${MYSQL_HOST}", "-u${MYSQL_USER}", "-p${MYSQL_PASSWORD}"]
      timeout: 20s
      retries: 10
    networks:
      - app-net
  app:
    build:
      context: ./spring
    ports:
      - 8080:8080
    restart: always
    depends_on:
      db:
        condition: service_healthy
    networks:
      - app-net
networks:
  app-net:
    driver: bridge
.env
MYSQL_DATABASE=<DB名>
MYSQL_USER=<DBユーザー名>
MYSQL_PASSWORD=<DBパスワード>
MYSQL_HOST=<DBホスト名>

エラーの原因分析と切り分け

ここからは、上記エラーとして考えられる原因、原因切り分けのためにやるべきこと、原因に対する解決方法をまとめます。エラーの原因として以下の3パターンが考えられます。

  1. dbコンテナが立ち上がる前にアクセスしている
  2. dbコンテナとappコンテナが同一のネットワークに存在していない
  3. アプリケーション側のDB設定が正しくない

以下では、それぞれの詳細を述べます。

1. dbコンテナが立ち上がる前にアクセスしている

切り分け

appコンテナが起動できるならば、execで中に入ってmvn clean packageをしてみましょう。正常にビルドされたらコンテナの実行タイミングが問題です。私はビルド時にテスト実行されてDBアクセス不良でエラーになっていたため、テストをスキップすることでコンテナ起動しました。

spring/Dockerfile
- RUN mvn clean package
+ RUN mvn -B -DskipTests clean package

解決法

力技で解決してしまった感があるため、正しい解決法があればご教示ください。以下の1-1, 1-2の実施が必要です。

1-1. docker-compose.ymlのdepends_onプロパティを利用する

docker-compose.ymlにdepends_onプロパティを設定することで、dbコンテナがreadyになったらappコンテナを実行するように設定します。

service_healthyを設定することで、db serviceのヘルスチェックが成功してからapp serviceを起動させることができます。

docker-compose.yml(抜粋)
version: '3.9'
services:
  db:
+    healthcheck:
+      test: ["CMD", "mysqladmin" ,"ping", "-h", "${MYSQL_HOST}", "-u${MYSQL_USER}", "-p${MYSQL_PASSWORD}"]
+      timeout: 20s
+      retries: 10
  app:
+    depends_on:
+      db:
+        condition: service_healthy

注意1
docker-composeのversion3以降ではdepends_on の条件形式はサポートされなくなりました。しかし、version3.9以降で復活しています。
適切なversionを設定してください。
https://github.com/docker/compose/issues/8154

注意2
Docker Composeのバージョンが古いとversion3.9が利用できない可能性があります。aptからのインストールはバージョンが古かったので、公式レポジトリからv.1.29.2を落としてインストールしました。(Compose v2はdocker composeコマンドになるため、LocalStackが使えず上記バージョンにしました。問題ない方は最新バージョンでもよいと思います)
https://github.com/docker/compose/releases

.envファイルにDB接続情報を記載してください。DBホスト名はdocker-compose.ymlのservice名(今回であればdb)です。

1.2. CMDでビルド実行する

上記対応でコンテナの起動順は制御できましたが、Dockerイメージのビルドプロセス(RUNコマンドなど)は変わりません。つまり、RUNで定義しているmvn clean packageはMySQLコンテナが起動していなくても実行されてしまうのです。したがって、このコマンドをCMDで実行します。

Dockerfile
- RUN mvn clean package
- ENTRYPOINT ["java","-jar","/app/target/demo-0.0.1-SNAPSHOT.jar"]
+ CMD ["sh", "-c", "mvn clean package && java -jar /app/target/demo-0.0.1-SNAPSHOT.jar"]
(補足)CMDとRUNコマンドの違い

RUN:DockerfileからImageを作成するときに一度だけ実行されます
CMD:Docker ImageからDockerコンテナを作成するときに実行されます

2. dbコンテナとappコンテナが同一のネットワークに存在していない

切り分け

appコンテナが起動できるならば、execで中に入ってpingを飛ばします。
今回は失敗したところの直前までビルドして、execして以下を実行しました。
※ dbはdocker-compose.ymlのservice名です

apt-get update && apt-get install iputils-ping
ping db

レスポンスが返ってきていなければ、network設定を見直しましょう。

解決法

2つのcontainerを同一networkに入れましょう。
参考:https://docs.docker.jp/compose/networking.html

docker-compose.yml(抜粋)
  services:
    db:
+     networks:
+       - app-net

    app:
+     networks:
+       - app-net
+ networks:
+   app-net:
+     driver: bridge

3. アプリケーション側のDB設定が正しくない

SpringBootであれば、application.propertiesのdatasource.url, datasource.username, datasource.passwordが正しく設定されていない可能性があります。

切り分け

Dockerの問題なのかを確かめます。ローカルにMySQLやSpringBoot環境を作って実行してみて、正しく動かなければこの可能性が高いです。ただ、そこまでする前にもう一度設定を見直すのが良いと思います。

解決法

正しく設定しましょう。
参考:https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html

application.properties
spring.datasource.url=jdbc:mysql://<DBホスト名>:3306/<DB名>
spring.datasource.username=<DBユーザー名>
spring.datasource.password=<DBパスワード>
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1