POD
Pod 是 K8S 的最小工作单元。每个 Pod 包含一个或多个容器,多个容器以“边车”模式运行。K8S 管理的也是 Pod 而不是直接管理容器,Pod 中的容器会作为一个整体被 Master 调度到一个 Node 上运行。
Pod分为普通pod和静态pod,静态pod信息不储存在etcd中,直接存放在某个node的配置文件中。
pod多容器之间网络共享:
每个 Pod 都会被分配一个唯一的 IP 地址。Pod 中的所有容器共享网络空间,包括 IP 地址和端口。**Pod 内部的容器可以使用 localhost 互相通信。**Pod 中的容器与外界通信时,必须分配共享网络资源(例如使用宿主机的端口映射)。
每个pod都有一个基础容器pause(infrastucture container底层容器),通过infra容器来实现多个容器的共享网络。
pod多容器之间存储共享:
可以为一个Pod指定多个共享的 Volume。容器之间的存储可以通过pause容器来共享文件存储。
Pause 容器
kubelet 的配置中有这样一个参数KUBELET_POD_INFRA_CONTAINER=--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure:latest
kubernetes 中的 pause 容器主要为每个业务容器提供以下功能:
- 在 pod 中担任 Linux 命名空间共享的基础;
- 启用 pid 命名空间,开启 init 进程,承担 PID 1 的角色。
如何实现共享网络
Infra container 是一个非常小的镜像,大概 700KB 左右,是一个 C 语言写的、永远处于“暂停”状态的容器。由于有了这样一个 Infra container 之后,其他所有容器都会通过 Join Namespace 的方式加入到 Infra container 的 Network Namespace 中。
所以说一个 Pod 里面的所有容器,它们看到的网络视图是完全一样的。即:它们看到的网络设备、IP 地址、Mac 地址等等,跟网络相关的信息,其实全是一份,这一份都来自于 Pod 第一次创建的这个 Infra container。这就是 Pod 解决网络共享的一个解法。
在 Pod 里面,一定有一个 IP 地址,是这个 Pod 的 Network Namespace 对应的地址,也是这个 Infra container 的 IP 地址。所以大家看到的都是一份,而其他所有网络资源,都是一个 Pod 一份,并且被 Pod 中的所有容器共享。这就是 Pod 的网络实现方式。
由于需要有一个相当于说中间的容器存在,所以整个 Pod 里面,必然是 Infra container 第一个启动。并且整个 Pod 的生命周期是等同于 Infra container 的生命周期的,与容器 A 和 B 是无关的。这也是为什么在 Kubernetes 里面,它是允许去单独更新 Pod 里的某一个镜像的,即:做这个操作,整个 Pod 不会重建,也不会重启,这是非常重要的一个设计。
跨Docker容器共享网络:--net=container:pause,共享内存:–ipc=shareable,共享pid:--pid=container:pause
# 创建一个共享内存的容器
$ docker run -d --name pause -p 8880:80 --ipc=shareable gcr.io/google_containers/pause-amd64:3.0
$ cat <<EOF >> nginx.conf
error_log stderr;
events { worker_connections 1024; }
http {
access_log /dev/stdout combined;
server {
listen 80 default_server;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:2368;
}
}
}
EOF
# 创建使用container:pause网络、内存、pid的容器,这样3个容器就像部署在同一个“节点”内
$ docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf --net=container:pause --ipc=container:pause --pid=container:pause nginx
$ docker run -d --name ghost --net=container:pause --ipc=container:pause --pid=container:pause ghost
在 Kubernetes Pod 中,容器的运行方式与上述大致相同,但为每个 Pod 创建一个特殊的暂停容器。这个暂停容器运行一个非常简单的进程,它不执行任何功能,但本质上永远休眠(参见pause()下面的调用)。
// 引入各种头文件
// 信号注册处理函数 (SIGINT, SIGTERM)
// 这种属于正常退出情况,所以退出码为 0
static void sigdown(int signo) {
psignal(signo, "Shutting down, got signal");
exit(0);
}
// 信号注册处理函数 (SIGCHLD)
// 由操作系统发送给父进程,通知子进程的状态变化
// 子进程终止:
// 子进程暂停或恢复运行:
static void sigreap(int signo) {
// 等待指定的子进程退出
// 函数原型:
// pid_t waitpid(pid_t pid, int *status, int options);
// 参数说明:
// -1: 任意子进程
// NULL: 子进程的退出状态无需存储
// WNOHANG: 非阻塞,没有找到子进程时直接返回 0
while (waitpid(-1, NULL, WNOHANG) > 0)
;
// 如果子进程的数量大于 0, 无限循环
}
int main(int argc, char **argv) {
// 打印版本号 ...
if (getpid() != 1)
// 如果 Pause 进程 ID 不等于 1, 输出警告信息
// 因为进程 ID 等于 1 是 init 进程,负责接收处理僵尸进程
// 所以如果 Pause 进程 ID 不等于 1, 可能产生僵尸进程的堆积
fprintf(stderr, "Warning: pause should be the first process\n");
// 注册 SIGINT 信号 (Ctrl + C)
if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 1;
// 注册 SIGTERM 信号 (例如执行 kill, systemctl stop 等命令)
if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 2;
// 注册 SIGCHLD 信号
if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
.sa_flags = SA_NOCLDSTOP},
NULL) < 0)
return 3;
// 无限循环
for (;;)
// pause 函数可以使当前进程陷入睡眠状态,避免浪费 CPU 资源
// 直到捕获到一个信号后被唤醒
pause();
// 一切都是42...
// https://en.wikipedia.org/wiki/Phrases_from_The_Hitchhiker%27s_Guide_to_the_Galaxy#The_Answer_to_the_Ultimate_Question_of_Life,_the_Universe,_and_Everything_is_42
return 42;
}