本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

使用 Kubernetes 进行集群级日志记录

Kubernetes 集群通常会稳定运行许多系统和应用程序 Pod。系统管理员如何收集、管理和查询系统 Pod 的日志?用户如何查询其应用程序的日志,该应用程序由许多 Pod 组成,这些 Pod 可能会被 Kubernetes 系统重启或自动生成?这些问题由 Kubernetes 集群级别日志服务解决。

Kubernetes 的集群级别日志允许我们收集在 Pod 容器镜像生命周期、Pod 生命周期甚至集群生命周期之外持久存在的日志。在本文中,我们假设已创建具有集群级别日志支持的 Kubernetes 集群,用于将日志发送到 Google Cloud Logging。这是创建 Google Container Engine (GKE) 集群时的一个选项,并且默认情况下为开源 Google Compute Engine (GCE) Kubernetes 发行版启用。集群创建后,您将拥有一个运行系统 Pod 的集合,这些 Pod 支持 Kubernetes 服务的名称的监控、日志记录和 DNS 解析。

$ kubectl get pods
NAME                                           READY     REASON    RESTARTS   AGE

fluentd-cloud-logging-kubernetes-minion-0f64   1/1       Running   0          32m

fluentd-cloud-logging-kubernetes-minion-27gf   1/1       Running   0          32m

fluentd-cloud-logging-kubernetes-minion-pk22   1/1       Running   0          31m

fluentd-cloud-logging-kubernetes-minion-20ej   1/1       Running   0          31m

kube-dns-v3-pk22                               3/3       Running   0          32m


monitoring-heapster-v1-20ej                    0/1       Running   9          32m

以下是相同信息的可视化表示,显示了 Pod 如何放置在特定节点上。

这是每个节点上运行内容的特写。

第一个图表显示了在 GCE 集群上创建的四个节点,每个 VM 节点的名称都显示在紫色背景上。每个节点的内部和公共 IP 显示在灰色框中,每个节点中运行的 Pod 显示在绿色框中。每个 Pod 框显示了 Pod 的名称及其运行的命名空间、Pod 的 IP 地址以及作为 Pod 执行的一部分运行的镜像。在这里,我们看到每个节点都运行着一个 fluentd-cloud-logging Pod,它收集同一节点上运行的容器的日志输出并将其发送到 Google Cloud Logging。一个提供 集群 DNS 服务的 Pod 运行在一个节点上,而一个提供监控支持的 Pod 运行在另一个节点上。

为了帮助解释集群级别日志的工作原理,我们从一个合成日志生成器 Pod 规范 counter-pod.yaml 开始。

  apiVersion : v1  
  kind : Pod  
  metadata :  
    name : counter  
  spec :  
    containers :  
   - name : count  
      image : ubuntu:14.04  
      args : [bash, -c,   
            'for ((i = 0; ; i++)); do echo "$i: $(date)"; sleep 1; done']  

这个 Pod 规范有一个容器,当容器启动时运行一个 bash 脚本。这个脚本简单地每秒写入一次计数器值和日期,并无限期运行。让我们创建 Pod。


$ kubectl create -f counter-pod.yaml


pods/counter

我们可以观察运行中的 Pod

$ kubectl get pods
NAME                                           READY     REASON    RESTARTS   AGE

counter                                        1/1       Running   0          5m

fluentd-cloud-logging-kubernetes-minion-0f64   1/1       Running   0          55m

fluentd-cloud-logging-kubernetes-minion-27gf   1/1       Running   0          55m

fluentd-cloud-logging-kubernetes-minion-pk22   1/1       Running   0          55m

fluentd-cloud-logging-kubernetes-minion-20ej   1/1       Running   0          55m

kube-dns-v3-pk22                               3/3       Running   0          55m

monitoring-heapster-v1-20ej                    0/1       Running   9          56m

此步骤可能需要几分钟来下载 ubuntu:14.04 镜像,在此期间 Pod 状态将显示为“Pending”。

其中一个节点现在正在运行计数器 Pod

当 Pod 状态变为“Running”时,我们可以使用 kubectl logs 命令查看此计数器 Pod 的输出。

$ kubectl logs counter

0: Tue Jun  2 21:37:31 UTC 2015

1: Tue Jun  2 21:37:32 UTC 2015

2: Tue Jun  2 21:37:33 UTC 2015

3: Tue Jun  2 21:37:34 UTC 2015

4: Tue Jun  2 21:37:35 UTC 2015

5: Tue Jun  2 21:37:36 UTC 2015

此命令从此容器中运行的镜像的 Docker 日志文件中获取日志文本。我们可以连接到正在运行的容器并观察正在运行的计数器 bash 脚本。

$ kubectl exec -i counter bash

ps aux

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

root         1  0.0  0.0  17976  2888 ?        Ss   00:02   0:00 bash -c for ((i = 0; ; i++)); do echo "$i: $(date)"; sleep 1; done

root       468  0.0  0.0  17968  2904 ?        Ss   00:05   0:00 bash

root       479  0.0  0.0   4348   812 ?        S    00:05   0:00 sleep 1

root       480  0.0  0.0  15572  2212 ?        R    00:05   0:00 ps aux

如果由于某种原因,此 Pod 中的镜像被终止,然后由 Kubernetes 重新启动,会发生什么?我们还会看到上一次容器调用产生的日志行,后面跟着重新启动的容器的日志行吗?还是我们会丢失原始容器执行产生的日志行,而只看到新容器的日志行?让我们找出答案。首先,让我们停止当前正在运行的计数器。

$ kubectl stop pod counter

pods/counter


Now let’s restart the counter.


$ kubectl create -f counter-pod.yaml

pods/counter

让我们等待容器重启并再次获取日志行。

$ kubectl logs counter

0: Tue Jun  2 21:51:40 UTC 2015

1: Tue Jun  2 21:51:41 UTC 2015

2: Tue Jun  2 21:51:42 UTC 2015

3: Tue Jun  2 21:51:43 UTC 2015

4: Tue Jun  2 21:51:44 UTC 2015

5: Tue Jun  2 21:51:45 UTC 2015

6: Tue Jun  2 21:51:46 UTC 2015

7: Tue Jun  2 21:51:47 UTC 2015

8: Tue Jun  2 21:51:48 UTC 2015

噢不!我们丢失了此 Pod 中容器第一次调用产生的日志行!理想情况下,我们希望保留 Pod 中每个容器每次调用产生的所有日志行。此外,即使 Pod 重新启动,我们仍然希望保留 Pod 中容器曾经发出的所有日志行。但别担心,这正是 Kubernetes 中集群级别日志提供的功能。当创建集群时,每个容器的标准输出和标准错误输出可以通过每个节点上运行的 Fluentd 代理收集到 Google Cloud Logging 或 Elasticsearch 中,并通过 Kibana 查看。本博文重点介绍 Google Cloud Logging。

当创建启用日志记录到 Google Cloud Logging 的 Kubernetes 集群时,系统会在集群的每个节点上创建一个名为 fluentd-cloud-logging 的 Pod,用于收集 Docker 容器日志。这些 Pod 在本博文开头,在第一个 get pods 命令的响应中有所展示。

这个日志收集 Pod 的规范看起来像这样 fluentd-gcp.yaml

apiVersion: v1

kind: Pod

metadata:

  name: fluentd-cloud-logging

spec:

  containers:

  - name: fluentd-cloud-logging

    image: gcr.io/google\_containers/fluentd-gcp:1.6

    env:

    - name: FLUENTD\_ARGS

      value: -qq

    volumeMounts:

    - name: containers

      mountPath: /var/lib/docker/containers

  volumes:

  - name: containers

    hostPath:

      path: /var/lib/docker/containers

此 Pod 规范将主机上包含 Docker 日志文件的目录 /var/lib/docker/containers 映射到容器内部具有相同路径的目录。该 Pod 运行一个镜像 gcr.io/google_containers/fluentd-gcp:1.6,该镜像配置为从 logs 目录收集 Docker 日志文件并将它们摄取到 Google Cloud Logging。此 Pod 的一个实例在集群的每个节点上运行。如果此 Pod 失败,Kubernetes 将会注意到并自动重启它。

我们可以点击 Google Developer Console 监控部分下的日志项,选择计数器容器的日志,其名称为 kubernetes.counter_default_count。这标识了发生日志收集的 Pod 名称 (counter)、命名空间 (default) 和容器名称 (count)。使用此名称,我们可以从下拉菜单中选择仅限我们计数器容器的日志。

(image-counter-new-logs.png)

当我们在开发人员控制台中查看日志时,我们观察到容器两次调用的日志。

(image-screenshot-2015-06-02)

请注意,第一个容器计数到 108 然后终止。当下一个容器镜像重启时,计数过程从 0 恢复。类似地,如果我们删除了 Pod 并重新启动它,只要 Pod 正在运行,我们就会捕获 Pod 中所有容器实例的日志。

摄取到 Google Cloud Logging 中的日志可以导出到其他各种目的地,包括 Google Cloud Storage 存储桶和 BigQuery。使用 Cloud Logging 控制台中的“导出”选项卡指定日志应流向何处(或点击此链接转到设置选项卡)。

我们可以使用 SQL 查询从 BigQuery 中查询摄取的日志,该查询报告计数器日志行并按最新行优先显示。

SELECT metadata.timestamp, structPayload.log FROM [mylogs.kubernetes_counter_default_count_20150611] ORDER BY metadata.timestamp DESC

以下是一些示例输出

(image-bigquery-log-new.png)

我们还可以将日志从 Google Cloud Storage 存储桶获取到我们的台式机或笔记本电脑,然后在本地搜索它们。以下命令获取在名为 myproject 的 GCE 项目中运行的集群中计数器 Pod 的日志。仅获取 2015-06-11 日的日志。

$ gsutil -m cp -r gs://myproject/kubernetes.counter\_default\_count/2015/06/11 .

现在我们可以在摄取的日志上运行查询。下面的示例使用 jq 程序来提取日志行。

$ cat 21\:00\:00\_21\:59\:59\_S0.json | jq '.structPayload.log'

"0: Thu Jun 11 21:39:38 UTC 2015\n"

"1: Thu Jun 11 21:39:39 UTC 2015\n"

"2: Thu Jun 11 21:39:40 UTC 2015\n"

"3: Thu Jun 11 21:39:41 UTC 2015\n"

"4: Thu Jun 11 21:39:42 UTC 2015\n"

"5: Thu Jun 11 21:39:43 UTC 2015\n"

"6: Thu Jun 11 21:39:44 UTC 2015\n"

"7: Thu Jun 11 21:39:45 UTC 2015\n"

本文简要介绍了支持在 Kubernetes 部署上收集集群级别日志的基础机制。这里的方法仅适用于收集 Pod 容器中运行进程的标准输出和标准错误输出。要收集存储在文件中的其他日志,可以使用一个 sidecar 容器来收集所需文件,具体方法请参阅页面 使用 Fluentd 收集容器内的日志文件并将其发送到 Google Cloud Logging 服务