Skip to content

Kubernetes(二)–Pods是如何工作的?

引言

这篇文章包含两部分:Pod相关的理论基础和实际操作。

Pod的理论基础

在以虚拟机为基础的虚拟化时代,只能通过以虚拟机为原子单元来部署应用,也就是说应用跑在虚拟机上。 后来有了Docker,部署应用的最小单元就变成了容器, 通过管理容器来管理应用。
就像虚拟机对于VMWare, 容器对于Docker一样,Pod是kubernetes 中用来部署应用的原子单元,就是说应用只能跑在Pod里。

Pods和containers的区别

上一篇说过Po是容器的宿主,更深层次地说Pod是一个可以被一个或多个容器共享的执行环境。
最简单也最常见的模型是一个Pod 只包含一个容器,但是Pod也支持多个容器。
什么情况下才会出现一个Pod包含多个容器?

  • 从应用的角度来分析, 应用中有紧密耦合的需要共同调度的子服务模块。两个自服务模块运行在各自的container中,但是它们需要共享内存,如果把它们调度到2个不同的nodes上,它们就无法工作了。 这里引申出一个结论,那就是Pod无法跨Nodes。 只有通过把这两个容器放在一个Pod里,才能确保它们能被调度到同一个Node上,并共享执行环境: 包括共享内存,网络,物理存储等。

  • 从架构的角度来看, Service Mesh 需要在一个Pod中包含多个容器,因为在Service Mesh的模型中,每一个应用的Pod中都需要有个代理容器,这个代理容器可以实现网络加密,网络检测,路由等。 具体细节需要参考Service Mesh模型。(PS:我也是刚了解一点)。就不再展开了。

Pod解析

从理论角度讲,Pod对于container来说是一个可共享的执行环境,包含了可共享的资源:IP, 端口,主机名,网络套接字,内存,物理存储等等。
具体来说,如果在Kubernetes 如果使用Docker作为容器运行时, Pod就是一个特殊的容器叫 pause container 。本质上来讲,Pause container也是包含了普通容器可以共享的系统资源的一个集合。 这些系统资源是内核命名空间包括: 网络命名空间(IP,Port, Route table etc), UTS命名空间(Hostname), IPC 命名空间(Sockets)。

Pods和共享网络(shared networking)

当一个Pod被创建时,Pod会拥有属于自己的网络命名空间:包括一个IP,一段TCP/UDP 端口范围,和一个单独的路由表。当一个Pod只包含一个容器时,这个容器拥有所有的资源。当包含多个容器时,容器共享这个资源,Pod中获得的是一个端口范围,因此每个可以用不同的端口,也必须用不同的端口。如下图所示:
PodwithPort
由于一个Pod只有一个IP,当访问10.0.10.15:80时,Pod将会把流量路由到main container。

那么Pod间是如何通信的?
Pod之间通过Pod network 模型进行通信,Pod创建时获得的IP是可以在Pod network上路由的。通信示意图如下所示。
PodNetwork

Pods和cgroups

cgroups(Control Groups)是linux 内核技术,可以阻止单个容器消耗掉所有可用的CPU,RAM和IOPS. Pod中的每一个容器都可以有自己的cgroup 限制。

Pods部署的原子性

部署Pod是原子操作,这意味着Pod中的所有容器必须同时部署成功,且Pod中的容器都会被调度到一个节点上。

Pods的生命周期

Pod的生命周期从定义YAML形式的manifest文件并发送给API server开始, API Server会把manifest的内容存在cluster store作为一个预期的状态。 之后Pod被调度到一个拥有足够资源的健康node, 一旦它被调度到一个具体的node, 这个Pod就进入到了Pending 状态,当Pod中的所有服务都能提供服务之后,它就进入了Running 状态,当Pod结束所有任务之后,Pod就会结束进入Succeeded 状态。 当一个Pod部署失败时,它会从Pending 状态进入Failed 状态。 整个生命周期如下图所示。
PodLifeCycle

Pods 特性总结

  • Pod 是Kubernetes调度的原子单元。
  • Pod可以包含多个容器。
  • Pod只能被调度到单个Node上,Pod不能跨Node
  • Pod通过manifest文件被声明,被发送到API Server后被Scheduler调度到node上。
  • 通常我们通过更高层次的控制器部署Pod比如Deployments, DaemonSets。

动手实践

这部分我们会用Docker 在本机创建一个Kubernetes cluster,并部署一个Pod,这个Pod运行一个简单的web 服务。我们本地有个服务访问localhost:8000会返回”Hello Kubernetes Pod!”。如下图所示。
8000localservice
然后我们把这个服务做成一个image, 先创建一个hello-pod.dockerfile,其内容如下:

FROM python:latest
COPY ./index.html /
CMD python -m http.server

执行命令

docker build -t backendsite/hello-pod:1.0 -f .\hello-pod.dockerfile .

这样就会得到一个image。接下来我们运行这个image。

docker run -p 8080:8000 backendsite/hello-pod:1.0

然后我们访问localhost:8080 就能访问这个服务了。其结果如下。
8080

到目前为止我们创建了一个image,并且基于docker 以容器的方式运行它,测试显示这个image是可以工作的。以上这些都还是Docker的概念
现在我们要基于这个image,创建一个Pod。 先创建一个hello-pod.manifest.yaml 文件
其内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: hello-pod
  labels:
    zone: prod
    version: v1
spec:
  containers:
  - name: hello-ctr
    image: backendsite/hello-pod:1.0
    ports:
    - containerPort: 8000 # 这里是container中application 监听的端口,我们这里是8000

然后我们部署这个Pod

kubectl apply -f hello-pod.manifest.yaml

然后查看Pod是否已经是Running State

kubectl get pods

PodsStatus
从结果中我们可以看到Pod已经是Running 状态。也就是说我们这个web service是应该可以被访问的。
那么如何访问部署在Pod中的web应用呢?
我做了以下两种尝试:
(a)仍然访问localhost:8000, 但是发现并不能到找到指定的页面
(b) 在manifest 中定义 一个hostPort:8090。

...
spec:
  containers:
  - name: hello-ctr
    image: backendsite/hello-pod:1.0
    ports:
    - containerPort: 8000
      hostPort:8090

然后访问localhost:8090,发现仍然不能访问。

后来了解到containerPort还是host port只是kubernetes 这个cluster内部可见的端口。
当我们在本地电脑上访问localhost时并不会把请求路由到kubernetes cluster中。
为了能够访问localhost 进而访问Pod中的应用。我们需要执行

kubectl port-forward hello-pod 8090:8000

把本机8090的请求路由到pod的8000端口。 这样就能够得到正确的结果了。
8090result

kubernetes的manifest 分为4 个部分

  • apiVersion
    apiVersion 的完整格式是 /。当api-group是core group时,我们可以省略api-group。
    Pods/Deployments/Services 全部定义在core group下,所以我们把api-group省略了。

  • kind
    这个字段表示kubernetes正在部署的类型,这里我们部署一个Pod

  • metadata
    metadata 指定了名字和标签
  • spec
    spec中定义了Pod中包含的container。包括container的名字,使用的image,监听的端口等。
Published inKubernetes

Comments are closed.

Author Copyriht by BackendSite