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 的:
- Liveness probe 失敗,則該 Container 會被 Kubelet 幹掉,然後該 Container 重啟/重建(當然,你的重啟策略得是 Always),另外一個 Container 不會重啟(也就是不會發生 Pod 級別的重啟/重建,因此 Pod IP 也不會變化);
- Readiness probe 失敗,這時候 Pod 狀態裡的 Ready 列自然是1/2,關鍵是 Service 會怎樣工作呢?
- 當使用1個 Service 負載流量到這個 Pod 的2個埠時,Service 對應的 Endpoint 會變成 NotReady,導致 Pod 中 ready 的那個 Container 也不能透過 Service 地址訪問到;
- 當使用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.path
和 readinessProbe.httpGet.path
從正確的 /
改成會引發404的 /hehe
,然後觀察 Pod 的狀態變化,Service/Endpoint 的狀態變化,就可以啦。
(對,不放截圖,顯得冗長,不是懶,真的不是懶!)
5. 結論
前面貼過結論了,複製貼上一波:
當1個 Pod 中包含2個 Containers 的時候,如果2個 Containers 分別對外暴露不同的埠(http 服務為例),當其中有1個 Container 的:
- Liveness probe 失敗,則該 Container 會被 Kubelet 幹掉,然後該 Container 重啟/重建(當然,你的重啟策略得是 Always),另外一個 Container 不會重啟(也就是不會發生 Pod 級別的重啟/重建,因此 Pod IP 也不會變化);
- Readiness probe 失敗,這時候 Pod 狀態裡的 Ready 列自然是1/2,關鍵是 Service 會怎樣工作呢?
- 當使用1個 Service 負載流量到這個 Pod 的2個埠時,Service 對應的 Endpoint 會變成 NotReady,導致 Pod 中 ready 的那個 Container 也不能透過 Service 地址訪問到;
- 當使用2個不同的 Service 分別負載流量到這個 Pod 的2個埠時,很遺憾,對應的2個 Endpoint 均會變成 NotReady,而不是1個 Ready,一個 NotReady。(這是和我最開始的猜測不相符的)
6. 結尾
沒看夠?別急嘛,關注微信公眾號“胡說雲原生”,來日方長,see you tomorrow。