当前位置: 首页 > news >正文

Ubuntu 18.04 部署 Discourse 的 Docker 化实践与故障排查

1. 项目概述:为什么在 Ubuntu 18.04 上部署 Discourse 仍值得认真对待

Discourse 不是又一个论坛插件,它是一套以“对话质量”为底层设计哲学的现代社区引擎——它的实时协作编辑、时间线式回溯、信任等级系统、反垃圾邮件自动策略,从代码层就拒绝了传统 PHP 论坛那种靠插件堆砌功能的路径。而 Ubuntu 18.04,虽然官方支持已于 2023 年 4 月终止,但它仍是大量生产环境、教育集群、老旧服务器和嵌入式边缘节点中真实存在的稳定基座。我去年接手的三个客户项目里,有两个运行在物理机上的 Ubuntu 18.04 LTS 环境,一台是高校实验室的旧 Dell R720,另一台是某制造企业内网隔离区的 HP ProLiant,它们既不能升级内核(因依赖特定驱动),也无法接入新版本 Docker Hub 的 TLS 1.3 强制策略。所以,“How To Install Discourse on Ubuntu 18.04”不是怀旧,而是对真实运维边界的尊重。

Discourse 官方早已将安装方式收敛为Docker Compose 单体部署模式,这并非为了炫技,而是把数据库、Redis、Sidekiq、NGINX、Mailcatcher(开发用)等十余个服务的依赖关系、端口映射、卷挂载、健康检查全部封装进docker-compose.yml和核心配置文件app.yml中。你不需要手动装 PostgreSQL 9.6、编译 Ruby 2.7.2、调试 Nginx 的proxy_buffering off参数,更不用反复修改/etc/postgresql/*/main/pg_hba.conf。Docker 把整个运行时环境打包成不可变镜像,而app.yml就是你唯一需要手写的“业务契约”——它定义了你的域名、SMTP 凭据、管理员邮箱、SSL 证书路径、备份策略,甚至是否启用 Redis 缓存压缩。这种设计让 Discourse 的可移植性极强:我在树莓派 4B(ARM64)上用相同app.yml模板跑过轻量测试站,在 AWS c5.2xlarge 上用同一套流程上线过日活 8000+ 的技术社区,差别只在于app.ymldb_shared_buffersUNICORN_WORKERS的数值调整。

你可能会问:既然 Ubuntu 20.04/22.04 更新,为什么还要啃 18.04 这块硬骨头?答案很实在:第一,很多政企采购合同规定服务器 OS 必须使用“经信委认证的长期支持版本”,而 18.04 是最后一批通过该认证的 Ubuntu 版本;第二,某些工业网关设备预装的 Debian 衍生系统,其内核 ABI 与 Ubuntu 18.04 高度兼容,复用其 Docker 安装脚本能大幅降低适配成本;第三,也是最关键的——Discourse 的 Docker 镜像构建脚本至今仍明确声明支持ubuntu:18.04基础镜像,其 CI 流水线每天都在验证该组合的构建成功率。这不是妥协,而是工程确定性的体现。当你看到./discourse-setup脚本在终端里输出All containers are healthy!的那一刻,你知道这套系统已经通过了 127 项自动化健康检查,包括 SMTP 连通性、Redis PUB/SUB 延迟、PostgreSQL WAL 归档状态、Let’s Encrypt ACME 挑战响应头校验——这些细节,远比“一键安装成功”的截图更有说服力。

2. 核心架构拆解:Discourse 的 Docker 化不是容器化,而是服务契约化

2.1 为什么必须用 Docker?绕开它的代价有多大

Discourse 官方文档开篇就写:“We do not support non-Docker installations.” 这句话常被误解为“懒政”,实则背后是十年社区运维沉淀出的血泪教训。我曾帮一家开源硬件公司迁移其老论坛,他们坚持用 Ubuntu 18.04 + Apache + 手动编译 Ruby on Rails 方式部署 Discourse 2.3。结果呢?光是解决libpq-devpostgresql-client-10的 ABI 冲突就花了三天;当他们想启用邮件通知时,发现系统自带的sendmail无法通过 Discourse 的 SMTP 认证握手(因 OpenSSL 版本太低导致 TLS 1.2 SNI 扩展缺失);最致命的是,某次内核更新后,tmpfs挂载参数变更导致 Sidekiq 后台任务队列无限重试,而错误日志里只有一行FATAL: out of shared memory,根本看不出是内核参数问题。最终他们用了 17 天才让论坛勉强上线,而用 Docker 部署同样配置,我的实测时间是 42 分钟——从apt update到收到首封管理员激活邮件。

Docker 对 Discourse 的价值,远不止于“隔离环境”。它本质是把一套复杂的分布式服务拓扑,压缩成四个可验证的契约接口:

  1. 网络契约:所有容器通过discourse自定义桥接网络通信,web容器暴露 80/443 端口,redis容器仅对websidekiq开放 6379,postgres容器只接受web的 5432 连接。你不需要手动配置iptablesufw规则,Docker 的--network参数就是防火墙策略。
  2. 存储契约shared/standalone目录被挂载为volume,其中uploads存用户附件,backups存每日 pg_dump,ssl存证书,log存各服务日志。这个目录结构是 Discourse 的“数据宪法”,任何手动修改都可能触发./launcher rebuild app时的校验失败。
  3. 配置契约containers/app.yml是唯一可信配置源。它不是.env文件,而是 YAML 格式的声明式配置,包含 200+ 可调参数。Discourse 的bootstrap脚本会解析它,生成nginx.confdatabase.ymlredis.yml等 11 个子配置,并注入到对应容器的环境变量中。改错一个缩进,整个重建就会卡在Running provision command步骤。
  4. 生命周期契约./launcher脚本封装了start/stop/rebuild/enter四个原子操作。rebuild不是重启,而是销毁旧容器、拉取新镜像、重新解析app.yml、执行数据库迁移(rake db:migrate)、预编译资产(rake assets:precompile),最后启动新容器组。这个过程保证了每次部署都是“干净的”,避免了传统部署中常见的“残留进程干扰新版本”的问题。

提示:不要试图用docker run -d -p 80:80 discourse/discourse这种裸命令启动 Discourse。官方镜像没有暴露ENTRYPOINT,它依赖launcher脚本提供的完整上下文。你看到的discourse/discourse镜像,其实是一个“空壳”,真正的业务逻辑在discourse/basediscourse/app两个基础镜像里,它们通过多阶段构建(multi-stage build)实现分层缓存,这是 Discourse 构建速度能控制在 8 分钟内的关键。

2.2 Ubuntu 18.04 的特殊约束:内核、Docker、TLS 的三角博弈

Ubuntu 18.04 默认内核是 4.15.0,而 Docker 20.10+ 要求内核 ≥4.18 才能启用cgroup v2。但 Discourse 的launcher脚本并不强制要求 cgroup v2,它兼容 cgroup v1。真正卡住你的是 TLS 协议栈。Let’s Encrypt 的 ACME v2 协议自 2021 年起已弃用 TLS 1.0/1.1,而 Ubuntu 18.04 的openssl包默认只支持到 TLS 1.2,且其s_client工具在握手时不会发送 SNI 扩展——这会导致./discourse-setup在申请证书时返回urn:acme:error:connection :: The server could not connect to the client to verify the domain错误。

解决方案不是升级 OpenSSL(那会破坏整个系统的 SSL 依赖链),而是用certbot--standalone模式绕过 Nginx,直接监听 443 端口完成验证。但certbot本身在 Ubuntu 18.04 的 apt 源里版本太老(0.31),不支持 ACME v2。所以必须用snap安装最新版:

sudo apt install snapd sudo snap install --classic certbot sudo ln -sf /snap/bin/certbot /usr/local/bin/certbot

这个操作看似简单,但背后是 Ubuntu 18.04 的snapd服务与 systemd 的兼容性问题——snapd在 18.04 上默认禁用apparmor配置文件,而certbot--standalone模式需要apparmor允许其绑定特权端口。因此你必须执行:

sudo systemctl enable --now snapd.apparmor sudo systemctl restart snapd

否则certbot会静默失败,日志里只有一行Permission denied。这是我踩过的最隐蔽的坑:它不报错,只是让你永远卡在证书申请环节。

另一个约束是 Docker 版本。Ubuntu 18.04 官方源里的docker.io包是 18.09,而 Discourse 要求 ≥19.03(因docker-composeprofiles功能)。所以必须卸载docker.io,改用 Docker 官方 APT 仓库:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable" | sudo tee /etc/apt/sources.list.d/docker.list sudo apt update sudo apt install docker-ce=5:19.03.15~3-0~ubuntu-bionic docker-ce-cli=5:19.03.15~3-0~ubuntu-bionic containerd.io

注意版本号必须精确到5:19.03.15~3-0~ubuntu-bionic,因为 Discourse 的launcher脚本里硬编码了docker version --format '{{.Server.Version}}'的输出格式,高版本 Docker 的Server.Version字段会多出-ce后缀,导致脚本解析失败。这个细节在 Discourse 的 GitHub Issue #12847 里有详细讨论,但官方文档从未提及。

2.3 app.yml:不是配置文件,而是你的社区宪法

app.yml是 Discourse 部署的“圣杯”,它决定了你的社区长什么样、怎么说话、如何保护自己。很多人把它当成.env文件随便改,结果重建时./launcher rebuild app报错undefined method '[]' for nil:NilClass,然后开始疯狂 Google。其实错误根源很简单:YAML 是严格缩进的语言,environment:下的每个键值对必须比environment:多两个空格,而env:下的DISCOURSE_SMTP_ADDRESS必须顶格写——这种不一致的设计,是 Discourse 为了兼容不同层级配置继承而做的妥协。

我们来拆解一个生产环境可用的app.yml核心段落(已脱敏):

## this is the all-in-one, standalone, simple setup ## which makes it easy to get started with Discourse ## after editing this file, run `./launcher rebuild app` # 1. 基础网络配置 —— 这里定义了你的社区“身份证” params: version: stable # 注意:domain 必须是完整的 FQDN,不能带 http://,也不能是 IP # 如果你用的是内网域名(如 forum.internal),这里必须填全称 domain: "forum.example.com" # SMTP 配置是独立模块,不是 environment 下的子项 smtp_address: "smtp.exmail.qq.com" smtp_port: 465 smtp_user_name: "admin@example.com" smtp_password: "your_app_password_here" # 注意:不是邮箱密码,是第三方 SMTP 的应用专用密码 smtp_enable_start_tls: true # 2. 环境变量 —— 这里定义了你的社区“性格” environment: # DISCOURSE_HOSTNAME 是前端访问地址,必须和 params.domain 一致 # 否则会出现混合内容警告(Mixed Content) DISCOURSE_HOSTNAME: 'forum.example.com' # DISCOURSE_DEVELOPER_EMAILS 是管理员白名单,多个邮箱用逗号分隔 # 这些邮箱注册时会自动获得管理员权限 DISCOURSE_DEVELOPER_EMAILS: 'admin@example.com,dev@team.example.com' # DISCOURSE_SMTP_* 系列变量必须和上面的 smtp_* 参数完全对应 # launcher 脚本会把 params.smtp_* 映射为 DISCOURSE_SMTP_* 环境变量 DISCOURSE_SMTP_ADDRESS: 'smtp.exmail.qq.com' DISCOURSE_SMTP_PORT: 465 DISCOURSE_SMTP_USER_NAME: 'admin@example.com' DISCOURSE_SMTP_PASSWORD: 'your_app_password_here' DISCOURSE_SMTP_ENABLE_START_TLS: 'true' # 3. 数据库与缓存 —— 这里定义了你的社区“记忆” # PostgreSQL 配置(Discourse 默认使用) # 这些参数直接影响查询性能,尤其在高并发搜索时 DB_POOL: 32 # Redis 配置(Discourse 强依赖 Redis 做会话、缓存、后台队列) # 如果你用的是云 Redis(如腾讯云 CKV),这里要填公网地址 REDIS_URL: 'redis://localhost:6379?pool=10' # Redis 密码如果为空,URL 里不能写 ?password=,否则解析失败 # 正确写法是 redis://:password@localhost:6379 # 4. SSL 与安全 —— 这里定义了你的社区“盔甲” # Let's Encrypt 自动证书(推荐用于公有云) # 如果你用的是内网或测试环境,可以设为 false,用自签名证书 LETSENCRYPT_ACCOUNT_EMAIL: 'admin@example.com' # 如果你已有证书,取消下面三行注释,并指向你的 PEM 文件 # DISCOURSE_SSL: 'true' # DISCOURSE_SSL_CERT: '/shared/ssl/forum.example.com.crt' # DISCOURSE_SSL_KEY: '/shared/ssl/forum.example.com.key' # 5. 高级功能开关 —— 这里定义了你的社区“能力” # 启用邮件通知(必须配置 SMTP 才有效) DISCOURSE_NOTIFICATIONS_ENABLED: 'true' # 启用主题摘要邮件(Digest Email) DISCOURSE_DIGEST_EMAILS_ENABLED: 'true' # 启用 RSS 订阅(对 SEO 和内容分发很重要) DISCOURSE_RSS_ENABLED: 'true' # 启用 Google Analytics(需填入你的 GA4 测量 ID) DISCOURSE_ANALYTICS_CODE: 'G-XXXXXXXXXX'

注意:DISCOURSE_SMTP_PASSWORD的值如果包含特殊字符(如@,/,:),必须用单引号包裹,否则 YAML 解析器会把它当成 URL 分隔符。我曾因密码里有个@符号,导致DISCOURSE_SMTP_USER_NAME被解析成admin@之前的部分),而DISCOURSE_SMTP_PASSWORD变成example.com:your_app_password_here,整个 SMTP 连接自然失败。这种错误在日志里只会显示Net::SMTPAuthenticationError,根本看不出是密码解析问题。

3. 实操全流程:从零开始的 47 分钟部署实录

3.1 环境准备:12 个必须确认的前置条件

在敲下第一个apt update之前,请花 3 分钟确认以下 12 个点。少一个,后续就可能卡在某个莫名其妙的环节,浪费你 2 小时排查。这不是 paranoia,而是 Ubuntu 18.04 的现实。

  1. 主机名与 DNS 解析hostname -f必须返回完整的 FQDN(如forum.example.com),且该域名必须能被公网 DNS 解析(如果你用 Let’s Encrypt)。如果是在内网,确保/etc/hosts里有127.0.0.1 forum.example.com条目。
  2. 内存与交换空间:Discourse 最小要求 2GB RAM,但 Ubuntu 18.04 默认 swap 是 2GB,而launcher脚本会检测free -m | awk '/^Mem:/ {print $2}',如果小于 1800,会警告并建议增加 swap。实测:在 2GB 内存机器上,swapon --show=NAME,TYPE,SIZE,USED,PRI显示 swap 使用率超过 70% 时,rebuild会因 OOM Killer 杀死ruby进程而失败。解决方案:sudo fallocate -l 2G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
  3. 磁盘空间/var/discourse目录所在分区至少预留 10GB 空间。launcher rebuild app过程中会下载discourse/base(约 1.2GB)、discourse/app(约 800MB)、postgres:12(约 300MB)三个镜像,加上构建缓存,峰值占用可达 4GB。
  4. 时区设置timedatectl status | grep "Time zone"必须是Asia/Shanghai或你的本地时区。Discourse 的定时任务(如每日备份、邮件摘要)依赖系统时区,如果设成UTC,你会看到邮件摘要总在凌晨 3 点发送。
  5. 防火墙状态sudo ufw status verbose必须显示Status: inactiveufw会干扰 Docker 的 iptables 规则,导致web容器无法被外部访问。如果必须启用防火墙,请用sudo ufw allow 80,443/tcp,然后sudo ufw reload
  6. SELinux 状态:Ubuntu 18.04 默认不启用 SELinux,但如果你是从 CentOS 迁移过来的,执行sestatus确认输出是disabled。SELinux 会阻止 Docker 挂载/var/discourse/shared/standalone目录。
  7. Docker 版本docker --version必须是Docker version 19.03.15, build 5:19.03.15~3-0~ubuntu-bionic。其他版本会触发launcher的版本校验失败。
  8. Docker daemon 配置sudo cat /etc/docker/daemon.json应为空或只含{}。Discourse 的launcher脚本不兼容insecure-registriesregistry-mirrors配置,会因json: cannot unmarshal object into Go struct field报错。
  9. systemd-resolved 状态sudo systemctl status systemd-resolved必须是active (running)。Ubuntu 18.04 的systemd-resolved服务管理/etc/resolv.conf,如果被禁用,Docker 容器会因 DNS 解析失败而无法拉取镜像。
  10. /etc/hosts 权限ls -l /etc/hosts输出的权限必须是-rw-r--r--(644)。如果被改成 600,launcher脚本在生成nginx.conf时会因无法读取 hosts 而失败。
  11. locale 设置locale命令输出中LANG必须是en_US.UTF-8zh_CN.UTF-8。Discourse 的rake任务依赖 UTF-8 locale,如果LANG=Cassets:precompile会因字符串编码错误而中断。
  12. root 权限:所有./launcher命令必须用sudo执行。Discourse 的launcher脚本内部会调用dockerdocker-composechownchmod等需要 root 权限的命令,普通用户执行会静默失败。

实操心得:我习惯把这 12 个检查点写成一个check-pre-req.sh脚本,每次部署前先运行它。脚本会逐条执行检查,失败时用红色字体打印错误信息,并给出修复命令。比如检查 swap 时,如果free -m | awk '/^Swap:/ {print $2}'返回 0,脚本会自动执行sudo fallocate -l 2G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile。这种“防御性脚本”让我部署成功率从 68% 提升到 99.2%。

3.2 安装 Discourse:7 个步骤的精准操作

现在,让我们进入真正的部署环节。以下每一步都经过我 127 次实测验证,精确到命令顺序和等待时间。

步骤 1:创建部署目录并克隆官方仓库

# 创建标准目录结构,Discourse 官方约定俗成 sudo mkdir -p /var/discourse sudo chown $USER:$USER /var/discourse cd /var/discourse # 克隆官方安装脚本仓库(不是 Discourse 源码) git clone https://github.com/discourse/discourse_docker.git . # 注意:最后的点号 . 很重要,它表示把仓库内容克隆到当前目录

这一步耗时约 15 秒。discourse_docker仓库只有 2MB,但它是 Discourse 的“安装大脑”,包含launchersamplesscripts三个核心目录。

步骤 2:生成初始 app.yml 配置文件

# 复制样本配置,这是所有部署的起点 cp samples/standalone.yml containers/app.yml # 用 sed 批量替换占位符(比手动编辑快且不易出错) sed -i "s/YOURDOMAIN.COM/forum.example.com/g" containers/app.yml sed -i "s/admin@example.com/admin@example.com/g" containers/app.yml # 如果你用的是腾讯企业邮,SMTP 地址是 smtp.exmail.qq.com,端口 465 sed -i "s/# smtp_address: 'smtp.gmail.com'/smtp_address: 'smtp.exmail.qq.com'/g" containers/app.yml sed -i "s/# smtp_port: 587/smtp_port: 465/g" containers/app.yml sed -i "s/# smtp_user_name: ''/smtp_user_name: 'admin@example.com'/g" containers/app.yml sed -i "s/# smtp_password: ''/smtp_password: 'your_app_password_here'/g" containers/app.yml sed -i "s/# smtp_enable_start_tls: false/smtp_enable_start_tls: true/g" containers/app.yml

注意:standalone.yml是为单机部署优化的模板,它启用了db_shared_buffers: "256MB"UNICORN_WORKERS: 4,适合 4GB 内存的服务器。如果你的机器只有 2GB,需要手动把UNICORN_WORKERS改成2,否则rebuild会因内存不足失败。

步骤 3:首次构建与启动(耗时约 28 分钟)

# 这是魔法开始的命令,它会: # 1. 拉取 discourse/base 镜像(约 12 分钟) # 2. 拉取 discourse/app 镜像(约 8 分钟) # 3. 解析 app.yml,生成 nginx.conf 等配置 # 4. 启动 postgres 容器并初始化数据库 # 5. 运行 rake db:migrate 迁移表结构 # 6. 预编译 assets(CSS/JS) # 7. 启动 web 和 sidekiq 容器 sudo ./launcher bootstrap app # 等待输出 "All containers are healthy!" 后,执行启动 sudo ./launcher start app

bootstrap是最耗时的环节,因为它要下载镜像、编译 Ruby Gem、生成静态资源。你可以用sudo ./launcher logs app实时查看进度。当看到I, [2023-10-15T10:23:45.123456 #123] INFO -- : Booting Discourse...时,说明核心服务已启动。

步骤 4:验证基础服务状态

# 检查容器状态(应该看到 web, db, redis, sidekiq 四个容器) sudo docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" # 检查 web 容器日志,确认无 ERROR sudo ./launcher logs app | grep -i "error\|exception\|failed" # 检查 PostgreSQL 是否可连接(用容器内 psql) sudo ./launcher enter app -- bash -c "psql -U discourse -d discourse -c 'SELECT version();'" # 检查 Redis 是否可 ping 通 sudo ./launcher enter app -- bash -c "redis-cli -h redis ping"

如果psql命令返回PONG,说明数据库连接正常;如果redis-cli返回PONG,说明缓存服务就绪。这两个是 Discourse 启动的基石。

步骤 5:配置 Let’s Encrypt SSL 证书(公有云必做)

# 运行官方 SSL 配置脚本(它会自动调用 certbot) sudo ./discourse-setup # 脚本会交互式提问: # - Domain name: forum.example.com (必须和 app.yml 里一致) # - Email for Let's Encrypt: admin@example.com # - 是否启用 HTTP 重定向:yes # - 是否启用 OCSP Stapling:yes # 脚本完成后,会自动重启 web 容器

./discourse-setup脚本本质是./launcher rebuild app的封装,但它会额外执行certbot --nginx命令。如果遇到The following errors were reported by the server...,请检查 DNS 解析是否生效(dig forum.example.com +short应返回你的服务器 IP),并确认 80 端口未被其他服务占用(sudo ss -tuln | grep :80)。

步骤 6:首次访问与管理员初始化
打开浏览器,访问https://forum.example.com。你会看到 Discourse 的欢迎页。点击 “Create your first user”,填写:

  • Email:admin@example.com(必须和DISCOURSE_DEVELOPER_EMAILS里的一致)
  • Username:admin
  • Password: 你的强密码

提交后,Discourse 会自动发送一封激活邮件到admin@example.com注意:这封邮件可能被 Gmail/Outlook 当作垃圾邮件,请检查垃圾箱。如果没收到,用sudo ./launcher logs app | grep -A5 -B5 "Sending activation email"查看邮件发送日志,确认 SMTP 连接是否成功。

步骤 7:验证邮件通知功能(关键验收点)
登录管理员后台(https://forum.example.com/admin),进入Settings > Emails,找到test_email设置项,输入你的测试邮箱(如test@gmail.com),点击Send Test Email。同时,在服务器上执行:

# 查看邮件发送日志 sudo ./launcher logs app | grep -A10 -B10 "Sent mail to test@gmail.com" # 如果看到 "Sent mail to test@gmail.com (1234.5ms)",说明 SMTP 配置成功 # 如果看到 "Net::SMTPAuthenticationError",说明密码错误或 SMTP 服务器拒绝连接

这是部署成功的黄金标准。Discourse 的核心价值之一就是高质量的异步通知,如果邮件不通,整个社区的活跃度会断崖式下跌。

3.3 SMTP 配置深度指南:为什么 88 邮箱能成为国内首选

网络热词里提到的“88 邮箱”,实指网易 163 邮箱的“授权码”机制(因其 SMTP 端口常为 465,被戏称为 88)。在国内,Discourse 的 SMTP 配置失败率高达 63%,主要源于三大陷阱:QQ 邮箱的“POP3/SMTP 服务”开关藏得太深、163 邮箱的“客户端授权码”与网页登录密码混淆、企业邮箱的“SMTP 服务器地址”填写错误。下面是我整理的国内主流邮箱配置速查表:

邮箱服务商SMTP 服务器地址端口加密方式授权码获取路径常见错误
网易 163 邮箱smtp.163.com465SSL/TLS登录网页版 → 设置 → POP3/SMTP/IMAP → 开启 SMTP → 获取授权码把网页登录密码当授权码填;未开启 SMTP 服务
腾讯企业邮smtp.exmail.qq.com465SSL/TLS管理后台 → 邮箱账号 → 修改密码 → 生成“第三方客户端密码”用邮箱密码代替应用专用密码;端口填 587(应为 465)
阿里云企业邮smtp.mxhichina.com465SSL/TLS管理控制台 → 邮箱管理 → 账号设置 → SMTP 密码SMTP 密码未单独设置,沿用登录密码
Gmailsmtp.gmail.com587STARTTLSGoogle 账户 → 安全 → 两步验证 → 应用专用密码未开启两步验证;应用专用密码未生成

实操心得:我测试过 17 种国内 SMTP 组合,最终发现腾讯企业邮 + 465 端口 + SSL是最稳定的。原因有三:第一,腾讯云 CKV Redis 与 Discourse 的 Redis 驱动兼容性最好,redis-rbgem 的timeout参数无需调整;第二,smtp.exmail.qq.com的 DNS 解析在全球 CDN 节点都有缓存,延迟低于 20ms;第三,腾讯企业邮的 SMTP 服务 SLA 是 99.95%,且提供详细的 SMTP 日志(在管理后台可查),便于排查535 Authentication failed类错误。相比之下,163 邮箱的 SMTP 服务在凌晨 2-4 点会有 5 分钟左右的维护窗口,期间 Discourse 的邮件队列会积压,需要手动执行rake jobs:workoff清理。

4. 故障排查实战:12 个高频问题与我的独家修复方案

4.1 问题 1:./launcher rebuild app卡在Running provision command,CPU 占用 100%

现象:终端长时间停在Running provision commandhtop显示ruby进程 CPU 占用 100%,df -h显示/var分区使用率 98%。

根因分析:Discourse 的provision脚本在执行rake assets:precompile时,会生成大量临时文件(.js,.css,.map),默认存放在/tmp。而 Ubuntu 18.04 的/tmptmpfs,大小等于内存的一半(2GB 内存机器上/tmp只有 1GB)。当编译资源时,/tmp空间耗尽,ruby进程陷入无限重试循环。

修复方案

# 临时增大 tmpfs 大小(重启失效,但够本次重建用) sudo mount -o remount,size=3G /tmp # 或者永久修改:编辑 /etc/default/tmpfs,添加 TMPFS_SIZE=3G # 然后重新运行 rebuild sudo ./launcher rebuild app

注意:不要用export TMPDIR=/var/tmp,因为 Discourse 的rake任务硬编码了/tmp路径。这是 Discourse 的一个已知限制(Issue #14201),官方建议用mount -o remount临时解决。

4.2 问题 2:Discourse 后台显示Redis connection lost,但redis-cli ping返回PONG

现象:Discourse 管理后台右上角出现红色警告Redis connection lost,但sudo ./launcher enter app -- redis-cli -h redis ping返回PONG,且redis-cli -h redis info clients显示connected_clients: 12

根因分析:Discourse 的 Redis 客户端(redis-rbgem)默认timeout是 1 秒,而 Ubuntu 18.04 的sysctl.confnet.ipv4.tcp_fin_timeout是 60 秒。当 Redis 服务器(redis容器)因内存压力触发maxmemory-policy volatile-lru时,会主动关闭空闲连接,但 Discourse 客户端未及时感知,导致连接池里存在“僵尸连接”。

修复方案

# 进入 app 容器,修改
http://www.cnnetsun.cn/news/2993018.html

相关文章:

  • Ubuntu 18.04 + GitLab 13.12.15 稳定部署实战指南
  • PHP伪协议在文件包含漏洞中的实战应用与防御策略
  • OpticsGPT:大语言模型如何革新光学设计流程
  • Ubuntu 20.04 部署 code-server 生产级远程开发环境全指南
  • Claude Code Skills 源码深度解析:AI原生工作流的契约式执行架构
  • IRIS2与Starlink低轨星座技术架构、仿真对比与战略差异深度解析
  • Kubernetes Ingress HTTPS自动化:cert-manager+NGINX实现Let’s Encrypt端到端证书管理
  • AI编程助手实战:从提示工程到优雅代码的完整协作指南
  • MAAC扩展应用:如何将注意力机制应用到自定义多智能体任务
  • hongyangWeixinArticles项目实战教程:如何将公众号文章转化为结构化知识库
  • 如何快速上手MCP-Security-Checklist:初学者完整教程与实战演练
  • Medium Editor Markdown未来展望:Markdown编辑器的演进趋势与技术挑战
  • rules_rust性能优化:10个提升Bazel Rust构建速度的技巧
  • 距离度量学习在计算机视觉中的关键作用:从理论到实践
  • 3步解决低显存部署难题:Qwen3-4B模型量化实战指南
  • post-robot集成指南:与React、Vue、Angular框架的完美结合
  • Qwen Code VS Code集成:在IDE中解锁AI编程助手的原生开发体验
  • 5个核心技巧:深入解析Unfinished-asteroids游戏引擎架构与实现原理
  • Graphene实战教程:如何将传统Linux应用迁移到SGX安全环境中运行
  • Safety-DB实战:识别和修复10个常见Python包安全漏洞
  • Asciidoctor.js:终极JavaScript文档处理器,快速将AsciiDoc转换为HTML5
  • SSD目标检测模型:从零到一掌握实时物体识别核心技术 [特殊字符]
  • Anycubic i3 MEGA系列3D打印机固件升级终极指南
  • Hunyuan3D-2终极指南:快速生成高分辨率3D资产
  • Vim终极武器:YouCompleteMe智能代码补全完全实战指南
  • CVE-2025-0282:Ivanti缓冲区溢出漏洞复现
  • temperature top-p
  • AI 串联软件测试流水线
  • 【Claude】OAuth token revoked / Org not allowed 错误的认证链路排查 bug报错已解决
  • 混合系统不变集计算:理论与机器人应用