K8s 裡多容器 Pod 的健康檢查探針工作機制分析

胡說雲原生發表於2023-12-12

1. 開篇

米娜桑,寶子們,ladies and 磚頭們…… 總之,我回來了!

你看這標題,沒錯,K8s 的。兜轉兩載,我還是決定從“DevOps 工程師”變回“機器學習平臺研發工程師”。直觀一點講,就是“雲平臺開發”那點事配上 GPU 那點料,是不是很好理解?

Anyway,以後又有機會玩 K8s 了,所以接下來我會繼續更新和 K8s 或者“機器學習平臺”相關的內容。總之總之,你們蹲了那麼久的更新,來了!

2. 聊啥

今天有個同事問我:在1個 Pod 中跑多個 Container,如果其中一個掛了,另外一個會怎樣?

嗯…… 我記得大概,不過沒有確切的結論,這個取決於 probes 是怎麼工作的,於是我實測了一下,發現和預期不是完全一致。

於是乎,今天和大夥分享下這個知識點。

3. 結論(TL;DR)

對,結論在開頭。畢竟,我知道你們都很忙。


一番操作猛如虎,然後我發現:

當1個 Pod 中包含2個 Containers 的時候,如果2個 Containers 分別對外暴露不同的埠(http 服務為例),當其中有1個 Container 的:

  1. Liveness probe 失敗,則該 Container 會被 Kubelet 幹掉,然後該 Container 重啟/重建(當然,你的重啟策略得是 Always),另外一個 Container 不會重啟(也就是不會發生 Pod 級別的重啟/重建,因此 Pod IP 也不會變化);
  2. Readiness probe 失敗,這時候 Pod 狀態裡的 Ready 列自然是1/2,關鍵是 Service 會怎樣工作呢?
    1. 當使用1個 Service 負載流量到這個 Pod 的2個埠時,Service 對應的 Endpoint 會變成 NotReady,導致 Pod 中 ready 的那個 Container 也不能透過 Service 地址訪問到;
    2. 當使用2個不同的 Service 分別負載流量到這個 Pod 的2個埠時,很遺憾,對應的2個 Endpoint 均會變成 NotReady,而不是1個 Ready,一個 NotReady。(這是和我最開始的猜測不相符的)

4. 測試過程

你居然看到了這裡,寶子,你是個求知慾很強的孩子啊!

4.1 準備測試用映象

我想用 NGINX 映象來完成這次 probes 特性測試,但是要讓2個 containers 在1個 Pod 裡監聽不同的埠,那就得重新打下映象,包一層了。

1. 首先,準備一個配置檔案

  • default.conf
server {
    listen 8080;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}

2. 然後準備一個 Dockerfile

  • Dockerfile
FROM nginx

RUN rm /etc/nginx/conf.d/default.conf

COPY default.conf /etc/nginx/conf.d/

EXPOSE 8080

注意到這裡我們將埠號指定成了8080。

3. 接著 build 一下

docker build -t my-nginx-8080:1.0 .

很酷,第一個映象有了。然後我們需要繼續搞一個監聽8081埠的新映象。

4. 更新配置檔案

  • default.conf
server {
    listen 8081;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}

5. 更新 Dockerfile

FROM nginx

RUN rm /etc/nginx/conf.d/default.conf

COPY default.conf /etc/nginx/conf.d/

EXPOSE 8081

6. build 第二個映象

docker build -t my-nginx-8081:1.0 .

OK,到這裡2個映象就準備完成了。接著如何將映象丟到 K8s worker 節點,大家就各顯神通吧,透過映象倉庫也行,手動複製也罷。

4.2 準備 Deployment YAML

首先跑一個 probe 能過的版本,確保“1 Pod 2 Container”啟起來。

  • deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx-container1
        image: my-nginx-8080:1.0
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
      - name: nginx-container2
        image: my-nginx-8081:1.0
        ports:
        - containerPort: 8081
        livenessProbe:
          httpGet:
            path: /
            port: 8081
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /
            port: 8081
          initialDelaySeconds: 5
          periodSeconds: 5

4.3 準備 Service YAML

然後準備一個 Service,用來測試 readinessProbe 相關行為。

  • svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx-service
spec:
  selector:
    app: nginx
  ports:
    - name: port1
      protocol: TCP
      port: 8080
      targetPort: 8080
    - name: port2
      protocol: TCP
      port: 8081
      targetPort: 8081

4.4 準備第二個 Service YAML

如果是分開的2個 Services 去轉發流量到 Pod 內的2個 Containers 呢?也試一下:

  • svc-2.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx-service-1
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: my-nginx-service-2
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 8081
      targetPort: 8081

4.5 測試過程

Apply YAML 後,依次將 Deployment 配置裡的 livenessProbe.httpGet.pathreadinessProbe.httpGet.path 從正確的 / 改成會引發404的 /hehe,然後觀察 Pod 的狀態變化,Service/Endpoint 的狀態變化,就可以啦。

(對,不放截圖,顯得冗長,不是懶,真的不是懶!)

5. 結論

前面貼過結論了,複製貼上一波:

當1個 Pod 中包含2個 Containers 的時候,如果2個 Containers 分別對外暴露不同的埠(http 服務為例),當其中有1個 Container 的:

  1. Liveness probe 失敗,則該 Container 會被 Kubelet 幹掉,然後該 Container 重啟/重建(當然,你的重啟策略得是 Always),另外一個 Container 不會重啟(也就是不會發生 Pod 級別的重啟/重建,因此 Pod IP 也不會變化);
  2. Readiness probe 失敗,這時候 Pod 狀態裡的 Ready 列自然是1/2,關鍵是 Service 會怎樣工作呢?
    1. 當使用1個 Service 負載流量到這個 Pod 的2個埠時,Service 對應的 Endpoint 會變成 NotReady,導致 Pod 中 ready 的那個 Container 也不能透過 Service 地址訪問到;
    2. 當使用2個不同的 Service 分別負載流量到這個 Pod 的2個埠時,很遺憾,對應的2個 Endpoint 均會變成 NotReady,而不是1個 Ready,一個 NotReady。(這是和我最開始的猜測不相符的)

6. 結尾

沒看夠?別急嘛,關注微信公眾號“胡說雲原生”,來日方長,see you tomorrow。

相關文章