在 Docker 中安装 Discourse,无法连接

所以我使用了标准安装文档,一切看起来都很顺利。然后我编辑了 app.yml 以更改使用的端口,注释掉了 Let’s Encrypt 并重新构建。看起来一切正常。

但是当我尝试通过最直接的方式连接时:

$lynx https://127.0.0.1:44443

我收到以下错误:

Alert!: 无法与远程主机建立安全连接。

我甚至无法 ping 通该端口。

这说得通吗?容器内有日志吗?

—编辑补充----

$ sudo ./launcher logs app
run-parts: 执行 /etc/runit/1.d/00-ensure-links
run-parts: 执行 /etc/runit/1.d/00-fix-var-logs
run-parts: 执行 /etc/runit/1.d/01-cleanup-web-pids
run-parts: 执行 /etc/runit/1.d/anacron
run-parts: 执行 /etc/runit/1.d/cleanup-pids
清理过期的 PID 文件
run-parts: 执行 /etc/runit/1.d/copy-env
runsvdir 已启动,PID 为 43
ok: run: redis: (pid 56) 0s
**chgrp: 无效的用户组:'syslog'**
ok: run: postgres: (pid 55) 0s
supervisor pid: 57 unicorn pid: 85
(57) 重新打开日志

----编辑补充更多内容----

root@hestia-app:/shared/log/rails# tail -f *.log
==\u003e production_errors.log \u003c==

==\u003e production.log \u003c==

==\u003e sidekiq.log \u003c==

==\u003e unicorn.stderr.log \u003c==
I, [2021-08-06T00:56:35.859967 #85]  INFO -- : master 完成重新打开日志
I, [2021-08-06T00:56:50.911700 #142]  INFO -- : worker=0 完成重新打开日志
I, [2021-08-06T00:56:50.911698 #152]  INFO -- : worker=1 完成重新打开日志
I, [2021-08-06T00:56:50.935834 #162]  INFO -- : worker=2 完成重新打开日志
I, [2021-08-06T00:56:50.941733 #233]  INFO -- : worker=7 完成重新打开日志
I, [2021-08-06T00:56:50.945447 #203]  INFO -- : worker=5 完成重新打开日志
I, [2021-08-06T00:56:50.947354 #172]  INFO -- : worker=3 完成重新打开日志
I, [2021-08-06T00:56:50.949949 #187]  INFO -- : worker=4 完成重新打开日志
I, [2021-08-06T00:56:50.953453 #213]  INFO -- : worker=6 完成重新打开日志

==\u003e unicorn.stdout.log \u003c==
Sidekiq PID 131 完成重新打开日志...
root@hestia-app:/var/log/nginx# tail *.log
==\u003e access.letsencrypt.log \u003c==

==\u003e access.log \u003c==

==\u003e error.letsencrypt.log \u003c==

==\u003e error.log \u003c==

一定是 Docker 的问题,因为……其他一切看起来都没问题。

我刚安装了一个映射到 8088 端口的裸 Apache 容器,用 lynx 访问完全正常。

我重新进入应用并运行了 ps ax,看起来一切正常。只是容器似乎不接受连接。

root@hestia-app:/var/www/discourse# ps ax
    PID TTY      STAT   TIME COMMAND
      1 pts/0    Ss+    0:00 /bin/bash /sbin/boot
     43 pts/0    S+     0:00 /usr/bin/runsvdir -P /etc/service
     44 ?        Ss     0:00 runsv cron
     45 ?        Ss     0:00 runsv rsyslog
     46 ?        Ss     0:00 runsv redis
     47 ?        Ss     0:00 runsv postgres
     48 ?        Ss     0:00 runsv unicorn
     49 ?        Ss     0:00 runsv nginx
     50 ?        S      0:00 cron -f
     51 ?        Sl     0:00 rsyslogd -n
     52 ?        S      0:00 nginx: master process /usr/sbin/nginx
     53 ?        S      0:00 svlogd /var/log/redis
     54 ?        S      0:00 svlogd /var/log/postgres
     55 ?        S      0:00 /usr/lib/postgresql/13/bin/postmaster -D /etc/postgresql/13/main
     56 ?        Sl     0:33 /usr/bin/redis-server *:6379
     57 ?        S      0:04 /bin/bash config/unicorn_launcher -E production -c config/unicorn.conf.rb
     64 ?        S      0:00 nginx: worker process
     65 ?        S      0:00 nginx: worker process
     66 ?        S      0:00 nginx: worker process
     67 ?        S      0:00 nginx: worker process
     68 ?        S      0:00 nginx: cache manager process
     79 ?        Ss     0:00 postgres: 13/main: checkpointer 
     80 ?        Ss     0:00 postgres: 13/main: background writer 
     81 ?        Ss     0:03 postgres: 13/main: walwriter 
     82 ?        Ss     0:00 postgres: 13/main: autovacuum launcher 
     83 ?        Ss     0:00 postgres: 13/main: stats collector 
     84 ?        Ss     0:00 postgres: 13/main: logical replication launcher 
     85 ?        Sl     0:13 unicorn master -E production -c config/unicorn.conf.rb
    113 ?        Ss     0:01 postgres: 13/main: discourse discourse [local] idle
    131 ?        SNl    0:48 sidekiq 6.2.1 discourse [0 of 5 busy]
    142 ?        Sl     0:06 unicorn worker[0] -E production -c config/unicorn.conf.rb
    152 ?        Sl     0:06 unicorn worker[1] -E production -c config/unicorn.conf.rb
    162 ?        Sl     0:07 unicorn worker[2] -E production -c config/unicorn.conf.rb
    172 ?        Sl     0:06 unicorn worker[3] -E production -c config/unicorn.conf.rb
    180 ?        Ss     0:00 postgres: 13/main: discourse discourse [local] idle
    187 ?        Sl     0:07 unicorn worker[4] -E production -c config/unicorn.conf.rb
    203 ?        Sl     0:07 unicorn worker[5] -E production -c config/unicorn.conf.rb
    213 ?        Sl     0:07 unicorn worker[6] -E production -c config/unicorn.conf.rb
    233 ?        Sl     0:07 unicorn worker[7] -E production -c config/unicorn.conf.rb
  11733 pts/1    Ss     0:00 /bin/bash --login
  11748 ?        S      0:00 sleep 1
  11749 pts/1    R+     0:00 ps ax

好的,所以我进入了应用并在 Discord 的 Docker 中安装了 Lynx,我确实能在那里看到欢迎页面!因此安装是成功的,问题出在从 Docker 内部到服务器的网络映射上。

以下是 #docker ps 的输出:

CONTAINER ID   IMAGE                 COMMAND        CREATED          STATUS          PORTS                                             NAMES
c6d0209e4e72   local_discourse/app   "/sbin/boot"   51 minutes ago   Up 51 minutes   127.0.0.1:8880->80/tcp, 127.0.0.1:4443->443/tcp   app

…以及 app.yml 的信息:

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"

expose:
  - "127.0.0.1:8880:80"   # http
  - "127.0.0.1:4443:443" # https

params:
  db_default_text_search_config: "pg_catalog.english"
  db_shared_buffers: "2048MB"

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

  UNICORN_WORKERS: 8

  DISCOURSE_HOSTNAME: [已隐藏]


  DISCOURSE_SMTP_ADDRESS: [已隐藏]
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: [已隐藏]
  DISCOURSE_SMTP_PASSWORD: "[已隐藏]"
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (可选,默认为 true)
  DISCOURSE_SMTP_DOMAIN: [已隐藏]
  DISCOURSE_NOTIFICATION_EMAIL: [已隐藏]


## Docker 容器是无状态的;所有数据都存储在 /shared 中
volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/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

## 构建后需要运行的任何自定义命令
run:
  - exec: echo "开始执行自定义命令"
  - exec: echo "结束执行自定义命令"

啊,我明白问题出在哪了。8880:80 端口映射工作正常,我只是在测试 4443:443 这一侧。由于存在证书错误,它无法正常工作。我打算在虚拟主机层面于 Web 服务器上安装并管理证书。一旦全部调试成功,我会发布最终解决方案供感兴趣的朋友参考。

安装环境
Hestia 控制面板(用于服务器管理)
Apache2(Web 服务器)
Discourse 运行在 Docker 中,并通过反向代理连接到 Apache2
Let’s Encrypt 不进行反向代理,而是由 Hestia 在 www_root 中管理

  1. 在 app.yml 中设置 Unix 套接字(并重新构建):
  - "templates/web.socketed.template.yml"
  1. 从 app.yml 中移除端口映射和 Let’s Encrypt(并重新构建):
#  - "templates/web.ssl.template.yml"
#  - "templates/web.letsencrypt.ssl.template.yml"
#  - "80:80"   # http
#  - "443:443" # https
#  LETSENCRYPT_ACCOUNT_EMAIL: {你的邮箱地址,可能类似这样}
  1. 为 HestiaCP 创建替代的 apache2.conf 模板,如下所示(对于未使用 HestiaCP 的用户,请注意 %{replace 标签}% 是非常标准且直观的,只要查看任何其他 apache2.conf 文件即可理解。最重要的部分很少使用 %{replace 标签}%。另外请注意,文本 standalone 根据你的设置也可能是 socket-only。如果你记不清,可以查看 /var/discourse/shared/ 目录中的内容。

{自定义模板}.stpl

<VirtualHost %ip%:%web_ssl_port%>

    ServerName %domain_idn%

    ProxyPreserveHost On
    ProxyRequests Off
    ProxyPass / unix:/var/discourse/shared/standalone/nginx.http.sock|http://localhost/
    ProxyPassReverse  / unix:/var/discourse/shared/standalone/nginx.http.sock|http://localhost/

    ServerAdmin %email%

    Alias /vstats/ %home%/%user%/web/%domain%/stats/
    Alias /error/ %home%/%user%/web/%domain%/document_errors/

    CustomLog /var/log/%web_system%/domains/%domain%.bytes bytes
    CustomLog /var/log/%web_system%/domains/%domain%.log combined
    ErrorLog /var/log/%web_system%/domains/%domain%.error.log
    <Directory %home%/%user%/web/%domain%/stats>
        AllowOverride All
    </Directory>
    <Directory %sdocroot%>
        AllowOverride All
        SSLRequireSSL
        Options +Includes -Indexes +ExecCGI
    </Directory>
    SSLEngine on
    SSLVerifyClient none
    SSLCertificateFile %ssl_crt%
    SSLCertificateKeyFile %ssl_key%
    %ssl_ca_str%SSLCertificateChainFile %ssl_ca%

    IncludeOptional %home%/%user%/conf/web/%domain%/%web_system%.ssl.conf_*

</VirtualHost>

{自定义模板}.tpl

<VirtualHost %ip%:%web_port%>

    ServerName %domain_idn%
    %alias_string%

    ProxyPreserveHost On
    ProxyRequests Off
    ProxyPass / unix:/var/discourse/shared/standalone/nginx.http.sock|http://localhost/
    ProxyPassReverse  / unix:/var/discourse/shared/standalone/nginx.http.sock|http://localhost/

    RewriteEngine On
    RewriteCond %{REQUEST_URI} !^.well-known/acme-challenge
    RewriteRule ^(.*) https://%domain_idn%/$1 [R=301,L]

    Alias /vstats/ %home%/%user%/web/%domain%/stats/
    Alias /error/ %home%/%user%/web/%domain%/document_errors/

    CustomLog /var/log/%web_system%/domains/%domain%.bytes bytes
    CustomLog /var/log/%web_system%/domains/%domain%.log combined
    ErrorLog /var/log/%web_system%/domains/%domain%.error.log

    <Directory %home%/%user%/web/%domain%/stats>
        AllowOverride All
    </Directory>
    <Directory %sdocroot%>
        AllowOverride All
        Options +Includes -Indexes +ExecCGI
    </Directory>

    IncludeOptional %home%/%user%/conf/web/%domain%/%web_system%.conf_*

</VirtualHost>

啊,我上面漏了一点,每个文件中还需要添加以下内容:

RequestHeader set X-Forwarded-Proto https