引子

继上次我们验证K3s在虚拟机上的集群部署,我们这次直接在RiscV开发板上实验部署K3s。

值得注意的是,k3s官方支持的架构里面是没有RiscV的,所以有了我们即将开展的适配工作,同时k3s的底层源码大部分都是由Golang构成,而Golang是适配RiscV的,所以我们针对RiscV的K3s移植工作是有可行性的。

本次实验利用了这两篇文章中的配置:玩转RISCV开发板01-烧录OpenEuler国产镜像,玩转RISCV开发板02-配置好容器化环境,关键在于使用了RiscV+Openeuler的架构系统组合,来自于SIG兴趣小组的23.09的镜像,以及其已经貌似适配好的k3s包。(具体请参考上面的两篇文章)

实验环境

实验过程

我们之前使用sudo dnf install -y k3s成功安装了k3及其依赖

[root@openeuler-riscv64 ~]# sudo dnf install -y k3s
mainline                                        3.8 kB/s | 3.0 kB     00:00    
epol                                            5.4 kB/s | 3.0 kB     00:00    
ceph-user                                       4.6 kB/s | 3.0 kB     00:00    
chromium-user                                   4.4 kB/s | 3.0 kB     00:00    
libreoffice                                     4.6 kB/s | 3.0 kB     00:00    
rust167-user                                    5.8 kB/s | 3.0 kB     00:00    
mesa-supplements                                3.7 kB/s | 3.0 kB     00:00    
oetestsuite                                     5.1 kB/s | 3.0 kB     00:00    
update                                          4.6 kB/s | 3.0 kB     00:00    
Dependencies resolved.
================================================================================
 Package                  Arch     Version                      Repo       Size
================================================================================
Installing:
 k3s                      riscv64  1.24.2+rc1+k3s2-3.oe2303     epol       59 M
Installing dependencies:
 container-selinux        noarch   2:2.163-1.oe2303             mainline   39 k
 k3s-selinux              noarch   1.1.stable.1-1.oe2303        epol       20 k
 policycoreutils          riscv64  3.4-1.oe2303                 mainline  156 k
 selinux-policy           noarch   38.6-4.oe2303                mainline   24 k
 selinux-policy-targeted  noarch   38.6-4.oe2303                mainline  6.8 M

Transaction Summary
================================================================================
Install  6 Packages

Total download size: 66 M
Installed size: 87 M
Downloading Packages:
(1/6): container-selinux-2.163-1.oe2303.noarch.  22 kB/s |  39 kB     00:01    
(2/6): selinux-policy-38.6-4.oe2303.noarch.rpm   13 kB/s |  24 kB     00:01    
(3/6): policycoreutils-3.4-1.oe2303.riscv64.rpm  84 kB/s | 156 kB     00:01    
(4/6): selinux-policy-targeted-38.6-4.oe2303.no 4.1 MB/s | 6.8 MB     00:01    
(5/6): k3s-selinux-1.1.stable.1-1.oe2303.noarch  14 kB/s |  20 kB     00:01    
(6/6): k3s-1.24.2+rc1+k3s2-3.oe2303.riscv64.rpm 4.2 MB/s |  59 MB     00:14    
--------------------------------------------------------------------------------
Total                                           4.2 MB/s |  66 MB     00:15     
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Running scriptlet: selinux-policy-targeted-38.6-4.oe2303.noarch           1/1 
  Preparing        :                                                        1/1 
  Installing       : policycoreutils-3.4-1.oe2303.riscv64                   1/6 
  Running scriptlet: policycoreutils-3.4-1.oe2303.riscv64                   1/6 
Created symlink /etc/systemd/system/multi-user.target.wants/restorecond.service → /usr/lib/systemd/system/restorecond.service.

  Installing       : selinux-policy-38.6-4.oe2303.noarch                    2/6 
  Running scriptlet: selinux-policy-38.6-4.oe2303.noarch                    2/6 
  Running scriptlet: selinux-policy-targeted-38.6-4.oe2303.noarch           3/6 
  Installing       : selinux-policy-targeted-38.6-4.oe2303.noarch           3/6 
  Running scriptlet: selinux-policy-targeted-38.6-4.oe2303.noarch           3/6 
  Running scriptlet: container-selinux-2:2.163-1.oe2303.noarch              4/6 
  Installing       : container-selinux-2:2.163-1.oe2303.noarch              4/6 
  Running scriptlet: container-selinux-2:2.163-1.oe2303.noarch              4/6 
  Running scriptlet: k3s-selinux-1.1.stable.1-1.oe2303.noarch               5/6 
  Installing       : k3s-selinux-1.1.stable.1-1.oe2303.noarch               5/6 
  Running scriptlet: k3s-selinux-1.1.stable.1-1.oe2303.noarch               5/6 
  Installing       : k3s-1.24.2+rc1+k3s2-3.oe2303.riscv64                   6/6 
  Running scriptlet: selinux-policy-targeted-38.6-4.oe2303.noarch           6/6 
  Running scriptlet: container-selinux-2:2.163-1.oe2303.noarch              6/6 
  Running scriptlet: k3s-selinux-1.1.stable.1-1.oe2303.noarch               6/6 
  Running scriptlet: k3s-1.24.2+rc1+k3s2-3.oe2303.riscv64                   6/6 
  Verifying        : container-selinux-2:2.163-1.oe2303.noarch              1/6 
  Verifying        : policycoreutils-3.4-1.oe2303.riscv64                   2/6 
  Verifying        : selinux-policy-38.6-4.oe2303.noarch                    3/6 
  Verifying        : selinux-policy-targeted-38.6-4.oe2303.noarch           4/6 
  Verifying        : k3s-1.24.2+rc1+k3s2-3.oe2303.riscv64                   5/6 
  Verifying        : k3s-selinux-1.1.stable.1-1.oe2303.noarch               6/6 

Installed:
  container-selinux-2:2.163-1.oe2303.noarch                                     
  k3s-1.24.2+rc1+k3s2-3.oe2303.riscv64                                          
  k3s-selinux-1.1.stable.1-1.oe2303.noarch                                      
  policycoreutils-3.4-1.oe2303.riscv64                                          
  selinux-policy-38.6-4.oe2303.noarch                                           
  selinux-policy-targeted-38.6-4.oe2303.noarch                                  

Complete!

验证部署Server节点

接下来我们需要验证部署Server节点,由于之前我们安装好的k3s只是一个运行脚本,所以我们需要执行 INSTALL_K3S_SKIP_DOWNLOAD=true k3s-install.sh 并执行kubectl get nodes检查集群中的节点。

[root@openeuler-riscv64 ~]# INSTALL_K3S_SKIP_DOWNLOAD=true k3s-install.sh
[INFO]  Skipping k3s download and verify
[INFO]  Skipping installation of SELinux RPM
[INFO]  Creating /usr/local/bin/kubectl symlink to k3s
[INFO]  Creating /usr/local/bin/crictl symlink to k3s
[INFO]  Creating /usr/local/bin/ctr symlink to k3s
[INFO]  Creating killall script /usr/local/bin/k3s-killall.sh
[INFO]  Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO]  env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO]  systemd: Creating service file /etc/systemd/system/k3s.service
[INFO]  systemd: Enabling k3s unit
Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service.
[INFO]  systemd: Starting k3s
[ 4166.015532] bridge: filtering via arp/ip/ip6tables is no longer available by default. Update your scripts to load br_netfilter if you need this.
[ 4166.085229] Bridge firewalling registered
[ 4436.489721] IPVS: Registered protocols (TCP, UDP)
[ 4436.501236] IPVS: Connection hash table configured (size=4096, memory=32Kbytes)
[ 4436.507135] IPVS: ipvs loaded.
[ 4438.223653] IPVS: [rr] scheduler registered.
[root@openeuler-riscv64 ~]# kubectl get nodes
NAME                STATUS   ROLES                  AGE     VERSION
openeuler-riscv64   Ready    control-plane,master   3m37s   v1.24.2+k3s-

这个过程中跳过了k3s的下载和架构验证,创建了系统 kubectl, crictl,ctr 三个命令的系统链接到 k3s. 创建了 kill all,uninstall 脚本,创建了环境变量,服务文件。

需要提醒的是,此时我们只有一个节点一个开发板,而在 K3s 的源码中默认Server既是Server又是Agent,这样才能在单机的情况下运行。

之后我们验证其关于 pod,容器等具体功能是否支持。

我们可以写一个简单的 yaml 文件,指定使用riscv64/busybox:latest这个镜像,执行kubectl apply -f b1.yaml进行部署 可以发现其创建成功了,但是在执行kubectl get pods时显示的状态一直是ContainerCreated 我们需要使用kubectl describe pod b1来观察这个pod创建的具体过程。

ps:由于本记录没有与实验同步进行(这是我需要吸取的一个教训)所以没有相应的截图可以贴出。 这里拿一个类似的情况举例,发现pause镜像无法拉取导致其他 Pod 无法正常启动.

 Warning  FailedCreatePodSandBox  38s (x129 over 108m)  kubelet  (combined from similar events): Failed to create pod sandbox: rpc error: code = Unknown desc = failed to get sandbox image "carvicsforth/pause:v3.10-v1.31.1": failed to pull image "carvicsforth/pause:v3.10-v1.31.1": failed to pull and unpack image "docker.io/carvicsforth/pause:v3.10-v1.31.1": failed to resolve reference "docker.io/carvicsforth/pause:v3.10-v1.31.1": failed to do request: Head "https://registry-1.docker.io/v2/carvicsforth/pause/manifests/v3.10-v1.31.1": dial tcp 208.101.60.87:443: i/o timeout

可以发现其成功 Scheduled 了,但是拉取镜像 pause:3.6 时遇到了 manifest 问题,其实就是pause镜像并不支持 RiscV架构. 于是我们就来深入研究一下 pause 镜像这个东西。

Pod需要一个中间容器Infa来实现,而这个Infra容器正是pause容器

Pause在K8s的源码位置 /kubernetes/build/pause/linux/pause.c,其作用就是

  • 无限循环调用 pause 系统调用并休眠直到收到信号。
  • 作为 PID 为1的进程,专一于维护命名空间和进程管理。

故我们需要先解决这个 pause 镜像的适配问题,由于其源码就是一段C代码而已,所以我们直接尝试在开发板上进行镜像的适配。

适配Pause镜像

如果没有安装编译工具链,请先安装dnf install -y gcc

将上述 pause.c文件下载或传输到开发板上,这里使用ssh相关命令

scp -r 本机文件路径 目标主机名@IP:目标路径

以下步骤参考整合于 Deepseek 与 ChatGpt 的回答。

1.直接编译现有代码

gcc -static -Os -o pause pause.c

2.检查并测试

ldd ./pause
# 测试版本显示
./pause -v

3.容器化使用

FROM scratch
COPY pause /pause
ENTRYPOINT ["/pause"]

4.测试容器

# 构建镜像
docker build -t my-pause .

# 运行测试
docker run --rm -it --name test-pause my-pause

# 在另一个终端发送停止信号
docker stop test-pause  # 应看到正常退出

5.docker保存镜像为tar文件

docker save my-pause > pause-riscv.tar

6.在k3s节点上加载镜像

# 先查看当前k3s使用的containerd命名空间,我的结果是k8s.io
k3s ctr namespace ls
# 再加载
k3s ctr images import pause-riscv.tar
# or
k3s ctr -n k8s.io images import pause-riscv.tar

7.将此镜像作为本地镜像

# 如果没有这个文件夹,自行创建
cd /var/lib/rancher/k3s/agent/images
mkdir -p /var/lib/rancher/k3s/agent/images
# 放置我们自己的pause镜像
cp ./pause-riscv.tar /var/lib/rancher/k3s/agent/images

8.将此镜像打标签以适应原本拉取时所要的镜像名称

# 先查看当前我们制作的镜像名称
ctr images ls
# 重新打标签 后面的名称是之前k3s拉取时拉取失败的镜像名称 
ctr -n k8s.io images tag <上一条命令的名称> docker.io/rancher/mirrored-pause:3.6

9.再使用 crictl,ctr 命令检查镜像

将这两个命令理解为docker命令即可,用来管理镜像。

ctr --namespace=k8s.io images list | grep pause
crictl images | grep pause

10.重新尝试busybox镜像

kubectl apply-f b1.yaml
# 再用crictl命令,使用debug标签观察其过程
crictl --debug pull riscv64/alpine
# 最后检查pod是否运行成功
kubectl get pods -o wide

最终发现busybox和alpine镜像均可以成功拉取,不会再出现pause镜像不适配的问题。

其他镜像可能会因为网络问题,适配问题无法拉取成功,这是我们后续需要进行的工作。

回到源码

回到源码之中,在cli/cmds/agent.go源码中,关于pause-image命令的代码如下

PauseImageFlag = &cli.StringFlag{
	Name:        "pause-image",
	Usage:       "(agent/runtime) Customized pause image for containerd or docker sandbox",
	Destination: &AgentConfig.PauseImage,
	Value:       "rancher/mirrored-pause:3.6",
}

所以在启动agent节点时可以直接在命令行中指定

sudo k3s agent \
  --pause-image=your-repo/your-pause:3.6-riscv64 \
  --server=https://<K3s-Server-IP>:6443 \
  --token=<Your-Token>

源码中关于pause的构建Dockefile如下:

ARG BASE
FROM ${BASE}
ARG ARCH
ADD bin/pause-linux-${ARCH} /pause
USER 65535:65535
ENTRYPOINT ["/pause"]

可供我们构建时参考。

总结

至此,我们完成在 RiscV+Openeuler 开发板上 k3s 的 Server 节点部署工作,并成功解决了其拉取 pause 镜像出现的架构不适配问题。 我们采用的方法是本地自行构建适合 riscv 的 pause 镜像并让 k3s 使用这个镜像启动.

后续目标再仔细研究 k3s/k8s 源码中有关于 Pod 创建的过程,看看其对 pause的具体应用在哪里. 如何能够将 pause 这部分的缺陷从源码的部分补充完整,可以不再需要直接从本地构建,而是可以直接部署。

更新(5.8)

从大佬的思路中得到启发,我们可以修改源码中的pkg/cli/cmds/const_linux.go文件的 DefaultPauseImage = "rancher/mirrored-pause:3.6" 换为我们自己仓库的地址,例如carvicsforth/pause:v3.10-v1.31.1(这是大佬的地址),这样就可以避免了本地构建.