深入理解POD的工作原理(中)

我们在部署第一个SpringCloud应用到Kubernetes集群的时候,使用的是kubectl create命令,但是如你所了解的,其实大部分情况下,我们部署应用到Kubernetes集群都是基于YAML或者JSON文件,今天这篇文章,我们来看看如何通过YAML的方式来部署应用到Kubernetes集群。

注:具体选择YAML还是JSON完全取决于组织和个人的喜好,基于笔者的实际经验,大部分团队会选择YAML,主要原因是YAML文件更符合人的认知习惯,并且我们可以像写代码那样,给YAML文件增加注释,可以帮助其他人理解YAML文件配置的具体目的和含义。

通过YAML文件来定义应用的部署结构,我们不需要编写复杂和容易出错的脚本,并且YAML文件可以放到源代码管理系统中,我们随时都可以通过YAML文件来重新构建应用的运行环境,并且还可以进行版本回溯。这种部署模式可以说解决了运维团队多年的痛点,我们终于可以像管理源代码那样,管理系统的运行环境。我们先从创建第一个YAML文件来部署我们的k8ssamples应用开始。

【通过YAML文件来在Kuberntes集群上部署应用】

为了部署k8ssample应用程序到Kubernetes平台,我们首先需要创建一个叫yunpan.yaml的文本文件,文件的内容如下图所示:

《图1.1 第一个YAML文件来部署SpringCloud应用》

从上图所示的YAML文件中可以看出,我们使用v1版本API来定义对象,并且对象的类型是POD,新创建POD的名称是yunpan,我们使用笔者在之前文章中开发的SpringCloud镜像来启动容器,以及使用的端口号为8085。

上边的这个YAML文件其实并不复杂,可以说很简单,你是不是会说这个太容易了,原来POD对象真没简单啊。其实这只是假象了,当我们把这个YAML文件推到API Server后,会创建具体的POD实例,如果我们再把POD实例的对象读取出来,你会发现属性值增加了10倍有余,感谢Kubernetes提供的这种默认配置的功能,让我们只需要关心和默认值不一致的属性,减轻了部署的负担。

接下来,我们就基于这个YAML文件来创建我们的应用程序,我们可以通过kubectl提供的apply命令来将应用程序部署到集群中,在自己的集群上运行kubectl apply -f yunpan.yaml,稍等片刻,影后用就会被成功部署。

kubectl apply命令可以被用来创建新的对象,也可以用来修改已有的对象,如果我们想对刚刚部署的应用进行版本更新,比如从v1.1回退到v1.0,那么我们就可以直接修改YAML文件,将版本号从v1.1更新回v1.0,然后重新运行kubectl apply命令,你会发现pod中运行的应用程序版本就回退了。POD已经成功运行起来,我们可以通过kubectl get pod yunpan -o yaml来读取对象的属性信息。

好了,我们的应用通过YAML文件部署成功了,接下来我们来访问一下这个应用,看看是否部署成功了,服务是否可以对外提供对应的能力。在前边的文章中,为了访问POD提供的接口能力,我们需要kubectl expose来创建一个Load balancer类型的Service,我们通过这个Service来访问POD提供的服务能力。不过这次我们换一种方式,特别是对于处于开发和测试阶段的服务,创建这些重量级的对象,并且大部分时间都处于闲置状态肯定不划算。

我们通过前边关于Kubernetes架构的介绍,知道每个POD其实都有IP地址,但是这个IP地址属于集群内部的地址,外部无法直接访问。因此我们必须通过其他方法来访问POD提供的服务,笔者会在这篇文章中介绍5中不同的方法,请大家继续阅读。

【第一种方法 - 通过POD的IP地址来访问】

我们可以通过获取到POD的完整YAML文件来找到podIP属性,或者通过kubectl get pod yunpan -o wide来找到pod的IP地址,以下输出是笔者在本地Kubernetes集群上运行命令的输出:

➜  kubernetes kubectl get pod yunpan -o wide

NAME    READY  STATUS    RESTARTS  AGE  IP          NODE      NOMINATED NODE  READINESS GATES

yunpan  1/1    Running  0          19s  172.17.0.8  minikube  <none>          <none>

有了IP地址后,我们还需要端口号,如果这个应用不是我部署的,其实我很难知道应用具体在哪个端口上监听请求,有些同学说你可以查看Dockerfile文件,我们在构建镜像的时候会指定端口号,但是这并不总是有效,因为如果应用不是我构建的,我可能并没有Dockerfile文件。

那么我们如何获取到应用的端口号呢?一个最佳实践就是在POD的定义YAML文件中,指定端口号,虽然这不是必须的,但是这种显式的声明会让适用者更容易调用我们提供的服务。

有了POD的IP地址和端口号,接下来我们就可以从工作节点上访问POD提供的服务了。还记得笔者前边的文章,Kubernetes的网络模型决定了任何部署在集群中的POD都可以从任意一台工作节点访问。因此只要我们能登陆到工作节点上,我们就可以从工作节点上访问POD的服务。如下是在笔者的macOS上登陆到minikube然后基于IP地址和端口号来访问服务:

➜  kubernetes minikube ssh

I0902 18:43:53.614308  65300 translate.go:65] Getting system locale failed: Could not detect Language

docker@minikube:~$ curl http://172.17.0.8:8085/v1/yunpan/k8s/hello

被访问机器的Host:yunpan  ,IP地址是:172.17.0.8

通常情况下我们不会使用这种方式来访问POD提供的服务,但是在一些极端情况下,特别是排除因为网络因素造成的服务无法访问的时候,这是一种最有效的方式。笔者目前在一家头部的航空企业做技术架构工作,最近就遇到了K8S集群的网络问题,我们无法知道到底是因为网络不同还是服务没有正常对外提供能力,我们通过直接登录到工作节点上,最后发现是网络的问题,和基础设施团队沟通后,问题很快就得到了解决。

因此,通过首先ssh到工作节点上,然后从工作节点上来访问POD中部署的服务,这是一种最短的访问路劲,可以直接验证我们的服务是否健康,是否正常对外提供服务,排除了很多其他的干扰因素。接着,我们来看第二种方法。

【第二种方法 - 通过运行一个一次性的客户端POD】

第二种方案是通过在另外一个POD中运行curl命令来访问我们的服务提供的能力,通过这种方式,我们可以测试POD是否能够被正常访问。因为有些情况下,即便是网络没有任何问题,但是POD自己因为代码的缺陷,hang住了,无法对请求作出及时的响应,笔者会在后续的文章中,介绍如何限制某个pod只能访问被允许的pod。

为了启动一次性的测试pod,需要运行如下的命令:kubectl run --image=tutum/curl -it --restart=Never --rm yunpan-pod,这个命令会基于tutum/curl镜像来创建一个叫yunpan-pod的pod对象,其中--rm参数告诉Kuernetes当POD运行完成后,将资源进行清理,通过这种方式来访问pod在测试pod到pod连通性的场景下会非常有用。

【第三种方法 - 通过kubectl port forward机制】

在开发和调试阶段,最简单和有效的访问POD提供的服务的方法就是使用kubectl port-forward命令,让我们可以通过一个代理对象来访问目标POD的服务,具体的访问机制如下图所示:

《图1.2 通过kubectl port-forward访问容器服务》

如上图所示,为了能够访问到POD中部署的容器服务,我们需要开通访问链路,通过执行kubectl port-forward yunpan 8085这个命令,我们就把本地8085端口上接受到的请求,通过代理对象发送给容器提供的服务。如下是笔者在自己的macOS上运行的结果:

➜  kubernetes kubectl port-forward yunpan 8085

Forwarding from 127.0.0.1:8085 -> 8085

Forwarding from [::1]:8085 -> 8085

Handling connection for 8085

➜  ~ curl http://localhost:8085/v1/yunpan/k8s/hello

被访问机器的Host:yunpan  ,IP地址是:172.17.0.8%

如上边的输出显示,curl命令会连接到本地本地代理,本地代理会将从容器服务返回的信息响应给curl命令,完成调用。虽然说port-forward这种方式是最简单的在本地调试和访问容器服务的方案,但是这也是最复杂的一种方式,如果你打开盖子来看背后发生的一切。

请求会经过多个组件,如果有在这个路径上的任何一个组件不按预期工作,那么你就访问不到对应的服务,即便是POD本身正在健康的运行。下图是当我们curl目标服务的时候,数据包的流动示意图:

《图1.3 通过port-forward机制来访问容器服务的数据包流动路径》

如上图所示,curl命令首先连接到代理(proxy)对象,通过代理对象连接到API Server,然后连接到工作节点的kubelet组件,kubelet通过pod的回路地址(localhost地址)连接到目标容器,访问对应的服务。光看这个图,你应该和我一样都认可,这个访问链路实在是太长了。好了, 三种容器服务的访问方法就介绍完了,接下来我们看看如何访问容器的日志。

【查看应用程序的日志】

通常情况下,运行在容器中的应用会将日志输出到stdout标准输出,以及将错误信息写到stderr,而容器运行时负责读取这些日志的输入,并且将日志信息写到一个统一的位置:/var/log/containers目录,并且提供对这些日志的访问。如果我们是通过Docker来运行容器,那么我们可以通过docker logs 容器的ID来访问应用的日志。

在Kuberntes平台上,我们也可以登录到宿主机上,通过docker logs来访问日志,但是Kubernetes提供了更加便利的方法,通过Kubernetes logs命令来访问日志。我们可以通过kubectl logs yunpan来访问pod的日志日志输出。特别是如果我们想持续的输出访问日志,可以使用命令:kubectl logs kubia -f,这样日志信息会持续不断的输出。

有些时候,我们需要给运行的容器中拷贝一些数据,或者把容器中的数据拷贝出来,虽然说修改容器的数据不应该是常态,但是起码在开发和调试阶段,这仍然是一种有用的技能。Kubernetes提供了cp命令来把文件从本地拷贝到容器中,或者从容器拷贝到本地。假设我们要从容器中将/etc/hosts文件拷贝到本地的/tmp目录中,我们可以使用命令:kubectl cp yunpan:/etc/hosts /tmp/yunpan-hosts。相应的如果我们要从本地拷贝文件到,使用命令:kubectl cp /path/to/local/file yunpan:path/in/container。kubectl cp命令需要容器中有tar工具,这个大家需要注意。

【在容器中运行工具】

有时候我们在调试应用程序的时候,需要分析应用运行的环境,这个环境是从容器进程的视角来观察,Kubernetes提供了在容器中运行任何二进制可执行程序的功能,我们可以使用kubectl exec命令。

举个例子,如果我们要罗列yunpan这个pod中所有当前目录的文件和文件夹,我们可以使用命令:kubectl exec yunpan -- ls,在笔者本地的macOS上输出如下:

➜  kubernetes kubectl exec yunpan -- ls

app

bin

boot

dev

etc

home

lib

lib64

(省略若干)

通过这种方式,我们就可以不用登陆到POD而远程运行命令。我们还可以直接指定要执行的命令,来访问容器中的服务:kubectl exec yunpan -- curl -s localhost:8085/v1/yunpan/k8s/hello,前边这个命令能够成功运行的基础是容器的镜像中必须安装了curl工具。好了,今天这篇文章的内容就这么多了,下篇文章笔者会介绍如何在一个POD中部署的多个容器实例,敬请期待!

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容