图文详解 - Kubernetes Pod 与 Docker 容器

容器本可以成为轻量级虚拟机的替代品。然而,最广泛使用的容器形式(由 Docker 普及并随后由 OCI 标准化)鼓励您只拥有一个过程每个容器的服务。这种方法有一些显着的优点- 增强的隔离性、简化的水平扩展、更高的可重用性等。但是,这种设计也有一个主要缺点 - 在现实世界中,虚拟机很少只运行一项服务。因此,容器抽象对于功能齐全的虚拟机替换来说通常可能过于有限。

当 Docker 试图提供创建多服务容器的解决方法时,Kubernetes 迈出了更大胆的一步,选择了一组称为Pod的内聚容器作为其最小的可部署单元,而不是单个单元。

对于具有 VM 或裸机经验的工程师来说,应该相对容易掌握 Pod 的概念,或者看起来是这样...... 🙈

开始使用 Kubernetes 时首先要了解的事情之一是为每个 Pod 分配一个唯一的 IP 地址和主机名。此外,Pod 内的容器可以通过 localhost 相互通信。因此,很快就会发现 Pod 类似于服务器的缩影。

但过了一段时间,您就会意识到 Pod 中的每个容器都有一个独立的文件系统,并且在一个容器内部,您看不到同一 Pod 中其他容器的文件和进程。那么,也许 Pod 并不是一个很小的服务器,而只是一组具有共享网络堆栈的容器?

但随后您就会了解到,一个 Pod 中的容器可以通过共享内存和其他典型的 Linux IPC 方式进行通信!所以,网络命名空间可能不是唯一共享的东西......

我决定深入研究并亲眼看看:

  • Pod 是如何在底层实现的。
  • Pod 和 Container 之间的实际区别是什么。
  • 使用标准 Docker 命令创建 Pod 需要什么。

听起来不错?那么就和我一起踏上旅程吧!至少,它可以帮助您巩固 Linux、Docker 和 Kubernetes 技能。

检查容器


让我们启动我们的容器:

css 复制代码
docker run --name foo --rm -d --memory='512MB' --cpus='0.5' nginx:alpine

检查容器的名称空间

首先,了解容器启动时创建了哪些隔离原语会很有趣。

以下是查找容器名称空间的方法:

bash 复制代码
NGINX_PID=$(pgrep nginx | sort | head -n 1)

sudo lsns -p ${NGINX_PID}
arduino 复制代码
        NS TYPE   NPROCS   PID USER COMMAND
...
4026532253 mnt         3  1269 root nginx: master process nginx -g daemon off;
4026532254 uts         3  1269 root nginx: master process nginx -g daemon off;
4026532255 ipc         3  1269 root nginx: master process nginx -g daemon off;
4026532256 pid         3  1269 root nginx: master process nginx -g daemon off;
4026532258 net         3  1269 root nginx: master process nginx -g daemon off;
4026532319 cgroup      3  1269 root nginx: master process nginx -g daemon off;

用于隔离 nginx 容器的命名空间是:

  • mnt ( Mount ) - 容器有一个独立的安装表。
  • utsUNIX 分时)- 容器能够拥有自己的主机名和域名。
  • ipc进程间通信)-容器内的进程只能通过系统级 IPC 与同一容器内的进程进行通信。
  • pid (进程 ID ) - 容器内的进程只能看到同一容器内或同一 pid 命名空间内的其他进程。
  • net ( Network ) - 容器拥有自己的网络堆栈。
  • cgroup ( Cgroup ) - 容器有自己的cgroup_虚拟化视图_(不要与 cgroup 本身混淆)。

请注意_User_命名空间没有被使用!OCI 运行时规范提到了用户 命名空间支持。然而,虽然 Docker 可以为其容器使用此命名空间,但由于固有的限制和可能增加的额外操作复杂性,默认情况下它不会这样做。因此,root容器中的用户可能是root主机系统中的用户。谨防!

列表中另一个需要特殊标注的命名空间是cgroup。我花了一段时间才明白cgroup命名空间与cgroups机制是不一样的。Cgroup 命名空间只是为容器提供了 cgroup 伪文件系统的隔离视图(将在下面讨论)。

检查容器的 cgroup

Linux 命名空间使容器内的进程认为它们在专用机器上运行。然而,看不到任何邻居并不意味着完全受到保护。一些饥饿的邻居可能会意外消耗主机资源的不公平份额。

Cgroups 来救援!

给定进程的 cgroup 限制可以通过检查 cgroup 伪文件系统 ( cgroupfs) 中的节点来检查,该节点通常安装在/sys/fs/cgroup。但首先,我们需要找出感兴趣进程的 cgroupfs 子树的路径:

css 复制代码
sudo systemd-cgls --no-pager
yaml 复制代码
Control group /:
-.slice
...
│
└─system.slice
  ...
  │
  ├─docker-866191e4377b052886c3b85fc771d9825ebf2be06f84d0bea53bc39425f753b6.scope ...
  │ ├─1269 nginx: master process nginx -g daemon off;
  │ └─1314 nginx: worker process
  ...

然后列出 cgroupfs 子树:

bash 复制代码
ls -l /sys/fs/cgroup/system.slice/docker-866191e4377b052886c3b85fc771d9825ebf2be06f84d0bea53bc39425f753b6.scope/
diff 复制代码
...
-rw-r--r-- 1 root root 0 Sep 27 11:12 cpu.max
-r--r--r-- 1 root root 0 Sep 27 11:12 cpu.stat
-rw-r--r-- 1 root root 0 Sep 27 11:12 cpu.weight
...
-rw-r--r-- 1 root root 0 Sep 27 11:51 io.max
-r--r--r-- 1 root root 0 Sep 27 11:12 io.stat
...
-rw-r--r-- 1 root root 0 Sep 27 11:12 memory.high
-rw-r--r-- 1 root root 0 Sep 27 11:12 memory.low
-rw-r--r-- 1 root root 0 Sep 27 11:12 memory.max
-rw-r--r-- 1 root root 0 Sep 27 11:12 memory.min
...
-r--r--r-- 1 root root 0 Sep 27 11:42 pids.current
-r--r--r-- 1 root root 0 Sep 27 11:51 pids.events
-rw-r--r-- 1 root root 0 Sep 27 11:12 pids.max

要查看具体的内存限制,需要读取文件中的值memory.max

bash 复制代码
cat /sys/fs/cgroup/system.slice/docker-866191e4377b052886c3b85fc771d9825ebf2be06f84d0bea53bc39425f753b6.scope/memory.max
bash 复制代码
536870912 # Exactly 512MB that were requested at the container start.

有趣的是,在没有显式设置任何资源限制的情况下启动容器无论如何都会为其配置一个 cgroup 切片。我还没有真正检查过,但我的猜测是,虽然默认情况下 CPU 和 RAM 消耗不受限制,但 cgroups 可能用于限制容器内部的一些其他资源消耗和设备访问(例如,/dev/sda和)。/dev/mem

以下是根据上述发现如何可视化容器的方法:

检查 Pod

现在,让我们看一下 Kubernetes Pod。为了保持容器与 Pod 比较的公平性,Pod 检查将在 Kubernetes 集群上进行,该集群使用与 Docker 相同的底层容器运行时 - containerd/runc。

与容器非常相似,Pod 的实现可能会有所不同。例如,当Kata 容器用作CRI 运行时(通过runtimeClassName在 Pod 规范上设置属性)时, Pod 就成为真正的虚拟机!预计,基于 VM 的 Pod 在实现和功能上将不同于使用传统 Linux 容器实现的 Pod。

将检查以下两个容器 Pod:

pod.yaml

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: foo
spec:
  containers:
    - name: app
      image: nginx:alpine
      ports:
        - containerPort: 80
      resources:
        limits:
          memory: "256Mi"
    - name: sidecar
      image: curlimages/curl:8.3.0
      command: ["/bin/sleep", "3650d"]
      resources:
        limits:
          memory: "128Mi"

您可以使用以下命令启动 Pod:

kubectl apply -f pod.yaml

检查 Pod 的容器

Pod 检查应在运行该 Pod 的 Kubernetes 集群节点上完成。使用kube-01右侧的选项卡,让我们尝试查找 Pod 的进程:

ps auxf

yaml 复制代码
 PID  USER      ELAPSED  CMD
...
1748  root        01:16  /var/lib/rancher/k3s/data/ab2055bc72380bad965b219e8
1769  65535       01:15   \_ /pause
1801  root        01:15   \_ nginx: master process nginx -g daemon off;
1846  message+    01:15   |   \_ nginx: worker process
1847  message+    01:15   |   \_ nginx: worker process
1859  _apt        01:15   \_ /bin/sleep 3650d

基于正常运行时和公共父进程的相似性,上述三个(顶级)进程很可能是在 Pod 启动期间创建的。这很有趣,因为在清单中只请求了两个容器nginxsleep.

可以使用名为 的 containerd 命令行客户端ctr交叉检查上述发现:

ini 复制代码
sudo ctr --namespace=k8s.io containers ls
lua 复制代码
CONTAINER      IMAGE                                 RUNTIME
1c0d4c94188aa  docker.io/library/nginx:alpine        io.containerd.runc.v2
3f2b45521b479  docker.io/curlimages/curl:8.3.0       io.containerd.runc.v2
fe99217fab7c5  docker.io/rancher/mirrored-pause:3.6  io.containerd.runc.v2
...

事实上,Kubernetes 容器运行时创建了三个新容器 - nginxsleeppause。与此同时,另一个与任何 Kubernetes CRI 运行时兼容的命令行客户端仅显示两个容器:

sudo crictl ps

CONTAINER      IMAGE          ...  NAME      POD ID         POD
d74ad720df223  ead0a4a53df89       coredns   bf8d6b16f6c10  coredns-6799fbcd5-6xksq
3f2b45521b479  99f2927cb384d       sidecar   fe99217fab7c5  foo
1c0d4c94188aa  433dbc17191a7       app       fe99217fab7c5  foo

但请注意上面的字段如何与 的输出中的容器 IDPOD ID相匹配!嗯,看来 Pod 有一个辅助容器。那么,它是做什么用的呢?

我不知道 Pod 是否有任何等效的 OCI 运行时规范。因此,当我对Kubernetes API 规范提供的信息不满意时,我通常会直接转到Kubernetes 容器运行时接口 (CRI) protobuf 文件:

scss 复制代码
// kubelet expects any compatible container runtime
// to implement the following gRPC methods:

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;
}

因此,Pod 实际上是根据沙箱和可以在这些沙箱中启动的容器来定义的。沙箱管理一些所有 Pod 容器共有的资源,pause容器在调用过程中启动RunPodSandbox()。上网一查,发现这个容器里面只有一个空闲进程

检查 Pod 的命名空间

以下是(相关)命名空间在集群节点上的样子:

sudo lsns

yaml 复制代码
        NS TYPE   NPROCS    PID   USER     COMMAND
...
4026532333 net         5   1769   65535    /pause
4026532397 mnt         1   1769   65535    /pause
4026532398 uts         5   1769   65535    /pause
4026532399 ipc         5   1769   65535    /pause
4026532400 pid         1   1769   65535    /pause
4026532401 mnt         3   1801   root     nginx: master process nginx -g daemon off;
4026532402 pid         3   1801   root     nginx: master process nginx -g daemon off;
4026532403 cgroup      3   1801   root     nginx: master process nginx -g daemon off;
4026532404 mnt         1   1859   _apt     /bin/sleep 3650d
4026532405 pid         1   1859   _apt     /bin/sleep 3650d
4026532406 cgroup      1   1859   _apt     /bin/sleep 3650d

与第一部分中的 Docker 容器非常相似,该pause容器有五个命名空间 - netmntutsipcpid 。但显然,nginx容器sleep仅由三个命名空间组成:mntpidcgroup

事实证明,lsns可能不是检查进程名称空间的最佳工具。相反,要检查某个进程使用的名称空间,/proc/${PID}/ns可以引用该路径:

rust 复制代码
# 1801 is the PID of the nginx container
sudo ls -l /proc/1801/ns


...
lrwxrwxrwx 1 root root 0 sep 28 12:00 cgroup -> 'cgroup:[4026532403]'
lrwxrwxrwx 1 root root 0 Sep 28 12:00 ipc -> 'ipc:[4026532399]'
lrwxrwxrwx 1 root root 0 Sep 28 12:00 mnt -> 'mnt:[4026532401]'
lrwxrwxrwx 1 root root 0 Sep 28 12:00 net -> 'net:[4026532333]'
lrwxrwxrwx 1 root root 0 Sep 28 12:00 pid -> 'pid:[4026532402]'
lrwxrwxrwx 1 root root 0 Sep 28 12:00 uts -> 'uts:[4026532398]'
...
rust 复制代码
# 1859 is the PID of the sleep container
sudo ls -l /proc/1859/ns


...
lrwxrwxrwx 1 _apt messagebus 0 Sep 28 12:00 cgroup -> 'cgroup:[4026532406]'
lrwxrwxrwx 1 _apt messagebus 0 Sep 28 12:00 ipc -> 'ipc:[4026532399]'
lrwxrwxrwx 1 _apt messagebus 0 Sep 28 12:00 mnt -> 'mnt:[4026532404]'
lrwxrwxrwx 1 _apt messagebus 0 Sep 28 12:00 net -> 'net:[4026532333]'
lrwxrwxrwx 1 _apt messagebus 0 Sep 28 12:00 pid -> 'pid:[4026532405]'
lrwxrwxrwx 1 _apt messagebus 0 Sep 28 12:00 uts -> 'uts:[4026532398]'
...

虽然可能很难注意到,但nginxsleep容器实际上重用了容器的netutsipc 命名空间pause

同样,这可以通过以下方式进行交叉检查crictl

bash 复制代码
# 1c0d4c94188aa is the ID of the nginx container
sudo crictl inspect 1c0d4c94188aa | jq .info.runtimeSpec.linux.namespaces


[
  {
    "type": "pid"
  },
  {
    "type": "ipc",
    "path": "/proc/1769/ns/ipc"
  },
  {
    "type": "uts",
    "path": "/proc/1769/ns/uts"
  },
  {
    "type": "mount"
  },
  {
    "type": "network",
    "path": "/proc/1769/ns/net"
  },
  {
    "type": "cgroup"
  }
]
bash 复制代码
# 3f2b45521b479 is the ID of the sleep container
sudo crictl inspect 3f2b45521b479 | jq .info.runtimeSpec.linux.namespaces


[
  {
    "type": "pid"
  },
  {
    "type": "ipc",
    "path": "/proc/1769/ns/ipc"
  },
  {
    "type": "uts",
    "path": "/proc/1769/ns/uts"
  },
  {
    "type": "mount"
  },
  {
    "type": "network",
    "path": "/proc/1769/ns/net"
  },
  {
    "type": "cgroup"
  }
]

我认为上述发现完美地解释了同一个 Pod 中容器的能力:

  • 互相通信
    • 通过和/或本地主机
    • 使用IPC手段(共享内存、消息队列等)
  • 拥有共享域和主机名。

然而,在看到所有这些命名空间如何在容器之间自由重用后,我开始怀疑默认边界可以被打破。事实上,对Pod API 规范的更彻底的阅读表明,将shareProcessNamespace标志设置为truePod 的容器将具有四个公共命名空间,而不是默认的三个。但还有一个更令人震惊的发现------ hostIPChostNetwork、 和hostPIDflags 可以让容器使用相应主机的命名空间!

有趣的是,CRI API 规范似乎更加灵活。至少在语法上,它允许将netpidipc命名空间的范围限定为CONTAINER、POD或NODE。因此,假设可以构建一个容器无法通过 localhost 相互通信的 Pod 🙈

检查 Pod 的 cgroup

好的,Pod 的 cgroup 怎么样了?systemd-cgls可以很好地可视化 cgroups 层次结构:

python 复制代码
sudo systemd-cgls --no-pager


Control group /:
-.slice
...
└─kubepods.slice
  └─kubepods-burstable.slice
    ...
    └─kubepods-burstable-pode88e5109_51f9_4f39_8d99_ada6fb281137.slice
      ├─cri-containerd-1c0d4c94188aa402db8751db1301de1d3adcc6739ee2ca78c6738273ee8251a7.scope ...
      │ ├─1801 nginx: master process nginx -g daemon off;
      │ ├─1846 nginx: worker process
      │ └─1847 nginx: worker process
      ├─cri-containerd-fe99217fab7c597c40f10b8086dfa2978394e9942b53d66569441b5b7d7d4ea0.scope ...
      │ └─1769 /pause
      └─cri-containerd-3f2b45521b479bc1c07a8eebf9409e244d5fbf9eb431156d250a37f492599604.scope ...
        └─1859 /bin/sleep 3650d

看起来 Pod 本身就有一个父节点,并且每个容器也可以单独调整。这符合我的预期,因为在 Pod 清单中,可以为 Pod 中的每个容器单独设置资源限制。

此时此刻,我脑海中的 Pod 看起来是这样的:

使用 Docker 实现 Pod

如果 Pod 底层被实现为一堆具有公共 cgroup 父级的半融合容器,是否可以使用 Docker 重现类似 Pod 的构造?

最近我已经尝试做类似的事情来让多个容器监听同一个套接字,并且我知道 Docker 允许创建一个使用语法重用现有网络命名空间的容器docker run --network container:<other-container-name>。但我也知道 OCI 运行时规范仅定义createstart命令。因此,当您在现有容器内执行命令时docker exec <existing-container> <command>,您实际上run(即createthen)start是一个完全新鲜的容器,它恰好重用了目标容器的所有名称空间。这让我非常有信心可以使用标准 Docker 命令来重现 Pod。

首先,需要配置父cgroup条目。 幸运的是,现在,为了简洁起见,我将仅配置cpu和内存的cgroup 控制器:

ini 复制代码
sudo cat <<EOF > /etc/systemd/system/mypod.slice
[Unit]
Description=My Pod Slice

[Slice]
MemoryLimit=512M
CPUQuota=50%
EOF
python 复制代码
sudo systemctl daemon-reload
sudo systemctl start mypod.slice

检查切片是否实际创建:

python 复制代码
sudo systemd-cgls --no-pager --all


Control group /:
-.slice
...
├─mypod.slice
...

其次,需要创建一个沙箱容器:

css 复制代码
docker run -d --rm \
  --name mypod_sandbox \
  --cgroup-parent mypod.slice \
  --ipc 'shareable' \
  alpine sleep infinity

最后,我们需要重用沙箱容器的命名空间来启动有效负载容器:

lua 复制代码
# app (nginx)
docker run -d --rm \
  --name app \
  --cgroup-parent mypod.slice \
  --network container:mypod_sandbox \
  --ipc container:mypod_sandbox \
  nginx:alpine
lua 复制代码
# sidecar (sleep)
docker run -d --rm \
  --name sidecar \
  --cgroup-parent mypod.slice \
  --network container:mypod_sandbox \
  --ipc container:mypod_sandbox \
  curlimages/curl sleep 365d

您注意到我省略了哪个名称空间吗?是的,我无法在容器之间共享uts命名空间。目前命令中似乎没有公开这种可能性docker run。嗯,这当然很遗憾。但除了uts命名空间之外,它是成功的!

cgroup 看起来很像 Kubernetes 本身创建的 cgroup:

css 复制代码
sudo systemd-cgls --no-pager --all

这次,切片将列出几个活动进程:

bash 复制代码
Control group /:
-.slice
...
├─mypod.slice
│ ├─docker-575fd1bbc28340fbd37c35374dd4ef8a91d796cf4abc2e97eaac42981ae2058a.scope ...
│ │ └─1480 sleep infinity
│ ├─docker-c36d2f83cf53ebe5354f7a6f60770b8728772e6c788979d8a35338da102a2fd6.scope ...
│ │ └─1312 sleep infinity
│ ├─docker-48dff78e59361aea6876385aa0677c1ad949b0951cb97b9cf7d1e8fba991dc3e.scope ...
│ │ └─1669 sleep 365d
│ └─docker-85b436943b55fdb4666d384711ad3577f41c0d03e58987c639633a35a37bacf4.scope ...
│   ├─1599 nginx: master process nginx -g daemon off;
│   └─1635 nginx: worker process

命名空间的全局列表看起来也很熟悉:

yaml 复制代码
        NS TYPE   NPROCS   PID USER    COMMAND
...
4026532322 mnt         1  1480 root    sleep infinity
4026532323 uts         1  1480 root    sleep infinity
4026532324 ipc         4  1480 root    sleep infinity
4026532325 pid         1  1480 root    sleep infinity
4026532327 net         4  1480 root    sleep infinity
4026532385 cgroup      1  1480 root    sleep infinity
4026532386 mnt         2  1599 root    nginx: master process nginx -g daemon off;
4026532387 uts         2  1599 root    nginx: master process nginx -g daemon off;
4026532388 pid         2  1599 root    nginx: master process nginx -g daemon off;
4026532389 cgroup      2  1599 root    nginx: master process nginx -g daemon off;
4026532390 mnt         1  1669 _apt    sleep 365d
4026532391 uts         1  1669 _apt    sleep 365d
4026532392 pid         1  1669 _apt    sleep 365d
4026532393 cgroup      1  1669 _apt    sleep 365d

并且app ( nginx) 和sidecar( curl) 容器似乎共享ipcnet命名空间:

bash 复制代码
# app container
sudo ls -l /proc/1599/ns
rust 复制代码
lrwxrwxrwx 1 root root 0 Sep 28 13:09 cgroup -> 'cgroup:[4026532389]'
lrwxrwxrwx 1 root root 0 Sep 28 13:09 ipc -> 'ipc:[4026532324]'
lrwxrwxrwx 1 root root 0 Sep 28 13:09 mnt -> 'mnt:[4026532386]'
lrwxrwxrwx 1 root root 0 Sep 28 13:09 net -> 'net:[4026532327]'
lrwxrwxrwx 1 root root 0 Sep 28 13:09 pid -> 'pid:[4026532388]'
lrwxrwxrwx 1 root root 0 Sep 28 13:09 uts -> 'uts:[4026532387]'
...
rust 复制代码
# sidecar container
sudo ls -l /proc/1669/ns


lrwxrwxrwx 1 _apt messagebus 0 Sep 28 13:09 cgroup -> 'cgroup:[4026532393]'
lrwxrwxrwx 1 _apt messagebus 0 Sep 28 13:09 ipc -> 'ipc:[4026532324]'
lrwxrwxrwx 1 _apt messagebus 0 Sep 28 13:09 mnt -> 'mnt:[4026532390]'
lrwxrwxrwx 1 _apt messagebus 0 Sep 28 13:09 net -> 'net:[4026532327]'
lrwxrwxrwx 1 _apt messagebus 0 Sep 28 13:09 pid -> 'pid:[4026532392]'
lrwxrwxrwx 1 _apt messagebus 0 Sep 28 13:09 uts -> 'uts:[4026532391]'
...

耶!我们刚刚(几乎)仅使用标准docker run命令创建了一个 Pod 🎉

总结

容器和 Pod 很相似。在幕后,他们严重依赖 Linux 命名空间和 cgroup。然而,Pod 不仅仅是容器组。Pod 是一个自给自足的高级构造。所有Pod的容器运行在同一台机器(集群节点)上,生命周期同步,弱化相互隔离,简化容器间通信。这使得 Pod 更接近传统 VM,带回了熟悉的部署模式,例如sidecar或客户端服务代理

相关推荐
weixin_4539650010 分钟前
[单master节点k8s部署]31.ceph分布式存储(二)
分布式·ceph·kubernetes
漫无目的行走的月亮44 分钟前
在Docker中运行微服务注册中心Eureka
docker
大G哥3 小时前
记一次K8S 环境应用nginx stable-alpine 解析内部域名失败排查思路
运维·nginx·云原生·容器·kubernetes
妍妍的宝贝3 小时前
k8s 中微服务之 MetailLB 搭配 ingress-nginx 实现七层负载
nginx·微服务·kubernetes
大道归简4 小时前
Docker 命令从入门到入门:从 Windows 到容器的完美类比
windows·docker·容器
zeruns8025 小时前
如何搭建自己的域名邮箱服务器?Poste.io邮箱服务器搭建教程,Linux+Docker搭建邮件服务器的教程
linux·运维·服务器·docker·网站
爱跑步的程序员~5 小时前
Docker
docker·容器
福大大架构师每日一题5 小时前
23.1 k8s监控中标签relabel的应用和原理
java·容器·kubernetes
程序那点事儿5 小时前
k8s 之动态创建pv失败(踩坑)
云原生·容器·kubernetes
疯狂的大狗5 小时前
docker进入正在运行的容器,exit后的比较
运维·docker·容器