持久化 KubeRay 自定义资源日志#
日志(包括系统日志和应用日志)对于 Ray 应用和集群的故障排除非常有用。例如,如果节点意外终止,你可能需要访问系统日志。
与 Kubernetes 类似,Ray 不提供日志数据的原生存储解决方案。用户需要自行管理日志的生命周期。本页提供有关如何从在 Kubernetes 上运行的 Ray 集群收集日志的说明。
Ray 日志目录#
默认情况下,Ray 将日志写入每个 Ray Pod 文件系统上的 /tmp/ray/session_*/logs
目录中的文件,包括应用日志和系统日志。在开始收集日志之前,请了解更多关于日志目录和日志文件以及日志轮换配置的信息。
日志处理工具#
Kubernetes 生态系统中有许多开源日志处理工具。本页展示了如何使用 Fluent Bit 提取 Ray 日志。其他流行的工具包括 Vector、Fluentd、Filebeat 和 Promtail。
日志收集策略#
要将收集到的日志写入 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 的单 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"}
将 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 文件作为参考。