当pod状态为ErrImagePull
或ImagePullBackOff
时,通常是由于以下3个原因(在排查网络故障的前提下):
imagePullSecrets
)当看到pod出现CrashLoopBackOff
状态时,说明K8S试图启动这个pod,但是pod内有一个或多个的容器启动失败。可以通过过describe
来查看pod的Event信息,通常从这些信息中可以找到Reason
和Exit Code
等提示信息。
对于应用的失败,当然少不了查看应用日志。如果应用日志是输出到stdout
的话(建议这样),就可以使用kubectl logs
命令来查看日志。
小技巧:
对于pod被重启的情况,通常有用的日志信息在之前的容器,这时,可以加上--previous
参数来查看容器前一个实例的日志
ConfigMap和Secret是在应用运行时将配置等信息注入最佳实践方式。但是,如果在应用启动前忘了创建ConfigMap或Secret,将会导致pod启动失败。
当pod要用到一个还没创建的ConfigMap时,状态会显示为RunContainerError
。此时用kubectl describe
可以查看事件信息,会有类似于:configmaps xxxxxxx not found
的事件提示信息。
假设pod将把名为myothersecret
的Secret挂载作为数据卷,当myothersecret
还并没有生成:
# missing-secret.yaml
apiVersion: v1
kind: Pod
metadata:
name: secret-pod
spec:
containers:
- name: test-container
image: gcr.io/google_containers/busybox
command: [ "/bin/sh", "-c", "env" ]
volumeMounts:
- mountPath: /etc/secret/
name: myothersecret
restartPolicy: Never
volumes:
- name: myothersecret
secret:
secretName: myothersecret
执行kubectl create -f missing-secret.yaml
后,会发现pod状态一直为ContainerCreating
。同样,通过kubectl describe
查看事件信息,会有类似于:secrets "myothersecret" not found
的提示。
当创建所需的ConfigMap/Secret之后,容器将能够正常启动。
当使用容器和k8s的时候,我们需要知道的很重要的一点就是:容器能够运行,但并不意味着是正常工作的。
k8s提供了Liveness/Readiness Probe这两个特性(他们会定期的执行一个http请求或建立一个tcp连接),用来确认应用是否正常工作。如果Liveness Probe失败,k8s会杀掉容器并创建一个新的(此时,事件信息里会发现类似提示:container "xxxxxxxx" is unhealthy, it will be killed and re-created
)。如果Readiness Probe失败,这个Pod将不会作为Service的可用后端,也就是不会有流量发送到这个Pod。
如下,该Pod定义了一个Liveness和Readiness Probe,他们以http方式定期的检查8080端口的/healthz地址:
apiVersion: v1
kind: Pod
metadata:
name: liveness-pod
spec:
containers:
- name: test-container
image: rosskukulinski/leaking-app
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 3
periodSeconds: 3
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 3
periodSeconds: 3
出现健康检查失败的三种可能情况:
initialDelaySeconds
;通常,遇到问题时,先从查看Pod日志开始排查。
K8S的集群管理员是可以对容器和Pod设置CPU或内存的使用限制的,当在创建一个Deployment时,设置的请求资源大于了限定值,Deployment将无法部署成功。
例:如下Deployment中,resources.requests.memory
设置为5Gi
# gateway.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: gateway
spec:
template:
metadata:
labels:
app: gateway
spec:
containers:
- name: test-container
image: nginx
resources:
requests:
memory: 5Gi
执行kubectl create -f gateway.yaml
后,并没有pod创建成功。通过kubectl describe
查看此deployment:
$ kubectl describe deployment/gateway
Name: gateway
Namespace: fail
CreationTimestamp: Sat, 11 Feb 2017 15:03:34 -0500
Labels: app=gateway
Selector: app=gateway
Replicas: 0 updated | 1 total | 0 available | 1 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 0 max unavailable, 1 max surge
OldReplicaSets:
NewReplicaSet: gateway-764140025 (0/1 replicas created)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
4m 4m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set gateway-764140025 to 1
可以看到,这个deployment创建了一个名为gateway-764140025
的ReplicaSet,但是available还是0。再进一步kubectl describe
查看这个ReplicaSet:
$ kubectl describe rs/gateway-764140025
Name: gateway-764140025
Namespace: fail
Image(s): nginx
Selector: app=gateway,pod-template-hash=764140025
Labels: app=gateway
pod-template-hash=764140025
Replicas: 0 current / 1 desired
Pods Status: 0 Running / 0 Waiting / 0 Succeeded / 0 Failed
No volumes.
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
6m 28s 15 {replicaset-controller } Warning FailedCreate Error creating: pods "gateway-764140025-" is forbidden: [maximum memory usage per Pod is 100Mi, but request is 5368709120., maximum memory usage per Container is 100Mi, but request is 5Gi.]
这里就可以找到原因了:每个Pod和Container的最大内存可用值为100Mi,但是这里请求了5Gi。
注意:可以通过
kubectl describe limitrange
查看当前的资源限制信息。
与第5点的资源limits类似,K8S允许管理员为每个namespace设置Resource Quotas,比如:可运行的pod个数等。
当要创建的资源超过了限定的配额时,多出的申请将不会成功。
例:这里创建一个名为gateway-quota的Deployment
# test-quota.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: gateway-quota
spec:
template:
spec:
containers:
- name: test-container
image: nginx
成功后查看到如下pod信息:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
gateway-quota-551394438-pix5d 1/1 Running 0 16s
接下来,执行命令kubectl scale deploy/gateway-quota --replicas=3
将其扩到3个pod。此时再查看pod信息:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
gateway-quota-551394438-pix5d 1/1 Running 0 9m
发现pod仍然只有一个。执行kubectl describe deploy/gateway-quota
查看信息:
$ kubectl describe deploy/gateway-quota
Name: gateway-quota
Namespace: fail
CreationTimestamp: Sat, 11 Feb 2017 16:33:16 -0500
Labels: app=gateway
Selector: app=gateway
Replicas: 1 updated | 3 total | 1 available | 2 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
OldReplicaSets:
NewReplicaSet: gateway-quota-551394438 (1/3 replicas created)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
9m 9m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set gateway-quota-551394438 to 1
5m 5m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set gateway-quota-551394438 to 3
可以看到,最后一行确实有执行扩展ReplicaSet到3个,但是unavailable值为2。继续kubectl describe replicaset
查看对应的ReplicaSet信息:
kubectl describe replicaset gateway-quota-551394438
Name: gateway-quota-551394438
Namespace: fail
Image(s): nginx
Selector: app=gateway,pod-template-hash=551394438
Labels: app=gateway
pod-template-hash=551394438
Replicas: 1 current / 3 desired
Pods Status: 1 Running / 0 Waiting / 0 Succeeded / 0 Failed
No volumes.
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
11m 11m 1 {replicaset-controller } Normal SuccessfulCreate Created pod: gateway-quota-551394438-pix5d
11m 30s 33 {replicaset-controller } Warning FailedCreate Error creating: pods "gateway-quota-551394438-" is forbidden: exceeded quota: compute-resources, requested: pods=1, used: pods=1, limited: pods=1
这里可以找到原因了:exceeded quota: compute-resources, requested: pods=1, used: pods=1, limited: pods=1
如果你的集群没有做自动扩容,有一天可能会出现集群的CPU和内存资源耗尽的情况。这并不是指CPU和内存被完全用光,而是Kubernetes调度计量资源已被使用,而无法再进行调度分配。
假设有一个集群可用CPU资源为2,这里部署如下Deployment:
# cpu-scale.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: cpu-scale
spec:
template:
metadata:
labels:
app: cpu-scale
spec:
containers:
- name: test-container
image: nginx
resources:
requests:
cpu: 1
此Deployment会消耗1个CPU的资源,同时,Kubernetes内部服务也会消耗一定的CPU/Memory资源,所以实际剩余可调度的CPU资源是小于1的。
如果此时执行kubectl scale deploy/cpu-scale --replicas=2
以扩到2个pod的话,第二个pod就会处于Pending
的状态:
$ kubectl scale deploy/cpu-scale --replicas=2
deployment "cpu-scale" scaled
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
cpu-scale-908056305-phb4j 0/1 Pending 0 4m
cpu-scale-908056305-xstti 1/1 Running 0 5m
通过describe命令查看pod的日志:
$ kubectl describe pod cpu-scale-908056305-phb4j
Name: cpu-scale-908056305-phb4j
Namespace: fail
Node: gke-ctm-1-sysdig2-35e99c16-qwds/10.128.0.4
Start Time: Sun, 12 Feb 2017 08:57:51 -0500
Labels: app=cpu-scale
pod-template-hash=908056305
Status: Pending
IP:
Controllers: ReplicaSet/cpu-scale-908056305
[...]
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
3m 3m 1 {default-scheduler } Warning FailedScheduling pod (cpu-scale-908056305-phb4j) failed to fit in any node
fit failure on node (gke-ctm-1-sysdig2-35e99c16-wx0s): Insufficient cpu
fit failure on node (gke-ctm-1-sysdig2-35e99c16-tgfm): Insufficient cpu
fit failure on node (gke-ctm-1-sysdig2-35e99c16-qwds): Insufficient cpu
可以看到原因是:调度系统不能找到符合要求的node节点(Insufficient cpu)而调度失败。
对于将希望实现Kubernetes集群自动伸缩功能的话,可以参考一下cluster-autoscaler工具。
另一个常见的错误就是试图创建一个Deployment但指向的PersistentVolumes不存在。不论你使用的是哪一种持久卷,这种问题的结果都很相似。
如下是一个Deployment试图去使用名为my-data-disk的GCE PersistentDisk存储:
# volume-test.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: volume-test
spec:
template:
metadata:
labels:
app: volume-test
spec:
containers:
- name: test-container
image: nginx
volumeMounts:
- mountPath: /test
name: test-volume
volumes:
- name: test-volume
# This GCE PD must already exist (oops!)
gcePersistentDisk:
pdName: my-data-disk
fsType: ext4
当执行创建后,发现容器一直处于ContainerCreating
状态:
kubectl get pods
NAME READY STATUS RESTARTS AGE
volume-test-3922807804-33nux 0/1 ContainerCreating 0 3m
查看事件日志:
$ kubectl describe pod volume-test-3922807804-33nux
Name: volume-test-3922807804-33nux
Namespace: fail
Node: gke-ctm-1-sysdig2-35e99c16-qwds/10.128.0.4
Start Time: Sun, 12 Feb 2017 09:24:50 -0500
Labels: app=volume-test
pod-template-hash=3922807804
Status: Pending
IP:
Controllers: ReplicaSet/volume-test-3922807804
[...]
Volumes:
test-volume:
Type: GCEPersistentDisk (a Persistent Disk resource in Google Compute Engine)
PDName: my-data-disk
FSType: ext4
Partition: 0
ReadOnly: false
[...]
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
4m 4m 1 {default-scheduler } Normal Scheduled Successfully assigned volume-test-3922807804-33nux to gke-ctm-1-sysdig2-35e99c16-qwds
1m 1m 1 {kubelet gke-ctm-1-sysdig2-35e99c16-qwds} Warning FailedMount Unable to mount volumes for pod "volume-test-3922807804-33nux_fail(e2180d94-f12e-11e6-bd01-42010af0012c)": timeout expired waiting for volumes to attach/mount for pod "volume-test-3922807804-33nux"/"fail". list of unattached/unmounted volumes=[test-volume]
1m 1m 1 {kubelet gke-ctm-1-sysdig2-35e99c16-qwds} Warning FailedSync Error syncing pod, skipping: timeout expired waiting for volumes to attach/mount for pod "volume-test-3922807804-33nux"/"fail". list of unattached/unmounted volumes=[test-volume]
3m 50s 3 {controller-manager } Warning FailedMount Failed to attach volume "test-volume" on node "gke-ctm-1-sysdig2-35e99c16-qwds" with: GCE persistent disk not found: diskName="my-data-disk" zone="us-central1-a"
可以看到,pod已经被成功调度到node节点,但是kubelet不能成功挂载期望的数据卷。最下面一行controller-manager的提示信息是最终的原因:GCE persistent disk not found: diskName="my-data-disk" zone="us-central1-a"
。因为还没有创建这个my-data-disk,所以创建之后pod就能够正常起来了。
当编写的资源配置yaml出现写法错误时,也是阻挠我们成功部署的常见错误。例如:
# test-application.deploy.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: test-app
spec:
template:
metadata:
labels:
app: test-app
spec:
containers:
- image: nginx
name: nginx
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 100Mi
配置咋一看上去,感觉没有什么问题。当执行的时候会出现如下的报错:
$ kubectl create -f test-application.deploy.yaml
error: error validating "test-application.deploy.yaml": error validating data: found invalid field resources for v1.PodSpec; if you choose to ignore these errors, turn validation off with --validate=false
结合报错信息,这时可以知道问题在于:resources字段不是v1.PodSpec下的,正确的应该是在v1.Container下面。解决的方法是,将resources字段的配置信息缩进到containsers下面。
除了上述这种字段声明错误外,单词拼写错误也是很常犯的,为了避免这种问题,建议在执行操作前做一些验证检查。比如:
python -c 'import yaml,sys;yaml.safe_load(sys.stdin)' < test-application.deployment.yaml
--dry-run
参数来检查Kubernetes API对象是否正确:kubectl create -f test-application.deploy.yaml --dry-run --validate=true
关于镜像的拉取,有时候会有这种情况:你修改了镜像,但是仍用原来的名字和标签上传到了镜像仓库,而重新创建的pod用的镜像并没有被更新。
出现这种问题的原因是:没有正确的配置镜像拉取策略,即:ImagePullPolicy。改字段有3个可选值:
在没有配置策略的情况下,会采用默认策略:如果镜像的标签是latest
,将按Always执行;如果镜像的标签不是latest
,则采用IfNotPresent。
因此,为了解决个问题,能够想到如下3中方法:
latest
标签(非常不推荐)实际部署过程中,可能会出现很多意料之外的问题,debug是在所难免。熟悉一下常用debug命令,能有助于快速定位问题:
kubectl describe deployment/<deployname>
kubectl describe replicaset/<rsname>
kubectl get pods
kubectl describe pod/<podname>
kubectl logs <podname> --previous