引子
接着上一篇文章,我们继续解决开发板上的k3s集群遇到的问题,回顾一下之前遇到的问题,使用命令查看当前所有 Pods。
kubectl get pods -A -o wide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
default redis-696579c6c8-v2wns 1/1 Running 4 (4m10s ago) 7d6h 10.42.0.17 openeuler-riscv64 <none> <none>
kube-system helm-install-traefik-pv4hv 0/1 ImagePullBackOff 0 14d 10.42.0.20 openeuler-riscv64 <none> <none>
kube-system local-path-provisioner-7b7dc8d6f5-48jjf 0/1 ImagePullBackOff 0 14d 10.42.0.19 openeuler-riscv64 <none> <none>
kube-system helm-install-traefik-crd-ktfth 0/1 ImagePullBackOff 0 14d 10.42.0.21 openeuler-riscv64 <none> <none>
kube-system metrics-server-668d979685-jthzj 0/1 ImagePullBackOff 0 14d 10.42.0.22 openeuler-riscv64 <none> <none>
kube-system coredns-5f8bb7cf9f-5h5kj 0/1 Running 0 11s 10.42.0.36 openeuler-riscv64 <none> <none>
可以发现在 kube-system 命名空间下的这些 Pod 还是处于 ImagePullBackOff 阶段,只有我们上次解决的 CoreDNS 是 Running, 只是还存在网络问题没有解决,所以并没有 Ready.
所以这次我们来解决其他 local-path-provisioner 的问题。
什么是local-path-provisioner
官方的介绍是:Dynamically provisioning persistent local storage with Kubernetes
众所周知,如果我们起一个 Pod,里面的容器一定是需要挂载外部的存储目录的,否则容器一旦销毁那么相关的数据也不在了,无法做到持久化存储,而挂载外部存储目录本质上要求 PVC——持久化卷声明,举例说明:
volumeMounts:
- mountPath: /data
name: redis-storage
volumes:
- name: redis-storage
persistentVolumeClaim:
claimName: redis-data
这里我们声明了具体的 pvc persistentVolumeClaim:claimName: redis-data,这是一个真正的磁盘路径,见下方 PVC 的声明写法。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redis-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
一旦写了 PVC,local-path-provisioner 就会起作用:它自动为你的 PVC 创建一个目录,
如 /opt/local-path-provisioner/pvc-xxx/,将此目录映射给容器作为持久化的目录。
所以,这个镜像的修复是至关重要的一步,没有它我们就无法进行数据的持久化。
修复问题
与CoreDNS不同的是,这次在官方的 release 界面并没有发现其针对于riscv架构的二进制文件,所以我们得尝试自己构建了。
(后续更新,发现其已经支持了 RiscV 架构,只是没有跑通流水线部署,但是代码中已经 Commit 了,所以可以直接使用了)
拉取到其源代码之后在我的 mac 本机进行构建
1.构建自定义镜像
git clone git@github.com:rancher/local-path-provisioner.git
cd local-path-provisioner
GOOS=linux GOARCH=riscv64 CGO_ENABLED=0 go build -o local-path-provisioner
构建成功,这证明其中并没有不适配riscv的地方(例如coredns中构建时出现过的syscall不支持问题)得到一个local-path-provisioner的二进制文件,我们可以将这个二进制文件封装在一个Dockerfile中构建其自定义的镜像来代替默认的镜像。
dockerfile如下:注意拷贝的路径选择为/usr/bin
FROM alpine:latest
# 使用阿里源(可选)
RUN sed -i 's#https\?://dl-cdn.alpinelinux.org/alpine#http://mirrors.aliyun.com/alpine#g' /etc/apk/repositories
# 拷贝编译好的可执行文件
COPY local-path-provisioner-riscv64 /usr/bin/local-path-provisioner
# 启动命令
ENTRYPOINT ["local-path-provisioner"]
如果不使用/usr/bin的话会遇到问题:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 79s default-scheduler Successfully assigned kube-system/local-path-provisioner-5cdfcc7d9c-hpqnm to openeuler-riscv64
Normal Pulled 35s (x4 over 79s) kubelet Container image "ltx/local-path-provisioner:v0.0.21" already present on machine
Normal Created 35s (x4 over 79s) kubelet Created container local-path-provisioner
Warning Failed 35s (x4 over 79s) kubelet Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "local-path-provisioner": executable file not found in $PATH: unknown
Warning BackOff 9s (x7 over 78s) kubelet Back-off restarting failed container
构建镜像并推送至本地的私有镜像仓库。
docker build --network host --platform=linux/riscv64 -f Dockerfile.local-path-provisioner-riscv64 -t 192.168.173.76:6000/riscv64/local-path-provisioner:1.1 .
docker push 192.168.173.76:6000/riscv64/local-path-provisioner:1.1
2.拉取镜像修改配置文件
在 riscv 开发板进行拉取镜像并打标签至默认 local-storage 中指明的镜像名称。
crictl pull riscv64/local-path-provisioner:1.1
ctr -n k8s.io images tag docker.io/riscv64/local-path-provisioner:1.1 docker.io/rancher/local-path-provisioner:v.0.0.21
可以使用 kubectl -n kube-system edit deployment local-path-provisioner 检查其Deployment文件;
或者与coreDNS相同,进入 /var/lib/rancher/k3s/server/manifests 目录下查看其 local-storage.yaml 文件.
这是local-path-provisioner的部署文件。
如果未能识别到本地镜像,根据这个回答,我们只要修改前缀不是rancher的就可以识别到本地的镜像
接下来与 coreDNS 相同,这是系统级别的镜像,为了防止每次重启都被覆盖,需要在k3s启动命令中添加--disable标签.
因为我们使用systemctl来管理k3s,所以需要修改/etc/systemd/system/k3s.service
在此之前我们要先拷贝一份manifests目录下的yaml文件,修改为我们自定义的custom-local-storage.yaml文件,让之后的k3s识别到这个文件进行容器的构建。
cp local-storage.yaml custom-local-storage.yaml
由于我们之前已经把标签打成了与默认 yaml 文件中相同名称的镜像,所以这个 custom 文件中不需要修改什么东西。
回到刚才,继续修改启动命令。
vi /etc/systemd/system/k3s.service
# 在ExecStart中添加 --disable
ExecStart=/usr/local/bin/k3s \
server \
--disable=local-storage,coredns \
这一方法来源于官方文档,或者这篇文章,加上这个标签会使k3s在启动时选择我们设定的自定义yaml文件并且删除默认的yaml文件与构建的容器。
你还可以选择文章中的方法二,在manifests目录下添加 local-storage.yaml.skip 文件,这样会让k3s跳过某个容器的构建,但我没有测试其是否会去选择我们自定义的配置文件。
接下来进行删除,检测。最好重启一下k3s,systemctl restart k3s
kubectl delete pod -n kube-system -l app=local-path-provisioner
kubectl get pods -n kube-system -l app=local-path-provisioner
kubectl describe pod <PodName> -n kube-system
总结
经历了 coreDNS 和 local-path-provisioner 这两个系统级别的镜像之后,我开始反思整个适配过程,我觉得应该先去看看如何从头适配,即使 OREV 的兴趣小组先进行了一个基础适配,使得我们可以先下载到 k3s。
google 后发现在官方的ISSUE中有这个适配问题,其中提到一个巨大挑战是rancher-mirrored 镜像中缺乏对 riscv 平台的支持。
这也正是我们之前处理的那些镜像,默认是拉取的 rancher 官方维护的镜像副本 rancher-mirrored,我们也对这些镜像做了适配。(后来知道可以修改默认镜像仓库,在启动命令时加上私有的仓库即可)
While we could simply add this to the arch list, this would change the multi-arch digest for any tags that we currently mirror that have this platform available.
下面也提到虽然可以简单地将 riscv 添加到镜像的架构列表中,但会导致当前镜像的多架构 digest 发生变化。
- 镜像的 digest 是镜像的唯一标识,一个哈希值
sha256:xxx - 我们平时用的镜像名称,其实就是 tag,指向的是一个多架构的manifest,这个 manifest 会列出所有对应架构的镜像(见下方图像)
- manifest list 本身是一个 JSON 文件,他也有 digest 标识;如果添加一个 riscv 字段进去,整个 JSON 内容变化导致 digest 变化
- 很多生产系统,CI/CD 都依赖这个稳定可验证的 digest,这就导致牵一发而动全身, 会重新拉取镜像,打破缓存;某些系统会误以为内容被篡改或更新了,造成意外后果;不利于镜像的同步和管理
- 更愿意的做法是:不改变现有版本(及之前版本)的镜像 manifest,在新的版本中添加对新架构的支持.

We should figure out how to add this platform to only new tags, in a way that is minimally disruptive to the list maintenance process, and can be reused when adding new platforms in the future.
这是他们提出的解决方法,只将 RISC-V 平台添加到**新标签(新的 manifest,新的镜像)**中,这样对镜像列表的维护干扰最小。
为了保证镜像的稳定性,K3s 团队不修改已有镜像的多架构配置,而是只给新添加的镜像构建 RISC-V 版本,靠 git blame + 时间区间控制,确保 digest 不变且流程可复用。
但是后续的讨论就围绕着该如何支持新的架构,官方开发者更倾向于对新的镜像加入新的架构而不去改动老的镜像,这是很好理解的策略。
接下来我们将探索其他系统级别镜像,例如 Helm,Traefik
这里是LTX,感谢您阅读这篇博客,人生海海,和自己对话,像只蝴蝶纵横四海。
后续更新
可以发现 rancher 官方提供了关于 local-path-provisioner 在 riscv 上的镜像.

所以大佬在 commit 中只是修改了其版本到29.
故我们只需要将其拉取到本地并推送到私有镜像仓库即可.