日志持久化#

日志(包括系统和应用程序日志)对于故障排除Ray应用程序和集群非常有用。例如,如果一个节点意外终止,您可能需要访问系统日志。

与Kubernetes类似,Ray不提供日志数据的本地存储解决方案。用户需要自行管理日志的生命周期。本页提供如何从运行在Kubernetes上的Ray集群收集日志的说明。

小技巧

跳转到 部署说明 以查看一个示例配置,展示如何从 Ray pod 中提取日志。

Ray 日志目录#

默认情况下,Ray 将日志写入每个 Ray pod 文件系统中的 /tmp/ray/session_*/logs 目录下的文件,包括应用程序和系统日志。在开始收集日志之前,请了解更多关于 日志目录和日志文件 以及 日志轮转配置 的信息。

日志处理工具#

在Kubernetes生态系统中,有许多开源的日志处理工具可用。本页展示了如何使用 [Fluent Bit][FluentBit] 提取Ray日志。其他流行的工具包括 [Vector][Vector]、[Fluentd][Fluentd]、[Filebeat][Filebeat] 和 [Promtail][Promtail]。

日志收集策略#

要将收集的日志写入Pod的文件系统,请使用以下两种日志记录策略之一:边车容器守护进程集。在 [Kubernetes 文档][KubDoc] 中阅读有关这些日志记录模式的更多信息。

边车容器#

我们在本指南中提供了一个 示例 ,展示了边车策略。您可以通过为每个 Ray pod 配置一个日志处理边车来处理日志。Ray 容器应配置为通过卷挂载与日志边车共享 /tmp/ray 目录。

你可以配置边车执行以下任一操作:

  • 将 Ray 日志流式传输到边车的标准输出。

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

Daemonset#

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

使用 Fluent Bit 设置日志边车#

在本节中,我们提供了一个如何为 Ray pod 设置日志发送 [Fluent Bit][FluentBit] sidecar 的示例。

请参阅带有日志边车的单个pod RayCluster的完整配置 [此处][ConfigLink]。现在我们将讨论此配置并展示如何部署它。

配置日志处理#

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

这是一个最小的 ConfigMap,它告诉 Fluent Bit 边车容器

  • Tail Ray 日志。

  • 将日志输出到容器的标准输出。

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
    [OUTPUT]
        Name stdout
        Match *

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

  • 除了将日志流式传输到标准输出外,您还可以使用 [OUTPUT] 子句将日志导出到 Fluent Bit 支持的任何 [存储后端][FluentBitStorage]。

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

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

将日志边车添加到 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 边车#

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

        - name: fluentbit
          image: fluent/fluent-bit:1.9.6
          # 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 卷使边车容器能够访问 Ray 的日志。fluentbit-config 卷使边车容器能够访问日志配置。

将所有内容整合在一起#

将上述所有元素结合起来,我们为具有日志处理边车的单个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
    [OUTPUT]
        Name stdout
        Match *
---
# RayCluster CR with a FluentBit sidecar
apiVersion: ray.io/v1alpha1
kind: RayCluster
metadata:
  labels:
    controller-tools.k8s.io: "1.0"
  name: raycluster-complete-logs
spec:
  rayVersion: '2.3.0'
  headGroupSpec:
    rayStartParams:
      dashboard-host: '0.0.0.0'
    template:
      spec:
        containers:
        - name: ray-head
          image: rayproject/ray:2.3.0
          lifecycle:
            preStop:
              exec:
                command: ["/bin/sh","-c","ray stop"]
          # This config is meant for demonstration purposes only.
          # Use larger Ray containers in production!
          resources:
            limits:
              cpu: "1"
              memory: "1G"
            requests:
              cpu: "1"
              memory: "1G"
          # Share logs with Fluent Bit
          volumeMounts:
          - mountPath: /tmp/ray
            name: ray-logs
        # Fluent Bit sidecar
        - name: fluentbit
          image: fluent/fluent-bit:1.9.6
          # 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

使用日志边车部署 RayCluster#

要部署上述配置,如果尚未部署 KubeRay Operator,请执行以下操作:参考 入门指南 获取此步骤的说明。

现在,运行以下命令来部署 Fluent Bit ConfigMap 和一个带有 Fluent Bit 边车的单节点 RayCluster。

kubectl apply -f https://raw.githubusercontent.com/ray-project/ray/releases/2.4.0/doc/source/cluster/kubernetes/configs/ray-cluster.log.yaml

确定 Ray pod 的名称

kubectl get pod | grep raycluster-complete-logs

检查 FluentBit 边车容器的 STDOUT 以查看 Ray 组件进程的日志。

# Substitute the name of your Ray pod.
kubectl logs raycluster-complete-logs-head-xxxxx -c fluentbit

在 DaemonSet 上使用 Fluent Bit 设置日志记录#

Fluent Bit 是一个轻量级代理,允许你从 Kubernetes 集群收集日志并发送到各种目的地,如 Elasticsearch、CloudWatch、S3 等。以下步骤将 [Fluent Bit][FluentBit] 设置为 DaemonSet 以将日志发送到 CloudWatch Logs。

AWS EKS 设置#

使用 eksctlus-west-2 区域创建一个名为 fluent-bit-demo 的 Amazon EKS 集群,如 EKS 文档 所示。

eksctl create cluster --name fluent-bit-demo --region us-west-2

查看您的集群节点:

$ kubectl get nodes -o wide
NAME                                          STATUS   ROLES    AGE    VERSION               INTERNAL-IP     EXTERNAL-IP      OS-IMAGE         KERNEL-VERSION                  CONTAINER-RUNTIME
ip-192-168-59-62.us-west-2.compute.internal   Ready    <none>   157m   v1.27.5-eks-43840fb   192.168.59.62   54.190.144.241   Amazon Linux 2   5.10.192-183.736.amzn2.x86_64   containerd://1.6.19
ip-192-168-86-99.us-west-2.compute.internal   Ready    <none>   157m   v1.27.5-eks-43840fb   192.168.86.99   34.219.16.107    Amazon Linux 2   5.10.192-183.736.amzn2.x86_64   containerd://1.6.19

EKS 集群节点需要能够访问 CloudWatch Logs 以便 Fluent Bit 使用。将 CloudWatchLogsFullAccess 策略附加到附加到集群节点的 IAM 角色上:

ROLE_NAME=$(eksctl get nodegroup --cluster fluent-bit-demo --region us-west-2 -o json | jq -r '.[].NodeInstanceRoleARN' | cut -f2 -d/)

aws iam attach-role-policy \
  --role-name $ROLE_NAME \
  --policy-arn arn:aws:iam::aws:policy/CloudWatchLogsFullAccess

部署 Fluent Bit DaemonSet#

如果你还没有一个名为 amazon-cloudwatch 的命名空间,可以通过输入以下命令来创建一个:

kubectl apply -f https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/cloudwatch-namespace.yaml

运行以下命令以创建一个名为 cluster-info 的 ConfigMap,其中包含集群名称和发送日志的区域:

ClusterName=fluent-bit-demo
RegionName=us-west-2
FluentBitHttpPort='2020'
FluentBitReadFromHead='Off'
[[ ${FluentBitReadFromHead} = 'On' ]] && FluentBitReadFromTail='Off'|| FluentBitReadFromTail='On'
[[ -z ${FluentBitHttpPort} ]] && FluentBitHttpServer='Off' || FluentBitHttpServer='On'
kubectl create configmap fluent-bit-cluster-info \
    --from-literal=cluster.name=${ClusterName} \
    --from-literal=http.server=${FluentBitHttpServer} \
    --from-literal=http.port=${FluentBitHttpPort} \
    --from-literal=read.head=${FluentBitReadFromHead} \
    --from-literal=read.tail=${FluentBitReadFromTail} \
    --from-literal=logs.region=${RegionName} -n amazon-cloudwatch

通过运行以下命令将 Fluent Bit DaemonSet 部署到集群中:

kubectl apply -f https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/fluent-bit/fluent-bit.yaml

通过输入以下命令验证您是否成功部署了 Fluent Bit:

kubectl -n amazon-cloudwatch logs ds/fluent-bit

验证命令是否创建了日志组:

...
[2023/10/10 06:13:55] [ info] [output:cloudwatch_logs:cloudwatch_logs.0] Created log group /aws/containerinsights/fluent-bit-demo/application
[2023/10/10 06:13:57] [ info] [output:cloudwatch_logs:cloudwatch_logs.2] Created log group /aws/containerinsights/fluent-bit-demo/host
[2023/10/10 06:13:57] [ info] [output:cloudwatch_logs:cloudwatch_logs.1] Created log group /aws/containerinsights/fluent-bit-demo/dataplane
...

查看 CloudWatch 仪表板#

最后,检查 CloudWatch 仪表板以查看日志。打开 CloudWatch 控制台,网址为 https://console.aws.amazon.com/cloudwatch/。

在搜索框中输入 /aws/containerinsights/fluent-bit-demo/

CloudWatch 仪表板

选择 /aws/containerinsights/fluent-bit-demo/application。你应该能看到来自包括 Ray 在内的应用程序 pod 的日志。

在日志流下,点击任意日志流。你应该能看到来自pod的日志:

CloudWatch 日志

您可以根据pod名称、命名空间等指定过滤器。了解如何在此 过滤器模式语法文档 中编写过滤器。

将 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,将工作线程日志重定向到驱动程序 的功能将无法工作。如果你需要这些功能,可以考虑使用上面提到的 Fluent Bit 解决方案。对于 VM 上的集群,不要将日志重定向到 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 文件