前面讲到,Docker Container 通过三板斧,- Namespace 的隔离 - Cgroups 的限制 - Rootfs 的文件系统
,构造了一个相对隔离的环境。
既然有了 Container,为什么在 Kubernetes 中又会多出来一个 Pod 的对象呢?
且在 Kubernetes 的设计中,Pod 才是最小的调度单位。
其实,Container 只是个进程,而 Pod 更多对应的是进程组的概念,所以一个 Pod 里面是可以运行多个并行的 Container。
两者定义的概念并不一致,所以并不存在谁替代谁。
只是了解不多的人可能会觉得,一个 Pod 启动了一个 Container,下意识觉得 Pod 跟 Container 没有差别。
讲到这里,就有一个很有意思的问题出现了。
前面讲到, Container 为了不让自己成为僵尸进程,会找 Containerd-shim 作为自己的父进程,起到一个垫片的效果。
这样,当 Container 退出以后,Containerd-shim 可以帮它"收尸"。
那 Pod 作为管理多个 Container 的存在,势必也会存在一个 "父 Container" 来作为其他 "子 Container" 的依托。
这就是我们的 Infra Container。
Infra Container 不是 Init Container,它会比 Init Container 启动的更早,是第一个启动的容器。
Kubernetes 官方库里面找了一个 Pause 镜像作为 Infra Container。
KUBELET_POD_INFRA_CONTAINER=--pod-infra-container-image=gcr.io/google_containers/pause-amd64:lastest
Pause 的特点是镜像非常小,且永远处于暂停的状态。
且它的存在,是为了解决一个最重要的问题,那就是多个 Container 之间资源的共享问题。
同个 Pod 里面的 Container,会共享同一个 Network Namespace 来保持通信,且可以共享同一个 Volume。
这个 Network Namespace 是属于 Infra Container 的,所以从某种程度来说,我们可以认为流量的出入只经由 Infra Container。
所以 Pod 的生命周期,只跟它的 Infra Container 相关,与其下的 Container A,Container B 无关。
现在我们来看一个常见的场景,假设我们现在在设计一个在云上的压测,每次压测前,都需要更新 jmx,然后执行压测。
如果我们把 jmx 脚本写死在容器里面,每次都需要重新构建容器的镜像。
所以我们可以利用 Container 的共享来这样做。
apiVersion: v1
kind: Pod
metadata:
name: jmeter-test
spec:
initContainers:
- image: shaonianyr/jmx-container:demo
name: jmx
command: ["cp", "/test.jmx", "/app/test.jmx"]
volumeMounts:
- mountPath: /app
name: app-volume
containers:
- image: shaonianyr/jmeter-container:demo
name: jmeter
command: [ "/bin/sh","-c" ]
args: [ "jmeter -n -t /app/test.jmx "]
volumeMounts:
- mountPath: /app
name: app-volume
ports:
- containerPort: 8080
hostPort: 8001
volumes:
- name: app-volume
emptyDir: {}
这里的 Init Container,也可以说起到了 Sidecar 的效果。
Sidecar 的本质,就是独立于用户容器,作为一个辅助容器,来负责某些事情。
像后面我会提到的 Istio ,就很好的利用了 Sidecar 特性,再配合 Network Namespace 的共享,实现了微服务的治理。
当然,这是后话。
Preset 的意思是预设。
如果我们想批量给某些 Pod 注入 Volume,Secret 等等,但是又不想逐个修改 Pod 的 Template,就可以使用 PodPreset。
很多时候可能开发提供的 Yaml 文件缺斤少两,并不符合运维的预期,那么运维就可以通过这种方式,去做修改注入。
PodPreset 不需要关心原来的 Template 是什么样的,只需要根据一定的筛选条件,通过 Selector,找到需要作业的 Pod。
有一点需要注意的是,PodPreset 并不会对 Pod 的控制器做更改,只是在 Pod 对象创建前,直接追加进去。
就这样,Pod 的对象设计,提供了一个类似进程组的概念,更高效的管理 Container,并在设计模式上做了解耦和复用,为后续多种控制器的复杂编排,奠定了厚实的基础。
......
未完待续,有空就开更。