在折腾了几天 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 等,按你喜好命名。
内容应如下所示:
请注意,我们使用 Unix socket 来连接外部的 Nginx 和后端的 Discourse 容器,而不是监听 80 或 443 端口,同时也移除了后端容器的 SSL 配置。
请注意,这里每个论坛只有一个容器,而不是为每个论坛分别设置数据容器和 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"
## 如果你希望添加 Let's Encrypt (https),请取消注释以下两行
# - "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%。
## 将由 bootstrap 根据检测到的 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 核心数。
## 将由 bootstrap 根据检测到的 CPU 自动设置,你也可以覆盖
UNICORN_WORKERS: 4
## TODO: 此 Discourse 实例将响应的域名
## 必需项。Discourse 无法仅通过 IP 地址运行。
DISCOURSE_HOSTNAME: bbs.antivte.com
## 如果你想让容器以与上述指定的相同的主机名(-h 选项)启动,请取消注释
#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)
## 如果你添加了 Let's 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 "开始自定义命令"
## 如果你想设置首次注册的“发件人”电子邮件地址,请取消注释并修改:
## 收到第一封注册邮件后,请重新注释该行。只需运行一次。
#- exec: rails r "SiteSetting.notification_email='info@unconfigured.discourse.org'"
- exec: echo "自定义命令结束"
2.2 设置
创建一个如下所示的 setup 脚本:
#!/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 socket 是否正常:
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;
# 必须定义 DNS 解析器才能使 OCSP 装订功能正常工作。
#
# 此示例使用 Google 的 DNS 服务器。你可能想使用系统的
# 默认 DNS 服务器,可在 /etc/resolv.conf 中找到。如果你的网络
# 不支持 IPv6,你可能希望使用 "ipv6=off" 标志禁用 IPv6 结果
# (例如 "resolver 8.8.8.8 ipv6=off")。
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 提升
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 提升
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 提升
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. 你将得到你想要的结果
Woola!