0.1. google镜像下载地址 0.2. 安装前检查 0.2.1. Master 0.2.2. Worker 0.3. 安装运行时环境 0.4. Docker 0.5. Kubeadm、Kubelet、kubectl 0.5.1. 配置主节点的kubelet的Cgroup驱动 0.6. 部署高可用集群 0.6.1. 为kube-apiserver部署负载均衡器 0.6.2. 安装keepalived & HAProxy 0.7. 部署堆叠集群 0.7.1. 部署第一个控制平面节点 0.7.2. 添加其他控制平面节点 0.7.3. 添加worker节点 0.1. google镜像下载地址 google-image 0.2. 安装前检查 Linux操作系统:Centos 7 2GB以上内存、2核以上CPU 集群之间网络互通 每个节点需要有唯一的hostname、MAC地址和produc_uuid 打开特定的端口 禁用swap(必须禁用swap才能使kubelet正常工作) # 获取网络接口的MAC地址 ip link ifconfig -a # 查看product_uuid sudo cat /sys/class/dmi/id/product_uuid 某些虚拟机可能有相同的值,但是硬件设备具有唯一的值,Kubernetes使用这些值来唯一标识集群中的节点,如果这些值对于每个节点都不是唯一的,会导致安装失败。 如果有多个网络适配器,并且在默认路由上无法访问的Kubernetes组件,建议添加IP路由,以便通过适当的适配器访问Kubernetes群集。 0.2.1. Master 协议 方向 端口范围 目的 使用者 TCP 入站 6443* Kubernetes API server All TCP 入站 2379-2380 etcd server client API kube-apiserver, etcd TCP 入站 10250 Kubelet API Self, Control plane TCP 入站 10251 kube-scheduler Self TCP 入站 10252 kube-controller-manager Self *标记的端口可以自定义为其他端口。etcd也可以使用集群外的集群或自定义的其他端口。 0.2.2. Worker 协议 方向 端口范围 目的 使用者 TCP 入站 10250 Kubelet API Self, Control plane TCP 入站 30000-32767 NodePort Services** All **标记的端口是对外提供服务是的Service的默认端口范围。Pod的网络插件也需要使用特定的端口。 0.3. 安装运行时环境 kubeadm将尝试通过扫描已知的域套接字列表来自动检测Linux节点上的容器运行时,可以在下表中找到所使用的可检测运行时和套接字路径。 Runtime Domain Socket Docker /var/run/docker.sock containerd /run/containerd/containerd.sock CRI-O /var/run/crio/crio.sock 如果同时检测到Docker和containerd,则Docker优先。这是必需的,因为Docker 18.09附带了containerd,两者都是可检测的,如果检测到任何其他两个或更多运行时,kubeadm将退出并显示相应的错误消息。 如果选择的容器运行时是Docker,则通过kubelet内置的dockershim CRI实现使用它。 以root用户或者在增加命令前缀sudo。 0.4. Docker # Install Docker CE ## Set up the repository ### Install required packages. yum install yum-utils device-mapper-persistent-data lvm2 ### Add Docker repository. yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo ## Install Docker CE. yum update && yum install docker-ce-18.06.2.ce ## Create /etc/docker directory. mkdir /etc/docker # Setup daemon. cat > /etc/docker/daemon.json <<EOF { "exec-opts": ["native.cgroupdriver=systemd"], #这个参数修改默认的Cgroups管理器 "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2", "storage-opts": [ "overlay2.override_kernel_check=true" ] } EOF mkdir -p /etc/systemd/system/docker.service.d # Restart Docker systemctl daemon-reload systemctl restart docker 更多安装细节参考 0.5. Kubeadm、Kubelet、kubectl 每个节点都需要安装: kubeadm:用于安装集群 kubelet:在群集中的所有计算机上运行的组件,并执行诸如启动pod和容器之类的操作 kubectl:与集群通信的命令行工具 cat <<EOF > /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg exclude=kube* EOF # Set SELinux in permissive mode (effectively disabling it) # 关闭 SeLinux 使得容器能够访问宿主机文件系统(例如容器网络) setenforce 0 sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes systemctl enable --now kubelet # 由于iptables被绕过而导致流量路由不正确的问题 # 确保在`sysctl`中将`net.bridge.bridge-nf-call-iptables`设置为1 cat <<EOF > /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system # 确保`br_netfilter`模块已经加载 lsmod | grep br_netfilter # 查看模块是否加载 modprobe br_netfilter # 加载模块 完成以上设置后,kubelet进入一个crashloop中(每隔几秒重新启动一次)等待kubeadm告诉它该怎么做。 0.5.1. 配置主节点的kubelet的Cgroup驱动 当使用Docker作为容器运行时,kubeadm会自动检测cgroup驱动,并且在运行时期间将其设置在/var/lib/kubelet/kubeadm-flags.env文件中。 如果使用其他CRI,需要修改/etc/default/kubelet文件中cgroup-driver的值,例如:KUBELET_EXTRA_ARGS=--cgroup-driver=<value> kubeadm init和kubeadm join将使用此文件为kubelet提供额外的用户定义参数。 请注意,如果CRI的cgroup驱动程序不是cgroupfs,才需要进行修改,因为这已经是kubelet中的默认值。 修改完成之后,需要重启kubelet: systemctl daemon-reload systemctl restart kubelet 0.6. 部署高可用集群 0.6.1. 为kube-apiserver部署负载均衡器 创建一个名称可解析为DNS的kube-apiserver负载均衡器 负载平衡器必须能够通过apiserver端口与所有控制平面节点通信,还必须允许其监听端口上的传入流量 HAProxy可以作为一个负载均衡器 确保负载均衡器的地址始终与kubeadm的Control Plane Endpoint的地址匹配 添加第一个控制平面节点到负载均衡器中,并测试通信 nc -v LOAD_BALANCER_IP PORT 由于apiserver尚未运行,因此预计会出现连接拒绝错误。但是,超时意味着负载均衡器无法与控制平面节点通信。如果发生超时,请重新配置负载平衡器以与控制平面节点通信。 将剩余的控制平面节点添加到负载平衡器目标组 0.6.2. 安装keepalived & HAProxy 通过keepalived + haproxy实现的,其中: keepalived是提供一个VIP,通过VIP关联所有的Master节点 然后haproxy提供端口转发功能 由于VIP在Master的机器上,默认配置API Server的端口是6443,所以需要将另外一个端口关联到这个VIP上,一般用8443。如下图所示: 在Master手工安装keepalived, haproxy yum install keepalived yum install haproxy 修改HAProxy的配置文件 配置文件是:haproxy.cfg,默认路径是/etc/haproxy/haproxy.cfg,同时需要手动创建/run/haproxy目录,否则haproxy会启动失败。 注意: bind绑定的就是VIP对外的端口号,这里是8443 balance指定的负载均衡方式是roundrobin方式 server指定的就是实际的Master节点地址以及真正工作的端口号,这里是6443,有多少台Master就写多少条记录 # haproxy.cfg sample global log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy stats socket /var/run/haproxy-admin.sock mode 660 level admin stats timeout 30s user haproxy group haproxy daemon nbproc 1 defaults log global timeout connect 5000 timeout client 50000 timeout server 50000 listen admin_stats bind 0.0.0.0:10080 mode http log 127.0.0.1 local0 err stats refresh 30s stats uri /status stats realm welcome login\ Haproxy stats auth admin:123456 stats hide-version stats admin if TRUE listen kube-master bind 0.0.0.0:8443 mode tcp option tcplog balance roundrobin server tuo-1 172.18.52.34:6443 check inter 10000 fall 2 rise 2 weight 1 server tuo-2 172.18.52.33:6443 check inter 10000 fall 2 rise 2 weight 1 server tuo-3 172.18.52.32:6443 check inter 10000 fall 2 rise 2 weight 1 修改keepalived的配置文件 修改keepalived的配置文件,配置正确的VIP,keepalived的配置文件keepalived.conf的默认路径是/etc/keepalived/keepalived.conf 注意: priority决定Master的主次,数字越小优先级越高 virtual_router_id决定当前VIP的路由号,实际上VIP提供了一个虚拟的路由功能,该VIP在同一个子网内必须是唯一 virtual_ipaddress提供的就是VIP的地址,该地址在子网内必须是空闲未必分配的 # keepalived.cfg sample(Master) global_defs { router_id K8s_Master } vrrp_script check_haproxy { script "killall -0 haproxy" interval 3 weight -2 fall 10 rise 2 } vrrp_instance VI-kube-master { state MASTER interface eno16777728 priority 150 virtual_router_id 51 advert_int 3 authentication { auth_type PASS auth_pass transwarp } virtual_ipaddress { 172.18.52.33 } track_script { check_haproxy } } # keepalived.cfg sample(Backup) global_defs { router_id K8s_Backup_1 } vrrp_script check_haproxy { script "killall -0 haproxy" interval 3 weight -2 fall 10 rise 2 } vrrp_instance VI-kube-master { state BACKUP interface eno16777728 priority 140 virtual_router_id 51 advert_int 3 authentication { auth_type PASS auth_pass transwarp } virtual_ipaddress { 172.18.52.33 } track_script { check_haproxy } } 优先启动主Master的keepalived和haproxy systemctl enable keepalived systemctl start keepalived systemctl enable haproxy systemctl start haproxy 检查keepalived是否启动成功 ip a s # 查看是否有VIP地址分配 # 如果看到VIP地址已经成功分配在eth0网卡上,说明keepalived启动成功 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000 link/ether 00:50:56:a9:d5:be brd ff:ff:ff:ff:ff:ff inet 10.86.13.32/23 brd 10.86.13.255 scope global eth0 valid_lft forever preferred_lft forever **inet 10.86.13.36/32 scope global eth0** valid_lft forever preferred_lft forever inet6 fe80::250:56ff:fea9:d5be/64 scope link valid_lft forever preferred_lft forever # 更保险的方法是查看keepalived的状态和HAProxy的状态 systemctl status keepalived -l ● keepalived.service - LVS and VRRP High Availability Monitor Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; vendor preset: disabled) Active: active (running) since Thu 2018-02-01 10:24:51 CST; 1 months 16 days ago Main PID: 13448 (keepalived) Memory: 6.0M CGroup: /system.slice/keepalived.service ├─13448 /usr/sbin/keepalived -D ├─13449 /usr/sbin/keepalived -D └─13450 /usr/sbin/keepalived -D Mar 20 04:51:15 kube32 Keepalived_vrrp[13450]: VRRP_Instance(VI-kube-master) Dropping received VRRP packet... **Mar 20 04:51:18 kube32 Keepalived_vrrp[13450]: (VI-kube-master): ip address associated with VRID 51 not present in MASTER advert : 10.86.13.36 Mar 20 04:51:18 kube32 Keepalived_vrrp[13450]: bogus VRRP packet received on eth0 !!!** systemctl status haproxy -l ● haproxy.service - HAProxy Load Balancer Loaded: loaded (/usr/lib/systemd/system/haproxy.service; enabled; vendor preset: disabled) Active: active (running) since Thu 2018-02-01 10:33:22 CST; 1 months 16 days ago Main PID: 15116 (haproxy-systemd) Memory: 3.2M CGroup: /system.slice/haproxy.service ├─15116 /usr/sbin/haproxy-systemd-wrapper -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid ├─15117 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds └─15118 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds # 查看kubernetes集群信息 kubectl version **Client Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.1", \ GitCommit:"3a1c9449a956b6026f075fa3134ff92f7d55f812", GitTreeState:"clean", \ BuildDate:"2018-01-03T22:31:01Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.1", \ GitCommit:"3a1c9449a956b6026f075fa3134ff92f7d55f812", GitTreeState:"clean", \ BuildDate:"2018-01-03T22:18:41Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}** 此时,说明keepalived和haproxy都是成功,可以依次将其他Master节点的keepalived和haproxy启动。 此时,通过ip a s命令去查看其中一台非主Master时看不到VIP,因为VIP永远只在主Master节点上,只有当主Master节点挂掉后,才会切换到其他Master节点上。 主Master获取VIP是需要时间的,如果多个Master同时启动,会导致冲突。最稳妥的方式是先启动一台主Master,等VIP确定后再启动其他Master。 0.7. 部署堆叠集群 0.7.1. 部署第一个控制平面节点 在第一个控制平面节点上创建kubeadm-config.yaml文件: apiVersion: kubeadm.k8s.io/v1beta1 kind: ClusterConfiguration kubernetesVersion: stable # 应该设置为使用的版本,例如stable controlPlaneEndpoint: "LOAD_BALANCER_DNS:LOAD_BALANCER_PORT" # 应匹配负载均衡器的地址(或DNS)和端口 ClusterConfiguration: networking: podSubnet: 192.168.0.0/16 建议kubeadm,kubelet,kubectl和Kubernetes的版本匹配。 注意,一些CNI网络插件,需要CIDR,如192.168.0.0/16,但是有些不需要。在ClusterConfiguration配置项的networking对象中设置podSubnet:192.168.0.0/16字段来为Pod设置CIDR。 初始化控制平台 sudo kubeadm init --config=kubeadm-config.yaml --experimental-upload-certs # --experimental-upload-certs参数用于将需要在所有控制平面节点之间共享的证书上传到集群中 # 删除这个参数,实现手动证书复制分发 # 命令执行完成后,会看到如下信息 ... You can now join any number of control-plane node by running the following command on each as a root:\ kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash \ sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 \ --experimental-control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07 Please note that the certificate-key gives access to cluster sensitive data, keep it secret!\ As a safeguard, uploaded-certs will be deleted in 2 hours; If necessary, you can use kubeadm init phase upload-certs to reload certs afterward. Then you can join any number of worker nodes by running the following on each as root:\ kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv \ --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 # 将输出信息保存到文本中,添加控制平面节点和工作节点到集群时,需要使用 # 在kubeadm init的时候使用了`experimental-upload-certs`参数后,主控制平面的证书被加密并上传到kubeadm-certs Secret中 # 要重新上传证书并生成新的解密密钥,请在已加入群集的控制平面节点上使用以下命令: sudo kubeadm init phase upload-certs --experimental-upload-certs 注意,kubeadm-certsSecret和解密秘钥的有效时间是两个小时。 部署CNI插件 必须安装pod网络插件,以便pod可以相互通信,且每个集群只能安装一个Pod网络。 必须在任何应用程序之前部署网络。此外,CoreDNS将不会在安装网络之前启动。 kubeadm仅支持基于容器网络接口(CNI)的网络(并且不支持kubenet)。 注意,Pod网络不能与任何主机网络网络重叠。如果网络插件的首选Pod网络与某些主机网络之间发生冲突,应该考虑一个合适的CIDR替换,并在kubeadm init期间使用--pod-network-cidr并在网络插件的YAML中替换它。 安装Flannel时需要在kubeadm init中添加参数--pod-network-cidr=10.244.0.0/16 设置/proc/sys/net/bridge-nf-call-iptables的值为1,将桥接的IPv4流量传递给iptables的链 保防火墙规则允许参与覆盖网络的所有主机的UDP端口8285和8472流量 一旦pod网络安装完成,CoreDNS就能正常运行,然后就可以开始添加其他节点。 0.7.2. 添加其他控制平面节点 警告:只有在第一个节点完成初始化后,才能按顺序添加新的控制平面节点。 对于每一个控制平面节点,执行如下操作: 执行先前由第一个节点上的kubeadm init输出提供给您的join命令 sudo kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv \ --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 \ --experimental-control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07 # `--experimental-control-plane`参数是告诉kubeadm join创建一个新的控制平面节点 # ` --certificate-key`参数将导致控制平面证书从集群中下`kubeadm-certs`Secret中下载下来,并使用对应的秘钥进行解密 其余控制平面节点添加完成后,开始添加worker节点。 0.7.3. 添加worker节点 可以使用先前存储的命令将工作节点连接到集群,作为kubeadm init命令的输出: sudo kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv \ --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866
0.1. 设计原理 0.1.1. Kata Containers 0.1.2. gVisor 0.1.2.1. 方式一 0.1.2.2. 方式二 0.1.3. Firecracker 0.2. 对比 基于虚拟化或者独立内核的安全容器项目。 Kata Containers的本质就是一个精简的轻量级虚拟机,所以它的特点,就是“像虚拟机一样安全,像容器一样敏捷”。 gVisor项目给容器进程配置一个用Go语言实现的,运行在用户态的、极小的“独立内核”。这个内核对容器进程暴露Linux内核ABI,扮演着“Guest Kernel”的角色,从而达到了将容器和宿主机隔离的目的。 这两种容器实现的本质,都是给进程分配一个独立的操作系统内核,从而避免了让容器共享宿主机的内核。这样,容器进程能够看到的攻击面,就从整个宿主机内核编程了一个极小的、独立的、以容器为单位的内核,从而有效解决了容器进程发生“逃逸”或者多去整个宿主机的控制权的问题。 区别在于: Kata Container使用的是传统的虚拟化及时,通过虚拟硬件模拟出来了一台“小虚拟机”,然后在整个小虚拟机里安装一个裁剪后的Linux内核来实现强隔离。 gVisor的做法更激进,直接使用Go语言模拟出了一个运行在用户态的操作系统内核,然后通过这个模拟的内核来代替容器进程想宿主机发起有限的、可控的系统调用。 0.1. 设计原理 0.1.1. Kata Containers 工作原理如下图所示: Kata Containers的本质是一个轻量化虚拟机,所以启动一个Kata Containers之后,就会看到一个正常的虚拟机在运行,所以一个标准的虚拟机管理程序(VMM,Virtual Machine Manager)是运行Kata Containers必备的一个组件,如图中的Qemu。 使用了虚拟机作为进程的隔离环境之后,Kata Containers原生就带有Pod的概念,这个Kata Containers启动的虚拟机就是一个pod,用户定义的容器,就是运行在这个轻量级虚拟机里的进程。 在具体实现上,Kata Container的虚拟机里会有一个特殊的Init进程负责管理虚拟机里面的用户容器,并且职位这些容器开启Mount Namespace,所以这些用户容器直接,原生就是共享Network以及其他Namespace的。 为了跟上层编排框架如kubernetes对接,Kata Containers项目会启动一系列跟用户容器对应的shim进程,来负责操作这些用户容器的生命周期。这些操作实际上还是要靠虚拟机里的Init进程来帮忙做到。 具体的架构上,Kata Containers的实现方式同一个正常的虚拟机非常类似,原理如下图所示: 当Kata Containers运行起来之后,虚拟机里的用户进程(容器),实际上只能看到虚拟机里的、被裁剪过的Guest Kernel,已经通过Hypervisor虚拟出来的硬件设备。为了能够对这个虚拟机的I/O性能进行优化,Kata Containers也会通过vhost技术(如vhost-user)来实现Guest和Host之间高效的网络通信,并且使用PCI Passthrough(PCI 穿透)技术来让Guest里的进程直接访问宿主机上的物理设备。这些架构设计与实现,其实与常规的虚拟机优化手段基本一致。 0.1.2. gVisor 相比于Kata Containers,gVisor的设计更激进,原理如下图所示: gVisor工作的核心在于它为应用进程、也就是用户容器,启动一个名叫Sentry的进程。而Sentry进程的主要职责,就是提供一个传统的操作系统内核的能力,运行用户程序,执行系统调用。Sentry并不是使用Go语言重新实现了一个完整的Linux内核,只是一个多应用进程“冒充”内核的系统组件。 在这种设计思想下,Sentry需要自己实现一个完整的Linux 内核网络栈,以便处理应用进程的通信请求,然后把封装好的二层帧直接发送给kubernetes设置的Pod的Network Namespace即可。 Sentry对于Volume的操作,需要通过9P协议交给Gofer代理进程来完成。Gofer会代替应用进程直接操作数组局上的文件,并依靠seccomp机制将自己的能力现在在最小集,从而防止恶意应用进程通过Gofer来从容器中“逃逸”出去。 具体实现上,gVisor的Sentry进程有两种不同的实现方式,如下图所示: 0.1.2.1. 方式一 使用Ptrace机制来拦截用户应用的系统调用(system call),然后把这些系统调用交给Sentry来进行处理。这个过程对应用进程来说是完全透明的。 Sentry接下来就扮演操作系统的角色,在用户态执行用户程序,然后仅在需要的时候,才想宿主机发起Sentry自己所需要执行的系统调用。这就是gVisor对用户应用进程进行强隔离的主要手段。不过Ptrace进行系统拦截的性能太差,仅供demo使用。 0.1.2.2. 方式二 这种方式更具有普适性,如下图所示: 在这个实现里,Sentry使用KVM进行系统调用的拦截,这个性能比Ptrace好很多,为了能够做到这一点,Sentry进程必须扮演Guset Kernel的角色,负责执行用户程序,发起系统调用。而这些系统调用被KVM拦截后,还要继续交给Sentry进行处理,只是这个时候,Sentry就切换成了一个普通的宿主机进程的角色,来向宿主机发起它所需要的系统调用。 在这种实现里,Sentry并不会真的像虚拟机那样去虚拟出硬件设备、安装Guest操作系统,它只是借助KVM进行系统调用的拦截,以及处理地址空间切换等细节。 0.1.3. Firecracker Firecracker安全容器项目。这个项目的核心是一个用Rust语言编写的VMM(虚拟机管理器),所有Firecracker和Kata Containers的本质是一样的,不过Kata Containers默认使用的VMM是Qemu,而Firecracker则是自己编写的VMM。所以理论上Kata Containers也可以使用Firecracker运行起来。 0.2. 对比 类别 性能 启动速度和占用资源 系统调用支持 KataContainers 差不多 强一点(系统调用密集的应用,如重I/O,重网络的应用) 高 gVisor(基于KVM) 差不多 强一点(基于用户态内核),频繁拦截系统调用会出现性能急剧下降的情况 低(使用Sentry模拟内核,只是Linux系统调用的一个子集) gVisor这种在用户态运行一个操作系统内核,来为应用进程提供强隔离的思路,是未来安全容器进一步演化的一个非常有前途的方向。gVisor使用User Mode Linux(UML)技术在用户态运行起了一个真正的Linux Kernel来为应用进程提供强隔离,从而避免了重新实现Linux Kernel带来的各种麻烦。这个方向应该是安全容器进化的未来,比Unikernels这种不适合实际场景中使用的思路靠谱。