以沙箱的方式執行容器:安全容器gvisor

人生的哲理發表於2024-06-20

目錄
  • 一.系統環境
  • 二.前言
  • 三.安全容器隔離技術簡介
  • 四.Gvisor簡介
  • 五.容器runtime簡介
  • 六.docker容器缺陷
  • 七.配置docker使用gVisor作為runtime
    • 7.1 安裝docker
    • 7.2 升級系統核心
    • 7.3 安裝gvisor
    • 7.4 配置docker預設的runtime為gVisor
    • 7.5 docker使用gVisor作為runtime建立容器
  • 八.配置containerd使用gvisor作為runtime
    • 8.1 安裝containerd
    • 8.2 安裝gVisor
    • 8.3 配置containerd支援gVisor
    • 8.4 containerd使用gvisor作為runtime建立容器
  • 九.在k8s環境裡,配置containerd作為高階別runtime,containerd使用gvisor作為低階別runtime
    • 9.1 把ubuntuk8sclient節點加入k8s叢集
    • 9.2 配置kubelet使其支援gVisor
    • 9.3 建立容器執行時類(Runtime Class)
    • 9.4 使用gVisor建立pod
  • 十.總結

一.系統環境

本文主要基於Kubernetes1.22.2和Linux作業系統Ubuntu 18.04。

伺服器版本 docker軟體版本 Kubernetes(k8s)叢集版本 gVisor軟體版本 containerd軟體版本 CPU架構
Ubuntu 18.04.5 LTS Docker version 20.10.14 v1.22.2 1.0.2-dev 1.6.4 x86_64

Kubernetes叢集架構:k8scludes1作為master節點,k8scludes2,k8scludes3作為worker節點。

伺服器 作業系統版本 CPU架構 程序 功能描述
k8scludes1/192.168.110.128 Ubuntu 18.04.5 LTS x86_64 docker,kube-apiserver,etcd,kube-scheduler,kube-controller-manager,kubelet,kube-proxy,coredns,calico k8s master節點
k8scludes2/192.168.110.129 Ubuntu 18.04.5 LTS x86_64 docker,kubelet,kube-proxy,calico k8s worker節點
k8scludes3/192.168.110.130 Ubuntu 18.04.5 LTS x86_64 docker,kubelet,kube-proxy,calico k8s worker節點

二.前言

容器技術的發展極大地提高了開發和部署的效率,但容器的安全性一直是一個不容忽視的問題。傳統的Docker容器雖然方便快捷,但在隔離機制上存在一定的缺陷。本文將介紹一種更為安全可靠的容器執行時解決方案——Gvisor。

以沙箱的方式執行容器的前提是已經有一套可以正常執行的Kubernetes叢集,關於Kubernetes(k8s)叢集的安裝部署,可以檢視部落格《Ubuntu 安裝部署Kubernetes(k8s)叢集》https://www.cnblogs.com/renshengdezheli/p/17632858.html。

三.安全容器隔離技術簡介

安全容器是一種執行時技術,為容器應用提供一個完整的作業系統執行環境,但將應用的執行與宿主機作業系統隔離開,避免應用直接訪問主機資源,從而可以在容器主機之間或容器之間提供額外的保護。

四.Gvisor簡介

gVisor是由Google開發的一種輕量級的容器隔離技術。它透過在容器與主機作業系統之間插入一個虛擬化層來實現隔離。gVisor提供了一個類似於Linux核心的API,使得容器可以在一個更加受控的環境中執行。它使用了一種稱為“Sandbox”的機制,將容器的系統呼叫轉換為對gVisor的API呼叫,然後再由gVisor轉發給宿主作業系統。這種方式可以有效地隔離容器與主機作業系統之間的資源訪問,提高了容器的安全性。

gVisor的虛擬化層引入了一定的效能開銷,但是相對於傳統的虛擬機器來說,它的效能損失較小。根據Google的測試資料,gVisor的效能損失在10%左右。這主要是因為gVisor使用了一些最佳化技術,如JIT編譯器和快取機制,來減少虛擬化層的開銷。gVisor還支援多核併發,可以在多核系統上實現更好的效能。

gVisor 工作的核心,在於它為應用程序、也就是使用者容器,啟動了一個名叫 Sentry 的程序。 而 Sentry 程序的主要職責,就是提供一個傳統的作業系統核心的能力,即:執行使用者程式,執行系統呼叫。所以說,Sentry 並不是使用 Go 語言重新實現了一個完整的 Linux 核心,而只是一個對應用程序“冒充”核心的系統元件。

在這種設計思想下,我們就不難理解,Sentry 其實需要自己實現一個完整的 Linux 核心網路棧,以便處理應用程序的通訊請求。然後,把封裝好的二層幀直接傳送給 Kubernetes 設定的 Pod 的 Network Namespace 即可。

image-20240607111625439

五.容器runtime簡介

在容器技術中,執行時(Runtime)是管理容器生命週期的軟體。根據其提供的功能複雜度,可以將容器執行時分為低階別執行時和高階別執行時。

低階別執行時(Low-Level Runtime)通常指的是直接與作業系統核心互動的容器執行時管理工具。這些工具負責容器映象的載入、容器的建立、啟動、停止以及容器內部程序的管理。低階別執行時提供的功能主要包括:

  • 容器映象管理:處理容器的映象下載、儲存和更新。
  • 容器生命週期管理:包括容器的建立、執行、暫停、恢復、停止和刪除。
  • 程序和資源隔離:透過作業系統的控制組(cgroups)和名稱空間(namespaces)實現資源的隔離和分配。
  • 網路配置:為容器提供網路介面和IP地址,以及容器間的通訊機制。

低階別執行時有runC,lxc,gvisor,kata等等。

高階別執行時(High-Level Runtime)則通常是指在低階別執行時之上的容器編排和管理工具,它們提供了更高階的抽象和更多的管理功能。這些工具通常包括:

  • 容器編排:自動化容器的部署、擴充套件和管理。
  • 服務發現和負載均衡:自動配置服務間的相互發現和流量分配。
  • 儲存編排:管理容器的持久化資料和儲存卷。
  • 資源監控和日誌管理:收集容器執行的監控資料和日誌資訊,以供分析和監控使用。

高階別執行時有docker,containerd,podman,ckt,cri-o,高階別執行時會呼叫低階別runtime。

k8s本身是不管理容器的,管理容器需要呼叫高階別執行時,k8s呼叫高階別執行時需要使用shim(墊片)介面,呼叫docker使用dockershim,呼叫containerd使用containerdshim,以此類推,kubelet裡內建了dockershim,k8s1.24的時候要去除dockershim程式碼。

在實際應用中,低階別執行時和高階別執行時通常是協作工作的。低階別執行時負責底層的容器管理,而高階別執行時則在此基礎上提供了更復雜的業務邏輯和自動化管理功能。

六.docker容器缺陷

可以檢視docker預設的執行時,現在預設的runtime是runc。

root@k8scludes1:~# docker info | grep Runtime
 Runtimes: runc io.containerd.runc.v2 io.containerd.runtime.v1.linux
 Default Runtime: runc

現在宿主機上沒有nginx程序。現在提出一個問題:“在宿主機上使用runc執行一個nginx容器,nginx容器執行著nginx程序,宿主機沒執行nginx程序,在宿主機裡能否看到nginx程序嗎?”

root@k8scludes1:~# ps -ef | grep nginx | grep -v grep

現在有一個nginx映象。

root@k8scludes1:~# docker images | grep nginx
nginx                                                             latest    605c77e624dd   5 months ago    141MB

使用nginx映象建立一個容器。關於建立容器的詳細操作,請檢視部落格《一文搞懂docker容器基礎:docker映象管理,docker容器管理》。

root@k8scludes1:~# docker run -dit --name=nginxrongqi --restart=always nginx
7844b98cf01cc1b6ba05c575d284146c47cb3fb66e1fa61d6eeac696f0dbc1c3

root@k8scludes1:~# docker ps | grep nginx
7844b98cf01c   nginx                                               "/docker-entrypoint.…"   8 seconds ago    Up 6 seconds    80/tcp    nginxrongqi

檢視宿主機的nginx程序,宿主機可以看到nginx程序。

docker預設的runtime為runc,透過runc建立出來的容器,會共享宿主機的程序空間和核心空間,容器的程序是暴露給宿主機的,如果容器裡存在漏洞,不法分子會使用容器漏洞影響到宿主機的安全。

root@k8scludes1:~# ps -ef | grep nginx 
root      45384  45337  0 15:33 pts/0    00:00:00 nginx: master process nginx -g daemon off;
systemd+  45465  45384  0 15:33 pts/0    00:00:00 nginx: worker process
systemd+  45466  45384  0 15:33 pts/0    00:00:00 nginx: worker process
systemd+  45467  45384  0 15:33 pts/0    00:00:00 nginx: worker process
systemd+  45468  45384  0 15:33 pts/0    00:00:00 nginx: worker process
root      46215   6612  0 15:34 pts/0    00:00:00 grep --color=auto nginx

以沙箱的方式執行容器,在宿主機裡就看不到容器裡執行的程序了,runc預設是不支援以沙箱的方式執行容器的,所以我們需要配置高階別runtime呼叫其他的低階別runtime執行,以實現沙箱的方式執行容器。

七.配置docker使用gVisor作為runtime

7.1 安裝docker

我們在客戶端機器etcd2(centos系統)上安裝docker。

[root@etcd2 ~]# yum -y install docker-ce

設定docker開機自啟動並現在啟動docker。

[root@etcd2 ~]# systemctl enable docker --now
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.

[root@etcd2 ~]# systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: active (running) since 二 2022-06-07 11:07:18 CST; 7s ago
     Docs: https://docs.docker.com
 Main PID: 1231 (dockerd)
   Memory: 36.9M
   CGroup: /system.slice/docker.service
           └─1231 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

檢視docker版本。

[root@etcd2 ~]# docker --version
Docker version 20.10.12, build e91ed57

配置docker映象加速器。

[root@etcd2 ~]# vim /etc/docker/daemon.json

[root@etcd2 ~]# cat /etc/docker/daemon.json
{
"registry-mirrors": ["https://frz7i079.mirror.aliyuncs.com"] 
}

重啟docker。

[root@etcd2 ~]# systemctl restart docker

設定iptables不對bridge的資料進行處理,啟用IP路由轉發功能。

[root@etcd2 ~]# vim /etc/sysctl.d/k8s.conf 

[root@etcd2 ~]# cat /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1

使配置生效。

[root@etcd2 ~]# sysctl -p /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1

現在docker預設的runtime為runc。

[root@etcd2 ~]# docker info | grep -i runtime
 Runtimes: runc io.containerd.runc.v2 io.containerd.runtime.v1.linux
 Default Runtime: runc

下面開始配置docker使用gvisor作為runtime。

7.2 升級系統核心

檢視作業系統版本。

[root@etcd2 ~]# cat /etc/redhat-release 
CentOS Linux release 7.4.1708 (Core) 

檢視系統核心。

[root@etcd2 ~]# uname -r
3.10.0-693.el7.x86_64

gVisor supports x86_64 and ARM64, and requires Linux 4.14.77+ ,安裝gVisor需要Linux核心高於4.14.77,而當前核心版本只有3.10.0,需要升級系統核心。升級系統核心分為離線升級系統核心和線上升級系統核心,在部落格《centos7 離線升級/線上升級作業系統核心》中進行了詳細描述。

本文采用離線升級系統核心的方法。

更新yum源倉庫。

[root@etcd2 ~]# yum -y update

啟用 ELRepo 倉庫,ELRepo 倉庫是基於社群的用於企業級 Linux 倉庫,提供對 RedHat Enterprise (RHEL) 和 其他基於 RHEL的 Linux 發行版(CentOS、Scientific、Fedora 等)的支援。ELRepo 聚焦於和硬體相關的軟體包,包括檔案系統驅動、顯示卡驅動、網路驅動、音效卡驅動和攝像頭驅動等。

匯入ELRepo倉庫的公共金鑰。

[root@etcd2 ~]#  rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org

安裝ELRepo倉庫的yum源。

[root@etcd2 ~]#  rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm

從elrepo下載系統核心包,如果不匯入ELRepo倉庫的公共金鑰和安裝ELRepo倉庫的yum源,是下載不了核心包的。

[root@etcd2 ~]# wget https://elrepo.org/linux/kernel/el7/x86_64/RPMS/kernel-lt-5.4.160-1.el7.elrepo.x86_64.rpm

清華的這個映象站可以直接下載。

[root@etcd2 ~]# wget https://mirrors.tuna.tsinghua.edu.cn/elrepo/kernel/el7/x86_64/RPMS/kernel-lt-5.4.197-1.el7.elrepo.x86_64.rpm --no-check-certificate

現在核心包就下載好了。

kernel-ml代表主線版本,總是保持主線最新的核心,kernel-lt代表長期支援版本,支援週期更長,如果你要追求最新的版本,直接選擇帶ml的rpm包即可,如果你要追求穩定且更長的支援週期,直接選擇lt版本即可。

[root@etcd2 ~]# ll -h kernel-lt-5.4.197-1.el7.elrepo.x86_64.rpm*
-rw-r--r-- 1 root root 51M 6月   5 19:47 kernel-lt-5.4.197-1.el7.elrepo.x86_64.rpm

安裝核心包。

[root@etcd2 ~]# rpm -ivh kernel-lt-5.4.197-1.el7.elrepo.x86_64.rpm
警告:kernel-lt-5.4.197-1.el7.elrepo.x86_64.rpm: 頭V4 DSA/SHA256 Signature, 金鑰 ID baadae52: NOKEY
準備中...                          ################################# [100%]
正在升級/安裝...
   1:kernel-lt-5.4.197-1.el7.elrepo   ################################# [100%]

核心升級完畢後,需要我們修改核心的啟動順序,預設啟動的順序應該為1,升級以後核心是往前面插入為0,設定GRUB_DEFAULT=0。一般新安裝的核心在第一個位置,所以設定default=0,意思是 GRUB 初始化頁面的第一個核心將作為預設核心。

預設的grub檔案,GRUB_DEFAULT=saved。

[root@etcd2 ~]# cat /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="gfxterm"
GRUB_CMDLINE_LINUX="rhgb quiet nomodeset"
GRUB_DISABLE_RECOVERY="true"

使 GRUB_DEFAULT=0。

[root@etcd2 ~]# vim /etc/default/grub

[root@etcd2 ~]# cat /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=0
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="gfxterm"
GRUB_CMDLINE_LINUX="rhgb quiet nomodeset"
GRUB_DISABLE_RECOVERY="true"

設定預設啟動核心,grub2-set-default 0和/etc/default/grub檔案裡的GRUB_DEFAULT=0意思一樣。

[root@etcd2 ~]# grub2-set-default 0

檢視所有的核心。

[root@etcd2 ~]# awk -F\' '$1=="menuentry " {print i++ " : " $2}' /boot/grub2/grub.cfg
0 : CentOS Linux 7 Rescue 12667e2174a8483e915fd89a3bc359fc (5.4.197-1.el7.elrepo.x86_64)
1 : CentOS Linux (5.4.197-1.el7.elrepo.x86_64) 7 (Core)
2 : CentOS Linux (3.10.0-693.el7.x86_64) 7 (Core)
3 : CentOS Linux (0-rescue-80c608ceab5342779ba1adc2ac29c213) 7 (Core)

重新生成grub配置檔案。

[root@etcd2 ~]# vim /boot/grub2/grub.cfg

[root@etcd2 ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-5.4.197-1.el7.elrepo.x86_64
Found initrd image: /boot/initramfs-5.4.197-1.el7.elrepo.x86_64.img
Found linux image: /boot/vmlinuz-3.10.0-693.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-693.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-12667e2174a8483e915fd89a3bc359fc
Found initrd image: /boot/initramfs-0-rescue-12667e2174a8483e915fd89a3bc359fc.img
Found linux image: /boot/vmlinuz-0-rescue-80c608ceab5342779ba1adc2ac29c213
Found initrd image: /boot/initramfs-0-rescue-80c608ceab5342779ba1adc2ac29c213.img
done

重啟並檢視核心版本。

[root@etcd2 ~]# reboot

可以看到核心升級成功。

[root@etcd2 ~]# uname -r
5.4.197-1.el7.elrepo.x86_64

[root@etcd2 ~]# uname -rs
Linux 5.4.197-1.el7.elrepo.x86_64

7.3 安裝gvisor

檢視CPU架構。

[root@etcd2 ~]# uname -m
x86_64

下載runsc,containerd-shim-runsc-v1,以及對應的校驗和:runsc.sha512,containerd-shim-runsc-v1.sha512。

[root@etcd2 ~]# wget https://storage.googleapis.com/gvisor/releases/release/latest/x86_64/runsc https://storage.googleapis.com/gvisor/releases/release/latest/x86_64/runsc.sha512 https://storage.googleapis.com/gvisor/releases/release/latest/x86_64/containerd-shim-runsc-v1 https://storage.googleapis.com/gvisor/releases/release/latest/x86_64/containerd-shim-runsc-v1.sha512

[root@etcd2 ~]# ll -h runsc* containerd-shim*
-rw-r--r-- 1 root root 25M 5月  17 00:22 containerd-shim-runsc-v1
-rw-r--r-- 1 root root 155 5月  17 00:22 containerd-shim-runsc-v1.sha512
-rw-r--r-- 1 root root 38M 5月  17 00:22 runsc
-rw-r--r-- 1 root root 136 5月  17 00:22 runsc.sha512

使用sha512sum校驗檔案是否完整。

[root@etcd2 ~]# sha512sum -c runsc.sha512 -c containerd-shim-runsc-v1.sha512
runsc: 確定
containerd-shim-runsc-v1: 確定

[root@etcd2 ~]# cat *sha512
f24834bbd4d14d0d0827e31276ff74a1e08b7ab366c4a30fe9c30d656c1ec5cbfc2544fb06698b4749791e0c6f80e6d16ec746963ff6ecebc246dc6e5b2f34ba  containerd-shim-runsc-v1
e5bc1c46d021246a69174aae71be93ff49661ff08eb6a957f7855f36076b44193765c966608d11a99f14542612438634329536d88fccb4b12bdd9bf2af20557f  runsc

授予可執行許可權。

[root@etcd2 ~]# chmod a+rx runsc containerd-shim-runsc-v1

把檔案移動到/usr/local/bin目錄下。

[root@etcd2 ~]# mv runsc containerd-shim-runsc-v1 /usr/local/bin

安裝gvisor。

[root@etcd2 ~]# /usr/local/bin/runsc install
2022/06/07 13:04:16 Added runtime "runsc" with arguments [] to "/etc/docker/daemon.json".

安裝gvisor之後,/etc/docker/daemon.json檔案會新增runtimes:runsc: "path": "/usr/local/bin/runsc"。

注意:/etc/docker/daemon.json檔案裡的"runtimes":"runsc",runsc可以更改為其他名字,比如:"runtimes":"gvisor"。

[root@etcd2 ~]# cat /etc/docker/daemon.json
{
    "registry-mirrors": [
        "https://frz7i079.mirror.aliyuncs.com"
    ],
    "runtimes": {
        "runsc": {
            "path": "/usr/local/bin/runsc"
        }
    }
}

重新載入配置檔案並重啟docker。

[root@etcd2 ~]# systemctl daemon-reload ;systemctl restart docker 

檢視runtime,可以發現Runtimes裡現在已經有runsc了,說明現在docker是支援gvisor這個runtime的。

[root@etcd2 ~]# docker info | grep -i runtime
 Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc runsc
 Default Runtime: runc

檢視runsc版本。

[root@etcd2 ~]# runsc --version
runsc version release-20220510.0
spec: 1.0.2-dev

7.4 配置docker預設的runtime為gVisor

檢視docker狀態。

[root@etcd2 ~]# systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: active (running) since 二 2022-06-07 17:02:18 CST; 12min ago
     Docs: https://docs.docker.com
 Main PID: 1109 (dockerd)
   Memory: 130.7M
   CGroup: /system.slice/docker.service
           └─1109 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

docker啟動引數如下:

[root@etcd2 ~]# cat /usr/lib/systemd/system/docker.service | grep ExecStart
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

檢視docker幫助,--default-runtime可以指定docker的Default Runtime。

[root@etcd2 ~]# dockerd --help | grep default-runtime
      --default-runtime string                  Default OCI runtime for containers (default "runc")

現在需要修改docker的啟動引數ExecStart,指定docker預設使用runsc作為runtime。

[root@etcd2 ~]# vim /usr/lib/systemd/system/docker.service

#--default-runtime runsc指定docker的Default Runtime為gvisor
[root@etcd2 ~]# cat /usr/lib/systemd/system/docker.service | grep ExecStart
ExecStart=/usr/bin/dockerd --default-runtime runsc -H fd:// --containerd=/run/containerd/containerd.sock

重新載入配置檔案並重啟docker。

[root@etcd2 ~]# systemctl daemon-reload ; systemctl restart docker

現在docker的Default Runtime就為gvisor了。

[root@etcd2 ~]# docker info | grep -i runtime
 Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc runsc
 Default Runtime: runsc

7.5 docker使用gVisor作為runtime建立容器

拉取nginx映象。

[root@etcd2 ~]# docker pull hub.c.163.com/library/nginx:latest
latest: Pulling from library/nginx
5de4b4d551f8: Pull complete 
d4b36a5e9443: Pull complete 
0af1f0713557: Pull complete 
Digest: sha256:f84932f738583e0169f94af9b2d5201be2dbacc1578de73b09a6dfaaa07801d6
Status: Downloaded newer image for hub.c.163.com/library/nginx:latest
hub.c.163.com/library/nginx:latest

[root@etcd2 ~]# docker images 
REPOSITORY                    TAG       IMAGE ID       CREATED        SIZE
hub.c.163.com/library/nginx   latest    46102226f2fd   5 years ago    109MB

使用nginx映象建立一個容器,預設是使用gVisor(runsc)建立的容器。

如果已經安裝了gVisor,但是docker的Default Runtime為runc,則可以使用--runtime=runsc指定gvisor作為runtime建立容器,即:docker run -dit --runtime=runsc --name=nginxweb --restart=always hub.c.163.com/library/nginx:latest

[root@etcd2 ~]# docker run -dit  --name=nginxweb --restart=always hub.c.163.com/library/nginx:latest
9a7b9091d0d07052ae972b480687e7a345ae22e0e4968e91133b1ad6ac1d5b3a

檢視容器。

[root@etcd2 ~]# docker ps
CONTAINER ID   IMAGE                                COMMAND                  CREATED         STATUS          PORTS     NAMES
9a7b9091d0d0   hub.c.163.com/library/nginx:latest   "nginx -g 'daemon of…"   3 minutes ago   Up 3 minutes    80/tcp    nginxweb
bc99f286802f   quay.io/calico/node:v2.6.12          "start_runit"            3 months ago    Up 19 seconds             calico-node

gvisor以沙箱的方式執行容器,在宿主機裡就看不到容器裡執行的程序了。

[root@etcd2 ~]# ps -ef | grep nginx
root       9031   2916  0 17:54 pts/1    00:00:00 grep --color=auto nginx

刪除容器。

[root@etcd2 ~]# docker rm -f nginxweb
nginxweb

[root@etcd2 ~]# docker ps
CONTAINER ID   IMAGE                         COMMAND         CREATED        STATUS         PORTS     NAMES
bc99f286802f   quay.io/calico/node:v2.6.12   "start_runit"   3 months ago   Up 7 seconds             calico-node

八.配置containerd使用gvisor作為runtime

8.1 安裝containerd

如果你熟悉docker,但是不瞭解containerd,請檢視部落格《在centos下使用containerd管理容器:5分鐘從docker轉型到containerd》,裡面有詳細講解。

我們在客戶端機器ubuntuk8sclient(ubuntu系統)上安裝containerd。

更新軟體源。

root@ubuntuk8sclient:~#  apt-get update

安裝containerd。

root@ubuntuk8sclient:~# apt-get -y install containerd.io cri-tools 

設定containerd開機自啟動並現在啟動containerd。

root@ubuntuk8sclient:~# systemctl enable containerd --now

檢視containerd狀態。

root@ubuntuk8sclient:~# systemctl is-active containerd
active

root@ubuntuk8sclient:~# systemctl status containerd
● containerd.service - containerd container runtime
   Loaded: loaded (/lib/systemd/system/containerd.service; enabled; vendor preset: enabled)
   Active: active (running) since Sat 2022-06-04 15:54:08 CST; 58min ago
     Docs: https://containerd.io
 Main PID: 722 (containerd)
    Tasks: 8
   CGroup: /system.slice/containerd.service
           └─722 /usr/bin/containerd

containerd的配置檔案為/etc/containerd/config.toml 。

root@ubuntuk8sclient:~# ll -h /etc/containerd/config.toml
-rw-r--r-- 1 root root 886 May  4 17:04 /etc/containerd/config.toml

containerd的預設配置檔案/etc/containerd/config.toml 內容如下:

root@ubuntuk8sclient:~# cat /etc/containerd/config.toml
#   Copyright 2018-2022 Docker Inc.

#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at

#       http://www.apache.org/licenses/LICENSE-2.0

#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

disabled_plugins = ["cri"]

#root = "/var/lib/containerd"
#state = "/run/containerd"
#subreaper = true
#oom_score = 0

#[grpc]
#  address = "/run/containerd/containerd.sock"
#  uid = 0
#  gid = 0

#[debug]
#  address = "/run/containerd/debug.sock"
#  uid = 0
#  gid = 0
#  level = "info"

可以使用containerd config default > /etc/containerd/config.toml生成預設的配置檔案,containerd config default生成的配置檔案內容還是挺多的。

root@ubuntuk8sclient:~# containerd config default > /etc/containerd/config.toml

root@ubuntuk8sclient:~# vim /etc/containerd/config.toml 

containerd config dump顯示當前的配置。

root@ubuntuk8sclient:~# containerd config dump
disabled_plugins = []
imports = ["/etc/containerd/config.toml"]
oom_score = 0
plugin_dir = ""
required_plugins = []
root = "/var/lib/containerd"
......
......
  address = ""
  gid = 0
  uid = 0

檢視containerd版本。

root@ubuntuk8sclient:~# containerd --version
containerd containerd.io 1.6.4 212e8b6fa2f44b9c21b2798135fc6fb7c53efc16

root@ubuntuk8sclient:~# containerd -v
containerd containerd.io 1.6.4 212e8b6fa2f44b9c21b2798135fc6fb7c53efc16

修改配置檔案,新增阿里雲映象加速器。

root@ubuntuk8sclient:~# vim /etc/containerd/config.toml 

root@ubuntuk8sclient:~# grep endpoint /etc/containerd/config.toml
    endpoint = "https://frz7i079.mirror.aliyuncs.com"

SystemdCgroup = false修改為SystemdCgroup = true。

root@ubuntuk8sclient:~# vim /etc/containerd/config.toml 

root@ubuntuk8sclient:~# grep SystemdCgroup -B 11 /etc/containerd/config.toml 
          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
            BinaryName = ""
            CriuImagePath = ""
            CriuPath = ""
            CriuWorkPath = ""
            IoGid = 0
            IoUid = 0
            NoNewKeyring = false
            NoPivotRoot = false
            Root = ""
            ShimCgroup = ""
            SystemdCgroup = true

有個sandbox的映象,k8s.gcr.io/pause:3.6訪問不了。

root@ubuntuk8sclient:~# grep sandbox_image /etc/containerd/config.toml
    sandbox_image = "k8s.gcr.io/pause:3.6"

修改sandbox映象為可以訪問的阿里雲映象。

root@ubuntuk8sclient:~# vim /etc/containerd/config.toml 

root@ubuntuk8sclient:~# grep sandbox_image /etc/containerd/config.toml
    sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.6"

重新載入配置檔案並重啟containerd服務。

root@ubuntuk8sclient:~# systemctl daemon-reload ; systemctl restart containerd

containerd 客戶端工具有 ctr 和 crictl ,如果使用 crictl 命令的話,需要執行 crictl config runtime-endpoint unix:///var/run/containerd/containerd.sock ,不然會有告警。

root@ubuntuk8sclient:~# crictl config runtime-endpoint unix:///var/run/containerd/containerd.sock

檢視containerd資訊。

root@ubuntuk8sclient:~# crictl info 
{
  "status": {
    "conditions": [
      {
        "type": "RuntimeReady",
        "status": true,
        "reason": "",
        "message": ""
      },
......
    "enableUnprivilegedPorts": false,
    "enableUnprivilegedICMP": false,
    "containerdRootDir": "/var/lib/containerd",
    "containerdEndpoint": "/run/containerd/containerd.sock",
    "rootDir": "/var/lib/containerd/io.containerd.grpc.v1.cri",
    "stateDir": "/run/containerd/io.containerd.grpc.v1.cri"
  },
  "golang": "go1.17.9",
  "lastCNILoadStatus": "cni config load failed: no network config found in /etc/cni/net.d: cni plugin not initialized: failed to load cni config",
  "lastCNILoadStatus.default": "cni config load failed: no network config found in /etc/cni/net.d: cni plugin not initialized: failed to load cni config"
}

containerd裡有名稱空間的概念,docker裡沒有名稱空間,對於containerd,在default名稱空間裡拉取的映象和建立的容器,在其他名稱空間是看不到的,如果這個containerd節點加入到k8s環境中,則k8s預設使用k8s.io這個名稱空間。

檢視名稱空間。

root@ubuntuk8sclient:~# ctr ns list
NAME         LABELS 
moby                
plugins.moby        

檢視映象。

root@ubuntuk8sclient:~# ctr i list
REF TYPE DIGEST SIZE PLATFORMS LABELS 

root@ubuntuk8sclient:~# crictl images
IMAGE               TAG                 IMAGE ID            SIZE

使用crictl拉取映象。

root@ubuntuk8sclient:~# crictl pull nginx
Image is up to date for sha256:0e901e68141fd02f237cf63eb842529f8a9500636a9419e3cf4fb986b8fe3d5d

root@ubuntuk8sclient:~# crictl images
IMAGE                     TAG                 IMAGE ID            SIZE
docker.io/library/nginx   latest              0e901e68141fd       56.7MB

ctr和crictl更多命令細節,請檢視部落格《在centos下使用containerd管理容器:5分鐘從docker轉型到containerd》。

containerd 客戶端工具 ctr 和 crictl 不好用,推薦使用nerdctl,nerdctl是containerd的cli客戶端工具,與docker cli大部分相容,用法類似docker命令。

使用nerdctl命令需要兩個安裝包nerdctl-0.20.0-linux-amd64.tar.gz和cni-plugins-linux-amd64-v1.1.1.tgz。

nerdctl-0.20.0-linux-amd64.tar.gz下載地址:https://github.com/containerd/nerdctl/releases

網路外掛cni-plugins-linux-amd64-v1.1.1.tgz下載地址:https://github.com/containernetworking/plugins/releases

root@ubuntuk8sclient:~# ll -h cni-plugins-linux-amd64-v1.1.1.tgz nerdctl-0.20.0-linux-amd64.tar.gz 
-rw-r--r-- 1 root root  35M Jun  5 12:19 cni-plugins-linux-amd64-v1.1.1.tgz
-rw-r--r-- 1 root root 9.8M Jun  5 12:15 nerdctl-0.20.0-linux-amd64.tar.gz

分別進行解壓。

root@ubuntuk8sclient:~# tar xf nerdctl-0.20.0-linux-amd64.tar.gz -C /usr/local/bin/

root@ubuntuk8sclient:~# ls /usr/local/bin/
containerd-rootless-setuptool.sh  containerd-rootless.sh  nerdctl

root@ubuntuk8sclient:~# mkdir -p /opt/cni/bin

root@ubuntuk8sclient:~# tar xf cni-plugins-linux-amd64-v1.1.1.tgz -C /opt/cni/bin/

root@ubuntuk8sclient:~# ls /opt/cni/bin/
bandwidth  bridge  dhcp  firewall  host-device  host-local  ipvlan  loopback  macvlan  portmap  ptp  sbr  static  tuning  vlan  vrf

配置nerdctl命令tab自動補全,新增source <(nerdctl completion bash)。

root@ubuntuk8sclient:~# vim /etc/profile

root@ubuntuk8sclient:~# cat /etc/profile | head -3
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).
source <(nerdctl completion bash)

root@ubuntuk8sclient:~# nerdctl completion bash

使配置檔案/etc/profile生效。

root@ubuntuk8sclient:~# source /etc/profile

檢視映象。

root@ubuntuk8sclient:~# nerdctl images 
REPOSITORY    TAG    IMAGE ID    CREATED    PLATFORM    SIZE    BLOB SIZE

檢視名稱空間。

root@ubuntuk8sclient:~# nerdctl ns list
NAME            CONTAINERS    IMAGES    VOLUMES    LABELS
default         0             0         0              
k8s.io          0             4         0              
moby            0             0         0              
plugins.moby    0             0         0    

nerdctl的命令和docker命令很相似,只要把docker命令裡的docker換成nerdctl,基本都能執行成功。

拉取映象。

root@ubuntuk8sclient:~# nerdctl pull hub.c.163.com/library/nginx:latest
                
root@ubuntuk8sclient:~# nerdctl images
REPOSITORY                     TAG       IMAGE ID        CREATED           PLATFORM       SIZE         BLOB SIZE
hub.c.163.com/library/nginx    latest    8eeb06742b41    22 seconds ago    linux/amd64    115.5 MiB    41.2 MiB

檢視containerd資訊。

root@ubuntuk8sclient:~# nerdctl info 

8.2 安裝gVisor

Note: gVisor supports x86_64 and ARM64, and requires Linux 4.14.77+,gvisor要求核心版本大於4.14.77,此機器版本為4.15.0-112-generic,因此不用升級核心。如果需要升級核心,請參考部落格《centos7 離線升級/線上升級作業系統核心》。

root@ubuntuk8sclient:~# uname -r
4.15.0-112-generic

下載gvisor對應的可執行檔案。

root@ubuntuk8sclient:~# wget https://storage.googleapis.com/gvisor/releases/release/latest/x86_64/runsc https://storage.googleapis.com/gvisor/releases/release/latest/x86_64/runsc.sha512 https://storage.googleapis.com/gvisor/releases/release/latest/x86_64/containerd-shim-runsc-v1 https://storage.googleapis.com/gvisor/releases/release/latest/x86_64/containerd-shim-runsc-v1.sha512   

root@ubuntuk8sclient:~# ll -h runsc* containerd-shim*
-rwxr-xr-x 1 root root 25M Jun  7 18:24 containerd-shim-runsc-v1*
-rw-r--r-- 1 root root 155 Jun  7 18:24 containerd-shim-runsc-v1.sha512
-rwxr-xr-x 1 root root 38M Jun  7 18:24 runsc*
-rw-r--r-- 1 root root 136 Jun  7 18:24 runsc.sha512

進行檔案校驗。

root@ubuntuk8sclient:~# sha512sum -c runsc.sha512 -c containerd-shim-runsc-v1.sha512
runsc: OK
containerd-shim-runsc-v1: OK

root@ubuntuk8sclient:~# cat *sha512
f24834bbd4d14d0d0827e31276ff74a1e08b7ab366c4a30fe9c30d656c1ec5cbfc2544fb06698b4749791e0c6f80e6d16ec746963ff6ecebc246dc6e5b2f34ba  containerd-shim-runsc-v1
e5bc1c46d021246a69174aae71be93ff49661ff08eb6a957f7855f36076b44193765c966608d11a99f14542612438634329536d88fccb4b12bdd9bf2af20557f  runsc

授予可執行許可權並移動到/usr/local/bin目錄。

root@ubuntuk8sclient:~# chmod a+rx runsc containerd-shim-runsc-v1

root@ubuntuk8sclient:~# mv runsc containerd-shim-runsc-v1 /usr/local/bin

可以發現現在containerd只支援runc一種runtime。

root@ubuntuk8sclient:~# crictl info | grep -A10 runtimes
      "runtimes": {
        "runc": {
          "runtimeType": "io.containerd.runc.v2",
          "runtimePath": "",
          "runtimeEngine": "",
          "PodAnnotations": [],
          "ContainerAnnotations": [],
          "runtimeRoot": "",
          "options": {
            "BinaryName": "",
            "CriuImagePath": "",

8.3 配置containerd支援gVisor

需要先修改配置檔案,使containerd支援多種runtime。

原本的內容是plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc,新新增plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc使containerd支援gvisor,runtime_type = "containerd-shim-runsc-v1"就是我們下載的containerd-shim-runsc-v1檔案。

runtime_type = "containerd-shim-runsc-v1"這種寫法後面驗證了一下,在containerd裡建立容器沒問題,但是到k8s裡就有問題,正確的寫法應該是:runtime_type = "io.containerd.runsc.v1"

root@ubuntuk8sclient:~# vim /etc/containerd/config.toml 

root@ubuntuk8sclient:~# cat /etc/containerd/config.toml | grep -A27 "containerd.runtimes.runc"
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
          base_runtime_spec = ""
          cni_conf_dir = ""
          cni_max_conf_num = 0
          container_annotations = []
          pod_annotations = []
          privileged_without_host_devices = false
          runtime_engine = ""
          runtime_path = ""
          runtime_root = ""
          runtime_type = "io.containerd.runc.v2"

          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
            BinaryName = ""
            CriuImagePath = ""
            CriuPath = ""
            CriuWorkPath = ""
            IoGid = 0
            IoUid = 0
            NoNewKeyring = false
            NoPivotRoot = false
            Root = ""
            ShimCgroup = ""
            SystemdCgroup = true
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
          base_runtime_spec = ""
          cni_conf_dir = ""
          cni_max_conf_num = 0
          container_annotations = []
          pod_annotations = []
          privileged_without_host_devices = false
          runtime_engine = ""
          runtime_path = ""
          runtime_root = ""
          runtime_type = "io.containerd.runsc.v1"

重新載入配置檔案並重啟containerd。

root@ubuntuk8sclient:~# systemctl daemon-reload ;systemctl restart containerd

現在就可以看到containerd支援兩種runtime了:runc和runsc。

root@ubuntuk8sclient:~# crictl info | grep -A36 runtimes
      "runtimes": {
        "runc": {
          "runtimeType": "io.containerd.runc.v2",
          "runtimePath": "",
          "runtimeEngine": "",
          "PodAnnotations": [],
          "ContainerAnnotations": [],
          "runtimeRoot": "",
          "options": {
            "BinaryName": "",
            "CriuImagePath": "",
            "CriuPath": "",
            "CriuWorkPath": "",
            "IoGid": 0,
            "IoUid": 0,
            "NoNewKeyring": false,
            "NoPivotRoot": false,
            "Root": "",
            "ShimCgroup": "",
            "SystemdCgroup": true
          },
          "privileged_without_host_devices": false,
          "baseRuntimeSpec": "",
          "cniConfDir": "",
          "cniMaxConfNum": 0
        },
        "runsc": {
          "runtimeType": "containerd-shim-runsc-v1",
          "runtimePath": "",
          "runtimeEngine": "",
          "PodAnnotations": [],
          "ContainerAnnotations": [],
          "runtimeRoot": "",
          "options": null,
          "privileged_without_host_devices": false,
          "baseRuntimeSpec": "",
          "cniConfDir": "",

檢視容器。

root@ubuntuk8sclient:~# nerdctl ps 
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES

檢視映象。

root@ubuntuk8sclient:~# nerdctl images
REPOSITORY                      TAG                                                                 IMAGE ID        CREATED         PLATFORM       SIZE         BLOB SIZE
hub.c.163.com/library/nginx     latest                                                              8eeb06742b41    2 days ago      linux/amd64    115.5 MiB    41.2 MiB
sha256                          e5bc191dff1f971254305a0dbc58c4145c783e34090bbd4360a36d7447fe3ef2    8eeb06742b41    2 days ago      linux/amd64    115.5 MiB    41.2 MiB

使用nginx映象建立容器,預設使用runc作為runtime。

root@ubuntuk8sclient:~# nerdctl run -d --name=nginxweb --restart=always hub.c.163.com/library/nginx:latest
bdef5e3fa6e6fb7c08f4df19810a42c81b7bc1bf7a16b3beaca53508ac4cedab

檢視容器。

root@ubuntuk8sclient:~# nerdctl ps
CONTAINER ID    IMAGE                                 COMMAND                   CREATED          STATUS    PORTS    NAMES
bdef5e3fa6e6    hub.c.163.com/library/nginx:latest    "nginx -g daemon off;"    4 seconds ago    Up                 nginxweb    

containerd預設使用runc作為runtime建立的容器,會共享宿主機的程序空間和核心空間,容器的程序是暴露給宿主機的,如果容器裡存在漏洞,不法分子會使用容器漏洞影響到宿主機的安全。

root@ubuntuk8sclient:~# ps -ef | grep nginx
root       6540   6505  0 21:36 ?        00:00:00 nginx: master process nginx -g daemon off;
systemd+   6625   6540  0 21:36 ?        00:00:00 nginx: worker process
root       6634   6251  0 21:36 pts/1    00:00:00 grep --color=auto nginx

刪除容器。

root@ubuntuk8sclient:~# nerdctl rm -f nginxweb
nginxweb

刪除容器之後,宿主機就看不到nginx程序了。

root@ubuntuk8sclient:~# nerdctl ps
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES

root@ubuntuk8sclient:~# ps -ef | grep nginx
root       6726   6251  0 21:38 pts/1    00:00:00 grep --color=auto nginx

8.4 containerd使用gvisor作為runtime建立容器

建立容器,--runtime=runsc指定containerd使用gvisor作為runtime建立容器。

root@ubuntuk8sclient:~# nerdctl run -d --runtime=runsc --name=nginxweb --restart=always hub.c.163.com/library/nginx:latest
8ea86e8936374efbb626d11f79a9cb79fb32d9a44fafd71c02556a5ae842cac7

containerd使用gvisor作為runtime,以沙箱的方式執行容器,在宿主機裡就看不到容器裡執行的程序了。

root@ubuntuk8sclient:~# nerdctl ps
CONTAINER ID    IMAGE                                 COMMAND                   CREATED           STATUS    PORTS    NAMES
8ea86e893637    hub.c.163.com/library/nginx:latest    "nginx -g daemon off;"    51 seconds ago    Up                 nginxweb    

root@ubuntuk8sclient:~# ps -ef | grep nginx
root       7153   6251  0 21:41 pts/1    00:00:00 grep --color=auto nginx

刪除不了正在執行的容器。

root@ubuntuk8sclient:~# nerdctl rm -f nginxweb
WARN[0000] failed to delete task 8ea86e8936374efbb626d11f79a9cb79fb32d9a44fafd71c02556a5ae842cac7  error="unknown error after kill: runsc did not terminate successfully: exit status 128: sandbox is not running\n: unknown"
WARN[0000] failed to remove container "8ea86e8936374efbb626d11f79a9cb79fb32d9a44fafd71c02556a5ae842cac7"  error="cannot delete running task 8ea86e8936374efbb626d11f79a9cb79fb32d9a44fafd71c02556a5ae842cac7: failed precondition"
WARN[0000] failed to remove container "8ea86e8936374efbb626d11f79a9cb79fb32d9a44fafd71c02556a5ae842cac7"  error="cannot delete running task 8ea86e8936374efbb626d11f79a9cb79fb32d9a44fafd71c02556a5ae842cac7: failed precondition"
WARN[0000] failed to release name store for container "8ea86e8936374efbb626d11f79a9cb79fb32d9a44fafd71c02556a5ae842cac7"  error="cannot delete running task 8ea86e8936374efbb626d11f79a9cb79fb32d9a44fafd71c02556a5ae842cac7: failed precondition"
FATA[0000] cannot delete running task 8ea86e8936374efbb626d11f79a9cb79fb32d9a44fafd71c02556a5ae842cac7: failed precondition 

先停止容器,再刪除容器。

root@ubuntuk8sclient:~# nerdctl stop nginxweb
nginxweb

root@ubuntuk8sclient:~# nerdctl rm nginxweb

root@ubuntuk8sclient:~# nerdctl ps
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES

九.在k8s環境裡,配置containerd作為高階別runtime,containerd使用gvisor作為低階別runtime

9.1 把ubuntuk8sclient節點加入k8s叢集

注意docker作為k8s的高階別runtime的時候,不支援gvisor作為docker的低階別runtime,只有單機版的時候,gvisor才能作為docker的低階別runtime。

描述一下當前的系統環境:現在有一個k8s叢集,1個master,2個worker,三臺機器都是使用docker作為高階別runtime,現在新增一個新的worker節點,新的worker節點使用containerd作為高階別runtime,gvisor作為containerd的低階別runtime

現在把ubuntuk8sclient機器加入k8s叢集,ubuntuk8sclient的CONTAINER-RUNTIME為containerd。

檢視叢集節點。

root@k8scludes1:~# kubectl get nodes -o wide
NAME         STATUS   ROLES                  AGE   VERSION   INTERNAL-IP       EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION       CONTAINER-RUNTIME
k8scludes1   Ready    control-plane,master   55d   v1.22.2   192.168.110.128   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   docker://20.10.14
k8scludes2   Ready    <none>                 55d   v1.22.2   192.168.110.129   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   docker://20.10.14
k8scludes3   Ready    <none>                 55d   v1.22.2   192.168.110.130   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   docker://20.10.14

先在所有的機器配置IP主機名對映(以ubuntuk8sclient為例)。

root@ubuntuk8sclient:~# vim /etc/hosts

root@ubuntuk8sclient:~# cat /etc/hosts
127.0.0.1	localhost
127.0.1.1	tom
192.168.110.139 ubuntuk8sclient
192.168.110.128 k8scludes1
192.168.110.129 k8scludes2
192.168.110.130 k8scludes3

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

配置軟體源,軟體源如下,最後三行是k8s源。

root@ubuntuk8sclient:~# cat /etc/apt/sources.list
deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse

deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu bionic stable
# deb-src [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu bionic stable

apt-key.gpg是k8s的deb源公鑰,載入k8s的deb源公鑰 apt-key add apt-key.gpg。

下載並載入k8s的deb源公鑰:curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - ; apt-get update。但是谷歌的網址訪問不了,我們直接去網上下載apt-key.gpg檔案,載入k8s的deb源公鑰。

root@ubuntuk8sclient:~# cat apt-key.gpg | apt-key add -
OK

更新軟體源。

root@ubuntuk8sclient:~# apt-get update

Linux swapoff命令用於關閉系統交換分割槽(swap area)。如果不關閉swap,就會在kubeadm初始化Kubernetes的時候報錯:“[ERROR Swap]: running with swap on is not supported. Please disable swap”。

root@ubuntuk8sclient:~# swapoff -a ;sed -i '/swap/d' /etc/fstab

root@ubuntuk8sclient:~# cat /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
/dev/mapper/tom--vg-root /               ext4    errors=remount-ro 0       1

檢視containerd版本。

root@ubuntuk8sclient:~# containerd -v
containerd containerd.io 1.6.4 212e8b6fa2f44b9c21b2798135fc6fb7c53efc16

registry.aliyuncs.com/google_containers/pause:3.6這個映象需要提前拉取好。

root@ubuntuk8sclient:~# cat /etc/containerd/config.toml | grep pause
    pause_threshold = 0.02
    sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.6"

拉取映象。

root@ubuntuk8sclient:~# nerdctl pull registry.aliyuncs.com/google_containers/pause:3.6

檢視映象。

root@ubuntuk8sclient:~# nerdctl images | grep pause
registry.aliyuncs.com/google_containers/pause    3.6                                                                 3d380ca88645    3 days ago    linux/amd64    672.0 KiB    294.7 KiB

root@ubuntuk8sclient:~# crictl images | grep pause
registry.aliyuncs.com/google_containers/pause   3.6                 6270bb605e12e       302kB

設定containerd當前名稱空間為k8s.io。

root@ubuntuk8sclient:~# cat /etc/nerdctl/nerdctl.toml | head -3
namespace = "k8s.io"

載入overlay和br_netfilter模組。

root@ubuntuk8sclient:~# cat > /etc/modules-load.d/containerd.conf <<EOF 
> overlay 
> br_netfilter 
> EOF

root@ubuntuk8sclient:~# cat /etc/modules-load.d/containerd.conf
overlay 
br_netfilter 

root@ubuntuk8sclient:~# modprobe overlay

root@ubuntuk8sclient:~# modprobe br_netfilter

設定iptables不對bridge的資料進行處理,啟用IP路由轉發功能。

root@ubuntuk8sclient:~# cat <<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 
> EOF

使配置生效。

root@ubuntuk8sclient:~# sysctl -p /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1

為了k8s節點間的通訊,需要安裝cni網路外掛,提前下載好calico映象,calico映象版本要和k8s的那三個節點的calico版本一致。

root@ubuntuk8sclient:~# nerdctl pull docker.io/calico/cni:v3.22.2

root@ubuntuk8sclient:~# nerdctl pull docker.io/calico/pod2daemon-flexvol:v3.22.2
                               
root@ubuntuk8sclient:~# nerdctl pull docker.io/calico/node:v3.22.2
                        
root@ubuntuk8sclient:~# nerdctl pull docker.io/calico/kube-controllers:v3.22.2
                               
root@ubuntuk8sclient:~# nerdctl images | grep calico
calico/cni                                       v3.22.2                                                             757d06fe361c    4 minutes ago     linux/amd64    227.1 MiB    76.8 MiB
calico/kube-controllers                          v3.22.2                                                             751f1a8ba0af    20 seconds ago    linux/amd64    128.1 MiB    52.4 MiB
calico/node                                      v3.22.2                                                             41aac6d0a440    2 minutes ago     linux/amd64    194.2 MiB    66.5 MiB
calico/pod2daemon-flexvol                        v3.22.2                                                             413c5ebad6a5    3 minutes ago     linux/amd64    19.0 MiB     8.0 MiB

安裝kubelet,kubeadm,kubectl。

  • Kubelet 是 kubernetes 工作節點上的一個代理元件,執行在每個節點上;
  • Kubeadm 是一個快捷搭建kubernetes(k8s)的安裝工具,它提供了 kubeadm init 以及 kubeadm join 這兩個命令來快速建立 kubernetes 叢集;kubeadm 透過執行必要的操作來啟動和執行一個最小可用的叢集;
  • kubectl是Kubernetes叢集的命令列工具,透過kubectl能夠對叢集本身進行管理,並能夠在叢集上進行容器化應用的安裝部署。
root@ubuntuk8sclient:~# apt-get -y install kubelet=1.22.2-00 kubeadm=1.22.2-00 kubectl=1.22.2-00

設定kubelet開機自啟動並現在啟動。

root@ubuntuk8sclient:~# systemctl enable kubelet --now

在k8s的master節點,檢視k8s worker節點加入k8s叢集的token。

root@k8scludes1:~# kubeadm token create --print-join-command
kubeadm join 192.168.110.128:6443 --token rwau00.plx8xdksa8zdnfrn --discovery-token-ca-cert-hash sha256:3f401b6187ed44ff8f4b50aa6453cf3eacc3b86d6a72e3bf2caba02556cb918e 

把ubuntuk8sclient節點加入k8s叢集。

root@ubuntuk8sclient:~# kubeadm join 192.168.110.128:6443 --token rwau00.plx8xdksa8zdnfrn --discovery-token-ca-cert-hash sha256:3f401b6187ed44ff8f4b50aa6453cf3eacc3b86d6a72e3bf2caba02556cb918e
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

去k8s master節點檢視是否加入k8s叢集,可以看到ubuntuk8sclient成功加入k8s叢集,並且CONTAINER-RUNTIME為containerd://1.6.4。

root@k8scludes1:~# kubectl get node -o wide
NAME              STATUS   ROLES                  AGE   VERSION   INTERNAL-IP       EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION       CONTAINER-RUNTIME
k8scludes1        Ready    control-plane,master   55d   v1.22.2   192.168.110.128   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   docker://20.10.14
k8scludes2        Ready    <none>                 55d   v1.22.2   192.168.110.129   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   docker://20.10.14
k8scludes3        Ready    <none>                 55d   v1.22.2   192.168.110.130   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   docker://20.10.14
ubuntuk8sclient   Ready    <none>                 87s   v1.22.2   192.168.110.139   <none>        Ubuntu 18.04.5 LTS   4.15.0-112-generic   containerd://1.6.4

現在需要配置containerd支援多個runtime,使其支援gvisor。

原本的內容是plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc,新新增plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc使containerd支援gvisor,runtime_type = "containerd-shim-runsc-v1"就是我們下載的containerd-shim-runsc-v1檔案。

root@ubuntuk8sclient:~# cat /etc/containerd/config.toml | grep -A27 "containerd.runtimes.runc"
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
          base_runtime_spec = ""
          cni_conf_dir = ""
          cni_max_conf_num = 0
          container_annotations = []
          pod_annotations = []
          privileged_without_host_devices = false
          runtime_engine = ""
          runtime_path = ""
          runtime_root = ""
          runtime_type = "io.containerd.runc.v2"

          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
            BinaryName = ""
            CriuImagePath = ""
            CriuPath = ""
            CriuWorkPath = ""
            IoGid = 0
            IoUid = 0
            NoNewKeyring = false
            NoPivotRoot = false
            Root = ""
            ShimCgroup = ""
            SystemdCgroup = true
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
          base_runtime_spec = ""
          cni_conf_dir = ""
          cni_max_conf_num = 0
          container_annotations = []
          pod_annotations = []
          privileged_without_host_devices = false
          runtime_engine = ""
          runtime_path = ""
          runtime_root = ""
          runtime_type = "containerd-shim-runsc-v1"

重新載入配置檔案並重啟containerd。

root@ubuntuk8sclient:~# systemctl daemon-reload ;systemctl restart containerd  

現在就可以看到containerd支援兩種runtime了:runc和runsc。

root@ubuntuk8sclient:~# crictl info | grep -A36 runtimes
      "runtimes": {
        "runc": {
          "runtimeType": "io.containerd.runc.v2",
          "runtimePath": "",
          "runtimeEngine": "",
          "PodAnnotations": [],
          "ContainerAnnotations": [],
          "runtimeRoot": "",
          "options": {
            "BinaryName": "",
            "CriuImagePath": "",
            "CriuPath": "",
            "CriuWorkPath": "",
            "IoGid": 0,
            "IoUid": 0,
            "NoNewKeyring": false,
            "NoPivotRoot": false,
            "Root": "",
            "ShimCgroup": "",
            "SystemdCgroup": true
          },
          "privileged_without_host_devices": false,
          "baseRuntimeSpec": "",
          "cniConfDir": "",
          "cniMaxConfNum": 0
        },
        "runsc": {
          "runtimeType": "containerd-shim-runsc-v1",
          "runtimePath": "",
          "runtimeEngine": "",
          "PodAnnotations": [],
          "ContainerAnnotations": [],
          "runtimeRoot": "",
          "options": null,
          "privileged_without_host_devices": false,
          "baseRuntimeSpec": "",
          "cniConfDir": "",

9.2 配置kubelet使其支援gVisor

配置kubelet,使其可以支援gvisor作為containerd的低階別runtime,修改kubelet引數,讓其支援runsc作為runtime。

root@ubuntuk8sclient:~# 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" 
> EOF


root@ubuntuk8sclient:~# cat /etc/systemd/system/kubelet.service.d/0-cri-containerd.conf
[Service] 
Environment="KUBELET_EXTRA_ARGS=--container-runtime=remote --runtime-request-timeout=15m  --container-runtime-endpoint=unix:///run/containerd/containerd.sock" 

重新載入配置檔案並重啟kubelet。

root@ubuntuk8sclient:~# systemctl daemon-reload ; systemctl restart kubelet

root@ubuntuk8sclient:~# systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled)
  Drop-In: /etc/systemd/system/kubelet.service.d
           └─0-cri-containerd.conf, 10-kubeadm.conf
   Active: active (running) since Sat 2022-06-11 18:00:31 CST; 14s ago
     Docs: https://kubernetes.io/docs/home/
 Main PID: 31685 (kubelet)
    Tasks: 13 (limit: 1404)
   CGroup: /system.slice/kubelet.service
           └─31685 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --container-runtime=remote --con

一切就緒,現在就建立pod。

給ubuntuk8sclient節點定義一個標籤:con=gvisor。

root@k8scludes1:~# kubectl label nodes ubuntuk8sclient con=gvisor
node/ubuntuk8sclient labeled

root@k8scludes1:~# kubectl get node -l con=gvisor
NAME              STATUS   ROLES    AGE   VERSION
ubuntuk8sclient   Ready    <none>   29m   v1.22.2

建立目錄存放檔案。

root@k8scludes1:~# mkdir containerd-gvisor

root@k8scludes1:~# cd containerd-gvisor/

編輯pod配置檔案,nodeSelector:con: gvisor 指定pod執行在ubuntuk8sclient節點,使用nginx映象建立pod。

root@k8scludes1:~/containerd-gvisor# vim pod.yaml 

root@k8scludes1:~/containerd-gvisor# cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: podtest
  name: podtest
spec:
  #當需要關閉容器時,立即殺死容器而不等待預設的30秒優雅停機時長。
  terminationGracePeriodSeconds: 0
  #nodeSelector:con: gvisor 指定pod執行在ubuntuk8sclient節點
  nodeSelector:
    con: gvisor
  containers:
  - image: hub.c.163.com/library/nginx:latest
    #imagePullPolicy: IfNotPresent:表示如果本地已經存在該映象,則不重新下載;否則從遠端 Docker Hub 下載該映象
    imagePullPolicy: IfNotPresent
    name: podtest
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

建立pod。

root@k8scludes1:~/containerd-gvisor# kubectl apply -f pod.yaml 
pod/podtest created

root@k8scludes1:~/containerd-gvisor# kubectl get pod -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP             NODE              NOMINATED NODE   READINESS GATES
podtest   1/1     Running   0          16s   10.244.228.1   ubuntuk8sclient   <none>           <none>

建立pod之後,去ubuntuk8sclient檢視,看看宿主機是否能看到容器裡的nginx程序,宿主機裡看到了pod裡的nginx程序,這說明pod是預設使用runc作為低階別runtime建立pod的。

root@ubuntuk8sclient:~# ps -ef | grep nginx
root      38308  38227  0 18:15 ?        00:00:00 nginx: master process nginx -g daemon off;
systemd+  38335  38308  0 18:15 ?        00:00:00 nginx: worker process
root      39009  27377  0 18:17 pts/1    00:00:00 grep --color=auto nginx

刪除pod。

root@k8scludes1:~/containerd-gvisor# kubectl delete pod podtest 
pod "podtest" deleted

刪除pod之後,宿主機也就沒有nginx程序了。

root@ubuntuk8sclient:~# ps -ef | grep nginx
root      40044  27377  0 18:20 pts/1    00:00:00 grep --color=auto nginx

9.3 建立容器執行時類(Runtime Class)

在k8s裡使用gvisor建立pod,需要使用到容器執行時類(Runtime Class)。

RuntimeClass 是一個用於選擇容器執行時配置的特性,容器執行時配置用於執行 Pod 中的容器。你可以在不同的 Pod 設定不同的 RuntimeClass,以提供效能與安全性之間的平衡。 例如,如果你的部分工作負載需要高階別的資訊保安保證,你可以決定在排程這些 Pod 時,儘量使它們在使用硬體虛擬化的容器執行時中執行。 這樣,你將從這些不同執行時所提供的額外隔離中獲益,代價是一些額外的開銷。

你還可以使用 RuntimeClass 執行具有相同容器執行時,但具有不同設定的 Pod。

注意RuntimeClass是全域性生效的,不受名稱空間限制。

檢視runtimeclass。

root@k8scludes1:~/containerd-gvisor# kubectl get runtimeclass
No resources found

編輯RuntimeClass配置檔案,handler後面寫runtime的名字,我們要使用gvisor就寫runsc。

root@k8scludes1:~/containerd-gvisor# vim myruntimeclass.yaml

#建立runtimeclass,指定使用runsc
root@k8scludes1:~/containerd-gvisor# cat myruntimeclass.yaml 
# RuntimeClass 定義於 node.k8s.io API 組
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  # 用來引用 RuntimeClass 的名字
  # RuntimeClass 是一個叢集層面的資源
  name: myruntimeclass  
# 對應的 CRI 配置的名稱
#handler: myconfiguration
#注意:handler後面寫runtime的名字,我們要使用gvisor就寫runsc
handler: runsc

建立runtimeclass。

root@k8scludes1:~/containerd-gvisor# kubectl apply -f myruntimeclass.yaml 
runtimeclass.node.k8s.io/myruntimeclass created

root@k8scludes1:~/containerd-gvisor# kubectl get runtimeclass
NAME             HANDLER   AGE
myruntimeclass   runsc     20s

9.4 使用gVisor建立pod

一旦完成叢集中 RuntimeClasses 的配置, 你就可以在 Pod spec 中指定 runtimeClassName 來使用它。

runtimeClassName這一設定會告訴 kubelet 使用所指的 RuntimeClass 來執行該 pod。 如果所指的 RuntimeClass 不存在或者 CRI 無法執行相應的 handler, 那麼 pod 將會進入 Failed 終止 階段。 你可以檢視相應的事件, 獲取執行過程中的錯誤資訊。如果未指定 runtimeClassName ,則將使用預設的 RuntimeHandler,相當於禁用 RuntimeClass 功能特性。

編輯pod配置檔案,runtimeClassName: myruntimeclass指定用myruntimeclass裡的runsc來執行pod。

root@k8scludes1:~/containerd-gvisor# vim pod.yaml 

root@k8scludes1:~/containerd-gvisor# cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: podtest
  name: podtest
spec:
  #當需要關閉容器時,立即殺死容器而不等待預設的30秒優雅停機時長。
  terminationGracePeriodSeconds: 0
  runtimeClassName: myruntimeclass
  nodeSelector:
    con: gvisor
  containers:
  - image: hub.c.163.com/library/nginx:latest
    #imagePullPolicy: IfNotPresent:表示如果本地已經存在該映象,則不重新下載;否則從遠端 Docker Hub 下載該映象
    imagePullPolicy: IfNotPresent
    name: podtest
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

建立pod。

root@k8scludes1:~/containerd-gvisor# kubectl apply -f pod.yaml 
pod/podtest created

檢視pod,但是建立失敗。

root@k8scludes1:~/containerd-gvisor# kubectl get pod -o wide
NAME      READY   STATUS              RESTARTS   AGE   IP       NODE              NOMINATED NODE   READINESS GATES
podtest   0/1     ContainerCreating   0          24s   <none>   ubuntuk8sclient   <none>           <none>

檢視pod描述,invalid runtime name containerd-shim-runsc-v1, correct runtime name should be either format like io.containerd.runc.v1 or a full path to the binary: unknown 告訴我們containerd-shim-runsc-v1的格式不對。

root@k8scludes1:~/containerd-gvisor# kubectl describe pod podtest 
Name:         podtest
Namespace:    minsvcbug
Priority:     0
Node:         ubuntuk8sclient/192.168.110.139

    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              con=gvisor
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason                  Age                 From               Message
  ----     ------                  ----                ----               -------
  Normal   Scheduled               49s                 default-scheduler  Successfully assigned minsvcbug/podtest to ubuntuk8sclient
  Warning  FailedCreatePodSandBox  22s (x25 over 47s)  kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to create containerd task: failed to start shim: failed to resolve runtime path: invalid runtime name containerd-shim-runsc-v1, correct runtime name should be either format like `io.containerd.runc.v1` or a full path to the binary: unknown

刪除pod。

root@k8scludes1:~/containerd-gvisor# kubectl delete pod podtest 
pod "podtest" deleted

回到ubuntuk8sclient修改containerd配置檔案,runsc的runtime_type不應該寫為containerd-shim-runsc-v1,而應該是runtime_type = "io.containerd.runsc.v1"。

root@ubuntuk8sclient:~# vim /etc/containerd/config.toml 

root@ubuntuk8sclient:~# grep runtime_type /etc/containerd/config.toml
        runtime_type = ""
          runtime_type = "io.containerd.runc.v2"
          runtime_type = "io.containerd.runsc.v1"
        runtime_type = ""

重新載入配置檔案並重啟containerd。

root@ubuntuk8sclient:~# systemctl daemon-reload ;systemctl restart containerd

繼續建立pod。

root@k8scludes1:~/containerd-gvisor# kubectl apply -f pod.yaml 
pod/podtest created

pod建立成功了。

root@k8scludes1:~/containerd-gvisor# kubectl get pod -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP              NODE              NOMINATED NODE   READINESS GATES
podtest   1/1     Running   0          10s   10.244.228.27   ubuntuk8sclient   <none>           <none>

在宿主機上檢視nginx容器。

root@ubuntuk8sclient:~# nerdctl ps | grep podtest
d4604b2b8b39    registry.aliyuncs.com/google_containers/pause:3.6             "/pause"                  46 seconds ago    Up                 k8s://minsvcbug/podtest                            
dcb76b70a98e    hub.c.163.com/library/nginx:latest                            "nginx -g daemon off;"    45 seconds ago    Up                 k8s://minsvcbug/podtest/podtest                    

gvisor以沙箱的方式執行容器,在宿主機裡就看不到容器裡執行的程序了。

root@ubuntuk8sclient:~# ps -ef | grep nginx
root     111683  27377  0 02:36 pts/1    00:00:00 grep --color=auto nginx

刪除pod。

root@k8scludes1:~/containerd-gvisor# kubectl delete pod podtest 
pod "podtest" deleted

十.總結

Gvisor作為一種安全容器執行時,透過引入沙箱機制,實現了對容器程序的細粒度控制,有效提高了容器的安全性。雖然相較於傳統容器技術,Gvisor可能帶來一定的效能開銷,但其在安全性方面的優勢足以彌補這一不足。

相關文章