引子
根据前面几篇 k3s 相关的文章,我们得到了一个结论——k3s 在上的适配首先要适配几个系统镜像.
之前在 issues 中看到一位大佬为 k3s-root 提供了 riscv 的支持. 跟随他的足迹,我找到了他已经做出了的适配工作.
适配工作中可以发现,早在2023年他就对这些系统镜像的源码进行了 commit,提供了 riscv 的支持.
在翻阅其 fork 的有关 riscv 分支上的提交时. 可以发现他的修改主要是将系统镜像的 yaml 文件中的镜像名称修改为了其私有镜像仓库,所以我们本节先来构建一个私有镜像仓库. 目的是可以实现开发板拉取镜像从私有仓库拉取适合的跨架构镜像,而不是默认的 rancher 仓库.
大佬用的是 dockerhub 中自己的仓库,由于某些原因我们的网络无法访问 dockerhub, 并且为了方便后续我们要自定义某些镜像,例如之前的 redis , nginx 等镜像.
最终暂时要达到的效果是从我的 MacBook 上构建跨架构镜像,推送至私有仓库中,开发板可以从仓库中拉取对应镜像.
搭建私有镜像仓库
在之前的这篇文章中我们曾经尝试过私有镜像仓库. 本文中采用的是云服务器+域名+SSL 的方式,比之前本地的仓库更加灵活.(不用写死IP,不再用http)
云服务器+域名
云服务器我们采用雨云提供的美国服务器,目的是后续与域名结合时无需备案. 毕竟备案比较复杂,我们只是学习实验用途,少一些麻烦.(如果大家想要租云服务器可以点击上面的链接) 服务器配置随意,我这里选择的是 debian 的宝塔配置.购买后会得到一个公网 IP,后面要用.
关于域名,我之前在阿里云购买了一年的域名,所以继续使用这个域名. 我们需要对域名进行 dns 解析,即将域名与上面得到的 IP 地址结合起来. 这样在别人访问此域名的时候就会解析出我们服务器的 IP 地址,具体配置如下:
主机记录处填写你想起的域名名称,记录值填写服务器的 IP 地址.
至此我们完成了域名的绑定,接下来我们配置 SSL .
SSL
关于 SSL的概念,在这篇文章中有讨论,感兴趣的朋友可以去看看.这里我们采用 acme+Let’s Encrypt 的组合. 根据 acme 官方的教程我们执行以下步骤:
登陆服务器.首先,安装 acme 脚本
curl https://get.acme.sh | sh -s email=my@example.com
source ~/.bashrc
由于我直接在 root 目录下执行的,所以他会将脚本安装在其下.
接着,创建必要目录,由于 acme 后面的命令需要某些特定的目录,所以执行:
sudo mkdir -p /home/wwwroot/jimlt.bfsmlt.top
sudo chown -R www-data:www-data /home/wwwroot/jimlt.bfsmlt.top # 确保Nginx有读写权限
查看 nginx 的配置情况,我的 nginx 的 server 部分原始监听的是 phpmyadmin,所以我们需要添加一个 server 用来使 acme 后续可以访问80端口.
nginx -t # 检查并获取nginx配置,可以得到配置文件路径
sudo vi /path/to/nginx.conf
# 添加如下server块
server {
listen 80;
server_name jimlt.bfsmlt.top ;
root /home/wwwroot/jimlt.bfsmlt.top;
location /.well-known/acme-challenge/ {
allow all;
}
# 可选静态资源优化等配置...
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 30d;
}
location ~ .*\.(js|css)?$ {
expires 12h;
}
location ~ /\.
{
deny all;
}
access_log /www/wwwlogs/access.log;
}
重载 nginx 并检查配置
sudo nginx -t && sudo systemctl reload nginx
nginx -t
看到最后的 success 即代表配置无误.
接下来我们生成证书,并使用 Let’s Encrypt 作为我们的 CA 服务器.
acme.sh --set-default-ca --server letsencrypt
acme.sh --issue -d mydomain.com -d www.mydomain.com --webroot /home/wwwroot/mydomain.com/
最终会得到证书,结果类似下方:
Your cert is in: /root/.acme.sh/jimlt.bfsmlt.top_ecc/jimlt.bfsmlt.top.cer
[Fri May 9 09:53:29 AM CST 2025] Your cert key is in: /root/.acme.sh/jimlt.bfsmlt.top_ecc/jimlt.bfsmlt.top.key
[Fri May 9 09:53:29 AM CST 2025] The intermediate CA cert is in: /root/.acme.sh/jimlt.bfsmlt.top_ecc/ca.cer
[Fri May 9 09:53:29 AM CST 2025] And the full-chain cert is in: /root/.acme.sh/jimlt.bfsmlt.top_ecc/fullchain.cer
接下来我们复制证书给 registry 容器使用.
sudo mkdir -p /etc/docker/registry/certs
acme.sh --install-cert -d jimlt.bfsmlt.top \
--key-file /etc/docker/registry/certs/jimlt.bfsmlt.top.key \
--fullchain-file /etc/docker/registry/certs/jimlt.bfsmlt.top.crt \
--reloadcmd "docker restart registry"
sudo chmod 600 /etc/docker/registry/certs/*
# 验证证书的安装结果
ls -l /etc/docker/registry/certs/
# 应看到:
# - jimlt.bfsmlt.top.key
# - jimlt.bfsmlt.top.crt
部署 registry 容器
使用 htpasswd 模式,这里我们直接让 registry 容器自己监听了443,是一种简单直接的方式. 但是并不推荐,后续考虑改为 nginx 统一处理443端口,让不同的服务监听其他端口.
docker run -d \
--name registry \
-p 443:443 \
-v /etc/docker/registry/certs:/certs \
-v /etc/docker/registry/auth:/auth \
-v /var/lib/registry:/var/lib/registry \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/jimlt.bfsmlt.top.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/jimlt.bfsmlt.top.key \
-e REGISTRY_AUTH=htpasswd \
-e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
--restart=always \
registry:2
验证容器状态docker ps -f name=registry
接着我们需要知道私有仓库的账号和密码,通过 htpasswd 进行设置.
sudo mkdir -p /etc/docker/registry/auth
docker run --rm \
--entrypoint htpasswd \
httpd:2 -Bbn <username> <password> | sudo tee /etc/docker/registry/auth/htpasswd
docker restart registry
至此我们就完成了带有 SSL 的私有镜像仓库搭建,接着我们可以在其他机器上进行登陆验证 docker login https://jimlt.bfsmlt.top
输入设置的账号与密码即可.
如果遇到认证问题,在需要登陆的机器上从服务器上复制证书 但是 Docker 默认信任 Let’s Encrypt 的有效证书,所以一般无需复制.
sudo mkdir -p /etc/docker/certs.d/jimlt.bfsmlt.top
sudo scp root@你的服务器IP:/etc/docker/registry/certs/jimlt.bfsmlt.top.crt \
/etc/docker/certs.d/jimlt.bfsmlt.top/ca.crt
总结流程
- 安装 acme.sh 脚本
- 修改 nginx 关于80端口配置,使 acme 可以访问
- 使用 acme 的 webroot 模式生成证书
- 复制证书给 registry 容器使用
测试搭建效果
还记得我们最开始的目标吗? Macos-服务器-开发板 这样一个工作流. 所以我们在 macos 上构建一个之前的 pause 镜像并推送和拉取.
这里我们仿照大佬的构建思路, Dockerfile.pause 如下:
FROM ubuntu:latest AS builder
RUN apt-get update && \
apt-get install -y wget gcc && \
wget https://raw.githubusercontent.com/kubernetes/kubernetes/v1.31.1/build/pause/linux/pause.c && \
gcc -Os -Wall -Werror -static -DVERSION=v3.10-v1.31.1 -o pause pause.c
FROM scratch
COPY --from=builder /pause /pause
USER 65535:65535
ENTRYPOINT ["/pause"]
在 mac 上进行构建并推送到私有仓库中(记得先登陆)
docker build --network host --platform linux/riscv64 \
-f Dockerfile.pause \
-t jimlt.bfsmlt.top/pause:v3.10-v1.31.1 \
--push .
这里标签必须写作私有仓库的域名,否则会推送到 dockerhub 中.
在开发板上拉取该镜像 docker pull jimlt.bfsmlt.top/pause:v3.10-v1.31.1
检查是否存在
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
jimlt.bfsmlt.top/pause v3.10-v1.31.1 d98ad82e7eed 14 minutes ago 593kB
至此,整条流程打通,暂时告一段落.
接下来的想法就是仿照大佬的 commit 中的修改,对 k3s 的源码进行修改进行镜像的自定义.
我觉得这个过程一定会遇到很多问题,例如我们不仅要修改镜像名称还要修改 docker.io 这部分;例如可能遇到的网络等问题.
更新:添加 WebUI 查看仓库内的镜像
推荐使用 WebUI 来查看仓库内的镜像,并作为一个简要的可视化界面;同时采用 nginx 的反向代理功能来管理不同的 https 请求. 最终达到的目的是客户端发来的 https 请求由服务器上的 nginx 处理,处理后转发到 registry 和 WebUI 容器内. 容器与 nginx 的交互通过 http.
WebUI
需要注意的是,由于 Let’s Encrypt 给出的证书是一个非泛域名的证书,所以我们需要为每个子域名都申请一个证书.
这里我们同样申请了一个新的子域名来作为可视化界面的地址:registryui.bfsmlt.top
与上面步骤相同,我们修改 nginx 关于80端口配置
server {
listen 80;
server_name registryui.bfsmlt.top;
root /home/wwwroot/registryui.bfsmlt.top;
location /.well-known/acme-challenge/ {
allow all;
}
} # 记得检查并重载 nginx 配置
新建对应目录 sudo mkdir -p /home/wwwroot/registryui.bfsmlt.top
生成证书acme.sh --issue -d registryui.bfsmlt.top --webroot /home/wwwroot/registryui.bfsmlt.top/
因为要让 nginx 反向代理,所以将证书复制给 nginx 使用 sudo mkdir -p /etc/nginx/certs
acme.sh --install-cert -d registryui.bfsmlt.top \
--key-file /etc/nginx/certs/registryui.bfsmlt.top.key \
--fullchain-file /etc/nginx/certs/registryui.bfsmlt.top.crt \
--reloadcmd "systemctl reload nginx"
再次修改 nginx 配置,使 nginx 为我们代理传来的 https 请求
server {
listen 443 ssl;
server_name registryui.bfsmlt.top;
ssl_certificate /etc/nginx/certs/registryui.bfsmlt.top.crt;
ssl_certificate_key /etc/nginx/certs/registryui.bfsmlt.top.key;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
部署 WebUI 容器,指明要访问的私有仓库地址
docker run -d \
--name registry-ui \
-p 8080:80 \
-e REGISTRY_TITLE="My Private Registry" \
-e REGISTRY_URL=https://jimlt.bfsmlt.top \
-e DELETE_IMAGES=true \
--restart=always \
joxit/docker-registry-ui:static
registry
同样的,我们也要修改之前的 registry 容器;删除并以下面的方式启动(不再显示带有 https,交给 nginx)
docker run -d \
--name registry \
-p 5000:5000 \
-v /etc/docker/registry/auth:/auth \
-v /var/lib/registry:/var/lib/registry \
-e REGISTRY_HTTP_ADDR=0.0.0.0:5000 \
-e REGISTRY_AUTH=htpasswd \
-e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-e REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin='["https://registryui.bfsmlt.top"]' \
-e REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods='["GET", "DELETE", "PUT", "POST", "OPTIONS"]' \
-e REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers='["Authorization", "Accept", "Cache-Control", "Content-Type"]' \
-e REGISTRY_HTTP_HEADERS_Access-Control-Allow-Credentials='["true"]' \
--restart=always \
registry:2
这是加了允许 CORS 跨域请求的,有时间我们出一期跨域请求的文章.
(如果你没有配置证书,请仿照之前的步骤来)然后同理修改 nginx 配置文件,以达到我们让 nginx 来管理 https 命令的目的.
server {
listen 443 ssl;
server_name jimlt.bfsmlt.top;
ssl_certificate /etc/docker/registry/certs/jimlt.bfsmlt.top.crt;
ssl_certificate_key /etc/docker/registry/certs/jimlt.bfsmlt.top.key;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Authorization $http_authorization;
proxy_pass_request_headers on;
}
}
最后重启 nginx,再次进行测试
- 进行 docker pull/push 检查私有镜像仓库是否可以访问
- 在浏览器中访问 WebUI 的地址看是否可以访问
最终效果如下,我们可以直观地看到自己的私有镜像仓库有哪些镜像:

总结
本片文章中已经顺利完成了带有SSL的私有镜像仓库搭建,为我接下来的实验提供了有力的镜像保证.
在这个过程中我们遇到了很多的概念,例如 SSL/TLS, CA, Nginx配置等,我们已经在其他文章中进行了总结.