メインコンテンツまでスキップ

nginxのdocker imageでリバースプロキシを構築する

· 約10分

本投稿の内容

ヒント
  • nginx のリバースプロキシの設定方法
  • Let's Encrypt を使った証明書の発行
  • 上記を docker compose で行う

nginxのリバースプロキシの設定

とりあえず次の2点を記述すればプロキシできます。

ディレクティブ説明
server_nameプロキシしたいドメイン名
proxy_passプロキシ先のパス

/ext/nginx/nginx.conf の記述例

http {
...
server {
listen 80;
server_name {your domain};
location / {
proxy_pass http://{backend domain}:{backend port}/;
}
}
}

複数のドメインを1つのバックエンドにプロキシしたい場合は、server_name にスペース区切りでドメインを並べればOKです。

server_name {your domain 1} {your domain 2};

ただし、これだけだとプロキシ先に Host ヘッダが渡らなくて、プロキシ先でページ遷移した時に localhost にアクセスしてしまう問題があります。諸々ヘッダも渡すようにした方がよいでしょう。

  server {
...
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
...
}

あとは必要であれば HTTPS の設定も追加した方がよいでしょう。

  server {
...
listen 443 ssl;

ssl_certificate /etc/letsencrypt/live/{your domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{your domain}/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;

ssl_protocols TLSv1.3 TLSv1.2;
ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers off;

add_header Strict-Transport-Security "max-age=2592000" always;
...
}

証明書のパスは証明書は Let's Encrypt で発行することを前提としています。Let's Encrypt による証明書の発行は後ほど紹介します。

別のプロキシ先を追加したい場合は、server ディレクティブを追加すればOKです。

nginx の Docker 化

上記の nginx.conf が設定された Docker イメージを作成します。Dockerfile と同じディレクトリに nginx.conf を配置して、以下のような Dockerfile を作ります。

FROM nginx:1.23.3-alpine

COPY nginx.conf /etc/nginx/nginx.conf

docker compose 化

docker compose を用いてビルドします。以下のようなディレクトリ構成にします。

.
├── docker-compose.yml
└── nginx
├── Dockerfile
└── nginx.conf

docker-compose.yml の内容は以下です。

version: '3'

services:
nginx:
build: ./nginx
restart: always
ports:
- 80:80
- 443:443

ビルドして起動します。

docker-compose up -d --build nginx

停止するには以下のコマンドを叩きます。

docker-compose rm -fsv nginx

Dockerのロギング

突然ですが、ここで Docker のロギングについて考えたいと思います。というのも nginx が吐くログをローテーションするためです。そこでDocker のロギングの基本的なことを以下にまとめました。

ヒント
  • 基本的にはコンテナ内のログは標準出力/標準エラー出力に吐く
  • 標準出力で吐かれたログは、ホスト側の /var/lib/docker/containers/<container id>/ に保存される
  • コンテナが吐くログの形式は、コンテナ起動時に --log-driver オプションで指定する
  • デフォルトの log-driver は json-file
  • サポートされている log-driver は 公式ページ を参照
  • --log-opt max-size でログファイルのサイズを指定できる
  • --log0opt max-file で保存しるログファイルの数を指定できる

nginxのログを標準出力/標準エラー出力に吐く

ということで、コンテナ内のログは標準出力に吐くのがよいらしいので、nginx.conf に次の2行を追加です。

http {
...
access_log /dev/stdout main;
error_log /dev/stderr warn;
...
}

そして、ログをローテーションするための設定を docker-compose.yml に追加します。

version: '3'

services:
nginx:
build: ./nginx
restart: always
ports:
- 80:80
- 443:443
logging:
driver: "json-file"
options:
max-size: "100k"
max-file: "10"

Let's Encrypt の設定

さて、証明書の発行です。前述のように証明書の発行は Let's Encrypt を用いて行います。ドメインの取得、DNSレコードの設定をしている前提でお話を進めます。

Let's Encrypt の証明書を発行する certbot というコマンドがあります。このコマンドを実行できる公式の Docker イメージがあるので、それを用いることにしましょう。

docker を用いて certbot を実行するには以下のようにします。

docker run --rm certbot/certbot:latest {options}

certbot は目的に応じてサブコマンドを指定するようになっています。証明書を発行するサブコマンドは certonly です。certonly には standalone と webroot という重要なオプションがあります。

このオプションを説明する前に、certbot が証明書を発行する手順を知る必要があります。まず、certbot は発行したい証明書のドメインでが正しいか判断しなければなりません。どうするのかというと、certbot は実際にそのドメインの 80 番ポートにアクセスして、certbot が期待するレスポンスを受け取ることで、そのドメインが正しいことを確認します。つまり、証明書を発行するには、certbot が実行される端末でウェブサーバが予め起動してある必要があります。さらに、そのウェブサーバに外部から 80 番ポートと 443 番ポートでアクセスでき、certbot がそのウェブサーバのドキュメントルートに書き込めることも必要です。

certbot の基本的に仕組みが分かったところで、standalone と webroot の説明に戻ります。この2つのオプションは、証明書を発行する際に使用するウェブサーバの選択肢を表しています。つまり、standalone は「ウェブサーバは certbot が自分で立てるので、あたなたは立てなくてよいですよ」というオプションです。一方、webroot は「ウェブサーバはあなたが立ててくださいね。ポートは 80 と 443 の両方用意して、さらにドキュメントルートも書き込めるようにしてね。」というオプションです。webroot を使う場合は、ドキュメントルートのディレクトリをオプションとして渡す必要があります。

以下に webroot と standalone の例を紹介します。

webroot を使った例

certbot certonly --webroot -w /var/www/sample/public -d xxxx.sample.com

standalone を使った例

certbot certonly --standalone -d xxxx.sample.com

ここでは、standalone を使うことにします。前述のように certbot の docker image を使うので、通常のコマンドに比べていくつか注意することがあります。それは、standalone のサーバにアクセスできるようにコンテナのポートを晒したり、証明書の出力先をホスト側のディレクトにマウントしたりすることです。以下がその例です。

docker run --rm -p 80:80 -p 443:443 -v /etc/letsencrypt:/etc/letsencrypt -v /var/log/letsencrypt:/var/log/letsencrypt certbot/certbot:latest certonly --standalone  -m {your email} --agree-tos -d {your domain}

Let's Encrypt の docker-compose.yml

最後に docker-compose.yml にまとめます。certbot で出力した証明書を nginx と共有する必要があるので、named volume を使っています。

version: '3'

services:
nginx:
build: ./nginx
restart: always
ports:
- 80:80
- 443:443
volumes:
- letsencrypt-cert:/etc/letsencrypt:ro
logging:
driver: "json-file"
options:
max-size: "100k"
max-file: "10"

letsencrypt:
image: certbot/certbot
ports:
- 80:80
- 443:443
volumes:
- letsencrypt-cert:/etc/letsencrypt
- letsencrypt-log:/var/log/letsencrypt
entrypoint:
certbot certonly
--standalone
-m {your email}
--agree-tos
-d {your domain}

volumes:
letsencrypt-cert:
letsencrypt-log:

証明書を発行するには、以下のコマンド実行します。

docker-compose up letsencrypt

そして、nginx を起動します。

docker-compose up -d --build nginx

今回は、以上になります。

参考