SSL付きで1つのインストールに3つのドメイン用3コンテナを実行

Let’s Encrypt を数日間いじった後、複数のフォ

1 目的

以下のようないくつかのドメインを持ち、3 つのフォーラムを運用したいと仮定します。

bbs.antivte.com
cp.antivte.com
ytb.antivte.com

2. Discourse コンテナ

2.1 準備

異なるフォーラム用に 3 つの app.yml ファイルを用意してください。名前は bbs.yml、cp.yml、ytb.yml など、お好みの名前で構いません。

内容は以下のようになります。

注意:外部の nginx とバックエンドの Discourse コンテナを接続する際、80 ポートや 443 ポートでリッスンする代わりに Unix ソケットを使用します。また、バックエンドコンテナの SSL 設定も削除します。

注意:ここでは各フォーラムごとに 1 つのコンテナのみを使用し、データ用と Web 用のコンテナを個別に用意していません。

## これはオールインワンのスタンドアロン Discourse Docker コンテナテンプレートです
##
## このファイルを編集した後、必ず再構築してください
## /var/discourse/launcher rebuild app
##
## 編集時は非常に注意してください!
## YAML ファイルは空白やアライメントのミスに極めて敏感です!
## 必要に応じて http://www.yamllint.com/ でこのファイルを検証してください

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Lets Encrypt (https) を追加したい場合は、以下の 2 行のコメントを外してください
#  - "templates/web.ssl.template.yml"
#  - "templates/web.letsencrypt.ssl.template.yml"
  - "templates/web.socketed.template.yml"  # <-- 追加
## このコンテナが公開する TCP/IP ポートはどれですか?
## Discourse を Apache や nginx などの他の Web サーバーとポートを共有したい場合は、
## https://meta.discourse.org/t/17247 を参照してください
**#expose:**
**#  - "8080:80"   # http**
**#  - "8443:443" # https**

params:
  db_default_text_search_config: "pg_catalog.english"

  ## db_shared_buffers を総メモリの最大 25% に設定します。
  ## ブートストラップ時に検出された RAM に基づいて自動設定されますが、上書きすることも可能です
  db_shared_buffers: "256MB"

  ## ソート性能を向上させることができますが、接続ごとのメモリ使用量が増加します
  #db_work_mem: "40MB"

  ## このコンテナが使用する Git リビジョンはどれですか?(デフォルト: tests-passed)
  #version: tests-passed

env:
  LANG: en_US.UTF-8
  # DISCOURSE_DEFAULT_LOCALE: en

  ## 同時 Web リクエストのサポート数です。メモリと CPU コア数に依存します。
  ## ブートストラップ時に検出された CPU に基づいて自動設定されますが、上書きすることも可能です
  UNICORN_WORKERS: 4

  ## TODO: この Discourse インスタンスが応答するドメイン名
  ## 必須です。Discourse は IP アドレスのみでは動作しません。
  DISCOURSE_HOSTNAME: bbs.antivte.com

  ## 上記で指定したホスト名と同じ (-h オプション) でコンテナを起動したい場合は、
  ## 以下のコメントを外してください(デフォルト: "$hostname-$config")
  #DOCKER_USE_HOSTNAME: true

  ## TODO: 初期登録時に管理者および開発者として設定されるメールアドレスのリスト
  ## 例: 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'techempower@163.com'

  ## TODO: 新しいアカウントの検証や通知送信に使用する SMTP メールサーバー
  # SMTP アドレス、ユーザー名、パスワードは必須です
  # 警告: SMTP パスワードに含まれる '#' 文字は問題を引き起こす可能性があります!
  DISCOURSE_SMTP_ADDRESS: smtp.mailgun.org
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: postmaster@mail.antivte.com
  DISCOURSE_SMTP_PASSWORD: "67c9458eb7a6ff11b4db70f097b1b5c3-f7910792-0e7dbcc9"
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (オプション、デフォルト: true)

  ## Lets Encrypt テンプレートを追加した場合、以下のコメントを外して無料の SSL 証明書を取得してください
  LETSENCRYPT_ACCOUNT_EMAIL: techempower@163.com

  ## この Discourse インスタンスの http または https CDN アドレス(プルするように設定)
  ## 詳細は https://meta.discourse.org/t/14857 を参照してください
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com

## Docker コンテナはステートレスです。すべてのデータは /shared に保存されます
volumes:
  - volume:
      **host: /var/discourse/shared/bbs**
      guest: /shared
  - volume:
      **host: /var/discourse/shared/bbs/log/var-log**
      guest: /var/log

## プラグインはここに記述します
## 詳細は https://meta.discourse.org/t/19157 を参照してください
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/procourse/procourse-installer

## ビルド後に実行するカスタムコマンド
run:
  - exec: echo "カスタムコマンドの開始"
  ## 最初の登録時の 'From' メールアドレスを設定したい場合は、以下のコメントを外して変更してください
  ## 最初の登録メールを受信した後、再度コメントアウトしてください。一度だけ実行すれば十分です
  #- exec: rails r "SiteSetting.notification_email='info@unconfigured.discourse.org'"
  - exec: echo "カスタムコマンドの終了"

2.2 設定

以下のようなセットアップスクリプトを作成します。

#!/usr/bin/env bash
./launcher bootstrap bbs
./launcher bootstrap test
./launcher bootstrap cp
./launcher bootstrap ytb
./launcher start bbs
./launcher  start test
./launcher  start  cp
./launcher  start  ytb

このスクリプトをすでに使用して各コンテナを一度起動済みの場合、app.yml の内容を編集した後は、以下のコマンドを実行して変更を反映させる必要があります。

./launcher rebuild bbs

2.3 確認

3 つのコンテナが正常に起動していることを確認し、Unix ソケットが適切に機能しているかチェックします。

root@docker-s-1vcpu-2gb-sgp1-01:~# docker ps
CONTAINER ID        IMAGE                 COMMAND             CREATED             STATUS              PORTS               NAMES
9702f94ea9b4        local_discourse/bbs   "/sbin/boot"        9 hours ago         Up 9 hours                              bbs
dc13c303c38e        local_discourse/cp    "/sbin/boot"        9 hours ago         Up 9 hours                              cp
dafa592ee16f        local_discourse/ytb   "/sbin/boot"        9 hours ago         Up 9 hours                              ytb
root@docker-s-1vcpu-2gb-sgp1-01:~#  curl --unix-socket /var/discourse/shared/bbs/nginx.http.sock http:/images/json
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <title>血栓之家</title>

3. 外部 nginx

サーバーに nginx または OpenResty をインストールし、お好みのディレクトリに nginx.conf を作成します。ディレクトリは nginx の実行コマンドにのみ影響します。nginx.conf の内容は以下の通りです。

ここでは Cloudflare の証明書と鍵を取得し、ホストディレクトリに以下のパスで配置しています。

3.1 nginx.conf

    ssl_certificate      /var/discourse/shared/ssl/antivte.com.cert.pem;
    ssl_certificate_key  /var/discourse/shared/ssl/antivte.com.key.pem;

将来的には、外部 nginx での Let’s Encrypt による自動証明書・鍵の取得方法についても説明します。

events {
  worker_connections 1024;
}

http {
  # "auto_ssl" 共有辞書には、証明書データを保持するのに十分なストレージ容量を割り当てる必要があります。
  # 1MB のストレージで約 100 個のドメインの証明書を保持できます。
  lua_shared_dict auto_ssl 1m;
  # "auto_ssl_settings" 共有辞書は、ポート 8999 のフックサーバーで使用されるシークレットなど、
  # 各種設定を一時的に保存するために使用されます。変更または省略しないでください。
  lua_shared_dict auto_ssl_settings 64k;

  # OCSP ステープリングを機能させるには、DNS リゾルバーの定義が必要です。
  #
  # この例では Google の DNS サーバーを使用しています。システムデフォルトの DNS サーバーを使用したい場合は、
  # /etc/resolv.conf で確認できます。ネットワークが IPv6 非対応の場合、
  # "ipv6=off" フラグ(例: "resolver 8.8.8.8 ipv6=off")を使用して IPv6 結果を無効化することも可能です。
  resolver 8.8.8.8;
server {
    listen 80; listen [::]:80;
    server_name bbs.antivte.com;  # <-- ここを変更

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;  listen [::]:443 ssl http2;
    server_name bbs.antivte.com;  # <-- ここを変更

    ssl_certificate      /var/discourse/shared/ssl/antivte.com.cert.pem;
    ssl_certificate_key  /var/discourse/shared/ssl/antivte.com.key.pem;
#    ssl_dhparam          /var/discourse/shared/standalone/ssl/dhparams.pem;
    ssl_session_tickets off;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;

    http2_idle_timeout 5m; # デフォルトの 3m から 5m に引き上げ

    location / {
        proxy_pass http://unix:/var/discourse/shared/bbs/nginx.http.sock;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

server {
    listen 80; listen [::]:80;
    server_name cp.antivte.com;  # <-- ここを変更

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;  listen [::]:443 ssl http2;
    server_name cp.antivte.com;  # <-- ここを変更

    ssl_certificate      /var/discourse/shared/ssl/antivte.com.cert.pem;
    ssl_certificate_key  /var/discourse/shared/ssl/antivte.com.key.pem;
#    ssl_dhparam          /var/discourse/shared/standalone/ssl/dhparams.pem;
    ssl_session_tickets off;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;

    http2_idle_timeout 5m; # デフォルトの 3m から 5m に引き上げ

    location / {
        proxy_pass http://unix:/var/discourse/shared/cp/nginx.http.sock;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP $remote_addr;
    }
}



server {
    listen 80; listen [::]:80;
    server_name ytb.antivte.com;  # <-- ここを変更

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;  listen [::]:443 ssl http2;
    server_name ytb.antivte.com;  # <-- ここを変更

    ssl_certificate      /var/discourse/shared/ssl/antivte.com.cert.pem;
    ssl_certificate_key  /var/discourse/shared/ssl/antivte.com.key.pem;
#    ssl_dhparam          /var/discourse/shared/standalone/ssl/dhparams.pem;
    ssl_session_tickets off;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;

    http2_idle_timeout 5m; # デフォルトの 3m から 5m に引き上げ

    location / {
        proxy_pass http://unix:/var/discourse/shared/ytb/nginx.http.sock;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP $remote_addr;
    }
}



}

3.2 nginx の起動

nginx.conf を配置したディレクトリに移動し、以下のコマンドを実行します。

 nginx -p `pwd`/ -c nginx.conf

4. これで目的が達成されます

おめでとうございます!

これは、すでに利用可能な Discourse のマルチサイト機能のより複雑なバージョンのようです。同じリソースを巡って競合する3つの独立したコンテナを動かすことで、1つ(ウェブとデータを分離する場合は2つ)を動かす場合に比べて、どのような利点があるのでしょうか?

それはすべて予算の問題です。また、プロトタイプで顧客やマーケティングのテストを行う前の段階と考えることもできます。

予算編成の段階であっても、マルチサイトの方がはるかにシンプルで実用的な実装になると思います。そうすれば、設定面で気にすべきことが減り、クライアントや顧客の要件に充てる時間がより多く確保できます。

これらはすべて、単一サーバー上でテスト環境と本番環境を構築したいという要望によるものです。ホストの 80 ポートと 443 ポートが使用されている場合、これを実現する他の方法はありません。

シェアしていただきありがとうございます。ただ、これが#howto として成立するには、さらに詳細な情報が必要だと感じますので、カテゴリを変更します。