やりたいこと

自分はさくらのVPSを借りて複数のサービスを運営している。

本当なら1つのサービスに対して1台のVPSを契約するのが理想だが、趣味の開発なのでそこまでお金はかけられない。

なので1台のVPS上に複数のコンテナを立ち上げて、nginxのリバースプロキシを使ってアクセスを振り分けるという形式を採用している。

例えばhttps://a.zurukumo.devへのアクセスはコンテナAに、https://b.zurukumo.devへのアクセスはコンテナBに振り分けるというような感じだ。

nginxの設定もSSL証明書の発行も手動で実行していたが、だんだんめんどくさくなってきたので、もっと楽にできる方法がないか調べてみた。

そこで発見したのがnginx-proxyacme-companionというDockerイメージだった。

この2つのイメージを組み合わせることで、最小限の設定でリバースプロキシの設定からSSL証明書の発行までを自動化することができた。

今回の記事はその方法のご紹介である。

方法

まず、ざっくりとした構成を仮定する。

kagoとnbamashという2つのサービスを運営していて、どちらのサービスもwebコンテナ + dbコンテナという構成である。

両サービスともdocker composeで立ち上げているとする。

ここでhttps://kago-api.zurukumo.devへのアクセスはkagoのwebコンテナに、https://nbamash-api.zurukumo.devへのアクセスはnbamashのwebコンテナに振り分けることを目標にする。

1. DNSに利用したいドメインを登録しておく

向き先はVPSのIPアドレスにする。

自分の場合はこんな感じ(黒塗りの部分は両方同じIPアドレス)。

DNSの設定

2. ネットワークを作成する

nginx-proxyとacme-companionは動いてるDockerコンテナを検知すると自動でプロキシの設定やSSL証明書の発行を行ってくれる。

動いているコンテナを検知できるようにするるために、nginx-proxyとacme-companionを各種サービスと接続するためのネットワークを作成する。

docker network create proxy

今回はproxyという名前のネットワークを作成したが、名前はご自由に。

3. リバースプロキシ&SSL証明書取得用のdocker-compose.ymlを作成する

公式GitHubのDockerコマンドをdocker-compose.ymlに素直に書き換えた上で、networksの設定だけ追加した。

proxy/docker-compose.yml
services:
  proxy:
    image: nginxproxy/nginx-proxy
    ports:
      - 80:80
      - 443:443
    volumes:
      - certs:/etc/nginx/certs
      - html:/usr/share/nginx/html
      - /var/run/docker.sock:/tmp/docker.sock:ro
    networks:
      - proxy

  cert:
    image: nginxproxy/acme-companion
    volumes_from:
      - proxy
    volumes:
      - acme:/etc/acme.sh
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - DEFAULT_EMAIL=(自分のメールアドレス)
    networks:
      - proxy

volumes:
  certs:
  html:
  acme:

networks:
  proxy:
    external: true

HTTP-01 チャレンジに80番ポートを使用するため、80番ポートを開放している。

もしファイアフォールなどで80番ポートを閉じている場合は、開放しておく必要がある。

4. 各種サービスのdocker-compose.ymlを微修正する

例えば以下のようなkago用のdocker-compose.ymlがあるとする。

kago/docker-compose.yml
services:
  web:
    (...中略...)

  db:
    (...中略...)

nginx-proxyとacme-companionがkagoのwebコンテナを検知できるようにネットワークを追加する。

kago/docker-compose.yml
services:
  web:
    (...中略...)
    networks:
      - default
      - proxy

  db:
    (...中略...)
    networks:
      - default

networks:
  proxy:
    external: true

ここでnetworksにproxyだけでなくdefaultも指定している。

docker composeはデフォルトでdefaultというネットワークを作成してくれて、そのおかげで特に設定せずともwebコンテナとdbコンテナが接続できている。

しかし、今回はnetworksに明示的にproxyを設定したことで、defaultの設定がスキップされてしまう。

そこでdefaultの設定も明示的に指定して、webコンテナとdbコンテナの通信経路を確保している。

5. SSL証明書の発行用の環境変数の設定

各種サービスのwebコンテナに以下に示す環境変数を設定する。

この設定を行うことでacme-companionが自動でkago-api.zurukumo.dev用のSSL証明書を発行してくれる。

ちなみにVIRTUAL_PORTはサーバが80番ポートで動いている場合のみ省略可能。

kago/docker-compose.yml
services:
  web:
    (...中略...)
    environment:
      - VIRTUAL_HOST=kago-api.zurukumo.dev
      - VIRTUAL_PORT=8000
      - LETSENCRYPT_HOST=kago-api.zurukumo.dev
    networks:
      - default
      - proxy

  db:
    (...中略...)
    networks:
      - default

networks:
  proxy:
    external: true

6. コンテナを立ち上げる

あとはproxy/docker-compose.ymlkago/docker-compose.ymlnbamash/docker-compose.ymlをそれぞれ立ち上げるだけである。

まとめ

こんなに便利なDockerイメージがあるんですなあ…。