您可以在单个服务器上托管多个独立的 Discourse 安装(使用单独的容器/端口/ app.yml),而无需使用 Discourse 的“多站点”功能。
这比多站点模式更手动,但它能保持实例隔离,并使将来将单个站点迁移到独立服务器变得更加容易。
一种实用的模式是:
• 外部 PostgreSQL(单个实例)
• 外部 Redis(单个实例)
• 多个 Discourse Web 容器
• 一个 Sidekiq 节点
• 带健康检查的反向代理
这完全避免了多站点模式,同时仍能在低流量设置中实现成本节约。
DISCOURSE 多容器运行手册
外部 PostgreSQL + Redis + HAProxy + app1 / app2
- 主机软件包
| 步骤 | 命令 |
|---|---|
| 更新系统 | apt-get update |
| 安装基础工具 | apt-get install -y ca-certificates curl gnupg lsb-release |
| 安装 HAProxy + certbot + socat | apt-get install -y haproxy certbot socat |
- Docker 网络(必需)
需要用户定义的 Docker 网络,以便容器可以通过名称解析彼此。
| 步骤 | 命令 |
|---|---|
| 创建网络 | docker network create discourse-net |
| 验证 | docker network ls | grep discourse-net |
这将使以下配置正常工作:
• DISCOURSE_DB_HOST=pg
• DISCOURSE_REDIS_HOST=redis
- 密钥
| 用途 | 命令 |
|---|---|
| PostgreSQL 超级用户 | export PG_SUPERPASS='REPLACE_ME_super_strong' |
| Discourse 数据库密码 | export DISCOURSE_DBPASS='REPLACE_ME_discordb_strong' |
| Redis 密码 | export REDIS_PASS='REPLACE_ME_redis_strong' |
| 密钥基 | export SECRET_KEY_BASE="$(openssl rand -hex 64)" |
- PostgreSQL 容器
| 步骤 | 命令 |
|---|---|
| 创建目录 | mkdir -p /var/discourse/external/postgres |
| 运行容器 | docker run -d --name pg --restart=always --network=discourse-net -e POSTGRES_PASSWORD="$PG_SUPERPASS" -v /var/discourse/external/postgres:/var/lib/postgresql/data postgres:15 |
| 验证 | docker ps | grep pg |
- 创建数据库
| 步骤 | 命令 |
|---|---|
| 创建角色 | docker exec -it pg psql -U postgres -c "CREATE ROLE discourse LOGIN PASSWORD '$DISCOURSE_DBPASS';" |
| 创建数据库 | docker exec -it pg psql -U postgres -c "CREATE DATABASE discourse OWNER discourse ENCODING 'UTF8' TEMPLATE template0;" |
| 文本搜索 | docker exec -it pg psql -U postgres -d discourse -c "ALTER DATABASE discourse SET default_text_search_config = 'pg_catalog.english';" |
| 测试登录 | docker exec -it pg psql -U discourse -d discourse -c "select 1;" |
- PGVECTOR 扩展
现代 Discourse 版本需要此扩展。
| 步骤 | 命令 |
|---|---|
| 安装 | docker exec -it pg bash -lc 'apt-get update && apt-get install -y postgresql-15-pgvector && rm -rf /var/lib/apt/lists/*' |
| 创建扩展 | docker exec -it pg psql -U postgres -d discourse -c "CREATE EXTENSION IF NOT EXISTS vector;" |
| 验证 | docker exec -it pg psql -U postgres -d discourse -c "SELECT extname FROM pg_extension WHERE extname='vector';" |
- Redis 容器
| 步骤 | 命令 |
|---|---|
| 创建目录 | mkdir -p /var/discourse/external/redis |
Redis 配置模板:
requirepass REPLACE_ME_REDIS
appendonly yes
save 900 1
save 300 10
save 60 10000
| 步骤 | 命令 |
|---|---|
| 写入配置 | tee /var/discourse/external/redis/redis.conf >/dev/null <<EOF |
| 插入密码 | sed -i "s/REPLACE_ME_REDIS/$REDIS_PASS/" /var/discourse/external/redis/redis.conf |
| 运行 Redis | docker run -d --name redis --restart=always --network=discourse-net -v /var/discourse/external/redis:/data -v /var/discourse/external/redis/redis.conf:/usr/local/etc/redis/redis.conf redis:7-alpine redis-server /usr/local/etc/redis/redis.conf |
| 测试认证 | docker exec -it redis redis-cli -a "$REDIS_PASS" ping |
- Discourse 目录布局
| 步骤 | 命令 |
|---|---|
| 创建基础目录 | mkdir -p /var/discourse |
| 进入目录 | cd /var/discourse |
| 克隆仓库 | git clone https://github.com/discourse/discourse_docker.git |
| 容器目录 | mkdir -p /var/discourse/containers |
| 共享日志 | mkdir -p /var/discourse/shared/web-only/log/var-log |
| 链接容器 | ln -sfn /var/discourse/containers /var/discourse/discourse_docker/containers |
| 链接启动器 | ln -sfn /var/discourse/discourse_docker/launcher /var/discourse/launcher |
- 应用容器
app1.yml
• Web + Sidekiq
• 端口 8001
docker_args: "--network=discourse-net"
expose:
- "8001:80"
app2.yml
• 仅 Web
• 端口 8002
• 禁用 Sidekiq
docker_args: "--network=discourse-net"
expose:
- "8002:80"
run:
- exec: bash -lc 'mkdir -p /etc/service/sidekiq && touch /etc/service/sidekiq/down'
- 引导启动
| 步骤 | 命令 |
|---|---|
| 进入目录 | cd /var/discourse/discourse_docker |
| 引导 app1 | ./launcher bootstrap app1 |
| 启动 app1 | ./launcher start app1 |
| 引导 app2 | ./launcher bootstrap app2 |
| 启动 app2 | ./launcher start app2 |
- 健康检查
| 步骤 | 命令 |
|---|---|
| app1 | curl -sSf http://127.0.0.1:8001/srv/status |
| app2 | curl -sSf http://127.0.0.1:8002/srv/status |
| app1 的 Sidekiq | docker exec -it app1 pgrep -fa sidekiq |
| app2 的 Sidekiq | `docker exec -it app2 pgrep -fa sidekiq |
- TLS 证书
| 步骤 | 命令 |
|---|---|
| 停止代理 | systemctl stop haproxy |
| 签发证书 | certbot certonly --standalone -d example.com --agree-tos -m you@example.com --non-interactive |
| 启动代理 | systemctl start haproxy |
- HAProxy 逻辑
frontend fe_discourse
bind :80
bind :443 ssl crt /etc/letsencrypt/live/example.com/haproxy.pem
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
redirect scheme https code 301 if !{ ssl_fc }
use_backend be_discourse if { nbsrv(be_discourse) gt 0 }
default_backend be_maint
backend be_discourse
balance roundrobin
option httpchk GET /srv/status
server app1 127.0.0.1:8001 check
server app2 127.0.0.1:8002 check
backend be_maint
http-request return status 503 content-type text/html string "<h1>Maintenance</h1>"
- 零停机重建
| 步骤 | 命令 |
|---|---|
| 禁用 app1 | echo "disable server be_discourse/app1" | socat stdio /run/haproxy/admin.sock |
| 重建 app1 | ./launcher rebuild app1 |
| 启用 app1 | echo "enable server be_discourse/app1" | socat stdio /run/haproxy/admin.sock |
| 步骤 | 命令 |
|---|---|
| 禁用 app2 | echo "disable server be_discourse/app2" | socat stdio /run/haproxy/admin.sock |
| 重建 app2 | ./launcher rebuild app2 |
| 启用 app2 | echo "enable server be_discourse/app2" | socat stdio /run/haproxy/admin.sock |
结束
需要 Docker 网络
外部 PostgreSQL 和 Redis
已安装 pgvector
Sidekiq 隔离到 app1
已启用 HAProxy 健康检查
维护回退已激活
支持滚动重建
将来将单个站点迁移到独立服务器
运行完全独立的 Discourse 安装(而不是多站点)的一个优势是迁移简单且风险低。
每个 Discourse 实例已具备:
• 自己的容器
• 自己的上传文件
• 自己的数据库
• 自己的 Redis 使用
• 自己的 app.yml
无需进行多站点的解耦。
高层迁移步骤
- 配置新的 VPS
在新服务器上正常安装 Docker 和 Discourse。
不要配置多站点。
- 创建完整备份
从源站点:
管理员 → 备份 → 创建备份
下载备份文件。
这包括:
• 数据库
• 上传文件
• 用户
• 设置
• 主题
- 在新服务器上恢复
在新服务器上:
• 完成初始设置
• 以管理员身份登录
• 上传备份
• 执行恢复
Discourse 会自动处理模式兼容性。
- DNS 切换
更新域名的 A 记录,使其指向新服务器的 IP。
一旦 DNS 生效,用户将无缝迁移。
- 停用旧容器
在原始服务器上:
• 停止旧容器
• 确认无误后将其删除
同一主机上的其他 Discourse 安装不受影响。
为何这比多站点更简单
在多站点设置中,迁移通常需要进行:
• 分离数据库
• 提取站点特定数据
• 调整 multisite.yml
• 重新设计 Sidekiq
• 重新配置上传和邮件
对于独立安装,这些都不需要。
每个站点已经是独立的。
总结
此方法以早期稍高的运维复杂性为代价,换取了后期非常简单的分离。
它特别适合实验阶段或早期社区建设。
何时此方法可能不太合适
在以下情况下,此设置通常不是一个好主意:
• 站点预计早期就有中等或高流量
• 您严重依赖 Discourse 官方支持
• 您不习惯调试 Docker、网络或反向代理
• 正常运行时间要求严格或关乎业务关键
• 多个站点在运维上紧密耦合
• 您期望在所有实例上进行频繁的插件实验
在这些情况下,以下任一方案通常会导致更少的运维意外:
• 受支持的多站点设置
或
• 每个 Discourse 安装使用独立的服务器
重要提示
此方法增加了基础设施的灵活性,
但也增加了管理员的责任。
当操作人员能够掌控整个技术栈,
并将偶尔的故障视为学习过程的一部分时,此方法效果最佳。
如果稳定性和可支持性是主要目标,
那么受支持的配置几乎总是更好的选择。