1 现象
在k8s中集群中每当我们创建一个pod,都会伴随着一个pause容器产生,因此我们在node节点上会有很多的pause容器。
例如:
代码语言:javascript复制# master节点
[root@test-3-217 ~]# kubectl apply -f busybox.yaml
[root@test-3-217 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox-pod 1/1 Running 0 30m 10.244.2.45 test-3-219 <none> <none>
# node节查看busybox容器
[root@test-3-219 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0969c5802814 1c35c4412082 "sleep 3600" 52 seconds ago Up 50 seconds k8s_busybox_busybox-pod_default_ec874d6b-9d08-4a16-8f48-e22fd8bfca0a_0
434df7bbc3fb registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 "/pause" 53 seconds ago Up 51 seconds k8s_POD_busybox-p
由上可以看出,pause是一个容器,但不是pod,而且先于busybox容器产生,那么pause容器有什么作用呢?
在kubernetes中,pod是可以创建和管理的最小单元,由一个或多个相关联的容器组成。pod中的多个容器共享同一个network namespace,因此这些容器可以共享pod的IP和端口。
其中共享的network namespace就是通过pause容器实现的。
下面我们通过创建一个由nginx、ghost、busybox组成的pod来演示下。
2 pause功能演示 network namespace
2.1 创建 pod 资源
代码语言:javascript复制# 配置文件
vim pause-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: pause-test
spec:
containers:
- name: busybox
command:
- sleep
- "3600"
image: busybox
imagePullPolicy: IfNotPresent
- name: ghost
image: ghost:3.21
imagePullPolicy: IfNotPresent
- name: nginx
image: nginx:1.9.1
imagePullPolicy: IfNotPresent
restartPolicy: Always
# 运行
kubectl apply -f pause-test.yaml
# 查看pod
[root@test-3-217 pause]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pause-test 3/3 Running 3 3h11m 10.244.2.49 test-3-219 <none> <none>
# 查看容器
[root@test-3-219 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2d9d8566b53f 1c35c4412082 "sleep 3600" 22 minutes ago Up 22 minutes k8s_busybox_pause-test_default_02e7dcb7-5aeb-477f-a50b-74ce1b0a7704_3
9d8ccf485b8f 94ec7e53edfc "nginx -g 'daemon of…" 3 hours ago Up 3 hours k8s_nginx_pause-test_default_02e7dcb7-5aeb-477f-a50b-74ce1b0a7704_0
faa2fb108ab3 db9598608d2e "docker-entrypoint.s…" 3 hours ago Up 3 hours k8s_ghost_pause-test_default_02e7dcb7-5aeb-477f-a50b-74ce1b0a7704_0
cbaab0586322 registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 "/pause" 3 hours ago Up 3 hours k8s_POD_pause-test_default_02e7dcb7-5aeb-477f-a50b-74ce1b0a7704_0
我们创建了一个名为pause-test的pod资源,由busybox、ghost、nginx三个容器组成,其中:
busybox作用是提供Linux基础命令,提供ps、netstat等基础调试命令;
ghost作用是一个博客系统,默认端口2368;
nginx作用是为博客提供反向代理,默认端口80; 以上可模拟构建一套基于共享网络的测试环境。
2.2 查看 pod 中容器网络
代码语言:javascript复制[root@test-3-217 pause]# kubectl exec -it pause-test -- /bin/sh
# 默认进入pod中第一个容器
Defaulting container name to busybox.
Use 'kubectl describe pod/pause-test -n default' to see all of the containers in this pod.
/ # netstat -ntlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:2368 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
/ # ps aux
PID USER TIME COMMAND
1 root 0:00 sleep 3600
6 root 0:00 /bin/sh
12 root 0:00 ps aux
通过以上输出可以得出:
- netstat命令我们可以看到nginx和ghost各自的启动端口,因此得出pod中network namespace是可以由不同容器共享的。
- ps命令我们可以看出pid 为1 的进程是容器自身的ENTRYPOINT进程sleep 3600,而不是/pause。因此得出pod中pid namesapce 没有被容器所共享。
此时我们访问nginx的80端口:
代码语言:javascript复制[root@uvmsvr-3-217 pause]# curl 10.244.2.49
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...省略....
</html>
由于默认的nginx镜像没有进行proxy配置,访问的默认页面。由于此时nginx和ghost能够通信,因此我们通过nginx进行代理。
2.3 nginx代理ghost
代码语言:javascript复制# 进入nginx容器
[root@test-3-217 pause]# kubectl exec -it -c nginx pause-test -- /bin/sh
# cd /etc/nginx/conf.d
# 反向代理ghost
# cat > example.conf <<EOF
server {
listen 80 default_server;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:2368;
}
}
EOF
# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# nginx -s reload
2020/06/29 03:25:34 [notice] 15#15: signal process started
# 再次访问nginx
# 注意:由于没有设置service或ingress,只能使用内部ip调试
[root@test-3-217 pause]# curl 10.244.2.49
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Ghost</title>
<meta name="HandheldFriendly" content="True" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
....省略....
此时访问nginx不是默认页,而是ghost的页面(由于使用内部ip,暂时无法在浏览器展示),即pod中多个容器是可以共享网络的。
如果上面的例子你已经明白,我们再来看看pause的定义吧。
3 pause容器定义
Pause容器 全称infrastucture container(又叫infra)基础容器,即它会在每个 Pod 里,额外起一个Infra container 小容器来共享整个 Pod 的 Network Namespace。
Infra container 是一个非常小的镜像,C语言写的、永远处于“暂停”状态的容器。基于Infra container 之后,其他所有容器都会通过 Join Namespace 的方式加入到 Infra container 的 Network Namespace 中。
而在网上的大部分文章则用以下解释
kubernetes中的pause容器主要为每个业务容器提供以下功能:
- 在pod中担任Linux命名空间共享的基础;
- 启用pid命名空间,开启init进程;
通过上文的实验pause提供了 network namespace 可以印证第一点“在pod中担任Linux命名空间共享的基础”,但是pid namespace并不是共享的,而是由各自容器ENTRYPOINT自行管理,这是为什么呢?
下面继续通过实例进行讲解。
4 pause功能演示pid namespace
4.1 新建 pod 资源
代码语言:javascript复制apiVersion: v1
kind: Pod
metadata:
name: pause-test2
spec:
# 共享pid namespace
shareProcessNamespace: true
containers:
- name: busybox
command:
- sleep
- "3600"
image: busybox
imagePullPolicy: IfNotPresent
- name: ghost
image: ghost:3.21
imagePullPolicy: IfNotPresent
- name: nginx
image: nginx:1.9.1
imagePullPolicy: IfNotPresent
restartPolicy: Always
配置文件中我们新增了shareProcessNamespace: true 可以实现pid namespace在pod中容器共享。
4.2 进程查看
代码语言:javascript复制# 进入新建的pod pause-test2
[root@test-3-217 pause]# kubectl exec -it pause-test2 -- /bin/sh
Defaulting container name to busybox.
Use 'kubectl describe pod/pause-test2 -n default' to see all of the containers in this pod.
/ # ps aux
PID USER TIME COMMAND
1 root 0:00 /pause
6 root 0:00 sleep 3600
12 1000 0:17 node current/index.js
75 root 0:00 nginx: master process nginx -g daemon off;
80 104 0:00 nginx: worker process
86 root 0:00 /bin/sh
91 root 0:00 ps aux
此时我们可以看到pid 为1的进程是/pause,nginx进程、ghost进程等都可以在busybox容器中看到,即pid namespace在pod中的容器间共享,其好处是便于进程间通信,类似linux操作系统,避免由于容器的异常终止导致僵尸进程。
但是在 Kubernetes 1.8 版本之前,默认是启用 PID namespace 共享的,除非使用 kubelet 标志 --docker-disable-shared-pid=true 禁用。然而在 Kubernetes 1.8 版本以后,情况刚好相反,默认情况下 kubelet 标志 --docker-disable-shared-pid=true,如果要开启,还要设置成 false。也可以给通过pod.spec.shareProcessNamespace 决定是否启用 PID namespace 共享。
至于为什么要关闭pid namespace共享?
因为当应用程序不会产生其他进程,而且僵尸进程带来的问题就可以忽略不计时,就用不到 PID namespace 的共享了。
5 pause生命周期
代码语言:javascript复制# node节点停止pause容器
docker stop 9ebe5f09adfe
# 查看pod相关容器
[root@test-3-219 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
38be505f017a 94ec7e53edfc "nginx -g 'daemon of…" 23 seconds ago Up 22 seconds k8s_nginx_pause-test2_default_5c98d865-ab34-4ea2-a712-34ef5431864b_1
bdd98a7996de db9598608d2e "docker-entrypoint.s…" 24 seconds ago Up 23 seconds k8s_ghost_pause-test2_default_5c98d865-ab34-4ea2-a712-34ef5431864b_1
b2ff89c04fee 1c35c4412082 "sleep 3600" 24 seconds ago Up 23 seconds k8s_busybox_pause-test2_default_5c98d865-ab34-4ea2-a712-34ef5431864b_1
3d30657ff21e registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 "/pause" 25 seconds ago Up 24 seconds k8s_POD_pause-test2_default_5c98d865-ab34-4ea2-a712-34ef5431864b_1
# pod生命周期,没有随pause的停止而停止
[root@test-3-217 pause]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pause-test 3/3 Running 4 4h54m
pause-test2 3/3 Running 3 27m
由于pause容器作为基础容器,在pod内部第一个启动,并且提供namespace级别共享,因此pause生命周期决定了pod内部容器的生命周期。正如上面的实验,pause容器在node节点停止后,kubelet会重新拉起pause容器,此时pod内的容器都会重启,但pod的生命周期并没有随着pause容器的停止而重新计时。
文章参考:
https://www.bbsmax.com/A/VGzlQYmYJb/
https://www.ianlewis.org/en/almighty-pause-container
https://cloud.tencent.com/developer/article/1583919
原创: 三页 木纳大叔爱运维