🙄

[SadServers] 解説 "Bern": Docker web container can't connect

2022/12/30に公開

"Bern": Docker web container can't connect to db container.

SadServersの "Bern": Docker web container can't connect to db container. の解説です。

SadServers って何? って人は、 SadServers解説 を見てください。
一言でいうと、LeetCode (コーディング問題集) の Linuxサーバ設定版のようなものです。

問題

では、問題を見ていきます。

Scenario: "Bern": Docker web container can't connect to db container.

Level: Hard

Description: There are two Docker containers running, a web application (Wordpress or WP) and a database (MariaDB) as back-end, but if we look at the web page, we see that it cannot connect to the database. curl -s localhost:80 |tail -4 returns:

<body id="error-page"> <div class="wp-die-message"><h1>Error establishing a database connection</h1></div></body> </html>

This is not a Wordpress code issue (the image is :latest with some network utilities added). What you need to know is that WP uses "WORDPRESS_DB_" environment variables to create the MySQL connection string. See the ./html/wp-config.php WP config file for example (from /home/admin).

2つのDockerコンテナが動作し、Webアプリケーション(WordpressまたはWP)とバックエンドとしてデータベース(MariaDB)がありますが、Webページを見ると、データベースに接続できないことがわかります。 curl -s localhost:80 |tail -4 が返されます。

<body id="error-page"> <div class="wp-die-message"><h1>Error establishing a database connection</h1></div></body> </html>

これはWordpressのコードの問題ではありません(画像はネットワークユーティリティを追加した:latestです)。知っておくべきことは、WPはMySQL接続文字列を作成するために「WORDPRESS_DB_」環境変数を使用することです。例として、./html/wp-config.php WP設定ファイル(/home/adminから)をご覧ください。

Test: sudo docker exec wordpress mysqladmin -h mysql -u root -ppassword ping . The wordpress container is able to connect to the database in the mariadb container and returns mysqld is alive.

sudo docker exec wordpress mysqladmin -h mysql -u root -ppassword ping .wordpressコンテナはmariadbコンテナ内のデータベースに接続でき、mysqldが生きていることを返します。

Time to Solve: 20 minutes.

OS: Debian 11

解説

まず、 curl -s localhost:80 |tail -4 の動作確認をします。

admin@ip-172-31-19-232:/$ curl -s localhost:80 |tail -4
<body id="error-page">
        <div class="wp-die-message"><h1>Error establishing a database connection</h1></div></body>
</html>

問題の通りの結果が返ってきています。
次に、Testの sudo docker exec wordpress mysqladmin -h mysql -u root -ppassword pingを実行してみます。

admin@ip-172-31-19-232:/$ sudo docker exec wordpress mysqladmin -h mysql -u root -ppassword ping
mysqladmin: connect to server at 'mysql' failed
error: 'Unknown MySQL server host 'mysql' (-2)'
Check that mysqld is running on mysql and that the port is 3306.
You can check this by doing 'telnet mysql 3306'

当然ながら、エラーとなっています。
このコマンドは wordpressコンテナから、mysqladmin -h mysql -u root -ppassword pingを実行しています。エラーは、mysqlというサーバに接続できないことを示しています。

docker ps コマンドで、動作中のコンテナを確認します。
sudoをつけないと、permissionエラーとなるので、注意してください。sudoをしたくない場合は、dockerという名のグループを作ってユーザを登録すればいいのですが、ここではsudoで回避します。

admin@ip-172-31-19-232:/$ sudo docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED        STATUS         PORTS                    NAMES
6ffb084b515c   wordpress:sad    "docker-entrypoint.s…"   4 months ago   Up 2 minutes   0.0.0.0:80->80/tcp       wordpress
0eef97284c44   mariadb:latest   "docker-entrypoint.s…"   4 months ago   Up 2 minutes   0.0.0.0:3306->3306/tcp   mariadb

wordpressmariadbという2つのコンテナが動作していることがわかります。
wordpressコンテナのイメージは、wordpress:sadでポート80をオープンしています。
mariadbコンテナのイメージは、mariadb:latestでポート3306をオープンしています。
これらのコンテナについて調べていきます。

まず、docker inspectで、mariadbコンテナの詳細情報を確認します。

sudo docker inspect mariadb

            "Env": [
                "MYSQL_ROOT_PASSWORD=password",
                "MYSQL_DATABASE=wordpress",
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "GOSU_VERSION=1.14",
                "MARIADB_MAJOR=10.8",
                "MARIADB_VERSION=1:10.8.3+maria~jammy"
            ],

Envに示される環境変数から、MYSQL_ROOT_PASSWORD=passwordMYSQL_DATABASE=wordpressが設定されていることがわかります。

同様に、`wordpress コンテナの詳細情報を確認します。

sudo docker inspect wordpress

            "Env": [

		"WORDPRESS_DB_PASSWORD=password",
                "WORDPRESS_DB_USER=root",
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "PHPIZE_DEPS=autoconf \t\tdpkg-dev \t\tfile \t\tg++ \t\tgcc \t\tlibc-dev \t\tmake \t\tpkg-config \t\tre2c",
                "PHP_INI_DIR=/usr/local/etc/php",
                "APACHE_CONFDIR=/etc/apache2",
                "APACHE_ENVVARS=/etc/apache2/envvars",
                "PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64",
                "PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64",
                "PHP_LDFLAGS=-Wl,-O1 -pie",
                "GPG_KEYS=42670A7FE4D0441C8E4632349E4FDC074A4EF02D 5A52880781F755608BF815FC910DEB46F53EA312",
                "PHP_VERSION=7.4.30",
                "PHP_URL=https://www.php.net/distributions/php-7.4.30.tar.xz",
                "PHP_ASC_URL=https://www.php.net/distributions/php-7.4.30.tar.xz.asc",
                "PHP_SHA256=ea72a34f32c67e79ac2da7dfe96177f3c451c3eefae5810ba13312ed398ba70d"
            ],

Envに示される環境変数から、MYSQL_ROOT_PASSWORD=passwordWORDPRESS_DB_USER=rootが設定されていることがわかります。

問題にあるように、wordpressの設定は、/home/admin/html/wp-config.php の WORDPRESS_DB_ 変数にて設定されるということなので、wp-config.phpWORDPRESS_DB_を確認します。

admin@ip-172-31-19-232:~/html$ grep WORDPRESS_DB_ wp-config.php 
define( 'DB_NAME', getenv_docker('WORDPRESS_DB_NAME', 'wordpress') );
define( 'DB_USER', getenv_docker('WORDPRESS_DB_USER', 'example username') );
define( 'DB_PASSWORD', getenv_docker('WORDPRESS_DB_PASSWORD', 'example password') );
define( 'DB_HOST', getenv_docker('WORDPRESS_DB_HOST', 'mysql') );
define( 'DB_CHARSET', getenv_docker('WORDPRESS_DB_CHARSET', 'utf8') );
define( 'DB_COLLATE', getenv_docker('WORDPRESS_DB_COLLATE', '') );

phpはわからないですが、ググって調べると getenv_docker()は、第1引数で環境変数を指定して、第2引数で環境変数が無いときのデフォルト値のようです。

MYSQL_ROOT_PASSWORDWORDPRESS_DB_USERは環境変数が設定されているので、それぞれ環境変数の値passwordrootが使われ、それ以外は、デフォルト値が使われます。
が、これらの設定には特に問題はなさそうです。

wordpressコンテナから、 mariadbコンテナにアクセスできないことが問題なので、これを解決しましょう。
アクセスするだけなら、IPアドレスでアクセスするとかいろいろな手段があるのですが、テストが sudo docker exec wordpress mysqladmin -h mysql -u root -ppassword ping と ホスト名mysqlでアクセスできないといけないので、wordpressコンテナから、mariadbコンテナに、ホスト名mysqlでアクセスできるようにする必要があります。

コンテナ間の通信は、docker composeを使うと簡単なので、docker-compose.ymlファイルを作ります。

/home/admin/docker-compose.yml
version: "3"

services:
  mysql:
    image: mariadb:latest
    # コンテナ名を指定
    container_name: mysql
    # hostnameをmysqlとする。 wordpressコンテナから、mysqlでアクセスできる。
    hostname: mysql
    ports:
      - 3306:3306
    environment:
      # 元のmariadbコンテナの環境変数を設定
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: wordpress

  wordpress:
    depends_on:
      - mysql
    image: wordpress:sad
    container_name: wordpress
    hostname: wordpress
    ports:
      - 80:80
    environment:
      # 元のwordpressコンテナの環境変数を設定
      WORDPRESS_DB_PASSWORD: password
      WORDPRESS_DB_USER: root

既存のコンテナを停止してから、docker-composeで起動します。

# 停止
admin@ip-172-31-19-232:~$ sudo docker stop wordpress mariadb
wordpress
mariadb

admin@ip-172-31-19-232:~$ cd /home/admin

# /home/admin/ で、 docker-composeを実行。 
admin@ip-172-31-19-232:~$ sudo docker-compose up 
Recreating admin_mysql_1 ... done
Recreating admin_wordpress_1 ... done
Attaching to mysql, wordpress

テストを実行します。

admin@ip-172-31-19-232:/$ sudo docker exec wordpress mysqladmin -h mysql -u root -ppassword ping
mysqld is alive

mysqld is aliveが出力されました。成功です。

curlも試してみます。

admin@ip-172-31-19-232:/$ curl -v -s localhost:80
*   Trying 127.0.0.1:80...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.74.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< Date: Thu, 29 Dec 2022 11:15:14 GMT
< Server: Apache/2.4.54 (Debian)
< X-Powered-By: PHP/7.4.30
< Expires: Wed, 11 Jan 1984 05:00:00 GMT
< Cache-Control: no-cache, must-revalidate, max-age=0
< X-Redirect-By: WordPress
< Location: http://localhost/wp-admin/install.php
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
< 
* Connection #0 to host localhost left intact

先ほどとは違う結果で、302です。 リダイレクト先のデータを取得するために、-Lオプションを付けて再度実行してみます。

admin@ip-172-31-19-232:/$ curl -v  -L localhost:80
*   Trying 127.0.0.1:80...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.74.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< Date: Thu, 29 Dec 2022 14:54:01 GMT
< Server: Apache/2.4.54 (Debian)
< X-Powered-By: PHP/7.4.30
< Expires: Wed, 11 Jan 1984 05:00:00 GMT
< Cache-Control: no-cache, must-revalidate, max-age=0
< X-Redirect-By: WordPress
< Location: http://localhost/wp-admin/install.php
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
< 
* Connection #0 to host localhost left intact
* Issue another request to this URL: 'http://localhost/wp-admin/install.php'
* Found bundle for host localhost: 0x560f9bba12f0 [serially]
* Can not multiplex, even if we wanted to!
* Re-using existing connection! (#0) with host localhost
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /wp-admin/install.php HTTP/1.1
> Host: localhost
> User-Agent: curl/7.74.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Thu, 29 Dec 2022 14:54:01 GMT
< Server: Apache/2.4.54 (Debian)
< X-Powered-By: PHP/7.4.30
< Expires: Wed, 11 Jan 1984 05:00:00 GMT
< Cache-Control: no-cache, must-revalidate, max-age=0
< Vary: Accept-Encoding
< Content-Length: 6960
< Content-Type: text/html; charset=utf-8
< 
<!DOCTYPE html>

正しく、取得できているようです。

Check My Solutionを実行して、問題ないことを確認します。

Discussion