Pod是Kubernetes项目里定义的最小可调度单元,是Kubernetes对应用程序的抽象。在这篇文章里我将会介绍Kubernetes里Pod的基本概念,使用方式,生命周期以及如何使用Pod部署应用。读这篇文章的朋友我会默认你已经了解Kubernete是用来解决什么问题的,以及电脑上已经安装了Minikube这个能试验Kubernetes功能的工具。如果尚未做好这些准备工作,推荐先去看下面的两篇文章做好准备工作后再来学习这里的内容。
什么是Pod
在Kubernetes的API对象模型中,Pod是最小的API对象,换一个专业点的的说法可以这样描述:Pod,是 Kubernetes 的原子调度单位。在集群中,Pod表示正在运行的应用进程。Pod的内部可以有一个或多个容器,同属一个Pod的容器将会共享:
网络资源
相同的IP
存储
应用到Pod上的自定义配置
可以看到Pod是Kubernetes定义出来的一个逻辑概念,可以用另外一种方式来理解Pod:一种特定于应用程序的“逻辑主机”,其中包含一个或多个紧密协作的容器。例如,假设我们在Pod中有一个应用程序容器和一个日志记录容器。日志记录容器的唯一工作是从应用程序容器中提取日志。将两个容器放置同一个Pod里可消除额外的通信时间,因为它们位于同一个"主机",因此所有内容都是本地的并且它们共享所有资源,就跟在同一台物理服务器上执行这些操作一样。
此外也不是所有有“关联”的容器都属于同一个Pod。比如,应用容器和数据库虽然会发生访问关系,但并没有必要、也不应该部署在同一台机器上,它们更适合做成两个Pod。
Pod的模型
根据Pod里的容器数量可以将Pod分为两种类型:
单容器模型。由于
Pod是Kubernetes可识别的最小对象,Kubernetes管理调度Pod而不是直接管理容器,所以即使只有一个容器也需要封装到Pod里。多容器模型。在这个模型中,
Pod可以容纳多个紧密关联的容器以共享Pod里的资源。这些容器作为单一的,凝聚在一起的服务单元工作。
每个Pod运行应用程序的单个实例。如果需要水平扩展/缩放应用程序(例如运行多个副本),则可以为每个实例使用一个Pod。这与在单个Pod中运行同一应用程序的多个容器不同。
还需要提的一点是,Pod本身不具有调度功能。如果所在的节点发生故障或者你要维护节点,则Pod是不会自动调度到其他节点了。Kubernetes用一系列控制器来解决Pod的调度问题,Deployment就是最基础的控制器。通常我们都是在定义的控制器的配置里通过PodTemplate定义要控制的Pod,让控制器和所管控的Pod一起被创建出来(这部分内容后面单独写文章讨论)。
Pod生命周期的阶段
一个Pod的状态会告诉我们它当前正处于生命周期的哪个阶段,Pod的生命周期有5个阶段:
Pending:等待状态表明至少有一个
Pod内的容器尚未创建。Running:所有容器已经创建完成,并且
Pod已经被调度到了一个Node上。此时Pod内的容器正在运行,或者正在启动或重新启动。Succeeded:
Pod中的所有容器均已成功终止,并且不会重新启动。Faild: 所有容器均已终止,至少有一个容器发生了故障。失败的容器以非零状态退出。
Unknown:无法获得
Pod的状态。
在实践中使用Pod
我们已经讨论了Pod在理论上的含义,现在让我们看看它在实际中长什么样。我们将首先浏览一个简单的Pod定义YAML文件,然后部署一个示例应用程序来展示如何使用它。
Pod的YAML文件
Kubernetes里所有的API对象都由四部分组成:
apiVersion -- 当前使用的
Kubernetes的API版本。kind -- 你想创建的对象的种类。
metadata -- 元数据,用于唯一表示当前的对象,比如name、namespace等。
spec -- 我们的
Pod的指定配置,例如镜像名称,容器名称,数据卷等。
apiVersion,kind和metadata是必填字段,适用于所有Kubernetes对象,而不仅仅是pod。spec里指定的内容(spec也是必需字段)会因对象而异。下面的示例显示了Pod的YAML文件大概长什么样子。
apiVersion: "api version"
kind: "object to create"
metadata:
name: "Pod name"
labels:
app: "label value"
spec:
containers:
- name: "container name"
image: "image to use for container"
关于YAML的语法可以参考前面的文章:YAML,另一种标记语言?不止是标记语言!
理解了Pod配置文件的模板后,接下来我们看看如何使用配置文件创建上面说的两种模型的Pod。
单容器Pod
下面的pod-1.yaml是个单容器Pod的清单文件。它会运行一个Nginx容器。
apiVersion: v1
kind: Pod
metadata:
name: first-pod
labels:
app: myapp
spec:
containers:
- name: my-first-pod
image: nginx
接下来,我们通过运行Kubectl create -f pod-1.yaml将清单文件部署到本地的Kubernetes集群中。然后,我们运行kubectl get pods以确认我们的Pod运行正常。
kubectl get pods
NAME READY STATUS RESTARTS AGE
first-pod 1/1 Running 0 45s
可以到Pod的Nginx容器里执行以下service nginx status命令确保Nginx在正常运行。
kubectl exec first-pod -- service nginx status
nginx is running.
这会在Pod里执行service nginx status指令,类似docker exec命令。
现在,我们通过运行kubectl delete pod first-pod删除刚才创建的Pod。
kubectl delete pod first-pod
pod "firstpod" deleted
多容器Pod
下面我们将部署一个更复杂的Pod:一个拥有两个容器的Pod,这些容器相互协作作为一个实体工作。其中一个容器每10秒将当前日期写入一个文件,而另一个Nginx容器则为我们展示这些日志。这个Pod的YAML如下:
apiVersion: v1
kind: Pod
metadata:
name: multi-container-pod # pod的名称
spec:
volumes:
- name: shared-date-logs # 为Pod里的容器创建一个共享数据卷
emptyDir: {}
containers:
- name: container-writing-dates # 第一个容器的名称
image: alpine # 容器的镜像
command: ["/bin/sh"]
args: ["-c", "while true; do date >> /var/log/output.txt; sleep 10;done"] # 每10秒写入当前时间
volumeMounts:
- name: shared-date-logs
mountPath: /var/log # 将数据卷挂在到容器的/var/log目录
- name: container-serving-dates # 第二个容器的名字
image: nginx:1.7.9 # 容器的镜像
ports:
- containerPort: 80 # 定义容器提供服务的端口
volumeMounts:
- name: shared-date-logs
mountPath: /usr/share/nginx/html # 将数据卷挂载到容器的/usr/share/nginx/html
上面通过volumes指令定义了Pod内的数据卷
volumes:
- name: shared-date-logs # 为Pod里的容器创建一个数据卷
emptyDir: {}
第一个容器将数据卷挂载到了/var/log/每隔10秒往output.txt文件里写入时间,而第二个容器通过将数据卷挂载到/usr/share/nginx/html伺服了这个日志文件。
执行kubectl create -f pod-2.yaml创建这个多容器Pod:
kubectl create -f pod-2.yaml
pod "multi-container-pod" created
然后确保Pod已经正确部署:
kubectl get pods
NAME READY STATUS RESTARTS AGE
multi-container-pod 2/2 Running 0 1m
通过运行kubectl describe pod podName,查看Pod的详细信息,里面会包含两个容器的信息。(下面的内容只截取了容器相关的信息)
Containers:
container-writing-dates:
Container ID: docker://e5274fb901cf276ed5d94b...
Image: alpine
Image ID: docker-pullable://alpine@sha256:621c2f39...
Port:
Host Port:
Command:
/bin/sh
Args:
-c
while true; do date >> /var/log/output.txt; sleep 10;done
State: Running
Started: Sat, 1 Aug 2020 11:31:44 +0800
Ready: True
Restart Count: 0
Environment:
Mounts:
/var/log from shared-date-logs (rw)
/var/run/secrets/Kubernetes.io/serviceaccount from default-token-8dl5j (ro)
container-serving-dates:
Container ID: docker://f9c85f3fe3...
Image: nginx:1.7.9
Image ID: docker-pullable://nginx@sha256:e3456c851...
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Sat, 1 Aug 2020 11:31:44 +0800
Ready: True
Restart Count: 0
Environment:
Mounts:
/usr/share/nginx/html from shared-date-logs (rw)
/var/run/secrets/Kubernetes.io/serviceaccount from default-token-8dl5j (ro)
两个容器都在运行,下面我们将进到Pod里确保两个容器都在执行分配的作业。
通过运行kubectl exec -it multi-container-pod -c container-serving-dates -- bash连接到Nginx容器里。
在容器内运行curl'http://localhost:80/output.txt',它应该返回时间日志文件的内容给我们。(如果容器中未安装curl,请先运行apt-get update && apt-get install curl,然后再次运行curl'http://localhost:80/output.txt'。)
curl 'http://localhost:80/app.txt'
Sat Aug 1 11:31:44 CST 2020
Sat Aug 1 11:31:54 CST 2020
Sat Aug 1 11:32:04 CST 2020
SideCar模式
除了上面说的那些之外,我们可以在一个Pod中按照顺序启动一个或多个辅助容器,来完成一些独立于主进程(主容器)之外的工作,完成工作后这些辅助容器会依次退出,之后主容器才会启动,这种容器设计模式叫做sidecar。
比如对于前端Web应用,如果把构建后的Js项目放到Nginx镜像的/usr/share/nginx/html目录下,Nginx和Js应用做成一个镜像运行容器,每次应用有更新或者Nginx要做升级、更新配置操作都需要重新做一个镜像,非常麻烦。
有了Pod之后,这样的问题就很容易解决了。我们可以把前端Web应用和Nginx分别做成镜像,然后把它们作为一个Pod里的两个容器"组合"在一起。这个Pod的配置文件如下所示:
apiVersion: v1
kind: Pod
metadata:
name: web-2
spec:
initContainers:
- image: kevinyan/front-app:v2
name: front
command: ["cp", "/www/application/*", "/app"]
volumeMounts:
- mountPath: /app
name: app-volume
containers:
- image: nginx:1.7.9
name: nginx
ports:
- containerPort: 80 # 定义容器提供服务的端口
volumeMounts:
- mountPath: /usr/share/nginx/html
name: app-volume
volumes:
- name: app-volume
emptyDir: {}
所有spec.initContainers定义的容器,都会比spec.containers定义的用户容器先启动。并且,Init容器会按顺序逐一启动,直到它们都启动并且退出了,用户容器才会启动。所以,这个Init类型的容器启动后,执行了一句"cp /www/application/* /app",把应用包拷贝到"/app"目录下,然后退出。这个"/app"目录,挂载了一个名叫app-volume 的Volume。接下来Nginx容器,同样声明了挂载app-volume到自己的"/usr/share/nginx/html"目录下。由于这个Volume 是被Pod里的容器共享的所以等Nginx容器启动时,它的目录下就一定会存在前端项目的文件。这个文件正是上面的Init容器启动时拷贝到Volume里面的。
这就是容器设计模式里最常用的一种模式:sidecar。顾名思义,sidecar指的就是我们可以在一个Pod中,启动一个辅助容器,来完成一些独立于主进程(主容器)之外的工作。
总结
Pod把多个紧密关联的容器组织在一起,让他们共享自己的资源,这点有些像是这些容器的"主机",只不过这个"主机"是个逻辑概念。当你需要把一个运行在虚拟机里的应用迁移到容器中时,一定要仔细分析到底有哪些进程(组件)运行在这个虚拟机里。然后,你就可以把整个虚拟机想象成为一个 Pod,把这些进程分别做成容器镜像,把有顺序关系的容器,定义为 Init Container。这才是更加合理的、松耦合的容器编排诀窍,也是从传统应用架构,到“微服务架构”最自然的过渡方式。
最后关于Docker In Docker这种把整个应用塞到一个容器里的方法的弊端请查看之前的文章:Docker容器的"单进程模型"。
- END -
关注公众号,获取更多精选技术原创文章

本文深入讲解Kubernetes中Pod的概念,包括其基本原理、使用方式、生命周期及如何部署应用。Pod作为Kubernetes的最小调度单元,可包含多个紧密关联的容器,共享资源如网络和存储。
929

被折叠的 条评论
为什么被折叠?



