Kubernetes 中的日志记录
自计算机诞生以来,应用程序和系统日志输出到操作系统或系统路径上的某个神奇位置一直是每个系统管理员和开发人员的救星。
在 Kubernetes 中,情况也并无不同。
如果没有适当的日志记录和全面的日志消费,您将永远无法排查 Kubernetes 环境中发生的问题。
在这篇博客文章中,您将了解什么是日志记录、Kubernetes 中的不同日志记录方法以及如何在生产环境中实现它们。
什么是日志记录?
您在尝试安装某些程序或使用特定工具/平台时,是否曾经在屏幕上看到过错误提示?
你是否曾经需要在电脑上查看某个应用程序的输出结果?
这些是日志。
日志包含有关系统、操作系统、应用程序以及许多其他方面的信息,前提是查看日志的平台支持日志记录。这意味着每个应用程序都有能力发送日志,但有时它们不会这样做。是否为应用程序实现保存/输出日志的功能完全取决于开发者。
日志记录几乎对技术领域的每个人都至关重要。无论你是系统管理员、云工程师、开发人员,还是介于两者之间的任何角色,你几乎肯定都曾在某个时候查看过日志来解决特定问题。
简而言之,日志是计算机或应用程序上发生的事件。
Kubernetes 中的日志
在 Kubernetes 中,您需要关注两种不同类型的日志:
- 集群日志
- Kubernetes 资源/对象日志
集群日志反映的是集群的运行状况。它记录控制平面是否健康、工作节点是否健康,以及 Kubernetes 组件(例如 Etcd、调度器、API 服务器等)的运行状况(具体取决于它们的安装方式)。例如,在标准的 Kubeadm 部署中,Etcd 会以 Pod 的形式运行,因此它会被视为“Kubernetes 资源/对象日志”。但是,如果 Etcd 配置在多个集群中运行于其自身的服务器上,则它可能不会以 Pod 的形式启动,因此会被视为“集群日志”。
Kubernetes 资源/对象日志是指任何正在运行的 Kubernetes 资源的日志。您可以获取各种 Kubernetes 资源的日志和事件,但工程师通常主要关注 Pod 和容器日志,因为这是获取运行中应用程序信息的主要位置。
查看 Pod 日志最快捷的方法是使用kubectl logs pod_name命令。但是,该命令kubectl logs只能查看 Pod 的日志,不能查看其他 Kubernetes 资源的日志。
Kubernetes 中不同类型的日志记录方法
在考虑 Kubernetes 环境的日志记录策略时,有几种不同的方法可以输出日志:
- 应用程序转发
- 边车
- 节点代理转发
让我们来逐一分析。
应用程序转发是在应用程序代码内部完成的。例如,假设您正在编写一个前端应用程序。在应用程序内部,您可以指定“将日志从此处发送到此系统”的逻辑。虽然这听起来很简单,但可以说是管理日志最糟糕的方式。为什么呢?因为您将代码和日志系统都依赖于此。如果日志系统发生变化怎么办?难道您要为每个应用程序以及每个应用程序的每个部分都实现此功能吗?除非有极其充分的理由,否则应始终避免使用这种方法。例如,如果您有一个已经嵌入日志功能的遗留应用程序,那么使用这种方法就是一个充分的理由(尽管您应该计划如何在未来更改此功能)。
如果你在 Kubernetes Pod 中实现日志聚合器,那么就会用到 sidecar 方法。这样一来,同一个 Pod 中就会有两个容器:一个容器运行应用程序本身,另一个容器运行你使用的日志系统。这种方法虽然“尚可”,也应该优先于应用程序转发,但还有更好的方法——节点代理转发。
节点代理转发方法是指在每个 Kubernetes 工作节点上运行一个 Pod。该 Pod 的任务是读取容器化应用程序的日志文件,并将其发送到您使用的任何日志工具/平台。这种方法之所以是最佳选择,原因有以下几点:1)无需实现 sidecar,从而避免了 Pod 功能上的冗余,因为 Pod 本身就应该只负责一项任务。2)它隔离了工作负载进程,避免了依赖关系,因此您只需一个 Kubernetes 资源(Pod)来执行一项任务(发送日志)。
Kubernetes 审计日志
日志的特殊之处在于,它几乎可以涵盖任何输出,例如事件日志、应用程序日志、终端输出、退出事件等等。正因如此,我们无法面面俱到地介绍所有内容,否则这篇文章就变成一份白皮书了,所以我们还是重点关注最重要的部分吧。
要开始这篇博客文章的实践部分,我们将从 Kubernetes 审计日志开始。
Kubernetes 审计日志使您能够捕获和查看 Kubernetes 资源创建的任何输出。例如,当您创建一个 Kubernetes Deployment 时,会发生很多事情。
- 容器镜像正在从镜像仓库拉取。
- Pod 正在被调度到工作节点上。
- Pod 要么正在启动,要么正在失败;如果失败,则有其原因。
- Pod 的扩展已完成。
正因为如此,您可能希望指定收集某些日志,或者您可能希望收集所有日志。
例如,下面是Policy用于创建审计策略的 Kubernetes 资源,该策略会从每个 Kubernetes 资源中收集所有可能的信息。
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
首先,我们来看看如何在本地 Kubernetes 集群中实现这一点。例如,我们使用 Kubeadm 进行引导,因为它与云服务有很大不同。然后,您将看到如何为云服务配置审计。
Kubeadm
首先,为要创建的策略创建一个新的 YAML 文件。
sudo vim /etc/kubernetes/kubeadmpolicy.yaml
接下来,指定策略。例如,您可以使用以下策略,该策略会收集 Pod 以及所有通过 API 进行身份验证的用户的审计日志。
apiVersion: audit.k8s.io/v1
kind: Policy
omitStages:
- "RequestReceived"
rules:
- level: RequestResponse
resources:
- group: ""
resources: ["pods"]
- level: Metadata
resources:
- group: ""
resources: ["pods/log", "pods/status"]
- level: None
userGroups: ["system:authenticated"]
nonResourceURLs:
- "/api*"
- "/version"
保存后,需要更新 API 服务器。使用 Kubeadm 引导 Kubernetes 集群时,构成控制平面的所有 Pod 的清单都位于/etc/kubernetes/manifests.
打开清单kube-apiserver.yaml。
sudo vim /etc/kubernetes/manifests/kube-apiserver.yaml
保存以下kube-apiserver配置行,以指定日志的保存位置以及您上面创建的策略。
- --audit-log-path=/var/log/audit.log
- --audit-policy-file=/etc/kubernetes/kubeadmpolicy.yaml
接下来,指定审计策略和日志挂载点。
*- mountPath: /etc/kubernetes/kubeadmpolicy.yaml*
*name: audit*
*readOnly: true*
*- mountPath: /var/log/audit.log*
*name: audit-log*
*readOnly: false*
最后一步是为之前创建的挂载路径指定卷的主机路径。
*- hostPath:
path: /etc/kubernetes/kubeadmpolicy.yaml
type: File
name: audit
- hostPath:
path: /var/log/audit.log
type: FileOrCreate
name: audit-log*
完成后,重启 Kubelet。
sudo systemctl restart kubelet
您现在应该可以在以下位置看到日志。
tail -f /var/log/audit.log
Azure Kubernetes 服务
默认情况下,AKS 上启用了审计功能,并制定了审计策略来收集所有 Kubernetes 资源的所有内容。
但是,查看审计日志的功能默认情况下未启用。要启用此功能,请执行以下操作。
首先,进入您的 AKS 集群,在“监控”下,点击“诊断设置”。然后,点击蓝色的“ + 添加诊断设置”按钮。
在“诊断设置”下,单击“Kubernetes 审计”类别。此时,您可以选择日志的保存位置。在本节中,您可以选择“发送到 Log Analytics 工作区”选项。
现在审计功能已启用,您可以打开分析工作区并运行查询来检索事件。
例如,以下查询将查询 Kubernetes 审计日志的所有内容。
AzureDiagnostics
| where Category == "kube-audit"
| project log_s
下面可以看到输出示例。
AWS Elastic Kubernetes Service
对于 AWS,审计日志默认情况下未启用。您可以在创建 Kubernetes 集群时启用它,启用后审计日志将默认发送到 CloudWatch。
Kubernetes 事件
在本节中,您将学习如何检索 Kubernetes 事件,它们本身就是日志。
事件是 Kubernetes 资源/对象,当另一个 Kubernetes 资源/对象(例如 Pod、Service、Node 等)发生更改时就会触发。
你可以运行该kubectl get events命令来查看集群内部正在发生的事情。
kubectl get events
LAST SEEN TYPE REASON OBJECT MESSAGE
5m1s Normal NodeHasSufficientMemory node/stdkubeadmwn Node stdkubeadmwn status is now: NodeHasSufficientMemory
5m1s Normal NodeHasNoDiskPressure node/stdkubeadmwn Node stdkubeadmwn status is now: NodeHasNoDiskPressure
5m1s Normal NodeHasSufficientPID node/stdkubeadmwn Node stdkubeadmwn status is now: NodeHasSufficientPID
5m1s Normal NodeReady node/stdkubeadmwn Node stdkubeadmwn status is now: NodeReady
5m11s Normal NodeNotReady node/stdkubeadmwn Node stdkubeadmwn status is now: NodeNotReady
然而,由于该命令是在一个新集群上运行的,所以目前并没有太多动态kubectl get events。让我们施加一些流量来生成一些事件。
运行以下 YAML 文件,即可部署 Nginx Kubernetes Deployment。
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginxdeployment
replicas: 2
template:
metadata:
labels:
app: nginxdeployment
spec:
containers:
- name: nginxdeployment
image: nginx:latest
ports:
- containerPort: 80
EOF
如果再次运行该kubectl get events命令,您将看到更多事件,因为 Nginx Deployment 的 Pod 已创建,并且 Nginx 容器镜像已从 Docker 注册表中拉取。
mike@stdkubeadmcp:~$ kubectl get events
LAST SEEN TYPE REASON OBJECT MESSAGE
1s Normal Scheduled pod/nginx-deployment-574db6c95f-fr8qb Successfully assigned default/nginx-deployment-574db6c95f-fr8qb to stdkubeadmwn
1s Normal Pulling pod/nginx-deployment-574db6c95f-fr8qb Pulling image "nginx:latest"
1s Normal Scheduled pod/nginx-deployment-574db6c95f-llzzd Successfully assigned default/nginx-deployment-574db6c95f-llzzd to stdkubeadmwn
1s Normal Pulling pod/nginx-deployment-574db6c95f-llzzd Pulling image "nginx:latest"
1s Normal SuccessfulCreate replicaset/nginx-deployment-574db6c95f Created pod: nginx-deployment-574db6c95f-fr8qb
1s Normal SuccessfulCreate replicaset/nginx-deployment-574db6c95f Created pod: nginx-deployment-574db6c95f-llzzd
1s Normal ScalingReplicaSet deployment/nginx-deployment Scaled up replica set nginx-deployment-574db6c95f to 2
6m16s Normal NodeHasSufficientMemory node/stdkubeadmwn Node stdkubeadmwn status is now: NodeHasSufficientMemory
6m16s Normal NodeHasNoDiskPressure node/stdkubeadmwn Node stdkubeadmwn status is now: NodeHasNoDiskPressure
6m16s Normal NodeHasSufficientPID node/stdkubeadmwn Node stdkubeadmwn status is now: NodeHasSufficientPID
6m16s Normal NodeReady node/stdkubeadmwn Node stdkubeadmwn status is now: NodeReady
6m26s Normal NodeNotReady node/stdkubeadmwn Node stdkubeadmwn status is now: NodeNotReady
可以想象,运行该命令kubectl get events最终会检索到大量数据,具体取决于集群的新旧程度。
要获取更具体的事件列表,您可以指定一个特定的 Kubernetes 资源。
在下面的示例中,您可以看到该describe命令用于查看 Kubernetes Deployment 的事件(以及许多其他组件)。
kubectl describe deployment nginx-deployment
如果向下滚动describe命令的输出,您将看到类似于下面的输出,其中显示了资源的事件。
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 38s deployment-controller Scaled up replica set nginx-deployment-574db6c95f to 2
结论
Kubernetes 中的日志记录与其他日志记录方法非常相似——收集事件、查看事件,并利用它们来排查故障或了解系统和应用程序中正在发生的事情。需要记住的是,务必将日志发送到整个团队都可以访问的正确位置。
文章来源:https://dev.to/thenjdevopsguy/logging-in-kubernetes-2l7m






