内容


对 Kubernetes 应用程序进行调试和记录

了解 Kubernetes 集群

Comments

Kubernetes 是用于管理多个主机间的容器化应用程序的开源系统,它为应用程序部署、维护和扩展提供了基本机制。开源项目由云原生计算基金会托管,在本教程中,托管在 IBM Cloud Kubernetes Service 上。

学习目标

本教程面向希望深入了解 Kubernetes 集群以及应用程序调试方式和日志获取方式的开发者。

前提条件

开始本教程前,您需要:

预估时间

完成本教程大约需要 20 分钟。

步骤

容器日志

检验和调试 pod

让我们来聊聊应用程序部署。有时部署会很顺利,您会看到 deployment <your-app-name> created 消息!但如果应用程序部署失败该怎么办?您可以执行以下几个步骤:收集信息、制定修复计划、测试并执行。先来看下信息收集,可用于信息收集的工具和方法多种多样。

  1. 要获取 pod 的基本信息,可以使用以下简单命令:
    $ kubectl get pods
    NAME                         READY     STATUS    RESTARTS   AGE
    guestbook-75786d799f-fg72k   1/1       Running   0          7m
  2. 如果对特定 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 中的一种资源类型。因此,执行此命令时,它将列出各种事件,就像列出任何其他资源一样,并提供汇总视图。应记住,事件资源按名称空间划分,因此如果要获取特定事件,应注明要获取的事件所属的名称空间,或者留空即可获取所有事件。

  1. 使用以下命令获取 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 以运行测试。

  1. 为实现此目的,我们需要使用 /bin/bash 创建新 pod:
     $ kubectl create -f https://k8s.io/examples/application/shell-demo.yaml
  2. 将 shell 放入容器:
     $ kubectl exec -it shell-demo -- /bin/bash
  3. 现在,列出根目录:
    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,并收到错误。

  1. 要查看所有服务,可通过使用如下简单命令来查看所有 pod:
    $ kubectl get svc
    NAME        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
    guestbook   172.21.30.218   <nodes> 3000:32412/TCP 45m
  2. 如果您要查找的服务不存在,那么可使用以下命令来创建此服务:
    $ kubectl expose deployment <your-deployment-name> --type="NodePort" --port=3000
     service "guestbook" exposed

    如果您已有服务,则应查看其配置是否存在问题。

  3. 让我们来试着获取有关此服务的更多信息:
                 $ 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 命令再次核查是否存在错误。

  4. 您还可获取 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 使用的协议是否相同?

  5. 此外,还有其他一些方法可用于检验您的服务。您可阅读此处,获取更多信息。

检查 DNS

部分网络问题可能是由 DNS 配置或错误所导致的。因此,您首先需要检查 DNS 是否正常工作。

  1. 使用 get pods 命令获取 pod 名称:
    $ kubectl get pods
    NAME                         READY     STATUS    RESTARTS   AGE
    guestbook-75786d799f-brrhf   1/1       Running   0          47m
  2. 使用以下命令检查 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 文件是否正确。

  3. 进入 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 用户都会自动获得 Grafana 访问权和帐户。要开始使用 Grafana,可阅读此处教程。要了解如何使用 Grafana 并为 Kubernetes 集群创建仪表板,可在此处阅读了解更多信息

结束语

既然您已学习了 Kubernetes 应用程序的日志记录和调试方面的基础知识,现在就可以尝试使用并探索其他一些工具:

参考资源

本文翻译自:Debug and log your Kubernetes applications(2019-02-04)


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Cloud computing
ArticleID=1065308
ArticleTitle=对 Kubernetes 应用程序进行调试和记录
publish-date=03282019