引言
这篇文章包含两部分: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中获得的是一个端口范围,因此每个可以用不同的端口,也必须用不同的端口。如下图所示:
由于一个Pod只有一个IP,当访问10.0.10.15:80时,Pod将会把流量路由到main container。
那么Pod间是如何通信的?
Pod之间通过Pod network 模型进行通信,Pod创建时获得的IP是可以在Pod network上路由的。通信示意图如下所示。
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 状态。 整个生命周期如下图所示。
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!”。如下图所示。
然后我们把这个服务做成一个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 就能访问这个服务了。其结果如下。
到目前为止我们创建了一个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
从结果中我们可以看到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端口。 这样就能够得到正确的结果了。
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,监听的端口等。
Comments are closed.