幕后英雄SIG-Node与CRI
- 调度完成后,kubelet就负责将这个调度成功的Pod在宿主机上创建出来,并把容器运行起来
- k8s里面不可被替代的组件
- 第一个就是kube-apiserver
- 第二个就是kubelet
- kubelet本身也是按照控制器模式来工作的,工作原理如下图
- 驱动SyncLoop控制循环运行的事件包括四种:
- Pod更新事件
- Pod生命周期变化
- kubelet本身设置的执行周期
- 定时的清理事件
- kubelet启动第一件事情就是设置Listeners,注册关心的各种事件的Informer
- 此外kubelet还负责维护很多子控制循环,一般被成为某Manager,如VolumeManager 等
- SyncLoop根据Pod对象的变化进行容器操作
- 通过Watch机制,监听自己相关的Pod的变化
- 具体处理过程会启动一个单独的goroutine进行
kubelet调用下层容器运行时时,不会直接调用dockerAPI,而是通过一组叫做CRI (Container Runtime Interface)的gRPC接口来间接完成的
除了dockershim外,其他容器的CRI shim都需要额外单独部署
- 比如CNCF里containerd项目的CRI shim,把k8s的调用转成runC容器
- CRI待实现的两组接口
- RuntimeService
- 主要是跟容器相关的操作接口,比如创建、启动、删除、执行命令等
- 确保这个接口本身,只关注容器,不关注Pod
- ImageService
- 主要是跟镜像相关的操作,比如拉取、删除镜像等
- RuntimeService
- CRI原则
- 接口本身只关注容器,不关注Pod
- Pod是编排概念,不是运行时概念
- 如果CRI里有Pod,接下来Pod有更新CRI就需要更新,没有解耦
- 接口本身只关注容器,不关注Pod
- CRI调用流程(不同shim有不同的实现)
- StreamingServer与StreamingAPI
绝不仅是安全:KataContainers与gVisor
- 安全容器的实现
- 以上两种方案都是给进程分配一个独立的内核,避免共享宿主机内核
- 容器进程能看到的攻击面就变成了自己的内核
- 示意图如下
- KataContainer
- 本质就是一个精简后的轻量级虚拟机
- 像虚拟机一样安全,像容器一样敏捷
- 使用传统的虚拟化技术,通过虚拟硬件模拟了虚拟机,然后在虚拟机里安装裁剪后的内核实现强隔离
- gVisor
- 给容器进程配置一个go语言实现的、运行在用户态的、极小的“独立内核”
- 内核对容器进程暴露Linux内核ABI,扮演“GuestKernel”角色,把容器和宿主机隔离开
- go语言模拟出了一个用户态的内核,通过这个模拟内核代替容器进程向宿主机发起系统调用
- gVisor的Sentry进程分为两种不同实现
- Ptrace机制拦截用户应用的系统调用,把这些系统调用交给Sentry处理(性能差,仅Demo)
- Sentry使用KVM进行系统调用的拦截,处理地址空间等细节,并不会创建出虚拟硬件设备等
- 总结
- 启动和占用资源上gVisor略胜一筹
- 对于系统调用密集等应用(重IO/重网络)gVisor因为需要频繁的拦截系统调用而性能下降
- gVisor用Sentry模拟一个内核,所以支持的系统调用有限
- 但是与应用进程强隔离,安全性有保证