pod概況
Pod 是 Kubernetes 應用程序的基本執行單元,即它是 Kubernetes 對象模型中創建或部署的最小和最簡單的單元。Pod 表示在 集羣 上運行的進程。
Pod 封裝了應用程序容器(或者在某些情況下封裝多個容器)、存儲資源、唯一網絡 IP 以及控制容器應該如何運行的選項。
Kubernetes 集羣中的 Pod 可被用於以下兩個主要用途:
運行單個容器的 Pod。"每個 Pod 一個容器"模型是最常見的 Kubernetes 用例;在這種情況下,可以將 Pod 看作單個容器的包裝器,並且 Kubernetes 直接管理 Pod,而不是容器。
運行多個協同工作的容器的 Pod。 Pod 可能封裝由多個緊密耦合且需要共享資源的共處容器組成的應用程序。 這些位於同一位置的容器可能形成單個內聚的服務單元 —— 一個容器將文件從共享卷提供給公衆,而另一個單獨的“掛斗”(sidecar)容器則刷新或更新這些文件。 Pod 將這些容器和存儲資源打包爲一個可管理的實體。
Pod 爲其組成容器提供了兩種共享資源:網絡 和 存儲。
網絡
每個 Pod 分配一個唯一的 IP 地址。 Pod 中的每個容器共享網絡命名空間,包括 IP 地址和網絡端口。 Pod 內的容器 可以使用 localhost 互相通信。 當 Pod 中的容器與 Pod 之外 的實體通信時,它們必須協調如何使用共享的網絡資源(例如端口)。
存儲
一個 Pod 可以指定一組共享存儲卷 。 Pod 中的所有容器都可以訪問共享卷,允許這些容器共享數據。 卷還允許 Pod 中的持久數據保留下來,以防其中的容器需要重新啓動。
- 重啓策略
- PodSpec 中有一個 restartPolicy 字段,可能的值爲 Always、OnFailure 和Never。默認爲 Always。
- Pod 的生命
- 一般Pod 不會消失,直到人爲銷燬他們,這可能是一個人或控制器。
- 建議創建適當的控制器來創建 Pod,而不是直接自己創建 Pod。因爲單獨的
Pod 在機器故障的情況下沒有辦法自動復原,而控制器卻可以。
- 三種可用的控制器:
- 使用 Job 運行預期會終止的 Pod,例如批量計算。Job 僅適用於重啓策略
爲 OnFailure 或 Never 的 Pod。 - 對預期不會終止的 Pod 使用 ReplicationController、ReplicaSet 和
Deployment ,例如 Web 服務器。 ReplicationController 僅適用於具
有 restartPolicy 爲 Always 的 Pod。 - 提供特定於機器的系統服務,使用 DaemonSet 爲每臺機器運行一個 Pod 。
- 使用 Job 運行預期會終止的 Pod,例如批量計算。Job 僅適用於重啓策略
pod生命週期
在k8s的每個結點上都有這樣一個鏡像:
[root@server4 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
reg.caoaoyuan.org/library/pause 3.2 80d28bedfe5d 4 months ago 683kB
//這是k8s爲pod準備的跟鏡像,每個pod通過它來啓動,它爲pod準備了啓動的環境(網路,卷)。
pod在運行容器之前,會先運行一個或多個init初始化容器,他們一次進行,他們運行完,應用容器纔會進行啓動。
- Init容器與普通的容器非常像,除了如下兩點:
- 它們總是運行到完成。
- Init 容器不支持 Readiness,因爲它們必須在 Pod 就緒之前運行完成。
- 每個 Init 容器必須運行成功,下一個才能夠運行。
- 如果 Pod 的 Init 容器失敗,Kubernetes 會不斷地重啓該 Pod,直到 Init 容
器成功爲止。然而,如果 Pod 對應的 restartPolicy 值爲 Never,它不會重新
啓動。
初始化容器結束後,普通的容器(main container)開始運行,普通容器的啓動和停止通過探針來檢測,探針有存活探針和就緒探針,不指定的話默認爲成功,liveness探針用來進行存活檢測,看容器是否正常的運行着。readness用來檢測容器服務是否就緒.
[root@server2 manifest]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-5fd54d7f56-k5jb9 1/1 Running 3 7d8h
coredns-5fd54d7f56-xb5m4 1/1 Running 4 7d19h
etcd-server2 1/1 Running 7 7d23h
存活探針檢測成功纔會時running的狀態,就緒檢測通過纔會時ready的狀態,用戶纔可以訪問,纔可以有流量進來。
init容器
更多詳情參考:https://kubernetes.io/zh/docs/concepts/workloads/pods/init-containers/
- Init 容器能做什麼?
- Init 容器可以包含一些安裝過程中應用容器中不存在的實用工具或個性化
代碼。 - Init 容器可以安全地運行這些工具,避免這些工具導致應用鏡像的安全性
降低。 - 應用鏡像的創建者和部署者可以各自獨立工作,而沒有必要聯合構建一個
單獨的應用鏡像。 - Init 容器能以不同於Pod內應用容器的文件系統視圖運行。因此,Init容器
可具有訪問 Secrets 的權限,而應用容器不能夠訪問。 - 由於 Init 容器必須在應用容器啓動之前運行完成,因此 Init 容器提供了一
種機制來阻塞或延遲應用容器的啓動,直到滿足了一組先決條件。一旦前
置條件滿足,Pod內的所有的應用容器會並行啓動。
- Init 容器可以包含一些安裝過程中應用容器中不存在的實用工具或個性化
[root@server2 manifest]# vim init.yml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
initContainers:
- name: init-myservice
image: busybox:latest
command: ['sh', '-c', "until nslookup myservice.default.svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:latest
command: ['sh', '-c', "until nslookup mydb.default.svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
containers:
- name: myapp-container
image: busybox:latest
~
“until nslookup myservice.default.svc.cluster.local; do echo waiting for myservice; sleep 2; done”]
的意思是,執行nslookup指令解析myservice.default.svc.cluster.local
這個地址,如果解析成功則繼續,不成功則一直處於初始化狀態,循環do裏面的內容。
[root@server2 manifest]# kubectl get pod -w
NAME READY STATUS RESTARTS AGE
myapp-pod 0/2 Init:0/1 0 5s
//正在進行初始化容器,但是卡在這裏了,一直在進行init
Init Containers:
init-myservice:
Container ID: docker://3b83c7896675f49dab5b8c103e52b5f5e4b62bf6ce6d3659267267ebddc0978e
Image: busyboxplus
Image ID: docker-pullable://busyboxplus@sha256:9d1c242c1fd588a1b8ec4461d33a9ba08071f0cc5bb2d50d4ca49e430014ab06
Port: <none>
Host Port: <none>
Command:
sh
-c
until nslookup myservice.default.svc.cluster.local; do echo waiting for myservice; sleep 2; done
State: Running //一直在運行
Started: Fri, 26 Jun 2020 18:56:21 +0800
Ready: False
但是當前這個容器時無法運行的,因爲沒有創建myservice這個服務,不能完成解析。
注意要關閉外網,不然會通過主機的dns解析出去。
爲兩個初始化容器創建服務:
[root@server2 manifest]# vim service.yml
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
[root@server2 manifest]# kubectl apply -f service.yml
service/myservice created
service/mydb created
[root@server2 manifest]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8d
mydb ClusterIP 10.100.111.80 <none> 80/TCP 7s
myservice ClusterIP 10.107.87.62 <none> 80/TCP 7s
[root@server2 manifest]# kubectl get pod -w //再次查看
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:1/2 0 9s
myapp-pod 0/1 PodInitializing 0 13s //pod初始化
myapp-pod 1/1 Running 0 52s //就運行了
此時我們在關閉service,這個容器也不會在受影響了,因爲init時運行在容器之前的,運行完就結束了。不會在對後面的容器產生影響了。
進入到應用容器的運行後,接下來我們就可以去佈置探針了。用來檢測服務和運行狀態了。
探針
- 探針 是由 kubelet 對容器執行的定期診斷:
- ExecAction:在容器內執行指定命令。如果命令退出時返回碼爲 0 則認
爲診斷成功。 - TCPSocketAction:對指定端口上的容器的 IP 地址進行 TCP 檢查。如果
端口打開,則診斷被認爲是成功的。 - HTTPGetAction:對指定的端口和路徑上的容器的 IP 地址執行 HTTP
Get 請求。如果響應的狀態碼大於等於200 且小於 400,則診斷被認爲是
成功的。
- ExecAction:在容器內執行指定命令。如果命令退出時返回碼爲 0 則認
- 每次探測都將獲得以下三種結果之一:
- 成功:容器通過了診斷。
- 失敗:容器未通過診斷。
- 未知:診斷失敗,因此不會採取任何行動。
- Kubelet 可以選擇是否執行在容器上運行的三種探針執行和做出反應:
- livenessProbe:指示容器是否正在運行。如果存活探測失敗,則 kubelet
會殺死容器,並且容器將受到其 重啓策略 的影響。如果容器不提供存活探
針,則默認狀態爲 Success。 - readinessProbe:指示容器是否準備好服務請求。如果就緒探測失敗,端
點控制器將從與 Pod 匹配的所有 Service 的端點中刪除該 Pod 的 IP 地址。
初始延遲之前的就緒狀態默認爲 Failure。如果容器不提供就緒探針,則默
認狀態爲 Success。 - startupProbe: 指示容器中的應用是否已經啓動。如果提供了啓動探測
(startup probe),則禁用所有其他探測,直到它成功爲止。如果啓動探測
失敗,kubelet 將殺死容器,容器服從其重啓策略進行重啓。如果容器沒有
- livenessProbe:指示容器是否正在運行。如果存活探測失敗,則 kubelet
startupProbe
存在時就會忽略
上面的存活探針
和就緒探針
,所以我們只對存活探針和就緒探針進行實驗.
liveness
[root@server2 manifest]# vim init.yml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
initContainers:
- name: init-myservice
image: busyboxplus
command: ['sh', '-c', "until nslookup myservice.default.svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
containers:
- name: myapp-container
image: myapp:v1
imagePullPolicy: IfNotPresent
livenessProbe: //存活探針
tcpSocket:
port: 80 //檢測80端口,打開則正常運行
initialDelaySeconds: 1 //容器開啓後延遲1秒
periodSeconds: 2 //每2秒檢測一次
timeoutSeconds: 1 //超時
[root@server2 manifest]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8d
myservice ClusterIP 10.99.245.243 <none> 80/TCP 15h //我們的svc開啓着
[root@server2 manifest]# kubectl apply -f init.yml
pod/myapp-pod created
[root@server2 manifest]# kubectl get pod -w
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 PodInitializing 0 4s
myapp-pod 1/1 Running 0 4s
[root@server2 manifest]# kubectl exec -it myapp-pod -- sh
/ # nginx -s stop //進入同期內部,關閉nginx服務
2020/06/27 02:51:21 [notice] 12#12: signal process started
/ # command terminated with exit code 137
[root@server2 manifest]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 1 3m42s //自動重啓了。
[root@server2 manifest]# kubectl describe pod myapp-pod
Liveness: tcp-socket :80 delay=1s timeout=1s period=2s #success=1 #failure=3 //成功檢測一次,失敗檢測三次
存活探針和容器的生命週期保持一直,會不斷的進行容器的存活檢測,當檢測到端口關閉的時候,會讓kubelet 去重啓這個容器,但是前提時重啓策略不是Never。
此時我們的service無法識別pod的ip地址,只是做了一個解析,我們應該:
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
selector: //加上選擇器,選擇我們的pod的標籤加入到service的服務中
app: myapp //service就是作一個端口的暴露
[root@server2 manifest]# kubectl apply -f service.yml
service/myservice created
[root@server2 manifest]# kubectl describe svc myservice
Name: myservice
Namespace: default
Labels: <none>
Annotations: Selector: app=myapp
Type: ClusterIP
IP: 10.100.34.244
Port: <unset> 80/TCP
TargetPort: 9376/TCP
Endpoints: 10.244.1.60:9376 //pod的資源就到了service上了,這是在就緒探針完成的狀態下,
Session Affinity: None
Events: <none>
[root@server2 manifest]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-pod 1/1 Running 1 22m 10.244.1.60 server3 <none> <none>
readiness
上面我們只加了存活探針,就緒探針不加的話是默認成功,所以我們才能看到pod的ip加到svc上,可以訪問。下面我們加上就緒探針進行檢測。
[root@server2 manifest]# vim init.yml
...
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 1
periodSeconds: 2
timeoutSeconds: 1
readinessProbe: //把就緒探針加進去
httpGet:
path: /hostname.html //讓它檢測這個頁面
port: 80 //和80端口
initialDelaySeconds: 1
periodSeconds: 2
timeoutSeconds: 1
[root@server2 manifest]# kubectl apply -f init.yml
pod/myapp-pod created
[root@server2 manifest]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 2m9s
//當前的ready狀態是正常的,因爲檢測的兩個東西都正常
[root@server2 manifest]# kubectl exec -it myapp-pod -- sh //做一些更改
/ # vi /etc/nginx/conf.d/default.conf
#location = /hostname.html {
# alias /etc/hostname; //註釋掉着兩行
#}
/ # nginx -s reload
2020/06/27 03:23:53 [notice] 23#23: signal process started
/ # ps ax
PID USER TIME COMMAND
1 root 0:00 nginx: master process nginx -g daemon off;
14 root 0:00 sh
24 nginx 0:00 nginx: worker process
25 root 0:00 ps ax
/ # [root@server2 manifest]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Running 0 6m50s //容器運行,但是沒準備好
//就緒狀態就關閉了,因爲就緒探針生效,它找不到 path: /hostname.html 這個路徑了。
[root@server2 manifest]# kubectl describe svc myservice
Name: myservice
Namespace: default
Labels: <none>
Annotations: Selector: app=myapp
Type: ClusterIP
IP: 10.100.34.244
Port: <unset> 80/TCP
TargetPort: 9376/TCP
Endpoints: //就不會把pod的地址接入到這裏了。
[root@server2 manifest]# kubectl exec -it myapp-pod -- sh
/ # vi /etc/nginx/conf.d/default.conf //我們在進入容器打開哪幾個參數
/ # nginx -s reload
2020/06/27 04:03:16 [notice] 32#32: signal process started
/ # [root@server2 manifest]# kubectl describe svc myservice
Name: myservice
Namespace: default
Labels: <none>
Annotations: Selector: app=myapp
Type: ClusterIP
IP: 10.100.34.244
Port: <unset> 80/TCP
TargetPort: 9376/TCP
Endpoints: 10.244.1.61:9376 //加近來了
Session Affinity: None
Events: <none>
[root@server2 manifest]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 46m
[root@server2 manifest]# kubectl run demo --image=busyboxplus -it --restart=Never
cuelIf you don't see a command prompt, try pressing enter.
/ # curl 10.102.77.37
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
// 然後通過svc的ip地址就可以訪問容器了,svc通過標籤把容器的接入近來。
我們在多加一個pod:
[root@server2 manifest]# vim pod2.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-example
spec:
replicas: 2 //有兩個副本
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp //設置標籤相同。
spec:
containers:
- name: nginx
image: myapp:v2 //使用v2鏡像
[root@server2 manifest]# kubectl apply -f pod2.yml
kubec deployment.apps/deployment-example created
[root@server2 manifest]# kubectl get pod
NAME READY STATUS RESTARTS AGE
demo 0/1 Completed 0 10m
deployment-example-5c9fb4c54c-4f9dj 1/1 Running 0 5s
deployment-example-5c9fb4c54c-pnfzb 1/1 Running 0 5s
myapp-pod 1/1 Running 0 64m
[root@server2 manifest]# kubectl describe svc myservice
Name: myservice
Namespace: default
Labels: <none>
Annotations: Selector: app=myapp
Type: ClusterIP
IP: 10.102.77.37
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.61:80,10.244.2.36:80,10.244.2.37:80 //把新開的兩個容器頁加進去了
Session Affinity: None
Events: <none>
這樣我們就可以做負載均衡了。
[root@server2 manifest]# kubectl run demo --image=busyboxplus -it --restart=Never
If you don't see a command prompt, try pressing enter.
/ # curl 10.102.77.37
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
/ # curl 10.102.77.37
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
/ # curl 10.102.77.37
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
/ # curl 10.102.77.37
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
/ # curl 10.102.77.37
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
/ # curl 10.102.77.37
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
/ # curl 10.102.77.37
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>