05 K8s最小部署资源对象Pod
你好,我是雪飞。
上一课我们学习了 YAML 文件的相关知识,同时动手编写了 YAML 文件,现在你就可以使用 YAML 文件来与 K8s 集群沟通,从而实现应用部署和集群管理的需求。在接下来的课程中,我将继续深入介绍 K8s 中常用的资源对象,这些资源对象提供了应用部署、参数配置、网络访问、持久化存储、安全策略等等功能,满足各种实际的应用场景。对于 K8s 资源对象的管理,我会同时使用 kubectl 命令和 YAML 文件这两种方式来加深你的理解和记忆。
今天,我给你讲一下 K8s 中最核心的资源对象——Pod,它是 K8s 管理的最小可部署资源对象。其他很多资源对象都是基于 Pod 来实现更复杂的应用部署和管理。例如,Deployment 就是通过管理多个 Pod 来实现应用的高可用性和滚动更新。接下来,让我们详细了解一下什么是 Pod。
认识 Pod
Pod 的中文意思为豌豆荚,因为 Pod 中包含了一个或多个容器,容器和 Pod 的关系很像豌豆和豆荚的关系,所以这就是 Pod 名称的来历。在 K8s 中,Pod 是一个逻辑概念,而容器才是运行着应用镜像的实际载体,那为啥 K8s 不直接管理容器,而是还要搞出一个 Pod 呢?
最重要的原因就是:在一些常见的应用场景下,容器并不是孤零零的存在,它可能会跟其他容器之间有着很亲密的关系,它们相互协作,一起启动,一起停止,一起提供服务,同时它们需要彼此间可以方便的通信以及访问相同的数据。所以 K8s 就在容器的基础上创建了 Pod 这个更高层级的资源对象,Pod 可以将多个容器组合到一起部署和管理,同一 Pod 中的容器可以共享网络和存储空间,这种机制保证了对容器管理的一致性和灵活性。
举个具体例子, Pod 中多容器常用于边车模式(Sidecar),这种模式包含主容器和辅助容器,主容器运行核心业务应用,而辅助容器则负责日志收集、监控等任务,在不影响主业务的情况下扩展了应用功能。这种关系类似三轮摩托车,在摩托车的一侧加装一个边车来增加额外的空间,所以被叫做边车模式。此外,新一代 Service Mesh(服务网格)技术也利用了这种边车模式来增强服务间的通信和管理能力。
在实际应用中,Pod 可以包含一个容器也可以包含多个容器,非常灵活。对于多个容器,Pod 实现了容器间共享网络和存储空间的机制,下面我们来看下 Pod 是如何实现这两种机制。
共享网络
首先,让我们讨论一下 Pod 中容器共享网络的机制。K8s 使用了一种简单的方式来实现共享网络,那就是在 Pod 中的容器启动之前,先自动运行一个 Infra Container(基础容器),这个容器运行了一个非常小的镜像,大概几百 KB,是一个 C 语言写的永远处于 pause(暂停)状态的容器,又叫 pause 容器。然后,当 Pod 中的其他容器启动时,它们都会加入到 Infra Container 的网络中,从而使用 Infra Container 的网络中的网络设备、IP 地址等网络相关的信息,这样就实现了容器之间共享网络。这有点像是一个电脑上同时有两个网络应用,它们都是用这个电脑的同一块网卡来处理网络请求,所以通过 localhost 也就能相互访问。
共享存储
接下来,我们再看一下 Pod 中多个容器共享存储,Pod 是通过多个容器挂载共同的数据卷 Volume(数据卷)来实现共享存储的。你可以把数据卷想象成一个共享网盘,Pod 中的容器都可以通过它来存取文件。例如之前说到的边车模式,主容器可以将日志文件写到共享存储空间,然后边车容器可以直接读取共享存储空间的文件来获取日志信息,从而实现业务功能和统计分析功能的分离,就像两个同事共用一个文件夹一样方便。这样,不仅能让各个容器各司其职,还能让整个系统更加灵活和易于管理。
部署一个多容器的 Pod
了解了 Pod 的共享原理,下面我们就动手部署一个多容器的 Pod,来验证一下容器间的共享网络和共享存储。部署多容器的 Pod 是 CKA 考试的核心考点,考试中需要用 YAML 文件的方式部署 Pod,所以你要了解这个 YAML 文件的写法。
首先,编写一个包含两个容器的 Pod 的 YAML 文件(two_container_pod.yaml),其中一个是 Web 服务应用 Nginx,另一个是 Busybox。Busybox 是一个工具镜像,其中包含了 Linux 系统命令行工具,在Busybox容器启动的时候执行了一条 sleep 休眠命令,用于保持容器持续运行。
注意: 课程中的例子我都放到了公开的 GitHub 仓库 中供你参考,你可以使用 Git 工具把仓库拉到本地查看示例。
我在 Pod 的 spec 属性里创建了一个名为 data 的本地临时 Volume(数据卷类型是 emptyDir),同时在 Pod 的每个容器中都挂载了这个 Volume。
# two_container_pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- image: nginx
name: nginx
volumeMounts: # 容器 Nginx 挂载 Volume
- name: data
mountPath: /data
- image: busybox
name: busybox
command: ["/bin/sh","-c","sleep 3600"]
volumeMounts: # 容器 Busybox 挂载 Volume
- name: data
mountPath: /data
volumes: # 定义了一个临时 Volume
- name: data
emptyDir: {}
然后运行以下命令部署 YAML 文件,等待 Pod 运行成功。
通过 “kubectl get pod -o wide” 命令,可以查看 Pod 部署的详细列表信息。此时,可以看到 Pod 已经成功部署到了 k8s-worker1 节点上。
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test 2/2 Running 0 92s 10.244.194.68 k8s-worker1 <none> <none>
1. 验证共享网络
我们远程登录到 k8s-worker1 节点上,并使用 “docker ps” 命令查看节点上正在运行的容器。通过这种方式,我们可以确认在 k8s-worker1 节点上运行着 pause 容器,也就是提供共享网络的 Infra Container。
[root@k8s-worker1 ~]# docker ps | grep test
aab5e75d353f busybox "/bin/sh -c 'sleep 3…" 5 minutes ago Up 5 minutes k8s_busybox_test_default_e20592c1-79fa-41f7-9f1a-f79e851db4dc_0
87a6c4de517d nginx "/docker-entrypoint.…" 6 minutes ago Up 5 minutes k8s_nginx_test_default_e20592c1-79fa-41f7-9f1a-f79e851db4dc_0
438f772319e5 registry.aliyuncs.com/google_containers/pause:3.9 "/pause" 6 minutes ago Up 6 minutes k8s_POD_test_default_e20592c1-79fa-41f7-9f1a-f79e851db4dc_0
回到 Master 节点上,我们使用 “kubectl exec -it” 命令进入 Busybox 容器内部。
- -it: 表示使用终端应用作为访问容器的标准输入。
- -c: 表示要进入的容器名称,如果 Pod 里只有一个容器,就不需要这个参数。
- -- sh: 表示进入容器之后打开 shell 终端应用。
Nginx 容器部署成功后,就会启动了一个 Web 服务,访问 Nginx 容器的 80 端口,就可以访问到它的默认 “index.html” 文件。所以我们在 Busybox 容器的终端应用中,使用 wget 命令来请求本地网络地址 Localhost 的 80 端口,这个命令通常用来远程获取文件,可以看到 wget 命令获取到了 Nginx 容器的 80 端口返回的 “index.html” 文件,这证明了两个容器共享同一个网络空间,并且可以通过 Localhost 地址互相通信。
[root@k8s-master ~]# kubectl exec -it test -c busybox -- sh
/ # wget localhost:80
Connecting to localhost:80 (127.0.0.1:80)
saving to 'index.html'
index.html 100% |****************************| 615 0:00:00 ETA
'index.html' saved
2. 验证共享存储
我们继续在 Busybox 容器内,进入到 “/data” 目录,这个就是容器挂载数据卷的目录,在这个目录内创建一个内容为 “Hello, I am Busybox” 的文本文件(hello.txt)。
然后退出 Busybox 容器。只需输入 exit 命令,就可以回到集群 Master 节点的终端。
这时,我们已经在共享存储空间中创建了一个文件,现在就要去 Nginx 容器中去看看是否能找到这个文件。使用 “kubectl exec -it” 命令进入到 Nginx 容器内部,然后通过 “cat” 命令直接查看 “/data/hello.txt” 文件内容。
[root@k8s-master ~]# kubectl exec -it test -c nginx -- sh
/ # cat /data/hello.txt
Hello, I am Busybox
此时可以看到,在 Nginx 容器挂载的 “/data” 目录下,已经可以读取到在 Busybox 容器中创建的 “/data/hello.txt” 文件内容了。这证明了同一个 Pod 下的多个容器通过挂载相同的数据卷 Volume 实现了共享存储空间。
使用 kubectl 命令管理 Pod
刚才我们用 YAML 文件的方式来创建了多容器的 Pod,然而通过 kubectl 命令是不能直接创建多容器的 Pod,它只能创建单容器的简单 Pod,所以我们也可以看出使用 kubectl 命令与 YAML 文件的一个区别,YAML 文件更适合实现复杂的部署要求。
现在我们就来学习如何使用 Kubectl 命令来管理 Pod。
创建 Pod
使用 “kubectl run” 命令可以快速创建一个简单的 Pod,命令后面可以根据不同的容器镜像应用类型而增加不同参数。
:这是新创建的 Pod 的名称。 - –image=nginx:指定 Pod 使用的容器镜像是 nginx。
- –port=80:设置容器暴露的端口为 80,对于 Web 类型应用,外部请求会通过该端口号访问到容器。如果不需要端口访问,也可以不用该参数。
- – sleep 3600:–参数可以指定容器启动时要执行的命令,这里是执行 “sleep 3600” 命令,也就是让 Busybox 容器保持运行3600秒。如果容器启动时不需要执行命令,也可以不用该参数。
查看 Pod
使用 “kubectl get pod” 命令可以查看 Pod 列表信息,由于 Pod 的缩写(SHORTNAMES)是 po,也可以在命令中使用 po 代替 pod。命令后面可以根据需要增加不同参数。
- -o wide: 用来查看到更多的 Pod 信息,例如 Pod 的 IP 地址、部署所在节点等等。
- -n
: -n 参数可以指定命名空间名称,命名空间是对资源对象的一种逻辑分组隔离方式,Pod 可以创建到不同的命名空间下,这时就需要增加 -n 参数来指定 Pod 所在的命名空间,不加 -n 参数则是查看默认命名空间(default)下的 Pod。 - -o yaml: 用来以 YAML 文件的方式查看指定的 Pod。
使用 “kubectl describe pod” 命令可以查看 Pod 的详细信息。如果 Pod 不在默认命名空间,需要使用 “-n” 参数指定 Pod 所在的命名空间。
命令返回结果(返回信息太多,我用 “…” 做了省略)中看到的 Pod 信息非常丰富,包括基础信息、容器信息、状态信息、挂载存储卷信息、调度策略信息、事件信息等等。这些数据对 Pod 的故障排查非常有用,后续我们也会经常使用。
[root@k8s-master ~]# kubectl describe pod test
Name: test
Namespace: default
......
Containers:
nginx:
......
Conditions:
Type Status
Initialized True
......
Volumes:
data:
......
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 56s default-scheduler Successfully assigned default/test to k8s-worker1
Normal Pulling 55s kubelet Pulling image "nginx"
其他命令
管理 Pod 的其他命令参考以下小卡片,有些命令已经出现过很多次,相信你已经很熟悉了。如果 Pod 不在默认命名空间(default),这些命令都需要使用 “-n” 参数指定 Pod 所在的命名空间,否则会提示 Pod 没找到。
删除 Pod:kubectl delete pod <pod名称>
编辑 Pod,修改 YAML 文件后自动生效:kubectl edit pod <pod名称>
进入 Pod 内部:kubectl exec -it <pod名称> -- sh
查看 Pod 日志:kubectl logs <pod名称>
了解了 Pod 的共享机制和相关命令,我们对 Pod 已经有了一个全面的认识。最后我再给你讲一种特殊的 Pod —— 静态 Pod。
静态 Pod
在 K8s 中,静态 Pod 是一种非常特殊的 Pod,它的 YAML 文件就直接存放在集群各节点的指定目录位置(“/etc/kubernetes/manifests”),静态 Pod 会自动部署,而不是通过 K8s 的 API Server 创建。静态 Pod 通常用于部署集群级别的系统组件,如 K8s 集群自己的很多系统组件就是静态 Pod,这些组件需要在集群启动时就可用,并且可以随着节点重启而自动重启。对于静态 Pod 的任何更新都需要管理员手动修改它的 YAML 文件并重启 Pod。
创建静态 Pod 不需要使用任何命令,只需要把 Pod 的 YAML 文件放到该节点的“/etc/kubernetes/manifests” 目录下,节点的 kubelet 组件就会自动创建静态 Pod。其实集群搭建的时候我们已经部署了一些静态 Pod,我们就去看一下吧。
[root@k8s-master ~]# cd /etc/kubernetes/manifests/
[root@k8s-master manifests]# ls
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
在管理节点上的 manifests 目录中,可以看到 etcd、kube-apiserver、kube-controller-manager、kube-scheduler 这些 K8s 组件就是以静态 Pod 的方式部署到集群 kube-system 的命名空间下。
小结
今天,我重点介绍了 K8s 中的核心资源对象——Pod。在 K8s 中,Pod 是容器管理的基本单位,每个 Pod 可以包含一个或多个容器,这些容器通过 Infra Container(基础容器)共享网络,同时通过挂载相同的数据卷 Volume 来实现共享存储,这两种机制保证了 Pod 对容器管理的一致性和灵活性,简化了对容器的管理。
接着,我带你使用 YAML 文件的方式部署了一个包含两个容器的 Pod ,并且验证了 Pod 的共享网络和共享存储的机制。部署多容器 Pod 是 CKA 的考点,你一定要熟悉它的 YAML 文件的写法。你也需要熟悉 Pod 相关的 kubectl 命令,特别是创建 Pod 使用的 “kubectl run” 命令,以及查看 Pod 信息使用的 “kubectl get pod” 和 “kubectl describe pod” 命令。
最后,我介绍了静态 Pod。静态 Pod 是一种通过节点上指定目录(“/etc/kubernetes/manifests”)中的 YAML 文件自动部署的特殊 Pod,在集群启动时就可用,并且可以随着节点重启而自动重启,通常用于部署集群级别的系统组件,K8s 的组件就是以静态 Pod 的方式运行。创建静态 Pod 只需要把 Pod 的 YAML 文件放到该节点的指定目录下,节点的 kubelet 组件就会自动创建静态 Pod。
思考题
这就是今天的全部内容,这节课通常会有一个考点,就是给 Pod 配置多个容器,所以最后留两道练习题给你。
- 创建一个 Pod,其中运行着 Nginx、Redis、Memcache 3 个容器。
- 在节点 k8s-worker1 中运行一个 Nginx 的静态 Pod。
相信经过动手实践,会让你对知识的理解更加深刻。