1.概述
類似于許多具有組件生命周期鉤子的編程語言框架怎憋,例如Angular扛或,Kubernetes為Containers提供了生命周期鉤子敲街。 鉤子使Container能夠了解其管理生命周期中的事件闷愤,并在執(zhí)行相應(yīng)的生命周期鉤子時運行在處理程序中實現(xiàn)的代碼砌创。
2.容器鉤子(以下為官方原文)
There are two hooks that are exposed to Containers:
PostStart
This hook executes immediately after a container is created. However, there is no guarantee that the hook will execute before the container ENTRYPOINT. No parameters are passed to the handler.
PreStop
This hook is called immediately before a container is terminated due to an API request or management event such as liveness probe failure, preemption, resource contention and others. A call to the preStop hook fails if the container is already in terminated or completed state. It is blocking, meaning it is synchronous, so it must complete before the call to delete the container can be sent. No parameters are passed to the handler.
A more detailed description of the termination behavior can be found in Termination of Pods.
主要注意以下幾點:
- PostStart hook是在容器創(chuàng)建(created)之后立馬被調(diào)用,并且PostStart跟容器的ENTRYPOINT是異步執(zhí)行的社牲,無法保證它們之間的順序
- PreStop hook是容器處于Terminated狀態(tài)時立馬被調(diào)用(也就是說要是Job任務(wù)的話粪薛,執(zhí)行完之后其狀態(tài)為completed悴了,所以不會觸發(fā)PreStop的鉤子)搏恤,同時PreStop是同步阻塞的,PreStop執(zhí)行完才會執(zhí)行刪除Pod的操作
3.Hook handler execution
When a Container lifecycle management hook is called, the Kubernetes management system executes the handler in the Container registered for that hook.
Hook handler calls are synchronous within the context of the Pod containing the Container. This means that for a PostStart hook, the Container ENTRYPOINT and hook fire asynchronously. However, if the hook takes too long to run or hangs, the Container cannot reach a running state.
The behavior is similar for a PreStop hook. If the hook hangs during execution, the Pod phase stays in a Terminating state and is killed after terminationGracePeriodSeconds of pod ends. If a PostStart or PreStop hook fails, it kills the Container.
Users should make their hook handlers as lightweight as possible. There are cases, however, when long running commands make sense, such as when saving state prior to stopping a Container.
主要注意以下幾點:
- PostStart會阻塞容器成為Running狀態(tài)
- PreStop會阻塞容器的刪除湃交,但是過了terminationGracePeriodSeconds時間后熟空,容器會被強制刪除
- 如果PreStop或者PostStart失敗的話, 容器會被殺死
4.Example
apiVersion: v1
kind: Pod
metadata:
name: test-post-start
spec:
containers:
- name: test-post-start-container1
image: busybox
command: ["/bin/sh", "-c", "sleep 600"]
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "sleep 20"]
- name: test-post-start-container
image: busybox
command: ["/bin/sh", "-c", "echo $(date) 'written by entrypoint' >> log.log && sleep 600"]
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo $(date) 'written by post start' >> log.log && sleep 5"]
部署這個pod之后,通過kubectl describe pod test-post-start后得到以下結(jié)果
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 1m default-scheduler Successfully assigned default/test-post-start to node012060
Normal Pulling 59s kubelet, node012060 pulling image "busybox"
Normal Pulled 54s kubelet, node012060 Successfully pulled image "busybox"
Normal Created 54s kubelet, node012060 Created container
Normal Started 54s kubelet, node012060 Started container
Normal Pulling 34s kubelet, node012060 pulling image "busybox"
Normal Pulled 29s kubelet, node012060 Successfully pulled image "busybox"
Normal Created 29s kubelet, node012060 Created container
Normal Started 29s kubelet, node012060 Started container
# 進入到容器里面查看log.log
Thu Jun 13 07:44:57 UTC 2019 written by entrypoint
Thu Jun 13 07:44:57 UTC 2019 written by post start
從這個例子我們可以得到以下結(jié)論
- 一個Pod中容器的啟動時有順序的搞莺,排在前面容器的先啟動息罗。同時第一個容器執(zhí)行完ENTRYPOINT和PostStart之后,k8s才會創(chuàng)建第二個容器(這樣的話就可以保證第一個容器創(chuàng)建多長時間后再啟動第二個容器)
- 并且PostStart跟容器的ENTRYPOINT是異步執(zhí)行的才沧,無法保證它們之間的順序, PostStart并不會阻塞ENTRYPOINT的啟動
5.鉤子使用的具體例子
使用 prestop hook 保證服務(wù)安全退出
在實際生產(chǎn)環(huán)境中使用spring框架迈喉,由于服務(wù)更新過程中绍刮,服務(wù)容器被直接終止,部分請求仍然被分發(fā)到終止的容器挨摸,導(dǎo)致出現(xiàn)500錯誤孩革,這部分錯誤的請求數(shù)據(jù)占比較少,也可以忽略得运。
考慮添加優(yōu)雅的終止方式膝蜈,將錯誤請求降到最低,直至沒有錯誤出現(xiàn)熔掺。
這里介紹 spring cloud 的服務(wù)發(fā)現(xiàn)組件:
Eureka 是一個基于 REST 的服務(wù)饱搏,作為服務(wù)注冊中心,用于定位服務(wù)來進行中間層服務(wù)器的負載均衡和故障轉(zhuǎn)移置逻。各服務(wù)啟動時,會向Eureka Server注冊自己的信息(IP,端口,服務(wù)信息等),Eureka Server會存儲這些信息.微服務(wù)啟動后,會周期性(默認30秒)的向Eureka Server發(fā)送心跳以續(xù)約自己的”租期”推沸,并可以從eureka中獲取其他微服務(wù)的地址信息,執(zhí)行相關(guān)的邏輯考慮現(xiàn)在eureka server 修改注冊實例的狀態(tài)券坞,暫停服務(wù)( InstanceStatus.OUT_OF_SERVICE )坤学,保留一段時間后,再刪除服務(wù)报慕。
禁用某個服務(wù):
curl -X PUT “http://admin:admin@192.168.101.100:8761/eureka/apps/{appName}/{instanceId}/status?value=OUT_OF_SERVICE"
說明:admin:admin是eureka的登錄名和密碼深浮,如果沒有,直接去掉前面這段眠冈;
instanceId是上面打開的鏈接顯示的服務(wù)列表中的標簽內(nèi)容,如:myapp:192.168.1.100:8080
在k8s 中的具體操作:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: NAME-service-deployment
spec:
replicas: 3
selector:
matchLabels:
app: NAME-service
template:
metadata:
labels:
app: NAME-service
spec:
containers:
- name: NAME-service
lifecycle:
preStop:
exec:
command:
- "/bin/sh"
- "-c"
- " \
APPLICATION=NAME-service; \
APPLICATION_PORT=8016; \
curl -s -X PUT http://eureka01-server.domain.com/eureka/apps/${APPLICATION}/$(hostname):${APPLICATION}:${APPLICATION_PORT}/status?value=OUT_OF_SERVICE; \
sleep 30; \
"
刪除了無用的信息飞苇,重點關(guān)注 lifecycle,首先定義了服務(wù)名和端口的環(huán)境變量蜗顽,把這部分單獨作為變量布卡,便于不同的服務(wù)進行修改。使用 curl PUT 到eureka 配置狀態(tài)為 OUT_OF_SERVICE雇盖。配置一個sleep時間忿等,作為服務(wù)停止緩沖時間。