持久化 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 的标准输出。

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

Daemonset#

另一种方法是在 Kubernetes 节点级别收集日志。为此,可以在 Kubernetes 集群的节点上部署一个日志处理 Daemonset。使用此策略的关键是将 Ray 容器的 /tmp/ray 目录挂载到相关的 hostPath

使用 Fluent Bit 设置日志记录 Sidecar#

在本节中,我们将举例说明如何为 Ray Pod 设置发送日志到 Grafana Loki 的日志发射 Fluent Bit Sidecar,从而实现集中式日志管理和查询。

带日志记录 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 容器的标准输出中看到日志。

  • Kubernetes Downward API填充了 FILTER 部分中使用的 POD_LABELS 变量。它从 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 所在的命名空间)。

将日志记录 Sidecar 添加到 RayCluster 自定义资源 (CR)#

添加日志和配置卷#

对于我们的 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 能够访问日志记录配置。

整合所有配置#

将上述所有元素整合在一起,我们就得到了一个带日志处理 Sidecar 的单 Pod RayCluster 的 YAML 配置。

# 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.9.0'
  headGroupSpec:
    rayStartParams: {}
    template:
      spec:
        containers:
        - name: ray-head
          image: rayproject/ray:2.9.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 在本地 http://localhost:3000 可用。

  • 用户名:“admin”

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

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

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

{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 文件作为参考。