"所有深夜里的 Debug,都是为了黎明时那一抹翠绿的 Running。"

第一次搭网站。原本以为只是简单的 kubectl apply,却没曾想在之前没踩过坑的 K3s 和K8s的镜像拉取与数据库版本纠葛中,度过了一个漫长却充实的夜晚。

01. 序言:卡在“Sandbox”的初阵

当我按照之前正常流程操作,结果Pod 状态栏出现那一串冰冷的 ImagePullBackOffContainerCreating时,我估计晚上是睡不着了

由于众所周知的原因,镜像拉取一直是头疼的问题,平时在本机用梯子解决,这次在服务器上打算用了ssh NR命令,不过在腾讯云的服务器上docker拉取一直没走我设定的通路。

最后只好直接通过在服务器端开启linux的 Clash,算是能成功把镜像拉下来了。

02. 攻坚:K3s 的“内院”与“外院”

“最遥远的距离,不是镜像不存在,而是它就在我的磁盘里,K3s 却在它的‘内院’里对我摇头说:‘我没看见’。”

已经通过 sudo ctr -n k8s.io images tag 成功给镜像打了标,而且运行 sudo ctr images list 时,确实亲眼看到了 mysql:8.1halohub/halo:2.10 稳稳地躺在列表里。但是,即便镜像在 ctr 列表里,Kubernetes 依然疯狂报错 ErrImageNeverPull,说明在本地找不到镜像。

为了查明真相,尝试执行了 K3s 内置的查询命令:

sudo k3s crictl images | grep -E "mysql|halo"

结果输出是:完全空白

反复折腾了几次,不断更改镜像名,跟ai进行了数轮对话,大约一个小时,悟了:

外院 (System Containerd):直接输入 ctr 命令时,它默认连接的是系统路径(比如/run/containerd/containerd.sock)。镜像虽然打进去了,但那是给普通 Docker 或系统级容器用的。

内院 (K3s Internal Containerd):K3s 为了保证环境纯净,使用了独立的 Socket 路径(/run/k3s/containerd/containerd.sock)。K3s 的 Kubelet 只会盯着这个“内院”看,对“外院”发生的任何打标、拉取都视而不见。

既然症结已经找到,我采取了经典的战术:

  1. 外院采买:利用 Docker + 代理强行拉取镜像。

  2. 跨院传送:使用 docker save | k3s ctr images import - 管道,将镜像精准投喂进 K3s 的内库。

  3. 身份绑定:通过 ctr images tag 给镜像打上 Pod 认准的“实名标签”。

sudo docker save halohub/halo:2.21.7 | sudo k3s ctr -n k8s.io images import -

此刻,屏幕前的终端跳动着 1/1 Running。窗外或许已经有了黎明的微光。

03. 转折:H2 的告别与 MySQL 的新生

升级到 2.20+ 后,遇到了报错:MVStoreException: The write format 2 is smaller than supported format 3

这是 H2 数据库版本跨越的阵痛,果断选择了拥抱 MySQL

  • 通过 kubectl patch 修正环境变量。

  • imagePullPolicy 强行设为 Never,断掉 Pod 拉取外网信息对账的机制。

  • 使用 Cluster-IP 绕过了不稳定的内部 DNS 解析。

04. 终局:2.21.7 的平滑着陆

最终,当镜像版本锁定在 2.21.7,且所有的环境变量精准指向持久化的 /data/halo 目录时,网页终于不再是 502,而是重新露出了熟悉的登录界面。

# 核心秘籍:持久化卷挂载
volumes:
- hostPath:
    path: /data/halo
    type: ""
  name: halo-data

感谢今晚的自己,没有在 ErrImagePull 面前低头。

未来,这里将记录更多的代码与生活。欢迎来到我的新世界。


Published at: 2025-12-24 Status: 🟢 Running Mode: Zen & Geek