Sổ tay kỹ thuật

Pipeline syslog thiết bị mạng bằng Vector – Loki và ClickHouse

how-to#vector#syslog#loki#clickhouse· cập nhật 2026-11-06

Dùng khi cần gom syslog từ router/switch qua Vector, tách mnemonic, gắn tên thiết bị/site rồi ghi đồng thời vào Loki và ClickHouse.

Sửa trang này
Mục lục

Tóm tắt: Vector nhận syslog UDP 514 bằng source socket, chương trình VRL trong transform remap parse header syslog (parse_syslog), tách mnemonic kiểu %ROUTING-BGP-5-ADJCHANGE, rồi tra bảng CSV để gắn source (tên thiết bị) và site theo IP gửi. Kết quả đi hai đường: Loki (tìm log nhanh, label ít) và ClickHouse (lưu lâu, thống kê bằng SQL).

Luồng hoạt động

flowchart LR XR["IOS XR (UDP 514)"] --> S["Vector source socket"] JN["Junos (UDP 514)"] --> S S --> T["remap: parse_syslog + regex"] CSV["devices.csv (enrichment)"] -.-> T T --> L["sink loki"] T --> C["sink clickhouse"] L --> G[Grafana] C --> G

Cấu hình

Thiết bị gửi syslog

IOS XR:

logging trap informational
logging 198.51.100.50 vrf default
logging source-interface Loopback0

Junos (structured-data gửi theo RFC 5424):

set system syslog host 198.51.100.50 any info
set system syslog host 198.51.100.50 structured-data
set system syslog source-address 198.51.100.1

Đặt NTP đúng trên thiết bị: timestamp trong log lấy từ đồng hồ của router.

Bảng tra thiết bị

/etc/vector/devices.csv
ip,name,site
198.51.100.1,pe1-hni,hni
198.51.100.2,pe2-hcm,hcm

vector.yaml

/etc/vector/vector.yaml
api:
  enabled: true                  # cho vector top / vector tap
  address: 127.0.0.1:8686
 
enrichment_tables:
  devices:
    type: file
    file:
      path: /etc/vector/devices.csv
      encoding:
        type: csv
    schema:
      ip: string
      name: string
      site: string
 
sources:
  syslog_udp:
    type: socket
    mode: udp
    address: 0.0.0.0:514
 
transforms:
  parse:
    type: remap
    inputs: [syslog_udp]
    source: |
      .source_ip = del(.host)            # IP thiết bị gửi (peer UDP)
      del(.source_type)
      del(.port)
 
      # Header syslog RFC 3164/5424 -> hostname, appname, severity, facility, timestamp, message
      parsed, err = parse_syslog(.message)
      if err == null {
        . = merge(., parsed)
      }
 
      # Mnemonic Cisco: %ROUTING-BGP-5-ADJCHANGE
      m, err = parse_regex(.message, r'%(?P<facility>[A-Z0-9_]+(?:-[A-Z0-9_]+)*)-(?P<sev>[0-7])-(?P<mnemonic>[A-Z0-9_]+)')
      if err == null {
        .mnemonic = m.mnemonic
        .cisco_facility = m.facility
      }
 
      # Gắn tên thiết bị / site theo IP gửi
      row, err = get_enrichment_table_record("devices", {"ip": .source_ip})
      if err == null {
        .source = row.name
        .site = row.site
      } else {
        .source = string(.hostname) ?? string(.source_ip) ?? "unknown"
        .site = "unknown"
      }
      if !exists(.severity) { .severity = "unknown" }
 
sinks:
  loki:
    type: loki
    inputs: [parse]
    endpoint: http://loki.example.net:3100
    encoding:
      codec: json
    labels:                          # giữ label ít và ổn định
      job: syslog
      source: "{{ source }}"
      site: "{{ site }}"
      severity: "{{ severity }}"
 
  clickhouse:
    type: clickhouse
    inputs: [parse]
    endpoint: http://clickhouse.example.net:8123
    database: logs
    table: syslog
    skip_unknown_fields: true        # field thừa (procid, msgid...) bỏ qua
    date_time_best_effort: true      # nhận timestamp ISO 8601 của Vector
    auth:
      strategy: basic
      user: vector
      password: "${CLICKHOUSE_PASSWORD}"
    batch:
      max_events: 10000
      timeout_secs: 5

Vector thay ${VAR} bằng biến môi trường lúc nạp config, nên password để trong /etc/default/vector (gói deb/rpm đọc file này) thay vì ghi thẳng vào YAML. Syslog qua TCP thì thêm một source socket với mode: tcp.

Bảng ClickHouse

CREATE DATABASE IF NOT EXISTS logs;
 
CREATE TABLE logs.syslog
(
    timestamp      DateTime64(3, 'UTC'),
    source         LowCardinality(String),
    source_ip      String,
    site           LowCardinality(String),
    severity       LowCardinality(String),
    facility       LowCardinality(String),
    appname        LowCardinality(String),
    mnemonic       LowCardinality(String),
    message        String
)
ENGINE = MergeTree
PARTITION BY toDate(timestamp)
ORDER BY (source, timestamp)
TTL toDateTime(timestamp) + INTERVAL 180 DAY;

Giải thích ORDER BY, PARTITION BY, TTL và cách thêm bảng tổng hợp xem ClickHouse schema và TTL.

Chạy và kiểm tra

vector validate /etc/vector/vector.yaml          # kiểm config + healthcheck sink
vector validate --no-environment /etc/vector/vector.yaml   # chỉ kiểm cú pháp, không gọi Loki/ClickHouse
systemctl restart vector && journalctl -u vector -f
 
logger -n 127.0.0.1 -P 514 -d --rfc3164 -t bgp '%ROUTING-BGP-5-ADJCHANGE : neighbor 203.0.113.1 Up'
vector tap --outputs-of parse                    # xem event sau remap (cần api.enabled)
vector top                                       # throughput/lỗi từng component
vector vrl                                       # REPL để thử chương trình VRL

Truy vấn log trên Loki xem LogQL cheatsheet. Trên ClickHouse:

SELECT source, mnemonic, count() AS n
FROM logs.syslog
WHERE timestamp > now() - INTERVAL 1 HOUR AND mnemonic != ''
GROUP BY source, mnemonic ORDER BY n DESC LIMIT 20;

Lỗi thường gặp

Triệu chứng Nguyên nhân Cách xử lý
Vector không start, Permission denied khi bind :514 Port < 1024 cần quyền Thêm AmbientCapabilities=CAP_NET_BIND_SERVICE vào unit, hoặc listen 5514 rồi DNAT
tcpdump -ni any udp port 514 thấy gói nhưng không có event Firewall host chặn (nftables), sai address Mở UDP 514, ss -lunp | grep 514
Lỗi compile VRL unhandled fallible assignment Gọi hàm có thể lỗi mà không xử lý Dùng x, err = f(...), f!(...) hoặc ?? giá trị mặc định
Lỗi VRL unnecessary error coalesce Dùng ?? sau biểu thức không thể lỗi Bỏ ??, dùng if exists(...)
source luôn là IP IP gửi không có trong devices.csv (router dùng source khác Loopback) Đặt source-interface / source-address, bổ sung CSV
Loki báo entry too far behind hoặc timestamp too old Đồng hồ router lệch, Loki từ chối log quá cũ Sửa NTP; chỉnh reject_old_samples_max_age phía Loki
Loki chậm, maximum active stream limit exceeded Label động (peer, interface) Chỉ để job/source/site/severity làm label
ClickHouse Cannot parse input ... DateTime64 Định dạng timestamp Bật date_time_best_effort: true
ClickHouse Unknown field found while parsing JSONEachRow Event có field không có cột skip_unknown_fields: true hoặc thêm cột
ClickHouse TOO_MANY_PARTS Batch quá nhỏ Tăng batch.max_events / timeout_secs, xem ClickHouse xử lý sự cố

Tham khảo