Crius
(Crius)
2023 年5 月 13 日 17:23
1
我有一个正在运行的 Discourse 安装(实际上有两个,一个在暂存环境,一个在生产环境,分别在不同的虚拟机上)。我正在暂存环境中进行测试。安装是通过官方指南 完成的。
目前,Grafana/Prometheus/Node Exporter 堆栈是通过 docker compose 部署在与 Discourse 安装相同的虚拟机上。
这是 docker-compose.yaml:
version: "3"
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
restart: unless-stopped
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
networks:
- prometheus-cadvisor
node_exporter:
image: quay.io/prometheus/node-exporter:latest
container_name: node_exporter
command:
- '--path.rootfs=/host'
pid: host
restart: unless-stopped
volumes:
- '/:/host:ro,rslave'
networks:
- prometheus-node_exporter
prometheus:
image: prom/prometheus:latest
restart: unless-stopped
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus:/app.cfg
networks:
- world
- prometheus-cadvisor
- prometheus-node_exporter
- discourse
- grafana-prometheus
command: >-
--config.file=/app.cfg/prometheus.yaml
--storage.tsdb.path=/prometheus
--web.console.libraries=/usr/share/prometheus/console_libraries
--web.console.templates=/usr/share/prometheus/consoles
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_USER: [OMITTED]
GF_SECURITY_ADMIN_PASSWORD: [OMITTED]
GF_PATHS_PROVISIONING: '/app.cfg/provisioning'
volumes:
- ./grafana:/app.cfg
- ./grafana/provisioning:/etc/grafana/provisioning
networks:
- world
- grafana-prometheus
networks:
world:
grafana-prometheus:
internal: true
prometheus-cadvisor:
internal: true
prometheus-node_exporter:
internal: true
discourse:
external: true
我重建了 Discourse,指定了一个网络,使其不部署在 bridge 上,并将 Prometheus 连接到同一个网络。
docker network create -d bridge discourse
/var/discourse/launcher rebuild app --docker-args '--network discourse'
我通过进入 Prometheus 容器并使用内部网络别名 ping Discourse 容器进行了测试,它能够到达。
现在,在配置 Prometheus 作业以抓取指标时,使用内部 IP,我只能看到 server returned HTTP status 404 Not Found。
这是 Prometheus 的配置:
global:
scrape_interval: 30s
scrape_timeout: 10s
rule_files:
scrape_configs:
- job_name: prometheus
metrics_path: /metrics
static_configs:
- targets:
- 'prometheus:9090'
- job_name: node_exporter
static_configs:
- targets:
- 'node_exporter:9100'
- job_name: discourse_exporter
static_configs:
- targets:
- 'vmuniqueID-app:80'
vmuniqueID 是实际虚拟机名称的替换。
根据文档此处 ,通过内部 IP 访问应该是允许的:
开箱即用,我们允许 metrics 路由访问管理员和私有 IP。
请帮我看看我遗漏了什么
Crius
(Crius)
2023 年5 月 13 日 17:47
2
为了进一步探究,我尝试从 Discourse 生成一个 API 密钥,并使用内部主机名访问它,响应不是 301,这是正确的,因为每个请求都应该重定向到 https。
我认为问题在于,即使是来自内部 IP 的请求,也被视为未经授权,并因此导致 404。
pfaffman
(Jay Pfaffman)
2023 年5 月 14 日 02:14
3
您是否已安装并启用了 Prometheus 插件?它应该允许来自私有地址的请求,但您可以尝试设置环境变量以允许来自您正在拉取的 IP 的访问。
Crius
(Crius)
2023 年5 月 14 日 22:04
4
是的,Prometheus 也在同一个虚拟机上,并作为 Docker 容器部署。一切正常(我还有其他导出器已部署),但不知何故,Discourse Prometheus 插件即使明显已启动并运行,也无法接受请求。
您所说的环境变量是指 Discourse 的 app.yaml 文件中的环境吗?
那么,类似这样:
env:
DISCOURSE_PROMETHEUS_TRUSTED_IP_ALLOWLIST_REGEX: 172.20.0.3
172.20.0.3 是 Prometheus 在与 Discourse 连接的 Docker 虚拟网络上的当前内部 IP。
我已经尝试使用所有容器共享的外部 IP(VM 的静态 IP),但由于它们在同一个网络上,当一个容器尝试访问另一个容器时,它会通过内部 IP 进行访问。
./launcher restart app 应该足以让环境变量生效,对吧?
在这种情况下,我得到:
Get "http://vmi1187507-app:80/metrics": dial tcp: lookup vmi1187507-app on 127.0.0.11:53: server misbehaving
vmi1187507-app 是其网络中的容器网络名称。名称是正确的,我可以在运行的 Prometheus 容器中 ping 通它。
说实话,我不知道 127.0.0.11:53 是从哪里来的
如果我注释掉环境变量,消息也是一样的。
pfaffman
(Jay Pfaffman)
2023 年5 月 14 日 22:18
5
我认为 是的,但也不完全确定。你可以从容器内部进行测试,看看是否能从那里 curl 它。
Crius
(Crius)
2023 年5 月 14 日 22:33
6
从 prometheus 容器运行 wget 返回:
/prometheus # wget http://vmi1229594-app:80/metrics
Connecting to vmi1229594-app:80 (172.20.0.2:80)
Connecting to [public URL] (172.67.69.84:443)
wget: note: TLS certificate validation not implemented
wget: server returned error: HTTP/1.1 404 Not Found
我猜这是 Discourse nginx 容器的自动重定向?
发生的情况是,它会转发到公共域名的 https,这是一个内部 Cloudflare IP,当然会告诉任何请求返回。
现在这无关紧要,因为如果来自内部 IP 的请求路径是 http://yourwebsite.com/metrics,则不应发生此重定向,我期望插件通过添加一个添加此规则的 nginx conf 来处理此问题,但显然没有发生?
Discourse 开发者能否介入?我不想随意 ping 人,而且感觉很奇怪,以前没有人报告过这个问题。
编辑:我重建时还指定了网络配置的静态主机名,因为我注意到每次重建时都会为容器分配一个新的随机主机名。
之后,我还尝试将 prometheus 作业设置为访问 https 版本的指标,但问题又回到了第一步:
global:
scrape_interval: 30s
scrape_timeout: 10s
rule_files:
scrape_configs:
# other jobs
# [...]
- job_name: discourse_exporter
scheme: https
tls_config:
insecure_skip_verify: true
static_configs:
- targets:
- 'discourse_app'
/prometheus # wget https://discourse_app/metrics
Connecting to discourse_app (172.20.0.2:443)
wget: note: TLS certificate validation not implemented
Connecting to [public URL] (104.26.4.193:443)
wget: server returned error: HTTP/1.1 404 Not Found
此时,这似乎是插件本身的问题。
pfaffman
(Jay Pfaffman)
2023 年5 月 15 日 00:40
7
听起来没错。你需要使用主机名访问它,而不是容器名。
Crius
(Crius)
2023 年5 月 15 日 07:42
8
我正在使用主机名,我写了很多并且很晚,可能有些令人困惑,但绝对使用的是内部网络主机名。
Crius
(Crius)
2023 年5 月 15 日 10:21
10
感谢 @JammyDodger ,但不幸的是,这些资源并没有帮助。
它们遇到了类似但略有不同的问题,以至于不适用于此情况。
为确保万无一失,我尝试了其中一个主题(以及 @pfaffman )提出的建议,并调整了 DISCOURSE_PROMETHEUS_TRUSTED_IP_ALLOWLIST_REGEX 环境变量。
我测试了:
注释掉它
添加并使用内部 IP 值
添加并使用外部 IP 值
还尝试更改 Prometheus 抓取作业,将 Discourse 安装地址设置为:
直接内部 IP
Docker 内部主机名
直接外部 IP
公共域名
在所有情况下,我都尝试了 http 和 https。
在所有情况下,我都收到 404 错误。
我期望的是实际的页面响应,因为请求来自内部 IP。
1 个赞
leonardo
(Leonardo Mosquera)
2023 年5 月 15 日 16:41
11
Jay 的意思是,你需要使用配置的主机名(在你的容器 .yml 定义中为 DISCOURSE_HOSTNAME),而不是任何碰巧解析到同一 IP 地址的主机名。
这是故意的,这样你就不能轻易地从任何地方反向代理一个公共实例,并且只接受配置的主机名:
$ curl -I https://try.discourse.org/about.json
HTTP/2 200
server: nginx
date: Mon, 15 May 2023 16:25:05 GMT
content-type: application/json; charset=utf-8
[...]
# The following is equivalent to creating a DNS record at
# try.somebogusreverseproxy.com pointing to the same IP address as try.discourse.org,
# and then requesting https://try.somebogusreverseproxy.com/about.json
$ curl -H 'Host: try.somebogusreverseproxy.com' -I https://try.discourse.org/about.json
HTTP/2 404
cache-control: no-cache
content-length: 1427
content-type: text/html
cdck-proxy-id: app-router-tiehunter02.sea1
cdck-proxy-id: app-balancer-tieinterceptor1b.sea1
反之,如果你尝试这样做:
curl -H 'Host: YOUR_CONFIGURED_HOSTNAME' -I https://discourse_app/metrics
它应该可以工作,但这是一种 hack。预期是你将根据需要设置 DNS,以便可以透明地通过配置的主机名访问 Discourse:
curl -I https://YOUR_CONFIGURED_HOSTNAME/metrics
如何做到这一点很大程度上取决于你的需求,但最简单的选择是在 HTTP 请求发起的源头在 /etc/hosts 中设置一个别名。
3 个赞
Prometheus exporter 不在 80 端口运行 - 它侦听自己的端口。默认情况下是 端口 9405 。
5 个赞
Crius
(Crius)
2023 年5 月 16 日 10:28
13
但是,如果我尝试定位该特定端口,我会收到“连接被拒绝”消息。
Get "http://discourse_app:9405/metrics": dial tcp 172.20.0.2:9405: connect: connection refused
为了确保万无一失,我还从 Prometheus 容器内部使用 wget 进行了测试。
/prometheus # ping discourse_app
PING discourse_app (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.223 ms
64 bytes from 172.20.0.2: seq=1 ttl=64 time=0.270 ms
^C
--- discourse_app ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.223/0.246/0.270 ms
/prometheus # wget discourse_app:9405/metrics
Connecting to discourse_app:9405 (172.20.0.2:9405)
wget: can't connect to remote host (172.20.0.2): Connection refused
是的,我改用 wget 测试了(Prometheus 容器是一个精简的 busybox),但最终还是能够访问 metrics。
那么您的意思是,我应该找到一种方法让运行 Prometheus 的容器在 /etc/hosts 中有一个解析条目……抱歉,我没听懂
我所做的是添加另一个只有 nginx 的 docker,并提供一个正向代理配置,该配置将 Host 标头添加到它接收到的请求中。它不公开任何端口,因此只能通过内部虚拟网络访问。
那么情况如何变化?
Prometheus Job:
- job_name: discourse_exporter_proxy
scheme: http
static_configs:
- targets:
- 'discourse_forward_proxy:8080'
docker-compose.yaml(仅代理部分)
version: "3"
services:
# [...]
discourse_forward_proxy:
image: nginx:latest
container_name: discourse_forward_proxy
restart: unless-stopped
volumes:
- ./discourse_forward_proxy/:/etc/nginx/conf.d
networks:
- prometheus-discourse_forward_proxy
- discourse
# [...]
networks:
prometheus-discourse_forward_proxy:
internal: true
discourse:
external: true
在您的 docker-compose.yaml 所在的目录中,有一个 ./discourse_forward_proxy/discourse_forward_proxy.conf
server {
listen 8080;
location /metrics {
proxy_set_header Host "YOUR_DOMAIN_HERE.COM";
proxy_pass https://discourse_app/metrics;
}
}
看,就这样了:
1 个赞
Crius
(Crius)
2023 年5 月 16 日 12:17
14
为了方便以后查阅,我有一个仓库,里面设置了所有必需的东西。
其中包含一些硬编码的值(例如我们网站在正向代理配置文件中的完全限定域名),如果其他人想使用它,则需要进行更改,但也许它对其他有需要的人会有所帮助。
它包含了所有内容,从 docker compose 到 nginx conf 以及资源的 grafana 配置和仪表板。
Contribute to netgamers-forum/ngi-monitor-stack development by creating an account on GitHub.
这是由于下一行:
GlobalSetting.add_default :prometheus_collector_port, 9405
GlobalSetting.add_default :prometheus_webserver_bind, "localhost"
GlobalSetting.add_default :prometheus_trusted_ip_allowlist_regex, ""
绑定到 localhost 意味着它只能通过 localhost IP 连接,这就是为什么连接到 172.20.0.2 会失败。这是一种安全措施,可确保它不会意外地暴露给比预期更广泛的受众。
如果在容器定义文件中设置:
DISCOURSE_PROMETHEUS_WEBSERVER_BIND: '*'
它将监听所有 IP 地址,您将能够从另一个容器连接到它。
这使其起作用的原因:
server {
listen 8080;
location /metrics {
proxy_set_header Host "YOUR_DOMAIN_HERE.COM";
proxy_pass https://discourse_app/metrics;
}
}
是因为这个 nginx 容器现在通过 localhost IP 与 prometheus 通信。
如果您不确定服务正在监听的 IP 或端口,您可以使用 ss -ltp 或 netstat -ltp(在 容器内部!所需的包分别是 net-tools 和 iproute2)来查看它们。例如,我刚刚使用 prometheus 插件重建了一个容器,看到:
root@discourse-docker-app:/# ss -ltp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 127.0.0.1:3000 0.0.0.0:*
LISTEN 0 128 0.0.0.0:postgresql 0.0.0.0:*
LISTEN 0 128 0.0.0.0:https 0.0.0.0:* users:(("nginx",pid=555,fd=7))
LISTEN 0 128 127.0.0.1:9405 0.0.0.0:*
LISTEN 0 128 0.0.0.0:redis 0.0.0.0:*
LISTEN 0 128 0.0.0.0:http 0.0.0.0:* users:(("nginx",pid=555,fd=6))
LISTEN 0 128 [::]:postgresql [::]:*
LISTEN 0 128 [::]:https [::]:* users:(("nginx",pid=555,fd=8))
LISTEN 0 128 [::]:redis [::]:*
root@discourse-docker-app:/# curl http://172.17.0.2:9405/metrics
curl: (7) Failed to connect to 172.17.0.2 port 9405: Connection refused
root@discourse-docker-app:/# curl http://localhost:9405/metrics
# HELP discourse_collector_working Is the master process collector able to collect metrics
# TYPE discourse_collector_working gauge
discourse_collector_working 1
# HELP discourse_collector_rss total memory used by collector process
# TYPE discourse_collector_rss gauge
discourse_collector_rss 38178816
…
这是名称服务器拒绝为 vmi1187507-app 进行 IP 查找请求。端口 53 是 DNS。
2 个赞
Crius
(Crius)
2023 年5 月 16 日 23:18
19
Michael,这太棒了,感谢你花时间写下来。
我将在周末进行测试,因为这周的工作日我已经花了太多时间了
在我尝试的过程中,我曾尝试将 Prometheus 容器请求指标的内部 IP 添加到 DISCOURSE_PROMETHEUS_TRUSTED_IP_ALLOWLIST_REGEX,但没有成功。
你建议的是 DISCOURSE_PROMETHEUS_WEBSERVER_BIND。我能问一下你从哪里得到它的吗?我猜它是在 app.yml 文件中要添加的另一个环境变量,对吗?
Crius:
但它没起作用
怎么 没起作用?
如果连接失败,那么允许列表的设置就没有意义了,因为它是在 L4 连接之后 操作的。
Crius:
我可以问一下你从哪里得到的吗?
Discourse 代码库中有魔法 ,如果你在 ENV 中设置了 DISCOURSE_SITE_OR_GLOBAL_SETTING_NAME,它就会覆盖。
所以设置它将覆盖:
GlobalSetting.add_default :prometheus_webserver_bind, "localhost"
system
(system)
关闭
2023 年6 月 16 日 00:08
21
This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.