云原生后端微服务【免费下载链接】classicswarmSwarm Classic: a container clustering system. Not to be confused with Docker Swarm which is at https://github.com/docker/swarmkit项目地址https://gitcode.com/gh_mirrors/cl/classicswarm点击查看免费下载runc 是依据 OCIOpen Container Initiative规范实现容器创建与运行的命令行工具也是 Swarm Classicclassicswarm集群调度体系所依赖的容器运行时组件。本文以仓库内 vendored 的 runc README 为主体结合 libcontainer 文档、nsenter 文档 及 vendor.conf 中的版本记录系统讲解 runc 的定位、构建、测试、日常使用、systemd 集成方式以及其底层的 libcontainer 容器生命周期管理与命名空间注入原理帮助读者完整掌握这一 OCI 运行时工具。一、runc 是什么面向 OCI 规范的容器运行时runc是一个用于按照 OCFOpen Container Format规范创建和运行容器的 CLI 工具。它不负责镜像构建、也不包含 Docker 式的高级编排能力而是聚焦于最核心的一环给定一个已就绪的 rootfs 和一份描述容器配置的 JSON 文件把进程安全地放入隔离环境并启动它。从项目定位看runc 是 OCI 规范的一个实现。它依赖 OCI 官方规格仓库中定义的类型每当规范更新并完成版本化后runc 会同步更新自身对规格仓库的依赖以支持新版规范。值得注意的是文档明确提示在规范尚未最终定型v1之前runc 的配置格式会持续变化这意味着在早期阶段基于旧格式编写的config.json可能需要进行相应迁移。在 classicswarm 仓库中runc 并非直接调用的业务模块而是作为被 vendored 的第三方依赖随仓库分发。在根目录的 vendor.conf 中可以查到当前锁定版本github.com/opencontainers/runc 2c3115481ee1782ad687a9e0b4834f89533c2acf同时在 docker/docker 的 vendor.conf 与 swarmkit 的 vendor.conf 中也各自锁定了对应版本的 runc说明它是 Docker 系技术栈Docker Engine、SwarmKit、Swarm Classic共同依赖的运行时底层。二、构建与安装平台要求、make 流程与 Build Tagsrunc 的构建有一个硬性前提目前只支持 Linux 平台。这与其实现本质直接相关——它依赖 Linux 命名空间namespaces、cgroups 等内核机制来构建隔离环境。2.1 标准构建流程将源码放入 GOPATH 对应路径后执行# 进入 github.com/opencontainers 目录位于 GOPATH/src 下 cd github.com/opencontainers git clone https://github.com/opencontainers/runc cd runc make sudo make installmake负责编译make install将runc可执行文件安装到系统路径典型位置为/usr/local/bin/runcsystemd 示例中即引用该路径。2.2 可选特性与构建标签runc 通过构建标签Build Tags决定是否编译进特定安全特性。要启用 seccomp 支持需要先在系统上安装 libseccomp如果不需要 seccomp可以在执行 make 时传入BUILDTAGS来禁用该特性make BUILDTAGS三个可选特性的依赖关系如下表Build Tag特性依赖seccomp系统调用过滤Syscall filteringlibseccompselinuxselinux 进程与挂载标签无 apparmorapparmor profile 支持libapparmor这组标签对应了三大主流 Linux 安全加固机制seccomp 用于限制容器内进程可发起的系统调用SELinux 与 AppArmor 用于强制访问控制与标签管理实际选择取决于宿主机所在发行版的安全体系。三、运行测试make test 与指定用例runc 自带的测试通过make test执行make test需要注意两点运行前提测试用例运行在 Docker 容器中因此宿主机必须先安装 docker测试要求在容器内挂载 cgroups这一操作由 docker 代为完成因此需要docker 版本高于 1.8.0-rc2。如果只想运行某个特定测试函数可通过TESTFLAGS传入 Go 测试的-run参数进行筛选make test TESTFLAGS-runSomeTestFunction这与 Go 标准测试框架的用法一致便于在开发迭代时只跑与改动相关的用例。四、runc 的使用模型bundle、runc start 与 OCI JSON 配置4.1 基本使用方式runc 的工作模型是以某个 bundle容器运行目录的根目录为工作目录通过runc start加上容器 ID 来启动容器。例如启动 ID 为 test 的容器runc start test / $ ps PID USER COMMAND 1 daemon sh 5 daemon sh / $可以看到容器内 PID 1 即为容器内第一个进程进程视图与宿主机相互隔离符合 PID 命名空间的基本特征。4.2 OCI 容器 JSON 格式与 runc specrunc 使用基于 OCI 规范定义的 JSON 格式来描述容器。文档给出的约定是通过runc spec命令生成config.json并且该生成过程假设文件系统位于名为rootfs的目录中该文件系统内存在 uid 和 gid 均为 0 的用户即 root 用户用于容器内进程的默认身份映射。因此一个最小可运行 bundle 的典型布局是bundle/ ├── config.json # runc spec 生成 └── rootfs/ # 解包后的容器文件系统五、实战示例一从 Docker 镜像构建可运行的 rootfs利用 Docker 的 busybox 镜像需要 Docker 1.3 及以上版本可以快速验证 runc 的完整流程第一步安装 docker 并下载 busybox 镜像docker pull busybox第二步由该镜像创建容器并把其内容导出为 tar 包docker export $(docker create busybox) busybox.tar第三步解包 tar 得到 rootfs 目录mkdir rootfs tar -C rootfs -xf busybox.tar第四步生成容器配置runc spec第五步在 bundle 目录下启动容器并进入 shellrunc start test / # ps PID USER COMMAND 1 root sh 9 root ps至此一个由 Docker 镜像导出、由 runc 直接运行的最小容器即完成验证。这一流程清晰地揭示了 runc 与镜像体系的边界镜像的拉取、导出由 Docker 完成runc 只负责消费 rootfs config.json 来运行容器。六、实战示例二与 systemd 集成runc 启动的容器可以作为服务由 systemd 托管。文档给出的示例是在/usr/lib/systemd/system/下创建minecraft.service单元文件服务名、描述、工作目录可按需修改[Unit] DescriptionMinecraft Build Server Documentationhttp://minecraft.net Afternetwork.target [Service] CPUQuota200% MemoryLimit1536M ExecStart/usr/local/bin/runc start minecraft Restarton-failure WorkingDirectory/containers/minecraftbuild [Install] WantedBymulti-user.target使用前需要把 bundle 的根目录和 JSON 配置文件放在WorkingDirectory指定的目录中然后执行systemctl daemon-reload systemctl start minecraft.service这里有一个关键的配置细节如果用runc spec生成的 JSON 配置必须修改config.json中的process.terminal为 false。原因是 systemd 服务无法从 stdin 设置终端若保持 terminal 为 truerunc 会尝试创建 tty 而导致服务启动异常。这正体现了 OCI 配置中process.terminal字段对容器 I/O 模型的决定性影响。七、底层原理libcontainer 的容器生命周期管理runc 的运行时能力建立在 libcontainer 之上。根据 libcontainer 文档libcontainer 是一个用 Go 原生实现创建容器的库提供对命名空间namespaces、cgroups、capabilities 和文件系统访问控制filesystem access controls的支持并允许在容器创建后执行额外的生命周期管理操作。7.1 两阶段启动模型与 init 入口容器采用两步骤进程启动模型需要一个作为容器 init 进程执行的二进制libcontainer 使用当前二进制/proc/self/exe来执行 init 阶段这一首阶段进程被称为 bootstrap。因此调用方程序中必须存在一个以 init 为入口的 init 函数func init() { if len(os.Args) 1 os.Args[1] init { runtime.GOMAXPROCS(1) runtime.LockOSThread() factory, _ : libcontainer.New() if err : factory.StartInitialization(); err ! nil { logrus.Fatal(err) } panic(--this line should have never been executed, congratulations--) } }注意这里的两处细节runtime.GOMAXPROCS(1)与runtime.LockOSThread()配合确保初始化阶段线程模型符合命名空间操作的约束而末尾的 panic 是一种防御性写法——正常流程不会执行到这里。7.2 工厂Factory与配置configs.Config创建容器前需要先初始化一个工厂实例工厂负责容器的创建与初始化。示例中指定了根目录、cgroup 驱动方式Cgroupfs以及 init 参数factory, err : libcontainer.New(/var/lib/container, libcontainer.Cgroupfs, libcontainer.InitArgs(os.Args[0], init)) if err ! nil { logrus.Fatal(err) return }随后构造一个configs.Config结构体描述容器的创建方式。该结构体是理解容器配置的枢纽其主要字段包括Rootfs容器根文件系统路径Capabilities授予容器内进程的 Linux capabilities 列表如CAP_CHOWN、CAP_NET_RAW、CAP_SETUID、CAP_SYS_CHROOT等遵循最小权限原则按需授予Namespaces需要隔离的命名空间类型列表示例中启用了 NEWNS挂载、NEWUTS主机名、NEWIPC、NEWPID进程、NEWUSER用户、NEWNET网络Cgroupscgroup 配置包含名称、父级Parent如 system以及资源限制Resources例如MemorySwappiness、设备访问策略AllowAllDevices与AllowedDevicesMaskPaths / ReadonlyPaths对容器内/proc等敏感路径进行屏蔽或只读化的安全策略示例中屏蔽了/proc/kcore并将/proc/sys、/proc/sysrq-trigger、/proc/irq、/proc/bus设为只读Devices默认自动创建的设备节点Hostname容器主机名Mounts挂载点列表示例涵盖 proc、tmpfs/dev、devpts/dev/pts、shm/dev/shm、mqueue/dev/mqueue、sysfs/sys只读并给出默认挂载标志MS_NOEXEC | MS_NOSUID | MS_NODEVUidMappings / GidMappings用户与组 ID 映射示例将容器内 ID 0 映射到宿主 ID 1000映射范围 65536这是 user namespace 的核心配置Networks网络配置示例中配置了回环设备127.0.0.1/0Rlimits资源限制示例中设置RLIMIT_NOFILE文件描述符上限软硬限制均为 1025。7.3 创建、启动、等待与销毁配置填充完成后即可创建容器并以 bash 作为初始进程启动获取进程 PID 以便后续 wait、signal 或 killcontainer, err : factory.Create(container-id, config) if err ! nil { logrus.Fatal(err) return } process : libcontainer.Process{ Args: []string{/bin/bash}, Env: []string{PATH/bin}, User: daemon, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, } err : container.Start(process) if err ! nil { logrus.Fatal(err) container.Destroy() return } // wait for the process to finish. _, err : process.Wait() if err ! nil { logrus.Fatal(err) } // destroy the container. container.Destroy()7.4 运行中容器的其他操作libcontainer 还提供了一组对运行中容器的查询与控制接口// 返回容器内所有进程的 pid processes, err : container.Processes() // 获取容器及其进程的 cpu、memory、io、network 详细统计信息 stats, err : container.Stats() // 暂停容器内所有进程 container.Pause() // 恢复所有被暂停的进程 container.Resume()这些能力为上层编排系统如 Swarm Classic 对容器的健康监控与资源统计提供了原语支撑。八、nsenter在 Go 运行时启动前完成 setnslibcontainer 的命名空间注入依赖 nsenter 包它解决了一个 Go 生态中的经典难题Go 运行时是多线程的在多线程环境下执行 setns 会导致命名空间状态在多个线程间不一致。nsenter 的方案是注册一个特殊的 init 构造函数在 Go 运行时启动之前执行 C 代码完成 setns。从 nsenter.go 可以看到该包通过 cgo 机制在 import 时注册__attribute__((constructor))的init()函数其 preamble 直接声明extern void nsexec()并调用它。这意味着只要应用 import 了nsenter包C 侧的nsexec()就会在 Go 运行时启动前自动执行。gccgo 场景下则有对应的 nsenter_gccgo.go 变体通过导出变量引用 C 函数来确保链接器不会优化掉相关代码。根据 nsenter READMEnsexec()的执行流程是检查环境变量_LIBCONTAINER_INITPID获取要加入的容器进程 PID从/proc/[pid]/ns找到对应的命名空间 fd通过setns系统调用加入从_LIBCONTAINER_INITPIPE获取管道编号错误信息可通过该管道回传若配置了 tty_LIBCONTAINER_CONSOLE_PATH会有值此时启动一个 console 用于输出最后 clone 一个子进程、父进程退出由 Go 运行时接管。在 nsexec.c 的实现中可以看到更完整的信息函数首先校验_LIBCONTAINER_INITTYPE取值为 standard 时直接返回取值为 setns 时才进入加入命名空间的流程随后通过_LIBCONTAINER_INITPIPE读取 netlink 消息以获取 PID、console 路径等属性INIT_MSG、PID_ATTR、CONSOLE_PATH_ATTR等消息类型在注释中标注定义于 libcontainer 的 message_linux.go。该 C 代码还包含对 glibc 2.12 等旧版本缺少setns封装的兼容处理——直接使用__NR_setns原始系统调用体现了对老发行版运行环境的适配考量。九、Checkpoint Restore基于 CRIU 的容器迁移能力libcontainer 集成了 CRIU 以支持容器的 checkpoint 与 restoreCheckpoint把容器内运行进程的状态保存到磁盘Restore将保存的状态恢复到新进程中可以在同一台机器或其他机器上完成。这为容器迁移、暂停恢复等高级运维场景提供了基础。文档明确要求criu 版本 1.5.2 或更高如果系统尚未安装可自行从源码构建官方 docker 镜像在构建 libcontainer 时也会预装 criu。十、注意事项与小结围绕 runc 的使用有几个关键要点值得总结平台限定runc 仅支持 Linux其全部能力建立在 Linux 命名空间、cgroups、capabilities 等内核机制之上配置演进OCI 规范尚未定稿期间runc 的配置格式会变化早期 config.json 需要跟随升级特性开关seccomp、selinux、apparmor 三个特性通过构建标签控制seccomp 依赖 libseccomp可按需通过BUILDTAGS关闭bundle 约定默认配置假设存在rootfs目录与 uid/gid 为 0 的用户runc spec负责生成标准 config.jsonsystemd 集成需将process.terminal改为 false并将 bundle 置于服务的 WorkingDirectory 中底层支撑libcontainer 提供两阶段启动、工厂模式、配置结构体与完整生命周期管理 APInsenter 则在 Go 运行时启动前完成 setns二者共同构成了 runc 的运行时内核。在 classicswarm 仓库中runc 以固定版本见 vendor.conf随源码一起分发是 Swarm Classic 所依赖的容器运行时生态的一部分。通过本文的构建、测试、使用与源码级原理剖析读者可以完整掌握从用 runc 跑一个容器到理解其底层实现机制的整条知识链。赞分享云原生后端微服务【免费下载链接】classicswarmSwarm Classic: a container clustering system. Not to be confused with Docker Swarm which is at https://github.com/docker/swarmkit项目地址https://gitcode.com/gh_mirrors/cl/classicswarm点击查看免费下载相关推荐探索RUNC容器运行时的基石探索RUNC容器运行时的基石 是一个开源项目由Open Containers Initiative OCI 维护它是一个符合 OCI 规范的轻量级工具用云原生容器运行时CLI深入解析 libcontainer 和 runc容器运行时的核心技术指南深入解析 libcontainer 和 runc容器运行时的核心技术指南 在现代云计算和容器化技术中 libcontainer 和 runc 作为容器运行时操作系统云原生终极指南Docker容器运行时原理详解——从runC到containerd的完整技术架构终极指南Docker容器运行时原理详解——从runC到containerd的完整技术架构 Docker作为容器化技术的领军者其核心在于高效的容器运行时架构。云原生容器运行时虚拟化容器编排上一篇如何利用udacity-nanodegrees免费学习编程10个实用技巧下一篇蓝光过滤终极指南Captura如何有效缓解眼部疲劳创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考