#RunCVM:容器与虚拟机的融合艺术 - 搅拌均匀
序言
非常抱歉前几天拖更了,最近有点忙,在这里向各位关注我的朋友道一声抱歉,今天,推荐一个好用的容器化的虚拟技术,是融合了两种技术。具体看下文。
在咱们这个日新月异的科技时代,技术就好比是厨房里的柴米油盐,缺了它们,生活这道大餐就少了滋味。今儿个,咱们聊的不是怎么炒菜,而是怎么用一种新颖的“容器”——RunCVM 来烹饪出一道道美味佳肴。
想象一下,你是一位身怀绝技的大厨,面前摆满了各式各样的食材(数据和应用程序),而你需要的不仅仅是一口普通的锅(容器技术),而是一个多功能、能够精准控制火候和环境的智能灶台(RunCVM)。这不仅仅是烹饪,这是一场科技与创新的盛宴!
RunCVM,它就像是那个总能在你需要时递上趁手工具的厨房老手,让你在虚拟化的世界里,轻松驾驭容器和虚拟机。它不仅能让你的应用程序在虚拟机中翩翩起舞,还能让它们在容器的海洋中自由航行。而且,它还承诺,这一切都将像使用 Docker 一样简单——哪怕你只是想要一个小巧的 Podman。
所以,系好围裙,准备好你的炒锅,咱们要一起走进 RunCVM 的奇妙世界,看看它是如何让技术变得不仅高效,而且有趣!
快速开始
安装:
curl -s -o - https://raw.githubusercontent.com/newsnowlabs/runcvm/main/runcvm-scripts/runcvm-install-runtime.sh | sudo sh
现在启动一个监听 8080 端口的 nginx 虚拟机:
docker run --runtime=runcvm --name nginx1 --rm -p 8080:80 nginx
启动一个拥有 2 个 CPU 和 2G 内存的 MariaDB 虚拟机,监听 3306 端口:
docker run --runtime=runcvm --name mariadb1 --rm -p 3306:3306 --cpus 2 --memory 2G --env=MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1 mariadb
启动一个带有交互式终端的纯净 ubuntu 虚拟机:
docker run --runtime=runcvm --name ubuntu1 --rm -it ubuntu
在ubuntu1
上获得另一个交互式控制台:
docker exec -it ubuntu1 bash
启动一个拥有 1G 内存和 1G ext4 格式的后端文件,挂载在/var/lib/docker
并存储在底层容器文件系统中的虚拟机:
docker run -it --runtime=runcvm --memory=1G --env=RUNCVM_DISKS=/disks/docker,/var/lib/docker,ext4,1G <docker-image>
启动一个拥有 2G 内存和 5G ext4 格式的后端文件,挂载在/var/lib/docker
并存储在主机上的专用 Docker 卷中的虚拟机:
docker run -it --runtime=runcvm --memory=2G --mount=type=volume,src=runcvm-disks,dst=/disks --env=RUNCVM_DISKS=/disks/docker,/var/lib/docker,ext4,5G <docker-image>
在 MTU 为 9000 的网络上启动一个 3 节点 Docker Swarm,并在 Swarm 上启动一个 http 全局服务:
git clone https://github.com/newsnowlabs/runcvm.git && \
cd runcvm/tests/00-http-docker-swarm && \
NODES=3 MTU=9000 ./test
系统工作负载
Docker+Sysbox 运行时演示 - 启动运行 Systemd 和 Docker 的 Ubuntu,并使用Sysbox[1] 运行时;然后在其中运行一个 Alpine Sysbox 容器;在那个容器内安装 dockerd 并运行'hello-world'镜像的容器:
cat <<EOF | docker build --tag=ubuntu-docker-sysbox -
FROM ubuntu:jammy
RUN apt update && apt -y install apt-utils kmod wget iproute2 systemd \
ca-certificates curl gnupg udev dbus && \
curl -fsSL https://get.docker.com | bash
RUN wget -O /tmp/sysbox.deb \
https://downloads.nestybox.com/sysbox/releases/v0.6.2/sysbox-ce_0.6.2-0.linux_amd64.deb && \
apt -y install /tmp/sysbox.deb
ENTRYPOINT ["/lib/systemd/systemd"]
ENV RUNCVM_DISKS='/disks/docker,/var/lib/docker,ext4,1G;/disks/sysbox,/var/lib/sysbox,ext4,1G'
VOLUME /disks
EOF
docker run -d --runtime=runcvm -m 2g --name=ubuntu-docker-sysbox ubuntu-docker-sysbox
docker exec ubuntu-docker-sysbox bash -c "docker run --rm --runtime=sysbox-runc alpine ash -x -c 'apk add docker; dockerd &>/dev/null & sleep 5; docker run --rm hello-world'"
docker rm -fv ubuntu-docker-sysbox
在 Asciinema 上观看[2]
嵌套 RunCVM 演示 - 启动运行 Systemd 和 Docker 的 Ubuntu,并安装 RunCVM 运行时;然后在其中运行一个 Alpine RunCVM 容器/虚拟机;在那个容器内安装 dockerd,并在那个容器内运行'hello-world'镜像的容器:
cat <<EOF | docker build --tag=ubuntu-docker-runcvm -
FROM ubuntu:jammy
RUN apt update && apt -y install apt-utils kmod wget iproute2 systemd \
ca-certificates curl gnupg udev dbus && \
curl -fsSL https://get.docker.com | bash
COPY --from=newsnowlabs/runcvm:latest /opt /opt/
RUN rm -f /etc/init.d/docker && \
bash /opt/runcvm/scripts/runcvm-install-runtime.sh --no-dockerd && \
echo kvm_intel >>/etc/modules
ENTRYPOINT ["/lib/systemd/systemd"]
ENV RUNCVM_DISKS='/disks/docker,/var/lib/docker,ext4,1G'
VOLUME /disks
EOF
docker run -d --runtime=runcvm -m 2g --name=ubuntu-docker-runcvm ubuntu-docker-runcvm
docker exec ubuntu-docker-runcvm bash -c "docker run --rm --runtime=runcvm alpine ash -x -c 'apk add docker; dockerd &>/dev/null & sleep 5; docker run --rm hello-world'"
docker rm -fv ubuntu-docker-runcvm
Docker+GVisor 运行时演示 - 启动运行 Systemd 和 Docker 的 Ubuntu,并使用Sysbox[3] 运行时;然后在其中运行一个 Alpine Sysbox 容器;在那个容器内安装 dockerd 并运行'hello-world'镜像的容器:
cat <<EOF | docker build --tag=ubuntu-docker-gvisor -
FROM ubuntu:jammy
RUN apt update && apt -y install apt-utils kmod wget iproute2 systemd \
ca-certificates curl gnupg udev dbus && \
curl -fsSL https://get.docker.com | bash
RUN curl -fsSL https://gvisor.dev/archive.key | gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
RUN echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" >/etc/apt/sources.list.d/gvisor.list && \
apt update && \
apt-get install -y runsc
ENTRYPOINT ["/lib/systemd/systemd"]
ENV RUNCVM_DISKS='/disks/docker,/var/lib/docker,ext4,1G;/disks/sysbox,/var/lib/sysbox,ext4,1G'
VOLUME /disks
EOF
docker run -d --runtime=runcvm -m 2g --name=ubuntu-docker-sysbox ubuntu-docker-sysbox
docker exec ubuntu-docker-sysbox bash -c "docker run --rm --runtime=sysbox-runc alpine ash -x -c 'apk add docker; dockerd &>/dev/null & sleep 5; docker run --rm hello-world'"
docker rm -fv ubuntu-docker-sysbox
启动OpenWrt[4] - 端口转发到 LuCI web UI 的 10080 端口:
docker import --change='ENTRYPOINT ["/sbin/init"]' https://archive.openwrt.org/releases/23.05.2/targets/x86/generic/openwrt-23.05.2-x86-generic-rootfs.tar.gz openwrt-23.05.2 && \
docker network create --subnet 172.128.0.0/24 runcvm-openwrt && \
echo -e "config interface 'loopback'\n\toption device 'lo'\n\toption proto 'static'\n\toption ipaddr '127.0.0.1'\n\toption netmask '255.0.0.0'\n\nconfig device\n\toption name 'br-lan'\n\toption type 'bridge'\n\tlist ports 'eth0'\n\nconfig interface 'lan'\n\toption device 'br-lan'\n\toption proto 'static'\n\toption ipaddr '172.128.0.5'\n\toption netmask '255.255.255.0'\n\toption gateway '172.128.0.1'\n" >/tmp/runcvm-openwrt-network && \
docker run -it --rm --runtime=runcvm --name=openwrt --network=runcvm-openwrt --ip=172.128.0.5 -v /tmp/runcvm-openw
rt-network:/etc/config/network -p 10080:80 openwrt-23.05.2
在 Asciinema 上观看[5]
RunCVM 在 Portainer 中的演示
![与RunCVM玩耍,一个Docker运行时插件](https://i.ytimg.com/vi/OENaWDlCWKg/maxresdefault.jpg "![与RunCVM玩耍,一个Docker运行时插件")[6]
动机
RunCVM 的出现是因为在使用 Docker 和 Podman CLI 启动Kata Containers v2[7]时遇到的困难,以及一种信念,即使用 Docker 在 VM 中启动容器化工作负载不应该那么复杂。
动机包括:努力为 docker/podman 重新添加 OCI CLI 命令以支持 Kata v2[8];其他 Kata 问题#3358[9]、#1123[10]、#1133[11]、#3038[12];#5321[13];#6861[14];Podman 问题#8579[15]和#17070[16];以及 Kubernetes 问题#40114[17];尽管请注意,自从编写 RunCVM 以来,一些问题可能已经解决。
像 Kata 一样,RunCVM 旨在成为一个安全的容器运行时,使用轻量级虚拟机,感觉和性能都像容器,但使用硬件虚拟化技术提供更强的工作负载隔离。
然而,虽然 Kata 旨在在 VM 内部的受限权限命名空间中启动标准容器镜像,运行单个固定且经过大量定制的内核和 Linux 发行版,专门为此目的优化,RunCVM 有意启动容器或 VM镜像作为VM 的根文件系统,使用标准或定制的 Linux 内核,结果是 RunCVM 的可以运行 Kata 的安全和内核模型明确阻止的 VM 工作负载。
例如:
RunCVM 可以启动期望直接与硬件接口的系统镜像,如OpenWRT[18] RunCVM 可以在 RunCVM VM 内部启动 VM 嵌套 - 即'内部'RunCVM 容器/虚拟机客户端可以由在'外部'RunCVM 容器/虚拟机客户端内部运行的 Docker 启动(假设主机支持嵌套 VM) - 在这种意义上,RunCVM 是'可重入'的。
RunCVM 的特点:
与 docker run
兼容(实验性支持podman run
)。使用轻量级'包装运行时'技术,颠覆了标准容器运行时 runc
的行为,导致在容器内启动 VM(使其代码占用空间和外部依赖非常小,其内部结构非常简单,易于理解和定制以满足特定目的)。在提供 KVM 的 Linux 发行版和开发平台上高度可移植。甚至可以安装在GitHub Codespaces[19]上! 使用现成的开源组件,几乎完全用 shell 脚本编写,以简化、可移植性和易于开发。
RunCVM 为了这种简单性做出了一些权衡。请参阅功能和限制[20]的完整列表。
RunCVM 的应用
RunCVM 的主要应用如下:
运行和测试应用程序,这些应用程序:
不适用于(或需要增强权限才能适用于)标准容器运行时(例如 systemd
、dockerd
、Docker swarm 服务、Kubernetes[21])需要运行内核,或需要主机上不可用的内核版本或模块 需要可以仿真的特定硬件,例如磁盘、图形显示
RunCVM 的工作原理
RunCVM 的“包装”运行时runcvm-runtime
接收由docker
run
/create
命令触发的容器创建命令,并修改请求的容器配置,以便创建的容器将启动一个从容器的文件系统启动的 VM,然后将请求传递给标准容器运行时(runc
)来实际创建和启动容器。
有关 RunCVM 内部的深入探讨,请参见开发 RunCVM[25]部分。
系统要求
RunCVM 应该在任何运行 Linux 内核>= 5.10 的 amd64(x86_64)硬件(或 VM)上运行,并且支持KVM[26] 和 Docker。所以如果你的主机已经可以运行KVM[27] VM 和Docker[28],那么它应该可以运行 RunCVM。
RunCVM 没有其他主机依赖项,除了 Docker(或实验性的 Podman)和kvm
和tun
内核模块。RunCVM 包含所有运行所需的二进制文件和库(包括它自己的 QEMU 二进制文件)。
RunCVM 在 Debian Bullseye 和GitHub Codespaces[29]上进行了测试。
rp_filter sysctl 设置
为了让 RunCVM 支持容器/VM 中的 Docker DNS,必须满足以下/proc/sys/net/ipv4/conf/
的条件:
all/rp_filter
和<bridge>/rp_filter
的最大值应为 0('无源验证')或 2(根据 RFC3704 定义的宽松模式) (其中<bridge>
是支持 RunCVM 容器/VM 将附加的 Docker 网络的任何桥接)
这意味着:
如果 all/rp_filter
将设置为 0,则<bridge>/rp_filter
必须设置为 0 或 2 (或者,如果<bridge>
尚未或可能尚未创建,则default/rp_filter
必须设置为 0 或 2)如果 all/rp_filter
将设置为 1,则<bridge>/rp_filter
必须设置为 2 (或者,如果<bridge>
尚未或可能尚未创建,则default/rp_filter
必须设置为 2)如果 all/rp_filter
将设置为 2,则无需进一步操作
截至目前:
Debian 默认值为 0
;Ubuntu 默认值为 2
;Google Cloud Debian 映像默认值为 1
,必须修改或覆盖/etc/sysctl.d/60-gce-network-security.conf
中的rp_filter
设置以支持 RunCVM。
我们建议将all/rp_filter
设置为 2,因为这是最简单且提供良好安全平衡的更改。
安装
运行:
curl -s -o - https://raw.githubusercontent.com/newsnowlabs/runcvm/main/runcvm-scripts/runcvm-install-runtime.sh | sudo sh
这将:
将 RunCVM 软件包安装到 /opt/runcvm
(目前不支持安装在其他地方)对于 Docker 支持: 通过修补 /etc/docker/daemon.json
将runcvm
添加到runtimes
属性,启用 RunCVM 运行时如果可以检测到系统如何重启 dockerd
(例如systemctl restart docker
),则重新启动dockerd
通过 docker info
验证 RunCVM 是否被识别对于 Podman 支持(实验性) 显示修补 /etc/containers/containers.conf
的说明检查系统网络设备 rp_filter
设置,并在必要时进行修改
安装后,启动一个基本的测试 RunCVM 容器/VM:
docker run --runtime=runcvm --rm -it hello-world
在 Google Cloud 上安装
创建一个允许实例具有 VMX 功能的映像:
gcloud compute images create debian-12-vmx --source-image-project=debian-cloud --source-image-family=debian-12 --licenses="https://compute.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx"
现在启动一个 VM,安装 Docker 和 RunCVM:
cat >/tmp/startup-script.sh <<EOF
#!/bin/bash
apt update && apt -y install apt-utils kmod wget iproute2 systemd \
ca-certificates curl gnupg udev dbus jq && \
mkdir -p /etc/docker && echo '{"userland-proxy": false}' >/etc/docker/daemon.json && \
curl -fsSL https://get.docker.com | bash && \
curl -s -o - https://raw.githubusercontent.com/newsnowlabs/runcvm/main/runcvm-scripts/runcvm-install-runtime.sh | sudo REPO=newsnowlabs/runcvm:latest sh
EOF
gcloud compute instances create runcvm-vmx-test --zone=us-central1-a --machine-type=n2-highmem-2 --network-interface=network-tier=PREMIUM,stack-type=IPV4_ONLY,subnet=default --metadata-from-file=startup-script=/tmp/startup-script.sh --no-restart-on-failure --maintenance-policy=TERMINATE --provisioning-model=SPOT --instance-termination-action=STOP --no-service-account --no-scopes --create-disk=auto-delete=yes,boot=yes,image=debian-12-vmx,mode=rw,size=50,type=pd-ssd --no-shielded-secure-boot --shielded-vtpm --shielded-integrity-monitoring --labels=goog-ec-src=vm_add-gcloud --reservation-affinity=any
升级
要升级,请按照以下程序操作:
停止所有 RunCVM 容器。 运行 /opt/runcvm/scripts/runcvm-install-runtime.sh
(或重新运行安装命令 - 它运行相同的脚本)启动任何 RunCVM 容器。
功能和限制
在下面的 RunCVM 当前主要功能和限制的总结中,[+] 用于表示与标准容器运行时的兼容性区域,[-] 用于表示标准容器运行时的功能,该功能不受支持。
注意。未在下面列出的
docker run
和docker exec
选项不受支持,如果使用,其效果是未指定的。
docker run
[+] 对于大多数应用程序的基本操作不需要挂载点。可能需要运行 dockerd
或提高磁盘性能的卷或磁盘挂载点[-] dockerd
的里程会有所不同,除非在/var/lib/docker
上挂载了卷或磁盘[+] --cpus
用于指定 VM CPU 数量[+] --memory
(或-m
)用于指定 VM 内存[-] 其他容器资源限制选项(如 --cpu-*
)、块 IO(--blkio-*
)、内核内存(--kernel-memory
)不受支持或未经测试[+] --detach
(或-d
)受支持[+] --interactive
(或-i
)受支持[+] --tty
(或-t
)受支持(但要进入 CTRL-T 必须按两次 CTRL-T)[+] --attach
(或-a
)受支持[+] 标准输出和标准错误输出应该与在标准 runc
容器中运行相同工作负载大致相似[-] 标准输出和标准错误不是独立多路复用的,所以 docker run --runtime=runcvm debian bash -c 'echo stdout; echo stderr >&2' >/tmp/stdout 2>/tmp/stderr
不会产生预期的结果[-] 在 VM 启动后立即发送的标准输出和标准错误可能会由于串行控制台问题而损坏 [-] 在 VM 关闭前立即发送的标准输出和标准错误可能不会总是完全刷新 [+] --user
(或-u
)受支持[?] --workdir
(或-w
)受支持[+] --env
(或-e
)、--env-file
受支持[+] --entrypoint
受支持[+] --init
- 受支持(但运行的是 RunCVM 自己的 VM 初始化进程,而不是 Docker 的默认tini
)[+] 支持默认桥接网络 [+] 支持使用 --network
指定的自定义/用户定义网络,包括 Docker DNS 解析容器名称和尊重自定义网络 MTU[+] 支持通过 docker run --network
或docker network connect
附加的多个网络接口(包括scope=overlay
网络和具有多个子网的网络)[+] --publish
(或-p
)受支持[+] --dns
、--dns-option
、--dns-search
受支持[+] --ip
受支持[+] --hostname
(或-h
)受支持[-] 不支持在运行中的容器上使用 docker network connect
[-] 不支持 --network=host
和--network=container:name|id
[-] 不支持 IPv6 [+] --mount
(或-v
)支持卷挂载、tmpfs 挂载以及主机文件和目录绑定挂载(dst
挂载路径/disks
是保留的)[-] 绑定挂载主机套接字或设备,和 --device
不受支持挂载 网络 执行环境 标准输入输出/终端 资源分配和限制 退出代码 - [+] 返回入口点的退出代码受支持,但目前需要应用程序支持 - [-] 要返回退出代码,你的入口点可能要么将退出代码写入 /.runcvm/exit-code
(支持的退出代码 0-255)要么调用/opt/runcvm/sbin/qemu-exit <code>
(支持的退出代码 0-127)。后续版本将提供从入口点自动处理退出代码的功能。磁盘性能 docker exec
[+] --user
(或-u
)、--workdir
(或-w
)、--env
(或-e
)、--env-file
、--detach
(或-d
)、--interactive
(或-i
)和--tty
(或-t
)都受支持[+] 标准输出和标准错误是独立多路复用的,所以 docker exec <container> bash -c 'echo stdout; echo stderr >&2' >/tmp/stdout 2>/tmp/stderr
确实会产生预期的结果安全 RunCVM 软件包在 /opt/runcvm
中以只读方式安装在 RunCVM 容器内。容器应用程序不能破坏 RunCVM,但它们可以在 RunCVM 包内执行二进制文件。后续版本可能会将 VM 可用的二进制文件集减少到最小。内核 [+] 使用任何内核,无论是与 RunCVM 预包装的内核还是你自己定制的内核 [+] RunCVM 将尝试根据正在启动的镜像中的 /etc/os-release
检查来选择适当的内核
RunCVM 与 Kata 的比较
下表提供了 RunCVM 和 Kata 在各种功能(如内核、网络/DNS、内存分配、命名空间处理、操作方法和性能特征)方面的高级比较:
功能 | RunCVM | Kata |
---|---|---|
方法论 | 从使用容器文件系统直接挂载为根文件系统的发行版内核启动 VM,使用 virtiofs。VM 设置代码和内核模块被绑定挂载到容器中。VM 的 PID1 运行设置代码以在 VM 内再现容器的网络环境,然后执行容器的原始入口点。 | 从具有自定义根磁盘映像的自定义内核启动 VM,将 virtiofsd 共享的主机容器文件系统挂载到目标文件夹,并在限制命名空间内执行容器的入口点,该命名空间已 chroot 到该文件夹。 |
权限/限制 | 容器代码对 VM 及其设备具有完全的 root 访问权限。它可以运行任何在 VM 中运行的东西,挂载文件系统、安装内核模块、访问设备。RunCVM 辅助进程对ps 等可见。 | 在具有限制权限的 VM 命名空间内运行容器代码。使用挂载、内核模块受到限制。Kata 辅助进程(如 kata-agent 和 chronyd)对ps 不可见。 |
内核 | 启动标准的 Alpine、Debian、Ubuntu 内核。内核/lib/modules 自动挂载在 VM 内。无需主机重新配置即可安装任何所需模块。 | 启动定制内核。内核模块不挂载,需要主机重新配置才能安装。 |
网络/DNS | Docker 容器网络+内部/外部 DNS 开箱即用。不支持docker network connect/disconnect | DNS 问题呈现:使用自定义网络时,外部 ping 工作正常,但内部 docker 主机和外部主机的 DNS 查找失败。[^1] |
内存 | VM 分配并报告总内存,如--memory <mem> 指定 | VM 通过free 报告的总内存似乎与指定的--memory <mem> 无关 [^2] |
CPU | VM 分配并报告 CPU,如--cpus <cpus> 指定 | 必须在 Kata 主机配置中硬编码 CPU |
性能 | 定制内核优化可能会提供改进的启动(~3.2s)或操作性能(~15%) | |
virtiofsd | 在容器命名空间中运行virtiofsd | 未知 |
总结
好了,各位食客,咱们的 RunCVM 盛宴也差不多到了尾声。希望你在这场技术与美食的交融之旅中,不仅吃得满意,还乐在其中。RunCVM 不仅仅是一个工具,它更像是那位在宴席上总能用幽默和智慧让大家开怀大笑的好友。
记住,不管你是在寻找一个能够处理复杂烹饪任务的厨房好帮手,还是一个能够在数字世界中提供强大支持的技术伙伴,RunCVM 都是你的上佳选择。它不仅能让你的工作更加高效,还能让你的日常工作变得轻松愉快。
所以,下次当你需要在虚拟世界中大展身手时,别忘了 RunCVM——那个总能给你带来惊喜的技术好手。咱们后会有期,期待下一次的相聚!
Sysbox: https://github.com/nestybox/sysbox
[2]在 Asciinema 上观看: https://asciinema.org/a/630032
[3]Sysbox: https://github.com/nestybox/sysbox
[4]OpenWrt: https://openwrt.org/
[5]在 Asciinema 上观看: https://asciinema.org/a/631857
[6]与RunCVM玩耍,一个Docker运行时插件: https://www.youtube.com/watch?v=OENaWDlCWKg
[7]Kata Containers v2: https://katacontainers.io/
[8]为 docker/podman 重新添加 OCI CLI 命令以支持 Kata v2: https://github.com/kata-containers/kata-containers/issues/722
[9]#3358: https://github.com/kata-containers/kata-containers/issues/3358
[10]#1123: https://github.com/kata-containers/kata-containers/issues/1123
[11]#1133: https://github.com/kata-containers/kata-containers/issues/1133
[12]#3038: https://github.com/kata-containers/runtime/issues/3038
[13]#5321: https://github.com/kata-containers/runtime/issues/5321
[14]#6861: https://github.com/kata-containers/runtime/issues/6861
[15]#8579: https://github.com/containers/podman/issues/8579
[16]#17070: https://github.com/containers/podman/issues/17070
[17]#40114: https://github.com/kubernetes/website/issues/40114
[18]OpenWRT: https://openwrt.org/
[19]GitHub Codespaces: https://github.com/features/codespaces
[20]功能和限制: #功能和限制
[21]Kubernetes: https://kubernetes.io/
[22]fly.io: https://fly.io
[23]Dockside: https://dockside.io/
[24]RunCVM 和 Dockside: #RunCVM和Dockside
[25]开发 RunCVM: #开发
[26]KVM: https://www.linux-kvm.org/page/Main_Page
[27]KVM: https://www.linux-kvm.org/page/Main_Page
[28]Docker: https://docker.com
[29]GitHub Codespaces: https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=514606231