《K8s 实战》——在 Pod 中使用宿主节点的 Linux 命名空间

  • Pod 中的容器通常在分开的 Linux 命名空间中运行。这些命名空间将容器中的进程与其他容器中,或者宿主机默认命名空间中的进程隔离开来
  • 例如,每一个 Pod 有自己的 IP 和端口空间,这是因为它拥有自己的网络命名空间。类似地,每一个 Pod 拥有自己的进程树,因为它有自己的 PID 命名空间。同样地 Pod 拥有自己的 IPC 命名空间,仅允许同一 Pod 内的进程通过进程间通信(Inter Process Communication)机制进行交流

1. 使用宿主节点的网络命名空间

pod-with-host-network

  • 部分 Pod(特别是系统 Pod)需要在宿主节点的默认命名空间中运行,以允许它们看到和操作节点级别的资源和设备。例如,某个 Pod 可能需要使用宿主节点上的网络适配器,而不是自己的虚拟网络设备
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: pod-with-host-network
spec:
hostNetwork: true # *
containers:
- name: main
image: alpine
command: ["sleep", "999999"]
  • 此时,这个 Pod 可以使用宿主节点的网络接口,而不是拥有自己独立的网络。这意味着这个 Pod 没有自己的 IP 地址,如果这个 Pod 中的某一进程绑定了某个端口,那么该进程将被绑定到宿主节点的端口上
1
2
3
4
5
6
7
8
9
10
11
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
...
$ kubectl exec pod-with-host-network -- ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
...
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-with-host-network 1/1 Running 0 19s 10.4.7.202 debian202 <none> <none>

2. 绑定宿主节点上的端口而不使用宿主节点的网络命名空间

  • Pod 可以在拥有自己的网络命名空间的同时,将端口绑定到宿主节点的端口上
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Pod
metadata:
name: pod-with-hostport
spec:
containers:
- name: kubia
image: luksa/kubia:v1
ports:
- containerPort: 8080
hostPort: 9000 # *
protocol: TCP
  • 对于使用 hostPort 的 Pod,仅有运行该 Pod 的节点会绑定相应的端口,且到达宿主节点端口的连接会被直接转发到 Pod 的对应端口上。而 NodePort 类型的服务会在所有的节点上绑定端口,且到达宿主节点端口的连接会被转发到随机选取的 Pod 上

hostport-nodeport

1
2
3
4
5
6
7
8
9
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-with-hostport 1/1 Running 0 109s 10.244.64.210 debian202 <none> <none>
$ curl 10.244.64.210:8080
This is v1 running in pod pod-with-hostport
$ curl debian202:9000
This is v1 running in pod pod-with-hostport
$ curl debian201:9000
curl: (7) Failed to connect to debian201 port 9000: Connection refused
  • 如果使用 hostPort,一个宿主节点只能调度一个副本,因为两个进程不能绑定宿主节点上的同一个端口

hostports

3. 使用宿主节点的 PID 与 IPC 命名空间

  • Pod spec 中的 hostPIDhostIPChostNetwork 类似,被设置为 true 时,Pod 中的容器会使用宿主节点的 PID 和 IPC 命名空间,分别允许它们看到宿主机上的全部进程,或者通过 IPC 机制与它们通信
1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: pod-with-host-pid-ipc
spec:
hostPID: true
hostIPC: true
containers:
- name: main
image: alpine
command: ["sleep", "999999"]
  • 现在这个 Pod 可以看到宿主机上的所有进程,并且 Pod 中的进程可以通过 IPC 机制与宿主机上的进程通信
1
2
3
4
5
6
7
$ kubectl exec pod-with-host-pid-ipc -- ps aux
PID USER TIME COMMAND
1 root 0:03 {systemd} /sbin/init
2 root 0:00 [kthreadd]
3 root 0:00 [rcu_gp]
4 root 0:00 [rcu_par_gp]
...