Kubernetes 是用于管理多个主机间的容器化应用程序的开源系统,它为应用程序部署、维护和扩展提供了基本机制。开源项目由云原生计算基金会托管,在本教程中,托管在 IBM Cloud Kubernetes Service 上。
学习目标
本教程面向希望深入了解 Kubernetes 集群以及应用程序调试方式和日志获取方式的开发者。
前提条件
开始本教程前,您需要:
- 一个免费 IBM Cloud
帐户:如果还没有 IBM Cloud 帐户,可以在此处创建帐户。 - 在 IBM Cloud 上创建 Kubernetes 集群。
- 部署样本应用程序并将 kubectl 连接到 Kubernetes 集群。您可遵循实验室 0 和实验室 1 获取相关说明。
预估时间
完成本教程大约需要 20 分钟。
步骤
容器日志
检验和调试 pod
让我们来聊聊应用程序部署。有时部署会很顺利,您会看到 deployment <your-app-name> created
消息!
但如果应用程序部署失败该怎么办?您可以执行以下几个步骤:收集信息、制定修复计划、测试并执行。
先来看下信息收集,可用于信息收集的工具和方法多种多样。
要获取 pod 的基本信息,可以使用以下简单命令:
$ kubectl get pods NAME READY STATUS RESTARTS AGE guestbook-75786d799f-fg72k 1/1 Running 0 7m
如果对特定 pod 加以描述,就可获得更详细的信息,如下所示:
$ kubectl describe pod <your-pod-name> Name: guestbook-75786d799f-fg72k Namespace: default Node: 10.47.84.98/10.47.84.98 Start Time: Sun, 19 Aug 2018 12:56:23 +0300 Labels: pod-template-hash=3134283559 run=guestbook Annotations: kubernetes.io/psp=ibm-privileged-psp Status: Running ...
在上例中,您可看到容器和 pod 的相关配置信息(标签、资源需求等)以及容器和 pod 的相关状态信息(状态、准备情况、重新启动计数、事件等)。
有时使用这些基本命令应已足矣。例如,通过查看 kubectl describe
,您可以发现镜像提取错误,您只是忘记了放入部分信息(如镜像提取密钥)。或者可能您使用了最新版本的镜像,但它未能正常运行,因此您想要切换回较低版本。
但如果我们未找到任何错误并需要深入挖掘日志,那么会发生什么?许多开发者不知道的是,事件 (kubectl get events
) 实际上是 Kubernetes 中的一种资源类型。
因此,执行此命令时,它将列出各种事件,就像列出任何其他资源一样,并提供汇总视图。
应记住,事件资源按名称空间划分,因此如果要获取特定事件,应注明要获取的事件所属的名称空间,或者留空即可获取所有事件。
使用以下命令获取 pod 的事件列表:
$kubectl get events [--namespace=default] LASTSEEN FIRSTSEEN COUNT NAME KIND SUBOBJECT TYPE REASON SOURCE MESSAGE 3m 3m 1 guestbook-75786d799f-r6mxl Pod Normal Scheduled default-scheduler Successfully assigned guestbook-75786d799f-r6mxl to 10.77.155.84 3m 3m 1 guestbook-75786d799f-r6mxl Pod Normal SuccessfulMountVolume kubelet, 10.77.155.84 MountVolume.SetUp succeeded for volume "default-token-5rlxc" 3m 3m 1 guestbook-75786d799f-r6mxl Pod spec.containers{guestbook} Normal Pulled kubelet, 10.77.155.84 Container image "ibmcom/guestbook:v1" already present on machine 3m 3m 1 guestbook-75786d799f-r6mxl Pod spec.containers{guestbook} Normal Created kubelet, 10.77.155.84 Created container 3m 3m 1 guestbook-75786d799f-r6mxl Pod spec.containers{guestbook} Normal Started kubelet, 10.77.155.84 Started container 3m 3m 1 guestbook-75786d799f-xvpvv Pod spec.containers{guestbook} Normal Killing kubelet, 10.77.155.84 Killing container with id docker://guestbook:Need to kill Pod 3m 3m 1 guestbook-75786d799f ReplicaSet Normal SuccessfulDelete replicaset-controller Deleted pod: guestbook-75786d799f-xvpvv 3m 3m 1 guestbook-75786d799f ReplicaSet Normal SuccessfulCreate replicaset-controller Created pod: guestbook-75786d799f-r6mxl 3m 3m 1 guestbook Deployment Normal ScalingReplicaSet deployment-controller Scaled down replica set guestbook-75786d799f to 0 3m 3m 1 guestbook Deployment Normal ScalingReplicaSet deployment-controller Scaled up replica set guestbook-75786d799f to 1
通过事件通常检测到的一种情况是,创建的 pod 并不适用于任何节点,它将保持“Pending”状态,发生这种情况的原因可能是缺少资源等,您可在“事件”部分中查看问题。 让我们看一下重要事件参数,您会发现事件记录中包含如下信息:
KIND
指示事件相关资源的类型(Pod、ReplicaSet、Deployment 等)。TYPE
即事件的状态。它可标记为“Normal”或“Warning”,将来可能添加新类型。REASON
表示转换为对象当前状态的原因。SOURCE
表示报告此事件的组件来源。MESSAGE
指示这是事件的人类可读描述。
此外,还有更多参数未显示在以上列表中,这些参数可能对您很有用,如 METADATA
,即标准对象元数据。 EVENTTIME
是首次观察到事件的时间。ACTION
是针对对象已执行或已失败的特定操作。
获取应用程序日志
应用程序和系统日志可帮助您深入了解集群内发生的状况。您可获取特定 pod 的日志,如果此 pod 包含多个容器,那么您可指定所需的容器。
查看日志
要查看日志,可运行以下简单命令:
$ kubectl logs <your-pod-name>
[negroni] listening on :3000
[negroni] 2018-08-19T11:55:39Z | 200 | 332.277µs | 173.193.106.55:32412 | GET /
[negroni] 2018-08-19T11:55:39Z | 200 | 140.407µs | 173.193.106.55:32412 | GET /style.css
[negroni] 2018-08-19T11:55:39Z | 200 | 123.595µs | 173.193.106.55:32412 | GET /script.js
[negroni] 2018-08-19T11:55:39Z | 200 | 87.508µs | 173.193.106.55:32412 | GET /lrange/guestbook
[negroni] 2018-08-19T11:55:39Z | 404 | 74.307µs | 173.193.106.55:32412 | GET /favicon.ico
[negroni] 2018-08-19T11:57:30Z | 304 | 89.418µs | 173.193.106.55:32412 | GET /
[negroni] 2018-08-19T11:57:30Z | 200 | 60.671µs | 173.193.106.55:32412 | GET /lrange/guestbook
[negroni] 2018-08-19T12:06:23Z | 304 | 152.557µs | 173.193.106.55:32412 | GET /
[negroni] 2018-08-19T12:06:23Z | 200 | 94.091µs | 173.193.106.55:32412 | GET /lrange/guestbook
注意:要打印到日志中,从应用程序写入 stdout/stderr
。此外,还应注意的一点是,日志命令中没有 get
,这表示日志并非类似事件的资源。
为帮助您从日志中获取更好的结果,您可使用 “kubetail”,这是为每种类型的日志添加不同颜色的开源项目。链接如下: https://github.com/johanhaleby/kubetail。
先前日志
您始终可使用 --previous
标志请求获取先前日志,但应知晓另外两种日志级别:节点级别和集群级别。这两种日志级别之间也存在较大差异。让我们来了解下它们的差异:
- 节点级别:容器化应用程序写入由容器引擎处理的
stdout
和stderr
。容器引擎将这两种流重定向至日志记录驱动程序,在 Kubernetes 中将此驱动程序配置为写入 JSON 格式的文件。 JSON 日志记录驱动程序将每一行都作为一条独立消息来处理。使用日志记录驱动程序时,不会直接支持多行消息。您需要在日志记录代理程序级别或更高级别来处理多行消息。 默认情况下,如果某个容器重新启动,kubelet 会保留一个已终止的容器及其日志。如果从节点收回某个 pod,那么将同时收回所有对应的容器及其日志。 - 集群级别:虽然 Kubernetes 并没有为集群级别日志记录提供本机解决方案,但还是有多种常用方法可供您考虑。以下是部分可供选择的方法:
- 使用在每个节点上运行的节点级别日志记录代理程序。
- 在应用程序 pod 中包含一个专用伴生容器用于日志记录。
- 将日志从应用程序内直接推送至后端。
要获取有关这些方法的更多信息,可以阅读本指南。
在运行中的容器内使用 shell
大部分情况下,您的容器日志即 pod 日志,尤其是当 pod 中仅有一个容器时。如果集群中确实出现问题,并且您无法使用 kubectl
从 pod 中获取日志,那么您可能必须进入容器并获取日志(调试容器),这样才能完全掌控容器内发生的状况。
您可以使用 kubectl exec
来访问容器中运行的 shell,识别容器中哪部分流程出现问题,这样才能更准确地调试容器。
但应记住,仅当您的容器内包含 Shell exec 时,才能执行此操作。如果您构建容器时所使用的镜像不包含 Shell exec,那么您将无法使用 exec
命令。
Shell exec 可能会占用一些空间,导致增加容器负载,因此您可能会问:“我为何要把它放在容器内?使用容器的最大优势不就是负载轻吗?”
在生产环境中,您可能希望最大限度提升性能,因此不应将 shell 放入镜像。而在测试环境中,您可能乐于在运行容器时访问 shell 以运行测试。
为实现此目的,我们需要使用 /bin/bash 创建新 pod:
$ kubectl create -f https://k8s.io/examples/application/shell-demo.yaml
将 shell 放入容器:
$ kubectl exec -it shell-demo -- /bin/bash
现在,列出根目录:
root@shell-demo:/# ls / bin dev home lib64 mnt proc run srv tmp boot etc lib media opt root sbin sys usr
集群联网问题
联网问题是最常见的问题,因为您无法参照任何要点来按步骤解决所有问题,您必须了解集群网络中出现的具体问题。 为帮助您找到这些问题,我列出了当您怀疑网络出现问题时应查看的关键领域。 延伸阅读:要获取有关 Kubernetes 联网的部分指南,可查看 Kubernetes Networking: A lab on basic networking concepts。
调试服务
新安装 Kubernetes 时常见的一个问题是服务无法正常运行,即您运行自己的部署并创建服务,但仍无法获取任何响应。在本部分中,我们将查看一些可能有助于您找出问题的命令。
有时,我们忘记为 pod 创建服务,因为服务是必需的,所以,我们将无法访问 pod,并收到错误。
要查看所有服务,可通过使用如下简单命令来查看所有 pod:
$ kubectl get svc NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE guestbook 172.21.30.218 <nodes> 3000:32412/TCP 45m
如果您要查找的服务不存在,那么可使用以下命令来创建此服务:
$ kubectl expose deployment <your-deployment-name> --type="NodePort" --port=3000 service "guestbook" exposed
如果您已有服务,则应查看其配置是否存在问题。
- 让我们来试着获取有关此服务的更多信息:
$ kubectl describe service <your-service-name> Name: guestbook Namespace: default Labels: run=guestbook Annotations: <none> Selector: run=guestbook Type: NodePort IP: 172.21.138.209 Port: <unset> 3000/TCP NodePort: <unset> 31235/TCP Endpoints: 172.30.87.71:3000 Session Affinity: None Events: <none>
检查您用于访问容器的 NodePort 是否正确,确保端点已存在。
如果找到错误信息(如无端点),可尝试重新创建服务,并使用 kubectl expose
命令再次核查是否存在错误。
您还可获取 JSON 格式的信息:
$ kubectl get service <your-service-name> -o json { "apiVersion": "v1", "kind": "Service", "metadata": { "creationTimestamp": "2018-08-19T11:55:12Z", "labels": { "run": "guestbook" }, "name": "guestbook", "namespace": "default", "resourceVersion": "1118", "selfLink": "/api/v1/namespaces/default/services/guestbook", "uid": "baa2e98d-a3a6-11e8-a994-f20601bb534c" }, "spec": { "clusterIP": "172.21.30.218", "externalTrafficPolicy": "Cluster", "ports": [ { "nodePort": 32412, "port": 3000, "protocol": "TCP", "targetPort": 3000 } ], "selector": { "run": "guestbook" }, "sessionAffinity": "None", "type": "NodePort" }, "status": { "loadBalancer": {} } }
您应始终仔细检查可能导致问题的配置,如确认 spec.ports 是否是对应于您的 pod 的正确 targetPort。服务与 pod 使用的协议是否相同?
- 此外,还有其他一些方法可用于检验您的服务。您可阅读此处,获取更多信息。
检查 DNS
部分网络问题可能是由 DNS 配置或错误所导致的。 因此,您首先需要检查 DNS 是否正常工作。
使用
get pods
命令获取 pod 名称:$ kubectl get pods NAME READY STATUS RESTARTS AGE guestbook-75786d799f-brrhf 1/1 Running 0 47m
使用以下命令检查 pod DNS:
$ kubectl exec -ti <your-pod-name> -- nslookup kubernetes.default Server: 172.21.0.10 Address 1: 172.21.0.10 kube-dns.kube-system.svc.cluster.local Name: kubernetes.default Address 1: 172.21.0.1 kubernetes.default.svc.cluster.local
应注意,如果您不使用“default”名称空间,那么应根据集群名称空间尝试使用其他名称。如果您的 pod 与服务位于不同名称空间内,尝试使用名称空间限定的名称 (default) – 但您需要将自己的应用调整为使用跨名称空间的名称,或者在相同名称空间内运行您的应用和服务。如果仍失败,尝试使用标准名称(例如,default.svc.cluster.local
)。
如果 nslookup
命令失败,应在配置中执行一些检查或查找错误。
nslookup: can't resolve 'kubernetes.default'
之类的错误可能指示 coredns/kube-dns 附加组件或关联服务中存在问题。
如果您能够执行标准名称查找,但不能执行相对名称查找,那么就需要检查 /etc/resolv.conf 文件是否正确。
进入 resolv.conf 文件,查看参数是否正确:
$ kubectl exec <your-pod-name> cat /etc/resolv.conf nameserver 172.21.0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5
确保 nameserver 行指示您的集群 DNS 服务;使用 –cluster-dns 标志将此内容传递到 kubelet 中。search 行必须包含相应的后缀,供您查找服务名称。在此例中,将查找本地名称空间中的服务 (default.svc.cluster.local)、所有名称空间中的服务 (svc.cluster.local) 和集群 (cluster.local)。根据您自己的安装,您可能还有其他记录。使用 –cluster-domain 标志将集群后缀传递到 kubelet 中。options 行必须将 ndots 设置为足够高的值,以便 DNS 客户机库考虑搜索路径。默认情况下,Kubernetes 将其设置为 5
,该值足以涵盖它生成的所有 DNS 名称。
如果以上所有操作均告失败,您可能需要检查 kube-proxy。
网络策略
NetworkPolicy 可定义允许 pod 彼此通信以及与其他网络端点进行通信的方式。NetworkPolicy 使用标签来管理 pod 之间的流量。 如果您无法与 pod 进行通信,那么可检查网络策略,看看是否不允许此 pod 获取任何请求。 默认情况下,pod 不会被隔离,它们可以接受流量。但为 NetworkPolicy 选择特定 pod 后,就会拒绝通过未经授权的连接进行任何通信。
让我们来看一个 NetworkPolicy 示例:
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: access-nginx
spec:
podSelector:
matchLabels:
run: nginx
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
access: "true"
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
就像所有其他 Kubernetes 配置一样,NetworkPolicy 具有 kind、apiVersion 和 metadata 参数,用于提供常规信息。 您可在以上示例中看到 spec 内包含 podSelector,用于选择要在此 NetworkPolicy 中包含的 pod。在此示例中,将包含所有 nginx pod。 注意:如果 podSelector 为空,此 NetworkPolicy 将影响相同名称空间内的所有 pod!
您需要检查的另外两个重要参数是 ingress 和 egress;这两个参数会影响与 podSelector 中的 pod 通信时支持的传入和传出网络。您应仔细核对这些参数,确保插入正确的标签、IP 和端口,以便允许 pod 进行通信,避免其遭到阻止。
使用 Weave Scope
Weave Scope 是用于 Docker 和 Kubernetes 的开源可视化和监视工具。它提供了您的应用和整个基础架构的自上而下的视图,并且支持您在将分布式容器化应用部署到云提供者时,实时诊断其中存在的任何问题。 要在 Kubernetes 集群上安装 Weave Cope,可在此处查找说明。 如果不使用 Weave Scope,我非常鼓励您使用一个类似的监视工具,轻松显示容器正在执行的操作以及执行相应操作的原因。
使用 Grafana
Grafana 是一个开源的通用仪表板和图形编辑器,作为 Web 应用程序运行。
您可使用 Grafana 来获取集群和 pod 的 CPU、内存及负载指标。
每位 IBM Cloud
结束语
既然您已学习了 Kubernetes 应用程序的日志记录和调试方面的基础知识,现在就可以尝试使用并探索其他一些工具:
本文翻译自:Debug and log your Kubernetes applications(2019-02-04)