Pod容器應用"優雅發布" - 運維筆記


 

K8S自身帶有優雅終止Pod容器的機制,發送SIGTERM終止信號,在規定的terminationGracePeriodSeconds優雅時間內完成Pod優雅終止動作。

terminationGracePeriodSeconds默認是30秒,該時間是從Pod的Termination狀態開始計算的,包括了Prestop鈎子處理時間、SIGTERM信號發送即程序優雅處理時間。


Pod容器終止流程:
1)新Pod啟動,通過Readiness就緒性探測,加入service的endpoint服務列表。

2)老pod進入Termination狀態,從service的endpoint服務列表摘除,此時不會有新請求打到即將終止的老pod上。

3)如果設置了Prestop鈎子,則優先執行Prestop里的優雅動作。如果在規定的terminationGracePeriodSeconds優雅時間內(默認30s)完成不了,則kubelet會發送SIGTERM終止信號,並等待2秒,如果2秒后還未終止pod容器,則發送SIGKILL信號強制終止。

4)如果沒有設置Prestop鈎子,則發送SIGTERM終止信號優雅關閉容器進程,如果在規定的terminationGracePeriodSeconds優雅時間內(默認30s)未能終止pod容器,則發送SIGKILL信號強制終止。

 

需要注意:

1)SIGTERM終止信號只能被那些pid為1的父進程捕捉到,並優雅關閉容器進程。對於那些pid不為1的子進程是捕捉不到SIGTERM終止信號的。

所以對於單個容器只有一個pid為1的進程來說,使用K8S默認的優雅機制就可以,只需要拉長terminationGracePeriodSeconds優雅時間,確保在規定時間內完成容器優雅終止。

2)對於那些單個容器里有多個進程,即除了pid為1的進程外,還有子進程。這種情況下就需要設置Prestop鈎子函數,在prestop里提前優雅處理掉那些子進程,然后再通過SIGTERM正常終止掉pod容器。

注意設置好terminationGracePeriodSeconds優雅時間。

 

線上基於nacos注冊中心的優雅上線

對於請求通過k8s的service層到達pod容器的情況,可以通過k8s優雅機制來確保pod容器在上線滾動更新期間,做到業務"無感知"。但是目前線上pod容器服務主動注冊到nacos配置中心,業務方通過nacos網關調用pod容器服務,即調用請求繞過了k8s的service層。

這就出現了一個問題:pod容器更新期間,老pod已經優雅終止掉了,但是其ip和端口還在nacos的網關緩存里,調用請求會在nacos網關緩存未完全更新之前打到已經終止掉的pod地址,這就會出現連接超時,調用失敗錯誤,從而造成業務流量損失。

 

正確的做法:

1)拉長terminationGracePeriodSeconds的優雅時間。

2)設置Prestop鈎子,在Pod容器終止之前,在Prestop里通過nacos提供的API接口,主動摘除nacos注冊。接着sleep 30秒時間,用於刷新nacos網關緩存,摘除下線的pod地址。

3)最后再執行pod容器的優雅終止。

 

容器優雅發布的配置記錄:
這里以customer-services應用模塊的pod容器優雅配置為例:


1)將nacos主動下線的腳本在鏡像制作階段推送到容器內部
編寫customer-services主動下線nacos的腳本:

[root@k8s-storage01 ~]# ls /home/k8s_deploy/fin/online/deploy/customer-services/
Dockerfile  service_offline_nacos.sh customer-services.jar

[root@k8s-storage01 ~]# cat /home/k8s_deploy/fin/online/deploy/customer-services/service_offline_nacos.sh
#!/bin/bash
 
serviceName="customer-services"
groupName="kevin-app"
metadata="preserved.register.source=SPRING_CLOUD"
namespaceId="online"
port="9810"
Token=$(curl -s --location --request POST 'http://nacos:8848/nacos/v1/auth/users/login' --form 'username=nacos' --form 'password=nacos'|awk -F"accessToken" '{print $2}'|awk -F":" '{print $2}'|awk -F'"' '{print $2}')
 
# 從nacos注冊中心下線
curl --location --request PUT "http://nacos:8848/nacos/v1/ns/instance?&accessToken=${Token}" \
--form "serviceName=${serviceName}" \
--form "clusterName=DEFAULT" \
--form "groupName=${groupName}" \
--form "metadata=${metadata}" \
--form "namespaceId=${namespaceId}" \
--form "ip=${podip}" \
--form "port=${port}" \
--form "ephemeral=true" \
--form "weight=1" \
--form "enabled=false"
 
# 等待30s,刷新nacos網關緩存
sleep 30

  

制作finhub-customer-services服務的容器鏡像

[root@k8s-storage01 ~]# cat /home/k8s_deploy/fin/online/deploy/customer-services/Dockerfile
FROM 172.16.60.196/kevin/jdk1.8.0_192
RUN rm -f /etc/localtime \
&& ln -sv /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone
 
ENV LANG en_US.UTF-8

COPY customer-services.jar /usr/local/src
COPY service_offline_nacos.sh /opt/
WORKDIR /usr/local/src
EXPOSE 9810
CMD ["nohup","java","-jar","customer-services.jar","&"]

  

制作和推送鏡像

[root@k8s-storage01 ~]# docker build -t 172.16.60.196/kevin/customer-services_v1 . 
[root@k8s-storage01 ~]# docker push 172.16.60.196/kevin/customer-services_v1

  

2)配置pod的yml部署文件,添加env變量動態獲取pod ip地址、拉長terminationGracePeriodSeconds優雅時間、設置prestop鈎子

[root@k8s-master01 customer-services]# pwd
/opt/k8s/k8s-project/kevin/customer-services
 
[root@k8s-master01 customer-services]# cat customer-services.yml
apiVersion: v1
kind: Service
metadata:
  name: customer-services
  namespace: kevin
  labels:
    app: customer-services
spec:
  type: NodePort
  selector:
    app: customer-services
  ports:
  - name: http
    port: 9810
    targetPort: 9810
    nodePort: 39810
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: customer-services
  namespace: kevin
spec:
  replicas: 2
  minReadySeconds: 10
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
    type: RollingUpdate
  selector:
    matchLabels:
      app: customer-services
  template:
    metadata:
      labels:
        app: customer-services
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                      - customer-services
              topologyKey: "kubernetes.io/hostname"
      terminationGracePeriodSeconds: 60
      containers:
      - name: customer-services
        image: 172.16.60.196/kevin/customer-services_v1
        imagePullPolicy: Always
        ports:
        - name: customer-port
          containerPort: 9810
        env:
        - name: podip
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        resources:
          requests:
            cpu: 950m
            memory: 2048Mi
          limits:
            cpu: 1500m
            memory: 4096Mi
        lifecycle:
          postStart:
            exec:
              command: ["/bin/sh","-c","touch /tmp/health"]
          preStop:
            exec:
              command: ["/bin/sh","-c","sh /opt/service_offline_nacos.sh"]
        livenessProbe:
          exec:
            command: ["test","-e","/tmp/health"]
          initialDelaySeconds: 5
          timeoutSeconds: 5
          periodSeconds: 10
        readinessProbe:
          tcpSocket:
            port: customer-port
          initialDelaySeconds: 15
          timeoutSeconds: 5
          periodSeconds: 20
        volumeMounts:
        - name: customerlog
          mountPath: /var/log/customer-services
          readOnly: false
      volumes:
      - name: customerlog
        hostPath:
          path: /var/log/k8s_log/customer-services

  

容器發布流程:

 

生產環境通過Jenkins配置的Pod容器部署流程:即從Gitlab拉取代碼、打包、制作鏡像、上傳鏡像到Harbor倉庫、更新pod等發布流程。完成如上配置,后續通過Jenkins進行"容器應用的優雅"發版了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM