Docker でローカル環境構築。コンテナ同士を相互にアクセスできるようにする + 独自ドメインで管理

それぞれ違うコンテナにアクセスできるようにするまでに時間がかかったのでメモ。

例えばフロントエンドとバックエンドがあって、それぞれ別々に git で管理するのが普通だと思う。
そしてそれぞれに docker-compose.yaml があるのも多分普通だと思う。

Docker 初心者が普通に書いていくと別コンテナから別コンテナにアクセスできないことがわかってきた。
別コンテナにアクセスするには同じネットワーク内でコンテナを起動しないとアクセスできない。
(他に方法があるかもしれないけど、この方法が1番しっくりきた)

また、 localhost:8080 でポート番号を変更してそれぞれ管理するのは厳しいと思ったので、
ドメインで管理できるように nginx-proxy を使った。

同じネットワーク内で指定したドメインで別コンテナに cURL でアクセスすると
「curl: (7) Failed to connect to xxxx.xxx port 80: Connection refused」
というエラーが起きたのでこれも解決するのに時間がかかった。

環境

  • MacBook Pro (macOS: Big Sur)

ファイル構成

/backend
  ├ /app
  │   └ /public
  │       └ index.html
  ├ docker-compose.yaml
  ├ Dockerfile
  └ /nginx
      └ default.conf
/frontend
  ├ /app
  │   ├ /bin
  │   │  └ composer
  │   ├ composer.json
  │   └ /public
  │       └ index.html
  ├ docker-compose.yaml
  ├ Dockerfile
  └ /nginx
      └ default.conf
/nginx-proxy
  └ docker-compose.yaml

サンプルコード (nginx-proxy)

  • localhost:8080 ではなく、ドメインでアクセスできるようにする。

nginx-proxy/docker-compose.yaml

  • ポート番号を 8080 から 80 にする。
    • ポート番号をつけなくてもよくなる。
  • jwilder/nginx-proxy を使うことによってドメインで管理できるようになる。
  • 「docker-sample-network」というネットワークで別々のコンテナを繋ぐ
version: "3"

services:
    app:
        container_name: nginx-proxy
        image: jwilder/nginx-proxy
        ports:
            - 80:80
        volumes:
            - /var/run/docker.sock:/tmp/docker.sock:ro
        networks:
            - docker-sample-network

networks:
    docker-sample-network:
        name: docker_sample_network

サンプルコード (backend)

  • 適当なバックエンドを用意する。
  • 1行文字列を返すだけ

backend/docker-compose.yaml

  • ドメイン名を「api.docker.sample」にする。
  • コンテナ名を「docker_sample_api」に固定する。
  • ネットワーク名「docker-sample-network」を使用する。
version: "3"

services:
    app:
        build:
            context: .
        container_name: docker_sample_api
        volumes:
            - ./app:/var/www/html
        networks:
            - docker-sample-network
        environment:
            - VIRTUAL_HOST=api.docker.sample

networks:
    docker-sample-network:
        name: docker_sample_network

backend/Dockerfile

FROM nginx:1

WORKDIR /var/www/html

COPY ./app /var/www/html
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf

backend/app/public/index.html

1行返すだけの適当な html ファイル

hello world, docker-sample/nginx-proxy-frontend-backend/backend

backend/nginx/default.conf

server {
	listen       80;
	listen  [::]:80;
	server_name  localhost;

	root /var/www/html/public;

	index index.html;

	# redirect server error pages to the static page /50x.html
	#
	error_page   500 502 503 504  /50x.html;
	location = /50x.html {
		root   /usr/share/nginx/html;
	}

	# deny access to .htaccess files, if Apache's document root
	# concurs with nginx's one
	#
	location ~ /\.ht {
		deny  all;
	}
}

サンプルコード (frontend)

  • 適当に用意したフロントエンドでバックエンドに cURL でアクセスする。
  • cURL でアクセスする時は「コンテナ名 (container_name)」でアクセスする。
  • Ajax でアクセスする時は「ドメイン名」でアクセスうする。
  • コンテナを起動させてそのコンテナにログインして、
    「$ curl [container_name]」すればいいだけなんだけど、
    今回はフロントエンドもドメイン指定してるのでそれっぽく作ってみた。

frontend/docker-compose.yaml

  • ドメイン名を「docker.sample」にする。
  • ネットワーク名「docker-sample-network」を使用する。
    • php コンテナにも networks: docker-sample-network を指定しないと PHP が動かない。
  • 適当に PHP-fpm を使う。
    • app と php それぞれに volumes: に「- ./app:/var/www/html」 してるのは、
      app だけに記述すると PHP が動かず、
      php だけに記述すると PHP 以外のファイルを更新するとうまく同期されなかったため。
version: "3"

services:
    app:
        build:
            context: .
        volumes:
		    - ./app:/var/www/html
            - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
        depends_on:
            - php
        networks:
            - docker-sample-network
        environment:
            - VIRTUAL_HOST=docker.sample
    php:
        # https://hub.docker.com/_/php
        image: php:fpm
        volumes:
            - ./app:/var/www/html
        networks:
            - docker-sample-network

networks:
    docker-sample-network:
        name: docker_sample_network

frontend/Dockerfile

FROM nginx:1

WORKDIR /var/www/html

COPY ./app /var/www/html
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf

frontend/app/bin/composer

frontend/app/composer.json

{
    "require": {
        "guzzlehttp/guzzle": "^7.2"
    }
}

frontend/app/public/index.php

  • cURL でアクセスする時は「コンテナ名 (container_name)」を指定する。
  • Ajax でアクセスする時は「ドメイン名」を指定する。
  • 今回は cURL で試すので Guzzle を使う。
    • http://docker_sample_api
    • ドメイン名を指定すると「curl: (7) Failed to connect to xxxx.xxx port 80: Connection refused」というエラーが起きる。
  • Guzzle のサンプルコードをほとんどそのまま使用。
<!DOCTYPE html>
<html lang="ja" dir="ltr">
	<head>
		<meta charset="utf-8">
		<title>docker-sample/nginx-proxy-frontend-backend/frontend</title>
	</head>
	<body>
		<h1>hello world</h1>
		<p>docker-sample/nginx-proxy-frontend-backend/frontend</p>
		<hr>
		<h2>cURL</h2>
		<a href="http://api.docker.sample/">backend &gt;&gt;</a><br>
		<br>
		<?php

require './../vendor/autoload.php';

$client = new \GuzzleHttp\Client();
// $response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle');
$response = $client->request('GET', 'http://docker_sample_api/');

echo $response->getStatusCode(); // 200
echo '<br>';
echo $response->getHeaderLine('content-type'); // 'application/json; charset=utf8'
echo '<br>';
echo $response->getBody(); // '{"id": 1420053, "name": "guzzle", ...}'

		?>
	</body>
</html>

frontend/nginx/default.conf

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    root /var/www/html/public;

    index index.php index.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # using PHP
    # https://gist.github.com/md5/d9206eacb5a0ff5d6be0
    #
    location ~ [^/]\.php(/|$) {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        if (!-f $document_root$fastcgi_script_name) {
            return 404;
        }

        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO       $fastcgi_path_info;
        fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;

        fastcgi_pass   php:9000;
        fastcgi_index  index.php;
    }

    # using PHP Framework
    #
    try_files $uri $uri/ @rewrite;

    location @rewrite {
        rewrite ^(.*)$ /index.php?_url=$1;
    }
    location ~* ^/(css|img|js|flv|swf|download)/(.+)$ {
        root /var/www/html/public;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    location ~ /\.ht {
        deny  all;
    }
}

実行する

/etc/hosts

  • 存在しないドメインを使う時は /etc/hosts に使用するドメインを追加する。
$ sudo vi /etc/hosts
  • 最後の行に今回使用するドメインを追加
    • docker.sample
    • api.docker.sample
# /etc/hosts

##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1	localhost
255.255.255.255	broadcasthost
# ::1             localhost

# Added by Docker Desktop
# To allow the same kube context to work on the host and the container:
127.0.0.1 kubernetes.docker.internal
# End of section

127.0.0.1 docker.sample api.docker.sample

インストール

  • コンテナを起動する前に事前にインストールしておく
$ cd /path/to/your/frontend/app
$ bin/composer install

コンテナ起動

下記コンテナを全て起動させる。

  • nginx-proxy
  • backend
  • frontend
$ cd /path/to/your/nginx-proxy
$ docker-compose up

$ cd /path/to/your/backend
$ docker-compose up

$ cd /path/to/your/frontend
$ docker-compose up

ネットワークを確認する

「docker_sample_network」というネットワークが作成されている。

$ docker network ls

NETWORK ID     NAME                    DRIVER    SCOPE
33da857e13cc   bridge                  bridge    local
ff4eb0b291fb   docker_sample_network   bridge    local
2009c1425362   host                    host      local
5eec69202c29   none                    null      local

コンテナを確認する

ネットワークは同じで、
それぞれ別々にコンテナが起動している。

$ docker ps

CONTAINER ID   IMAGE                 COMMAND                  CREATED              STATUS              PORTS                NAMES
84b7c82b51e0   backend_app           "/docker-entrypoint.…"   About a minute ago   Up About a minute   80/tcp               docker_sample_api
e2cff9d5743f   frontend_app          "/docker-entrypoint.…"   About a minute ago   Up About a minute   80/tcp               frontend_app_1
9c1f32a55681   php:fpm               "docker-php-entrypoi…"   10 minutes ago       Up About a minute   9000/tcp             frontend_php_1
9b758f0f5077   jwilder/nginx-proxy   "/app/docker-entrypo…"   10 minutes ago       Up About a minute   0.0.0.0:80->80/tcp   nginx-proxy