Zabbix + PostgreSQL 16 + TimescaleDB 2.19.x 部署

Zabbix 支持 TimescaleDB 作为 PostgreSQL 扩展,用于大规模时间序列数据。

TimescaleDB 不支持给 Zabbix Proxy 当数据库 ,只能给中心 zabbix-server 的 PostgreSQL 后端使用。

Zabbix 7.0.6 开始把 TimescaleDB 最大支持版本提升到 2.17.x,7.0.13 提升到 2.19.x。

使用 Docker Compose 部署 PostgreSQL

  • pg16
  • timescaledb 2.19.x

目录结构示例:

.
├── config # postgresql 等配置文件路径
│ ├── pg_hba.conf
│ └── postgresql.conf
├── docker-compose.yaml
├── .env # 环境变量统一存储
├── initdb # 数据库初始化 sql,只有在数据库为空的情况下才会执行
│ ├── 01-create-zabbix-user.sql
│ ├── 02-create-timescaledb.sql
│ └── 03-create-db.sql
├── postgres-data # Postgresql/scaledb 持久化数据
│ └── pgdata
└── zabbix-data # zabbix 持久化数据
├── alertscripts
├── enc
├── export
├── externalscripts
└── ssl

10 directories, 6 files


.env 内容参考

.env
TZ=Asia/Shanghai

POSTGRES_DB=zabbix
POSTGRES_USER=postgres
POSTGRES_PASSWORD=ReplaceMe_SuperStrong_Zabbix

ZABBIX_DB_USER=zabbix
ZABBIX_DB_PASSWORD=ReplaceMe_SuperStrong_Zabbix

PGDATA=/var/lib/postgresql/data/pgdata

initdb 中的初始化 SQL

initdb/01-create-zabbix-user.sql
DO
$$
BEGIN
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'zabbix') THEN
CREATE ROLE zabbix LOGIN PASSWORD 'ReplaceMe_SuperStrong_Zabbix';
END IF;
END
$$;

initdb/02-create-timescaledb.sql
CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;
initdb/03-create-db.sql
GRANT ALL PRIVILEGES ON DATABASE zabbix TO zabbix;
ALTER DATABASE zabbix OWNER TO zabbix;

Postgresql 主配置 config/postgresql.conf

config/postgresql.conf
listen_addresses = '*'
port = 5432
max_connections = 200

shared_buffers = 8GB
effective_cache_size = 24GB
work_mem = 32MB
maintenance_work_mem = 1GB

wal_level = replica
max_wal_size = 8GB
min_wal_size = 1GB
checkpoint_timeout = 15min
checkpoint_completion_target = 0.9
wal_compression = on

random_page_cost = 1.1
effective_io_concurrency = 200

default_statistics_target = 200
shared_preload_libraries = 'timescaledb'

autovacuum = on
autovacuum_max_workers = 5
autovacuum_naptime = 10s
autovacuum_vacuum_scale_factor = 0.02
autovacuum_analyze_scale_factor = 0.01

log_timezone = 'Asia/Shanghai'
timezone = 'Asia/Shanghai'
log_min_duration_statement = 1000
log_checkpoints = on
log_connections = off
log_disconnections = off
log_lock_waits = on

timescaledb.telemetry_level = off

config/pg_hba.conf 配置示例

# TYPE  DATABASE        USER            ADDRESS                 METHOD

local all all trust
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256

# Docker 网段,仅示例
host zabbix zabbix 10.244.0.0/16 scram-sha-256
host zabbix zabbix 172.18.0.0/16 scram-sha-256

# 运维堡垒机
host all postgres 192.168.20.10/32 scram-sha-256

docker-compose.yaml 配置示例

docker-compose.yaml
services:
timescaledb:
image: timescale/timescaledb:2.19.3-pg16
container_name: zabbix-timescaledb
hostname: zabbix-timescaledb
restart: unless-stopped

env_file:
- .env

environment:
TZ: ${TZ}
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
ZABBIX_DB_USER: ${ZABBIX_DB_USER}
ZABBIX_DB_PASSWORD: ${ZABBIX_DB_PASSWORD}
PGDATA: ${PGDATA}
POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256 --auth-local=trust --data-checksums"

ports:
- "5432:5432"

volumes:
- ./postgres-data:/var/lib/postgresql/data
- ./initdb:/docker-entrypoint-initdb.d:ro
- ./config/postgresql.conf:/etc/postgresql/postgresql.conf:ro
- ./config/pg_hba.conf:/etc/postgresql/pg_hba.conf:ro
- /etc/localtime:/etc/localtime:ro

command:
[
"postgres",
"-c", "config_file=/etc/postgresql/postgresql.conf",
"-c", "hba_file=/etc/postgresql/pg_hba.conf"
]

healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 12

ulimits:
nofile:
soft: 65536
hard: 65536

zabbix-server:
image: zabbix/zabbix-server-pgsql:alpine-7.4-latest
container_name: zabbix-server
hostname: zabbix-server
restart: unless-stopped

env_file:
- .env

environment:
TZ: ${TZ}
DB_SERVER_HOST: timescaledb
DB_SERVER_PORT: 5432
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${ZABBIX_DB_USER}
POSTGRES_PASSWORD: ${ZABBIX_DB_PASSWORD}
ENABLE_TIMESCALEDB: "true"

depends_on:
timescaledb:
condition: service_healthy

ports:
- "10051:10051"

volumes:
- ./zabbix-data/alertscripts:/usr/lib/zabbix/alertscripts:ro
- ./zabbix-data/externalscripts:/usr/lib/zabbix/externalscripts:ro
- ./zabbix-data/export:/var/lib/zabbix/export
- ./zabbix-data/enc:/var/lib/zabbix/enc
- /etc/localtime:/etc/localtime:ro

zabbix-web:
image: zabbix/zabbix-web-nginx-pgsql:alpine-7.4-latest
container_name: zabbix-web
hostname: zabbix-web
restart: unless-stopped

env_file:
- .env

environment:
DB_SERVER_HOST: timescaledb
DB_SERVER_PORT: 5432
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${ZABBIX_DB_USER}
POSTGRES_PASSWORD: ${ZABBIX_DB_PASSWORD}
ZBX_SERVER_HOST: zabbix-server
PHP_TZ: ${TZ}

depends_on:
- zabbix-server

ports:
- "8080:8080"

volumes:
- ./zabbix-data/ssl:/etc/ssl/nginx:ro
- /etc/localtime:/etc/localtime:ro


常用操作

从 Zabbix Server 测试 Zabbix Agent 连通性

以下命令可以用于在 Zabbix Server 检查到 Zabbix Agent 的连通性,连接 Agent 正常会返回 1

docker compose exec -T zabbix-server zabbix_get -s 172.247.1.1 -p 10050 -k agent.ping

如果防火墙已经放通,却依然无法连接,很可能是 zabbix-agent2 配置中的 Server= 配置的 IP 不是 Zabbix Server 实际使用的 IP。此时可以通过 tcpdump 抓包确定请求 Zabbix Agent 的 Zabbix Server IP

# tcpdump -n dst port 10050
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes

12:39:34.936583 IP 172.18.0.3.37688 > 172.247.2.14.10050: Flags [S], seq 1718798137, win 64240, options [mss 1460,sackOK,TS val 2935844814 ecr 0,nop,wscale 7], length 0
12:39:35.946554 IP 172.18.0.3.37688 > 172.247.2.14.10050: Flags [S], seq 1718798137, win 64240, options [mss 1460,sackOK,TS val 2935845824 ecr 0,nop,wscale 7], length 0

也可查看 zabbix-agent2 日志:

# tail -f /var/log/zabbix/zabbix_agent2.log 
2026/03/30 01:48:15.505489 connection from "172.18.0.3" rejected, allowed hosts: "23.225.3.6/29"
2026/03/30 01:48:39.128438 connection from "172.18.0.3" rejected, allowed hosts: "23.225.3.6/29"

分路由发送告警

为了将不同的告警发送给不同的用户,首先需要为每个用户添加 Media,以 Admin 用户为例,为其添加 Media。定位到 Users -> Users: Admin -> Media -> Add ,以添加 Telegram 为例,在 Send to 中填入 群组 ID

配置 Medias 。定位到 Alerts -> Media types 。Zabbix Server 已经配置了非常多可用的告警 Medias,选择 Telegram,将 api_token 修改为你自己的 Telegram Bot API Token,确保配置中的 api_chat_id 的值为 {ALERT.SENDTO}

不要在这里硬编码 api_chat_id ,否则使用 Telegram 这个渠道只能将所有的消息发送到同一个群组,无法达到告警分流的作用,正确做法是将其配置为 {ALERT.SENDTO} ,当用户 Media 被调用时,会自动使用用户 Media 变量 Send to 的值达到告警分流到不通用户的 Media

创建 Actions 触发发送告警 。告警 Medias 配置好后,需要创建一个 Action(或修改默认的),以决定什么时候触发告警

定位到 Alerts -> Actions -> Trigger actions 。配置 Action

配置 Operations 发送消息。在 Alerts -> Actions -> Trigger actions -> Action: Operations 中配置

确定 Actions Trigger ,在 Monitoring -> Problems 中观察具体告警中的 Actions,看是否有关联,如果 Actions 为空,说明Action 根本没有被触发 ,检查 Action 配置

如果 Problems 中的事件是在创建 Action 之前产生,那么它不会自动关联到新建的 Actions。此时可以 Update problems,将其关闭(Close),使其重新产生, Action 配置正常的话,会自动关联到 Action。

Zabbix 告警默认只会发送一次,(未恢复)不会持续发送。要配置未恢复的问题间隔几分钟后再次发送。可以参考以下配置:

  1. Alerts -> Actions -> Trigger actions -> Action: Operations 中配置时间间隔
    Default operation step duration = 5m
    每一步之间间隔 5分钟
  2. 增加多个 Step

常见问题处理

Zabbix Web 中主机状态显示 Unknow

在 Zabbix Server 测试主机连通性返回 1 ,说明 网络 + agent 都是 OK 的

root@zabbix:/opt/zabbix# docker compose exec -T zabbix-server zabbix_get -s 172.17.0.1 -p 10050 -k agent.ping
1

root@zabbix:/opt/zabbix# docker compose exec -T zabbix-server zabbix_get -s 172.17.0.1 -p 10050 -k system.uptime
368373

但是 Zabbix Web 中主机状态显示 Unknow 。这种情况大概率是 Host 配置问题,重点检查:

  • 是否绑定了 Templates ,如果没有模版,就没有抓取项(Items),Zabbix Server 不会被动去抓取主机指标
  • Interfaces 配置是否正确