使用 Stackdriver 生成日志

    为了接收日志,你必须将 Stackdriver 日志代理部署到集群中的每个节点。 此代理是一个已配置的 fluentd,其配置存在一个 ConfigMap 中,且实例使用 Kubernetes 的 DaemonSet 进行管理。 ConfigMapDaemonSet 的实际部署,取决你的集群设置。

    Google Kubernetes Engine

    对于部署在 Google Kubernetes Engine 上的集群,Stackdriver 是默认的日志解决方案。 Stackdriver 日志机制会默认部署到你的新集群上,除非你明确地不选择。

    其他平台

    为了将 Stackdriver 日志机制部署到你正在使用 kube-up.sh 创建的集群上,执行如下操作:

    1. 设置环境变量 KUBE_LOGGING_DESTINATIONgcp
    2. 如果不是跑在 GCE 上,在 KUBE_NODE_LABELS 变量中包含 beta.kubernetes.io/fluentd-ds-ready=true

    集群启动后,每个节点都应该运行 Stackdriver 日志代理。 DaemonSetConfigMap 作为附加组件进行配置。 如果你不是使用 kube-up.sh,可以考虑不使用预先配置的日志方案启动集群,然后部署 Stackdriver 日志代理到正在运行的集群。

    部署到一个已知集群

    1. 在每个节点上打标签(如果尚未存在)

      Stackdriver 日志代理部署使用节点标签来确定应该将其分配到给哪些节点。 引入这些标签是为了区分 Kubernetes 1.6 或更高版本的节点。 如果集群是在配置了 Stackdriver 日志机制的情况下创建的,并且节点的版本为 1.5.X 或更低版本,则它将使用 fluentd 用作静态容器。 节点最多只能有一个 fluentd 实例,因此只能将标签打在未分配过 fluentd pod 的节点上。 你可以通过运行 kubectl describe 来确保你的节点被正确标记,如下所示:

      输出应类似于如下内容:

      1. Name: NODE_NAME
      2. Role:
      3. Labels: beta.kubernetes.io/fluentd-ds-ready=true
      4. ...

      确保输出内容包含 beta.kubernetes.io/fluentd-ds-ready=true 标签。 如果不存在,则可以使用 kubectl label 命令添加,如下所示:

      1. kubectl label node $NODE_NAME beta.kubernetes.io/fluentd-ds-ready=true
    2. 通过运行以下命令,部署一个带有日志代理配置的 ConfigMap

      1. kubectl apply -f https://k8s.io/examples/debug/fluentd-gcp-configmap.yaml

      该命令在 default 命名空间中创建 ConfigMap。你可以在创建 ConfigMap 对象之前手动下载文件并进行更改。

    3. 通过运行以下命令,部署日志代理的 DaemonSet

      1. kubectl apply -f https://k8s.io/examples/debug/fluentd-gcp-ds.yaml

      你也可以在使用前下载和编辑此文件。

    部署 Stackdriver DaemonSet 之后,你可以通过运行以下命令来查看日志代理的部署状态:

      1. NAMESPACE NAME DESIRED CURRENT READY NODE-SELECTOR AGE
      2. ...
      3. default fluentd-gcp-v2.0 3 3 3 beta.kubernetes.io/fluentd-ds-ready=true 5m
      4. ...

      要了解使用 Stackdriver 进行日志记录的工作方式,请考虑以下具有日志生成的 pod 定义 :

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: counter
      5. spec:
      6. containers:
      7. - name: count
      8. image: busybox
      9. args: [/bin/sh, -c,
      10. 'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

      这个 pod 定义里有一个容器,该容器运行一个 bash 脚本,脚本每秒写一次计数器的值和日期时间,并无限期地运行。 让我们在默认命名空间中创建此 pod。

      你可以观察到正在运行的 pod:

      1. kubectl get pods
      1. NAME READY STATUS RESTARTS AGE
      2. counter 1/1 Running 0 5m

      在短时间内,你可以观察到 “pending” 的 pod 的状态,因为 kubelet 必须先下载容器镜像。 当 pod 状态变为 Running 时,你可以使用 kubectl logs 命令查看此 counter pod 的输出。

      1. kubectl logs counter
      1. 0: Mon Jan 1 00:00:00 UTC 2001
      2. 1: Mon Jan 1 00:00:01 UTC 2001
      3. 2: Mon Jan 1 00:00:02 UTC 2001
      4. ...

      正如日志概览所述,此命令从容器日志文件中获取日志项。 如果该容器被 Kubernetes 杀死然后重新启动,你仍然可以访问前一个容器的日志。 但是,如果将 Pod 从节点中驱逐,则日志文件会丢失。让我们通过删除当前运行的 counter 容器来演示这一点:

      1. kubectl delete pod counter
      1. pod "counter" deleted

      然后重建它:

      1. kubectl create -f https://k8s.io/examples/debug/counter-pod.yaml

      一段时间后,你可以再次从 counter pod 访问日志:

        1. 0: Mon Jan 1 00:01:00 UTC 2001
        2. 1: Mon Jan 1 00:01:01 UTC 2001
        3. 2: Mon Jan 1 00:01:02 UTC 2001
        4. ...

        如预期的那样,日志中仅出现最近的日志记录。 但是,对于实际应用程序,你可能希望能够访问所有容器的日志,特别是出于调试的目的。 这就是先前启用的 Stackdriver 日志机制可以提供帮助的地方。

        Stackdriver 日志代理为每个日志项关联元数据,供你在后续的查询中只选择感兴趣的消息: 例如,来自某个特定 Pod 的消息。

        元数据最重要的部分是资源类型和日志名称。 容器日志的资源类型为 container,在用户界面中名为 GKE Containers(即使 Kubernetes 集群不在 Google Kubernetes Engine 上)。 日志名称是容器的名称,因此,如果你有一个包含两个容器的 pod,在 spec 中名称定义为 container_1container_2,则它们的日志的名称分别为 container_1container_2

        系统组件的资源类型为 ,在接口中名为 GCE VM Instance。 系统组件的日志名称是固定的。 对于 Google Kubernetes Engine 节点,系统组件中的每个日志项都具有以下日志名称之一:

        • docker
        • kubelet
        • kube-proxy

        你可以在 上了解有关查看日志的更多信息。

        查看日志的一种可能方法是使用 Google Cloud SDK 中的 命令行接口。 它使用 Stackdriver 日志机制的 过滤语法查询特定日志。 例如,你可以运行以下命令:

        1. gcloud beta logging read 'logName="projects/$YOUR_PROJECT_ID/logs/count"' --format json | jq '.[].textPayload'
        1. ...
        2. "2: Mon Jan 1 00:01:02 UTC 2001\n"
        3. "1: Mon Jan 1 00:01:01 UTC 2001\n"
        4. "0: Mon Jan 1 00:01:00 UTC 2001\n"
        5. ...
        6. "2: Mon Jan 1 00:00:02 UTC 2001\n"
        7. "1: Mon Jan 1 00:00:01 UTC 2001\n"
        8. "0: Mon Jan 1 00:00:00 UTC 2001\n"

        如你所见,尽管 kubelet 已经删除了第一个容器的日志,日志中仍会包含 counter 容器第一次和第二次运行时输出的消息。

        你可以将日志导出到 或 BigQuery 进行进一步的分析。 Stackdriver 日志机制提供了接收器(Sink)的概念,你可以在其中指定日志项的存放地。 可在 Stackdriver 上获得更多信息。

        • 你可能需要添加更多资源,因为默认的行为表现无法满足你的需求。
        • 你可能需要引入额外的解析机制以便从日志消息中提取更多元数据,例如严重性或源代码引用。
        • 你可能想要将日志不仅仅发送到 Stackdriver 或仅将部分日志发送到 Stackdriver。

        在这种情况下,你需要更改 DaemonSetConfigMap 的参数。

        先决条件

        如果使用的是 GKE,并且集群中启用了 Stackdriver 日志机制,则无法更改其配置, 因为它是由 GKE 管理和支持的。 但是,你可以禁用默认集成的日志机制并部署自己的。

        若要禁用默认的日志记录集成,请使用以下命令:

        1. gcloud beta container clusters update --logging-service=none CLUSTER

        你可以在部署部分中找到有关如何将 Stackdriver 日志代理安装到 正在运行的集群中的说明。

        当集群中有 Stackdriver 日志机制的 DaemonSet 时,你只需修改其 spec 中的 template 字段,daemonset 控制器将为你更新 Pod。 例如,假设你按照上面的描述已经安装了 Stackdriver 日志机制。 现在,你想更改内存限制,来给 fluentd 提供的更多内存,从而安全地处理更多日志。

        获取集群中运行的 DaemonSet 的 spec:

        1. kubectl get ds fluentd-gcp-v2.0 --namespace kube-system -o yaml > fluentd-gcp-ds.yaml

        然后在 spec 文件中编辑资源需求,并使用以下命令更新 apiserver 中的 DaemonSet 对象:

        1. kubectl replace -f fluentd-gcp-ds.yaml

        一段时间后,Stackdriver 日志代理的 pod 将使用新配置重新启动。

        更改 fluentd 参数

        Fluentd 的配置存在 ConfigMap 对象中。 它实际上是一组合并在一起的配置文件。 你可以在上了解 fluentd 的配置。

        假设你要向配置添加新的解析逻辑,以便 fluentd 可以理解默认的 Python 日志记录格式。 一个合适的 fluentd 过滤器类似如下:

        现在,你需要将其放入配置中,并使 Stackdriver 日志代理感知它。 通过运行以下命令,获取集群中当前版本的 Stackdriver 日志机制的 ConfigMap

        1. kubectl get cm fluentd-gcp-config --namespace kube-system -o yaml > fluentd-gcp-configmap.yaml

        然后在 containers.input.conf 键的值中,在 source 部分之后插入一个新的过滤器。

        在 apiserver 中更新 ConfigMap 比更新 DaemonSet 更复杂。 最好考虑 ConfigMap 是不可变的。 如果是这样,要更新配置,你应该使用新名称创建 ConfigMap,然后使用 上面的指南DaemonSet 更改为指向它。

        Fluentd 用 Ruby 编写,并允许使用 扩展其功能。 如果要使用默认的 Stackdriver 日志机制容器镜像中未包含的插件,则必须构建自定义镜像。 假设你要为来自特定容器添加 Kafka 信息接收器,以进行其他处理。 你可以复用默认的容器镜像源,并仅添加少量更改:

        • 将 Makefile 更改为指向你的容器仓库,例如 PREFIX=gcr.io/<your-project-id>
        • 将你的依赖项添加到 Gemfile 中,例如 gem 'fluent-plugin-kafka'