如何使用 LogForward 将日志转发到外部系统

LogForward 自定义资源允许租户将平台的日志流转发到外部系统(Elasticsearch、OpenSearch、Loki、syslog、HTTP、ClickHouse、Kafka、console 或 advanced-sink 逃生机制),且独立于平台内置的存储 operator。每个 CR 都会读取平台的 Kafka 主题,并生成一个 Vector Deployment。

LogForward 独立于平台的存储流水线,且不会替代该流水线。记录仍会按常规写入存储集群的 ClickHouse 或 OpenSearch;LogForward 会生成一份额外副本。

权威 CRD 源文件位于 api/v1alpha1/logforward_types.go,所在位置为 log-storage-operator。以下示例经过精简,来源于 config/samples/logforward_*.yaml。

前提条件

  • 租户所在的存储集群已安装 Alauda Container Platform Log Storage Management,并且 PlatformLogForward/platform-default 为 Ready。请参阅安装。
  • 外部目标可从存储集群的 cpaas-system 命名空间访问。
  • spec.output.authSecretRef 或 spec.output.tls.caSecretRef 引用的任何 Secret 都位于 cpaas-system 中。Webhook 会拒绝引用其他命名空间中 Secret 的 LogForward。
Warning

LogForward 只能位于 cpaas-system 准入 Webhook validate-lf.log.alauda.io 会拒绝在 cpaas-system 之外创建的任何 LogForward。这是 Bug 205(零接触 operator 命名空间)规定的硬性约束:operator 只会在其 operand 命名空间中协调 Vector Deployments 和 Secrets,并且跨命名空间的 Secret 管理已被有意移除。平台管理员(而非租户)应负责创建每个 LogForward。

Spec 结构

每个 LogForward 都包含四个部分:

  • spec.inputs[] — CR 消费的一个或多个逻辑主题。每个输入都会选择一个 type(log、audit 或 event),用于映射到平台的 Kafka 主题;还可以选择一个可选的 selector,按 namespaces 或 podLabels 进行过滤。
  • spec.filters[] — 可选的类型化转换列表(parse、filter、redact、prune、remapFields)。不允许使用原始 VRL。
  • spec.pipelines[] — 必需列表,用于将每个流水线的 inputRefs 和可选的 filterRefs 连接到单个输出。
  • spec.output — 恰好一个 sink。type 用于选择 sink,匹配的特定类型块则包含其参数;共享的 endpoint、authSecretRef 和 tls 字段位于输出的顶层。

常见配方

将平台的日志流和审计流镜像到用户 Kafka

两个输入,不使用过滤器,每个输入一个流水线,全部汇入同一个 Kafka 镜像 sink。

apiVersion: log.alauda.io/v1alpha1
kind: LogForward
metadata:
  name: audit-mirror
  namespace: cpaas-system
spec:
  replicas: 2
  inputs:
    - name: audit-in
      type: audit
      initialOffset: Latest
    - name: log-in
      type: log
      initialOffset: Latest
      selector:
        namespaces:
          include: [default, kube-system]
  pipelines:
    - name: audit-pipe
      inputRefs: [audit-in]
    - name: log-pipe
      inputRefs: [log-in]
  output:
    type: kafka
    # Secret keys: username / password. Mounted as OUTPUT_KAFKA_USERNAME
    # and OUTPUT_KAFKA_PASSWORD, rendered into [sinks.output.sasl].
    authSecretRef:
      name: user-kafka-mirror-auth
    kafka:
      bootstrapServers: user-broker-1.example.com:9092,user-broker-2.example.com:9092
      topic: platform-mirror-logs
      saslMechanism: SCRAM-SHA-512      # PLAIN | SCRAM-SHA-256 | SCRAM-SHA-512 | ""
      compression: gzip                 # none | gzip | snappy | lz4 | zstd

将平台的日志流转发到用户 OpenSearch,并进行 prune 和 redact

apiVersion: log.alauda.io/v1alpha1
kind: LogForward
metadata:
  name: user-opensearch
  namespace: cpaas-system
spec:
  replicas: 2
  inputs:
    - name: platform-log
      type: log
      initialOffset: Latest
  filters:
    - name: strip-noise
      type: prune
      prune:
        deny: [pod_labels, node_name]
    - name: redact-token
      type: redact
      redact:
        fields: [access_token, password]
        replacement: "[REDACTED]"
  pipelines:
    - name: to-os
      inputRefs: [platform-log]
      filterRefs: [strip-noise, redact-token]
  output:
    type: opensearch                     # elasticsearch driver, OS-compatible
    endpoint: https://user-os.example.com:9200
    authSecretRef:
      name: user-os-basic-auth           # Secret keys: username / password
    tls:
      verify: true
      caSecretRef:
        name: user-os-tls-ca             # Secret key: ca.crt
    opensearch:
      index: acp-forward-%Y%m%d          # strftime template
      bulkAction: create                 # create | index | update

将审计记录转发到 HTTP intake

apiVersion: log.alauda.io/v1alpha1
kind: LogForward
metadata:
  name: compliance-audit-http
  namespace: cpaas-system
spec:
  inputs:
    - name: audit-in
      type: audit
  pipelines:
    - name: to-intake
      inputRefs: [audit-in]
  output:
    type: http
    endpoint: https://intake.example.com/logs
    authSecretRef:
      name: audit-intake-basic           # Secret keys: username / password
    http:
      method: post                       # post | put
      encodingCodec: json                # json | text
      framingMethod: newline_delimited   # newline_delimited | character_delimited
HTTP

authSecretRef 和 Authorization header 互斥 vector 的 http sink 同时配置 [sinks.output.auth] 和一个 headers.Authorization 条目时会拒绝启动 — 消息为 Authorization header can not be used with defined auth options。请选择一种方式:

  • Basic auth:设置 authSecretRef,并让 http.headers 不包含 Authorization 条目。
  • API token / JWT:不设置 authSecretRef,并自行将完整值填入 http.headers.Authorization(例如 Bearer <jwt> 或 Splunk <token>)。

Webhook 会在准入时拒绝此冲突。(Bug 210 F-1。)

将命名空间的日志流发送到 syslog 收集器

apiVersion: log.alauda.io/v1alpha1
kind: LogForward
metadata:
  name: security-workload-syslog
  namespace: cpaas-system
spec:
  inputs:
    - name: security-ns
      type: log
      selector:
        namespaces:
          include: [security]
  pipelines:
    - name: to-syslog
      inputRefs: [security-ns]
  output:
    type: syslog
    endpoint: syslog.example.com:514
    syslog:
      mode: tcp                          # tcp | udp | unix | tls
Syslog 线格式

输出使用 encoding.codec = "json",并将整个事件作为每条记录中的一个 JSON 字符串写入。operator 不会构造 RFC 5424 PRI header,也不会将字段映射到 syslog facility / severity — 接收服务器应解析 JSON 正文。这与 v4.3 的 chart-alauda-log-agent 线上协议一致;尝试生成 RFC 5424 的 pt L / M 重写在两个 vector 0.55 codec bug 静默丢弃每个事件后,于 beta.322 中被还原。

验证

应用 LogForward 后,log-storage-operator 会在 cpaas-system 中为其构建一个 Vector Deployment。监视该资源,直到其报告 Ready=True:

kubectl -n cpaas-system get logforward <name> -w

如果资源未进入 Ready 状态,请读取 conditions:

kubectl -n cpaas-system get logforward <name> \
  -o jsonpath='{range .status.conditions[*]}{.type}{"\t"}{.status}{"\t"}{.reason}{"\t"}{.message}{"\n"}{end}'

LogForward 状态经过刻意精简 — 仅包含 observedGeneration、Phase 和 readiness conditions。它不会报告消费者组、主题、堆积量、分区、offset、从节点详细信息、输出名称、endpoint 或 Secret 名称。对于运行时数据路径问题,请读取生成的 Vector Deployment 的 pod 日志。

限制和注意事项

  • 一个 LogForward 恰好生成一个 Vector Deployment。每个副本都会加入同一个 Kafka 消费者组。
  • 消费者组名称为 logforward-<lf-name>。平台的 KafkaUser 必须包含一个 logforward- 组前缀 ACL 规则;如果没有该规则,vector 会因 GroupAuthorizationFailed 静默失败,而 LF 会报告 Ready=True,同时读取不到任何记录。所需 ACL 请参阅安装第 1.1.1 节第 3 步。(Bug 168d v2。)
  • 一个 LogForward 恰好包含一个 output。如需扇出到多个目标,请为每个目标创建一个 LogForward。
  • 每个类型为 filter 的 filter 都必须在每个条件中至少设置一个非空谓词(equals、notEquals、contains 或 matches)。完全为空的条件会编译为 false,并静默中止每个事件 — webhook 会在准入时拒绝该条件。(Bug 173 pt1。)
  • 卸载 Alauda Container Platform Log Storage Management 会停止 operator;任何活动的 LogForward Deployments 都会在存储 operator 卸载过程中被移除。
  • LogForward 并非用于替代平台的存储管道。移除存储 operator 会从控制台移除日志查询功能;仅使用 LogForward 无法恢复该功能。

相关主题

  • 安装 — 如何安装发布 LogForward CRD 的存储 operator,以及如何设置 logforward- KafkaUser 前缀 ACL 规则。
  • 如何将日志归档到第三方存储 — 较旧的 razor consumer --database-type=file 归档工作流,用于 NFS / S3 归档,而非实时转发。