问题发生

一个 http 应用, 监听在 8080 端口, 编译命令:

go build -o app

Dockerfile

FROM alpine:3.16

WORKDIR /app

COPY app .

CMD ["./app"]

打包镜像的命令:

docker build -t daydaylw3/k8s-node:v1 .

问题1: 找不到该镜像

然后使用 minikube 部署该镜像, 部署名称为 k8s-bootcamp:

kubectl create deployment k8s-bootcamp --image=daydaylw3/k8s-node:v1

发现一直没有 READY, 查看发现状态是

NAME                              READY   STATUS             RESTARTS   AGE
k8s-bootcamp-68566978d4-x2t2m     0/1     ImagePullBackOff   0          20s

然后 describe pod 的事件如下:

~ kubectl describe pod k8s-bootcamp-68566978d4-6x4xb
... 省略其他内容 ...
Events:
  Type     Reason     Age                    From               Message
  ----     ------     ----                   ----               -------
  Normal   Scheduled  4m54s                  default-scheduler  Successfully assigned default/k8s-bootcamp-68566978d4-6x4xb to minikube
  Normal   Pulling    3m8s (x4 over 4m54s)   kubelet            Pulling image "daydaylw3/k8s-node:v1"
  Warning  Failed     3m2s (x4 over 4m48s)   kubelet            Failed to pull image "daydaylw3/k8s-node:v1": Error response from daemon: Head "https://registry-1.docker.io/v2/daydaylw3/k8s-node/manifests/v1": Get "https://auth.docker.io/token?scope=repository%3Adaydaylw3%2Fk8s-node%3Apull&service=registry.docker.io": EOF
  Warning  Failed     3m2s (x4 over 4m48s)   kubelet            Error: ErrImagePull
  Warning  Failed     2m47s (x6 over 4m47s)  kubelet            Error: ImagePullBackOff
  Normal   BackOff    2m34s (x7 over 4m47s)  kubelet            Back-off pulling image "daydaylw3/k8s-node:v1"

一开始以为默认行为是一定要从远程拉取, 因此打算编辑部署 yaml 文件

kubectl edit deployment k8s-bootcamp

但是发现配置如下:

# ... 省略其他配置
  spec:
    containers:
    - image: daydaylw3/k8s-node:v1
      imagePullPolicy: IfNotPresent
      name: k8s-node
      resources: {}
# ... 省略其他配置

IfNotPresent: 说明是会优先寻找本地仓库的

解决

Minikube 默认使用的 runtime 可能跟 Docker 本地环境不是同一个,所以没法识别本地的镜像。

在 Minikube 内部导入你的本地镜像:

minikube image load daydaylw3/k8s-node:v1

或者在构建镜像时直接使用 minikube 的 docker 来构建

eval $(minikube docker-env)
docker build -t daydaylw3/k8s-node:v1 .

问题2: 镜像里的可执行文件不是当前系统架构支持的格式

导入了镜像之后重新部署, 这时候 pod 直接 crashed

(base) ➜  ~ kubectl get pods
NAME                              READY   STATUS             RESTARTS      AGE
k8s-bootcamp-68566978d4-cnbl4     0/1     CrashLoopBackOff   2 (27s ago)   42s

查看事件:

Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  61s                default-scheduler  Successfully assigned default/k8s-bootcamp-68566978d4-cnbl4 to minikube
  Normal   Pulled     19s (x4 over 60s)  kubelet            Container image "daydaylw3/k8s-node:v1" already present on machine
  Normal   Created    19s (x4 over 60s)  kubelet            Created container k8s-node
  Normal   Started    19s (x4 over 60s)  kubelet            Started container k8s-node
  Warning  BackOff    4s (x5 over 59s)   kubelet            Back-off restarting failed container k8s-node in pod k8s-bootcamp-68566978d4-cnbl4_default(eb0bdc82-c1a4-40e8-aa2d-172274b39d70)

以及日志:

~ kubectl logs -p k8s-bootcamp-68566978d4-cnbl4
exec ./app: exec format error

这个错误通常就代表镜像里的可执行文件 ./app 不是当前系统架构支持的格式, 于是登录 minikube 控制台确认 minikube 的架构

~ minikube ssh
docker@minikube:~$ uname -m
aarch64

确认了 minikube 的架构为 arm64, 然后同样确认下 minikube 中的镜像架构:

docker inspect daydaylw3/k8s-node:v1
...
"Architecture": "arm64"
...

说明镜像也是 arm64 的

在 minikube 控制台上直接运行该镜像也是报相同的错

退出 minikube 控制台, 直接在物理机上(mac)运行该镜像又一切正常

解决

编译 app 可执行文件时使用交叉编译的选项:

GOOS=linux GOARCH=arm64 go build -o app
docker build -t daydaylw3/k8s-node:v1 .

然后在 minikube 控制台中删除原镜像, 重新导入, 再次部署, pod 就成功启动!