Kubernetes部署失败的10个最常见原因

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/1       ImagePullBackOff   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} spec.containers{fail}   Normal      Pulling     pulling image "rosskukulinski/dne:v1.0.0"
5m        2m      5   {kubelet gke-nrhk-1-default-pool-a101b974-wfp7} spec.containers{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} spec.containers{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 没有权限去拉取这个镜像。参考镜像拉取 Secrets 来解决这个问题。
如果失败了,那么我会继续用不显式带 tag 的镜像测试 - docker pull rosskukulinski/dne - 这会尝试拉取 tag 为 latest 的镜像。如果这样成功,表明原来指定的 tag 不存在。这可能是人为原因,拼写错误,或者 CI/CD 的配置错误。

如果 docker pull rosskukulinski/dne(不指定 tag)也失败了,那么我们碰到了一个更大的问题:我们所有的镜像仓库中都没有这个镜像。默认情况下,Kubernetes 使用 Dockerhub 镜像仓库,如果你在使用 Quay.io,AWS ECR,或者 Google Container Registry,你要在镜像地址中指定这个仓库的 URL,比如使用 Quay,镜像地址就变成 quay.io/rosskukulinski/dne:v1.0.0。

如果你在使用 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/1       CrashLoopBackOff   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 最佳实践建议通过 ConfigMaps 或者 Secrets 传递应用的运行时配置。这些数据可以包含数据库认证信息,API endpoints,或者其它配置信息。

一个常见的错误是,创建的 deployment 中引用的 ConfigMaps 或者 Secrets 的属性不存在,有时候甚至引用的 ConfigMaps 或者 Secrets 本身就不存在。

缺失 ConfigMap

第一个例子,我们将尝试创建一个 Pod,它加载 ConfigMap 数据作为环境变量:

# cat 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/1       RunContainerError   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}   spec.containers{test-container} Normal      Pulling     pulling image "gcr.io/google_containers/busybox"
18s        2s      3   {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}   spec.containers{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/1       ContainerCreating   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/1       Running   4          2m

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, 11 Feb 2017 14: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, 11 Feb 2017 14:40:34 -0500
Last State:        Terminated
  Reason:        Error
  Exit Code:    137
  Started:        Sat, 11 Feb 2017 14:37:10 -0500
  Finished:        Sat, 11 Feb 2017 14:37:45 -0500
[...]
Events:
FirstSeen    LastSeen    Count   From                        SubObjectPath           Type        Reason      Message
---------    --------    -----   ----                        -------------           --------    ------      -------
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}   spec.containers{test-container} Normal      Created     Created container with docker id 0fb5f1a56ea0; Security:[seccomp=unconfined]
8m        8m      1   {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}   spec.containers{test-container} Normal      Started     Started container with docker id 0fb5f1a56ea0
7m        7m      1   {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}   spec.containers{test-container} Normal      Created     Created container with docker id 3f2392e9ead9; Security:[seccomp=unconfined]
7m        7m      1   {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}   spec.containers{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}   spec.containers{test-container} Warning Unhealthy   Liveness probe failed: Get http://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}   spec.containers{test-container} Warning Unhealthy   Readiness probe failed: Get http://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 正在杀这个容器,因为容器的活跃度探测失败了。

这里有三种可能性:
你的探测不正确,健康检查的 URL 是否改变了?
你的探测太敏感了, 你的应用是否要过一会才能启动或者响应?
你的应用永远不会对探测做出正确响应,你的数据库是否配置错了

查看 Pod 日志是一个开始调测的好地方。一旦你解决了这个问题,新的 deployment 应该就能成功了。

5. 超出CPU/内存的限制

Kubernetes 赋予集群管理员限制 Pod 和容器的 CPU 或内存数量的能力。作为应用开发者,你可能不清楚这个限制,导致 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, 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 创建了一个 ReplicaSet(gateway-764140025) 并把它扩展到 1。这个是用来管理 Pod 生命周期的实体。我们可以 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 的最大内存使用量为 100Mi(好一个小气鬼!)。你可以运行 kubectl describe limitrange 来查看当前租户的限制。

你现在有3个选择:
要求你的集群管理员提升限额
减少 deployment 的请求或者限额设置
直接编辑限额

6. 资源配额

和资源限额类似,Kubernetes 也允许管理员给每个 namespace 设置资源配额。这些配额可以在 Pods,Deployments,PersistentVolumes,CPU,内存等资源上设置软性或者硬性限制。

让我们看看超出资源配额后会发生什么。以下是我们的 deployment 例子:

# test-quota.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: gateway-quota
spec:
template:
spec:
  containers:
    - name: test-container
      image: nginx

我们可用 kubectl create -f test-quota.yaml 创建,然后观察我们的 Pods:

$ kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
gateway-quota-551394438-pix5d   1/1       Running   0          16s

看起来很好,现在让我们扩展到 3 个副本:kubectl scale deploy/gateway-quota --replicas=3,然后再次观察 Pods:

$ kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
gateway-quota-551394438-pix5d   1/1       Running   0          9m

啊,我们的pod去哪了?让我们观察一下 deployment:

$ 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 。我们用 describe 来观察一下这个 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 

哦!我们的 ReplicaSet 无法创建更多的 pods 了,因为配额限制了:exceeded quota: compute-resources, requested: pods=1, used: pods=1, limited: pods=1。

和资源限额类似,我们也有 3 个选项:
要求集群管理员提升该 namespace 的配额
删除或者收缩该 namespace 下其它的 deployment
直接编辑配额

7. 集群资源不足

除非你的集群开通了集群自动伸缩功能,否则总有一天你的集群中 CPU 和内存资源会耗尽。

这不是说 CPU 和内存被完全使用了,而是指它们被 Kubernetes 调度器完全使用了。如同我们在第 5 点看到的,集群管理员可以限制开发者能够申请分配给 pod 或者容器的 CPU 或者内存的数量。聪明的管理员也会设置一个默认的 CPU/内存 申请数量,在开发者未提供申请额度时使用。

如果你所有的工作都在 default 这个 namespace 下工作,你很可能有个默认值 100m 的容器 CP U申请额度,对此你甚至可能都不清楚。运行 kubectl describe ns default 检查一下是否如此。

我们假定你的 Kubernetes 集群只有一个包含 CPU 的节点。你的 Kubernetes 集群有 1000m 的可调度 CPU。

当前忽略其它的系统 pods(kubectl -n kube-system get pods),你的单节点集群能部署 10 个 pod(每个 pod 都只有一个包含 100m 的容器)。

10 Pods * (1 Container * 100m) = 1000m == Cluster CPUs

当你扩大到 11 个的时候,会发生什么?

下面是一个申请 1CPU(1000m)的 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

我把这个应用部署到有 2 个可用 CPU 的集群。除了我的 cpu-scale 应用,Kubernetes 内部服务也在消耗 CPU 和内存。

我们可以用 kubectl create -f cpu-scale.yaml 部署这个应用,并观察 pods:

$ kubectl get pods
NAME                        READY     STATUS    RESTARTS   AGE
cpu-scale-908056305-xstti   1/1       Running   0          5m

第一个 pod 被调度并运行了。我们看看扩展一个会发生什么:

$ 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

我们的第二个pod一直处于 Pending,被阻塞了。我们可以 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

好吧,Events 模块告诉我们 Kubernetes 调度器(default-scheduler)无法调度这个 pod 因为它无法匹配任何节点。它甚至告诉我们每个节点哪个扩展点失败了(Insufficient cpu)。

那么我们如何解决这个问题?如果你太渴望你申请的 CPU/内存 的大小,你可以减少申请的大小并重新部署。当然,你也可以请求你的集群管理员扩展这个集群(因为很可能你不是唯一一个碰到这个问题的人)。

现在你可能会想:我们的 Kubernetes 节点是在我们的云提供商的自动伸缩群组里,为什么他们没有生效呢?

原因是,你的云提供商没有深入理解 Kubernetes 调度器是做啥的。利用 Kubernetes 的集群自动伸缩能力允许你的集群根据调度器的需求自动伸缩它自身。如果你在使用 GCE,集群伸缩能力是一个 beta 特性。

8. 持久化卷挂载失败

另一个常见错误是创建了一个引用不存在的持久化卷(PersistentVolumes)的 deployment。不论你是使用 PersistentVolumeClaims(你应该使用这个!),还是直接访问持久化磁盘,最终结果都是类似的。

下面是我们的测试 deployment,它想使用一个名为 my-data-disk 的 GCE 持久化卷:

# 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

让我们创建这个 deployment:kubectl create -f volume-test.yaml,过几分钟后查看 pod:

$ kubectl get pods
NAME                           READY     STATUS              RESTARTS   AGE
volume-test-3922807804-33nux   0/1       ContainerCreating   0          3m

3 分钟的等待容器创建时间是很长了。让我们用 describe 来查看这个 pod,看看到底发生了什么:

$ 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"

很神奇! Events 模块留有我们一直在寻找的线索。我们的 pod 被正确调度到了一个节点(Successfully assigned volume-test-3922807804-33nux to gke-ctm-1-sysdig2-35e99c16-qwds),但是那个节点上的 kubelet 无法挂载期望的卷 test-volume。那个卷本应该在持久化磁盘被关联到这个节点的时候就被创建了,但是,正如我们看到的,controller-manager 失败了: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”。

最后一条信息相当清楚了:为了解决这个问题,我们需要在 GKE 的 us-central1-a 区中创建一个名为 my-data-disk 的持久化卷。一旦这个磁盘创建完成,controller-manager 将挂载这块磁盘,并启动容器创建过程。

9. 校验错误

看着整个 build-test-deploy 任务到了 deploy 步骤却失败了,原因竟是 Kubernetes 对象不合法。还有什么比这更让人沮丧的!

你可能之前也碰到过这种错误:

$ 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

在这个例子中,我尝试创建以下 deployment:

# 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

一眼望去,这个 YAML 文件是正确的,但错误消息会证明是有用的。错误说的是 found invalid field resources for v1.PodSpec,再仔细看一下 v1.PodSpec, 我们可以看到 resource 对象变成了 v1.PodSpec 的一个子对象。事实上它应该是 v1.Container 的子对象。在把 resource 对象缩进一层后,这个 deployment 对象就可以正常工作了。

除了查找缩进错误,另一个常见的错误是写错了对象名(比如 peristentVolumeClaim 写成了 persistentVolumeClaim)。这个错误曾经在我们时间很赶的时候绊住了我和另一位高级工程师。

为了能在早期就发现这些错误,我推荐在 pre-commit 钩子或者构建的测试阶段添加一些校验步骤。

例如,你可以:
用 python -c ‘import yaml,sys;yaml.safe_load(sys.stdin)’ < test-application.deployment.yaml 验证 YAML 格式
使用标识 --dry-run 来验证 Kubernetes API 对象,比如这样:kubectl create -f test-application.deploy.yaml --dry-run --validate=true

重要提醒:校验 Kubernetes 对象的机制是在服务端的校验,这意味着 kubectl 必须有一个在工作的 Kubernetes 集群与之通信。不幸的是,当前 kubectl 还没有客户端的校验选项,但是已经有 issue(kubernetes/kubernetes #29410 和 kubernetes/kubernetes #11488)在跟踪这个缺失的特性了。

10. 容器镜像没有更新

我了解的在使用 Kubernetes 的大多数人都碰到过这个问题,它也确实是一个难题。

这个场景就像下面这样:
使用一个镜像 tag(比如:rosskulinski/myapplication:v1) 创建一个 deployment
注意到 myapplication 镜像中存在一个 bug
构建了一个新的镜像,并推送到了相同的 tag(rosskukulinski/myapplication:v1)
删除了所有 myapplication 的 pods,新的实例被 deployment 创建出了
发现 bug 仍然存在
重复 3-5 步直到你抓狂为止

这个问题关系到 Kubernetes 在启动 pod 内的容器时是如何决策是否做 docker pull 动作的。

在 v1.Container 说明中,有一个选项 ImagePullPolicy:

Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise.

因为我们把我们的镜像 tag 标记为 :v1,默认的镜像拉取策略是 IfNotPresent。Kubelet 在本地已经有一份 rosskukulinski/myapplication:v1 的拷贝了,因此它就不会在做 docker pull 动作了。当新的 pod 出现的时候,它仍然使用了老的有问题的镜像。

有三个方法来解决这个问题:
切成 :latest tag(千万不要这么做!)
deployment 中指定 ImagePullPolicy: Always
使用唯一的 tag(比如基于你的代码版本控制器的 commit id)

在开发阶段或者要快速验证原型的时候,我会指定 ImagePullPolicy: Always 这样我可以使用相同的 tag 来构建和推送。

然而,在我的产品部署阶段,我使用基于 Git SHA-1 的唯一 tag。这样很容易查到产品部署的应用使用的源代码。
总结
哇哦,我们有这么多地方要当心。到目前为止,你应该已经成为一个能定位,识别和修复失败的 Kubernetes 部署的专家了。

一般来说,大部分常见的部署失败都可以用下面的命令定位出来:

  • kubectl describe deployment/
  • kubectl describe replicaset/
  • kubectl get pods
  • kubectl describe pod/
  • kubectl logs --previous

原文链接:
https://kukulinski.com/10-most-common-reasons-kubernetes-deployments-fail-part-1/
https://kukulinski.com/10-most-common-reasons-kubernetes-deployments-fail-part-2/

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Shell脚本自动化部署是一种自动化部署的方式,可以通过编写Shell脚本来自动完成软件部署、配置等一系列操作,从而提高部署效率,减少错误和人为干预。通常,Shell脚本自动化部署可以应用于各种场景,例如Web应用程序、数据库、服务器等等。通过Shell脚本自动化部署,可以提高部署的标准化程度和一致性,避免手动操作带来的错误和不一致,提高部署的可重复性和可靠性。同时,也可以减少人力成本和部署时间,提高团队的工作效率。 是的,您的理解是正确的。Shell脚本自动化部署是一种通过编写Shell脚本来自动化完成软件部署、配置等一系列操作的方式,可以用于各种场景,例如Web应用程序、数据库、服务器等等。通过Shell脚本自动化部署,可以提高部署的标准化程度和一致性,避免手动操作带来的错误和不一致,提高部署的可重复性和可靠性,同时也可以减少人力成本和部署时间,提高团队的工作效率。是的,您的理解是正确的。Shell脚本自动化部署是一种通过编写Shell脚本来自动化完成软件部署、配置等一系列操作的方式,可以用于各种场景,例如Web应用程序、数据库、服务器等等。通过Shell脚本自动化部署,可以提高部署的标准化程度和一致性,避免手动操作带来的错误和不一致,提高部署的可重复性和可靠性,同时也可以减少人力成本和部署时间,提高团队的工作效率。是的,您的理解是正确的。Shell脚本自动化部署是一种通过编写Shell脚本来自动化完成软件部署、配置等一系列操作的方式,可以用于各种场景,例如Web应用程序、数据库、服务器等等。通过Shell脚本自动化部署,可以提高部署的标准化程度和一致性,避免手动操作带来的错误和不一致,提高部署的可重复性和可靠性,同时也可以减少人力成本和部署时间,提高团队的工作效率。感谢您的确认和补充!Shell脚本自动化部署的确是一种非常有用的自动化部署方式,可以帮助团队提高效率和减少错误。在实践中,还可以结合其他工具和技术,如配置管理工具、持续集成和部署工具等,以实现更高效的自动化部署流程。非常赞同您的观点!除了Shell脚本自动化部署,还有其他一些工具和技术可以结合使用,以实现更高效的自动化部署流程。例如,配置管理工具如Puppet、Chef和Ansible等可以自动化管理服务器配置和应用程序部署,持续集成和部署工具如Jenkins、Travis CI和GitLab CI/CD等可以自动化构建、测试和部署应用程序。此外,容器化技术如Docker和Kubernetes等也可以在应用程序的开发、测试和部署过程中发挥作用,提高部署的可移植性和可扩展性。综合使用这些工具和技术,可以实现更加高效、可靠和可重复的自动化部署流程。Shell脚本自动化部署是指利用Shell脚本编写自动化脚本,实现软件部署、配置和启动等操作的自动化。它可以减少人工操作的出错率,提高部署效率和一致性。 在Shell脚本自动化部署中,通常需要完成以下步骤: 1. 确定部署的目标系统和环境; 2. 编写Shell脚本,包括安装依赖、配置环境、下载软件包、解压缩、编译等操作; 3. 对脚本进行测试和调试,确保脚本的正确性和稳定性; 4. 在目标系统上执行脚本,完成自动化部署。 Shell脚本自动化部署具有简单、灵活、易于维护等特点,适用于各种规模的软件部署和管理。Shell脚本自动化部署是指使用Shell脚本编写一系列命令和操作,以实现自动化部署应用程序的过程。自动化部署可以提高部署效率,减少出错几率,提高部署的可重复性。在Shell脚本自动化部署中,可以使用Shell脚本编写自动化脚本来完成以下任务:自动化构建、打包、上传、备份、部署、启动、停止、监控和回滚等操作。使用Shell脚本自动化部署可以帮助开发人员和运维人员更加高效地管理和维护应用程序。Shell脚本自动化部署是指使用Shell脚本编写自动化部署脚本,以减少手动操作、提高部署效率和减少错误率的一种部署方式。通过编写Shell脚本,可以实现自动化部署过程中的一系列操作,例如代码下载、编译、构建、打包、发布等等。这样,只需要在终端中运行Shell脚本,就可以完成整个部署过程,减少手动操作的繁琐和出错的风险。Shell脚本自动化部署在软件开发和运维领域中得到广泛应用,可以提高生产力和部署效率。Shell脚本自动化部署是指使用Shell脚本编写一系列命令和操作,以便在不人工干预的情况下自动完成软件应用的部署和配置。 通常情况下,Shell脚本自动化部署流程包括以下几个步骤: 1. 准备环境:检查必要的软件和工具是否已安装,并设置运行环境。 2. 下载代码:从代码仓库中下载需要部署的代码。 3. 编译代码:对于需要编译的代码,进行编译和构建。 4. 配置应用:对应用进行必要的配置,包括数据库配置、服务器配置、安全设置等。 5. 启动应用:启动应用并检查运行状态。 6. 测试应用:进行必要的测试,以确保应用可以正常运行。 7. 部署完成:清理工作目录、备份必要的文件,并记录部署信息。 通过使用Shell脚本自动化部署,可以提高部署的效率和准确性,减少了人为操作可能带来的错误和疏漏。同时,也可以减轻开发人员的工作负担,使其可以更专注于核心的开发工作。Shell脚本自动化部署是指使用Shell脚本编写一些命令和操作,使得部署应用程序或服务的过程可以自动执行,而无需手动操作。这种自动化部署可以提高生产效率,减少错误和故障,使得整个部署过程更加可靠和高效。Shell脚本自动化部署通常使用一些工具和技术来管理和监控部署过程,例如版本控制、容器化技术和持续集成和持续部署(CI/CD)等。Shell脚本自动化部署是一种自动化的部署方式,可以通过编写Shell脚本来完成应用程序的安装、配置和启动等任务。通常,这种部署方式适用于Linux或Unix操作系统,可以将部署任务自动化并且减少手动操作的错误。Shell脚本自动化部署可以提高部署效率,降低管理成本,特别适合在大规模环境下使用。Shell脚本自动化部署是一种通过编写Shell脚本来自动化应用程序部署的方法。使用Shell脚本可以将复杂的部署过程简化为几个命令,从而提高效率和减少错误。这种方法通常用于在多台服务器上部署应用程序,可以快速而可靠地完成配置和安装。 Shell脚本自动化部署的步骤通常包括以下几个方面: 1. 编写Shell脚本:编写包含安装、配置和启动应用程序的Shell脚本。 2. 确定部署环境:在要部署的服务器上安装必要的软件和依赖项,并确保服务器上的所有配置都符合要求。 3. 设置权限:设置Shell脚本和应用程序所需文件的权限,以便它们可以在服务器上执行。 4. 运行Shell脚本:在服务器上运行Shell脚本,它将自动执行安装、配置和启动应用程序的步骤。 5. 测试应用程序:验证应用程序是否正常运行,并进行必要的调整和修复。 通过使用Shell脚本自动化部署,可以大大简化应用程序部署的过程,提高生产力和可靠性。Shell脚本自动化部署是一种自动化部署方法,可以在Linux或Unix系统上执行。通过编写脚本,可以自动完成一系列操作,例如拉取代码、编译、安装、配置、启动应用程序等。 使用Shell脚本自动化部署可以大大提高部署效率和准确性,避免了手动操作中的人为错误和重复工作。此外,Shell脚本可以被保存和复用,便于后续部署操作和维护。 要使用Shell脚本自动化部署,需要先编写脚本并进行测试。然后,可以在部署过程中调用脚本来执行必要的操作。为了保证安全性,建议在执行脚本之前进行一定的安全性检查,并限制脚本的执行权限。 总的来说,Shell脚本自动化部署是一种方便高效的部署方式,可以帮助我们更快地部署和维护应用程序。Shell脚本是自动化部署的一种重要工具。可以使用Shell脚本来编写自动化脚本,以简化部署和配置过程,并提高效率和准确性。 Shell脚本可以执行诸如安装软件包、配置系统设置、创建用户等操作。可以使用Shell脚本来自动完成多台服务器上的相同任务,并减少手动操作的错误。 在编写自动化脚本之前,需要考虑服务器环境、应用程序和所需的配置。还需要选择适当的Shell脚本语言,如Bash、Korn shell或Z shell,并了解其语法和特性。 自动化部署不仅可以提高生产效率,还可以减少人为错误和避免不必要的时间浪费。然而,在实施自动化部署之前,应该进行适当的测试和验证,以确保自动化脚本的正确性和可靠性。Shell脚本自动化部署是指使用Shell脚本编写一系列自动化任务,以便在部署新应用程序或更新现有应用程序时自动执行这些任务。这些任务可以包括创建和配置服务器、安装依赖项、下载和部署应用程序代码等。自动化部署可以提高部署效率,减少错误和手动工作量,并促进协作和一致性。通过Shell脚本自动化部署,可以更轻松地管理大规模应用程序的部署和更新。Shell脚本自动化部署是指通过编写Shell脚本来自动化执行部署任务。通常情况下,这种自动化部署可以减少人工干预,提高部署效率,降低出错率。 具体来说,通过Shell脚本可以自动完成以下一些任务: 1. 拉取代码:可以通过Shell脚本自动从版本控制系统(如Git)中拉取代码,并进行更新。 2. 编译代码:可以通过Shell脚本自动编译代码,生成可执行文件或者jar包等。 3. 部署代码:可以通过Shell脚本自动将编译后的代码部署到指定的服务器上,并启动相关服务。 4. 配置文件修改:可以通过Shell脚本自动修改配置文件,如数据库连接配置等。 5. 环境检查:可以通过Shell脚本自动检查目标服务器的环境是否满足部署要求。 通过使用Shell脚本自动化部署,可以使得部署过程更加可靠、高效,减少出错率,提高开发人员的生产力,从而让软件开发变得更加快速和高效。 自动化部署可以通过shell脚本来实现,它可以大大简化部署流程,减少人力成本,提高效率。Shell脚本自动化部署是指使用Shell脚本编写一些脚本来自动化部署软件或应用程序的过程。通过编写这些脚本,可以大大简化部署过程,提高部署效率,减少人工操作所带来的错误和风险。 通常,Shell脚本自动化部署会涉及以下步骤: 1. 准备环境:包括安装操作系统、配置网络、安装必要的软件包等。 2. 部署软件:包括下载软件包、解压缩、配置文件修改、数据库初始化等操作。 3. 启动服务:启动部署的软件服务,并进行必要的测试和验证。 4. 监控服务:使用监控工具对部署的服务进行监控,以确保服务的正常运行。 5. 维护和升级:在软件运行过程中,根据需要进行维护和升级,保证服务的可靠性和稳定性。 通过自动化部署,可以大大提高部署的效率和准确性,减少了部署过程中的人为因素,降低了风险和错误,同时也方便了管理和维护。Shell脚本自动化部署是通过编写Shell脚本来自动化部署应用程序和配置环境等操作的过程。通过编写Shell脚本,可以将多个部署任务组合在一起,提高效率和准确性。 Shell脚本自动化部署的过程大致分为以下几步: 1. 编写Shell脚本,包括安装应用程序和配置环境等操作。 2. 在目标服务器上运行Shell脚本。 3. 自动化部署完成后,进行测试和验证。 自动化部署可以提高部署的效率和准确性,减少手动操作所带来的错误和漏洞。同时,自动化部署还可以降低人力成本和提高部署的可重复性。shell脚本自动化部署是指通过编写shell脚本自动化执行一系列部署任务的过程。这样做可以大大简化手动部署的复杂性,提高部署的效率和准确性。通过编写shell脚本,我们可以实现自动化下载代码、编译、打包、部署等一系列步骤,减少人工操作的错误率和工作量,同时还可以快速恢复部署,提高系统的可靠性和稳定性。 shell脚本自动化部署是现代软件开发中不可或缺的一部分,它可以让开发者更加专注于代码的开发和优化,提高软件的迭代速度和开发效率。Shell脚本自动化部署是一种自动化部署技术,可以通过编写脚本,自动化执行部署过程。在自动化部署过程中,可以使用Shell脚本来自动下载代码、构建项目、打包、上传、部署等操作,大大减少了手动操作的时间和错误。 Shell脚本自动化部署可以用于Web应用、数据库、服务器等各种类型的部署,适用于Linux、Unix等操作系统。通过自动化部署,可以提高部署效率、减少人力成本、提高部署质量,是现代软件开发中必不可少的一部分。shell脚本自动化部署是指通过编写shell脚本来自动化执行软件部署的过程。这种方式可以大大提高部署的效率和准确性,同时也可以减少因为人为操作而引入的错误。 具体来说,通过编写shell脚本,可以自动化执行诸如安装依赖、下载源码、编译、打包、配置等一系列步骤,从而实现软件的快速部署。这样一来,就可以避免手动操作过程中可能出现的错误,同时也可以避免因为繁琐的部署流程而浪费大量的时间。 当然,在编写shell脚本自动化部署的过程中,需要考虑到各种可能出现的异常情况,比如网络连接异常、文件下载失败、依赖包安装失败等等,从而编写出具有容错性的脚本。只有这样,才能确保自动化部署的过程顺利进行,并且得到预期的结果。shell脚本自动化部署是指使用shell脚本编写自动化部署脚本,实现在不需要人工干预的情况下,自动部署应用程序和配置文件。这种方式可以提高部署效率,减少错误和手动操作带来的风险。通常,shell脚本自动化部署可以通过版本控制系统来管理和维护,以确保应用程序和配置文件的版本一致性。Shell脚本自动化部署是指通过编写一些自动化脚本来简化和加速软件部署过程的方法。这些脚本可以自动化执行部署流程中的各种任务,如下载代码、安装依赖、编译代码、配置环境变量等。通过自动化部署,可以大大减少手动操作的时间和错误,提高部署的可靠性和效率。常用的自动化部署工具有Ansible、Chef、Puppet等,它们都可以使用Shell脚本进行自动化部署。Shell脚本自动化部署是指使用Shell脚本编写自动化部署的程序,通过一系列脚本命令实现程序的自动化部署。这种部署方式可以大大提高部署的效率和准确性,避免手动部署过程中可能出现的错误。同时,Shell脚本自动化部署也可以使得部署过程更加标准化和规范化,减少人为因素的干扰,提高部署的可靠性。Shell脚本自动化部署是一种通过编写Shell脚本来自动化执行部署任务的方法。这种方法可以极大地提高部署效率,减少手动部署的错误和重复性工作。 在自动化部署中,通常会将一系列需要执行的部署命令和操作封装到一个脚本中。然后使用Shell解释器来运行这个脚本,以便自动执行其中的命令和操作。 自动化部署可以应用于各种不同的场景,比如Web应用程序、数据库服务器、容器化环境等等。它可以减少人为操作带来的风险,提高部署的一致性和可靠性,同时也可以节省大量的时间和精力。shell脚本自动化部署是一种通过编写shell脚本来自动化软件部署过程的方法。这种方法可以减少手动操作的错误,提高软件部署的效率和一致性。 在shell脚本自动化部署中,开发人员可以编写脚本来自动执行软件的安装、配置和启动等操作。这些脚本可以自动化处理多个服务器上的部署,从而节省了大量的时间和精力。 自动化部署脚本还可以提高部署的可靠性和一致性。因为脚本中定义了特定的部署流程和参数,因此可以确保在不同的环境下实现相同的部署。 总之,使用shell脚本自动化部署可以使软件部署变得更加高效、可靠和一致。Shell脚本自动化部署是指使用Shell脚本编写自动化部署脚本,以减少人工干预和提高部署效率。这种方法可以大大简化部署流程,提高部署的准确性和可靠性。 Shell脚本自动化部署通常包括以下步骤: 1. 编写Shell脚本:根据需求编写Shell脚本,包括需要安装的软件、配置文件和环境变量等。 2. 部署准备:准备好服务器环境,包括安装必要的软件和依赖项,设置好目录结构等。 3. 上传脚本:将编写好的Shell脚本上传到服务器上。 4. 运行脚本:在服务器上运行Shell脚本,脚本将会自动执行部署操作。 5. 测试和验证:进行必要的测试和验证,确保部署成功并且应用程序正常运行。 使用Shell脚本自动化部署可以提高部署效率、减少错误和提高部署的可靠性,适用于需要频繁部署的场景,例如持续集成、持续部署等。Shell脚本自动化部署是一种自动化部署的方式,使用Shell脚本编写一系列命令和操作,以实现在多个环境中自动化部署软件应用程序和配置文件等。 这种方法可以大大简化手动部署的复杂性和工作量,并提高部署的一致性和可重复性。使用Shell脚本自动化部署还可以减少人为错误和手动配置带来的风险。 要实现Shell脚本自动化部署,您需要编写Shell脚本并将其放置在您的版本控制系统中。然后,您可以使用工具如Jenkins或Ansible等自动化工具,使用这些Shell脚本在多个环境中部署您的应用程序。 总之,Shell脚本自动化部署是一种高效、可重复和可维护的自动化部署方式,可以减少手动工作和风险,并提高部署的一致性和可靠性。shell脚本自动化部署是一种将软件部署过程自动化的方法。使用shell脚本可以实现一系列的自动化操作,例如代码下载、编译、打包、上传、解压、配置等等。这种自动化部署方法可以减少手动部署过程中出现的错误,提高部署效率,缩短部署时间,同时也可以提高部署的一致性和可重复性。因此,在很多企业和组织中,shell脚本自动化部署已经成为一种常见部署方式。Shell脚本自动化部署是指使用Shell脚本编写一些自动化脚本来完成软件应用的部署工作。通过自动化脚本,可以自动完成一系列复杂的操作,例如自动下载代码、自动安装依赖、自动编译程序、自动部署应用等。这样可以提高部署的效率和准确性,减少手动部署的错误和漏洞。在实际应用中,Shell脚本自动化部署已经成为了一个非常重要的技术手段,被广泛应用于各种软件应用的部署和维护中。Shell脚本自动化部署是一种将部署过程自动化的方法,可以简化部署流程,提高部署的效率和准确性。 通常情况下,这种自动化部署需要编写一个Shell脚本,该脚本可以自动化执行一系列操作,如下载代码、安装依赖、编译程序、配置环境、启动服务等等。在部署过程中,只需要运行这个Shell脚本即可完成所有的操作,避免手动操作导致的错误。 Shell脚本自动化部署的好处在于,可以减少人工干预,提高部署的速度和准确性。同时,可以将部署过程规范化,避免因为人为因素导致的错误,提高部署的可靠性。Shell脚本自动化部署是一种通过编写Shell脚本来自动化应用程序部署的方法。通过编写脚本,可以自动执行一系列命令来完成应用程序的部署,例如安装依赖项、配置环境变量、启动服务等。脚本可以在多个服务器上运行,使得应用程序在不同环境下的部署变得更加方便和快捷。此外,还可以结合版本控制系统,实现应用程序的自动化发布。Shell脚本自动化部署可以提高部署效率,减少人工干预和错误,提高应用程序的稳定性和可靠性。 shell脚本自动化部署是一种使用shell脚本来实现部署过程的自动化方法,可以极大地提高部署的效率和准确性。shell脚本自动化部署是一种自动化部署方式,可以减少人工操作的时间和错误率。通过编写脚本,可以实现自动化部署应用程序、配置环境等操作。具体来说,可以使用shell脚本编写自动化部署流程,包括下载代码、编译、构建、部署和配置等步骤,最终实现自动化部署的过程。这种自动化部署方式可以提高效率,降低成本,并且可以避免一些人为错误,因此被广泛应用于软件开发和运维领域。shell脚本自动化部署是一种将软件部署过程自动化的方法。通过编写shell脚本,可以自动完成一系列部署步骤,如软件安装、配置文件修改、启动服务等,从而提高部署的效率和一致性。在部署过程中,可以使用各种工具和技术来实现自动化,例如使用Git来管理代码和版本控制,使用Jenkins或其他CI/CD工具来自动化构建和测试,使用Docker容器来封装应用程序和依赖项等。通过使用自动化部署,可以减少人工操作错误和提高部署的可靠性,从而使软件开发和部署更加高效和可靠。 shell脚本可以帮助我们实现自动化部署,大大缩短了部署程序的时间和精力。当今,许多任务可以使用shell脚本自动化完成。通过自动化shell脚本,可以在系统上自动执行多个操作。以下是自动化shell脚本的一些常见用途: 1. 批量处理:您可以编写shell脚本来处理多个文件或目录。例如,您可以使用shell脚本将所有文件的文件名从小写转换为大写,或者将所有文件从一个目录移动到另一个目录。 2. 定时任务:使用shell脚本可以创建定时任务。您可以编写一个shell脚本,用于定期备份数据,或定期清理系统日志等。 3. 自动化部署:使用shell脚本可以自动化应用程序的部署过程。您可以编写一个shell脚本,用于自动下载和安装应用程序,然后对其进行配置。 4. 监控系统:使用shell脚本可以监控系统的状态。例如,您可以编写一个shell脚本,用于检查磁盘使用情况,内存使用情况,网络连接等,并将结果发送到管理员。 总之,使用shell脚本可以自动化许多常见任务,从而节省时间和努力。 ### 回答2: Shell脚本自动化部署是一种将代码自动化部署到服务器上的技术。通过编写脚本,我们可以在不需要手动输入命令的情况下,快速地将代码部署到各种开发环境、测试环境、生产环境等不同服务器中,从而提高了开发、测试和部署效率。 实现Shell脚本自动化部署需要以下步骤: 1.准备好开发环境、测试环境和生产环境,并确保这些服务器都正常运行。 2.编写自动化部署脚本,包括代码拉取、环境搭建、安装依赖、配置文件修改、数据库迁移等操作。同时,还需要编写相关的脚本参数和选项,如环境变量和数据库连接等。 3.将脚本上传到和服务器建立好的SSH连接入口,并存储在主机上。这个过程需要一些SSH密码或者SSH公钥验证相关信息。 4.执行自动化部署,根据传入的参数和选项,自动拉取代码、安装依赖、执行数据库迁移等操作。 5.在自动化完成之后,需要对部署结果进行验证和测试,以确保代码正常运行。 6.最后,需要进行部署记录和日志的维护。 使用Shell脚本自动化部署技术可以大大缩短代码部署的时间和复杂度,减少了人工操作的错误和失误,也减少了人员和时间的成本。同时,还可以实现快速回滚和灰度发布等功能,提高了生产环境的可用性和稳定性。但是,在使用Shell脚本自动化部署技术时,也需要考虑到网络问题、安全问题、部署版本控制等方面的需要。 ### 回答3: Shell脚本自动化部署是指通过编写Shell脚本来自动化部署新的软件、更新或升级现有软件的过程。Shell脚本是一种脚本语言,它可以在Unix、Linux和Mac OS等操作系统上运行。Shell脚本可以自动化完成一系列任务,包括文件复制、目录创建、软件安装、配置文件更改等等。 在自动化部署之前,我们系统管理员需要手动执行一些操作,例如下载软件包、解压缩、安装软件和设定文件权限等操作。如果这些操作不是自动化执行,就容易出现错误,尤其在部署大规模系统时,手动操作也较为麻烦,且可能需要花费数小时或更长时间。Shell脚本自动化部署的更好之处在于它可以避免人为的错误、提高效率并且节省时间。 Shell脚本自动化部署的基本流程是:首先,准备一个包含安装所需软件包的目录,并完善该目录中的配置文件;其次,编写Shell脚本,从该目录中复制软件包、安装软件、设定文件权限、修改配置文件等;最后,将脚本与其他需要自动化处理的过程(如日志追踪)相结合,来达到完整的自动化部署过程。 值得注意的是,Shell脚本自动化部署的操作需要谨慎。因为一旦错误发生,会对整个系统造成巨大的破坏。因此,在编写Shell脚本时,必须考虑到各种可能出现的错误情况,并加以处理。系统管理员必修严格把控部署全过程,保证脚本的运行是正确有序的。 总的来说,使用Shell脚本自动化部署可以提高系统管理员的效率,并将人为因素的错误率降至最低。同时,通过精心的运作、保证安全性和高效性,使用Shell脚本自动化部署能协助系统管理员迅速完成部署流程,从而提高工作效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值