管理Pod资源对象

Pod是Kubernetes系统的基础单元,是资源对象模型中可由用户创建或部署的最小组件,也是在K8S系统上运行容器化应用的资源对象。其他的大多数资源对象都是用于支撑喝扩展Pod对象功能的,例如,用于管控Pod运行的StatefulSet喝Deployment等控制器对象,用于暴漏Pod应用的Service和Ingress对象,为Pod提供存储的PersistenVolume存储资源对象等。这些资源对象大体可以分为有限的几个类别,并且可基于资源清单作为资源配置文件进程陈述式或声明式管理

容器与Pod资源对象

现代化的容器技术被设计用来运行单个进程时,该进程在容器中PID名称空间的进程号为1,可直接接收并处理信号,于是,在此进程终止时,容器就会终止退出。如果要在一个容器内运行多个进程,则需要为这些进程提供一个类似于Linux操作系统init进程的管控类进程。通常还需要日志进程来管理这些进程的日志,例如,将它们分别保存于不同的日志文件等,否则用户就不得不手动来分拣日志信息。因此绝大多数场景中都应该在一个容器中运行一个进程,它的日志信息直接输出到容器的标准输出,支持用户直接使用命令kubectl logs来获取,这也是docker及K8S使用容器的标准方式,在Docker启动时,如果docker镜像中启动应用的命令是后台进程,那么该命令结束docker就会认为容器结束,如果kubelet创建包含这个容器的Pod之后运行完该命令,即认为Pod执行结束,将立刻销毁Pod,如果为该Pod定义了Pod控制器,则系统会监控到该Pod已经终止,之后根据控制器中定义的副本数量重新生成新的Pod,一旦创建新的Pod,就在执行完命令后进入无限循环的过程中,这也是K8S需要我们自己创建docker镜像,并需要以一个前台命令作为启动命令的原因。对于无法改造为前台执行的应用,也可以使用开源工具Supervisor辅助进行前台运行的功能。supervisior提供了一种可以同时启动多个后台应用,并保持supervisor自身在前台执行的机制 不过,分别运行于各自容器的进程之间无法实现基于IPC的通信机制,此时,容器间的隔离机制对于依赖于此类通信方式的进程来说却又成了阻碍。Pod资源抽象正是用来解决此类问题的组件,前面多次提到,Pod对象是一组容器的集合,这些容器共享Network、UTS及IPC名称空间,因此具有相同的域名、主机名和网络接口,并可通过IPC直接通信,为一个Pod对象中的各容器提供网络名称空间等共享机制的底层基础是pause,需要注意的是,一个Pod对象中的多个容器需要运行在同一个工作节点之上。 尽管可以将Pod类比为物理机或VM,但一个Pod内通常仅应该运行一个应用,除非多个进程之间有密切的关联关系。这也意味着,实践中应该将多个应用分别构建到多个Pod中,这样也能更能符合容器化的轻量化设计和运行之目的 例如,一个有着前端(application server)和后端(database server)的应用,其前后端应该分别组织于各自的Pod中,而非同一Pod的不同容器中,这样做的好处在于,多个Pod可能被调度至多个不同的主机运行,提供了资源利用率。另外Pod也是K8S进行系统规模伸缩的基础单元,分别运行于不同Pod的多个应用可独立按需进行规模变动,这就增强了系统架构的灵活性。事实上,前后端应用的规模需求通常也不会相同,而且无状态应用的规模变动也比有状态容易的多,将他们组织于同一Pod中时将无法享受这种便利 不过,有些场景要求必须在同一个Pod中同时运行多个容器。此时,这些分布式应用,必须遵循某些最佳实践机制或基本准则。事实上K8S并非期望称为一个管理系统,而是一个支持这些最佳时间的向开发人员或管理人员提供更高级别服务的系统。分布式系统设计通常包含以下几种模型

  • Sideca pattern(边车模型或跨斗模型):为Pod的主应用容器提供协同的辅助应用容器,每个应用独立运行,最为典型的代表是将主应用容器的日志使用agent收集至日志服务器中,可以将agent进行为辅助容器,即sidecar,另一个典型的应用 为主应用容器中的 database server 启用本地缓存
  • Ambassador pattern(大使模型):即为远程服务创建一个本地代理,代理应用运行于容器中,主容器中的应用通过代理容器访问远程服务。一个典型的使用示例是主应用容器中的进程访问“一主多从”模型的远程redis应用时,可以在当前Pod容器中为redis服务创建一个Ambassador container,主应用容器中的进程直接通过localhost接口访问ambassador container即可。即便redis主从集群架构发生变动时,也仅需将ambassador container加以修改即可,主应用容器无须对此做出任何反应
  • Adapter pattern(适配器模式):此种模式一般用于将主应用容器中的内容进行标准化输出,例如,日志数据或指标数据的输出,这有助于调用者统一接收数据的接口。另外,某应用滚动升级后的版本不兼容旧的版本时,其报告信息的格式也存在不兼容的可能性,使用adapter container有助于避免哪些调用此报告数据的应用发生错误
    K8S系统的Pod资源对象用于运行单个容器化应用,此应用称为 Pod 对象的主容器( main container ),同时Pod也能容纳多个容器,不过额外容器一般工作为Sidecar模型,用于辅助主容器完成工作职能

管理Pod对象的容器

一个Pod对象中至少要存在一个容器,因此,containers字段是定义Pod时其嵌套字段Spec中的必选项,用于为Pod指定要创建的容器列表,一个Pod中可以定义多个容器。进行容器配置时,name为必选字段,用于指定容器名称,image字段为可选,以方便更高级别的管理类资源(如Deployment)等能覆盖此字段,但是自主式Pod并不可省略此字段,因此定义一个容器的基础框架如下

  1. [root@k8s-master01 nginx]# cat demo.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: test
  6. spec:
  7. #containers:表示定义一个容器对象列表
  8. containers:
  9. #name:表示定义容器的名称
  10. - name: busybox
  11. #image:表示获取镜像的URL
  12. image: registry.cn-hangzhou.aliyuncs.com/jiangyida/busybox:0.1
  13. [root@k8s-master01 nginx]#

此外,定义容器时还有一些其他常用的字段,例如,定义需要暴露的端口,改变镜像运行的默认程序,传递环境变量,定义可以用的系统资源配额等

镜像及其获取策略

各工作节点负责运行Pod对象,而Pod的核心功能在于运行容器,因此工作节点上必选配置容器引擎,如docker等。等启动容器时,容器引擎将首先在本地查找镜像文件,不存在的镜像则需要从指定的镜像仓库下载到本地 K8S系统支持用户自定义镜像文件的获取策略。例如在网络资源较为紧张时可以禁止从仓库中获取镜像文件等。容器的”imagePullPolicy”字段用于为期指定镜像获取策略,它的值包括如下几个

  • Always:镜像标签为“latest”或镜像不存在时总是从指定仓库中获取镜像
  • IfNotPresent:如果本地镜像缺失时才从仓库下载镜像
  • Never:禁止从仓库下载镜像,仅使用本地镜像,如果本地没有也不会从仓库下载
    下面的资源清单中的容器定义了镜像的获取策略
  1. [root@k8s-master01 nginx]# cat demo.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: test
  6. spec:
  7. #containers:表示定义一个容器对象列表
  8. containers:
  9. #name:表示定义容器的名称
  10. - name: busybox
  11. #image:表示获取镜像的URL
  12. image: registry.cn-hangzhou.aliyuncs.com/jiangyida/busybox:0.1
  13. #imagePullPolicy:表示定义镜像的获取策略
  14. imagePullPolicy: Always
  15. [root@k8s-master01 nginx]#

对于标签为“latest”的镜像文件,其默认的镜像获取策略即为“always”,而对于其他标签的镜像,其默认策略为“IfNotPresent”。需要注意的是,使用私有仓库中的镜像通常需要由registry服务器认证完成后才能进行。认证过程要么需要在相关节点上交互式执行 docker login命令,要么就是将认证信息定义为专有的secret资源,并配置Pod通过”imagePullSecret”字段调用此认证信息来完成

暴露端口

Docker的网络模型中,使用默认网络的容器化应用需要通过NAT机制将其端口“暴露(expose)”到外部网络中才能被其他节点智商的容器客户端所访问。然而,K8S系统的网络模型中,各Pod的IP地址处于同一网络平面中,无论是否为容器指定了要暴露的端口,都不会影响集群中其他节点智商的Pod客户端对其进行访问,这就意味着,任何监听在非lo接口(本地回环接口)上的端口都可以通过Pod网络直接被请求,从这个角度来说,容器端口只是信息性数据,它只是为集群用户提供一个快速了解相关Pod对象的可访问端口的信息,而且显示指定容器端口,还能为其赋予一个名称以方便调用 容器的ports字段的值是一个列表,由一到多个端口对象组成,它的常用嵌套字段包括如下几个

  • containerPort :必选字段,指定在Pod对象的IP地址上暴露的容器端口,有效范围为(0-65535),使用时,应该指定容器应用正常监听着的端口
  • name : 当前端口的名称,必选符合IANA_SVC_NAME规范且在当前Pod内必须是唯一的,此端口名可被Service资源调用
  • protocol: 端口相关的协议,可以为TCP或UDP协议,默认为TCP
    下面的资源配置清单实例中定义的test指定了暴露容器上TCP协议的80端口,并且给端口取名为http
  1. [root@k8s-master01 nginx]# cat demo.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: test
  6. spec:
  7. #containers:表示定义一个容器对象列表
  8. containers:
  9. #name:表示定义容器的名称
  10. - name: busybox
  11. #image:表示获取镜像的URL
  12. image: registry.cn-hangzhou.aliyuncs.com/jiangyida/busybox:0.1
  13. #imagePullPolicy:表示定义镜像的获取策略
  14. imagePullPolicy: Always
  15. #portts:定义一个端口列表
  16. ports:
  17. #name:定义该端口的名称
  18. - name: http
  19. #containerPort:定义需要暴露的容器端口,该值应该指定容器中正常监听着的端口
  20. containerPort: 80
  21. #protocol:指定该端口使用的协议
  22. protocol: TCP
  23. [root@k8s-master01 nginx]#

然而,Pod对象的IP地址仅在集群内部可达,它们无法直接接收来自集群外部客户端的请求,尽管它们的服务可达性不受工作节点边界的约束,但依然受限于集群边界。一个简单的解决方案是通过其所在的工作节点的IP地址和端口将其暴露到集群外部

  • hostPort : 主机端口,它将接收到的请求通过NAT机制转发至由containerPort字段指定的容器端口
  • hostIP : 主机端口要绑定的IP,默认为0.0.0.0,即主机之上所有可用的IP地址,考虑到托管的Pod对象是由调度器调度运行的,工作节点的IP地址难以明确指定,因此此字段通常使用默认值
    需要注意的是,hostPort与NodePort类型的Service对象暴露端口的方式不同,NodePort是通过所有节点暴露容器服务,而,集群外部可以通过访问任何工作节点的IP和端口(NodeIP:NodePort)都能访问到Pod内的容器资源,即使该Pod没有运行在被访问的工作节点上面,集群外部的客户端通过该IP和端口也能访问到资源。而hostPort则是由Pod对象所在节点的IP地址来进行
    可以通过kubectl explain pods.spec.containers.ports获取ports对象可用的字段列表

自定义运行的容器化应用

由docker镜像启动容器时运行的程序在相应的Dockerfile中由ENTRYPOINT指令进行定义,传递给程序的参数则由CMD指令指定,ENTRYPOINT指令不存在时,CMD指令用于同时指定程序及其参数

  1. [root@k8s-node02 ~]# docker inspect registry.cn-hangzhou.aliyuncs.com/jiangyida/busybox:0.1 -f {{.Config.Cmd}}
  2. [/bin/httpd -f -h /data/html]
  3. [root@k8s-node02 ~]#
  4. [root@k8s-node02 ~]# docker inspect registry.cn-hangzhou.aliyuncs.com/jiangyida/busybox:0.1 -f {{.Config.Entrypoint}}
  5. []
  6. [root@k8s-node02 ~]#

Pod的command字段能够指定不同于镜像默认运行的应用程序,并且可以同时使用args字段进行参数传递,它们将覆盖镜像中的默认定义。不过,如果仅为Pod定义了args字段,那么它将作为参数传递给镜像默认指定的运行的命令,如果仅为Pod定义了command字段,那么它将覆盖镜像中默认定义的运行的命令及参数,并以无参数方式运行。例如下面的资源清单文件中,我们将busybox的默认应用程序修改了为”/bin/sh”,传递的参数修改了为”-c while true; do sleep 30; done”

  1. [root@k8s-master01 nginx]# cat demo.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: test
  6. spec:
  7. #containers:表示定义一个容器对象列表
  8. containers:
  9. #name:表示定义容器的名称
  10. - name: busybox
  11. #image:表示获取镜像的URL
  12. image: registry.cn-hangzhou.aliyuncs.com/jiangyida/busybox:0.1
  13. #imagePullPolicy:表示定义镜像的获取策略
  14. imagePullPolicy: Always
  15. #portts:定义一个端口列表
  16. ports:
  17. #name:定义该端口的名称
  18. - name: http
  19. #containerPort:定义需要暴露的容器端口,该值应该指定容器中正常监听着的端口
  20. containerPort: 80
  21. #protocol:指定该端口使用的协议
  22. protocol: TCP
  23. #command:定义Pod里面默认要运行的程序
  24. command: ["/bin/sh"]
  25. #args:定义了cmmand命令需要用到的参数
  26. args: ["-c","while true; do sleep 30; done"]
  27. [root@k8s-master01 nginx]#

自定义args,也是向容器的应用程序传递配置信息常用的方式之一,对于非云原生的应用程序,这几乎也是最简单的配置方式。另外一个常用的方式是使用环境变量

环境变量

非容器化的传统管理方式中,复杂应用程序的配置信息多数由配置文件进行指定,用户可以借助于简单的文本编辑器完成配置管理。然而,对于容器隔离出的环境中的应用程序,用户就不得不穿透容器边界在容器内进行配置编辑并进行重载,这种方式复杂且低效。于是,由环境变量在容器启动时传递配置信息就成为一种备受青睐的方式 注意:这种方式依赖于应用程序支持通过环境变量进行配置的能力,否则,用户在制作Docker镜像时需要通过entrypoint脚本完成环境变量到程序配置文件的同步 向Pod对象中的容器环境变量传递数据的方法有两种:env和evFrom,这里先介绍第一种方式 通过环境变量配置容器化应用时,需要在容器配置段中嵌套使用env字段,它的值是一个由环境变量构成的列表

  • name : 环境变量的名称,必选字段
  • volue : 传递给环境变量的值,通过$(var_NAME)引用,逃逸格式为”$$(VAR_NAME)”,默认为空
    下面的配置清单中定义的Pod对象为其容器filebeat传递了两个环境变量,REDIS_HOST定义了filebeat收集的日志信息要发往的REDIS主机地址,LOG_LEVEL定义了filebeat的日志级别
  1. [root@k8s-master01 nginx]# cat filebeat.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: filebeat-env
  6. spec:
  7. containers:
  8. - name: filebat
  9. image: registry.cn-hangzhou.aliyuncs.com/jiangyida/filebeat:0.1
  10. imagePullPolicy: IfNotPresent
  11. env:
  12. - name: REDIS_HOE
  13. value: 192.168.1.100:6379
  14. - name: LOG_LEVEL
  15. value: info
  16. [root@k8s-master01 nginx]#

这些环境变量可以直接注入到容器的shell环境中,无论他们是否真正被用到,使用printenv一类的命令都能够在容器中获取到所有环境变量的列表

共享节点的网络名称空间

同一个Pod对象的各容器均运行于一个独立的、隔离的网络名称空间中,共享同一个网络协议栈及相关的网络设备。也有一些特殊的Pod对象需要运行于所在节点的名称空间中,执行系统级的管理任务,例如查看和操作节点的网络资源甚至是网络设备,事实上,仅需设置spec.hostNetwork的属性为true即可创建共享节点网络名称空间的Pod

  1. [root@k8s-master01 nginx]# cat demo.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: test
  6. spec:
  7. #containers:表示定义一个容器对象列表
  8. containers:
  9. #name:表示定义容器的名称
  10. - name: busybox
  11. #image:表示获取镜像的URL
  12. image: registry.cn-hangzhou.aliyuncs.com/jiangyida/busybox:0.1
  13. #imagePullPolicy:表示定义镜像的获取策略
  14. imagePullPolicy: Always
  15. #portts:定义一个端口列表
  16. ports:
  17. #name:定义该端口的名称
  18. - name: http
  19. #containerPort:定义需要暴露的容器端口,该值应该指定容器中正常监听着的端口
  20. containerPort: 80
  21. #protocol:指定该端口使用的协议
  22. protocol: TCP
  23. #hostNetwork:表示是否开启共享使用工作节点的网络名称空间,默认为false
  24. hostNetwork: true
  25. [root@k8s-master01 nginx]#
  26. [root@k8s-master01 nginx]# kubectl apply -f demo.yaml
  27. pod/test created
  28. [root@k8s-master01 nginx]# kubectl get pods
  29. NAME READY STATUS RESTARTS AGE
  30. client 0/1 Completed 0 3d22h
  31. my-nginx-854bbd7557-xnbn9 1/1 Running 0 3d2h
  32. test 1/1 Running 0 3s
  33. [root@k8s-master01 nginx]# kubectl exec -it test -- sh
  34. / # ifconfig
  35. docker0 Link encap:Ethernet HWaddr 02:42:2F:AF:18:56
  36. inet addr:10.244.38.1 Bcast:0.0.0.0 Mask:255.255.255.0
  37. UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
  38. RX packets:1923578 errors:0 dropped:0 overruns:0 frame:0
  39. TX packets:2014394 errors:0 dropped:0 overruns:0 carrier:0
  40. collisions:0 txqueuelen:0
  41. RX bytes:132254359 (126.1 MiB) TX bytes:621114437 (592.3 MiB)
  42. ens33 Link encap:Ethernet HWaddr 00:0C:29:AD:95:28
  43. inet addr:172.18.15.114 Bcast:172.18.15.255 Mask:255.255.248.0
  44. UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
  45. RX packets:101057717 errors:0 dropped:1027686 overruns:0 frame:0
  46. TX packets:89582963 errors:0 dropped:0 overruns:0 carrier:0
  47. collisions:0 txqueuelen:1000
  48. RX bytes:13716087414 (12.7 GiB) TX bytes:12800549992 (11.9 GiB)
  49. flannel.1 Link encap:Ethernet HWaddr 06:62:CA:E0:4C:13
  50. inet addr:10.244.38.0 Bcast:0.0.0.0 Mask:255.255.255.255
  51. UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
  52. RX packets:310 errors:0 dropped:0 overruns:0 frame:0
  53. TX packets:321 errors:0 dropped:0 overruns:0 carrier:0
  54. collisions:0 txqueuelen:0
  55. RX bytes:29832 (29.1 KiB) TX bytes:27710 (27.0 KiB)
  56. lo Link encap:Local Loopback
  57. inet addr:127.0.0.1 Mask:255.0.0.0
  58. UP LOOPBACK RUNNING MTU:65536 Metric:1
  59. RX packets:561872 errors:0 dropped:0 overruns:0 frame:0
  60. TX packets:561872 errors:0 dropped:0 overruns:0 carrier:0
  61. collisions:0 txqueuelen:1
  62. RX bytes:29275581 (27.9 MiB) TX bytes:29275581 (27.9 MiB)
  63. vetha359243 Link encap:Ethernet HWaddr 9E:10:4F:35:99:C2
  64. UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
  65. RX packets:1247766 errors:0 dropped:0 overruns:0 frame:0
  66. TX packets:1315713 errors:0 dropped:0 overruns:0 carrier:0
  67. collisions:0 txqueuelen:0
  68. RX bytes:103353125 (98.5 MiB) TX bytes:403603652 (384.9 MiB)
  69. / # exit
  70. [root@k8s-master01 nginx]#

如上述命令的结果显示,它打印出的是工作节点的网络设备及其相关的接口信息,这意味着,Pod对象中运行的容器化应用也监听于所在的工作节点的IP地址之上

  1. [root@k8s-master01 nginx]# kubectl get pods -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  3. client 0/1 Completed 0 3d23h 10.244.59.3 172.18.15.113 <none> <none>
  4. my-nginx-854bbd7557-xnbn9 1/1 Running 0 3d2h 10.244.59.2 172.18.15.113 <none> <none>
  5. test 1/1 Running 0 25s 172.18.15.114 172.18.15.114 <none> <none>
  6. [root@k8s-master01 nginx]# curl http://172.18.15.114
  7. this is test Version V1 V1 V1 V1 V1
  8. [root@k8s-master01 nginx]#

另外,在Pod对象中时还可以分别使用spec.hostPID和spec.hostIPC来共享工作节点的PID和PIC名称空间

设置Pod对象的上下文

Pod对象的安全上下文用于设定Pod或容器的权限和访问控制功能,其支持设置的常用属性包括以下几个方面

  • 基于用户ID(UID)和组ID(GID)控制访问对象(如文件)时的权限
  • 以特权或非 特权的方式运行
  • 通过Linux Capabilities为其提供部分特权
  • 基于Seccomp过滤进程的系统调用
  • 基于Selinux的安全标签
  • 是否能够进行权限升级
    Pod对象的安全上下文定义在spec.securityContext字段中,而容器的安全上下文则定义spec.containers[].securityContext字段中,且二者可嵌套使用的字段还有所不同。下面的配置清单实例以busybox容器定义了安全上下文,它以UID为1000的非特权用户运行容器,并禁止权限升级
  1. [root@k8s-master01 nginx]# cat demo.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: test
  6. spec:
  7. containers:
  8. - name: busybox
  9. image: registry.cn-hangzhou.aliyuncs.com/jiangyida/busybox:0.1
  10. command: ["/bin/sh","-c","sleep 3600"]
  11. securityContext:
  12. runAsNonRoot: true
  13. runAsUser: 1000
  14. allowPrivilegeEscalation: false
  15. hostNetwork: true
  16. [root@k8s-master01 nginx]#
  17. [root@k8s-master01 nginx]# kubectl apply -f demo.yaml
  18. pod/test created
  19. [root@k8s-master01 nginx]# kubectl get pods
  20. NAME READY STATUS RESTARTS AGE
  21. client 0/1 Completed 0 3d23h
  22. my-nginx-854bbd7557-xnbn9 1/1 Running 0 3d3h
  23. test 1/1 Running 0 3s
  24. [root@k8s-master01 nginx]#
  25. [root@k8s-master01 nginx]# kubectl exec -ti test -- ps aux
  26. PID USER TIME COMMAND
  27. 1 1000 0:00 sleep 3600
  28. 6 1000 0:00 ps aux
  29. [root@k8s-master01 nginx]#
  30. #尝试下对Pod进行更新
  31. [root@k8s-master01 nginx]# cat demo.yaml
  32. apiVersion: v1
  33. kind: Pod
  34. metadata:
  35. name: test
  36. spec:
  37. containers:
  38. - name: busybox
  39. image: registry.cn-hangzhou.aliyuncs.com/jiangyida/busybox:0.1
  40. securityContext:
  41. runAsNonRoot: true
  42. runAsUser: 1000
  43. allowPrivilegeEscalation: false
  44. hostNetwork: true
  45. [root@k8s-master01 nginx]# kubectl apply -f demo.yaml
  46. The Pod "test" is invalid: spec: Forbidden: pod updates may not change fields other than `spec.containers[*].image`, `spec.initContainers[*].image`, `spec.activeDeadlineSeconds` or `spec.tolerations` (only additions to existing tolerations)
  47. core.PodSpec{
  48. Volumes: []core.Volume{{Name: "default-token-gvn55", VolumeSource: core.VolumeSource{Secret: &core.SecretVolumeSource{SecretName: "default-token-gvn55", DefaultMode: &420}}}},
  49. InitContainers: nil,
  50. Containers: []core.Container{
  51. {
  52. Name: "busybox",
  53. Image: "registry.cn-hangzhou.aliyuncs.com/jiangyida/busybox:0.1",
  54. - Command: nil,
  55. + Command: []string{"/bin/sh", "-c", "sleep 3600"},
  56. Args: nil,
  57. WorkingDir: "",
  58. ... // 17 identical fields
  59. },
  60. },
  61. EphemeralContainers: nil,
  62. RestartPolicy: "Always",
  63. ... // 24 identical fields
  64. }
  65. [root@k8s-master01 nginx]#

可以看到更新直接报错,提示无全新更新pod中的spec字段