Kubernetes进阶实战(第2版)
上QQ阅读APP看书,第一时间看更新

4.5 容器应用的管理接口

以镜像格式打包并托管运行于编排系统之上的容器就像是一个黑盒子,但任何为运行于云原生环境而开发的应用程序都应该为运行时环境提供监测自身运行状态的API,并支持通过生命周期管理API接收平台的管理事件,这是以统一方式自动化容器更新及生命周期的基本要求和先决条件。

4.5.1 健康状态监测接口

监测容器自身运行的API包括分别用于健康状态检测、指标、分布式跟踪和日志等实现类型,如图4-13所示。即便没有完全实现,至少容器化应用也应该提供用于健康状态检测(liveness和readiness)的API,以便编排系统能更准确地判定应用程序的运行状态。

图4-13 容器的可观测性API

Kubelet仅能在控制循环中根据容器主进程的运行状态来判断其健康与否,主进程以非0状态码退出代表处于不健康状态,其他均为正常状态。然而,有些异常场景中,仍处于运行状态的进程内部的业务处理机制可能已然处于僵死状态或陷入死循环等,无法正常处理业务请求,对于这种状态的判断便要依赖应用自身专用于健康状态监测的接口。

存活状态(liveness)检测用于定期检测容器是否正常运行,就绪状态(readiness)检测用于定期检测容器是否可以接收流量,它们能够通过减少运维问题和提高服务质量来使服务更健壮和更具弹性。Kubernetes在Pod内部的容器资源上提供了livenessProbe和readinessProbe两个字段,分别让用户自定义容器应用的存活状态和就绪状态检测。对于合格的云原生应用,它们可调用容器应用自身定义的相应API完成,而对于不具该类API的传统应用程序,用户也可精心设置一个更能反映其相应状态的系统命令或服务请求完成该功能。

▪存活状态检测:用于判定容器是否处于“运行”状态;若此类检测未通过,kubelet将杀死容器并根据其restartPolicy决定是否将其重启;未定义存活性检测的容器的默认状态为Success。

▪就绪状态检测:用于判断容器是否准备就绪并可对外提供服务;未通过该检测时,端点控制器(例如Service对象)会将其IP地址从所有匹配到此Pod对象的Service对象的端点列表中移除;检测通过之后,会再次将其IP添加至端点列表中;未定义就绪状态检测的容器的默认状态为Success。

容器探测是Pod对象生命周期中的一项重要日常任务,它由kubelet周期性执行。kubelet可在活动容器上分别执行由用户定义的启动状态检测(startupProbe)、存活状态检测(livenessProbe)和就绪状态检测(readinessProbe),定义在容器上的存活状态和就绪状态操作称为检测探针,它要通过容器的句柄(handler)进行定义。Kubernetes定义了用于容器探测的3种句柄。

▪ExecAction:通过在容器中执行一个命令并根据其返回的状态码进行的诊断操作称为Exec探测,状态码为0表示成功,否则即为不健康状态。

▪TCPSocketAction:通过与容器的某TCP端口尝试建立连接进行诊断,端口能够成功打开即为正常状态,否则为不健康状态。

▪HTTPGetAction:通过向容器IP地址的某指定端口的指定path发起http GET请求进行诊断,响应码为2xx或3xx即为成功,否则为失败。

上面的每种探测方式都可能存在3种返回结果:Success(成功)、Failure(失败)或Unknown(未知),仅第一种结果表示成功通过检测。

另外,Kubernetes自v1.16版本起还支持启动状态(startup)检测。将传统模式开发的大型应用程序迁移至容器编排平台运行时,可能需要相当长的时间进行启动后的初始化,但其初始过程是否正确完成的检测机制和探测参数都可能有别于存活状态检测,例如需要更长的间隔周期和更高的错误阈值等。该类检测的结果处理机制与存活状态检测相同,检测失败时kubelet将杀死容器并根据其restartPolicy决定是否将其重启,而未定义时的默认状态为Success。需要注意的是,一旦定义了启动检测探针,则必须等启动检测成功完成之后,存活探针和就绪探针才可启动。

4.5.2 容器存活状态检测

有些应用程序因存在缺陷(例如多线程导致的应用程序死锁等)会在长时间持续运行后逐渐转为不可用状态,并且仅能通过重启操作恢复,Kubernetes的容器存活性探测机制可发现诸如此类的问题,并依据探测结果结合重启策略触发后续的行为。存活性探测是隶属于容器级别的配置,kubelet可基于它判定何时需要重启容器。目前,Kubernetes在容器上支持的存活探针有3种类型:ExecAction、TCPSocketAction和HTTPGetAction。

1. 存活探针配置格式

Pod配置格式中,spec.containers.livenessProbe字段用于定义此类检测,配置格式如下所示。但一个容器之上仅能定义一种类型的探针,即exec、httpGet和tcpSocket三者互斥,它们不可在一个容器同时使用。


spec:
  containers:
  - name: …
    image: …
    livenessProbe:
      exec <Object>                   # 命令式探针
      httpGet <Object>                # http GET类型的探针
      tcpSocket <Object>              # tcp Socket类型的探针
      initialDelaySeconds <integer>   # 发起初次探测请求的延后时长
      periodSeconds <integer>         # 请求周期
      timeoutSeconds <integer>        # 超时时长
      successThreshold <integer>      # 成功阈值
      failureThreshold <integer>      # 失败阈值

探针之外的其他字段用于定义探测操作的行为方式,用户没有明确定义这些属性字段时,它们会使用各自的默认值,各字段的详细说明如下。

▪initialDelaySeconds <integer>:首次发出存活探测请求的延迟时长,即容器启动多久之后开始第一次探测操作,显示为delay属性;默认为0秒,即容器启动后便立刻进行探测;该参数值应该大于容器的最大初始化时长,以避免程序永远无法启动。

▪timeoutSeconds <integer>:存活探测的超时时长,显示为timeout属性,默认为1秒,最小值也为1秒;应该为此参数设置一个合理值,以避免因应用负载较大时的响应延迟导致Pod被重启。

▪periodSeconds <integer>:存活探测的频度,显示为period属性,默认为10秒,最小值为1秒;需要注意的是,过高的频率会给Pod对象带来较大的额外开销,而过低的频率又会使得对错误反应不及时。

▪successThreshold <integer>:处于失败状态时,探测操作至少连续多少次的成功才被认为通过检测,显示为#success属性,仅可取值为1。

▪failureThreshold:处于成功状态时,探测操作至少连续多少次的失败才被视为检测不通过,显示为#failure属性,默认值为3,最小值为1;尽量设置宽容一些的失败计数,能有效避免一些场景中的服务级联失败。

使用kubectl describe命令查看配置了存活性探测的Pod对象的详细信息时,其相关容器中会输出类似如下一行内容,它给出了探测方式及其额外的配置属性delay、timeout、period、success和failure及其各自的相关属性值。


Liveness:  …… delay=0s timeout=1s period=10s #success=1 #failure=3

2. exec探针

exec类型的探针通过在目标容器中执行由用户自定义的命令来判定容器的健康状态,命令状态返回值为0表示“成功”通过检测,其余值均为“失败”状态。spec.containers.livenessProbe.exec字段只有一个可用属性command,用于指定要执行的命令。

demoapp应用程序通过/livez输出内置的存活状态检测接口,服务正常时,它以200响应码返回OK,否则为5xx响应码,我们可基于exec探针使用HTTP客户端向该path发起请求,并根据命令的结果状态来判定容器健康与否。系统刚启动时,对该路径的请求将会延迟大约不到5秒的时长,且默认响应值为OK。它还支持由用户按需向该路径发起POST请求,并向参数livez传值来自定义其响应内容。下面是定义在资源清单文件liveness-exec-demo.yaml中的示例。


apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec-demo
  namespace: default
spec:
  containers:
  - name: demo
    image: ikubernetes/demoapp:v1.0
    imagePullPolicy: IfNotPresent
    livenessProbe:
      exec:
        command: ['/bin/sh', '-c', '[ "$(curl -s 127.0.0.1/livez)" == "OK" ]']
      initialDelaySeconds: 5
      periodSeconds: 5

该配置清单中定义的Pod对象为demo容器定义了exec探针,它通过在容器本地执行测试命令来比较curl -s 127.0.0.1/livez的返回值是否为OK以判定容器的存活状态。命令成功执行则表示容器正常运行,否则3次检测失败之后则将其判定为检测失败。首次检测在容器启动5秒之后进行,请求间隔也是5秒。


~$ kubectl apply -f liveness-exec-demo.yaml 
pod/liveness-exec-demo created

创建完成后,Pod中的容器demo会正常运行,存活检测探针也不会遇到检测错误而导致容器重启。若要测试存活状态检测的效果,可以手动将/livez的响应内容修改为OK之外的其他值,例如FAIL。


~$ kubectl exec liveness-exec-demo -- curl -s -X POST -d 'livez=FAIL' 127.0.0.1/livez

而后经过1个检测周期,可通过Pod对象的描述信息来获取相关的事件状态,例如,由下面命令结果中的事件可知,容器因健康状态检测失败而被重启。


~$ kubectl describe pods/liveness-exec-demo
……
Events: 
Warning  Unhealthy  17s (x3 over 27s)  kubelet, k8s-node03.ilinux.io  Liveness probe failed:
Normal   Killing     17s             kubelet, k8s-node03.ilinux.io  Container demo failed liveness probe, will be restarted

另外,下面输出信息中的Containers一段中还清晰显示了容器健康状态检测及状态变化的相关信息:容器当前处于Running状态,但前一次是为Terminated,原因是退出码为137的错误信息,它表示进程是被外部信号所终止。137事实上由两部分数字之和生成:128+signum,其中signum是导致进程终止的信号的数字标识,9表示SIGKILL,这意味着进程是被强行终止的。


Containers:
  demo:
    ……
    State:          Running
      Started:      Thu, 29 Aug 2020 14:30:02 +0800
    Last State:     Terminated
      Reason:       Error
      Exit Code:    137
      Started:      Thu, 29 Aug 2020 14:22:20 +0800
      Finished:     Thu, 29 Aug 2020 14:30:02 +0800
    Ready:          True
    Restart Count:  1
……

待容器重启完成后,/livez的响应内容会重置镜像中默认定义的OK,因而其存活状态检测不会再遇到错误,这模拟了一种典型的通过“重启”应用而解决问题的场景。需要特别说明的是,exec指定的命令运行在容器中,会消耗容器的可用计算资源配额,另外考虑到探测操作的效率等因素,探测操作的命令应该尽可能简单和轻量。

3. HTTP探针

HTTP探针是基于HTTP协议的探测(HTTPGetAction),通过向目标容器发起一个GET请求,并根据其响应码进行结果判定,2xx或3xx类的响应码表示检测通过。HTTP探针可用配置字段有如下几个。

▪host <string>:请求的主机地址,默认为Pod IP;也可以在httpHeaders使用“Host:”来定义。

▪port <string>:请求的端口,必选字段。

▪httpHeaders <[]Object>:自定义的请求报文头部。

▪path <string>:请求的HTTP资源路径,即URL path。

▪scheme:建立连接使用的协议,仅可为HTTP或HTTPS,默认为HTTP。

下面是一个定义在资源清单文件liveness-httpget-demo.yaml中的示例,它使用HTTP探针直接对/livez发起访问请求,并根据其响应码来判定检测结果。


apiVersion: v1
kind: Pod
metadata:
  name: liveness-httpget-demo
  namespace: default
spec:
  containers:
  - name: demo
    image: ikubernetes/demoapp:v1.0
    imagePullPolicy: IfNotPresent
    livenessProbe:
      httpGet:
        path: '/livez'
        port: 80
        scheme: HTTP
      initialDelaySeconds: 5

上面清单文件中定义的httpGet测试中,请求的资源路径为/livez,地址默认为Pod IP,端口使用了容器中定义的端口名称http,这也是明确为容器指明要暴露的端口的用途之一。下面测试其效果,首先创建此Pod对象:


~ $ kubectl apply -f liveness-httpget-demo.yaml
pod/liveness-httpget-demo created

首次检测为延迟5秒,这刚好超过了demoapp的/livez接口默认会延迟响应的时长。镜像中定义的默认响应是以200状态码响应、以OK为响应结果,存活状态检测会成功完成。为了测试存活状态检测的效果,同样可以手动将/livez的响应内容修改为OK之外的其他值,例如FAIL。


~$ kubectl exec liveness-httpget-demo -- curl -s -X POST -d 'livez=FAIL' 127.0.0.1/livez

而后经过至少1个检测周期后,可通过Pod对象的描述信息来获取相关的事件状态,例如,由下面命令的结果中的事件可知,容器因健康状态检测失败而被重启。


~ $ kubectl describe pods/liveness-httpget-demo
……
Warning  Unhealthy  7s (x3 over 27s)  kubelet, k8s-node01.ilinux.io  Liveness probe failed: HTTP probe failed with statuscode: 520
  Normal   Killing    7s          kubelet, k8s-node01.ilinux.io  Container demo failed liveness probe, will be restarted

一般来说,HTTP探针应该针对专用的URL路径进行,例如前面示例中特别为其准备的/livez,此URL路径对应的Web资源也应该以轻量化的方式在内部对应用程序的各关键组件进行全面检测,以确保它们可正常向客户端提供完整的服务。

需要注意的是,这种检测方式仅对分层架构中的当前一层有效,例如,它能检测应用程序工作正常与否的状态,但重启操作却无法解决其后端服务(例如数据库或缓存服务)导致的故障。此时,容器可能会被反复重启,直到后端服务恢复正常。其他两种检测方式也存在类似的问题。

4. TCP探针

TCP探针是基于TCP协议进行存活性探测(TCPSocketAction),通过向容器的特定端口发起TCP请求并尝试建立连接进行结果判定,连接建立成功即为通过检测。相比较来说,它比基于HTTP协议的探测要更高效、更节约资源,但精准度略低,毕竟连接建立成功未必意味着页面资源可用。

spec.containers.livenessProbe.tcpSocket字段用于定义此类检测,它主要有以下两个可用字段:

1)host <string>:请求连接的目标IP地址,默认为Pod自身的IP;

2)port <string>:请求连接的目标端口,必选字段,可以名称调用容器上显式定义的端口。

下面是一个定义在资源清单文件liveness-tcpsocket-demo.yaml中的示例,它向Pod对象的TCP协议的80端口发起连接请求,并根据连接建立的状态判定测试结果。为了能在容器中通过iptables阻止接收对80端口的请求以验证TCP检测失败,下面的配置还在容器上启用了特殊的内核权限NET_ADMIN。


apiVersion: v1
kind: Pod
metadata:
  name: liveness-tcpsocket-demo
  namespace: default
spec:
  containers:
  - name: demo
    image: ikubernetes/demoapp:v1.0
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    securityContext:
      capabilities:
        add:
        - NET_ADMIN
    livenessProbe:
      tcpSocket:
        port: http
      periodSeconds: 5
      initialDelaySeconds: 20

按照配置,将该清单中的Pod对象创建在集群之上,20秒之后即会进行首次的tcpSocket检测。


~$ kubectl apply -f liveness-tcpsocket-demo.yaml                                      
pod/liveness-tcpsocket-demo created

容器应用demoapp启动后即监听于TCP协议的80端口,tcpSocket检测也就可以成功执行。为了测试效果,可使用下面的命令在Pod的Network名称空间中设置iptables规则以阻止对80端口的请求:


~$ kubectl exec liveness-tcpsocket-demo -- iptables -A INPUT -p tcp --dport 80 -j REJECT

而后经过至少1个检测周期后,可通过Pod对象的描述信息来获取相关的事件状态,例如,由下面命令的结果中的事件可知,容器因健康状态检测失败而被重启。


~ $ kubectl describe pods/liveness-httpget-demo
……
Events:
……
Warning  Unhealthy  3s (x3 over 23s)  kubelet, k8s-node03.ilinux.io  Liveness probe failed: dial tcp 10.244.3.19:80: i/o timeout
    Normal   Killing    3s      kubelet, k8s-node03.ilinux.io  Container demo     failed liveness probe, will be restarted

不过,重启容器并不会导致Pod资源的重建操作,网络名称空间的设定附加在pause容器之上,因而添加的iptables规则在应用重启后依然存在,它是一个无法通过重启而解决的问题。若需要手消除该问题,删除添加至Pod中的iptables规则即可。

4.5.3 Pod的重启策略

Pod对象的应用容器因程序崩溃、启动状态检测失败、存活状态检测失败或容器申请超出限制的资源等原因都可能导致其被终止,此时是否应该重启则取决于Pod上的restartPolicy(重启策略)字段的定义,该字段支持以下取值。

1)Always:无论因何原因、以何种方式终止,kubelet都将重启该Pod,此为默认设定。

2)OnFailure:仅在Pod对象以非0方式退出时才将其重启。

3)Never:不再重启该Pod。

需要注意的是,restartPolicy适用于Pod对象中的所有容器,而且它仅用于控制在同一个节点上重新启动Pod对象的相关容器。首次需要重启的容器,其重启操作会立即进行,而再次重启操作将由kubelet延迟一段时间后进行,反复的重启操作的延迟时长依次为10秒、20秒、40秒、80秒、160秒和300秒,300秒是最大延迟时长。

事实上,一旦绑定到一个节点,Pod对象将永远不会被重新绑定到另一个节点,它要么被重启,要么被终止,直到节点故障、被删除或被驱逐。

4.5.4 容器就绪状态检测

Pod对象启动后,应用程序通常需要一段时间完成其初始化过程,例如加载配置或数据、缓存初始化等,甚至有些程序需要运行某类预热过程等,因此通常应该避免在Pod对象启动后立即让其处理客户端请求,而是需要等待容器初始化工作执行完成并转为“就绪”状态,尤其是存在其他提供相同服务的Pod对象的场景更是如此。

与存活探针不同的是,就绪状态检测是用来判断容器应用就绪与否的周期性(默认周期为10秒钟)操作,它用于检测容器是否已经初始化完成并可服务客户端请求。与存活探针触发的操作不同,检测失败时,就绪探针不会杀死或重启容器来确保其健康状态,而仅仅是通知其尚未就绪,并触发依赖其就绪状态的其他操作(例如从Service对象中移除此Pod对象),以确保不会有客户端请求接入此Pod对象。

就绪探针也支持Exec、HTTP GET和TCP Socket这3种探测方式,且它们各自的定义机制与存活探针相同。因而,将容器定义中的livenessProbe字段名替换为readinessProbe,并略做适应性修改即可定义出就绪性检测的配置来,甚至有些场景中的就绪探针与存活探针的配置可以完全相同。

demoapp应用程序通过/readyz暴露了专用于就绪状态检测的接口,它于程序启动约15秒后能够以200状态码响应、以OK为响应结果,也支持用户使用POST请求方法通过readyz参数传递自定义的响应内容,不过所有非OK的响应内容都被响应以5xx的状态码。一个简单的示例如下面的配置清单(readiness-httpget-demo.yaml)所示。


apiVersion: v1
kind: Pod
metadata:
  name: readiness-httpget-demo
  namespace: default
spec:
  containers:
  - name: demo
    image: ikubernetes/demoapp:v1.0
    imagePullPolicy: IfNotPresent
    readinessProbe:
      httpGet:
        path: '/readyz'
        port: 80
        scheme: HTTP
      initialDelaySeconds: 15
      timeoutSeconds: 2
      periodSeconds: 5
      failureThreshold: 3
  restartPolicy: Always

下面来测试该Pod就绪探针的作用。按照配置,将Pod对象创建在集群上约15秒后启动首次探测,在该探测结果成功返回之前,Pod将一直处于未就绪状态:


~ $ kubectl apply -f readiness-httpget-demo.yaml
pod/readiness-httpget-demo created

接着运行kubectl get -w命令监视其资源变动信息,由如下命令结果可知,尽管Pod对象处于Running状态,但直到就绪检测命令执行成功后Pod资源才转为“就绪”。


~$ kubectl get pods/readiness-httpget-demo -w
NAME                     READY   STATUS    RESTARTS   AGE
readiness-httpget-demo   0/1     Running   0          10s
readiness-httpget-demo   1/1     Running   0          20s

Pod运行过程中的某一时刻,无论因何原因导致的就绪状态检测的连续失败都会使得该Pod从就绪状态转变为“未就绪”,并且会从各个通过标签选择器关联至该Pod对象的Service后端端点列表中删除。为了测试就绪状态检测效果,下面修改/readyz响应以非OK内容。


~$ kubectl exec readiness-httpget-demo -- curl -s -XPOST -d 'readyz=FAIL' 127.0.0.1/readyz

而后在至少1个检测周期之后,通过该Pod的描述信息可以看到就绪检测失败相关的事件描述,命令及结果如下所示:


~$ kubectl describe pods/readiness-httpget-demo
……
Warning  Unhealthy  4s (x11 over 54s)  kubelet, k8s-node03.ilinux.io  Readiness probe failed: HTTP probe failed with statuscode: 521

这里要特别提醒读者的是,未定义就绪性检测的Pod对象在进入Running状态后将立即“就绪”,这在容器需要时间进行初始化的场景中可能会导致客户请求失败。因此,生产实践中,必须为关键性Pod资源中的容器定义就绪探针。

4.5.5 容器生命周期

许多编程语言框架都实现了生命周期管理的概念,其主要用于指定平台如何与它创建的组件在启动之后或停止之前进行交互。实现这类功能很重要,毕竟有时我们可能需要在Pod上执行一些操作,例如测试同一个或多个依赖项的连接性,以及在销毁Pod之前进行一些清理活动等。容器应用生命周期管理是指它可从平台接收管理事件并执行相应的操作,以便于平台能够更好地管理容器的生命周期机制,因而也称为容器生命周期管理API。

图4-14为容器生命周期接口工作示意图。

图4-14 容器生命周期接口

容器需要处理来自平台的最重要事件是SIGTERM信号,任何需要“干净”关闭进程的应用程序都需要捕捉该信号进行必要处理,例如释放文件锁、关闭数据库连接和网络连接等,而后尽快终止进程,以避免宽限期过后强制关闭信号SIGKILL的介入。SIGKILL信号是由底层操作系统接收的,而非应用进程,一旦检测到该信号,内核将停止为相应进程提供内核资源,并终止进程正在使用的所有CPU线程,类似于直接切断了进程的电源。

但是,容器应用很可能是功能复杂的分布式应用程序的一个组件,仅依赖信号终止进程很可能不足以完成所有的必要操作。因此,容器还需要支持postStart和preStop事件,前者常用于为程序启动前进行预热,后者则一般在“干净”地关闭应用之前释放占用的资源。

生命周期钩子函数lifecycle hook是编程语言(例如Angular)中常用的生命周期管理组件,它实现了程序运行周期中的关键时刻的可见性,并赋予用户为此采取某种行动的能力。类似地,容器生命周期钩子使它能够感知自身生命周期管理中的事件,并在相应时刻到来时运行由用户指定的处理程序代码。Kubernetes同样为容器提供了postStart和preStop两种生命周期钩子。

▪postStart:在容器创建完成后立即运行的钩子句柄(handler),该钩子定义的事件执行完成后容器才能真正完成启动过程,如图4-15中的左图所示;不过Kubernetes无法确保它一定会在容器的主应用程序(由ENTRYPOINT定义)之前运行。

▪preStop:在容器终止操作执行之前立即运行的钩子句柄,它以同步方式调用,因此在其完成之前会阻塞删除容器的操作;这意味着该钩子定义的事件成功执行并退出,容器终止操作才能真正完成,如图4-15中的右图所示。

图4-15 postStart和preStop

钩子句柄的实现方式类似于容器探针句柄的类型,同样有exec、httpGet和tcpSocket这3种,它们各自的配置格式和工作逻辑也完全相同,exec在容器中执行用户定义的一个或多个命令,httpGet在容器中向指定的本地URL发起HTTP连接请求,而tcpSocket则试图与指定的端口建立TCP连接。

postStart和preStop句柄定义在容器的lifecycle字段中,其内部一次仅支持嵌套使用一种句柄类型。下面的配置清单(lifecycle-demo.yaml)示例中同时使用了postStart和preStop钩子处理相应的事件。


apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
  namespace: default
spec:
  containers:
  - name: demo
    image: ikubernetes/demoapp:v1.0
    imagePullPolicy: IfNotPresent
    securityContext:
      capabilities:
        add:
        - NET_ADMIN
    livenessProbe:
      httpGet:
        path: '/livez'
        port: 80
        scheme: HTTP
      initialDelaySeconds: 5
    lifecycle:
      postStart:
        exec:
          command: ['/bin/sh','-c','iptables -t nat -A PREROUTING -p tcp 
          --dport 8080 -j REDIRECT --to-ports 80']
      preStop:
        exec:
          command: ['/bin/sh','-c','while killall python3; do sleep 1; done']
  restartPolicy: Always

示例中的demo容器通过postStart执行iptables命令设置端口重定向规则,将发往该Pod IP的8080端口的所有请求重定向至80端口,从而让容器应用能够同时从8080端口接收请求。demo容器又借助preStop执行killall命令,它假设该命令能够更优雅地终止基于Python3运行的容器应用demoapp。将清单中的Pod对象创建于集群中便可展开后续的测试:


~$ kubectl apply -f lifecycle-demo.yaml 
pod/lifecycle-demo created

而后可获取容器内网络名称空间中PREROUTING链上的iptables规则,验证postStart钩子事件的执行结果:


~$ kubectl exec lifecycle-demo -- iptables -t nat -nL PREROUTING  
Chain PREROUTING (policy ACCEPT)
target     prot opt     source    destination         
REDIRECT   tcp  --  0.0.0.0/0    0.0.0.0/0    tcp dpt:8080 redir ports 80

上面的配置清单中有意同时添加了httpGet类型的存活探针,我们可以人为地将探针检测结果置为失败状态,以促使kubelet重启demo容器验证preStop钩子事件的执行。不过,该示例中给出的操作是终止容器应用,那么容器成功重启即验证了相应脚本的运行完成。