页面加载中,请稍候
来源:开源Linux发布时间:2022-09-291043浏览
询问 AI
基于最后的发现,所以,我决定深入了解:
Pod 是如何在底层实现的
Pod 和 Container 之间的实际区别是什么
如何使用 Docker 创建 Pod
在此过程中,我希望它能帮助我巩固我的 Linux、Docker 和 Kubernetes 技能。
$ cat > Vagrantfile <<EOF# -*- mode: ruby -*-# vi: set ft=ruby :
Vagrant.configure("2") do |config| config.vm.box = "debian/buster64" config.vm.hostname = "docker-host" config.vm.define "docker-host" config.vagrant.plugins = ['vagrant-vbguest']
config.vm.provider "virtualbox" do |vb| vb.cpus = 2 vb.memory = "2048" end
config.vm.provision "shell", inline: <<-SHELL apt-get update apt-get install -y curl vim SHELL
config.vm.provision "docker"endEOF
$ vagrant up$ vagrant ssh
最后让我们启动一个容器:
$ docker run --name foo --rm -d --memory='512MB' --cpus='0.5' nginx探索容器的 namespace首先我们来看一下,当容器启动后,哪些隔离原语(primitives)被创建了:# Look up the container in the process tree.$ ps auxfUSER PID ... COMMAND...root 4707 /usr/bin/containerd-shim-runc-v2 -namespace moby -id cc9466b3e...root 4727 \_ nginx: master process nginx -g daemon off;systemd+ 4781 \_ nginx: worker processsystemd+ 4782 \_ nginx: worker process我们可以看到用于隔离以上容器的命名空间是以下这些:
# Find the namespaces used by 4727 process.$ sudo lsns NS TYPE NPROCS PID USER COMMAND...4026532157 mnt 3 4727 root nginx: master process nginx -g daemon off;4026532158 uts 3 4727 root nginx: master process nginx -g daemon off;4026532159 ipc 3 4727 root nginx: master process nginx -g daemon off;4026532160 pid 3 4727 root nginx: master process nginx -g daemon off;4026532162 net 3 4727 root nginx: master process nginx -g daemon off;
PID=$(docker inspect --format '{{.State.Pid}}' foo)似乎 Docker 使用 /docker/模式。好吧,不管怎样:
# Check cgroupfs node for the container main process (4727).$ cat /proc/${PID}/cgroup11:freezer:/docker/cc9466b3eb67ca374c925794776aad2fd45a34343ab66097a44594b35183dba010:blkio:/docker/cc9466b3eb67ca374c925794776aad2fd45a34343ab66097a44594b35183dba09:rdma:/8:pids:/docker/cc9466b3eb67ca374c925794776aad2fd45a34343ab66097a44594b35183dba07:devices:/docker/cc9466b3eb67ca374c925794776aad2fd45a34343ab66097a44594b35183dba06:cpuset:/docker/cc9466b3eb67ca374c925794776aad2fd45a34343ab66097a44594b35183dba05:cpu,cpuacct:/docker/cc9466b3eb67ca374c925794776aad2fd45a34343ab66097a44594b35183dba04:memory:/docker/cc9466b3eb67ca374c925794776aad2fd45a34343ab66097a44594b35183dba03:net_cls,net_prio:/docker/cc9466b3eb67ca374c925794776aad2fd45a34343ab66097a44594b35183dba02:perf_event:/docker/cc9466b3eb67ca374c925794776aad2fd45a34343ab66097a44594b35183dba01:name=systemd:/docker/cc9466b3eb67ca374c925794776aad2fd45a34343ab66097a44594b35183dba00::/system.slice/containerd.service
ID=$(docker inspect --format '{{.Id}}' foo)
# Check the memory limit.$ cat /sys/fs/cgroup/memory/docker/${ID}/memory.limit_in_bytes536870912 # Yay! It's the 512MB we requested!
# See the CPU limits.ls /sys/fs/cgroup/cpu/docker/${ID}
有趣的是在不明确设置任何资源限制的情况下启动容器都会配置一个 cgroup。实际中我没有检查过,但我的猜测是默认情况下,CPU 和 RAM 消耗不受限制,Cgroups 可能用来限制从容器内部对某些设备的访问。
这是我在调查后脑海中呈现的容器:

现在,让我们来看看 Kubernetes Pod。与容器一样,Pod 的实现可以在不同的 CRI 运行时(runtime)之间变化。例如,当 Kata 容器被用来作为一个支持的运行时类时,某些 Pod 可以就是真实的虚拟机了!并且正如预期的那样,基于 VM 的 Pod 与传统 Linux 容器实现的 Pod 在实现和功能方面会有所不同。
为了保持容器和 Pod 之间公平比较,我们会在使用 ContainerD/Runc 运行时的 Kubernetes 集群上进行探索。这也是 Docker 在底层运行容器的机制。
设置实验环境(playground)这次我们使用基于 VirtualBox driver 和 Containd 运行时的 minikube 来设置实验环境。要快速安装 minikube 和 kubectl,我们可以使用 Alex Ellis 编写的 arkade 工具:# Install arkade ()$ curl -sLS https://get.arkade.dev | sh实验的 Pod,可以按照下面的方式设置:
$ arkade get kubectl minikube
$ minikube start --driver virtualbox --container-runtime containerd
$ kubectl --context=minikube apply -f - <<EOFapiVersion: v1kind: Podmetadata: name: foospec: containers: - name: app image: docker.io/kennethreitz/httpbin ports: - containerPort: 80 resources: limits: memory: "256Mi" - name: sidecar image: curlimages/curl command: ["/bin/sleep", "3650d"] resources: limits: memory: "128Mi"EOF探索 Pod 的容器实际的 Pod 检查应在 Kubernetes 集群节点上进行:
$ minikube ssh让我们看看那里 Pod 的进程:$ ps auxfUSER PID ... COMMAND...root 4947 \_ containerd-shim -namespace k8s.io -workdir /mnt/sda1/var/lib/containerd/...root 4966 \_ /pauseroot 4981 \_ containerd-shim -namespace k8s.io -workdir /mnt/sda1/var/lib/containerd/...root 5001 \_ /usr/bin/python3 /usr/local/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k geventroot 5016 \_ /usr/bin/python3 /usr/local/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k geventroot 5018 \_ containerd-shim -namespace k8s.io -workdir /mnt/sda1/var/lib/containerd/...100 5035 \_ /bin/sleep 3650d基于运行的时间,上述三个进程组很有可能是在 Pod 启动期间创建。这很有意思,因为在清单文件中,只有两个容器,httpbin 和 sleep。可以使用名为 ctr 的 ContainerD 命令行来交叉检查上述的发现:
$ sudo ctr --namespace=k8s.io containers lsCONTAINER IMAGE RUNTIME...097d4fe8a7002 docker.io/curlimages/curl@sha256:1a220 io.containerd.runtime.v1.linux...dfb1cd29ab750 docker.io/kennethreitz/httpbin:latest io.containerd.runtime.v1.linux...f0e87a9330466 k8s.gcr.io/pause:3.1 io.containerd.runtime.v1.linux的确是三个容器被创建了。同时,使用另一个和 CRI 运行时监控的命令行 crictl 检测发现,仅仅只有两个容器:
$ sudo crictl psCONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID097d4fe8a7002 bcb0c26a91c90 About an hour ago Running sidecar 0 f0e87a9330466dfb1cd29ab750 b138b9264903f About an hour ago Running app 0 f0e87a9330466
但是注意,上述的 POD ID 字段和 ctr 输出的 pause:3.1 容器 id 一致。好吧,看上去这个 Pod 是一个辅助容器。所以,它有什么用呢?
我还没有注意到在 OCI 运行时规范中有和 Pod 相对应的东西。因此,当我对 Kubernetes API 规范提供的信息不满意时,我通常直接进入 Kubernetes Container Runtime 接口(CRI)Protobuf 文件中查找相应的信息:
// kubelet expects any compatible container runtime// to implement the following gRPC methods:所以,Pod 实际上就是由沙盒以及在沙盒中运行的容器组成的。沙盒管理 Pod 中所有容器的常用资源,pause 容器会在 RunPodSandbox() 调用中被启动。简单的互联网搜索就发现了该容器仅仅是一个 idle 进程。探索 Pod 的命名空间下面就是集群节点上的命名空间:
service RuntimeService { ... rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {} rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {} rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {} rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) {} rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) {}
rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {} rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {} rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) {} rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) {} rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {} rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {} rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse) {} rpc ReopenContainerLog(ReopenContainerLogRequest) returns (ReopenContainerLogResponse) {}
// ... }
message CreateContainerRequest { // ID of the PodSandbox in which the container should be created. string pod_sandbox_id = 1; // Config of the container. ContainerConfig config = 2; // Config of the PodSandbox. This is the same config that was passed // to RunPodSandboxRequest to create the PodSandbox. It is passed again // here just for easy reference. The PodSandboxConfig is immutable and // remains the same throughout the lifetime of the pod. PodSandboxConfig sandbox_config = 3;}
$ sudo lsns NS TYPE NPROCS PID USER COMMAND4026532614 net 4 4966 root /pause4026532715 mnt 1 4966 root /pause4026532716 uts 4 4966 root /pause4026532717 ipc 4 4966 root /pause4026532718 pid 1 4966 root /pause4026532719 mnt 2 5001 root /usr/bin/python3 /usr/local/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k gevent4026532720 pid 2 5001 root /usr/bin/python3 /usr/local/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k gevent4026532721 mnt 1 5035 100 /bin/sleep 3650d4026532722 pid 1 5035 100 /bin/sleep 3650d前面第一部分很像 Docker 容器,pause 容器有五个命名空间:net、mnt、uts、ipc 以及 pid。但是很明显,httpbin 和 sleep 容器仅仅有两个命名空间:mnt 和 pid。这是怎么回事?事实证明,lsns 不是检查进程名称空间的最佳工具。相反,要检查某个进程使用的命名空间,可以参考 /proc/${pid}/ns 位置:
# httpbin containersudo ls -l /proc/5001/ns...lrwxrwxrwx 1 root root 0 Oct 24 14:05 ipc -> 'ipc:[4026532717]'lrwxrwxrwx 1 root root 0 Oct 24 14:05 mnt -> 'mnt:[4026532719]'lrwxrwxrwx 1 root root 0 Oct 24 14:05 net -> 'net:[4026532614]'lrwxrwxrwx 1 root root 0 Oct 24 14:05 pid -> 'pid:[4026532720]'lrwxrwxrwx 1 root root 0 Oct 24 14:05 uts -> 'uts:[4026532716]'虽然不太容易去注意到,但 httpbin 和 sleep 容器实际上重用了 pause 容器的 net、uts 和 ipc 命名空间!我们可以用 crictl 交叉检测验证:
# sleep containersudo ls -l /proc/5035/ns...lrwxrwxrwx 1 100 101 0 Oct 24 14:05 ipc -> 'ipc:[4026532717]'lrwxrwxrwx 1 100 101 0 Oct 24 14:05 mnt -> 'mnt:[4026532721]'lrwxrwxrwx 1 100 101 0 Oct 24 14:05 net -> 'net:[4026532614]'lrwxrwxrwx 1 100 101 0 Oct 24 14:05 pid -> 'pid:[4026532722]'lrwxrwxrwx 1 100 101 0 Oct 24 14:05 uts -> 'uts:[4026532716]'
# Inspect httpbin container.$ sudo crictl inspect dfb1cd29ab750{ ... "namespaces": [ { "type": "pid" }, { "type": "ipc", "path": "/proc/4966/ns/ipc" }, { "type": "uts", "path": "/proc/4966/ns/uts" }, { "type": "mount" }, { "type": "network", "path": "/proc/4966/ns/net" } ], ...}
# Inspect sleep container.$ sudo crictl inspect 097d4fe8a7002...
我认为上述发现完美的解释了同一个 Pod 中容器具有的能力:
能够互相通信
通过 localhost 和/或
使用 IPC(共享内存,消息队列等)
共享 domain 和 hostname
$ sudo systemd-cglsControl group /:-.slice├─kubepods│ ├─burstable│ │ ├─pod4a8d5c3e-3821-4727-9d20-965febbccfbb│ │ │ ├─f0e87a93304666766ab139d52f10ff2b8d4a1e6060fc18f74f28e2cb000da8b2│ │ │ │ └─4966 /pause│ │ │ ├─dfb1cd29ab750064ae89613cb28963353c3360c2df913995af582aebcc4e85d8│ │ │ │ ├─5001 /usr/bin/python3 /usr/local/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k gevent│ │ │ │ └─5016 /usr/bin/python3 /usr/local/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k gevent│ │ │ └─097d4fe8a7002d69d6c78899dcf6731d313ce8067ae3f736f252f387582e55ad│ │ │ └─5035 /bin/sleep 3650d...所以,Pod 本身有一个父节点(Node),每个容器也可以单独调整。这符合我的预期,因为在 Pod 清单中,可以为 Pod 中的每个容器单独设置资源限制。此刻,我脑海中的 Pod 看起来是这样的:

$ sudo apt-get install cgroup-tools首先,让我们配置一个父 cgroup 条目。为了简洁起见,我将仅使用 CPU 和内存控制器:sudo cgcreate -g cpu,memory:/pod-foo然后我们创建一个沙盒容器:
# Check if the corresponding folders were created:ls -l /sys/fs/cgroup/cpu/pod-foo/ls -l /sys/fs/cgroup/memory/pod-foo/
$ docker run -d --rm \ --name foo_sandbox \ --cgroup-parent /pod-foo \ --ipc 'shareable' \ alpine sleep infinity最后,让我们启动重用沙盒容器命名空间的实际容器:
# app (httpbin)$ docker run -d --rm \ --name app \ --cgroup-parent /pod-foo \ --network container:foo_sandbox \ --ipc container:foo_sandbox \ kennethreitz/httpbin你注意到我省略了哪个命名空间吗?没错,我不能在容器之间共享 uts 命名空间。似乎目前在 docker run 命令中没法实现。嗯,是有点遗憾。但是除开 uts 命名空间之外,它是成功的!cgroups 看上去很像 Kubernetes 创建的:
# sidecar (sleep)$ docker run -d --rm \ --name sidecar \ --cgroup-parent /pod-foo \ --network container:foo_sandbox \ --ipc container:foo_sandbox \ curlimages/curl sleep 365d
$ sudo systemd-cgls memoryController memory; Control group /:├─pod-foo│ ├─488d76cade5422b57ab59116f422d8483d435a8449ceda0c9a1888ea774acac7│ │ ├─27865 /usr/bin/python3 /usr/local/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k gevent│ │ └─27880 /usr/bin/python3 /usr/local/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k gevent│ ├─9166a87f9a96a954b10ec012104366da9f1f6680387ef423ee197c61d37f39d7│ │ └─27977 sleep 365d│ └─c7b0ec46b16b52c5e1c447b77d67d44d16d78f9a3f93eaeb3a86aa95e08e28b6│ └─27743 sleep infinity全局命名空间列表看上去也很相似:
$ sudo lsns NS TYPE NPROCS PID USER COMMAND...4026532157 mnt 1 27743 root sleep infinity4026532158 uts 1 27743 root sleep infinity4026532159 ipc 4 27743 root sleep infinity4026532160 pid 1 27743 root sleep infinity4026532162 net 4 27743 root sleep infinity4026532218 mnt 2 27865 root /usr/bin/python3 /usr/local/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k gevent4026532219 uts 2 27865 root /usr/bin/python3 /usr/local/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k gevent4026532220 pid 2 27865 root /usr/bin/python3 /usr/local/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k gevent4026532221 mnt 1 27977 _apt sleep 365d4026532222 uts 1 27977 _apt sleep 365d4026532223 pid 1 27977 _apt sleep 365dhttpbin 和 sidecar 容器看上去共享了 ipc 和 net 命名空间:
# app container$ sudo ls -l /proc/27865/nslrwxrwxrwx 1 root root 0 Oct 28 07:56 ipc -> 'ipc:[4026532159]'lrwxrwxrwx 1 root root 0 Oct 28 07:56 mnt -> 'mnt:[4026532218]'lrwxrwxrwx 1 root root 0 Oct 28 07:56 net -> 'net:[4026532162]'lrwxrwxrwx 1 root root 0 Oct 28 07:56 pid -> 'pid:[4026532220]'lrwxrwxrwx 1 root root 0 Oct 28 07:56 uts -> 'uts:[4026532219]'
# sidecar container$ sudo ls -l /proc/27977/nslrwxrwxrwx 1 _apt systemd-journal 0 Oct 28 07:56 ipc -> 'ipc:[4026532159]'lrwxrwxrwx 1 _apt systemd-journal 0 Oct 28 07:56 mnt -> 'mnt:[4026532221]'lrwxrwxrwx 1 _apt systemd-journal 0 Oct 28 07:56 net -> 'net:[4026532162]'lrwxrwxrwx 1 _apt systemd-journal 0 Oct 28 07:56 pid -> 'pid:[4026532223]'lrwxrwxrwx 1 _apt systemd-journal 0 Oct 28 07:56 uts -> 'uts:[4026532222]'
新闻来源:开源Linux,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。
暂无评论哦,快来评论一下吧!
2026-06-17

2026-06-18

2026-06-05