我在 Caddy 和 Discourse 之间使用 Unix 套接字而非 TCP,但偶尔会出现 'unix:' 错误

日志显示如下内容:

PG::InvalidTextRepresentation (ERROR: invalid input syntax for type inet: "unix:" LINE 7: client_ip = 'unix:', ^ ) lib/mini_sql_multisite_connection.rb:109:in 'MiniSqlMult

Job exception: ERROR: invalid input syntax for type inet: "unix:" LINE 2: SET ip_address = 'unix:' ^  

发生此错误时,会显示一个**“Oops - Error 500”**页面。起初,我以为是 Caddy 的问题,因为它没有将客户端的 IP 地址转发给 Discourse,所以我尝试了多种不同的配置,但都没有解决问题。

值得一提的是,这种情况并不经常发生。它只是偶尔出现,而且当它发生时,只需刷新页面即可立即恢复网站。随后网站会正常运行相当长一段时间,直到错误再次随机出现。

我不是专家,而且我的自托管实例使用的是 Nginx 而不是 Caddy,但我能否请您提供 ./app/containers.yml 中实际的 Caddy 和 Discourse 模板配置?

您还记得在出错之前通常做了什么吗?我是说,更改管理员设置、发帖,还是使用了某些插件?

很抱歉无法为您的问题提供具体帮助,但我想您的回复能为您的问题增加价值,从而获得社区清晰快速的回应。

这意味着请求/用户有一个指向你的 Socket 的远程 IP,这表明你的代理链中的某些配置不正确。

@satonotdead @Falco

app.yml

templates:
  - "templates/postgres.18.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  
  - "templates/web.socketed.template.yml"
  
  - "templates/enable-ruby-yjit.yml"


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

params:
  ## 此容器应使用哪个 Git 版本?(默认:latest)
  version: latest
  ## 最大上传大小(默认:10m)
  upload_size: 150m
  
  db_default_text_search_config: "pg_catalog.english"

  ## 将 db_shared_buffers 设置为总内存的 25%。
  ## 引导程序会根据检测到的 RAM 自动设置,或者您可以覆盖
  db_shared_buffers: "2048MB"

  ## 可以提高排序性能,但会增加每个连接的内存使用量
  #db_work_mem: "40MB"

  ## 此容器应使用哪个 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: en

  ## https://meta.discourse.org/t/rescaling-the-server-which-configs-need-to-be-changed-unicorn-workers-memory-etc/252788
  ## 支持多少并发 Web 请求?取决于内存和 CPU 核心数。
  ## 引导程序会根据检测到的 CPU 自动设置,或者您可以覆盖
  UNICORN_WORKERS: 8

  ## TODO:此 Discourse 实例响应的域名
  ## 必填。Discourse 无法使用裸 IP 地址运行。
  DISCOURSE_HOSTNAME: example.com

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

  ## TODO:初始注册时将设为管理员和开发者的逗号分隔电子邮件列表
  ## 例如 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'admin+discourse@example.com'

  ## TODO:用于验证新账户和发送通知的 SMTP 邮件服务器
  # SMTP 地址是必填项
  # 警告:SMTP 密码应使用引号括起来以避免问题
  DISCOURSE_SMTP_ADDRESS: smtp.provider.com
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: noreply@example.com
  DISCOURSE_SMTP_PASSWORD: "***"
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (可选,默认:true)
  DISCOURSE_SMTP_DOMAIN: example.com # (某些提供商要求)
  DISCOURSE_NOTIFICATION_EMAIL: noreply@example.com
  #DISCOURSE_SMTP_OPENSSL_VERIFY_MODE: peer        # (可选,默认:peer,有效值:none, peer, client_once, fail_if_no_peer_cert)
  #DISCOURSE_SMTP_AUTHENTICATION: plain            # (默认:plain,有效值:plain, login, cram_md5)

  ## 如果您添加了 Let's Encrypt 模板,请取消注释下方以获取免费 SSL 证书
  # LETSENCRYPT_ACCOUNT_EMAIL: admin+letsencrypt@example.com

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

  ## 用于 IP 地址查找的 MaxMind 地理位置 IP 账户 ID 和许可证密钥
  ## 请参阅 https://meta.discourse.org/t/-/173941 了解详情
  #DISCOURSE_MAXMIND_ACCOUNT_ID: 123456
  #DISCOURSE_MAXMIND_LICENSE_KEY: 1234567890123456

  # 强制 HTTPS
  DISCOURSE_FORCE_HTTPS: true
  
  # 请求限制
  DISCOURSE_MAX_REQS_PER_IP_MODE: none
  DISCOURSE_MAX_ADMIN_API_REQS_PER_MINUTE: 12000
  
  DISCOURSE_MAX_DATA_EXPLORER_API_REQ_MODE: none
  DISCOURSE_MAX_DATA_EXPLORER_API_REQS_PER_10_SECONDS: 1000

  DISCOURSE_YJIT_ENABLED: true

## Docker 容器是无状态的;所有数据存储在 /shared 中
volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log
  - volume:
      host: /var/discourse/plugins
      guest: /var/plugins

## 插件放在这里
## 请参阅 https://meta.discourse.org/t/19157 了解详情
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - cp -a /var/plugins/. $home/plugins/

## 构建后运行的任何自定义命令
run:
  - exec: echo "Beginning of custom commands"
  ## 如果您想设置首次注册的“发件人”电子邮件地址,请取消注释并更改:
  ## 收到第一封注册电子邮件后,重新注释该行。它只需要运行一次。
  #- exec: rails r "SiteSetting.notification_email='info@unconfigured.discourse.org'"
  - exec: echo "End of custom commands"

Caddyfile

#---------------------------------------------------------------------------------

{
	storage file_system {
		root /var/caddy/data
	}
}

#---------------------------------------------------------------------------------

# 为了在 SSL 安全评分中获得 A+
(hsts) {
	header {
		Strict-Transport-Security "max-age=31536000; includeSubDomains"
	}
}

# 自托管 acme-dns 凭据
(tls-challenge) {
	tls admin+letsencrypt@example.com {
		dns acmedns {
			username ***
			password ***
			subdomain ***
			server_url http://acme.example.com:5005
		}
	}
}

#---------------------------------------------------------------------------------
# 论坛 (Discourse)
#---------------------------------------------------------------------------------
example.com {
	#-------------------------------------------------------------------------------
	import hsts
	import tls-challenge
	#-------------------------------------------------------------------------------
	request_header X-Real-IP {remote_host}
	reverse_proxy unix//var/discourse/shared/standalone/nginx.http.sock
	#-------------------------------------------------------------------------------
}

#---------------------------------------------------------------------------------
# 子域名
#---------------------------------------------------------------------------------

#---------------------------------------------------------------------------------
*.example.com {
	#-------------------------------------------------------------------------------
	import hsts
	import tls-challenge
	#-------------------------------------------------------------------------------

	# 强制使用非 www 版本的域名
	@www host www.example.com
	redir @www https://example.com{uri} permanent
  
	#-------------------------------------------------------------------------------
  
	@store host store.example.com
	handle @store {
		@wc_private {
			path /license.txt
			path /readme.html

			# 关键文件
			path /wp-config.php
			path /xmlrpc.php
			path /wp-settings.php
			path /wp-load.php
			path /wp-blog-header.php

			path /wp-admin.php

			path /wp-admin/install.php

			path /wp-content/uploads/wc-logs/*
			path /wp-content/uploads/nuvei-logs/*

			path /wp-content/uploads/woocommerce_uploads/*
		}

		# protect_php
		@wc_private_php {
			not path /wp-includes/ms-files.php

			path_regexp protect_php ^/(wp-includes|wp-admin/includes|wp-content/uploads)/.*\.php$
		}
		respond @wc_private 403
		respond @wc_private_php 403

		root * /usr/share/wordpress
		php_fastcgi unix//run/php/php8.3-fpm.sock
		file_server
	}
  
  #-------------------------------------------------------------------------------
  # 多个独立 HTML 文件
  #-------------------------------------------------------------------------------

  @join host join.example.com
  handle @join {
    root * /var/example/join
    file_server
  }

  #-------------------------------------------------------------------------------
  # 无匹配器
  #-------------------------------------------------------------------------------

	handle {
		respond 404
	}

	#-------------------------------------------------------------------------------
}

更具体地说,此错误在前一个错误发生后几小时出现,并不经常发生。我在 app.yml 或 Caddyfile 中没有看到任何异常。

如果套接字连接在断开之前一直正常,我认为您可以添加显式的超时设置和传输选项。

我认为你可以添加显式的超时设置和传输选项

怎么做?

嗯,正如我之前所说,我没有使用 Caddy,但你可以查看他们的官方文档

我想我找到问题了。如果我执行重建,我从不重启 Caddy。从现在开始,我会这样做:

./launcher rebuild app && systemctl reload caddy

我怀疑 Caddy 在重建后会使用 socket 缓存或类似的东西。但这通常不会在重建后立即发生。我们拭目以待。