1、Pod基础原理
Pod基础原理
目录
[toc]
1、Pod 基本概念
前面的课程中我们了解了 Kubernetes 的基本架构,以及如何使用资源清单在集群中部署一个应用。我们也了解到了 Pod 是 Kubernetes 集群中最基本的调度单元,我们平时在集群中部署的应用都是以 Pod 为单位的,而并不是我们熟知的容器,这样设计的目的是什么呢?为何不直接使用容器呢?
2、为什么需要 Pod
假设 Kubernetes 中调度的基本单元就是容器,对于一个非常简单的应用可以直接被调度直 接使用,没有什么问题。但是往往还有很多应用程序是由多个进程组成的,有的同学可能会说把这些进程都打包到一个容器中去不就可以了吗?理论上是可以实现的,但是不要忘记了容器运行时管理的进程是 pid=1 的主进程,其他进程死掉了就会成为僵尸进程,没办法进行管理了
,这种方式本身也不是容器推荐的运行方式,一个容器最好只干一件事情
(当然,你一个容器里跑多个进程也是可以的,例如用systemd/hypevisor
去管理),所以在真实的环境中不会使用这种方式。
那么我们就把这个应用的进程进行拆分,拆分成一个一个的容器总可以了吧?但是不要忘记一个问题,拆分成一个一个的容器后,是不是就有可能出现一个应用下面的某个进程容器被调度到了不同的节点上呀?往往我们应用内部的进程与进程间通信(通过 IPC 或者共享本地文件之类)都是要求在本地进行的
,也就是需要在同一个节点上运行。
所以我们需要一个更高级别的结构来将这些容器绑定在一起,并将他们作为一个基本的调度单元进行管理,这样就可以保证这些容器始终在同一个节点上面,这也就是 Pod 设计的初衷。
Pod是Kubernetes创建和管理的最小单元,一个Pod由一个容器或多个容器组成,这些容器共享存储、网络。
Pod 是一组紧密关联的容器集合,是 Kubernetes 调度的基本单位,容器进程运行在不同 PID、IPCNetwork 和 UTS namespace,且彼此之间共享网络。
Pod 的设计理念是支持多个容器在一个 Pod 中共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。
Pod特点:
- 一个Pod可以理解为是一个应用实例,提供服务
- Pod中容器始终部署在一个Node上
- Pod中容器共享网络、存储资源
- Kubernetes直接管理Pod,而不是容器;
Pod主要用法:
-
运行单个容器:最常见的用法,在这种情况下,可以将Pod看做是单个容器的抽象封装
-
运行多个容器:封装多个紧密耦合且需要共享资源的应用程序;(这种应用场景实际工作中还是比较少的,比较特殊一点的;)
如果有这些需求,你可以运行多个容器:
-
两个应用之间发生文件交互
-
两个应用需要通过127.0.0.1或者socket通信
-
两个应用需要发生频繁的调用
3、Pod 原理
在一个 Pod 下面运行几个关系非常密切的容器进程,这样一来这些进程本身又可以收到容器的管控,又具有几乎一致的运行环境,也就完美解决了上面提到的问题。
其实 Pod 也只是一个逻辑概念,真正起作用的还是 Linux 容器的 Namespace 和 Cgroup 这两个最基本的概念,Pod 被创建出来其实是一组共享了一些资源的容器而已。首先 Pod 里面的所有容器,都是共享的同一个 Network Namespace,但是涉及到文件系统的时候,默认情况下 Pod 里面的容器之间的文件系统是完全隔离的,但是我们可以通过声明来共享同一个 Volume。
1.pod中 共享网络的实现
共享网络:将业务容器网络加入到“负责网络的容器”实现网络共享。
我们可以指定新创建的容器和一个已经存在的容器共享一个 Network Namespace,在运行容器(docker 容器)的时候只需要指定 --net=container:目标容器名
这个参数就可以了。但是这种模式有一个明显的问题那就是容器的启动有先后顺序问题
,那么 Pod 是怎么来处理这个问题的呢?那就是加入一个中间容器(没有什么架构是加一个中间件解决不了的),这个容器叫做 Infra 容器,而且这个容器在 Pod 中永远都是第一个被创建的容器,这样是不是其他容器都加入到这 个 Infra 容器就可以了,这样就完全实现了 Pod 中的所有容器都和 Infra 容器共享同一个 Network Namespace 了,如下图所示:
注意: 每个pod一个ip; 一个pod至少有2个容器:一个infra容器,一个业务容器;
所以当我们部署完成 Kubernetes 集群的时候,首先需要保证在所有节点上可以拉取到默认的 Infra 镜像,默认情况下 Infra 镜像地址为 registry.k8s.io/pause:3.8
,这个容器占用的资源非常少,但是这个镜像默认是需要科学上网的,所以很多时候我们在部署应用的时候一直处于 Pending
状态或者报 sandbox image
相关的错误信息,大部分是因为所有 Pod 最先启动的容器镜像都拉不下来,肯定启动不了,启动不了其他容器肯定也就不能启动了:
[root@master1 ~]#kubelet --help|grep infra
--pod-infra-container-image string Specified image will not be pruned by the image garbage collector. CRI implementations have their own configuration to set this image. (default "registry.k8s.io/pause:3.8") (DEPRECATED: will be removed in 1.27. Image garbage collector will get sandbox image information from CRI.)
从上面图中我们可以看出普通的容器加入到了 Infra 容器的 Network Namespace 中,所以这个 Pod 下面的所有容器就是共享同一个 Network Namespace 了。普通容器不会创建自己的网卡,配置自己的 IP,而 是和 Infra 容器共享 IP、端口范围等,而且容器之间的进程可以通过 lo 网卡设备进行通信:
-
也就是容器之间是可以直接使用
localhost
进行通信的; -
看到的网络设备信息都是和 Infra 容器完全一样的;
-
也就意味着同一个 Pod 下面的容器运行的多个进程不能绑定相同的端口;
-
而且 Pod 的生命周期只跟 Infra 容器一致,而与容器 A 和 B 无关**。**
💘 实站:pod中共享网络的实现 infra container
-2022.12.8(成功测试)
- 实验环境
实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
k8s version:v1.21
CONTAINER-RUNTIME:docker://20.10.7
- 实验软件(无)
下面,我们来测试下:
- 用docker run创建一个nginx容器,并进入到容器里测试nginx服务:
[root@k8s-master1 ~]#docker run -d --name=web nginx
[root@k8s-master1 ~]#docker ps|grep nginx
8e409843a733 nginx "/docker-entrypoint.…" 10 seconds ago Up 9 seconds 80/tcp web
[root@k8s-master1 ~]#docker exec web curl localhost:80
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 615 100 615 0 0 300k 0 --:--:-- --:--:-- --:--:-- 600k
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
- 用docker run创建一个busybox测试容器,进入容器,测试是否可访问通nginx机器:
[root@k8s-master ~]#docker run -it busybox sh
[root@k8s-master1 ~]#docker run -it busybox sh
/ #
/ # curl localhost
sh: curl: not found
/ # curl localhost:80
sh: curl: not found
/ # wget localhost:80
Connecting to localhost:80 (127.0.0.1:80)
wget: can't connect to remote host (127.0.0.1): Connection refused
/
-
因为这2个容器处于不同的网络命名空间,因此是不可以通过127.0.0.1来访问的。
-
现在,我们来创建一个pod:
[root@k8s-master ~]#vim pod-net.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
app: test
name: pod-net-test
namespace: default
spec:
containers:
- image: busybox
name: test
command: ["/bin/sh","-c","sleep 12h"]
- image: nginx
name: web
- apply一下,并查看
[root@k8s-master1 ~]#kubectl apply -f pod-net.yaml
pod/pod-net-test created
[root@k8s-master1 ~]#kubectl get po
NAME READY STATUS RESTARTS AGE
pod-net-test 2/2 Running 0 2m7s
- 现在,我们以同样的方式进入到刚才创建的pod的2个容器里面:
先进到test pod里,可以看到里面监听了80端口,而buxybox容器是没有80端口服务的。这个是另一个容器监听的端口。
[root@k8s-master1 ~]#kubectl exec -it pod-net-test -c test -- netstat -antlp|grep 80
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:52518 127.0.0.1:80 TIME_WAIT -
tcp 0 0 :::80 :::* LISTEN -
[root@k8s-master1 ~]#kubectl exec -it pod-net-test -c web -- curl localhost:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@k8s-master1 ~]#
- 此时,是否可以在busybox容器了访问127.0.0.1:80服务呢?--是可以访问到的。
[root@k8s-master1 ~]#kubectl exec -it pod-net-test -c test -- 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
- 我们怎么可以看到这个infra container容器呢?-->到pod所在节点上用命令
docker ps|grep Pod名称
来查看即可。
[root@k8s-master1 ~]#kubectl get po -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-net-test 2/2 Running 0 9m20s 10.244.169.135 k8s-node2 <none> <none>
[root@k8s-master1 ~]#ssh root@172.29.9.33
root@172.29.9.33's password:
Last login: Wed Nov 23 07:33:13 2022 from k8s-master1
[root@k8s-node2 ~]#docker ps|grep pod-net-test
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1e74c55b0e5f nginx "/docker-entrypoint.…" 9 minutes ago Up 9 minutes k8s_web_pod-net-test_default_f82b3287-eb30-4bbb-8d7c-b046055cc572_0
306de81a82c7 busybox "/bin/sh -c 'sleep 1…" 10 minutes ago Up 10 minutes k8s_test_pod-net-test_default_f82b3287-eb30-4bbb-8d7c-b046055cc572_0
a56dcf0c53ad registry.aliyuncs.com/google_containers/pause:3.2 "/pause" 10 minutes ago Up 10 minutes k8s_POD_pod-net-test_default_f82b3287-eb30-4bbb-8d7c-b046055cc572_0
实验结束。😘
2.pod中共享存储的实现
共享存储:容器通过数据卷(volume)共享数据。
对于文件系统 Kubernetes 是怎么实现让一个 Pod 中的容器共享的呢?
默认情况下容器的文件系统是互相隔离的,要实现共享只需要在 Pod 的顶层声明一个 Volume,然后在需要共享这个 Volume 的容器中声明挂载即可。
比如下面 的示例:
apiVersion: v1
kind: Pod
metadata:
name: counter
spec:
volumes:
- name: varlog
hostPath:
path: /var/log/counter
containers:
- name: count
image: busybox
args:
- /bin/sh
- -c
- >
i=0;
while true;
do
echo "$i: $(date)" >> /var/log/1.log;
i=$((i+1));
sleep 1;
done
volumeMounts:
- name: varlog
mountPath: /var/log
- name: count-log
image: busybox
args: [/bin/sh, -c, 'tail -n+1 -f /opt/log/1.log']
volumeMounts:
- name: varlog
mountPath: /opt/log