はじめに
こんにちは!ITスクールRareTECHにてCS(Customer Support)を担当している池村です。今回の記事はDocker Composeでのコンテナ起動順のエラー解決についてです。先日うちの受講生からコンテナ起動がうまくいかないと相談があったので、その解決までの流れを残しておきます。
この記事はDocker Composeまでを学習した人向けです。
エラー内容
まず今回の環境構築の内容は以下です。
- Djangoのコンテナ
- MySQLのコンテナ
- Nginxのコンテナ
上記のコンテナをComposeで起動して、Djangoアプリケーションを動かすのが目的になっています。
ディレクトリ構成は以下になります。
.
├── fapp
│ ├── __pycache__
│ ├── migrations
│ │ └── __pycache__
│ └── templates
├── fproject
│ └── __pycache__
├── infra
│ ├── app
│ ├── db
│ │ └── db_data
│ │ ├── #innodb_redo
│ │ ├── #innodb_temp
│ │ ├── fteam_db
│ │ ├── mysql
│ │ ├── performance_schema
│ │ └── sys
│ └── nginx
└── static
└── admin
├── css
│ └── vendor
│ └── select2
├── img
│ └── gis
└── js
├── admin
└── vendor
├── jquery
├── select2
│ └── i18n
└── xregexp
※ファイル自体は非表示
docker-compose.ymlファイルは以下です。
services:
app:
build:
context: .
dockerfile: ./infra/app/Dockerfile
working_dir: "/usr/src/app"
tty: true
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./:/usr/src/app
ports:
- "8080:8000"
depends_on:
db:
condition: service_healthy
db:
build:
context: .
dockerfile: ./infra/db/Dockerfile
tty: true
volumes:
- ./infra/db/db_data:/var/lib/mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./infra/nginx/fnginx.conf:/etc/nginx/conf.d/default.conf # NginX設定ファイルのマウント
- ./static:/usr/src/app/static
depends_on:
- app
いざdocker compose up
を行ったものの、以下のエラーが出てしまっていました。
app-1 | File "/usr/local/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
app-1 | return func(*args, **kwargs)
app-1 | ^^^^^^^^^^^^^^^^^^^^^
app-1 | File "/usr/local/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 256, in get_new_connection
app-1 | connection = Database.connect(**conn_params)
app-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app-1 | File "/usr/local/lib/python3.11/site-packages/MySQLdb/__init__.py", line 121, in Connect
app-1 | return Connection(*args, **kwargs)
app-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
app-1 | File "/usr/local/lib/python3.11/site-packages/MySQLdb/connections.py", line 200, in __init__
app-1 | super().__init__(*args, **kwargs2)
app-1 | django.db.utils.OperationalError: (2002, "Can't connect to server on 'db' (115)")
db-1 | 2025-01-19T04:12:30.782723Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
db-1 | 2025-01-19T04:12:30.906233Z 0 [System] [MY-010229] [Server] Starting XA crash recovery...
db-1 | 2025-01-19T04:12:30.914796Z 0 [System] [MY-010232] [Server] XA crash recovery finished.
db-1 | 2025-01-19T04:12:30.986170Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
db-1 | 2025-01-19T04:12:30.986202Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
db-1 | 2025-01-19T04:12:30.991049Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
db-1 | 2025-01-19T04:12:31.011815Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
db-1 | 2025-01-19T04:12:31.011830Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.40' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
ファイルにおかしい記述がないか?
最初に目に入ったのは、django.db.utils.OperationalError: (2002, "Can't connect to server on 'db' (115)")
の部分でした。
DjangoからMySQLにコネクションできていないのは明らかだったので、設定ファイル類を確認しに行ってみました。
- yamlファイルの記述
- settings.pyの記述
- .envファイルの記述
これらどれも見に行ったんですが、特に問題はありませんでした。
ネットワークの問題か?となったので次に移ります。
コンテナ同士のネットワークは問題ないか?
コンテナ同士のネットワークが不完全ならエラーも吐くだろうと思い、まずはネットワークの確認を実施。
docker network ls
docker network inspect 2025winter-f_default
[
{
"Name": "2025winter-f_default",
"Id": "11689ce9407c6fb7a24dd2bf1855489e9ce2c7ba1fc313f5b153ca4605073cc7",
"Created": "2025-01-19T13:12:29.544150333+09:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "192.168.155.0/24",
"Gateway": "192.168.155.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"3fff70454185ea4052c60eceae22cc27291bad0ab444ce8fe772611d96d81734": {
"Name": "2025winter-f-app-1",
"EndpointID": "85d2b8e85874a6d7e040b8f17e45a7ef563db122b1415174f14c1fdc20d106be",
"MacAddress": "02:42:c0:a8:9b:03",
"IPv4Address": "192.168.155.3/24",
"IPv6Address": ""
},
"40f6838d5badbf7c7adb1c7913792635d5c991992a5a1b228df5cb415d9e20eb": {
"Name": "2025winter-f-db-1",
"EndpointID": "3505bfd7cc7d9b7a195cbc8a044449279ab471915d1d468ba88cd7bb31451691",
"MacAddress": "02:42:c0:a8:9b:02",
"IPv4Address": "192.168.155.2/24",
"IPv6Address": ""
},
"661c69aba669b6c7582401122336267a8b6c7418139ffe5cba661623c1ee89bc": {
"Name": "2025winter-f-nginx-1",
"EndpointID": "a8768c4fef514fee306ea5abaf90f75f34b3745e4dd0983bb95ea0524c6cbe2f",
"MacAddress": "02:42:c0:a8:9b:04",
"IPv4Address": "192.168.155.4/24",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {
"com.docker.compose.config-hash": "a9da0cb733a47ac917cae33f2192776cb3391d52500c4c004ddee21e33aea9ad",
"com.docker.compose.network": "default",
"com.docker.compose.project": "2025winter-f",
"com.docker.compose.version": "83.0.0"
}
}
]
特に問題なし。コンテナ間は同じネットワークにいる。
コンテナの中に入って確認
まあこれだけだと本当に繋がっているのか確信はなかったので、今度はコンテナの中に入ってコンテナ同士がつながるかどうかを確認してみます。あとやはりしっかりデータベースの中身も確認していきます。
MySQLのコンテナの中でデータベースができているか確認
まずはMySQLの中に入って、Djangoからアクセスする際のユーザー情報は合っているのか?と、実際にテーブルを見に行ってみます。
docker exec -it コンテナ名 bash
mysql -u root -p
※rootパスワードを入力して入ります。
SHOW GRANTS FOR 'ユーザー名'@'%';
+-----------------------------------------------------------+
| Grants for ユーザー名@% |
+-----------------------------------------------------------+
| GRANT USAGE ON *.* TO `ユーザー名`@`%` |
| GRANT ALL PRIVILEGES ON `データベース名`.* TO `ユーザー名`@`%` |
+-----------------------------------------------------------+
結論、データベースもテーブルもしっかりできており、今回使用するMySQLのユーザーにも適切な権限が付与されていました。
Djangoのコンテナの中から疎通確認 + DBアクセス
じゃあ次はと、今度はDjangoのコンテナの中に入ってデータベースにつながるかどうかを試してみます。
docker exec -it コンテナ名 bash
最初は何も入っていないので、まずはping
を入れてみます。
apt update
apt install iputils-ping -y
※コンテナの中にはrootで入るので、sudo
はいりません。
ping db
root@3fff70454185:/usr/src/app# ping db
PING db (192.168.155.2) 56(84) bytes of data.
64 bytes from 2025winter-f-db-1.2025winter-f_default (192.168.155.2): icmp_seq=1 ttl=64 time=1.66 ms
64 bytes from 2025winter-f-db-1.2025winter-f_default (192.168.155.2): icmp_seq=2 ttl=64 time=0.309 ms
64 bytes from 2025winter-f-db-1.2025winter-f_default (192.168.155.2): icmp_seq=3 ttl=64 time=0.082 ms
こちらもping
が通る時点で繋がっているようです。
ではMySQLにDjangoから接続できないのか?
apt install mysql-client -y
もしエラーを吐くなら以下でも大丈夫です。
apt install mariadb-client -y
では接続してみます。
mysql -u root -p
こちらも無事接続完了。じゃあどこだ!
改めてDockerのログを見てみる
Composeでコンテナを起動すると、そのログが表示されるのですが、そこにしっかりヒントがありました。
InnoDB initialization has ended.
初期化が完了しました。がDjangoのコンテナのエラーの後に表示されていたんですね。
ということは、コンテナの起動順じゃないか?という仮説になります。
docker stop Djangoのコンテナ
docker start Djangoのコンテナ
これをすると、問題なくコンテナが起動しました。
結論
これは結局、MySQLのコンテナ作成が遅かったことによるエラーでした。
いくらdocker-compose.ymlファイルに depends_on:
の記述をしていても、あくまでコンテナ作成順を保証しているのであって、コンテナの中で実行されるコマンド類はまた別問題なんですね。
解決方法
結局コンテナの作成順の問題なら、ヘルスチェックすれば解決します。
簡単にヘルスチェックについて解説します。
healthcheck
は、コンテナ内のサービスが正常に動作しているかを監視するためのDockerの機能になります。
db:
build:
context: .
dockerfile: ./infra/db/Dockerfile
tty: true
volumes:
- ./infra/db/db_data:/var/lib/mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
healthcheck:
test:
[
"CMD-SHELL",
"mysqladmin ping -h localhost -u ${MYSQL_USER} -p${MYSQL_PASSWORD} || exit 1",
]
interval: 10s
timeout: 5s
retries: 5
上記のようにhealthcheck:
を記述して、pingでMySQLにアクセスできるかどうかを見ています。
- interval:テストを実行する間隔を指定
- timeout:テストコマンドがタイムアウトするまでの時間を指定
- retries:テストが失敗した場合に再試行する回数を指定
コマンド内容は、MySQLサーバーに対してpingを実行し、成功すれば0(正常)を返し、失敗すれば1(異常)を返します。
depends_on:
db:
condition: service_healthy
依存先のサービス(dbのこと)が完全に起動して、健康状態が「healthy」と判定されるまで待機してくれます。
コマンドの実行結果が0の際にはhealthyと判定されるということですね。
おわりに
やはりログをみるのは大事ですね!英語といってもしっかり確認しないと無駄な時間がかかってしまってもったいないです。
ITスクールRareTECHでは年に4回チーム開発イベント、ハッカソンを行っています。受講生の方のレベルもどんどん上がっていて期待してしまいます。
今回はDockerの番外編ということで、誰かのエラー解決のきっかけになることを祈っています。