一:问题描述
本周,通过kubernetes搭建企业级jupyterhub服务的时候,遇到个问题:
通过helm部署jupyterhub的时候,hook-image-puller-b8p5p这个pod一直无法启动,反复于 ErrImagePull 和 ImagePullBackOff两个状态,如下
[zhanhaitao@master ~]$ kubectl -n jhub get pods
NAME READY STATUS RESTARTS AGE
hook-image-awaiter-qpsw6 1/1 Running 0 68s
hook-image-puller-b8p5p 0/1 ErrImagePull 0 68s
二:系统环境
centos:7.1版本
k8s 版本:1.13.2
helm 版本:v2.13.1
k8s集群是参考官网的用户手册通过kubeadm完成搭建的,一个master,一个node
jupyterhub的配置文件:
[zhanhaitao@master ~]$ cat config.yaml
proxy:
secretToken: ebcf616d2ae617e67bf8d5c93c4b131abb25adf5849d68a5be51bc922ef072c7
https:
enabled: false
部署命令:
[zhanhaitao@master ~]$ helm upgrade --install jhub jupyterhub/jupyterhub \
> --namespace jhub \
> --version=0.8.2 \
> --values config.yaml
三:问题分析
status=ErrImagePull 和 ImageBackOff,通过字面分析,应该是k8s启动pod时候,拉取镜像失败导致。
ImageBackOff是k8s中一个镜像相关的常见问题,一般导致的原因主要有如下几个:
1.镜像是本公司的私服镜像
2.镜像写法不对或者版本不对
3.镜像所在仓库无法访问,或者访问超时
3.1 确定具体的问题镜像
下面来分析是上面哪个原先导致,在k8s中,大部分的问题,都能通过logs和describe两个命令进行分析和定位
[zhanhaitao@master ~]$ kubectl -n jhub logs hook-image-puller-b8p5p
Error from server (BadRequest): container "pause" in pod "hook-image-puller-b8p5p" is waiting to start: trying and failing to pull image
# 通过logs验证了猜测,问题是pull image导致
# 然后通过describe查明问题所在
# 考虑篇幅,仅仅贴出了关键的信息
[zhanhaitao@master ~]$ kubectl -n jhub describe pod hook-image-puller-b8p5p
Containers:
pause:
Container ID:
Image: gcr.io/google_containers/pause:3.0
Image ID:
Port: <none>
Host Port: <none>
State: Waiting
Reason: ImagePullBackOff
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Failed 7m24s (x2 over 7m56s) kubelet, tx-phxmlp-demo-k8s-staging01.mt Failed to
pull image "gcr.io/google_containers/pause:3.0": rpc error: code = Unknown desc = Error response from daemon:
Get https://gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
# 通过上面的信息,明确了是 gcr.io/google_containers/pause:3.0 镜像拉去失败导致
3.2 确定镜像失败原因
镜像明确后,接下来就是分析为什么这个镜像会拉取失败,
如果对docker有一定的了解的话,看到gcr.io这个镜像前缀,就知道原因了,中国大概率是无法访问gcr.io相关镜像网站的
但是,这里我认为可能并不是每个人都知道这个先验知识,可能部分同学就是简单的操作官网的手册,参照着部署的,不一定具备相关的理论知识
下面,我们分析,镜像失败的原因,首先,看前缀,可以确定不是私服导致的,其实pause镜像是k8s的底层基础镜像,第三节会简单介绍下
其次,我们的部署配置config.yaml并没有这个镜像,所以这个镜像也不是我们拼写或者版本写错导致,所以,只能是网络问题
# 通过本地pull下,发现确实是网络问题导致
[zhanhaitao@master ~]$ sudo docker pull gcr.io/google_containers/pause:3.0
Error response from daemon: Get https://gcr.io/v2/: net/http: request canceled while waiting for connection
(Client.Timeout exceeded while awaiting headers)
四:问题解决
4.1 pause 镜像简介
在k8s中,服务是运行在一个个的Pod中,一个Pod包含一个或多个container,container中则根据指定的docker images运行着具体的服务(runtime container可以不是docker,可以是其他容器技术,这里通过docker举例),pod是k8s的最小调度单位,如下图示意

k8s通过kubelet这个组件管理着Pod的生命周期,pause正是每个Pod启动时,最开始加载的镜像,用于管理这个Pod的网络和命名空间,官方介绍如下:
The image whose network/ipc namespaces containers in each pod will use. This docker-specific flag only works when container-runtime is set to docker. (default “k8s.gcr.io/pause:3.1”)
正是这个镜像的拉取失败,导致整个Pod无法启动
4.2 解决办法
问题明确,背景知识也了解后,现在就是怎么解决问题了
1. 通用的解决办法
# 对于docker由于网络问题无法拉取的问题,较为通用的解决方法主要是:
# 本地取其他镜像源下载,然后替换名字,如下所示
# 这样拉取镜像的时候,发现本地有需要的镜像,就直接使用本地镜像了,避免去网上再次拉取
sudo docker pull docker.io/mirrorgooglecontainers/pause:3.0
sudo docker tag mirrorgooglecontainers/pause:3.0 gcr.io/google_containers/pause:3.0
# 看下image是否改成需要的名字,已经成功本地创建了一个同名的镜像了
[zhanhaitao@master ~]$ sudo docker images|grep pause
mirrorgooglecontainers/pause 3.0 99e59f495ffa 3 years ago 747kB
gcr.io/google_containers/pause 3.0 99e59f495ffa 3 years ago 747kB
2. 更为优雅的办法
第一个方法很方便,但是是类似补丁式的修复,治标不治本。一般适合小规模的镜像问题,或者日常使用,或者是团队没有维护镜像的私服
更为优雅的方式是通过配置私服的方式,通过配置 imagePullSecrets这个参数,因为我这次不是通过这种方式,就不展开了
具体可以自行Google或者参考:kubernetes troubleshooting
3. 针对pause这个特殊镜像的解决办法
通过前面的pause镜像介绍知道,它是Pod的基础架构镜像,所以还可以通过其他特定的方式进行解决,
我们知道,k8s是通过kubelet进行Pod管理的,kubelet启动参数有一个参数 --pod-infra-container-image 就是用来知道pause镜像名的,将其换成能够获取到的镜像名即可
# 1. 我们通过 ps -ef 先看下 kubelet的启动命令,发现其默认使用的是 k8s.gcr.io/pause:3.1
[zhanhaitao@master ~]$ ps -ef | grep pause
root 8924 1 0 Oct12 ? 00:12:27 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf
--kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --cgroup-driver=systemd
--network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.1
# 2. kubelet是配置成了系统服务,我们看下它的状态信息,发现其配置文件是/etc/systemd/system/kubelet.service.d下的10-kubeadm.conf
[zhanhaitao@master ~]$ systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: disabled)
Drop-In: /etc/systemd/system/kubelet.service.d
└─10-kubeadm.conf
Active: active (running) since Sat 2019-10-12 11:48:52 CST; 1 day 2h ago
Docs: https://kubernetes.io/docs/
Main PID: 8924 (kubelet)
Memory: 82.3M
CGroup: /system.slice/kubelet.service
└─8924 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/k...
# 3. 查看启动配置文件,已经查看里面配置的环境目录,可以找到pause的版本指定在:/var/lib/kubelet/kubeadm-flags.env 中
[zhanhaitao@master kubelet.service.d]$ cat 10-kubeadm.conf
# Note: This dropin only works with kubeadm and kubelet v1.11+
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
# This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
# This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use
# the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file.
EnvironmentFile=-/etc/sysconfig/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS
# 4. 将pause镜像改为指定的版本
[zhanhaitao@master kubelet.service.d]$ sudo cat /var/lib/kubelet/kubeadm-flags.env
KUBELET_KUBEADM_ARGS=--cgroup-driver=systemd --network-plugin=cni --pod-infra-container-image=docker.io/mirrorgooglecontainers/pause:3.1
# 5. kubelet服务重启
[zhanhaitao@master kubelet.service.d]$ sudo systemctl restart kubelet.service
本文总结了在部署Kubernetes服务时遇到Pod无法启动的问题,主要原因是pause镜像拉取失败。分析了可能的故障点,包括镜像问题、网络访问限制等,并提供了通用的解决办法,如手动替换镜像,以及更优雅的配置私有仓库方案。同时介绍了pause镜像在Pod中的作用和重要性。
5739





