Discourse 中引导失败的故障排除

我仍然在应对新的“预打包”(pre-bundling)流程。在我的测试实例上,引导(bootstrap)失败了,但我不明白原因。请注意,这是一个使用外部 PostgreSQL 数据库的部署。容器运行在作为 SSL 加速器的 HAProxy 之后。这里我只对 web_only 容器进行引导。当前的构建版本(在引导之前)是 3.5.0.beta8-dev,Commits · discourse/discourse · GitHub

这是当前失败的 yml 文件:

# 重要:在 Postgres 中为 Discourse 用户设置一个秘密密码
# TODO: 在此模板中更改 SOME_SECRET

templates:
  - "templates/web.template.yml"
  ## 取消注释下一行以启用 IPv6 监听器
  #- "templates/web.ipv6.template.yml"
  - "templates/web.ratelimited.template.yml"
  - "templates/web.socketed.template.yml"
  ## 如果您想添加 Lets Encrypt (https),请取消注释这两行
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"

## 此容器应暴露哪些 TCP/IP 端口?
## 如果您希望 Discourse 与另一个 Web 服务器(如 Apache 或 nginx)共享端口,
## 请参阅 https://meta.discourse.org/t/17247 了解详情
expose:
  - "80:80"   # http
  - "443:443" # https

# 使用 'links' 键将容器链接在一起,即使用 Docker --link 标志。
links:
  - link:
      name: redis
      alias: data

# 为 Docker 添加任何额外参数?
# docker_args:

params:
  ## 此容器应使用哪个 Git 修订版?(默认:tests-passed)
  #version: tests-passed

env:
  LC_ALL: en_US.UTF-8
  LANG: en_US.UTF-8
  LANGUAGE: en_US.UTF-8
  DISCOURSE_DEFAULT_LOCALE: de

  ## 支持多少并发 Web 请求?取决于内存和 CPU 核心数。
  ## 将根据检测到的 CPU 由引导程序自动设置,或者您可以覆盖
  UNICORN_WORKERS: 4

  ## TODO: 此 Discourse 实例将响应的域名
  DISCOURSE_HOSTNAME: 'forum2.netzwissen.de'

  ## 如果您希望容器以与上述相同的
  ## 主机名(-h 选项)启动,请取消注释(默认 "$hostname-$config")
  #DOCKER_USE_HOSTNAME: true

  ## TODO: 初始注册时将设为管理员和开发人员的逗号分隔邮箱列表
  ## 示例 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'admin@netzwissen.de'

  ## TODO: 用于验证新账户和发送通知的 SMTP 邮件服务器
  # 需要 SMTP 地址、用户名和密码
  # 警告:SMTP 密码中的字符 '#' 可能会导致问题!
  DISCOURSE_SMTP_ADDRESS: mail.netzwissen.de
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: discourse@netzwissen.de
  DISCOURSE_SMTP_PASSWORD: [xxxxxxxxxxxxxxxxx}
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (可选,默认为 true)
  #DISCOURSE_SMTP_DOMAIN: discourse.example.com    # (某些提供商要求)
  DISCOURSE_NOTIFICATION_EMAIL: discourse@netzwissen.de    # (发送通知的地址)

  ## 如果您添加了 Lets Encrypt 模板,请取消注释以下行以获取免费 SSL 证书
  #LETSENCRYPT_ACCOUNT_EMAIL: me@example.com

## 配置与数据库的连接
  DISCOURSE_DB_SOCKET: ''
  DISCOURSE_DB_USERNAME: [xxxxxxxxxxx]
  DISCOURSE_DB_NAME: [xxxxxxxxxxxxxxx]
  DISCOURSE_DB_HOST: [xxxxxxxxxxxxxxx]
  DISCOURSE_DB_PASSWORD: [xxxxxxxxxxxxxxx]
  ## 在相邻容器上进行 redis 缓存
  DISCOURSE_REDIS_HOST: redis

  ## 此 Discourse 实例的 http 或 https CDN 地址(配置为拉取)
  ## 详见 https://meta.discourse.org/t/14857
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com

  ## MaxMind 地理位置 IP 账户 ID 和许可证密钥,用于 IP 地址查询
  ## 详见 https://meta.discourse.org/t/-/173941
  #DISCOURSE_MAXMIND_ACCOUNT_ID: 123456
  #DISCOURSE_MAXMIND_LICENSE_KEY: 1234567890123456

volumes:
  - volume:
      host: /mnt/data/discourse/shared/web-only
      guest: /shared
  - volume:
      host: /mnt/data/discourse/shared/web-only/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/discourse/discourse-shared-edits.git
# 一些插件已被移除,因为它们现在已打包到核心中
# https://meta.discourse.org/t/bundling-more-popular-plugins-with-discourse-core/373574
## = 现已打包
##          - git clone https://github.com/discourse/discourse-chat-integration.git
##          - git clone https://github.com/discourse/discourse-openid-connect.git
##          - git clone https://github.com/discourse/discourse-calendar.git
##          - git clone https://github.com/angusmcleod/discourse-events.git
##          - git clone https://github.com/discourse/discourse-data-explorer.git
##          - git clone https://github.com/discourse/discourse-reactions.git
##          - git clone https://github.com/discourse/discourse-chat.git
##          - git clone https://github.com/discourse/discourse-ai.git
##          - git clone https://github.com/discourse/discourse-topic-voting.git
##          - git clone https://github.com/discourse/discourse-post-voting.git
##          - git clone https://github.com/discourse/discourse-user-notes.git
##          - git clone https://github.com/discourse/discourse-solved.git
          - git clone https://github.com/discourse/discourse-docs-card-filter.git
          - git clone https://github.com/discourse/discourse-doc-categories.git
##          - git clone https://github.com/discourse/discourse-assign.git
##          - git clone https://github.com/discourse/discourse-templates.git
          - git clone https://github.com/discourse/discourse-saved-searches.git
          - git clone https://github.com/discourse/discourse-tooltips.git
          - git clone https://github.com/discourse/discourse-category-experts.git
          - git clone https://github.com/discourse/discourse-activity-pub.git
          - git clone https://github.com/discourse/discourse-follow.git
          - git clone https://github.com/nathan-nz/discourse-wikified-posts.git
          - git clone https://github.com/discourse/discourse-whos-online.git
          - git clone https://github.com/merefield/discourse-workflow.git

## 请记住,这是 YAML 语法 - 您只能有一个具有名称的块
run:
  - exec: echo "自定义命令开始"

  ## 如果您想为 root 配置密码登录,请取消注释并更改:
  ## 仅使用以下行之一:
  #- exec: /usr/sbin/usermod -p 'PASSWORD_HASH' root
  - exec: /usr/sbin/usermod -p "$(mkpasswd -m sha-256 'xxxxxxxxxxxxxxx')" root

## 用于在带有本地 nginx 的 haproxy 负载均衡器后运行
  - replace:
      filename: /etc/nginx/conf.d/discourse.conf
      from: "types {"
      to: |
        set_real_ip_from 127.0.0.1/24;
        set_real_ip_from 10.0.0.0/24;
        real_ip_header X-Forwarded-For;
        real_ip_recursive on;
        proxy_set_header Host $http_host;
        proxy_set_header X-Request-Start “t=${msec}”;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https; # 替换了 $thescheme;
        proxy_set_header X-Real-IP $remote_addr;
        types {

  ## 如果您想授权其他用户,请取消注释并更改:
  #- exec: ssh-import-id username
  #- exec: ssh-import-id anotherusername

  - exec: echo "自定义命令结束"
  - exec: awk -F\# '{print $1;}' ~/.ssh/authorized_keys | awk 'BEGIN { print "此容器的授权 SSH 密钥:"; } NF\u003e=2 {print $NF;}'

即使我在 yml 中添加了一个 rm -rf 块:

- rm -rf discourse-chat-integration  
- rm -rf discourse-openid-connect         
- rm -rf discourse-calendar         
- rm -rf discourse-events         
- rm -rf discourse-data-explorer         
- rm -rf discourse-reactions         
- rm -rf discourse-chat         
- rm -rf discourse-ai         
- rm -rf discourse-topic-voting         
- rm -rf discourse-post-voting         
- rm -rf discourse-user-notes         
- rm -rf discourse-solved         
- rm -rf discourse-assign         
- rm -rf discourse-templates

引导仍然失败,错误如下:

/usr/local/lib/ruby/gems/3.3.0/gems/pups-1.3.0/lib/pups.rb
/usr/local/bin/pups --stdin

FAILED
--------------------
Pups::ExecError: cd /var/www/discourse && su discourse -c 'bundle exec rake db:migrate' 失败,返回 #<Process::Status: pid 669 exit 1>
失败位置:/usr/local/lib/ruby/gems/3.3.0/gems/pups-1.3.0/lib/pups/exec_command.rb:131:in `spawn'
执行失败,参数为 {"cd"=>"$home", "tag"=>"migrate", "hook"=>"db_migrate", "cmd"=>["su discourse -c 'bundle exec rake db:migrate'"]}

我将其解读为数据库迁移“bundle exec rake db:migrate”时的错误。但根本原因是什么?discourse2 数据库已经拥有了之前已安装的 discourse-ai 插件所需的 vector 扩展:

postgres=# \c discourse2  
psql (15.12 (Ubuntu 15.12-1.pgdg22.04+1), server 13.20 (Ubuntu 13.20-1.pgdg22.04+1))  
您现在已作为用户 "postgres" 连接到数据库 "discourse2"。  
discourse2=# \dx  
                                   已安装扩展列表  
  名称   | 版本 |   模式   |                            描述                               
----------+---------+------------+-------------------------------------------------------------------  
hstore   | 1.7     | public     | 用于存储 (key, value) 对集合的数据类型  
pg_trgm  | 1.5     | public     | 基于三字母组的文本相似度测量和索引搜索  
plpgsql  | 1.0     | pg_catalog | PL/pgSQL 过程语言  
unaccent | 1.1     | public     | 移除重音的文本搜索字典  
vector   | 0.8.0   | public     | 向量数据类型以及 ivfflat 和 hnsw 访问方法  
(5 rows)

稍后,即使 yml 中的“git clone”行全部被注释掉,我也看到了插件提示:

---
提示:插件 'discourse-openid-connect' 现已打包到 Discourse 中,不应包含在您的容器配置中。
请从 containers/web_only.yml 文件中删除行 'git clone https://github.com/discourse/discourse-openid-connect',然后重试。
更多信息,请参阅 https://meta.discourse.org/t/373574
---

老实说,我现在有点不知所措 ::sleepy_face:

free -h 返回什么?

root@docker3a:/var/discourse/containers# free -h
总量 使用中 空闲 共享 缓存/目录 可用
内存: 3.8Gi 2.0Gi 1.1Gi 3.8Mi 969Mi 1.8Gi

可能需要 4GB 的交换空间

抱歉,是我的错,输出被截断了

root@docker3a:~# free -h
总量 已用 空闲 共享. 缓冲区/缓存 可用
内存: 3.8Gi 2.0Gi 1.1Gi 3.8Mi 971Mi 1.8Gi
交换: 974Mi 0B 974Mi

该虚拟机已经有 4 GB 内存。我增加了 2 GB 并再次运行了 ./launcher rebuild web_only^,结果出现了相同的错误。所以我怀疑这里不是空间相关的构建问题……

嗯,我需要 8 GB RAM 配 2 GB 交换空间。

我再次尝试使用 8 GB RAM,结果错误相同。请注意,这是一个基于内核的虚拟机 (KVM),运行在 Proxmox 主机上 (Features - Proxmox Virtual Environment)。

QEMU/KVM 客户机会在其需要时向主机请求内存,但如果客户机操作系统不再需要,它不会 自动 将内存返回给主机。如果主机需要内存,内核会要求进程释放未使用的 RAM,因此 QEMU 会尝试回收一些内存,但这取决于客户机操作系统首先释放它。

即使在使用 discourse-ai 插件时,我也从未遇到过 4 GB RAM 和 4 CPU 设置的内存问题。引导问题仅在此捆绑公告之后出现……

您的 yml 文件中是否存在 discourse-openid-connect 插件?

这是一个迁移问题,您能向上滚动查找失败迁移的内容吗?

您的 YML 文件中是否存在 discourse-openid-connect 插件?

这是一个迁移问题,您能否向上滚动查找迁移失败的内容?

到目前为止,我的插件列表中包含 discourse-openid-connect。我已将其移除以进行“post-bundling build”。现在,我的 build YML 中只剩下这些插件:

## 非捆绑插件
- git clone https://github.com/discourse/docker_manager.git\\`

  • git clone https://github.com/discourse/discourse-shared-edits.git
  • git clone https://github.com/discourse/discourse-docs-card-filter.git
  • git clone https://github.com/discourse/discourse-doc-categories.git
  • git clone https://github.com/discourse/discourse-saved-searches.git
  • git clone https://github.com/discourse/discourse-tooltips.git
  • git clone https://github.com/discourse/discourse-category-experts.git
  • git clone https://github.com/discourse/discourse-activity-pub.git
  • git clone https://github.com/discourse/discourse-follow.git
  • git clone ``https://github.com/nathan-nz/discourse-wikified-posts.git
  • git clone https://github.com/discourse/discourse-whos-online.git
  • git clone \\\https://github.com/merefield/discourse-workflow.git\\

构建似乎在“bundle exec rake db:migrate”处崩溃。但在此之前的几行,我看到:

Gem::LoadError: can't activate multipart-post-2.4.0, already activated multipart-post-2.4.1 (Gem::LoadError)

[… ]
discourse-wikified-posts 已是最新兼容版本 discourse-workflow 已是最新兼容版本 docker_manager 已是最新兼容版本 I,
[2025-07-28T06:34:13.916393 #1] INFO -- : `> cd /var/www/discourse & su discourse -c 'bundle exec rake db:migrate' rake aborted! Gem::LoadError: can't activate multipart-post-2.4.0, already activated multipart-post-2.4.1 (Gem::LoadError) /var/www/discourse/lib/plugin_gem.rb:25:in `load' /var/www/discourse/lib/plugin/instance.rb:861:in `gem' [… ]

我对 Ruby 一无所知,但这是否是 multipart-post-2.4.0multipart-post-2.4.1 之间的包冲突?

谢谢!
Thommie

它导致我的两个构建都倒塌了。至少有一个 CDCK 插件使用了旧版本。

这是系统按设计工作

当我们使用 Gemfile 和 Gemfile.lock 管理 gem 依赖项时,我们会让 dependbot 扫描更新,并通过我们的内部流程来保持更新。

作为插件设计者,您现在可以依赖我们在核心中提供的 gem 依赖项。

这意味着您应该从插件的 plugin.rb 中删除 multipart-post,并在使用它的地方简单地 require 它。

插件作者少了一件需要担心的事情。

有用的反馈,谢谢。

@Thomas_Rother 我已从插件中移除依赖项,请再试一次构建。

@merefield @sam :smiley: - 成功了!!通过 Roberts 插件 GitHub - merefield/discourse-workflow: A workflow system for Topics that implements a configurable multi-stage process 中的提交 6be7a44,构建成功了。感谢分析和修复!