使用k8s容器鉤子確保服務安全退出

Kubernetes爲容器提供了生命週期鉤子。
鉤子能使容器感知其生命週期內的事件,而且當相應的生命週期鉤子被調用時運行指定的代碼。nginx

@[TOC]git

容器生命週期的鉤子

Kubernetes爲容器提供了生命週期鉤子。
鉤子能使容器感知其生命週期內的事件,而且當相應的生命週期鉤子被調用時運行指定的代碼。github

容器鉤子分爲兩類觸發點:容器建立後PostStart和容器終止前PreStop。web

PostStart

這個鉤子在容器建立後當即執行。
可是,並不能保證鉤子將在容器ENTRYPOINT以前運行。
沒有參數傳遞給處理程序。spring

容器ENTRYPOINT和鉤子執行是異步操做。
若是鉤子花費太長時間以致於容器不能運行或者掛起, 容器將不能達到running狀態api

PreStop

這個鉤子在容器終止以前當即被調用。
它是阻塞的,意味着它是同步的, 因此它必須在刪除容器的調用發出以前完成安全

若是鉤子在執行期間掛起, Pod階段將停留在running狀態而且永不會達到failed狀態。服務器

若是PostStart或者PreStop鉤子失敗, 容器將會被kill。
用戶應該使他們的鉤子處理程序儘量的輕量。
app

鉤子處理程序的實現

容器能夠經過實現和註冊該鉤子的處理程序來訪問鉤子。
能夠爲容器實現兩種類型的鉤子處理程序:負載均衡

  • Exec - 在容器的cgroups和命名空間內執行一個特定的命令,好比pre-stop.sh。
    該命令消耗的資源被計入容器。
  • HTTP - 對容器上的特定的端點執行HTTP請求。

在Pod的事件中沒有鉤子處理程序的日誌。 若是一個處理程序由於某些緣由運行失敗,它廣播一個事件。
對於PostStart, 這是FailedPostStartHook事件, 對於PreStop, 這是FailedPreStopHook事件。
你能夠經過運行kubectl describe pod <pod_name>來查看這些事件。

定義預啓動和預結束事件操做

下面將會建立含有一個容器的Pod,咱們將會給這個容器設置預啓動和預結束操做。
https://raw.githubusercontent.com/kubernetes/website/master/docs/tasks/configure-pod-container/lifecycle-events.yaml

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/usr/sbin/nginx","-s","quit"]

使用 prestop hook 保證服務安全退出

在實際生產環境中使用spring框架,因爲服務更新過程當中,服務容器被直接終止,部分請求仍然被分發到終止的容器,致使出現500錯誤,這部分錯誤的請求數據佔比較少,也能夠忽略。
考慮添加優雅的終止方式,將錯誤請求降到最低,直至沒有錯誤出現。

這裏介紹 spring cloud 的服務發現組件:
Eureka 是一個基於 REST 的服務,做爲服務註冊中心,用於定位服務來進行中間層服務器的負載均衡和故障轉移。
各服務啓動時,會向Eureka Server註冊本身的信息(IP,端口,服務信息等),Eureka Server會存儲這些信息.
微服務啓動後,會週期性(默認30秒)的向Eureka Server發送心跳以續約本身的」租期」,並能夠從eureka中獲取其餘微服務的地址信息,執行相關的邏輯。
imageimage

考慮如今eureka server 修改註冊實例的狀態,暫停服務( InstanceStatus.OUT_OF_SERVICE ),保留一段時間後,再刪除服務。

禁用某個服務:
curl -X PUT 「http://admin:admin@192.168.101.100:8761/eureka/apps/{appName}/{instanceId}/status?value=OUT_OF_SERVICE"

說明:admin:admin是eureka的登陸名和密碼,若是沒有,直接去掉前面這段;
instanceId是上面打開的連接顯示的服務列表中的標籤內容,如: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; \
                  "

刪除了無用的信息,重點關注 lifecycle
首先定義了服務名和端口的環境變量,把這部分單獨做爲變量,便於不一樣的服務進行修改。
使用 curl PUT 到eureka 配置狀態爲 OUT_OF_SERVICE。
配置一個sleep時間,做爲服務中止緩衝時間。

參考鏈接

  1. 容器生命週期的鉤子
  2. Pods 的終止
  3. 給容器生命週期設置操做事件
  4. eureka服務禁用
相關文章
相關標籤/搜索