标签 Kubernetes 下的文章

Kubernetes Deployment故障排除图解指南

img{512x368}


下面是一个示意图,可帮助你调试Kubernetes Deployment(你可以在此处下载它的PDF版本)。

img{512x368}

当你希望在Kubernetes中部署应用程序时,你通常会定义三个组件:

  • 一个Deployment – 这是一份用于创建你的应用程序的Pod副本的”食谱”;
  • 一个Service – 一个内部负载均衡器,用于将流量路由到内部的Pod上;
  • 一个Ingress – 描述如何流量应该如何从集群外部流入到集群内部的你的服务上。

下面让我们用示意图快速总结一下要点。

img{512x368}

在Kubernetes中,你的应用程序通过两层负载均衡器暴露服务:内部的和外部的

img{512x368}

内部的负载均衡器称为Service,而外部的负载均衡器称为Ingress

img{512x368}

Pod不会直接部署。Deployment会负责创建Pod并管理它们

假设你要部署一个简单的”HelloWorld”应用,该应用的YAML文件的内容应该类似下面这样:

// hello-world.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
  labels:
    track: canary
spec:
  selector:
    matchLabels:
      any-name: my-app
  template:
    metadata:
      labels:
        any-name: my-app
    spec:
      containers:
      - name: cont1
        image: learnk8s/app:1.0.0
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    name: app
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
  - http:
    paths:
    - backend:
        serviceName: app
        servicePort: 80
      path: /

这个定义很长,组件之间的相互关系并不容易看出来。

例如:

  • 什么时候应使用端口80,又是何时应使用端口8080?
  • 你是否应该为每个服务创建一个新端口以免它们相互冲突?
  • 标签(label)名重要吗?它们是否在每一处都应该是一样的?

在进行调试之前,让我们回顾一下这三个组件是如何相互关联的。

让我们从Deployment和Service开始。

一. 连接Deployment和Service

令人惊讶的消息是,Service和Deployment之间根本没有连接。

事实是:Service直接指向Pod,并完全跳过了Deployment。

因此,你应该注意的是Pod和Service之间的相互关系。

你应该记住三件事:

  • Service selector应至少与Pod的一个标签匹配;
  • Service的targetPort应与Pod中容器的containerPort匹配;
  • Service的port可以是任何数字。多个Service可以使用同一端口号,因为它们被分配了不同的IP地址。

下面的图总结了如何连接端口:

img{512x368}

考虑上面被一个服务暴露的Pod

img{512x368}

创建Pod时,应为Pod中的每个容器定义containerPort端口

img{512x368}

当创建一个Service时,你可以定义port和targetPort,但是哪个用来连接容器呢?

img{512x368}

targetPort和containerPort应该始终保持匹配

img{512x368}

如果容器暴露3000端口(containerPort),那么targetPort应该匹配这一个端口号

再来看看YAML,标签和ports/targetPort应该匹配:

// hello-world.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
  labels:
    track: canary
spec:
  selector:
    matchLabels:
      any-name: my-app
  template:
    metadata:
      labels:
        any-name: my-app
    spec:
      containers:
      - name: cont1
        image: learnk8s/app:1.0.0
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    any-name: my-app

那deployment顶部的track: canary标签呢?

它也应该匹配吗?

该标签属于deployment,service的选择器未使用它来路由流量。

换句话说,你可以安全地删除它或为其分配其他值。

matchLabels选择器呢?

它必须始终与Pod的标签匹配,并且被Deployment用来跟踪Pod。

假设你已经进行了所有正确的设置,该如何测试它呢?

你可以使用以下命令检查Pod是否具有正确的标签:

$ kubectl get pods --show-labels

或者,如果你拥有属于多个应用程序的Pod:

$ kubectl get pods --selector any-name=my-app --show-labels

any-name=my-app就是标签:any-name: my-app

还有问题吗?

你也可以连接到Pod!

你可以使用kubectl中的port-forward命令连接到service并测试连接。

$ kubectl port-forward service/<service name> 3000:80
  • service/ 是服务的名称- 在上面的YAML中是“my-service”
  • 3000是你希望在计算机上打开的端口
  • 80是service通过port字段暴露的端口

如果可以连接,则说明设置正确。

如果不行,则很可能是你填写了错误的标签或端口不匹配。

二. 连接Service和Ingress

接下来是配置Ingress以将你的应用暴露到集群外部。

Ingress必须知道如何检索服务,然后检索Pod并将流量路由给它们。

Ingress按名字和暴露的端口检索正确的服务。

在Ingress和Service中应该匹配两件事:

  • Ingress的servicePort应该匹配service的port
  • Ingress的serviceName应该匹配服务的name

下面的图总结了如何连接端口:

img{512x368}

你已经知道servive暴露一个port

img{512x368}

Ingress有一个字段叫servicePort

img{512x368}

service的port和Ingress的service应该始终保持匹配

img{512x368}

如果你为service指定的port是80,那么你也应该将ingress的servicePort改为80

实践中,你应该查看以下几行(下面代码中的my-service和80):

// hello-world.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-service   --- 需关注
spec:
  ports:
  - port: 80       --- 需关注
    targetPort: 8080
  selector:
    any-name: my-app
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
  - http:
    paths:
    - backend:
        serviceName: my-service --- 需关注
        servicePort: 80 --- 需关注
      path: /

你如何测试Ingress是否正常工作呢?

你可以使用与以前相同的策略kubectl port-forward,但是这次你应该连接到Ingress控制器,而不是连接到Service。

首先,使用以下命令检索Ingress控制器的Pod名称:

$ kubectl get pods --all-namespaces
NAMESPACE   NAME                              READY STATUS
kube-system coredns-5644d7b6d9-jn7cq          1/1   Running
kube-system etcd-minikube                     1/1   Running
kube-system kube-apiserver-minikube           1/1   Running
kube-system kube-controller-manager-minikube  1/1   Running
kube-system kube-proxy-zvf2h                  1/1   Running
kube-system kube-scheduler-minikube           1/1   Running
kube-system nginx-ingress-controller-6fc5bcc  1/1   Running

标识Ingress Pod(可能在其他命名空间中)并描述它以检索端口:

$ kubectl describe pod nginx-ingress-controller-6fc5bcc \
 --namespace kube-system \
 | grep Ports
Ports:         80/TCP, 443/TCP, 18080/TCP

最后,连接到Pod:

$ kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system

此时,每次你访问计算机上的端口3000时,请求都会转发到Ingress控制器Pod上的端口80。

如果访问http://localhost:3000,则应找到提供网页服务的应用程序。

回顾Port

快速回顾一下哪些端口和标签应该匹配:

  • service selector应与Pod的标签匹配
  • service的targetPort应与Pod中容器的containerPort匹配
  • service的端口可以是任何数字。多个服务可以使用同一端口,因为它们分配了不同的IP地址。
  • ingress的servicePort应该匹配service的port
  • serivce的名称应与ingress中的serviceName字段匹配

知道如何构造YAML定义只是故事的一部分。

出了问题后该怎么办?

Pod可能无法启动,或者正在崩溃。

三. kubernetes deployment故障排除的3个步骤

在深入研究失败的deployment之前,我们必须对Kubernetes的工作原理有一个明确定义的思维模型。

由于每个deployment中都有三个组件,因此你应该自下而上依次调试所有组件。

  • 你应该先确保Pods正在运行
  • 然后,专注于让service将流量路由到到正确的Pod
  • 然后,检查是否正确配置了Ingress

img{512x368}

你应该从底部开始对deployment进行故障排除。首先,检查Pod是否已就绪并正在运行。

img{512x368}

如果Pod已就绪,则应调查service是否可以将流量分配给Pod。

img{512x368}

最后,你应该检查service与ingress之间的连接。

1. Pod故障排除

在大多数情况下,问题出在Pod本身。

你应该确保Pod正在运行并准备就绪。

该如何检查呢?

$ kubectl get pods
NAME                    READY STATUS            RESTARTS  AGE
app1                    0/1   ImagePullBackOff  0         47h
app2                    0/1   Error             0         47h
app3-76f9fcd46b-xbv4k   1/1   Running           1         47h

在上述会话中,最后一个Pod处于就绪并正常运行的状态;但是,前两个Pod既不处于Running也不是Ready。

你如何调查出了什么问题?

有四个有用的命令可以对Pod进行故障排除:

  • kubectl logs 有助于检索Pod容器的日志
  • kubectl describe pod 检索与Pod相关的事件列表很有用
  • kubectl get pod 用于提取存储在Kubernetes中的Pod的YAML定义
  • kubectl exec -ti bash 在Pod的一个容器中运行交互式命令很有用

应该使用哪一个呢?

没有一种万能的。

相反,我们应该结合着使用它们。

常见Pod错误

Pod可能会出现启动和运行时错误。

启动错误包括:

  • ImagePullBackoff
  • ImageInspectError
  • ErrImagePull
  • ErrImageNeverPull
  • RegistryUnavailable
  • InvalidImageName

运行时错误包括:

  • CrashLoopBackOff
  • RunContainerError
  • KillContainerError
  • VerifyNonRootError
  • RunInitContainerError
  • CreatePodSandboxError
  • ConfigPodSandboxError
  • KillPodSandboxError
  • SetupNetworkError
  • TeardownNetworkError

有些错误比其他错误更常见。

以下是最常见的错误列表以及如何修复它们的方法。

ImagePullBackOff

当Kubernetes无法获取到Pod中某个容器的镜像时,将出现此错误。

共有三个可能的原因:

  • 镜像名称无效-例如,你拼错了名称,或者image不存在
  • 你为image指定了不存在的标签
  • 你尝试检索的image属于一个私有registry,而Kubernetes没有凭据可以访问它

前两种情况可以通过更正image名称和标记来解决。

针对第三种情况,你应该将私有registry的访问凭证通过Secret添加到k8s中并在Pod中引用它。

官方文档中有一个有关如何实现此目标的示例

CrashLoopBackOff

如果容器无法启动,则Kubernetes将显示错误状态为:CrashLoopBackOff。

通常,在以下情况下容器无法启动:

  • 应用程序中存在错误,导致无法启动
  • 未正确配置容器
  • Liveness探针失败太多次

你应该尝试从该容器中检索日志以调查其失败的原因。

如果由于容器重新启动太快而看不到日志,则可以使用以下命令:

$ kubectl logs <pod-name> --previous

这个命令打印前一个容器的错误消息。

RunContainerError

当容器无法启动时,出现此错误。

甚至在容器内的应用程序启动之前。

该问题通常是由于配置错误,例如:

  • 挂载不存在的卷,例如ConfigMap或Secrets
  • 将只读卷安装为可读写

你应该使用kubectl describe pod 命令收集和分析错误。

处于Pending状态的Pod

当创建Pod时,该Pod保持Pending状态。

为什么?

假设你的调度程序组件运行良好,可能的原因如下:

  • 集群没有足够的资源(例如CPU和内存)来运行Pod
  • 当前的命名空间具有ResourceQuota对象,创建Pod将使命名空间超过配额
  • 该Pod绑定到一个处于pending状态的 PersistentVolumeClaim

最好的选择是检查kubectl describe命令输出的“事件”部分内容:

$ kubectl describe pod <pod name>

对于因ResourceQuotas而导致的错误,可以使用以下方法检查集群的日志:

$ kubectl get events --sort-by=.metadata.creationTimestamp

处于未就绪状态的Pod

如果Pod正在运行但未就绪(not ready),则表示readiness就绪探针失败。

当“就绪”探针失败时,Pod未连接到服务,并且没有流量转发到该实例。

就绪探针失败是应用程序的特定错误,因此你应检查kubectl describe中的“ 事件”部分以识别错误。

2. 服务的故障排除

如果你的Pod正在运行并处于就绪状态,但仍无法收到应用程序的响应,则应检查服务的配置是否正确。

service旨在根据流量的标签将流量路由到Pod。

因此,你应该检查的第一件事是服务关联了多少个Pod。

你可以通过检查服务中的端点(endpoint)来做到这一点:

$ kubectl describe service <service-name> | grep Endpoints

端点是一对,并且在服务(至少)以Pod为目标时,应该至少有一个端点。

如果“端点”部分为空,则有两种解释:

  • 你没有运行带有正确标签的Pod(提示:你应检查自己是否在正确的命名空间中)
  • service的selector标签上有错字

如果你看到端点列表,但仍然无法访问你的应用程序,则targetPort可能是你服务中的罪魁祸首。

你如何测试服务?

无论服务类型如何,你都可以使用kubectl port-forward来连接它:

$kubectl port-forward service/<service-name> 3000:80

这里:

  • 是服务的名称
  • 3000 是你希望在计算机上打开的端口
  • 80 是服务公开的端口

3.Ingress的故障排除

如果你已到达本节,则:

  • Pod正在运行并准备就绪
  • 服务会将流量分配到Pod

但是你仍然看不到应用程序的响应。

这意味着最有可能是Ingress配置错误。

由于正在使用的Ingress控制器是集群中的第三方组件,因此有不同的调试技术,具体取决于Ingress控制器的类型。

但是在深入研究Ingress专用工具之前,你可以用一些简单的方法进行检查。

Ingress使用serviceName和servicePort连接到服务。

你应该检查这些配置是否正确。

你可以通过下面命令检查Ingress配置是否正确:

$kubectl describe ingress <ingress-name>

如果backend一列为空,则配置中必然有一个错误。

如果你可以在“backend”列中看到端点,但是仍然无法访问该应用程序,则可能是以下问题:

  • 你如何将Ingress暴露于公共互联网
  • 你如何将集群暴露于公共互联网

你可以通过直接连接到Ingress Pod来将基础结构问题与Ingress隔离开。

首先,获取你的Ingress控制器Pod(可以位于其他名称空间中):

$ kubectl get pods --all-namespaces
NAMESPACE   NAME                              READY STATUS
kube-system coredns-5644d7b6d9-jn7cq          1/1   Running
kube-system etcd-minikube                     1/1   Running
kube-system kube-apiserver-minikube           1/1   Running
kube-system kube-controller-manager-minikube  1/1   Running
kube-system kube-proxy-zvf2h                  1/1   Running
kube-system kube-scheduler-minikube           1/1   Running
kube-system nginx-ingress-controller-6fc5bcc  1/1   Running

描述它以检索端口:

# kubectl describe pod nginx-ingress-controller-6fc5bcc
 --namespace kube-system \
 | grep Ports

最后,连接到Pod:

$ kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system

此时,每次你访问计算机上的端口3000时,请求都会转发到Pod上的端口80。

现在可以用吗?

  • 如果可行,则问题出在基础架构中。你应该调查流量如何路由到你的集群。
  • 如果不起作用,则问题出在Ingress控制器中。你应该调试Ingress。

如果仍然无法使Ingress控制器正常工作,则应开始对其进行调试。

目前有许多不同版本的Ingress控制器。

热门选项包括Nginx,HAProxy,Traefik等。

你应该查阅Ingress控制器的文档以查找故障排除指南。

由于Ingress Nginx是最受欢迎的Ingress控制器,因此在下一部分中我们将介绍一些有关调试ingress-nginx的技巧。

调试Ingress Nginx

Ingress-nginx项目有一个Kubectl的官方插件

你可以用kubectl ingress-nginx来:

  • 检查日志,后端,证书等。
  • 连接到ingress
  • 检查当前配置

你应该尝试的三个命令是:

  • kubectl ingress-nginx lint,它会检查 nginx.conf
  • kubectl ingress-nginx backend,以检查后端(类似于kubectl describe ingress
  • kubectl ingress-nginx logs,查看日志

请注意,你可能需要为Ingress控制器指定正确的名称空间–namespace

四. 总结

如果你不知道从哪里开始,那么在Kubernetes中进行故障排除可能是一项艰巨的任务。

你应该始终牢记从下至上解决问题:从Pod开始,然后通过Service和Ingress向上移动堆栈。

你在本文中了解到的调试技术也可以应用于其他对象,例如:

  • failing Job和CronJob
  • StatefulSets和DaemonSets

本文翻译自learnk8s上的文章A visual guide on troubleshooting Kubernetes deployments


我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网上线了,感谢小伙伴们学习支持!

我爱发短信:企业级短信平台定制开发专家 https://51smspush.com/
smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

微博:https://weibo.com/bigwhite20xx
微信公众号:iamtonybai
博客:tonybai.com
github: https://github.com/bigwhite

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

Go语言开源十周年

本文翻译自Go官方博客Russ Cox代表Go核心团队发表的“Go Turns 10″一文。

生日快乐,Go!

这个周末,我们庆祝Go正式对外发布10周年,即Go作为开源编程语言和构建现代网络软件生态系统的10周年诞辰。

为了纪念这一时刻,Go gopher的创建者Renee French(用下面的新作)描绘了这个令人愉快的场景:

img{512x368}

庆祝Go十周年让我回想起2009年11月上旬,那时我们正准备与世界分享Go。我们不知道会发生什么样的反应,是否有人会关心这种新生的小语言。我希望即使没有人最终使用Go,我们也至少会引起人们对一些好的想法的关注,尤其是Go的并发和接口,这些想法可能会影响后续语言

当看到人们对Go感到兴奋,我便查看了CC++、Perl、Python和Ruby等流行语言的历史,并研究了每种语言花了多长时间才被广泛采用。例如,在我看来,Perl在1990年代中后期就已经完全形成了,带有CGI脚本和Web,但它于1987年首次发布。这种模式在我所研究的几乎所有语言中都重现了:在新语言真正腾飞之前,需要大约十年的时间进行安静、稳定的改进和传播。

(当时的)我想知道:十年后的Go会在哪里?

今天,我们可以回答这个问题:Go无处不在,全世界至少有100万开发人员在使用它。

Go最初的目标是网络系统基础架构,现在我们称为云软件(cloud software)。如今,每个主要的云计算平台提供商都使用用Go语言编写的核心云基础架构,例如DockerEtcdhttps://etcd.io/,IstioKubernetesPrometheusTerraformCloud Native Computing Foundation的大多数项目都是用Go编写的。无数公司也在使用Go将自己的工作迁移到云上,从初创公司从头开始构建到大企业更新软件堆栈。Go还发现对其的采用已经远远超出了最初的云计算目标,其使用范围从使用GoBotTinyGo控制小型嵌入式系统到使用GRAIL进行大规模的大数据分析和机器学习进行癌症检测,以及介于两者之间的所有内容。

这一切都说明Go超越了我们最疯狂的梦想。Go的成功不仅仅在于语言。这是关于语言,生态系统,尤其是社区的共同努力。

在2009年,该语言是一个不错的主意,并带有一个实现的工作草图。那时候go命令还不存在:我们使用命令6g编译源码和6l链接二进制文件,并借助Makefile实现这个过程的自动化。我们在语句末尾键入分号。整个程序在垃圾回收期间停止,然后努力利用两个CPU核。当时Go只能在Linux和Mac,32位和64位x86和32位ARM上运行。

在过去的十年中,在世界各地的Go开发人员的帮助下,我们已经将这一想法和草图发展为拥有出色的工具,生产级质量实现,先进的垃圾收集器和得到广泛移植的高效语言,Go支持12种操作系统和10种CPU体系结构

任何编程语言都需要蓬勃发展的生态系统的支持。开源发布是该生态系统的种子,但是自那时以来,许多人贡献了自己的时间和才干,用出色的教程,书籍,课程,博客文章,播客,工具,集成以及可重复使用的、支持go get的Go包来填充Go生态系统。没有这个生态系统的支持,Go永远不可能成功。

当然,生态系统需要蓬勃发展的社区的支持。在2019年,全球有数十个Go(技术)会议,以及超过150个Go聚会组织和90000名参会人员GoBridgeGoing Who Go通过指导,培训和会议奖学金帮助将新的声音带入Go社区。仅今年一年,他们就在讲习班上向数百名来自传统团体的人们进行了培训,在这些讲习班上,社区成员教导和指导刚接触Go的人。

全球有超过一百万的Go开发人员,全球各地的公司都在寻求雇用更多的人。实际上,人们经常告诉我们,学习Go帮助他们获得了技术行业的第一份工作。最后,我们为Go感到最自豪的不是设计完善的功能或巧妙的代码,而是Go在这么多人的生活中产生的积极影响。我们旨在创建一种可以帮助我们成为更好的开发人员的语言,我们很高兴Go帮助了许多其他人。

恰逢Go开源十周年的时刻,我希望每个人都花一点时间来庆祝Go社区以及我们所取得的一切。我代表Google的整个Go团队,感谢过去十年来加入我们的每个人。让我们开启下一个更加不可思议的十年吧!

img{512x368}


我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网上线了,感谢小伙伴们学习支持!

我爱发短信:企业级短信平台定制开发专家 https://51smspush.com/
smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

微博:https://weibo.com/bigwhite20xx
微信公众号:iamtonybai
博客:tonybai.com
github: https://github.com/bigwhite

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! 图片广告位1 图片广告位2 图片广告位3 商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats