kubernetes部署失败的原因
1.错误的容器镜像/⾮法的仓库权限
其中两个最普遍的问题是:(a)指定了错误的容器镜像,(b)使⽤私有镜像却不提供仓库认证信息。这在⾸次使⽤ Kubernetes 或者绑定 CI/CD 环境时尤其棘⼿。
让我们看个例⼦。⾸先我们创建⼀个名为 fail 的 deployment,它指向⼀个不存在的 Docker 镜像:
$ kubectl run fail --image=rosskukulinski/dne:v1.0.0
然后我们查看 Pods,可以看到有⼀个状态为 ErrImagePull 或者 ImagePullBackOff 的 Pod:
$ kubectl get pods
NAME                    READY    STATUS            RESTARTS  AGE
fail-1036623984-hxoas  0/1ImagePullBackOff  0          2m
想查看更多信息,可以 describe 这个失败的 Pod:
$ kubectl describe pod fail-1036623984-hxoas
查看 describe 命令的输出中 Events 这部分,我们可以看到如下内容:
Events:
FirstSeen    LastSeen    Count  From                        SubObjectPath      Type        Reason      Message
---------    --------    -----  ----                        -------------      --------    ------      -------
5m        5m      1  {default-scheduler }                            Normal      Scheduled  Successfully assigned fail-1036623984-hxoas to gke-nrhk-1-default-pool-a101b974-wfp7 5m        2m      5  {kubelet gke-nrhk-1-default-pool-a101b974-wfp7} ainers{fail}  Normal      Pulling    pulling image "rosskukulinski/dne:v1.0.0"
5m        2m      5  {kubelet gke-nrhk-1-default-pool-a101b974-wfp7} ainers{fail}  Warning    Failed      Failed to pull image "rosskukulinski/dne:v1.0.0": Error: image rosskukulinski/dne not found
5m        2m      5  {kubelet gke-nrhk-1-default-pool-a101b974-wfp7}            Warning    FailedSync  Error syncing pod, skipping: failed to "StartContainer"for"fail"with ErrImagePull: "Error: image rosskukulinski/dne not found"
5m    11s 19  {kubelet gke-nrhk-1-default-pool-a101b974-wfp7} ainers{fail}  Normal  BackOff    Back-off pulling image "rosskukulinski/dne:v1.0.0"
5m    11s 19  {kubelet gke-nrhk-1-default-pool-a101b974-wfp7}            Warning FailedSync  Error syncing pod, skipping: failed to "StartContainer"for"fail"with ImagePullBackOff: "Back-off pulling image \"rosskukulinski/dne:v1.0.0\""
显⽰错误的那句话:Failed to pull image "rosskukulinski/dne:v1.0.0": Error: image rosskukulinski/dne not found 告诉我们 Kubernetes⽆法到镜像 rosskukulinski/dne:v1.0.0。
为什么 Kubernetes 拉不下来镜像?除了⽹络连接问题外,还有三个主要元凶:
镜像 tag 不正确
镜像不存在(或者是在另⼀个仓库)
Kubernetes 没有权限去拉那个镜
如果你没有注意到你的镜像 tag 的拼写错误,那么最好就⽤你本地机器测试⼀下。
通常我会在本地开发机上,⽤ docker pull 命令,带上完全相同的镜像 tag,来跑⼀下。⽐如上⾯的情况,我会运⾏命令 docker pull rosskukulinski/dne:v1.0.0。
如果这成功了,那么很可能 Kubernetes 没有权限去拉取这个镜像。参考来解决这个问题。
如果失败了,那么我会继续⽤不显式带 tag 的镜像测试 - docker pull rosskukulinski/dne - 这会尝试拉取 tag 为 latest 的镜像。如果这样成功,表明原来指定的 tag 不存在。这可能是⼈为原因,拼写错误,或者 CI/CD 的配置错误。
如果 docker pull rosskukulinski/dne(不指定 tag)也失败了,那么我们碰到了⼀个更⼤的问题:我们所有的镜像仓库中都没有这个镜像。默认情况下,Kubernetes 使⽤镜像仓库,如果你在使⽤,,或者,你要在镜像地址中指定这个仓库的 URL,⽐如使⽤ Quay,镜像地址就变成。
如果你在使⽤ Dockerhub,那你应该再次确认你发布镜像到 Dockerhub 的系统,确保名字和 tag 匹配你的 deployment 正在使⽤的镜像。
注意:观察 Pod 状态的时候,镜像缺失和仓库权限不正确是没法区分的。其它情况下,Kubernetes 将报告⼀个 ErrImagePull 状态。
2. 应⽤启动之后⼜挂掉
⽆论你是在 Kubernetes 上启动新应⽤,还是迁移应⽤到已存在的平台,应⽤在启动之后就挂掉都是⼀个⽐较常见的现象。
我们创建⼀个 deployment,它的应⽤会在1秒后挂掉:
$ kubectl run crasher --image=rosskukulinski/crashing-app
我们看⼀下 Pods 的状态:
$ kubectl get pods
NAME                      READY    STATUS            RESTARTS  AGE
crasher-2443551393-vuehs  0/1CrashLoopBackOff  2          54s
CrashLoopBackOff 告诉我们,Kubernetes 正在尽⼒启动这个 Pod,但是⼀个或多个容器已经挂了,或者正被删除。
让我们 describe 这个 Pod 去获取更多信息
$ kubectl describe pod crasher-2443551393-vuehs
Name:        crasher-2443551393-vuehs
Namespace:    fail
Node:        gke-nrhk-1-default-pool-a101b974-wfp7/10.142.0.2
Start Time:    Fri, 10 Feb 2017 14:20:29 -0500
Labels:        pod-template-hash=2443551393
run=crasher
Status:        Running
IP:        10.0.0.74
Controllers:    ReplicaSet/crasher-2443551393
Containers:
crasher:
Container ID:    docker://51c940ab32016e6d6b5ed28075357661fef3282cb3569117b0f815a199d01c60
Image:        rosskukulinski/crashing-app
Image ID:        docker://sha256:cf7452191b34d7797a07403d47a1ccf5254741d4bb356577b8a5de40864653a5
Port:
State:        Terminated
Reason:        Error
Exit Code:    1
Started:        Fri, 10 Feb 2017 14:22:24 -0500
Finished:        Fri, 10 Feb 2017 14:22:26 -0500
Last State:        Terminated
Reason:        Error
Exit Code:    1
Started:        Fri, 10 Feb 2017 14:21:39 -0500
Finished:        Fri, 10 Feb 2017 14:21:40 -0500
Ready:        False
Restart Count:    4
...
好可怕,Kubernetes 告诉我们这个 Pod 正被 Terminated,因为容器⾥的应⽤挂了。我们还可以看到应⽤的 Exit Code 是 1。后⾯我们可能还会看到⼀个 OOMKilled 错误。
我们的应⽤正在挂掉?为什么?
⾸先我们查看应⽤⽇志。假定你发送应⽤⽇志到 stdout(事实上你也应该这么做),你可以使⽤ kubectl logs 看到应⽤⽇志:
$ kubectl logs crasher-2443551393-vuehs
不幸的是,这个 Pod 没有任何⽇志。这可能是因为我们正在查看⼀个新起的应⽤实例,因此我们应该查看前⼀个容器:
$ kubectl logs crasher-2443551393-vuehs --previous
什么!我们的应⽤仍然不给我们任何东西。这个时候我们应该给应⽤加点启动⽇志了,以帮助我们定位这个问题。我们也可以本地运⾏⼀下这个容器,以确定是否缺失环境变量或者挂载卷。
3. 缺失 ConfigMap 或者 Secret
Kubernetes 最佳实践建议通过或者传递应⽤的运⾏时配置。这些数据可以包含数据库认证信息,API endpoints,或者其它配置信息。
⼀个常见的错误是,创建的 deployment 中引⽤的 ConfigMaps 或者 Secrets 的属性不存在,有时候甚⾄引⽤的 ConfigMaps 或者 Secrets 本⾝就不存在。
缺失 ConfigMap
第⼀个例⼦,我们将尝试创建⼀个 Pod,它加载 ConfigMap 数据作为环境变量:
# configmap-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: configmap-pod
spec:
containers:
- name: test-container
image: gcr.io/google_containers/busybox
command: [ "/bin/sh", "-c", "env"]
env:
- name: SPECIAL_LEVEL_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.how
让我们创建⼀个 Pod:kubectl create -f configmap-pod.yaml。在等待⼏分钟之后,我们可以查看我们的 Pod:
$ kubectl get pods
NAME            READY    STATUS              RESTARTS  AGE
configmap-pod  0/1RunContainerError  0          3s
Pod 状态是 RunContainerError 。我们可以使⽤ kubectl describe 了解更多:
$ kubectl describe pod configmap-pod
[...]
Events:
FirstSeen    LastSeen    Count  From                        SubObjectPath          Type        Reason      Message
---------    --------    -----  ----                        -------------          --------    ------      -------
20s        20s    1  {default-scheduler }                                Normal      Scheduled  Successfully assigned configmap-pod to gke-ctm-1-sysdig2-35e99c16-tgfm
19s        2s      3  {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}  ainers{test-container} Normal      Pulling    pulling image "gcr.io/google_containers/busybox" 18s        2s      3  {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}  ainers{test-container} Normal      Pulled      Successfully pulled
image "gcr.io/google_containers/busybox"
18s        2s      3  {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}                  Warning    FailedSync  Error syncing pod, skipping: failed to "StartContainer"for"test-container"with RunContainerError: "GenerateRunContainerOptions: configmaps \"special-config\" not found"
Events 章节的最后⼀条告诉我们什么地⽅错了。Pod 尝试访问名为 special-config 的 ConfigMap,但是在该 namespace 下不到。⼀旦我们创建这个 ConfigMap,Pod 应该重启并能成功拉取运⾏时数据。
在 Pod 规格说明中访问 Secrets 作为环境变量会产⽣相似的错误,就像我们在这⾥看到的 ConfigMap错误⼀样。
但是假如你通过 Volume 来访问 Secrets 或者 ConfigMap会发⽣什么呢?
缺失 Secrets
下⾯是⼀个pod规格说明,它引⽤了名为 myothersecret 的 Secrets,并尝试把它挂为卷:
# 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。
⼏分钟后,我们 get Pods,可以看到 Pod 仍处于 ContainerCreating 状态:
$ kubectl get pods
NAME            READY    STATUS              RESTARTS  AGE
secret-pod  0/1ContainerCreating  0          4h
这就奇怪了。我们 describe ⼀下,看看到底发⽣了什么:
$ kubectl describe pod secret-pod
Name:        secret-pod
Namespace:    fail
Node:        gke-ctm-1-sysdig2-35e99c16-tgfm/10.128.0.2
Start Time:    Sat, 11 Feb 2017 14:07:13 -0500
Labels:
Status:        Pending
IP:
Controllers:
[...]
Events:
FirstSeen    LastSeen    Count  From                        SubObjectPath  Type        Reason      Message
---------    --------    -----  ----                        -------------  --------    ------      -------
18s        18s    1  {default-scheduler }                        Normal      Scheduled  Successfully assigned secret-pod to gke-ctm-1-sysdig2-35e99c16-tgfm
18s        2s      6  {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}          Warning    FailedMount MountVolume.SetUp failed for volume "kubernetes.io/secret/337281e7-f065-11e6-bd01-42010af0012c-myothersecret"(spec.Name: "myothersecret") pod "337281e7-f065-11e6-bd01-42010af0012c"(UID: "337281e7-f065-11e6-bd01-
42010af0012c") with: secrets "myothersecret"not found
Events 章节再次解释了问题的原因。它告诉我们 Kubelet ⽆法从名为 myothersecret 的 Secret 挂卷。为了解决这个问题,我们可以创
建 myothersecret,它包含必要的安全认证信息。⼀旦 myothersecret 创建完成,容器也将正确启动。
4. 活跃度/就绪状态探测失败
在 Kubernetes 中处理容器问题时,开发者需要学习的重要⼀课是,你的容器应⽤是 running 状态,不代表它在⼯作。
Kubernetes 提供了两个基本特性,称作。本质上来说,活跃度/就绪状态探测将定期地执⾏⼀个操作(例如发送⼀个 HTTP 请求,打开⼀个tcp 连接,或者在你的容器内运⾏⼀个命令),以确认你的应⽤和你预想的⼀样在⼯作。
如果活跃度探测失败,Kubernetes 将杀掉你的容器并重新创建⼀个。如果就绪状态探测失败,这个 Pod 将不会作为⼀个的后端 endpoint,也就是说不会流量导到这个 Pod,直到它变成 Ready。
如果你试图部署变更你的活跃度/就绪状态探测失败的应⽤,滚动部署将⼀直悬挂,因为它将等待你的所有 Pod 都变成 Ready。
这个实际是怎样的情况?以下是⼀个 Pod 规格说明,它定义了活跃度/就绪状态探测⽅法,都是基于8080端⼝对 /healthy 路由进⾏健康检查:
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
让我们创建这个 Pod:kubectl create -f liveness.yaml,过⼏分钟后查看发⽣了什么:
$ kubectl get pods
NAME          READY    STATUS    RESTARTS  AGE
liveness-pod  0/1Running  42m
2分钟以后,我们发现 Pod 仍然没处于 Ready 状态,并且它已被重启了4次。让我们 describe ⼀下查看更多信息:
$ kubectl describe pod liveness-pod
Name:        liveness-pod
Namespace:    fail
Node:        gke-ctm-1-sysdig2-35e99c16-tgfm/10.128.0.2
Start Time:    Sat, 11Feb 201714:32:36-0500
Labels:
Status:        Running
IP:        10.108.88.40
Controllers:
Containers:
test-container:
Container ID:    docker://8fa6f99e6fda6e56221683249bae322ed864d686965dc44acffda6f7cf186c7b
Image:        rosskukulinski/leaking-app
Image ID:        docker://sha256:7bba8c34dad4ea155420f856cd8de37ba9026048bd81f3a25d222fd1d53da8b7
Port:
State:        Running
Started:        Sat, 11Feb 201714:40:34-0500
Last State:        Terminated
Reason:        Error
Exit Code:    137
Started:        Sat, 11Feb 201714:37:10-0500
Finished:        Sat, 11Feb 201714:37:45-0500
[...]
Events:
FirstSeen    LastSeen    Count  From                        SubObjectPath          Type        Reason      Message
tcpip详解卷二pdf---------    --------    -----  ----                        -------------          --------    ------      -------
8m        8m      1{default-scheduler }                                Normal      Scheduled  Successfully assigned liveness-pod to gke-ctm-1-sysdig2-35e99c16-tgfm
8m        8m      1{kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}  ainers{test-container} Normal      Created    Created container with docker id 0fb5f1a56ea0; Security:[seccomp=unconfined]
8m        8m      1{kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}  ainers{test-container} Normal      Started    Started container with docker id 0fb5f1a56ea0
7m        7m      1{kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}  ainers{test-container} Normal      Created    Created container with docker id 3f2392e9ead9; Security:[seccomp=unconfined]
7m        7m      1{kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}  ainers{test-container} Normal      Killing    Killing container with docker id 0fb5f1a56ea0: pod "liveness-pod_fail(d75469d8-f090-11e6-bd01-42010af0012c)"container "test-container"is unhealthy, it will be killed and re-created.
8m    16s 10{kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}  ainers{test-container} Warning Unhealthy  Liveness probe failed: Get
10.108.88.40:8080/healthz: dial tcp 10.108.88.40:8080: getsockopt: connection refused
8m    1s  85{kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}  ainers{test-container} Warning Unhealthy  Readiness probe failed: Get
10.108.88.40:8080/healthz: dial tcp 10.108.88.40:8080: getsockopt: connection refused
Events 章节再次救了我们。我们可以看到活跃度探测和就绪状态探测都失败了。关键的⼀句话是 container "test-container" is unhealthy, it will be killed and re-created。这告诉我们 Kubernetes 正在杀这个容器,因为容器的活跃度探测失败了。
这⾥有三种可能性:
1. 你的探测不正确,健康检查的 URL 是否改变了?
2. 你的探测太敏感了,你的应⽤是否要过⼀会才能启动或者响应?
3. 你的应⽤永远不会对探测做出正确响应,你的数据库是否配置错了
查看 Pod ⽇志是⼀个开始调测的好地⽅。⼀旦你解决了这个问题,新的 deployment 应该就能成功了。
5. 超出CPU/内存的限制
Kubernetes 赋予集管理员的能⼒。作为应⽤开发者,你可能不清楚这个限制,导致 deployment 失败的时候⼀脸困惑。
我们试图部署⼀个未知 CPU/memory 请求限额的 deployment:
# 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
你会看到我们设了 5Gi 的。让我们创建这个 deployment:kubectl create -f gateway.yaml。
现在我们可以看到我们的 Pod:
$ kubectl get pods
No resources found.
为啥,让我们⽤ describe 来观察⼀下我们的 deployment:
$ kubectl describe deployment/gateway
Name:            gateway
Namespace:        fail
CreationTimestamp:    Sat, 11Feb 201715:03:34-0500
Labels:            app=gateway
Selector:        app=gateway
Replicas:        0updated | 1total | 0available | 1unavailable
StrategyType:        RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:    0max unavailable, 1max surge
OldReplicaSets:
NewReplicaSet:        gateway-764140025(0/1replicas created)
Events:
FirstSeen    LastSeen    Count  From                SubObjectPath  Type        Reason          Message
---------    --------    -----  ----                -------------  --------    ------          -------
4m        4m      1{deployment-controller }            Normal      ScalingReplicaSet  Scaled up replica set gateway-764140025to 1

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。