一、为什么需要使用沙箱运行容器

首先,我们来看看整个K8s调用容器的架构:

1.架构概述

架构分为3个部分,分别时High-level container management、High-level conatiner runtime、Low-level contianer runtime。

专注于运行容器的实际容器运行时通常被称为“Low-level contianer runtime”。支持更多高级功能的运行时,如图像管理和 gRPC/Web API,通常被称为"high-level container tools", “high-level container runtimes” 或者通常简称为 “container runtimes”

容器是使用Linux 命名空间和cgroups 实现的。命名空间让我们可以为每个容器虚拟化系统资源,例如文件系统或网络。Cgroups 提供了一种方法来限制每个容器可以使用的资源量,例如 CPU 和内存。低级运行时负责为容器设置这些命名空间和 cgroup,然后在这些命名空间和 cgroup 中运行命令。

通常,想要在容器中运行应用程序的开发人员需要的不仅仅是低级运行时提供的功能。他们需要有关图像格式、图像管理和共享图像的 API 和功能。这些功能由高级运行时提供。低级运行时只是没有为这种日常使用提供足够的功能。

最后K8s作为容器编排工具,调用高级进行时完成整个环境的容器部署。

2.Low-level contianer runtime
首先,低级别的容器运行时都需要遵循OCI开放容器标准,方便高级别容器运行时进行调用。此外,相比于高级容器运行时,它不具备如下功能:

  1. 创建了一个容器的网络;
  2. 管理容器镜像;
  3. 准备容器的环境;
  4. 管理本地/持久存储。

目前,我们有三个主要的低级容器运行时,其实现的工作有所不同:

  1. runc,这是大多数高级容器运行时(例如 Docker、Podman和contained)的默认设置。这是基于 Docker 最初捐赠的代码;
  2. kata-run来自“Kata Containers”项目,该项目旨在通过在轻量级 VM 中运行每个容器来提供更好的安全性和容器之间的隔离。它是runv和Intel Clear Containers项目的合并;
  3. gVisor由谷歌创建。它通过在严密的安全沙箱中运行每个容器来提供更好的隔离。

3.High-level contianer runtime
高级运行时负责容器镜像的传输和管理、解包镜像以及使用OCI传递给低级运行时来运行容器。通常,高级运行时提供一个守护程序应用程序和一个 API,远程应用程序可以使用它们来逻辑运行容器并监视它们。

同时,希望与 Kubernetes 集成的高级容器运行时需要实现 CRI。CRI 在 Kubernetes 1.5 中引入,充当kubelet和容器运行时之间的桥梁,具体对于每一个高级容器运行时,使用shim(垫片)完成。

目前三个主要的高级容器运行时:

  1. containerd是一个 CRI 兼容的容器运行时,由 Docker 捐赠给 CNCF。它目前是许多 Kubernetes 发行版中的默认设置,例如ad Canonical’s Charmed Kubernetes。它支持所有 OCI 兼容的运行时,并有一个特殊的 shim 用于kata-run;
  2. CRI-O是 Kubernetes 和 Red Hat 创建的符合 OCI 的运行时之间的桥梁。它的一大优势是它可以与 Kubernetes 本身同步发布。每个 CRI-O 版本都与具有相同版本号的 Kubernetes 版本兼容。此运行时是 OpenShift 中的默认值;
  3. Docker 本身也可以用作 CRI 兼容的容器运行时,使用docker-shim。然而,由于 Docker 增加了不必要的复杂性,许多 Kubernetes 分销商正在放弃这种解决方案。

4.High-level container management
本文中指的是K8s。Kubernetes 项目定义了许多标准。与本文相关的是CRI:容器运行时接口。此接口定义了 Kubernetes 如何与高级容器运行时对话。

kubelet 是一个代理,位于 Kubernetes 集群中的每个工作节点上。kubelet 负责管理其节点的容器工作负载。在实际运行工作负载时,kubelet 使用 CRI 与在同一节点上运行的容器运行时进行通信。通过这种方式,CRI 只是一个抽象层或 API,它允许我们切换容器运行时实现,而不是将它们内置到 kubelet 中。

5.为什么需要使用沙箱运行容器
最后,再回到最开始的问题,为什么需要使用沙箱运行容器?目前我们1.22.0版本,还可以使用docker作为容器运行时,今后将变为containerd。但是,无论时docker还是containerd,默认都时通过runc这个低级别容器运行时实现的。而在前面的笔记中,我们看到了,如果使用runc来部署容器,会共享宿主机的各种空间,比如进程空间、内核空间。那么,如果我们容器的进程也会存在于宿主机上。那么,如果容器运行的进程存在漏洞,一旦被入侵,就会有一定可能性对我们宿主机造成损坏。所以,我们可以用沙箱的方式运行容器,隔离容器与宿主机的环境,在宿主机内,也就看到不容器内的进程了。

而runc部署的容器时不持支持沙箱的,所以我们需要使用另外一个低级别的容器进行时gVisor来完成操作。

Google gVisor 是支持 Google 计算平台 (GPC) App Engine、Cloud Functions 和 CloudML 的沙箱技术。谷歌意识到在公共云基础设施中运行不受信任的应用程序的风险以及使用虚拟机沙箱应用程序的效率低下,并开发了一个用户空间内核用来对不受信任的应用程序进行沙箱处理。gVisor 沙箱通过拦截从应用程序到主机内核的所有系统调用,并在用户空间中使用 gVisor 的内核实现Sentry进行处理这些系统调用。那么就算容器的恶意代码对内核破坏也是容器的内核,而非宿主机的内核。

二、配置docker使用gVisor做为runtime

使用一台单独的ubuntu设备作为宿主机即可。

1.安装docker

apt-get update
apt-get install -y docker-ce

如果需要加速,可以添加镜像加速器:

cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors": ["https://frz7i079.mirror.aliyuncs.com"],
}
EOF

重启进程并设置docker开机启动。

systemctl daemon-reload ; systemctl restart docker; systemctl enable docker

可以看到,目前docker支持的runtime为runc:


root@vms75:~# docker info | grep runtime
WARNING: No swap limit supportRuntimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc

2.安装gVisor

安装最新的gVisor:

set -e
ARCH=$(uname -m)
URL=https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}
wget ${URL}/runsc ${URL}/runsc.sha512 \${URL}/containerd-shim-runsc-v1 ${URL}/containerd-shim-runsc-v1.sha512
sha512sum -c runsc.sha512 \-c containerd-shim-runsc-v1.sha512
rm -f *.sha512
chmod a+rx runsc containerd-shim-runsc-v1
sudo mv runsc containerd-shim-runsc-v1 /usr/local/bin

3.将gVisor设置为docker的runtime

命令如下:

/usr/local/bin/runsc install
sudo systemctl reload docker

再次查看/etc/docker/daemon.json文件,可以看到其中已经添加了runtimes的新路径,runsc为新runtime的名称,可以修改,最终路径指向了gvisor。

root@vms75:~# cat /etc/docker/daemon.json
{"runtimes": {"runsc": {"path": "/usr/local/bin/runsc"}}
}

可以通过如下的命令测试能够使用runsc运行容器:

docker run --rm --runtime=runsc hello-world

结果如下,说明runsc安装成功:

root@vms75:~# docker run --rm --runtime=runsc hello-worldHello from Docker!
This message shows that your installation appears to be working correctly.To generate this message, Docker took the following steps:1. The Docker client contacted the Docker daemon.2. The Docker daemon pulled the "hello-world" image from the Docker Hub.(amd64)3. The Docker daemon created a new container from that image which runs theexecutable that produces the output you are currently reading.4. The Docker daemon streamed that output to the Docker client, which sent itto your terminal.To try something more ambitious, you can run an Ubuntu container with:$ docker run -it ubuntu bashShare images, automate workflows, and more with a free Docker ID:https://hub.docker.com/For more examples and ideas, visit:https://docs.docker.com/get-started/

接着查看docker infor,可以看到如下信息:

 Runtimes: runc runscDefault Runtime: runc

现在docker存在两个低等级容器运行时,默认是runc,我们可以通过如下的命令其修改为runsc(即gvisor)。

vim /lib/systemd/system/docker.service

接着在ExecStart处设置–default-runtime runsc。

ExecStart=/usr/bin/dockerd --default-runtime runsc  -H fd:// --containerd=/run/containerd/containerd.sock

重启docke进程:

systemctl daemon-reload ; systemctl restart docker

再次查看docker info,可以看到默认的runtime已经修改为runsc了,后续使用gVisor运行容器时,不需要再使用–runtime=runsc指定了:

 Default Runtime: runsc

4.检查沙箱功能
创建一个nginx镜像,然后在宿主机中查看是否有对应的进程:

docker run -dit --name=web1 --restart=always nginx

查看宿主机进程:

root@vms75:~# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS     NAMES
2eda45493c41   nginx     "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes   80/tcp    web1
root@vms75:~# ps aux | grep -v grep | grep nginx

可以看到,虽然我们创建了nginx容器,但是宿主机上并没有对应的进程。

三、配置containerd使用gVisor做为runtime

使用一台单独的ubuntu设备作为宿主机即可。

1.安装containerd

apt-get update
apt-get install containerd.io cri-tools -y
systemctl enable containerd --now
containerd config default > /etc/containerd/config.toml

/etc/containerd/config.toml为containerd的配置文件,我们可以修改如下的内容:

  1. 搜索mirrors,修改加速器https://frz7i079.mirror.aliyuncs.com
  2. 搜索sandbox_image 修改为registry.aliyuncs.com/google_containers/pause:3.5
  3. 搜索runc.options修改为SystemdCgroup = true

containerd 客户端工具有ctr 和crictl 两个,如果想要执行crictl命令则需要使用如下命令:

crictl config runtime-endpoint unix:///var/run/containerd/containerd.sock

完成后重启contianerd:

systemctl restart containerd ; systemctl enable containerd

现在可以使用ctr或者crictl做一些镜像管理操作,例如:

  1. ctr i list/crictl images:查看镜像
  2. crictl pull xxx/ctr i pull xxx:下载镜像

需要注意的是,containerd 和docker 相比多了命名空间的概念。当使用crictl 命令的时候,都是在k8s.io这个命名空间里的,而ctr 默认是在default 这个命名空间里。所以当crictl 下载镜像之后,会自动创建一个k8s.io,而下载的镜像就是放在k8s.io 里的。

例如,现在我们使用crictl下载镜像:

crictl pull docker.io/nginx

使用ctr i list命令无法查看镜像,而crictl images则可以:

root@vms74:~# ctr i list
REF TYPE DIGEST SIZE PLATFORMS LABELS
root@vms74:~# crictl images
IMAGE                     TAG                 IMAGE ID            SIZE
docker.io/library/nginx   latest              f652ca386ed13       56.7MB

我们可以查看命名空间:

root@vms74:~# ctr ns list
NAME   LABELS
k8s.io

可以看到,这里创建了一个k8s.io的命名空间。如果想要切换命名空间到k8s.io,可以使用如下的命令:

export CONTAINERD_NAMESPACE=k8s.io

可以看到无论是ctr或者crictl的命令都和docker中格式不一致。当习惯使用docker后,为了方便使用containerd,我们可以安装nerdctl工具,让我们containerd的命令格式和docker一致。最新的二进制版本nerdctl可以从如下连接下载:https://github.com/containerd/nerdctl/releases,这里下载nerdctl-0.15.0-linux-amd64.tar.gz即可。

接着下载其依赖的CNI插件,下载地址为:https://github.com/containernetworking/plugins/releases,这里下载即可。

下载完成后,在ubuntu宿主机中通过如下的命令解压:

tar zxvf nerdctl-0.15.0-linux-amd64.tar.gz
mkdir -p /opt/cni/bin/
tar zxf cni-plugins-linux-amd64-v1.0.1.tgz -C /opt/cni/bin/

完成后,将当前目录下的nerdctl拷贝到/bin目录下即可:

mv nerdctl /bin/

然后启动nerdctl的补全功能,在/etc/profile里添加source <(nerdctl completion bash),最后再使用如下命令即可:

source /etc/profile

现在,我们就可以像使用docker一样的使用containerd了,只需要把docker命令中的docker修改为nerdctl即可。例如,查看镜像:

root@vms74:~# nerdctl images
REPOSITORY    TAG                                                                 IMAGE ID        CREATED           PLATFORM       SIZE
nginx         latest                                                              9522864dd661    22 minutes ago    linux/amd64    149.1 MiB
nginx         <none>                                                              9522864dd661    22 minutes ago    linux/amd64    149.1 MiB
sha256        f652ca386ed135a4cbe356333e08ef0816f81b2ac8d0619af01e2b256837ed3e    9522864dd661    22 minutes ago    linux/amd64    149.1 MiB

删除镜像:

root@vms74:~# nerdctl rmi nginx:latest
Untagged: docker.io/library/nginx:latest@sha256:9522864dd661dcadfd9958f9e0de192a1fdda2c162a35668ab6ac42b465f0603
Deleted: sha256:9321ff862abbe8e1532076e5fdc932371eff562334ac86984a836d77dfb717f5
Deleted: sha256:0664b7821b6050b321b14cdede97c2079ae45aff22beb4a42f7595294f5be62d
Deleted: sha256:c9fcd9c6ced8b793a0ad4f93820c1d51d94c3b1fca93000d93e9e8eefa6fdb38
Deleted: sha256:d3e1dca44e8225cdd06b6bf7cdfc847e3ab9f09ab6aeefb006e2e8f02f0dd26c
Deleted: sha256:82caad489ad7bc7e1ae6f17bb1e9ade2bca44a41a07cc8c5587af8a2de2f536a
Deleted: sha256:2bed47a66c07ecddfea2bc9c128d81b31272d99b69aff1fb4edc079c4dbf56e7

部署nginx容器和查看容器:

root@vms74:~# nerdctl run -d --name=web1 --restart=always nginx
docker.io/library/nginx:latest:                                                   resolved       |++++++++++++++++++++++++++++++++++++++|
index-sha256:9522864dd661dcadfd9958f9e0de192a1fdda2c162a35668ab6ac42b465f0603:    done           |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:4424e31f2c366108433ecca7890ad527b243361577180dfd9a5bb36e828abf47: done           |++++++++++++++++++++++++++++++++++++++|
config-sha256:f652ca386ed135a4cbe356333e08ef0816f81b2ac8d0619af01e2b256837ed3e:   done           |++++++++++++++++++++++++++++++++++++++|
elapsed: 1.1 s                                                                    total:   0.0 B (0.0 B/s)
a03b306cc186ab6e15024d460e8e281d5551f53aa5a1a8f87783e355faaaf535
root@vms74:~# nerdctl ps
CONTAINER ID    IMAGE                             COMMAND                   CREATED           STATUS    PORTS    NAMES
a03b306cc186    docker.io/library/nginx:latest    "/docker-entrypoint.…"    31 seconds ago    Up                 web1

2.安装gVisor

安装最新的gVisor

set -e
ARCH=$(uname -m)
URL=https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}
wget ${URL}/runsc ${URL}/runsc.sha512 \${URL}/containerd-shim-runsc-v1 ${URL}/containerd-shim-runsc-v1.sha512
sha512sum -c runsc.sha512 \-c containerd-shim-runsc-v1.sha512
rm -f *.sha512
chmod a+rx runsc containerd-shim-runsc-v1
sudo mv runsc containerd-shim-runsc-v1 /usr/local/bin

3.将gVisor设置为containerd的runtime
更新/etc/containerd/config.toml。确保选中containerd-shim-runsc-v1是 ${PATH}或在同一目录中containerd的二进制文件。这里的runsc也就是gvisor。

cat <<EOF | sudo tee /etc/containerd/config.toml
version = 2
[plugins."io.containerd.runtime.v1.linux"]shim_debug = true
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]runtime_type = "io.containerd.runsc.v1"
EOF

重新启动containerd:

systemctl daemon-reload ; systemctl restart containerd

查看containerd是否加载了gvisor:

root@vms74:~# crictl info | grep runtime"runtimes": {"runtimeType": "io.containerd.runc.v2","runtimeEngine": "","runtimeRoot": "","runtimeType": "io.containerd.runsc.v1","runtimeEngine": "","runtimeRoot": "",

可以看到,目前containerd已经支持了runsc。

4.检查沙箱功能

使用gvisor作为runtime:

run -d --name=web1 --restart=always --runtime=runsc nginx

查看宿主机上是否有对应的进程,可以看到,没有对应的进程。说明整个容器已经在沙箱中运行了。

root@vms74:~# ps aux | grep -v grep | grep nginx
root@vms74:~#

四、在K8s环境中,使用containerd(使用gVisor)做为runtime

注意:如果K8s使用docker作为high-level conatiner runtime,那么docker不支持使用gVisor作为low-level conatiner runtime。所以这里我们需要使用containerd作为K8s的high-level conatiner runtime。

1.k8s集群环境:

底层系统为ubuntu18.04,Master node的IP地址为192.168.26.71/24,三个Worker node的IP地址为192.168.26.72/24、192.168.26.73/24,192.168.26.74/24。网络通过calico来创建。

目前,已经搭建好了master1、worker1和worker2的K8s集群环境。在master上查看集群信息:可以看到目前三台设备都使用docker作为runtime。

root@vms71:~# kubectl get nodes -o wide
NAME            STATUS   ROLES                  AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION       CONTAINER-RUNTIME
vms71.rhce.cc   Ready    control-plane,master   15d   v1.22.0   192.168.26.71   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   docker://20.10.11
vms72.rhce.cc   Ready    <none>                 15d   v1.22.0   192.168.26.72   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   docker://20.10.11
vms73.rhce.cc   Ready    <none>                 15d   v1.22.0   192.168.26.73   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   docker://20.10.11

现在,我们需要将worker node3,也就是将再上一步已经配置完成的以containerd作为runtime的设备加入到这个已经存在的集群中。

2.加入worker node3
在worker node3上,配置/etc/hosts,关闭swap:

swapoff -a ; sed -i '/swap/d' /etc/fstab

切换命名空间到k8s.io:

export CONTAINERD_NAMESPACE=k8s.io

设置加入集群所需要的参数并让其生效:

cat > /etc/modules-load.d/containerd.conf <<EOF
overlay
br_netfilter
EOFmodprobe overlay
modprobe br_netfiltercat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOFsysctl -p /etc/sysctl.d/k8s.conf

在worker node3上下载calico所需要的镜像,首先下载对应的yaml文件:

curl https://docs.projectcalico.org/manifests/calico.yaml -O

然后使用如下命令,查看所需要的镜像,并下载到本地:

grep image calico.yaml

接着安装K8s:

apt-get install -y kubeadm=1.22.0-00 kubelet=1.22.0-00 kubectl=1.22.0-00
systemctl restart kubelet ; systemctl enable kubelet

在master上使用如下命令生成给worker node3加入集群的命令:

root@vms71:~# kubeadm token create --print-join-command
kubeadm join 192.168.26.71:6443 --token nvqate.m94p1pzp5obke6sq --discovery-token-ca-cert-hash sha256:8a808cf9415018407a86963ce4af14ce3b0c830c56eaa27ce9b52baa2504116a

复制加入集群的命令到worker node3上。等级一小段时间,然后回到master上,使用命令查看worker node3是否加入成功:

root@vms71:~# kubectl get nodes -owide
NAME            STATUS   ROLES                  AGE     VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION       CONTAINER-RUNTIME
vms71.rhce.cc   Ready    control-plane,master   15d     v1.22.0   192.168.26.71   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   docker://20.10.11
vms72.rhce.cc   Ready    <none>                 15d     v1.22.0   192.168.26.72   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   docker://20.10.11
vms73.rhce.cc   Ready    <none>                 15d     v1.22.0   192.168.26.73   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   docker://20.10.11
vms74.rhce.cc   Ready    <none>                 9m18s   v1.22.0   192.168.26.74   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   containerd://1.4.12

可以看到已经加入成功,状态为ready,且运行的high-level container runtime为containerd。

3.让k8s支持gVisor
在worker node3上,修改内核文件,让kubelet也能使用gVisor(runsc):

cat > /etc/systemd/system/kubelet.service.d/0-cri-containerd.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--container-runtime=remote --runtime-request-timeout=15m
--container-runtime-endpoint=unix:///run/containerd/containerd.sock"
EOFsystemctl daemon-reload ; systemctl restart kubelet.service

4.在k8s环境中部署使用runsc而非runc作为worker node3的low-level container runtime
在master上创建一个runtime class,yaml文件如下:

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:name: myclass
handler: runsc

handler是对应的的 CRI (low-level container runtime)配置的名称,这里指定runsc。应用yaml文件后查看是否部署成功:

root@vms71:~# kubectl get runtimeclasses.node.k8s.io
NAME      HANDLER   AGE
myclass   runsc     12s

5.部署pod在worker node3中的沙箱中工作

在master上,为worker node3打一个标签,方便后续部署pod:

root@vms71:~# kubectl label nodes vms74.rhce.cc xx=xx
node/vms74.rhce.cc labeled
root@vms71:~# kubectl get nodes -l xx=xx
NAME            STATUS   ROLES    AGE   VERSION
vms74.rhce.cc   Ready    <none>   24m   v1.22.0

所使用的yaml文件如下,指定pod将部署在worker node3上,同时指定了runtime class为我们创建的myclass:

apiVersion: v1
kind: Pod
metadata:creationTimestamp: nulllabels:run: pod1name: pod1
spec:terminationGracePeriodSeconds: 0runtimeClassName: myclassnodeSelector:xx: xxcontainers:- image: nginximagePullPolicy: IfNotPresentname: pod1resources: {}dnsPolicy: ClusterFirstrestartPolicy: Always
status: {}

接着使用如下yaml文件创建pod,并查看:

root@vms71:~# kubectl get pods -owide
NAME   READY   STATUS    RESTARTS   AGE   IP               NODE            NOMINATED NODE   READINESS GATES
pod1   1/1     Running   0          5s    10.244.183.193   vms74.rhce.cc   <none>           <none>

可以看到,pod1已经在worker node3上运行了。那么我们切换到worker node3,查看是否以沙箱的形式运行pod1。使用如下命令查看宿主机上是否存在nginx的进程:

root@vms74:~# ps aux | grep -v grep | grep nginx
root@vms74:~#

可以看到,pod1已经通过runsc以沙箱的形式运行了。

除此之外,KATA这个low-level container runtime也可以将容器环境和宿主机环境隔离,其使用和安装方法和gvisor十分相似。具体步骤请参考:https://github.com/kata-containers/documentation/blob/master/install/ubuntu-installation-guide.md

整理资料来源:
What’s up with CRI-O, Kata Containers and Podman?:https://merlijn.sebrechts.be/blog/2020-01-docker-podman-kata-cri-o/
Container Runtimes series:https://www.ianlewis.org/en/container-runtimes-part-1-introduction-container-r
Making Containers More Isolated: An Overview of Sandboxed Container Technologies: https://unit42.paloaltonetworks.com/making-containers-more-isolated-an-overview-of-sandboxed-container-technologies/
gvisor install:https://gvisor.dev/docs/user_guide/install/
runtimeclass:https://kubernetes.io/zh/docs/concepts/containers/runtime-class/#2-创建相应的-runtimeclass-资源
kata runtime ubuntu: https://github.com/kata-containers/documentation/blob/master/install/ubuntu-installation-guide.md
《老段CKS课程》

二十九、K8s最小服务漏洞3-gVisor沙箱相关推荐

  1. 二十八、K8s最小服务漏洞2-OPA

    一.OPA概述 OPA (Open Policy Agent)背后的主要动机是在整个环境中实现统一的策略实施.通常,组织需要在运行其应用程序的环境中应用各种策略.可能需要这些策略来满足合规性要求.实现 ...

  2. Docker(二十九)k8s 创建动态存储,基于nfs 的storageclass

    可参照官方的配置 https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client/deploy 第一种 ...

  3. 二十九、电子商务服务推荐项目基本描述

    1. 电子商务用户行为分析综合案例 本案例主要的研究对象是法律网站,它是一家电子商务类的大型法律咨询网站,致力于为用户提供丰富的法律信息与专业咨询服务,并为律师与律师事务所提供卓有成效的互联网整合营销 ...

  4. WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载]

    原文:WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载] 我们有两种典型的WCF调用方式:通过SvcUtil.exe(或者添加Web引用)导入发布的服务元数据生成服务代理相关的代码 ...

  5. 【Microsoft Azure 的1024种玩法】二十九.基于Azure VM快速实现网络入侵检测 (IDS) 及网络安全监视 (NSM)

    [简介] 数据包捕获是一个重要组件,可以实施网络入侵检测系统 (IDS) 并执行网络安全监视 (NSM). 我们可以借助开源 IDS 工具来处理数据包捕获,并检查潜在网络入侵和恶意活动的签名. 使用网 ...

  6. 2021年大数据Hadoop(二十九):​​​​​​​关于YARN常用参数设置

    全网最详细的Hadoop文章系列,强烈建议收藏加关注! 后面更新文章都会列出历史文章目录,帮助大家回顾知识重点. 目录 本系列历史文章 前言 关于yarn常用参数设置 设置container分配最小内 ...

  7. SAP UI5 应用开发教程之二十九 - SAP UI5 的路由和导航功能介绍试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...

  8. (二十九 ~ 三十一)巴菲特与索罗斯的投资习惯:投资习惯

    作者:chen_h 微信号 & QQ:862251340 微信公众号:coderpai (一)巴菲特与索罗斯的投资习惯:思考习惯的力量 (二)巴菲特与索罗斯的投资习惯:七种致命的投资信念 (三 ...

  9. 妙!二十九招驱蚊止痒不再愁

    天气热了,蚊子也多了,让人烦不胜烦,如何才可解决蚊子带来的困扰,现整理了二十九个妙法,解决蚊子不在愁! 1.在家庭中可用浓肥皂涂抹可迅速止痒,或用香皂蘸水在红肿处涂抹. 原因是肥皂高级脂肪酸的钠盐.如 ...

最新文章

  1. Form 中FND_Message的用法总结
  2. 中国汽车融资租赁业运营前景与投资战略规划报告2022-2028年版
  3. 数据结构_栈与队列_总结
  4. Java中的注解以及应用 @Deprecated @SupressWarning @Override
  5. bianma 水平 技巧
  6. 2018年全国多校算法寒假训练营练习比赛(第一场)C. 六子冲(模拟)
  7. JavaScript中call,apply,bind方法
  8. 深入理解 JVM 之 垃圾回收机制
  9. 国际版多时区设计方案【转】
  10. C# list删除 另外list里面的元素_Java集合大全Map,Set,List
  11. Apple M1 开启HiDPI的新方法,无需关闭SIP,无需SwitchResX
  12. 普通链接二维码打开小程序实现动态传递参数
  13. 论文标题管理----WPS自定义多级自动编号列表
  14. python flask豆瓣微信小程序案例
  15. 团队建设:如何打造一个优秀的团队
  16. html密码域的type属性,查看网页黑点密码 将type=password中password修改成为text
  17. ImageMagick将图片转为渐进式jpeg(progressive jpeg)
  18. 历届上海电影节获奖名单
  19. springboot工程中限流方式
  20. 正负分数计算机在线算使用,分数的表示以及计算(c++)

热门文章

  1. 当java碰到防火墙
  2. python如何定义正整数_Python如何将一个正整数分解为质因数相乘
  3. AD20的一些基本操作
  4. div实现拖动时候出现禁止图标解决——e.preventDefault e.preventDefault();
  5. html的tab页面切换刷新,切换tab页,页面局部刷新,地址栏路径修改
  6. Windows自带安卓模拟器;安装使用Hyper-V;好用的安卓模拟器;亚马逊应用商店;WSA;适用于Android的Windows系统;在Windows上安装apk;apk转exe;BIOS错误……
  7. 全国各地电台FM.ini汇总
  8. cobble批量装机原理与部署
  9. 获取海康摄像机/录像机rtsp视频流地址格式
  10. NS3 Tutorial 中文版:第三章 快速上手