Linux:用 runc 构建 ARM 平台容器

devtools/2025/3/24 8:13:28/

文章目录

  • 1. 前言
  • 2. 构建 runc
    • 2.1 准备 C 交叉编译器
    • 2.2 编译 libseccomp 库
    • 2.3 编译 runc
      • 2.3.1 安装 go 编译器
      • 2.3.2 编译 runc
  • 3. 构建 runc 镜像包 + 测试运行
    • 3.1 OCI 规范
    • 3.2 手工构建 OCI 镜像
    • 3.3 运行

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 构建 runc

runc容器的运行时容器运行时的基础组件,常见容器直接使用 Docker 构建,但在资源受限的一些嵌入式设备,Docker 整体就会显得庞大且冗余,这时候我们通常会使用 runc + OCI 镜像的简单组合,来构建容器

2.1 准备 C 交叉编译器

本文描述在 Ubuntu 22.04.5 LTS 桌面系统下,为 aarch64 平台构建容器的过程,不管是构建 runc 用到的 libseccomp 库,还是编译 runc 源代码中的 C 代码,都需要用到 aarch64 的交叉编译器,所以首先安装交叉编译器:

$ sudo apt-get install gcc-aarch64-linux-gnu

当然,读者也可以使用已有的交叉编译器。

2.2 编译 libseccomp 库

runc 使用了 libseccomp 库,我们先构建 libseccomp 。下载 libseccomp 源码并解压,切换到 libseccomp 源码目录:

$ sudo apt-get install autoconf libtool
$ ./autogen.sh
$ ./configure --host=aarch64-linux-gnu --prefix=`pwd`/_install
$ make && make install

成功编译完成后,在 libseccomp 源码目录下的 _install 目录下,生成了如下文件:

$ cd _install
$ tree
.
├── bin
│   └── scmp_sys_resolver
├── include
│   ├── seccomp.h
│   └── seccomp-syscalls.h
├── lib
│   ├── libseccomp.a
│   ├── libseccomp.la
│   ├── libseccomp.so -> libseccomp.so.0.0.0
│   ├── libseccomp.so.0 -> libseccomp.so.0.0.0
│   ├── libseccomp.so.0.0.0
│   └── pkgconfig
│       └── libseccomp.pc
└── share└── man├── man1│   └── scmp_sys_resolver.1└── man3......8 directories, 44 files

然后将 seccomp.h,seccomp-syscalls.h,libseccomp.so.0.0.0 安装到交叉编译器 gcc-aarch64-linux-gnu 的对应目录:

$ sudo cp include/*.h /usr/aarch64-linux-gnu/include
$ sudo cp lib/libseccomp.a /usr/aarch64-linux-gnu/lib
$ sudo cp lib/libseccomp.la /usr/aarch64-linux-gnu/lib
$ sudo cp lib/libseccomp.so.0.0.0 /usr/aarch64-linux-gnu/lib
$ sudo ln -s /usr/aarch64-linux-gnu/lib/lib/libseccomp.so.0.0.0 /usr/aarch64-linux-gnu/lib/lib/libseccomp.so.0
$ sudo ln -s /usr/aarch64-linux-gnu/lib/lib/libseccomp.so.0.0.0 /usr/aarch64-linux-gnu/lib/lib/libseccomp.so

2.3 编译 runc

2.3.1 安装 go 编译器

runc 源码使用了 goC 语言,C 语言需要的交叉编译器前面已经准备好了,现在参考 Download and install 安装 go 编译器

$ wget https://golang.google.cn/dl/go1.22.4.linux-amd64.tar.gz
$ rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz
$ export PATH=$PATH:/usr/local/go/bin
$ go version
go version go1.22.4 linux/amd64

2.3.2 编译 runc

下载 runc 源码解压到 runc 目录,然后编译:

$ cd runc
$ GOARCH=arm64 CGO_ENABLED=1 make
$ PREFIX=`pwd`/_install make install
$ file _install/sbin/runc
_install/sbin/runc: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=db10b08ff0a8c578e7c80a9009ca5234b258c9e2, for GNU/Linux 3.7.0, with debug_info, not stripped

libseccomp.so.0.0.0 拷贝到 aarch64 目标机器的目录 /usr/lib,并创建符号链接文件 libseccomp.so.0

# ln -s /usr/lib/libseccomp.so.0.0.0 /usr/lib/libseccomp.so.0

拷贝 runc 文件到 aarch64 目标机器的目录 /usr/sbin,然后添加可执行权限:

# chmod +x /usr/sbin/runc

测试运行:

# ./runc --version
runc version 1.2.0-rc.2+dev
commit: v1.2.0-rc.2-29-gad5b481d
spec: 1.2.0
go: go1.22.4

3. 构建 runc 镜像包 + 测试运行

runc 将配置文件 config.json 运行于一个通过 Linux 命名空间cgroup 隔离的独立环境。Linux 命名空间 用来控制可见的视野,cgroup 用于资源(CPU、内存、磁盘、网络等)的隔离。runc 运行需要一个镜像包,这个镜像包需要符合 (OCI: Open Container Initiative) 的相关规范。

3.1 OCI 规范

开放容器计划 (OCI: Open Container Initiative) 是一个轻量级的开放式治理结构(项目),在 Linux 基金会的支持下成立,其明确目的是围绕容器格式和运行时创建开放的行业标准。OCI 于 2015 年 6 月 22 日由 DockerCoreOS容器行业的其他领导者推出。

OCI 目前包含 3 个规范:运行时规范 (runtime-spec)镜像规范 (image-spec)分发规范 (distribution-spec)运行时规范概述了如何运行在磁盘上解压的 OCI 镜像包

为了支持 OCI 镜像格式包含足够的信息以在目标平台上启动应用程序(例如命令、参数、环境变量等)。此规范定义了如何创建 OCI 镜像(通常由构建系统完成),并输出镜像清单、文件系统(层)序列化、镜像配置。概括地说,镜像清单包含有关镜像内容和依赖关系的元数据,包括一个或多个文件系统序列化存档的内容可寻址标识,这些存档将被解压缩以构成最终的可运行文件系统。镜像配置包括应用程序参数、环境等信息。镜像清单镜像配置和一个或多个文件系统序列化的组合称为 OCI 镜像

3.2 手工构建 OCI 镜像

托管在 Docker Hub 上的 Docker 官方镜像,是现成可用的 OCI 镜像,但很可惜,国外的这些镜像无法正常访问,国内的一些镜像也不是长期可用。同时由于这些镜像对于嵌入式设备来说,往往显得庞大冗余,这时候我们就可以选择手工构建简易的、满足自身需求的、符合 OCI 相关规范的镜像文件。

构建一个运行于容器环境的测试程序 oci_test.c

#include <stdio.h>
#include <unistd.h>int main(void)
{int count = 0;while (1) {printf("oci test: count = %d\n", ++count);sleep(3);}return 0;
}
$ aarch64-linux-gnu-gcc -o oci_test oci_test.c
$ mkdir -p mycontainer/rootfs
$ mkdir -p mycontainer/rootfs/{bin,dev,etc,lib,proc,sbin,sys,tmp,usr/bin,usr/lib,usr/sbin,var}
$ cp oci_test mycontainer/rootfs/usr/bin
$ cp /usr/aarch64-linux-gnu/lib/ld-linux-aarch64.so.1 mycontainer/rootfs/lib/
$ cp /usr/aarch64-linux-gnu/lib/libc.so.6 mycontainer/rootfs/lib/
$ cp /usr/aarch64-linux-gnu/lib/libm.so.6 mycontainer/rootfs/lib/
$ cp /usr/aarch64-linux-gnu/lib/libresolv.so.2 mycontainer/rootfs/lib/$ tar -cf mycontainer.tar mycontainer

将镜像打包成 mycontainer.tarOCI 镜像就制作完成了。

3.3 运行

如章节 2.3 中准备好 runc,假定 runc 放置在 aarch64 目标机器/test 目录下,将章节 3.2 中制作好的 mycontainer.tar 也拷贝到 /test 目录下:

# cd /test
# ./runc spec # 生成配置文件 config.json

编辑 /test/config.json,将配置项 terminal 修改为 false容器入口程序修改为 oci_test

在这里插入图片描述

接着创建并启动容器实例:

# mv config.json ./mycontainer
# ./runc run -d -b ./mycontainer ocitest > ocitest.log 2>&1
# ./runc list
ID          PID         STATUS      BUNDLE                      CREATED                          OWNER
ocitest     16427       running     /test/mycontainer       1970-01-01T08:10:45.791058625Z   root
# ./runc ps ocitest
# ps -ef | grep -v grep | grep "oci_test"
16427 root     oci_test
# ls /sys/fs/cgroup/cpu/ocitest/ -l
total 0
-rw-r--r--    1 root     root             0 Jan  1 08:12 cgroup.clone_children
-rw-r--r--    1 root     root             0 Jan  1 08:10 cgroup.procs
-rw-r--r--    1 root     root             0 Jan  1 08:12 cpu.shares
-rw-r--r--    1 root     root             0 Jan  1 08:12 notify_on_release
-rw-r--r--    1 root     root             0 Jan  1 08:12 tasks
# cat ocitest.log 
oci test: count = 1
oci test: count = 2
oci test: count = 3
oci test: count = 4
oci test: count = 5
oci test: count = 6
oci test: count = 7
oci test: count = 8
oci test: count = 9
oci test: count = 10
oci test: count = 11
oci test: count = 12
oci test: count = 13
oci test: count = 14
oci test: count = 15
oci test: count = 16
oci test: count = 17
oci test: count = 18
oci test: count = 19
oci test: count = 20
oci test: count = 21
oci test: count = 22
oci test: count = 23
oci test: count = 24
oci test: count = 25
oci test: count = 26
oci test: count = 27
oci test: count = 28

停止容器实例:

# ./runc kill ocitest
# kill -9 16427
# ./runc list
ID          PID         STATUS      BUNDLE                      CREATED                          OWNER
ocitest     0           stopped     /test/mycontainer       1970-01-01T08:10:45.791058625Z   root

在删除容器实例之前,其 cgroup 信息仍然存在:

# ls /sys/fs/cgroup/cpu/ocitest/
cgroup.clone_children  cpu.shares             tasks
cgroup.procs           notify_on_release

最后,删除容器实例:

# ./runc delete ocitest
# ls /sys/fs/cgroup/cpu/ocitest/
ls: /sys/fs/cgroup/cpu/ocitest/: No such file or directory

http://www.ppmy.cn/devtools/169463.html

相关文章

Flink 通过 Chunjun Oracle LogMiner 实时读取 Oracle 变更日志并写入 Doris 的方案

文章目录 一、 技术背景二、 关键技术1、 Oracle LogMiner2、 Chunjun 的 LogMiner 关键流程3、修复 Chunjun Oracle LogMiner 问题 一、 技术背景 在大数据实时同步场景中&#xff0c;需要将 Oracle 数据库的变更数据&#xff08;CDC&#xff09; 采集并写入 Apache Doris&am…

网络编程中客户端与服务器的搭建与协议包应用

1.客户端的搭建 2.服务器搭建 3.TCP中的粘包现象 tcp协议为了提高发送的效率&#xff0c;会将短时间连续发送的小数据&#xff0c;当做一组数据统一发送 原理是&#xff1a; tcp协议本身存在一个1500字节的缓存区&#xff0c;tcp协议每次write发送数据的时候&#xff0c;总是…

快速入手-基于Django的主子表间操作mysql(五)

1、如果该表中存在外键&#xff0c;结合实际业务情况&#xff0c;那可以这么写&#xff1a; 2、针对特殊的字典类型&#xff0c;可以这么定义 3、获取元组中的字典值和子表中的value值方法 4、对应的前端页面写法

llama源码学习·model.py[3]ROPE旋转位置编码(4)ROPE的应用

一、源码注释 def apply_rotary_emb(xq: torch.Tensor, # 查询矩阵xk: torch.Tensor, # 键矩阵freqs_cis: torch.Tensor, # 旋转嵌入 ) -> Tuple[torch.Tensor, torch.Tensor]:# 首先将xq和xk张量转换为浮点数# 然后使用reshape将最后一个维度拆分为两个维度&#xff0c;每…

《南京日报》专题报道 | 耘瞳科技“工业之眼”加码“中国智造”

在江宁开发区&#xff0c;机器人已不再是科幻电影里的遥远想象&#xff0c;他们就像人类的“同事”&#xff0c;在工地上忙着贴砖、刷墙、搬运、检测&#xff1b; 在体育训练场上帮助运动员矫正姿势&#xff1b; 在医院里帮助医生发现帕金森早期征兆&#xff0c;在智慧工厂里…

C# 派生 详解

1.1派生 继承设计的目的&#xff1a;经常需要扩展现有类型来添加功能&#xff08;行为和数据&#xff09;。 定义派生类 要在类标识符后添加冒号&#xff0c;接着添加基类名称。 注意&#xff1a;1.通过继承&#xff0c;基类的每个成员都出现在派生类构成的链条中。 2.除非明…

开源模型应用落地-LangGraph101-多智能体协同实践(六)

一、前言 随着人工智能技术的快速发展,如何高效处理复杂任务成了 AI 系统的一大挑战。传统的线性架构在面对多轮对话和动态决策时常常显得无能为力。而 LangGraph 这种多智能体合作框架的出现,为这个问题提供了新的解决方案。 相关文章: 开源模型应用落地-LangGraph101-探索…

react 常用插件

ts项目中如果提示 path不存需要安装 pnpm i types/node --save-dev常用插件 axios ajax请求echarts 图表插件reduxjs/toolkit redux 插件antd ui插件nprogress页面上方或者下方加载loding 常用语路由跳转使用dayjs 日期格式转换react-quill-new 富文本组件 全面兼容react 18r…