riina-k.net

音楽・小説・プログラミングを手がけるリイナの拠点。

Docker Compose と nginx でリバースプロキシを作ろうとしたお話(ネットワーク編)

Docker Compose と nginx でリバースプロキシを作ろうとしたお話(SSL編) のさらにさらに続き。

今回は、nginx がつながってるフロント側のブリッジとは別に、バックエンド用のブリッジを作成し、そっちに MariaDB コンテナを作成してみようという試みです。

イメージはこんな感じ。

前回の SSL 編で nginx の自前 Docker ファイルをクビにしたので、ディレクトリ構成を変えました。

/
+ var/
  + docker/
    + app/
    | | docker-compose.yml <- Apache、MariaDB の設定ファイル
    | + db-data/ <- MariaDB のデータファイル(自動生成される)
    | + riina-k.me/
    | | | Dockerfile
    | | | httpd.conf
    | | | php.ini
    | | + var/
    | |   + www/
    | |     + html/
    | |       | index.php
    | + test.riina-k.me/
    |   | Dockerfile
    |   | httpd.conf
    |   | php.ini
    |   + var/
    |     + www/
    |       + html/
    |         | index.php
    + proxy/
      | docker-compose.yml <- nginx の Docker Compose 設定ファイル
      + certs/ <- SSL 証明書が入ってる(自動生成される)

勉強がてら、docker-compose.yml を2つに分けました。

/var/docker/proxy/docker-compose.yml

version: "3"

services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    restart: on-failure
    labels:
      - com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=jwilder/nginx-proxy
    container_name: nginx-proxy
    privileged: true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - proxy:/etc/nginx/vhost.d
      - proxy:/usr/share/nginx/html
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./certs:/etc/nginx/certs:ro
    networks:
      - front_bridge

  letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    restart: on-failure
    container_name: letsencrypt-nginx
    depends_on:
      - nginx-proxy
    volumes:
      - proxy:/etc/nginx/vhost.d
      - proxy:/usr/share/nginx/html
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./certs:/etc/nginx/certs:rw
    networks:
      - front_bridge

networks:
  front_bridge:
    external: true

volumes:
  proxy:

/var/docker/proxy/app/docker-compose.yml

version: "3"

services:
  apache.riina-k.me:
    build: ./riina-k.me
    restart: on-failure
    container_name: apache.riina-k.me
    environment:
      VIRTUAL_HOST: riina-k.me
      LETSENCRYPT_HOST: riina-k.me
      LETSENCRYPT_EMAIL: riinak.tv@gmail.com
    volumes:
      - ./riina-k.me/var/www/html:/var/www/html
    networks:
      - front_bridge
      - backend_bridge

  apache.test.riina-k.me:
    build: ./test.riina-k.me
    restart: on-failure
    container_name: apache.test.riina-k.me
    environment:
      VIRTUAL_HOST: test.riina-k.me
      LETSENCRYPT_HOST: test.riina-k.me
      LETSENCRYPT_EMAIL: riinak.tv@gmail.com
    volumes:
      - ./test.riina-k.me/var/www/html:/var/www/html
    networks:
      - front_bridge <- 敢えてバックエンドに接続しない

  mysql: <- MariaDB の設定
    image: mariadb 
    restart: on-failure
    container_name: docker-mysql
    ports:
      - "3306:3306"
    volumes:
      - ./db-data:/var/lib/mysql 
    environment:
      - MYSQL_ROOT_PASSWORD=password
    networks:
      - backend_bridge

networks:
  front_bridge:
    external: true
  backend_bridge:
    driver: bridge

ブリッジをフロントエンドとバックエンドで分け、Apache コンテナは両方に接続、MariaDB はバックエンドのみに接続という設定にしました。
ネットワーク接続のテストのため、Apache コンテナのうち riina-k.me のほうだけをバックエンドに接続し、test.riina-k.me はフロントエンドのみ接続にしています。

Docker Compose する前に、2つの yml で共用できるネットワークを作成します。

[root@riina-02 proxy]# docker network create front_bridge
7060e203653e342703fecbed0b34ef635c9e6f907a976eb8e8689ad153cbb9cb
[root@riina-02 proxy]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
07a6f7bb58db        bridge              bridge              local
7060e203653e        front_bridge        bridge              local
20a4f5043965        host                host                local
ea1abcb4c642        none                null                local

front_bridge が生まれました。

それでは Docker Compose を2つとも実行。
ネットワークにどう繋がってるか見てみましょう。

[root@riina-02 proxy]# docker network inspect front-bridge
[
    {
        "Name": "front_bridge",
        "Id": "49ed63570084fb22f2882b34c7d593b24fddce5f0267cf3a82ff6cdf5f5783d4",
        "Created": "2019-03-23T13:49:32.817457404+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "060793e0c280e8225e3ff70145f37001ca999160910613d7f3c262d30c561d79": {
                "Name": "letsencrypt-nginx",
                "EndpointID": "024df8cc6a69252ff60b4954220dc23da570762cabf17e10c6b5e6f2f1de957f",
                "MacAddress": "02:42:ac:13:00:03",
                "IPv4Address": "172.19.0.3/16",
                "IPv6Address": ""
            },
            "4e65fecbcd9768f377db0fb408ab3cc7e7b824b7998ae546943e338b3939d510": {
                "Name": "apache.test.riina-k.me",
                "EndpointID": "f04eb5b28c3fbcb31112c466524c68a315f3de9f574c4afdb91c6523832b139f",
                "MacAddress": "02:42:ac:13:00:04",
                "IPv4Address": "172.19.0.4/16",
                "IPv6Address": ""
            },
            "5f67b64c90b62b5debab7a49bc52649258a660e72f2dbce846def4f7d6621949": {
                "Name": "nginx-proxy",
                "EndpointID": "f25260af953ece2799b91cb7f4a16a36d676d2b5c10244d92e6a5f50c1355514",
                "MacAddress": "02:42:ac:13:00:02",
                "IPv4Address": "172.19.0.2/16",
                "IPv6Address": ""
            },
            "b7211ce9a57da9ec2ecb53016c2ad82903cdfe4fb7fd0165bb55df3ac3f12415": {
                "Name": "apache.riina-k.me",
                "EndpointID": "06bbe69ed4679af333121e7d9169d40e852755549e6b5e98502ae3077e368839",
                "MacAddress": "02:42:ac:13:00:05",
                "IPv4Address": "172.19.0.5/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
[root@riina-02 proxy]# docker network inspect app_backend_bridge
[
    {
        "Name": "app_backend_bridge",
        "Id": "e6d33e3cefee286021381552f3f8957db0eaa96fd0ad0623d089c1fa579a573b",
        "Created": "2019-03-23T14:28:57.081913745+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.24.0.0/16",
                    "Gateway": "172.24.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "9c75f516c99ad4d86efc525eee01a84e4b6b07120fee18a958a7f01e6d2a8da8": {
                "Name": "docker-mysql",
                "EndpointID": "ef995e7f767c66cf75ec89f827a612e4528285dfa991424606a1e10502fbc80f",
                "MacAddress": "02:42:ac:18:00:03",
                "IPv4Address": "172.24.0.3/16",
                "IPv6Address": ""
            },
            "b7211ce9a57da9ec2ecb53016c2ad82903cdfe4fb7fd0165bb55df3ac3f12415": {
                "Name": "apache.riina-k.me",
                "EndpointID": "a9a81ed84977be76e96d06603ba6214ceea86c8402959d5d9a7cbe4aed3a310e",
                "MacAddress": "02:42:ac:18:00:02",
                "IPv4Address": "172.24.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.network": "backend_bridge",
            "com.docker.compose.project": "app"
        }
    }
]

想定通り、フロント側には nginx, letsenctypt, 2つの Apache の4つが、バックエンドには MariaDB, apache.riina-k.me の2つが繋がっています。
さっそく PHP を動かしてみましょう。
まだデータベースを作成していないため、「すでに存在するデータベース一覧を取得」という SQL を投げてみます。

/var/docker/proxy/app/riina-k.me/var/www/html/index.php

<h1>riina-k.me</h1>

<?php
	ini_set('display_errors', true);

	echo "connect database<br />\n";
	try {
		$pdo = new PDO('mysql:host=docker-mysql;', 'root', 'password'); // host に MariaDB コンテナ名を書く
		$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
		
		foreach($pdo->query('SHOW DATABASES;') as $row) {
			var_dump($row);
			echo "<br />\n";
		}
	} catch (PDOException $e) {
		echo 'erorr : ';
		echo $e->getMessage();
		exit;
	}
?>

https://riina-k.me/

Fatal error: Class 'PDO' not found in /var/www/html/index.php on line 8

あれ。
mysql ドライバが入っていないようでした。
riina-k.me/Dockerfile を編集して

/var/docker/proxy/app/riina-k.me/Dockerfile

FROM centos:latest

RUN yum -y update
RUN yum -y install httpd
RUN rm -f /etc/httpd/conf.d/welcome.conf
RUN yum install -y php | true 
RUN yum install -y net-tools | true
RUN yum install -y php-mysqli

COPY ./httpd.conf /etc/httpd/conf/
COPY ./php.ini /etc/

EXPOSE 8080

CMD /usr/sbin/httpd -DFOREGROUND

改めてコンテナ起動して実行結果を見てみる。

https://riina-k.me/

riina-k.me
connect database
array(2) { ["Database"]=> string(18) "information_schema" [0]=> string(18) "information_schema" } 
array(2) { ["Database"]=> string(5) "mysql" [0]=> string(5) "mysql" } 
array(2) { ["Database"]=> string(18) "performance_schema" [0]=> string(18) "performance_schema" } 

ちゃんと接続できてます。
一方、バックエンドに接続していないため MariaDB が見えないはずの test.riina-k.me

https://test.riina-k.me/

test.riina-k.me
connect database

Warning: PDO::__construct(): php_network_getaddresses: getaddrinfo failed: Name or service not known in /var/www/html/index.php on line 8
erorr : SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known

「そんなホスト知らんわ」というエラー。想定どおりですね。


ここで「本当に別のサーバから MariaDB は見えないか」を実証。

[root@riina-02 app]# curl localhost:3306
5.5.5-10.3.13-MariaDB-1:10.3.13+maria~bioni>'Hq&b@Wソvi/JgIv8Sw|*mysql_native_password!#08S01Got packets out of order

あれ、見えちゃってない!?

別のサーバから mysql コマンドを打ってみる。

[riina@riina-k ~]$ mysql -h riina-k.me -u root -p
Enter password:
ERROR 1045 (28000): Access denied for user 'root'@'153.120.17.157' (using password: YES)

見えちゃってる……
というわけでまた yml を編集。

/var/docker/proxy/app/docker-compose.yml

(略)

  mysql:
    image: mariadb 
    restart: on-failure
    env_file: ./mysql/.env <- 追加
    container_name: docker-mysql
    ports:
      - "3306:3306" <- 削除
    volumes:
      - ./mysql/db-data:/var/lib/mysql <- パスを変更
    environment:
      - MYSQL_ROOT_PASSWORD=password <- 削除
    networks:
      - backend_bridge

(略)

/var/docker/app/mysql/.env

DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
MYSQL_ROOT_PASSWORD=password

これでもう一度コマンド打ってみる。

[root@riina-02 app]# curl localhost:3306
curl: (7) Failed connect to localhost:3306; 接続を拒否されました

別のサーバから

[riina@riina-k ~]$ mysql -h riina-k.me -u root -p
ERROR 2003 (HY000): Can't connect to MySQL server on 'riina-k.me' (113)

よし! これで外からは見えなくなった!


というわけで、全4回にわたる Docker Compose 関連のお話でした。
だいぶ扱いにも慣れてきて、テスト環境が乱立してる riina-k.info サーバにも導入したくなっちゃいました。
なによりも SSL 化がまさかの一瞬で終わるとは! このエントリ は何だったんだ……

  1. コメント 0

  1. トラックバック 0

return top