持久化 KubeRay 自定义资源日志#

日志(系统日志和应用日志)对于排查 Ray 应用和集群问题非常有帮助。例如,当节点意外终止时,您可能需要访问系统日志。

与 Kubernetes 类似,Ray 并不提供原生的日志数据存储解决方案。用户需要自行管理日志的生命周期。本文档将指导您如何在 Kubernetes 上收集正在运行的 Ray 集群的日志。

Ray 日志目录#

默认情况下,Ray 会将日志写入每个 Ray Pod 文件系统中的目录 /tmp/ray/session_*/logs,包括应用日志和系统日志。在开始收集日志之前,请详细了解 日志目录和日志文件 以及 日志轮转配置

日志处理工具#

Kubernetes 生态系统中提供了许多开源日志处理工具。本文档将展示如何使用 Fluent Bit 提取 Ray 日志。其他流行的工具包括 VectorFluentdFilebeatPromtail

日志收集策略#

要将收集到的日志写入 Pod 的文件系统,请使用以下两种日志记录策略之一:**sidecar 容器** 或 **daemonset**。请参阅 Kubernetes 文档 了解这些日志模式的更多信息。

Sidecar 容器#

本指南提供了一个关于 sidecar 策略的 示例。您可以为每个 Ray Pod 配置一个日志处理 sidecar 来处理日志。Ray 容器应配置为通过卷挂载与日志 sidecar 共享 /tmp/ray 目录。

您可以将 sidecar 配置为执行以下任一操作:

  • 将 Ray 日志流式传输到 sidecar 的 stdout。

  • 将日志导出到外部服务。

Daemonset#

或者,也可以在 Kubernetes 节点级别收集日志。为此,需要将日志处理 daemonset 部署到 Kubernetes 集群的节点上。采用此策略时,关键是将 Ray 容器的 /tmp/ray 目录挂载到相关的 hostPath

使用 Fluent Bit 设置日志 sidecar#

在本节中,我们将提供一个示例,说明如何为 Ray Pod 设置日志输出 Fluent Bit sidecar,以将日志发送到 Grafana Loki,从而实现集中的日志管理和查询。

在此处查看带有日志 sidecar 的单 Pod RayCluster 的完整配置:链接。现在,我们将讨论此配置并展示如何部署它。

部署 Loki 单体模式#

请按照 部署 Loki 单体模式 的说明,以单体模式部署 Grafana Loki。

部署 Grafana#

请按照 部署 Grafana 的说明,设置 Grafana Loki 数据源并部署 Grafana。

配置日志处理#

第一步是创建一个包含 Fluent Bit 配置的 ConfigMap。

下面的 ConfigMap 示例配置了一个 Fluent Bit sidecar,用于:

  • 跟踪 Ray 日志。

  • 将日志发送到 Grafana Loki 端点。

  • 向日志添加元数据,以便按标签进行过滤,例如 RayCluster

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentbit-config
data:
  fluent-bit.conf: |
    [INPUT]
        Name tail
        Path /tmp/ray/session_latest/logs/*
        Tag ray
        Path_Key true
        Refresh_Interval 5
    [FILTER]
        Name modify
        Match ray
        Add POD_LABELS ${POD_LABELS}
    [OUTPUT]
        Name loki
        Match *
        Host loki-gateway
        Port 80
        Labels RayCluster=${POD_LABELS}
        tenant_id test

关于上述配置的一些说明:

  • 上面的 Path_Key true 行确保文件名包含在 Fluent Bit 发出的日志记录中。

  • 上面的 Refresh_Interval 5 行指示 Fluent Bit 每 5 秒刷新一次日志目录中的文件列表,而不是默认的 60 秒。原因是目录 /tmp/ray/session_latest/logs/ 最初不存在(Ray 必须先创建它)。将 Refresh_Interval 设置得低一些,可以让我们更快地在 Fluent Bit 容器的 stdout 中看到日志。

  • Kubernetes वापरा Downward APIPOD_LABELS 变量填充到 FILTER 部分。它从 Pod 的元数据标签 ray.io/cluster 中拉取标签,该标签在 Fluent Bit sidecar 容器的环境中定义。

  • 字段 tenant_id 允许您将日志分配给不同的租户。在此示例中,Fluent Bit sidecar 将日志发送到 test 租户。您可以调整此配置以匹配您的 Grafana Loki 实例中设置的租户 ID,从而启用 Grafana 中的多租户支持。

  • 字段 Host 指定 Loki 网关的端点。如果 Loki 和 RayCluster 位于不同的命名空间中,则需要将 .namespace 追加到主机名,例如 loki-gateway.monitoring(将 monitoring 替换为 Loki 所在的命名空间)。

向 RayCluster 自定义资源 (CR) 添加日志 sidecar#

添加日志和配置卷#

对于 RayCluster CR 中的每个 Pod 模板,我们需要添加两个卷:一个用于 Ray 日志,另一个用于存储上面应用的 ConfigMap 中的 Fluent Bit 配置。

        volumes:
        - name: ray-logs
          emptyDir: {}
        - name: fluentbit-config
          configMap:
            name: fluentbit-config

挂载 Ray 日志目录#

将以下卷挂载添加到 Ray 容器的配置中。

          volumeMounts:
          - mountPath: /tmp/ray
            name: ray-logs

添加 Fluent Bit sidecar#

最后,将 Fluent Bit sidecar 容器添加到 RayCluster CR 中每个 Ray Pod 的配置中。

        - name: fluentbit
          image: fluent/fluent-bit:3.2.2
          # Get Kubernetes metadata via downward API
          env:
          - name: POD_LABELS
            valueFrom:
              fieldRef:
                fieldPath: metadata.labels['ray.io/cluster']
          # These resource requests for Fluent Bit should be sufficient in production.
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 100m
              memory: 128Mi
          volumeMounts:
          - mountPath: /tmp/ray
            name: ray-logs
          - mountPath: /fluent-bit/etc/fluent-bit.conf
            subPath: fluent-bit.conf
            name: fluentbit-config

挂载 ray-logs 卷可使 sidecar 容器访问 Ray 的日志。而fluentbit-config卷使 sidecar 能够访问日志配置。

整合所有内容#

将以上所有元素整合在一起,我们得到以下 YAML 配置,适用于带有日志处理 sidecar 的单 Pod RayCluster。

# Fluent Bit ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentbit-config
data:
  fluent-bit.conf: |
    [INPUT]
        Name tail
        Path /tmp/ray/session_latest/logs/*
        Tag ray
        Path_Key true
        Refresh_Interval 5
    [FILTER]
        Name modify
        Match ray
        Add POD_LABELS ${POD_LABELS}
    [OUTPUT]
        Name loki
        Match *
        Host loki-gateway
        Port 80
        Labels RayCluster=${POD_LABELS}
        tenant_id test
---
# RayCluster CR with a FluentBit sidecar
apiVersion: ray.io/v1
kind: RayCluster
metadata:
  name: raycluster-fluentbit-sidecar-logs
spec:
  rayVersion: '2.46.0'
  headGroupSpec:
    template:
      spec:
        containers:
        - name: ray-head
          image: rayproject/ray:2.46.0
          # This config is meant for demonstration purposes only.
          # Use larger Ray containers in production!
          resources:
            limits:
              cpu: 1
              memory: 2Gi
            requests:
              cpu: 500m
              memory: 1Gi
          # Share logs with Fluent Bit
          volumeMounts:
          - mountPath: /tmp/ray
            name: ray-logs
        # Fluent Bit sidecar
        - name: fluentbit
          image: fluent/fluent-bit:3.2.2
          # Get Kubernetes metadata via downward API
          env:
          - name: POD_LABELS
            valueFrom:
              fieldRef:
                fieldPath: metadata.labels['ray.io/cluster']
          # These resource requests for Fluent Bit should be sufficient in production.
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 100m
              memory: 128Mi
          volumeMounts:
          - mountPath: /tmp/ray
            name: ray-logs
          - mountPath: /fluent-bit/etc/fluent-bit.conf
            subPath: fluent-bit.conf
            name: fluentbit-config
        # Log and config volumes
        volumes:
        - name: ray-logs
          emptyDir: {}
        - name: fluentbit-config
          configMap:
            name: fluentbit-config

部署带有日志 sidecar 的 RayCluster#

要部署上述配置,请先安装 KubeRay Operator(如果尚未安装):请参考 入门指南 获取相关说明。

现在,运行以下命令来部署 Fluent Bit ConfigMap 和带有 Fluent Bit sidecar 的单 Pod RayCluster。

kubectl apply -f https://raw.githubusercontent.com/ray-project/kuberay/refs/heads/master/ray-operator/config/samples/ray-cluster.fluentbit.yaml

要从本地机器访问 Grafana,请通过运行以下命令设置端口转发:

export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=grafana" -o jsonpath="{.items[0].metadata.name}")
kubectl --namespace default port-forward $POD_NAME 3000

此命令使 Grafana 在本地可用,地址为 https://:3000

  • 用户名:“admin”

  • 密码:使用以下命令获取密码

kubectl get secret --namespace default grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo

最后,使用 LogQL 查询来查看特定 RayCluster 或 RayJob 的日志,并按照本示例中 FluentBit ConfigMap OUTPUT 配置中所设置的 RayCluster 进行过滤。

{RayCluster="raycluster-fluentbit-sidecar-logs"}

Loki Logs

将 Ray 日志重定向到 stderr#

默认情况下,Ray 会将日志写入 /tmp/ray/session_*/logs 目录中的文件。如果您的日志处理工具能够捕获写入 stderr 的日志记录,您可以通过在所有 Ray 节点上设置环境变量 RAY_LOG_TO_STDERR=1 将 Ray 日志重定向到 Ray 容器的 stderr 流。

警告:不推荐此做法。

如果设置了 RAY_LOG_TO_STDERR=1,Ray 将不会将日志写入文件。因此,此行为可能导致一些依赖日志文件的 Ray 功能出现故障。例如,如果您将 Ray 日志重定向到 stderr,则 将 worker 日志重定向到 driver 将无法正常工作。如果您需要这些功能,请考虑使用上面提到的 Fluent Bit 解决方案。对于虚拟机上的集群,请勿将日志重定向到 stderr。而是遵循 此指南 来持久化日志。

将日志重定向到 stderr 还会为每个日志记录消息添加一个 ({component}) 前缀,例如 (raylet)

[2022-01-24 19:42:02,978 I 1829336 1829336] (gcs_server) grpc_server.cc:103: GcsServer server started, listening on port 50009.
[2022-01-24 19:42:06,696 I 1829415 1829415] (raylet) grpc_server.cc:103: ObjectManager server started, listening on port 40545.
2022-01-24 19:42:05,087 INFO (dashboard) dashboard.py:95 -- Setup static dir for dashboard: /mnt/data/workspace/ray/python/ray/dashboard/client/build
2022-01-24 19:42:07,500 INFO (dashboard_agent) agent.py:105 -- Dashboard agent grpc address: 0.0.0.0:49228

这些前缀允许您按感兴趣的组件过滤日志的 stderr 流。但请注意,多行日志记录在每一行的开头 **不会** 有此组件标记。

请按照以下步骤在所有 Ray 节点上设置环境变量 RAY_LOG_TO_STDERR=1

使用 CLI 显式启动集群

env RAY_LOG_TO_STDERR=1 ray start

使用 ray.init 隐式启动集群

os.environ["RAY_LOG_TO_STDERR"] = "1"
ray.init()

在每个 Ray Pod 的 Ray 容器中,将 RAY_LOG_TO_STDERR 环境变量设置为 1。使用此 示例 YAML 文件 作为参考。