本文发布已超过一年。较旧的文章可能包含过时内容。请检查页面中的信息自发布以来是否已失效。
使用 Kubernetes 进行集群级别日志记录
Kubernetes 集群通常会顺畅运行许多系统和应用 Pod。系统管理员如何收集、管理和查询系统 Pod 的日志?用户如何查询由多个可能被 Kubernetes 系统重启或自动生成的 Pod 组成的应用的日志?这些问题由 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。
其中一个节点现在正在运行 counter Pod
当 Pod 状态变为 Running 时,我们可以使用 kubectl logs 命令查看此 counter 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 日志文件中获取日志文本。我们可以连接到正在运行的容器并观察正在运行的 counter 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 重启,会发生什么?我们是会看到容器先前运行的日志行,后面跟着新启动容器的日志行?还是会丢失原始容器执行的日志行,只看到新容器的日志行?让我们来探究一下。首先,让我们停止当前正在运行的 counter。
$ 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 项,并选择 counter 容器的日志,该日志的名称将是 kubernetes.counter_default_count。这标识了 Pod 的名称 (counter)、命名空间 (default) 和进行日志收集的容器名称 (count)。使用此名称,我们可以从下拉菜单中仅选择我们的 counter 容器的日志。
(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 中查询导入的日志,该查询将显示 counter 日志行,最新行在前。
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 项目中的集群中的 counter 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 服务。