本周,通过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.镜像所在仓库无法访问,或者访问超时
下面来分析是上面哪个原先导致,在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 镜像拉去失败导致
镜像明确后,接下来就是分析为什么这个镜像会拉取失败,
如果对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)
在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
无法启动
问题明确,背景知识也了解后,现在就是怎么解决问题了
# 对于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
第一个方法很方便,但是是类似补丁式的修复,治标不治本。一般适合小规模的镜像问题,或者日常使用,或者是团队没有维护镜像的私服
更为优雅的方式是通过配置私服的方式,通过配置 imagePullSecrets
这个参数,因为我这次不是通过这种方式,就不展开了
具体可以自行Google或者参考:kubernetes troubleshooting
通过前面的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