K8S實戰(三)| Pod 的本質

前言

上一節發佈了一個容器到 K8S 中,但其實實際操做的是 Pod ,那麼爲何是 Pod,而不是容器。html

更新歷史

Pod 的本質

Pod 包裝了一個或多個容器。
Pod 是 K8S 的最小執行單元。
Pod 是 K8S 中的一個進程。
Pod 可包裝 Docker,也支持包裝其餘類型容器。
Pod 包含封裝的容器、存儲資源、網絡資源、以及指導容器如何運行的配置。node

能夠把容器理解爲一個無掛鉤的光禿禿的集裝箱,K8S 這艘大船沒法直接掛載它,經過給集裝箱(容器)加裝掛鉤(IP地址)等造成一個 Pod,方便 K8S 來操做。python

也能夠把 Pod 理解爲傳統的虛擬機,而容器是傳統虛擬機中運行的程序,只不過虛擬機是一個實體,而 Pod 是一個邏輯概念。linux

K8S 經過編排 Pod 來調度容器,而不是直接操做容器,K8S 沒法直接操縱容器。nginx

Pod 中的共享資源

Pod 爲其中運行的多容器提供共享的網絡、存儲資源、命名空間。shell

網絡

Pod 具備惟一 IP 地址,Pod 中的多個容器共享一個 IP 地址和網絡端口等網絡資源
Pod 中多容器可以使用 localhost 通訊
Pod 中容器和外部通訊時候,多容器須要協調網絡端口
Pod 中的容器獲取的系統主機名與爲 Pod 配置的 name 相同api

存儲

Pod 可指定一組存儲卷
Pod 中多容器都可以訪問該存儲卷,以便互相共享數據
Pod 中的共享卷能夠持久保存,防止容器重啓丟失數據安全

Pod 的特色

若是使用 kind: Pod 的 yaml 文件來建立 Pod,當前節點服務器出現問題後,Pod 不能被自動調度到其餘可用服務器。bash

通常使用 kind: Deployment 的 yaml 來建立 Pod。服務器

Deployment 是一種控制器,能夠用來建立、管理 Pod。如建立多副本 Pod,滾動更新 Pod。

當 Pod 所在節點出現問題,Deployment 控制器能夠在集羣中其餘節點啓動新 Pod。

Pod 模板

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']

鏡像拉取策略

不具體指定的狀況下,imagePullPolicy 是 Always,即 kubelet 會嘗試從指定的倉庫拉取每一個鏡像。
若是容器屬性 imagePullPolicy 設置爲 IfNotPresent , 則會優先使用本地鏡像。
若是容器屬性 imagePullPolicy 設置爲 IfNotPresent  Never, 則會必定使用本地鏡像。

apiVersion: v1
kind: Pod
metadata:
  name: private-image-test-1
spec:
  containers:
    - name: uses-private-image
      image: nginx
      imagePullPolicy: Always
      command: [ "echo", "SUCCESS" ]

Pod 經常使用參數

NodeSelector

功能:將 Pod 與 Node 綁定
apiVersion: v1
kind: Pod
...
spec:
 nodeSelector:
   disktype: ssd

該 Pod 只能運行在攜帶了「disktype:ssd」標籤(Label)的節點上,若是沒有這種標籤的節點,調度將失敗。

NodeName

該字段通常由調度器設置,但咱們測試時候能夠手工設置該字段,讓調度器認爲該 Pod 已經被調度過了。

HostAliase

給 Pod 裏各容器的 /etc/hosts 文件設置內容

apiVersion: v1
kind: Pod
......
spec:
  hostAliases:
  - ip: "10.20.20.20"
    hostnames:
    - "test1.com"
    - "test2.com"

進容器檢查一下

[root@master01 ~]# kubectl exec -it nginx -- bash
root@nginx:/# cat /etc/hosts
......
# Entries added by HostAliases.
10.20.20.20     test1.com       test2.com

shareProcessNamespace

定義參數 shareProcessNamespace=true,那麼該 Pod 中全部容器將共享 PID Namespace

建立一個包含兩個容器的 Pod

[root@master01 ~]# cat nginx.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  shareProcessNamespace: true
  containers:
  - name: nginx
    image: nginx
  - name: shell
    image: busybox
    stdin: true
    tty: true

查看運行狀況

[root@master01 ~]# kubectl get pods -o wide
NAME    READY   STATUS    RESTARTS   AGE    IP               NODE     NOMINATED NODE   READINESS GATES
nginx   2/2     Running   0          117s   192.10.137.131   work03   <none>           <none>

進入到 Pod nginx 中的名爲 shell 的容器中

[root@master01 ~]# kubectl attach -it nginx -c shell
If you don't see a command prompt, try pressing enter.
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /pause
    6 root      0:00 nginx: master process nginx -g daemon off;
   33 101       0:00 nginx: worker process
   34 root      0:00 sh
   39 root      0:00 ps aux

能夠看到,shell 容器中能夠看到 nginx 容器的進程

Pod 的預設參數 PodPreset

咱們能夠預先設置好一些通用的配置,當用戶提交本身的個性化 Pod 配置時,PodPreset 就能夠自動附加通用配置到對應的 Pod 上。

PodPreset 裏定義的內容,只會在 Pod API 對象被建立以前追加在這個對象自己 上,而不會影響任何 Pod 的控制器的定義。

好比,咱們如今提交的是一個 nginx-deployment,那麼這個 Deployment 對象自己是永遠不會被 PodPreset 改變的,被修改的只是這個 Deployment 建立出來的全部 Pod。

未啓用 PodPreset 特性時

# kubectl get podpresets
error: the server doesn't have a resource type "podpresets"

啓用 PodPreset 特性

修改
[/etc/kubernetes/manifests/kube-apiserver.yaml] 
中的
spec.containers.command: 
修改原
- --runtime-config=api/all=true
爲
- --runtime-config=api/all=true,settings.k8s.io/v1alpha1=true
新加一行
- --enable-admission-plugins=PodPreset

3臺MASTER均執行重啓 kubelet
systemctl restart kubelet

預設值配置 preset.yaml

apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
  name: allow-tz-env
spec:
  selector:
    matchLabels:
  env:
    - name: TZ
      value: Asia/Shanghai

Pod配置 nginx.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx

建立 PodPreset

# kubectl get podpreset
No resources found in default namespace.

# kubectl apply -f podpreset.yaml
podpreset.settings.k8s.io/allow-tz-env created

# kubectl get podpreset
NAME           CREATED AT
allow-tz-env

建立 Pod

[root@master01 ~]# kubectl apply -f nginx.yaml 
pod/nginx created

查看該 Pod 是否被注入

# kubectl get pod nginx -o yaml 
...省略
spec:
  containers:
  - env:
    - name: TZ
      value: Asia/Shanghai
    image: nginx
    imagePullPolicy: Always
    name: nginx
    resources: {}
...省略

能夠看到 Pod 被注入了名爲 TZ 的 env

Init 容器

特色

  1. Init 容器在 Pod 內應用容器啓動以前運行。
  2. 一個 Pod 能夠有一個或多個 Init 容器。
  3. 每一個 Init 容器運行必須完成。
  4. 若是 Init 容器運行失敗,K8S 會不斷重啓該 Pod,直到 Init 容器運行成功。
  5. 但若是 Pod 對應的 restartPolicy 值爲 Never,它不會從新啓動。
  6. 若是一個 Pod 有多個 Init 容器,這些容器會按順序逐個運行。每一個 Init 容器必須運行成功,下一個纔可以運行。
  7. Init 容器能夠包含一些安裝過程當中應用容器中不存在的實用工具或個性化代碼。例如,沒有必要僅爲了在安裝過程當中使用相似 sed、 awk、 python 或 dig 這樣的工具而去FROM 一個鏡像來生成一個新的鏡像。
  8. Init 容器能夠安全地運行這些工具,避免這些工具致使應用鏡像的安全性下降。
  9. 應用鏡像的建立者和部署者能夠各自獨立工做,而沒有必要聯合構建一個單獨的應用鏡像。
  10. Init 容器能以不一樣於 Pod 內應用容器的文件系統視圖運行。所以,Init容器可具備訪問 Secrets 的權限,而應用容器不可以訪問。
  11. 因爲 Init 容器必須在應用容器啓動以前運行完成,所以 Init 容器提供了一種機制來阻塞或延遲應用容器的啓動,直到知足了一組先決條件。一旦前置條件知足,Pod 內的全部的應用容器會並行啓動。
  12. 在全部的 Init 容器沒有成功以前,Pod 將不會變成 Ready 狀態。
  13. Init 容器鏡像的變動會引發 Pod 重啓, 應用容器鏡像的變動僅會重啓應用容器。
  14. Pod 中每個容器副本啓動以前,都會執行一遍 Init 容器。

如何使用

  1. 定義一個具備 2 個 Init 容器的 Pod。
  2. init 容器爲 myservice 和 mydb。 這兩個 Init 容器都啓動完成,Pod 才能啓動 spec 區域中的應用容器 myapp-container

建立 Pod 的 YAML 文件:

cat myapp.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]

建立 mydb 和 myservice 兩個 Service 的 YAML 文件:

cat myservice.yaml

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

要啓動這個 Pod,能夠執行以下命令:

kubectl apply -f myapp.yaml
pod/myapp-pod created

檢查其狀態:

kubectl get -f myapp.yaml
NAME        READY     STATUS     RESTARTS   AGE
myapp-pod   0/1       Init:0/2   0          6m

使用下面命令查看更詳細的信息:

kubectl describe -f myapp.yaml

Name:          myapp-pod
Namespace:     default
[...]
Labels:        app=myapp
Status:        Pending
[...]
Init Containers:
  init-myservice:
[...]
    State:         Running
[...]
  init-mydb:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Containers:
  myapp-container:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]

查看Pod內 Init 容器的日誌

$ kubectl logs myapp-pod -c init-myservice
$ kubectl logs myapp-pod -c init-mydb

此時,Init 容器將會等待直到發現名稱爲mydb和myservice的 Service。

建立mydb和myservice的 service:

$ kubectl create -f services.yaml
service "myservice" created
service "mydb" created

能夠看到這些 Init 容器執行完畢,隨後my-app的Pod轉移進入 Running 狀態:

$ kubectl get -f myapp.yaml
NAME        READY     STATUS    RESTARTS   AGE
myapp-pod   1/1       Running   0          9m

只有咱們啓動了 mydb 和 myservice 這兩個 Service,Init 容器完成,myapp-pod 才能被建立。

Debug Pod

若是 Pod 被終止,可經過以下命令查看緣由

kubectl describe pod pod名稱

kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}'  pod名稱

結束語

Pod 包裝了容器,K8S 經過操做 Pod 來操做容器。
Pod 能夠包含多個應用容器和多個 Init 容器。
人工直接建立的 Pod 在服務器節點故障時,沒有自愈能力,須要使用控制器來解決這個問題。

聯繫我

微信公衆號:zuolinux_com

微信掃碼關注

相關文章
相關標籤/搜索