1. 为什么我们需要一个高可用的日志平台?

想象一下,你负责维护一个核心的业务系统,半夜突然收到告警,服务挂了。你火急火燎地登录服务器,第一反应就是看日志,结果发现日志服务器自己也宕机了,一片漆黑,什么线索都没有。那种感觉,就像在黑夜里找掉在地上的钥匙,既无助又抓狂。

这就是单点故障的可怕之处。在今天的云原生和微服务架构下,系统组件繁多,日志就是我们的“黑匣子”,是排查问题的生命线。如果这个“黑匣子”本身不可靠,那整个系统的可观测性就无从谈起。所以,构建一个高可用的日志平台,不再是“锦上添花”,而是“雪中送炭”的必需品。

Graylog 作为一个优秀的开源日志管理平台,本身支持分布式架构,这为我们构建高可用方案提供了坚实的基础。一个完整的高可用 Graylog 集群,通常包含几个核心部分:首先是存储层,也就是 Elasticsearch 集群,它负责海量日志的索引和搜索,必须通过集群保证数据不丢失;其次是配置和元数据存储,我们用 MongoDB 副本集来承担,确保 Graylog 服务器的配置信息安全可靠;然后是 Graylog Server 节点本身,我们需要部署多个,让它们协同工作;最后,还需要一个统一的“大门”,也就是负载均衡器(比如 Nginx),把外部的日志摄入请求和用户的管理查询请求,合理地分发到后端的多个 Graylog Server 上。

这么一套组合拳下来,任何一个节点宕机,其他节点都能立刻顶上,服务不中断,数据不丢失。下面,我就结合自己多次搭建和运维的经验,带你从零开始,手把手构建这样一个坚如磐石的企业级 Graylog 高可用集群。我会把重点放在实战操作和那些容易踩坑的细节上,让你不仅能搭起来,更能理解为什么这么搭。

2. 基石:搭建 Elasticsearch 与 MongoDB 集群

高可用大厦的地基,就是后端存储集群。这一步没做稳,后面的 Graylog 配置得再花哨也是空中楼阁。

2.1 Elasticsearch 集群搭建:数据可靠性的保障

Elasticsearch (ES) 是 Graylog 的日志存储和搜索引擎。生产环境绝对不能用单节点。一个最基本的 ES 高可用集群至少需要 3 个节点,以防止“脑裂”问题。我习惯用 elasticsearch-7.x 版本,与 Graylog 的兼容性比较成熟稳定。

首先,在每台服务器上安装 ES。这里我直接用 yum 安装,比较省事。你需要先导入 Elastic 的 GPG 密钥和仓库配置。

# 导入 GPG 密钥
rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch

# 创建仓库文件
cat > /etc/yum.repos.d/elasticsearch.repo << EOF
[elasticsearch-7.x]
name=Elasticsearch repository for 7.x packages
baseurl=https://artifacts.elastic.co/packages/7.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
EOF

# 安装 Elasticsearch
yum install -y elasticsearch-7.17.9

安装完成后,关键来了——配置 elasticsearch.yml。下面是一个三节点集群的配置示例(以 node-1 为例):

# /etc/elasticsearch/elasticsearch.yml (node-1)
cluster.name: graylog-cluster # 集群名称,所有节点必须一致
node.name: es-node-1 # 节点名称,每个节点要唯一
path.data: /var/lib/elasticsearch # 数据目录,确保空间充足
path.logs: /var/log/elasticsearch
network.host: 0.0.0.0 # 绑定所有网络接口,生产环境建议绑定内网IP
http.port: 9200
discovery.seed_hosts: ["192.168.1.101", "192.168.1.102", "192.168.1.103"] # 所有节点IP
cluster.initial_master_nodes: ["es-node-1", "es-node-2", "es-node-3"] # 初始主节点候选者
bootstrap.memory_lock: true # 锁住内存,提高性能

discovery.seed_hosts 和 cluster.initial_master_nodes 这两个参数是集群组建的关键。前者让节点彼此发现,后者在集群首次启动时确定主节点。配置好后,依次启动三台机器的 ES 服务:systemctl start elasticsearch && systemctl enable elasticsearch。用 curl http://localhost:9200/_cluster/health?pretty 检查集群状态,看到 "status" : "green" 并且 "number_of_nodes" : 3,就说明集群健康运行了。

这里有个我踩过的坑:防火墙和 SELinux。务必确保所有 ES 节点之间的 9300(集群通信端口)和 9200(HTTP API端口)是互通的。可以用 firewall-cmd --permanent --add-port={9200,9300}/tcp 放行端口,或者直接在内网环境临时关闭防火墙进行测试。SELinux 也可能会阻止进程绑定端口,如果遇到启动失败,可以尝试 setenforce 0 设置为宽容模式排查问题。

2.2 MongoDB 副本集搭建:配置信息的安全屋

Graylog 的配置、用户数据、报警定义等都存在 MongoDB 里。我们用副本集(Replica Set)来保证这些元数据的高可用。同样,至少需要3个节点。

安装 MongoDB(以 4.4 版本为例):

# 配置 MongoDB yum 源
cat > /etc/yum.repos.d/mongodb-org-4.4.repo << EOF
[mongodb-org-4.4]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/\$releasever/mongodb-org/4.4/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.4.asc
EOF

yum install -y mongodb-org

配置副本集,每个节点的 /etc/mongod.conf 核心部分如下:

# /etc/mongod.conf
storage:
  dbPath: /var/lib/mongo
  journal:
    enabled: true
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log
net:
  port: 27017
  bindIp: 0.0.0.0 # 同样,生产环境建议绑定内网IP
replication:
  replSetName: graylog-rs # 副本集名称,所有节点必须一致

启动所有节点的 MongoDB 服务后,连接到其中任意一个节点进行副本集初始化:

// 在 mongo shell 中执行
rs.initiate( {
   _id : "graylog-rs",
   members: [
      { _id: 0, host: "192.168.1.201:27017" },
      { _id: 1, host: "192.168.1.202:27017" },
      { _id: 2, host: "192.168.1.203:27017" }
   ]
})

执行 rs.status() 查看状态,确保其中一个节点显示为 "PRIMARY",其余为 "SECONDARY"。副本集就搭建完成了。这里有个小技巧,你可以在配置 Graylog 之前,先在 MongoDB 里创建一个专用的数据库和用户,这样更安全。不过 Graylog 也支持在连接字符串中自动创建,我们后面配置时会用到。

3. Graylog Server 集群:双机热备与配置详解

存储层搞定后,我们就可以部署 Graylog Server 本身了。高可用至少需要两个 Graylog Server 节点,一个主(Master),一个从(Slave),它们共享同一个 MongoDB 和 Elasticsearch 集群。

3.1 使用 Ansible 实现自动化部署

手动在一台台机器上安装配置效率太低,也容易出错。我强烈推荐使用 Ansible 进行剧本化部署。这不仅是一次性工作,更是为未来的运维、升级、扩容打下基础。下面我分享一个核心的 Playbook 示例。

假设我们有两台服务器:graylog-01(主)和 graylog-02(从)。首先,准备 Graylog 的 RPM 安装包。你可以从 Graylog 官网下载,我通常下载到本地,然后用 Ansible 的 copy 模块分发到目标机器。

# graylog_deploy.yml
- hosts: graylog_servers # 在 Ansible inventory 中定义的主机组
  become: yes
  vars:
    graylog_version: "4.3.6"
    master_node: "graylog-01"
  tasks:
  - name: 分发 Graylog Server RPM 包
    copy:
      src: "/local/path/graylog-server-{{ graylog_version }}.rpm"
      dest: "/tmp/graylog-server.rpm"

  - name: 安装 Graylog Server
    yum:
      name: "/tmp/graylog-server.rpm"
      state: present

  - name: 生成 password_secret (每个节点唯一)
    shell: |
      pwgen -N 1 -s 96
    register: password_secret
    delegate_to: localhost # 在控制机生成,然后传递给各个节点
    run_once: yes

  - name: 设置 root_password_sha2 (集群统一)
    set_fact:
      root_password_sha2: "{{ 'YourAdminPassword123!' | password_hash('sha256') }}"

这个 Playbook 做了几件事:分发安装包、安装软件、生成加密密钥。注意 password_secret 是每个 Graylog 节点用于加密通信和数据的密钥,必须唯一。而 root_password_sha2 是 Web 登录密码的 SHA256 哈希值,所有节点应该一致,这样你登录任何一个节点的 Web 界面密码都一样。

3.2 关键配置文件 server.conf 的差异化配置

安装完成后,最核心的就是 /etc/graylog/server/server.conf 文件。主节点和从节点的配置大部分相同,但有几处关键区别。我通过 Ansible 的模板功能来动态生成。

创建一个 Jinja2 模板文件 server.conf.j2:

# server.conf.j2 模板
is_master = {{ (inventory_hostname == master_node) | ternary('true', 'false') }}
node_id_file = /etc/graylog/server/node-id
password_secret = {{ password_secret.stdout }}
root_password_sha2 = {{ root_password_sha2 }}
root_username = admin
root_timezone = Asia/Shanghai
http_bind_address = {{ ansible_default_ipv4.address }}:9000
# 发布 URI,这是外部或负载均衡器访问本节点的地址
http_publish_uri = http://{{ ansible_default_ipv4.address }}:9000/
# Elasticsearch 集群连接
elasticsearch_hosts = http://192.168.1.101:9200,http://192.168.1.102:9200,http://192.168.1.103:9200
# MongoDB 副本集连接
mongodb_uri = mongodb://192.168.1.201:27017,192.168.1.202:27017,192.168.1.203:27017/graylog?replicaSet=graylog-rs

在 Playbook 中增加模板渲染任务:

  - name: 配置 Graylog Server
    template:
      src: server.conf.j2
      dest: /etc/graylog/server/server.conf
    notify: restart graylog-server

看第一行 is_master,我用了 Ansible 的条件判断 (inventory_hostname == master_node)。只有当当前执行的主机名是我们在变量里定义的 master_node(即 graylog-01)时,这个值才是 true,对于 graylog-02 自动变为 false。这样就完美实现了主从节点的自动区分。

另外,http_bind_address 我绑定了服务器的内网 IP,而 http_publish_uri 也使用了这个 IP。这里有个非常重要的点:http_publish_uri 是 Graylog 节点向集群内外宣告自己的地址,负载均衡器(Nginx)和后端 Sidecar 采集器都会用这个地址来连接。所以它必须是一个能被其他组件访问到的地址,不能是 127.0.0.1。

3.3 启动与验证

配置完成后,启动服务并设置开机自启:

systemctl daemon-reload
systemctl start graylog-server
systemctl enable graylog-server

查看日志 journalctl -u graylog-server -f,确保没有报错。分别访问两个节点的 http://<节点IP>:9000,应该都能看到 Graylog 的 Web 登录界面。用 admin 和你设置的密码登录。

登录后,进入 System -> Nodes 页面,你应该能看到两个节点,其中一个的 Is Master 列显示为 Yes(主节点),另一个为 No(从节点)。这证明你的 Graylog Server 集群已经成功组建,它们共享着 MongoDB 里的配置,并共同向 Elasticsearch 集群写入和查询数据。

4. 接入层:Nginx 负载均衡与反向代理配置

现在我们有了一主一从两个 Graylog Server,但对外暴露两个入口显然不合适。我们需要一个统一的入口,这就是 Nginx 的作用。它主要干两件事:一是作为反向代理,将用户访问 Web 界面的请求负载均衡到后端 Graylog 节点;二是作为负载均衡器,接收来自各种采集器(如 Sidecar、Syslog)的日志数据流,并分发给后端节点。

4.1 配置 Upstream 与 Web 界面代理

首先,我们定义一个 upstream 块,把两个 Graylog Server 节点加进去。我通常使用 least_conn(最少连接)算法,这样可以将新的请求发给当前连接数最少的节点,负载更均衡。

# 在 nginx.conf 的 http 块内
upstream graylog_web_servers {
    least_conn;
    server 192.168.1.11:9000; # graylog-01
    server 192.168.1.12:9000; # graylog-02
    # 可以设置权重、健康检查等参数
    # server 192.168.1.11:9000 weight=3;
    # server 192.168.1.12:9000 max_fails=3 fail_timeout=30s;
}

然后,配置一个 server 块来代理 Web 请求:

server {
    listen 80;
    # 如果你有域名,可以配置 server_name,例如 graylog.yourcompany.com
    server_name _;

    location / {
        proxy_pass http://graylog_web_servers;
        # 以下几行 proxy_set_header 至关重要,它们将客户端的真实信息传递给 Graylog
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        # 设置较长的超时时间,因为日志查询可能耗时
        proxy_read_timeout 300s;
        proxy_connect_timeout 75s;
    }

    # 可选:静态资源缓存,提升访问速度
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        proxy_pass http://graylog_web_servers;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

配置好后,执行 nginx -t 测试配置,然后 nginx -s reload 重载。现在,用户只需要访问 Nginx 的 IP 或域名,就可以无缝使用 Graylog Web 界面了。即使其中一个 Graylog 节点宕机,Nginx 会自动将请求转发到健康的节点,用户无感知。

4.2 配置 GELF/UDP/TCP 输入负载均衡

除了 Web 界面,Graylog 更重要的功能是接收日志。很多采集器(如 Sidecar 的 Filebeat、NXLog,或网络设备的 Syslog)会通过 GELF、UDP、TCP 等协议将日志发送到 Graylog。我们需要让 Nginx 也能对这些输入进行负载均衡。

但是请注意:Nginx 默认的 stream 模块(用于四层 TCP/UDP 代理)和 http 模块(七层代理)是分开的。对于 Graylog 的 GELF HTTP 输入(端口通常为 12201),我们可以用上面的 http 代理块类似处理,只需新增一个 location 或新的 upstream。

但对于 Syslog UDP/TCP 或 Raw/Plaintext TCP 输入,就需要用到 stream 模块。确保你的 Nginx 编译时包含了 --with-stream 模块。配置如下:

# 在 nginx.conf 的 main 层级,与 http 块并列
stream {
    upstream graylog_syslog_udp {
        server 192.168.1.11:1514; # Graylog 节点1的 Syslog UDP 输入端口
        server 192.168.1.12:1514; # Graylog 节点2的 Syslog UDP 输入端口
    }
    server {
        listen 514 udp; # Nginx 监听 UDP 514 端口(标准 Syslog 端口)
        proxy_pass graylog_syslog_udp;
        # UDP 代理需要指定缓冲区
        proxy_buffer_size 16k;
        proxy_responses 0; # 对于 UDP,通常设为 0,因为是无连接的
    }

    upstream graylog_gelf_tcp {
        server 192.168.1.11:12201;
        server 192.168.1.12:12201;
    }
    server {
        listen 12201; # 默认监听 TCP
        proxy_pass graylog_gelf_tcp;
        proxy_connect_timeout 10s;
        proxy_timeout 30m; # 长连接超时时间
    }
}

这样配置后,所有采集器只需要将日志发送到 Nginx 服务器的对应端口(如 your-nginx-ip:514 或 your-nginx-ip:12201),Nginx 就会把日志流负载均衡到后端的 Graylog 集群。这极大地提高了日志摄入的吞吐量和可用性。

5. Graylog Sidecar 的自动化部署与管理

Graylog Sidecar 是一个轻量级的日志采集器管理器,它本身不采集日志,而是去管理你服务器上真正的采集器,比如 Filebeat、Winlogbeat 等。它通过 API 从 Graylog Server 获取采集配置,然后应用到对应的 Beats 上,实现了配置的集中管理和下发。

5.1 获取 API Token 与 Sidecar 安装

部署 Sidecar 前,必须先从 Graylog Web 界面获取一个 API Token。登录 Graylog,进入 System -> Sidecars,在 Configuration 选项卡里,点击 Create a new token,给它起个名字(比如 prod-sidecar-token)。这个 Token 是 Sidecar 与 Graylog Server 通信的凭证,务必妥善保存。

Sidecar 的安装也可以通过 Ansible 轻松完成。首先,从 GitHub 下载 Sidecar 的 RPM 包。然后在 Playbook 中增加针对 Sidecar 主机组的任务:

- hosts: sidecar_agents # 所有需要部署采集器的服务器
  become: yes
  vars:
    graylog_server_url: "http://your-nginx-ip:80/api" # 注意是 Nginx 的地址和 API 路径
    sidecar_api_token: "你的长串API Token"
  tasks:
  - name: 分发 Sidecar RPM 包
    copy:
      src: "/local/path/graylog-sidecar.rpm"
      dest: "/tmp/graylog-sidecar.rpm"

  - name: 安装 Sidecar
    yum:
      name: "/tmp/graylog-sidecar.rpm"
      state: present

  - name: 配置 Sidecar
    template:
      src: sidecar.yml.j2
      dest: /etc/graylog/sidecar/sidecar.yml
    notify: restart graylog-sidecar

sidecar.yml.j2 模板内容如下:

server_url: {{ graylog_server_url }}
server_api_token: {{ sidecar_api_token }}
node_name: {{ inventory_hostname }} # 使用 Ansible 主机名作为节点名
update_interval: 30
tls_skip_verify: true # 如果使用自签名证书,设为 true。生产环境建议配置正式证书。
tags: ["linux", "prod"] # 给这台 Sidecar 打上标签,方便在 Graylog 里分组管理

这里的关键是 server_url,它必须指向我们前面配置的 Nginx 的地址和 API 路径(/api)。这样,所有 Sidecar 都通过统一的负载均衡入口与 Graylog 集群通信,避免了单点故障。

5.2 配置管理与故障转移实践

Sidecar 启动并注册后,你会在 Graylog 的 System -> Sidecars 页面看到它。接下来,你可以在 Administration -> Sidecar Configuration 中创建配置。比如,创建一个 “Nginx Access Log” 配置,指定使用 Filebeat 来采集 /var/log/nginx/access.log,并将日志输出到 Graylog 的 GELF HTTP 输入。

将这个配置分配给带有 linux 和 prod 标签的 Sidecar 组。几分钟内,对应的服务器上的 Filebeat 就会自动被 Sidecar 配置好并启动,开始采集日志。

高可用在这里的体现是:如果某个 Graylog Server 节点宕机,Sidecar 通过 Nginx 访问 API 的请求会自动被导向另一个健康的节点,配置拉取和状态上报不会中断。同时,Sidecar 管理的 Filebeat 是将日志发送到 Nginx 的负载均衡端口,同样不受后端单个 Graylog 节点故障的影响。整个日志采集链路都是高可用的。

6. 故障转移与日常维护要点

集群搭起来只是第一步,更重要的是知道它怎么“活”的,以及出了问题怎么“治”。

6.1 理解故障转移流程

  1. Graylog Server 主节点宕机:当 is_master=true 的节点宕机后,另一个 is_master=false 的节点不会自动切换为主节点。Graylog 的 Master 节点主要负责一些后台管理任务(如周期性的索引滚动、警报检查触发等)。Web 界面和日志摄入功能,两个节点都能独立处理。所以,即使主节点挂了,只要 Nginx 将请求代理到了从节点,基本功能不受影响。你可以手动修改从节点的 server.conf,将 is_master 改为 true 并重启,来恢复完整的后台管理功能。
  2. Elasticsearch 节点宕机:只要集群中存活节点数大于等于2(对于3节点集群),ES 集群仍可读写。分片和副本机制会保证数据可用性。Graylog 会尝试重连,期间可能会有短暂的写入延迟或错误日志。
  3. MongoDB 副本集节点宕机:如果是从节点宕机,不影响使用。如果是主节点宕机,副本集会自动选举新的主节点,这个过程通常几秒钟。Graylog Server 配置了完整的副本集连接字符串,驱动会自动处理重连和主节点发现。
  4. Nginx 宕机:这是单点故障!所有日志摄入和 Web 访问都会中断。解决方案:对 Nginx 本身做高可用,例如使用 Keepalived 实现 VIP 漂移,或者使用云厂商的负载均衡器服务。

6.2 监控与日常检查清单

  • Graylog 节点监控:定期查看 System -> Nodes 页面,确认所有节点状态为 Alive。关注 Process buffer 和 Output buffer 的使用率,持续过高可能意味着处理瓶颈。
  • Elasticsearch 健康度:在 Graylog 的 System -> Elasticsearch 页面查看集群状态,确保是 Green。也可以直接调用 ES API:curl -XGET 'http://es-node:9200/_cluster/health?pretty'。
  • MongoDB 副本集状态:定期连接 MongoDB Shell 执行 rs.status(),查看成员状态和选举信息。
  • Sidecar 状态:在 System -> Sidecars 页面,确保所有 Sidecar 状态为 Running,最后汇报时间在几分钟内。
  • 系统资源监控:监控 Graylog、ES、MongoDB 节点的 CPU、内存、磁盘 I/O 和磁盘空间。Elasticsearch 尤其吃内存和磁盘。

6.3 性能调优与扩容建议

  • Elasticsearch 索引策略:Graylog 默认按天创建索引。对于日志量大的环境,可以调整为按小时,或者根据日志量设置 elasticsearch_max_docs_per_index(单索引最大文档数)和 elasticsearch_max_number_of_indices(最大索引数),配合合理的 retention(保留)策略,防止磁盘被撑爆。
  • JVM 堆内存:Graylog 和 Elasticsearch 都是 Java 应用,需要合理配置 JVM 堆内存。一般建议设置为系统物理内存的 50%,但不要超过 32GB(超过后 JVM 指针压缩失效,反而降低性能)。在 /etc/graylog/server/server.conf 中配置 -Xms4g -Xmx4g。
  • 扩容:当处理能力不足时,水平扩容是最佳选择。可以轻松地增加 Graylog Server 节点,只需安装软件、配置 server.conf(指定正确的 ES 和 MongoDB 地址,并设置 is_master=false),然后将其 IP 添加到 Nginx 的 upstream 中即可。Elasticsearch 和 MongoDB 集群也可以按需增加节点。

搭建和维护这样一个高可用的 Graylog 集群,初期确实需要一些投入,但一旦稳定运行,它带来的运维效率提升和故障排查能力是巨大的。你会发现自己再也不用担心日志服务器挂掉,可以从容地面对海量日志,快速定位线上问题。这套架构在我们生产环境已经平稳运行了两年多,期间经历了多次服务器硬件故障和业务流量洪峰,都很好地扛了下来,希望这份实战经验也能帮你构建出属于自己的可靠日志中枢。

更多推荐