06 . Kubernetes之Pod控制器詳細介紹及應用

Pod API屬性詳解

Pod是k8s集群中的最小編排單位。將這個設計落實到API對象上,容器就成了Pod屬性里一個普通的欄位。那麼到底哪些屬性屬於Pod對象,哪些屬性屬於容器的呢?先看下面的一段描述:

假如把Pod看成傳統環境里的”機器”、那麼容器就是運行在這個”機器”里的”用戶程式”,這樣很多關於Pod對象的設計就非常容易理解了。凡是調度、網路、存儲,以及安全相關的屬性,基本上是Pod級別的。他們的共同特徵是:描述的是”機器”這個整體,而不是裡面運行的”程式”。比如:

# 配置這個"機器"的網卡——Pod 的網路定義  
# 配置這個"機器"的磁碟——Pod 的存儲定義  
# 配置這個"機器"的防火牆——Pod 的安全定義  
# 這台"機器"運行在哪個伺服器之上——Pod 的調度
關於標籤

所有的資源都可以設置標籤,目的就是為了給資源貼標識符,使用時用選擇器調用標籤,用到標籤的地方:

後端有以副本形式存在的Pod,這麼多副本最好做負載均衡,此時就在前面創建一個service,一般情況下是先創建rc,然後創建service,創建完成後需要將service和後端的rc關聯到一起,關聯到一起就是用標籤選擇器關聯的,即使不需要標籤也最好設置一個能用的標籤,標籤可以由多個字典元素組成. 標籤的格式: name:value,一個鍵值對表示一個標籤,調用時一起調用,例如: 做nginx,這裡是眾多nginx的第一個,故可以設置這個標籤由兩個字典元素組成,標籤可以有多行,但標籤的值不能是純數字.

Pod級別的相關屬性

凡是跟Namespace都是Pod級別的,比如,容器的Linux Namespace、容器共享宿主機的 Namespace
原因:Pod 的設計就是要讓它裡面的容器儘可能多地共享Linux Namespace,僅保留必要的隔離和限制能力。如此Pod模擬出的效果才能跟虛擬機里程式間的關係非常類似.

apiVersion

除了deployment是v1的升級版,其他的基本都是v1。 # kubectl api-versions

kind

指定這個API對象的資源類型: Pod、Deployment、Job、Ingress、Service等,資源類型的首字母需大寫.

metadata

描述創建資源的屬性,比如Pod的名稱,namspace、標籤等資訊.

spec

specification of the resource content: 指定該資源的內容,包括一些container,storage,volume以及其他k8s需要的參數,以及諸如是否在容器失敗時重新啟動容器的屬性,可在特定Kubernetes API找到完整的Kubernetes Pod屬性.

Spec常用欄位

# Pod資源:
	spec.containers <[]object >
spec:
  containers:
- name <string>
  image <string>			# 倉庫路徑,項目名稱,用戶,鏡像名稱,鏡像標籤
  ports:
  - name: http
    containerPort: 80
  - name: https
    containerPort: 443
  imagePullPolicy: IfNotPresent	# 鏡像獲取策略
  
  # 修改鏡像中的默認應用:
  	command,args
  # 標籤<最多六十三個字元>
  	key=value
  		key: 字元,數字,_-,.,# 只能以字母數字開頭及結尾
  		value: 可以為空,    # 只能字母或者數字開頭及結尾,中間可使用:
  	# Always,Never,IfNotPresent
# Always: 本地不管有沒有鏡像都是要到倉庫去下載,本地無論有還是沒有到要去倉庫下載,就算本地有鏡像他也不用: 
# 雖然會導致容器啟動變慢,但是可以防止被人惡意修改鏡像不被中招:
# Never: 永遠不下載,需要用戶手動去拖鏡像,鏡像一旦創建,不允許被更改,一旦編輯就報錯,除非刪除再創建:
# IfNotPresent: 本地不存在就去下載:
# 如果是latest標籤就是Always,否則就是IfNotPresent 
Example1(給Pod打標籤示例)
cat demo1-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: http
      containerPort: 80
    - name: https
      containerPort: 443
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command:
    - "/bin/sh"
    - "-c"
    - "sleep 3600"
查看Pod標籤
[root@master YAML]# kubectl get pods --show-labels
NAME          READY   STATUS    RESTARTS   AGE     LABELS
nginx-test2   1/1     Running   0          37m     run=nginx-test2
pod-demo      2/2     Running   0          2m30s   app=myapp,tier=frontend

[root@master YAML]# kubectl get pods -L app
# 顯示指定資源類別對象下所有標籤的值. 
NAME          READY   STATUS    RESTARTS   AGE   APP
nginx-test2   1/1     Running   0          36m   
pod-demo      2/2     Running   0          71s   myapp
  

[root@master YAML]# kubectl get pods -l app  
# -l才是過濾
NAME       READY   STATUS    RESTARTS   AGE
pod-demo   2/2     Running   0          84s
修改標籤
# 如果期望將金絲雀改變為穩定版,覺得版本發布沒問題,可以改過去
kubectl  label pods pod-demo release=canar    	    

# 標籤不能重名,如果需要重名加上--overwrite覆蓋即可.
kubectl label pod pod-demo release=stable --overwrite

# 修改節點標籤
kubectl label nodes node1 disktype=ssd
標籤選擇器

等值關係: =,==,!=都表示等值關係

kubectl get pods -l release=stable,app --show-labels
NAME       READY   STATUS    RESTARTS   AGE   LABELS
pod-demo   2/2     Running   0          11m   app=myapp,release=stable,tier=frontend

# 集合關係:
# KEY in (VALUE1,VALUE2...)
# KEY notin (VALUE1,VALUE2...)

nodeSelector <map[string]string>能影響Pod調度演算法

如果我們需要對一些節點擁有ssd硬碟的機器做監控就需要將相應節點打上ssd標籤.然後通過標籤選擇器將Pod運行在指定節點上.

我們在之前那個demo-pod配置文件加上一個欄位即可.

我們先給節點打標籤

kubectl get nodes --show-labels  
NAME     STATUS   ROLES    AGE   VERSION   LABELS  
master   Ready    master   43h   v1.17.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/master=  
node1    Ready    <none>   43h   v1.17.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux  
node2    Ready    <none>   43h   v1.17.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux  

kubectl label nodes node1 disktype=ssd  
node/node1 labeled  

kubectl get nodes --show-labels |grep disktype=ssd  
node1    Ready    <none>   43h   v1.17.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux

我們重新修改下demo1-pod.yaml

cat demo1-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: http
      containerPort: 80
    - name: https
      containerPort: 443
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command:
    - "/bin/sh"
    - "-c"
    - "sleep 3600"
  nodeSelector:
    disktype: ssd
kubectl create -f demo1-pod.yaml

kubectl get pods -o wide
kubectl get pods -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
nginx-test2   1/1     Running   0          49m   10.244.0.18   master   <none>           <none>
pod-demo      2/2     Running   0          53s   10.244.1.15   node1    <none>           <none>

annotations:

與label不同地方在於,他不能用於挑選資源對象,沒有字元限制,僅用於對象提供「元數據」和註解.

cat demo1-pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp
    tier: frontend
  annotations:
    youmen.com/create-by: "youmen"
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: http
      containerPort: 80
    - name: https
      containerPort: 443
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command:
    - "/bin/sh"
    - "-c"
    - "sleep 3600"
  nodeSelector:
    disktype: ssd

kubectl create -f pod-demo.yaml
kubectl describe pods pod-demo |grep Annotations
Annotations:  youmen.com/create-by: youmen
Pod生命周期

Pod生命周期
狀態

Pending:  # 調度尚未完成
Running:  # 正在運行
Failed:   # 失敗
Succeded: # 成功
Unknown:  # 未知狀態.

創建Pod過程

當用戶創建Pod時,這個請求提交給API Server,他先將狀態保存在etcd中,APIServer,接下來會請求Scheduler進行調度,如果調度成功後會將結果保存在etcd資源,更新到etcd狀態資源中,隨後目標節點調到node1上,根據那個清單創建所需要的Pod.

Probe機制
# Pod生命周期的重要行為:
#	初始化容器:
#	容器探測:
#		1. lieness probe:  <是否存活>
		# 如果檢查失敗,殺死容器,根據Pod的restartPolicy來操作
#		2. readiness probe: <是否就緒>
		# 如果檢查失敗,Kubernetes會把Pod從service endpoints中刪除.

#			兩種都支援三種探測行為:
#			1. exec: 執行自定義命令,看返回狀態碼是否為0
#			2. tcpSocket: 向指定TCP套接字發請求,埠發請求,發起
#			3. httpGet: 向指定http服務發請求,應用層get,url響應碼.
#				存活性探測: 主要用於判定主容器是否處於運行狀態
#				就緒性探測: 適用於判定容器中主進程是否處於就緒並可以對外進行服務.

readiness probe:

我們為動態有生命周期Pod提供一個固定的端點,service用標籤選擇器關聯到各個Pod資源:

此處有一個問題,如果有一個新的Pod,這個新Pod剛好符合service條件加入到後端,而有新請求進來就會被調度到新的Pod,但這個時候新Pod裡面服務很有可能還沒有就緒,裡面文件還沒有展開,而這個時候去訪問就有可能出現訪問失敗,如果不做就緒性探測,Pod一創建就關聯到service後出現大量訪問失敗:

	restartPolicy: <重啟策略>
		Always,UnFailure,Never,Default to Always.
		Always: # 一旦Pod中容器掛了就地將他重啟.
		UnFailure: # 狀態為錯誤才重啟,正常終止不重啟: # 延時重啟
		Never: # 掛了就掛了,從來不重啟.

Example

cat pod-probe.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    livenessProbe:
      httpGet:
        path: /index.html
        port: 80

kubectl logs nginx-pod
10.244.3.1 - - [22/Dec/2019:11:03:04 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.17" "-"
10.244.3.1 - - [22/Dec/2019:11:03:14 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.17" "-"
10.244.3.1 - - [22/Dec/2019:11:03:24 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.17" "-"
10.244.3.1 - - [22/Dec/2019:11:03:34 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.17" "-"
# 我們可以看到每十秒都會進行一次測試
# 接下來我們把那個頁面刪除,看會怎麼樣
kubectl exec -it nginx-pod /bin/bash
rm -rf usr/share/nginx/html/index.html
# 然後我們去事件裡面看看他做了什麼處理.
Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  3m35s                default-scheduler  Successfully assigned default/nginx-pod to node1
  Normal   Pulling    43s (x2 over 3m34s)  kubelet, node1     Pulling image "nginx"
  Warning  Unhealthy  43s (x3 over 63s)    kubelet, node1     Liveness probe failed: HTTP probe failed with statuscode: 404
  Normal   Killing    43s                  kubelet, node1     Container nginx failed liveness probe, will be restarted
  Normal   Pulled     21s (x2 over 3m14s)  kubelet, node1     Successfully pulled image "nginx"
  Normal   Created    21s (x2 over 3m14s)  kubelet, node1     Created container nginx
  Normal   Started    21s (x2 over 3m14s)  kubelet, node1     Started container nginx
# 我們可以看到當檢測不到報錯的話,他會自己重新創建一個新鏡像.此處只是個頁面檢測,
# 如果是其他類型服務也可以使用TCP套接字做檢測.
Pod的排錯
kubectl describe TYPE  NAME_PREFIX
kubectl  logs  nginx-xx
kubectl  exec -it  nginx-xx bash or /bin/bash
許多資源支援內嵌欄位

matchLabels: 直接給定鍵值
matchExpresslons: 基於給定的表達式來定義使用標籤選擇器,{key:”KEY”,operator: “OPERATOR” ,values: [VAL1,VAL2…]}

# 操作符
# In,NotIn: values欄位的值必須為空列表.
# Exists,NotExists: values欄位的值為空列表.

Pod控制器

Pod控制器用於實現代我們管理Pod中間層,幫我們確保Pod是我們所期望的目標狀態,如果出現故障,他會嘗試重啟,如果重啟失敗,他會嘗試重新編排重新構建一個,如果Pod副本數量低於用戶定義目標數量,他會自動補全,反之終止多餘目標資源

但此處Pod控制器只是一個泛稱,包含以下控制器

ReplicaSet控制器(ReplicationController)

核心作用在於代用戶創建指定數量的副本,並確保副本數量一直符合用戶所定義狀態,還支援擴縮容機制
kubernetes不建議我們直接使用ReplicaSet,而是用Deployment,因為它還支援滾動更新,回滾,聲明式配置更新
簡寫為rs

cat rs-demo1.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myapp
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
      release: canary 
  template:
    metadata: 
      name: myapp-pod
      labels:
        app: myapp
        release: canary
        environment: qa
    spec:
      containers:
      - name: myapp-container
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80
# 我們此處的標籤盡量條件匹配複雜一點,避免以後創建Pod標籤衝突,多退少補
# 如果需要添加副本數量直接edit配置文件即可,最好使用打修補程式方式修改.
# 如果需要更新版本,只有重建Pod才能更新成功.
# 還能控制更新節奏,加一個刪一個,加一個刪兩個,加兩個刪兩個.d
# 灰度發布: 刪一個創建一個
# 金絲雀發布: 刪一個,等過段時間看用戶反應,沒什麼不好的影響全部給升級
# 藍綠髮布: 在創建個RS,改下service匹配條件,讓請求都匹配到RS2上.
# Deployment就建構在RS之上.一個Deployment可以管理多個RS,

image.png

Deployment控制器

kubernetes不建議我們直接使用ReplicaSet,而是用Deployment,因為它還支援滾動更新,回滾,聲明式配置更新,管理無狀態非常好

cat pod-deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
      release: canary
  template:
    metadata:
      labels:
        app: myapp
        release: canary
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80

kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
myapp-deploy-7d574d56c7-44mqw   1/1     Running   0          52s
myapp-deploy-7d574d56c7-5msnk   1/1     Running   0          52s
# 此處的5msnk是模板的哈希值.我們可以通過apply增加副本數量,但是後面哈希值不變
kubectl describe deploy myapp-deploy

我們可以實時更新一下

kubectl get pods -l app=myapp -w
NAME                            READY   STATUS    RESTARTS   AGE
myapp-deploy-7d574d56c7-44mqw   1/1     Running   0          2m56s
myapp-deploy-7d574d56c7-5msnk   1/1     Running   0          2m56s
# 我們再另開一個終端修改配置文件或者打修補程式,然後實時查看此處的哈希變化.
kubectl edit deploy myapp-deploy
replicas: 4
- image: ikubernetes/myapp:v2
kubectl get pods -l app=myapp -w
NAME                            READY   STATUS    RESTARTS   AGE
myapp-deploy-7d574d56c7-44mqw   1/1     Running   0          8m15s
myapp-deploy-7d574d56c7-5msnk   1/1     Running   0          8m15s
myapp-deploy-7d574d56c7-gvl9q   0/1     Pending   0          0s
myapp-deploy-798dc9b584-z7jhh   0/1     Pending   0          0s
myapp-deploy-7d574d56c7-gvl9q   0/1     Pending   0          0s
myapp-deploy-7d574d56c7-nkwt6   0/1     Pending   0          0s
myapp-deploy-798dc9b584-z7jhh   0/1     Pending   0          1s
myapp-deploy-7d574d56c7-nkwt6   0/1     Pending   0          1s
myapp-deploy-7d574d56c7-gvl9q   0/1     ContainerCreating   0          1s
myapp-deploy-7d574d56c7-nkwt6   0/1     ContainerCreating   0          1s
myapp-deploy-798dc9b584-q8fxd   0/1     Pending             0          0s
myapp-deploy-798dc9b584-q8fxd   0/1     Pending             0          0s
myapp-deploy-798dc9b584-z7jhh   0/1     ContainerCreating   0          1s
myapp-deploy-7d574d56c7-gvl9q   0/1     Terminating         0          1s
myapp-deploy-798dc9b584-q8fxd   0/1     ContainerCreating   0          0s
myapp-deploy-7d574d56c7-gvl9q   0/1     Terminating         0          1s
myapp-deploy-7d574d56c7-nkwt6   1/1     Running             0          3s
myapp-deploy-798dc9b584-z7jhh   1/1     Running             0          7s
myapp-deploy-7d574d56c7-nkwt6   1/1     Terminating         0          7s
myapp-deploy-798dc9b584-sjj65   0/1     Pending             0          0s
myapp-deploy-798dc9b584-sjj65   0/1     Pending             0          0s
myapp-deploy-798dc9b584-sjj65   0/1     ContainerCreating   0          0s
myapp-deploy-7d574d56c7-gvl9q   0/1     Terminating         0          8s
myapp-deploy-7d574d56c7-gvl9q   0/1     Terminating         0          8s
myapp-deploy-7d574d56c7-nkwt6   0/1     Terminating         0          8s
myapp-deploy-7d574d56c7-nkwt6   0/1     Terminating         0          9s
myapp-deploy-7d574d56c7-nkwt6   0/1     Terminating         0          9s
myapp-deploy-798dc9b584-sjj65   1/1     Running             0          2s
myapp-deploy-7d574d56c7-44mqw   1/1     Terminating         0          9m13s
myapp-deploy-798dc9b584-rdtmd   0/1     Pending             0          0s
myapp-deploy-798dc9b584-rdtmd   0/1     Pending             0          0s
myapp-deploy-798dc9b584-rdtmd   0/1     ContainerCreating   0          0s
myapp-deploy-7d574d56c7-44mqw   0/1     Terminating         0          9m14s
myapp-deploy-798dc9b584-q8fxd   1/1     Running             0          10s
myapp-deploy-798dc9b584-rdtmd   1/1     Running             0          2s
myapp-deploy-7d574d56c7-5msnk   1/1     Terminating         0          9m15s
myapp-deploy-7d574d56c7-5msnk   0/1     Terminating         0          9m16s
myapp-deploy-7d574d56c7-5msnk   0/1     Terminating         0          9m17s
myapp-deploy-7d574d56c7-5msnk   0/1     Terminating         0          9m17s
myapp-deploy-7d574d56c7-44mqw   0/1     Terminating         0          9m22s
myapp-deploy-7d574d56c7-44mqw   0/1     Terminating         0          9m22s

我們可以查看一下rs,此處有兩個版本了,一個v1一個v2

kubectl get rs -o wide
NAME                      DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES                 SELECTOR
myapp-deploy-798dc9b584   4         4         4       4m15s   myapp        ikubernetes/myapp:v2   app=myapp,pod-template-hash=798dc9b584,release=canary
myapp-deploy-7d574d56c7   0         0         0       13m     myapp        ikubernetes/myapp:v1   app=myapp,pod-template-hash=7d574d56c7,release=canary
# 但是myapp:v1為歷史版本,會保留著隨時等待被回滾.

kubectl rollout history deployment myapp-deploy
deployment.apps/myapp-deploy 
REVISION  CHANGE-CAUSE
1         <none>
2         <none>

我們可以使用patch打修補程式方式修改實時更新策略

kubectl patch deployment myapp-deploy -p '{"spec":{"replicas":5}}'
# 此時副本數量就為5個了

kubectl describe deployment myapp-deploy	# 修改deployment最大不可用和最小保留

kubectl describe deployment myapp-deploy |grep RollingUpdateStrategy
RollingUpdateStrategy:  0 max unavailable, 1 max surge
# 接下來我們修改下鏡像版本,然後更新看一看(金絲雀發布)
kubectl set image deployment myapp-deploy myapp=ikubernetes/myapp:v3 && kubectl rollout pause deployment myapp-deploy
deployment.apps/myapp-deploy image updated
deployment.apps/myapp-deploy paused
# 另一個終端使用以下命令實時查看
kubectl get pods -l app=myapp -w
NAME                            READY   STATUS    RESTARTS   AGE
myapp-deploy-798dc9b584-bcdxf   1/1     Running   0          14m
myapp-deploy-798dc9b584-q8fxd   1/1     Running   0          57m
myapp-deploy-798dc9b584-rdtmd   1/1     Running   0          57m
myapp-deploy-798dc9b584-sjj65   1/1     Running   0          57m
myapp-deploy-798dc9b584-z7jhh   1/1     Running   0          57m
myapp-deploy-5dc9c974d7-qlpmq   0/1     Pending   0          0s
myapp-deploy-5dc9c974d7-qlpmq   0/1     Pending   0          0s
myapp-deploy-5dc9c974d7-qlpmq   0/1     ContainerCreating   0          0s
myapp-deploy-5dc9c974d7-qlpmq   1/1     Running             0          9s

kubectl rollout status deployment myapp-deploy
Waiting for deployment "myapp-deploy" rollout to finish: 1 out of 5 new replicas have been updated...
# 此時我們發現他會一直卡在這一個Pod更新,接下來我們讓他全部更新
kubectl rollout resume deployment myapp-deploy

# 我們發現此時有了第三個版本
 kubectl get rs -o wide
NAME                      DESIRED   CURRENT   READY   AGE    CONTAINERS   IMAGES                 SELECTOR
myapp-deploy-5dc9c974d7   5         5         5       5m4s   myapp        ikubernetes/myapp:v3   app=myapp,pod-template-hash=5dc9c974d7,release=canary
myapp-deploy-798dc9b584   0         0         0       64m    myapp        ikubernetes/myapp:v2   app=myapp,pod-template-hash=798dc9b584,release=canary
myapp-deploy-7d574d56c7   0         0         0       73m    myapp        ikubernetes/myapp:v1   app=myapp,pod-template-hash=7d574d56c7,release=canary

# 接下來我們可以用回滾
kubectl rollout undo deployment myapp-deploy --to-revision=1	# 回滾到第一個版本
kubectl rollout history deployment myapp-deploy
deployment.apps/myapp-deploy 
REVISION  CHANGE-CAUSE
2         <none>
3         <none>
4         <none>	# 此時我們看到已經有第四個版本了,而此時工作的是v1版
curl 10.244.2.20
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
DaemonSet控制器

在集群中每個節點只運行一個Pod副本
也可以根據自己需求,在部分節點運行一個Pod副本
DaemonSet有以下特點

# 1. 一般是無狀態的
# 2. 這種服務必須是守護進程,持續運行在後台

Example1

cat pod-ds.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
      role: logstor
  template:
    metadata:
      labels:
        app: redis
        role: logstor
    spec:
      containers:
      - name: redis
        image: redis:4.0-alpine
        ports:
        - name: redis
          containerPort: 6379
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: myapp-ds
  namespace: default
spec:
  selector:
    matchLabels:
      app: filebeat
      release: stable
  template:
    metadata:
      labels:
        app: filebeat
        release: stable
    spec:
      containers:
      - name: filebeat
        image: ikubernetes/filebeat:5.6.5-alpine 
        env:
        - name: REDIS_HOST
          value: redis.default.svc.cluster.local
        - name: REDIS_LOG_LEVEL
          value: info

kubectl exec -it redis-646cf89449-b5qkr /bin/bash
Address 1: 10.96.249.167 redis.default.svc.cluster.local

DaemonSet也支援滾動更新

kubectl get ds -o wide
NAME       DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE   CONTAINERS   IMAGES                              SELECTOR
myapp-ds   2         2         2       2            2           <none>          23m   filebeat     ikubernetes/filebeat:5.6.5-alpine   app=filebeat,release=stable
[root@master ~]# kubectl set image daemonsets myapp-ds filebeat=ikubernetes/filebeat:5.6.6-alpine

Example2 (Pod中容器和宿主機共享網路、進程通訊和進程PID Namespace)

cat ns1.yaml   
apiVersion: v1  
kind: Pod  
metadata:  
 name: web-nginx2  
spec:  
 hostNetwork: true  
 hostIPC: true  
 hostPID: true  
 containers:  
 - name: web-nginx2  
 image: daocloud.io/library/nginx  
 - name: shell  
 image: busybox  
 stdin: true  
 tty: true  
  
kubectl apply -f ns1.yaml  
  
kubectl attach -it web-nginx2 -c shell  
/ # ps ax |grep network  
 3522 root     15:32 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --\  
kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml \  
--cgroup-driver=systemd --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.1  
23958 root      0:00 grep network  
  
# Pod中的所有容器直接使用宿主機的網路、直接與宿主機進行IPC通訊,且能夠看到宿主機正在運行的所有進程  
Job

此類控制器主要為了完成指定任務、作業,只要沒完成作業就會不斷重建直至完成
幫我們確保Pod任務是正常完成的,沒有出現什麼異常
但是只是一次性任務
也可以使周期性任務,但他們不需要持續在後台運行,CronJob

以上控制器只能針對無狀態應用,

StatefulSet

針對每一種服務每一種報錯都要定義相應的恢復操作
比如說你Mysql主從節點的從節點宕機了,你要定義很多操作創建一個從節點然後讓他們銜接,稍有不慎,整個集群就崩潰了
我們可以通過Operator將一些運維技能給灌進去,但是支援的應用有限
直到後來出現了helm,類似於yum,類似於模板

Example1(Pod中容器共用一個Pid Namespace)
cat ns.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: web-nginx
spec:
  shareProcessNamespace: true
  containers:
  - name: web-nginx
    image: daocloud.io/library/nginx
  - name: shell
    image: busybox
    stdin: true
    tty: trdue
# 1、shareProcessNamespace: true表示此Pod中的容器共享PID,即每個容器都能看到其他容器內的進程  
# 2. 定義兩個容器:一個nginx;一個等同於使用-it選項的容器  
此Pod被創建後,就可以使用shell容器的tty跟這個容器進行交互。

kubectl apply -f ns.yaml

kubectl attach -it web-nginx -c shell
If you don't see a command prompt, try pressing enter.
/ # ps ax
PID   USER     TIME  COMMAND
    1 root      0:00 /pause
    6 root      0:00 nginx: master process nginx -g daemon off;
   11 101       0:00 nginx: worker process
   21 root      0:00 sh
   26 root      0:00 ps ax
# 在容器里能同時看到nginx和busybox以及Infra容器的/pause的進程,整個Pod里的每個容器進程對於所有容器來說都是可見的,
# 因為它們共享同一個 PID Namespace。

node的相關屬性

nodeName: 這個欄位一般由調度器負責設置,用戶也可以設置他來’騙過’調度器,一旦Pod的這個欄位被賦值,k8s就會被認為此Pod已經調度過,調度的結果就是賦值的節點名字,這個做法一般是測試或者調試的時候才會用到,簡單來說就是不讓調度器去選擇此Pod運行在哪個node上,而是用戶自行指定該Pod運行的node.

nodeSelector: 和nodeName的作用一樣,是一個供用戶將Pod與Node進行綁定的欄位,例如: 設置Pod永遠只能運行在攜帶了: “Kubernetes.io/hostname: k8s-node1″標籤的節點上: 否則,他將調度失敗.

Example1(使用nodeSelector指定pod運行的節點)

查看k8s-node1上的標籤都有哪些

kubectl describe pod web-nginx |grep ^Labels -C3
Priority:     0
Node:         node2/172.19.0.54
Start Time:   Thu, 19 Dec 2019 17:45:43 +0800
Labels:       <none>
Annotations:  <none>
Status:       Running
IP:           10.244.1.5

創建一個Pod指定運行在node1上

cat nginx1.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: test-nginx
  labels:
    web: nginx2
    nginx: nginx3
spec:
#  nodeName: 192.168.122.5 #也可以使用下面的nodeSelector。
  containers:
    - name: web-test
      image: daocloud.io/library/nginx
      ports:
        - containerPort: 80
  nodeSelector: 
    kubernetes.io/hostname: node1 
kubectl apply -f nginx1.yaml
容器中的hosts文件解析

HostAliases: 定義Pod容器中的/etc/hosts文件,解析主機名,且在k8s中,一定要通過此方法設置hosts文件中的內容,如果直接修改hosts文件,在Pod被刪除重建之後,kubelet會自動覆蓋被修改的內容

cat nginx2.yaml 
---
apiVersion: v1
kind: Pod
metadata:
  name: web-nginx3
  labels:
    web: nginx4
    nginx: nginx5
spec:
  hostAliases:
  - ip: "1.1.1.1"
    hostnames:
    - "cluster1.com"
    - "cluster2.com"
    - "cluster3.com"
  containers:
    - name: web2
      image: daocloud.io/library/nginx
      ports:
        - containerPort: 80

# kubectl apply -f nginx2.yaml 

kubectl exec -it web-nginx3 /bin/bash
root@web-nginx3:/# ls
bin   dev  home  lib64	mnt  proc  run	 srv  tmp  var
boot  etc  lib	 media	opt  root  sbin  sys  usr
root@web-nginx3:/# cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.244.1.6	web-nginx3

# Entries added by HostAliases.
1.1.1.1	cluster1.com	cluster2.com	cluster3.com

多個ip對應不同域名的寫法
spec:  
hostAliases:  
- ip: "1.1.1.1"  
hostnames:  
- "cluster1.com"  
- ip: "2.2.2.2"  
hostnames:  
- "cluster2.com"

容器的屬性

  • 容器是Pod中最重要的欄位,有”Containers” 和”Init Containers” 兩種寫法,內容完全相同.
  • Init Containers: 其生命周期會先於所有的容器(即優先啟動),並且嚴格按照定義的順序執行.
  • containers: 指定多個名稱表示多個容器,每一個容器都是列表中的一個元素,每一列元素中還會有字典,字典不同的元素表示容器的不同屬性,容器可以設置複雜的屬性,包括容器啟動運行的命令,使用的參數、工作目錄以及每次實例化是否拉取新的副本,還可以指定更深入的資訊,例如容器的退出日誌的位置等. 包括: name、image、command、args、workingDir、ports、env、resource、volumeMounts、livenessProbe、readinessProbe、livecycle、terminationMessagePath、imagePullPolicy、securityContext、stdin、stdinOnce、tty
name:容器的名字,起個有意義的名字  
image:容器鏡像  
command:容器啟動運行的命令  
args:容器使用的參數  
workingDir:容器的工作目錄  
ports:容器監聽的埠  
env:定義容器的環境變數,主要是給mysql這種應用使用的spec:containers:env  
  env:  
    - name: a  # 變數名稱  
value: "hello"  # 變數值,必須加引號  
volumeMounts:# 共享卷  
imagePullPolicy:# 鏡像拉取策略,此屬性有bug。  
# 默認值Always表示每次創建Pod都重新拉取一次鏡像,實際上是鏡像在本地無需拉取;  
Never:# 不拉取鏡像,本地有直接使用,本地不存在就報異常錯誤  
IfNotPresent: # 表示宿主機上不存在這個鏡像時才拉取,建議使用。
官方提供的鉤子示例**
  • 容器成功啟動之後,在/usr/share/message中寫入了一句「歡迎資訊」(PostStart定義),在這個容器被刪除之前,則先調用nginx的退出命令(preStop定義),從而實現容器中的應用從而關閉.
cat message.yaml
apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: daocloud.io/library/nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/usr/sbin/nginx","-s","quit"]
kubectl apply -f message.yaml

注釋

  • postStart: 開始之後執行的動作,雖然理論上是在Docker容器ENTRYPOINT執行之後,但它並不嚴格保證順序。即:在postStart啟動時,ENTRYPOINT有可能還沒有結束。如果postStart執行超時或者錯誤,k8s會在該Pod的Events中報出該容器啟動失敗的錯誤資訊,導致Pod也處於失敗的狀態。

  • preStop:停止之前指定的動作。操作的執行是同步的,所以它會阻塞當前的容器殺死流程,直到這個 Hook定義操作完成之後,才允許容器被殺死。

  • Pod | docker 容器的生命周期

  • Pod聲明周期的變化,主要體現在Pod API對象的Status部分,這是除了Metadata和Spec之外的第三個重要欄位,其中Pod.status.phase就是Pod的當前狀態,有如下幾種可能的情況:

  • Pending: 此狀態表示Pod的YAML文件已經提交給了kubernetes,API對象已經被創建並保存在Etcd中.但是這個Pod里有些容器因為某種原因不能被順利創建,比如: 調度不成功. (準備狀態: 準備好了就running,準備不好就是failed)

  • Running: 此狀態表示Pod已經調度成功,跟一個具體的節點綁定,它包含的容器都已經創建成功,並且至少有一個正在運行中.

  • Succeeded: 此狀態表示Pod里的所有容器都正常運行完畢,並且已經退出了,這種情況在運行一次性任務最為常見.

  • Failed: 此狀態表示Pod里至少有一個容器以不正常的狀態退出,這個狀態出現以為這個你得Debug這個容器的應用,比如查看Pod的Events和日誌.

  • Unknown: 這是一個異常狀態,表示Pod的狀態不能持續的kubelet彙報給kube-apiserver,這很有可能是主從節點(Master和Kubelet)之間的通訊出現了問題.


  • 細分Pod對象的Status欄位

  • Pod對象的Status欄位還可以細分一組Conditions,包括: PodScheduled、Ready、Initialized、以及Unschedulable,他們主要用於描述當前Status的具體原因,比如: Pod當前的Status是Pending,對應的Condition是Unschedulable,這表示他的調度出現了問題.

  • 投射數據卷Projected Volume

  • 是Kubernetes v1.11之後的新特性,在K8s中,有幾種特殊的Volume,他們既不是存放容器里的數據,也不是用來和宿主機之間的數據交換,而是為容器提供預先定義好的為新建容器提供字元串的,字元串有長有短,就可以使用不同類型的投射數據卷,當然這個工作普通的volumn也可以實現,但是普通的volumn很容易暴露密碼,token或者秘鑰等敏感數據,而使用投射數據卷方式可以把敏感數據的資源直接存放在k8s集群的資料庫中,可以更方便的控制這些數據,並減少暴露的風險,從容器的角度來看,這些Volume里的資訊彷彿是被k8s”投射”(Project)進入容器當中的.

k8s支援的Projected Volume

# Secret: 設置密碼可以使用
# ConfigMap: 準備配置文件使用
# Downward API: 設置環境變數
# ServiceAccountToken: 屬於一種特殊的Secret,做認證使用.

# 使用時分兩步:
# 做出相應的投射數據卷存放到資料庫
# 創建容器時使用.

Pod的重啟策略

三種重啟策略
Always:    # 當容器停止,總是重建容器,默認策略
OnFailure:   # 當容器異常退出(退出狀態碼非0)時,才重啟容器
Never:   # 當容器終止退出,從不重啟容器.
Example
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  restartPolicy: OnFailure