k8s 中 pod 是如何做到网络共享的

发布时间 2023-05-23 15:18:31作者: 烟草的香味

前言

在k8s中, pod是编排的最小单位, 在同一个pod中, 容器之间能够共享hostname network 等内容.

共享network, 简单说就是同一个pod中的容器, 可以通过访问localhost互相访问, 且端口占用会冲突.

在之前的介绍中提到过, 容器的隔离是通过namespace技术实现的, 网络隔离自然也是通过Network Namespace 实现. 每个network namespace中都有自己的一套网络资源, 比如: IP地址, 路由表, 网卡等.

那么网络共享的原理, 自然也就是将多个容器加入到同一个network namespace中咯.

令多个容器共用一套network namespace, 在docker中可以这样做:

# 方式一: 创建一个network, 然后所有容器都使用这个网络
docker network create mynetwork
docker run --name container1 --network=mynetwork nginx
docker run --name container2 --network=mynetwork nginx

# 方式二: 启动一个容器, 然后将新的容器加入到已有容器的网络中
docker run --name container1 nginx
docker run --name container2 --network container:container1 nginx

而k8s则是容器的管理者, 它又是怎么选择的呢?

k8s 的网络共享

k8s中, 选择了第二种方式来共享网络, 不止是网络, 包括volume也是这样. 这样设计可能是为了更大的灵活性吧. 具体原因没有细究.

但是, 如果说我们在启动容器的时候, 要将其加入到已有容器的网络中, pod中的容器就必须有一个是先启动的, 这样后续的容器才能加入. 那么问题来了, pod中哪个容器能够最先启动呢? 难道我们在定义pod时还需要定义容器的启动顺序吗? 显然不是这样的.

那么k8s是如何解决容器启动顺序的问题呢? 处理方式也十分简单粗暴, 在所有容器启动之前, 先启动一个默认的容器, 后续所有容器就可以都加入此容器的命名空间中了. 这个预先启动的容器什么都不做, 只是为了后续容器加入.

pause容器查看

口说无凭, 我们启动一个pod来看一下:

apiVersion: v1
kind: Pod
metadata:
  name: test
  namespace: hj
spec:
  shareProcessNamespace: true
  restartPolicy: Never
  containers:
    - name: nginx
      image: nginx
    - name: busybox
      image: busybox
      command: ["/bin/sh"]
      args: ["-c", "sleep infinity"]

此时查看运行中的容器列表, 那么你就能够看到pause容器:

image-20230523143629109

另外, 因为我们开启了shareProcessNamespace共享进程空间, 因此我们可以进入容器查看当前运行的所有进程:

image-20230523143922784

也可以清楚的看到pause进程, 与当前容器在同一个进程命名空间中.

(另外, 这里可以看到我们exec进入的sh进程, 其父进程PID是0, 与容器挂载目录中说的也对上了嘛. exec的原理是将进程强行加入已有的命名空间中)

pause容器

pause需要具有如下特点:

  1. 体积小 (方便快速拉取镜像)
  2. 不占用资源(否则每个pod启动一个, 用户资源都被吃光了)
  3. 稳定 (一旦挂了, 接入此容器命名空间的关联容器都会被波及, 所以此容器不能挂)

那么, pause到底做了什么呢? 在github可以看到镜像构建的源码. 其主要运行的源码在这里.

简单说, pause容器什么也没干. 进程在启动后, 不停的调用pause函数进入睡眠, 从而使得容器能够持续运行而又不消耗系统资源. 同时越是简单的就越是稳定. 而这个容器的大小也不过742KB.


至此, pod网络共享的谜团算是大致解开了. 虽然没有特别深入, 但对于理解pod也有一定的帮助.

原文地址: https://hujingnb.com/archives/900