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

使用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集群上创建的四个节点,每个虚拟机节点的名称位于紫色背景上。每个节点的内部和公共IP显示在灰色框中,每个节点中运行的Pod显示在绿色框中。每个Pod框显示Pod的名称及其运行的命名空间、Pod的IP地址以及作为Pod执行的一部分运行的镜像。在这里,我们看到每个节点都运行一个fluentd-cloud-logging Pod,该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的一个实例在集群的每个节点上运行。Kubernetes会注意到此Pod是否发生故障并自动重新启动它。

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

(image-counter-new-logs.png)

当我们在Developer Console中查看日志时,我们会观察到容器两次调用的日志。

(image-screenshot-2015-06-02)

请注意,第一个容器计数到108,然后终止。当下一个容器镜像重新启动时,计数过程从0开始恢复。同样,如果我们删除Pod并重新启动它,我们将在Pod运行时捕获Pod中所有容器实例的日志。

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

我们可以使用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存储桶中的日志提取到我们的台式机或笔记本电脑,然后在本地搜索它们。以下命令提取在集群中运行的计数器Pod的日志,该集群本身位于名为myproject的GCE项目中。仅提取日期为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服务中所述。