Kubernetes的資源控制器和Service(四)

MXC肖某某發表於2020-09-12

一、定義和分類

1,定義

  k8s 中內建了很多控制器(controller ),這些相當於一個狀態機,用來控制 Pod 的具體狀態和行為。

2,型別

  ReplicationController、ReplicaSet、DaemonSet、StatefulSet、Job/CronJob和Horizontal Pod Autoscaling

二、資源控制器的定義和示例

1,ReplicationController 和 ReplicaSet

  ReplicationController(RC)用來確保容器應用的副本數始終保持在使用者定義的副本數,即如果有容器異常退出,會自動建立新的Pod來替代,而如果異常多出來的容器也會被自動回收

  在新版本的k8s中建議使用ReplicaSet(RS)來取代RC,RS與RC沒有本質的不同,只是名字不一樣,並且RS支援集合式的selector,即通過標籤(labels)來管理多個Pod。

RS示例:(rs.yaml)

apiVersion: extensions/v1beta1
kind: ReplicaSet  #型別為RS
metadata:
  name: rs-1 #RS的名稱 
spec:
  replicas: 3  #Pod的副本數
  selector: #選擇器
    matchLabels:
      tier: frontend #需要匹配的Pod標籤
  template: #定義Pod
    metadata:
      labels:
        tier: frontend #當前Pod的標籤
    spec:
      containers:
      - name: nginx-container
        image: hub.xcc.com/my/mynginx:v1
        ports:
        - containerPort: 80

執行如下命令:

kubectl create -f rs.yaml
#獲取rs
kubectl get rs
#列出指定標籤
kubectl get pod --show-labels -l tier=frontend
#修改標籤為frontend1 
kubectl label pod rs-1-abc tier=frontend1 --overwrite=true
#檢視pod
kubectl get pod --show-labels
#刪除控制器rs-1
kubectl delete rs rs-1
kubectl get pod --show-labels

2,Deployment

  Deployment為Pod和RS提供了一個宣告式定義方法,用來替換以前的RC來方便的管理應用。

Deployment示例:(deployment.yaml)

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: deployment-1 #名稱
spec:
  replicas: 3 #Pod副本
  template:
    metadata:
      labels:
        app: nginx-app #Pod標籤
    spec:
      containers:
      - name: nginx-container
        image: hub.xcc.com/my/mynginx:v1
        ports:
        - containerPort: 80

建立檢視命令

#建立並儲存記錄
kubectl apply -f demployment.yaml --record
#檢視Deployment
kubectl get deployment
#檢視rs
kubectl get rs
#檢視Pod 顯示標籤
kubectl get pod --show-labels

擴容

# 將Pod的期望副本數擴容到5個
kubectl scale deployment deployment-1 --replicas 5
kubectl get pod --show-labels

滾動更新

[root@master01 ~]# kubectl set image deployment/deployment-1 nginx-container=hub.xcc.com/library/mynginx:v2
deployment.extensions/deployment-1 image updated

# 檢視 deployment,發現沒什麼變化
[root@master01 ~]# kubectl get deployment
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
deployment-1   5/5     5            5           24m

# 檢視 rs,出現了一個新的rs,pod數量維持在5個,並且原先的rs的pod數量變為0了
# deployment-1-6fd64dcb5 就是deployment升級後新建立的rs,並且Pod裡的容器使用的是新的映象 
[root@master01 ~]# kubectl get rs
NAME                      DESIRED   CURRENT   READY   AGE
deployment-1-6fd64dcb5    5         5         5       22s
deployment-1-7796b9c74d   0         0         0       22m
#檢視更新狀態
[root@master01 ~]# kubectl rollout status deployment/deployment-1
deployment "deployment-1" successfully rolled out
#檢視升級記錄
[root@k8s-master01 ~]# kubectl rollout history deployment/deployment-1
deployment.extensions/deployment-1 
REVISION  CHANGE-CAUSE
1         kubectl apply --filename=demployment.yaml --record=true
2         kubectl apply --filename=demployment.yaml --record=true
#檢視單個revision 的詳細資訊
kubectl rollout history deployment/deployment-1 --revision=2

清理 Policy

  設定 Deployment 中的 .spec.revisionHistoryLimit 項來指定保留多少舊的 ReplicaSet。 餘下的將在後臺被當作垃圾收集。預設的,所有的 revision 歷史就都會被保留。

  注意: 將該值設定為0,將導致所有的 Deployment 歷史記錄都會被清除,該 Deployment 就無法再回退了。

回滾更新

#回退到上一個版本
kubectl rollout undo deployment/deployment-1
#回退到指定版本 首先檢視版本記錄
kubectl rollout history deployment/deployment-1
#再回退到指定版本為2
kubectl rollout undo deployment/deployment-1 --to-revision=2

更新策略

  Deployment 可以保證在升級時只有一定數量的 Pod 是 down 的。預設的,它會確保至少有比期望的Pod數量少一個是up狀態(最多一個不可用)。

  Deployment 同時也可以確保只建立出超過期望數量的一定數量的 Pod。預設的,它會確保最多比期望的Pod數量多一個的 Pod 是 up 的(最多1個 surge )。

  在未來的 Kuberentes 版本中,將從1-1變成25%-25%。

Rollover(多個rollout並行)

  假如您建立了一個有5個 niginx:1.7.9 replica 的 Deployment,但是當還只有3個 nginx:1.7.9 的 replica 建立出來的時候您就開始更新含有5個 nginx:1.9.1 replica 的 Deployment。在這種情況下,Deployment 會立即殺掉已建立的3個 nginx:1.7.9 的 Pod,並開始建立 nginx:1.9.1 的 Pod。它不會等到所有的5個 nginx:1.7.9 的 Pod 都建立完成後才開始改變航道。

3,DaemonSet

  DaemonSet確保全部(或者一些)node 上執行一個 Pod 的副本。當有新的 node 加入叢集時,會自動為他們新增一個這樣的 Pod。當有叢集移除時,這些 Pod 也會被回收。刪除 DaemonSet 將會刪除它所建立的所有 Pod。

使用場景

  • 執行叢集儲存 daemon,例如在每個 node 上執行 glusterd、ceph。
  • 在每個 node 上執行日期收集 daemon,例如 fluentd、logstash。
  • 在每個 node 上執行監控 daemon,例如 Prometheus node Exporter、collectd、Datadog 代理或New Relic 代理。

DaemonSet 示例:(daemonset.yaml)

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: daemonset-1 #名稱
  labels:
    app: daemonset-app #daemonSet標籤
spec:
  selector:
    matchLabels:
      name: daemonset-label #選擇指定的Pod
  template:
    metadata:
      labels:
        name: daemonset-label #定義Pod的標籤
    spec:
      containers:
      - name: nginx-container
        image: hub.xcc.com/my/mynginx:v1
        ports:
        - containerPort: 80

執行命令

kubectl create -f daemonset.yaml
#檢視daemonSet
kubectl get ds
#檢視Pod標籤
kubectl get pod --show-labels
#檢視Pod執行的節點
kubectl get pod -o wide

  DaemonSet 確保了叢集中每一個節點都會執行一個 Pod(master 節點沒有執行是因為汙點的設定)。就算我們刪除某個節點的 Pod,DaemonSet 也會馬上為這個節點重新建立一個 Pod。當然此時如果有新的節點加入到叢集中,那麼 DaemonSet 也會為這個新節點自動建立一個這樣的 Pod。

4,Job

  Job 負責批處理任務,即僅執行一次的任務,它能夠確保批處理任務的一個或多個 Pod 執行成功。意思就是,執行一個 Job 來建立 Pod,讓裡面的容器成功執行了指定的次數,才認為這個 Job 是成功的,那麼這個 Job 才算執行完成。

特殊說明

  • spec.template 格式同 Pod
  • restartPolicy 策略僅支援 Never 或 OnFailure。
  • 單個 Pod 時,預設 Pod 成功執行後 Job 即結束。
  • .spec.completions 標誌 Job 結束需要成功執行的 Pod 個數,預設為 1。
  • .spec.parallelism 標誌並行執行的 Pod 個數,預設為 1。
  • spec.activeDeadlineSeconds 標誌失敗的重試最大時間,超過這個時間不會繼續重試。

Job示例:(job.yaml)

apiVersion: batch/v1
kind: Job
metadata:
  name: job-1 #job名稱
spec:
  template:
    metadata:
      name: job-app
    spec:
      containers:
      - name: busybox-container
        image: hub.xcc.com/my/mybusybox:v1
        command: ['sh', '-c', 'echo hello world']
      restartPolicy: Never

執行命令

kubectl create -f job.yaml 
kubectl get job 
kubectl get pod 
#檢視日誌 
kubectl log job-1-abcd1

5,CronJob

   CronJob 管理基於時間的 Job,即:

  • 在給定的時間點執行一次
  • 週期性的在給定時間點執行
  • 其本質就是在特定的時間迴圈建立 Job 來執行任務
  • 其表示式為:分、時、日、月、周

常用場景:

  • 在給定的時間點排程 Job 執行
  • 建立週期性的執行的 Job,例如:資料庫備份,傳送郵件。

CronJob 的示例:(cronjob.yaml)

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cronjob-1
spec:
  schedule: "*/1 * * * *"  #spec.schedule:排程,必須欄位,指定任務執行週期,格式為分、時、日、月、周。
  jobTemplate: #spec.jobTemplate:Job 模板,必須欄位,指定需要執行的任務,格式同 Job。
    spec:
      template:
        metadata:
          name: cronjob-app
        spec:
          containers:
          - name: busybox-container
            image: hub.xcc.com/my-xcc/my-busybox:v1
            command: ['sh', '-c', 'date && echo hello world']
          restartPolicy: Never
#spec.startingDeadlineSeconds:啟動 Job 的期限(秒級別),該欄位是可選的,如果因為任何原因而錯過了被排程的時間,那麼錯過指定時間的 Job 將被認為的失敗的。預設無期限。

執行命令

kubectl apply -f cronjob.yaml
kubectl get cj
#檢視job,每分鐘執行一個job
kubectl get job
kubectl get pod
#檢視pod日誌
kubectl log cronjob-1-15123460-acdf6

6,StatefulSet

  StatefulSet 為 Pod 提供唯一的標識,它可以保證部署和 scale 的順序。StatefulSet 是為了解決有狀態服努的問題,對應 Deployment 和 ReplicaSet 是為無狀態服務而設計。

  StatefulSet 有以下特點:

  • 穩定的持久化儲存,即 Pod 重新排程後還是能訪問到相同的持久化資料,基於PVC來實現。
  • 穩定的網路標誌,即 Pod 重新排程後,其 PodName 和 HostName 不變,基於 Headless Service(即沒有Cluster IP的 Service)來實現。
  • 有序部署,有序擴充套件。即 Pod 是有順序的,在部署或擴充套件的時候要依據定義的順序依次進行(即從 0 到 N-1,在下一個 Pod 執行之前,之前所有的 Pod 必須都是 Running 和 Ready 狀態),基於 init containers 來實現。
  • 有序收縮,有序刪除(即從 N- 1 到 0)。

7,HPA(Horizontal Pod Autoscaling)

  應用的資源使用率通常都有高峰和低谷的時候,如何削峰埋谷,提高叢集的整體資源利用率,讓 service 中的 Pod 個數自動調整暱?這就有賴於 Horizontal Pod Autoscaling 了,顧名思義,使 Pod 水平自動縮放。

三、Service

1,定義

  k8s 定義了這樣一種抽象:一個 Pod 的邏輯分組,一種可以訪問它們的策略 —— 通常稱為微服務。 這一組 Pod 能夠被 Service 訪問到,通常是通過 Label Selector 實現的。Service是k8s中的一個重要概念,主要是提供負載均衡和服務自動發現。

2,Service的型別

  • ClusterIp:預設型別,自動分配一個僅 Cluster 內部可以訪問的虛擬 IP。普通Service:通過為Kubernetes的Service分配一個叢集內部可訪問的固定虛擬IP(Cluster IP);Headless Service:DNS會將headless service的後端直接解析為podIP列表。
  • NodePort:在 ClusterIP 基礎上為 Service 在每臺機器上繫結一個埠,這樣就可以通過 : NodePort 來訪問該服務。
  • LoadBalancer:在 NodePort 的基礎上,藉助 cloud provider 建立一個外部負載均衡器,並將請求轉發到: NodePort 。
  • ExternalName:把叢集外部的服務引入到叢集內部來,在叢集內部直接使用。沒有任何型別代理被建立,這隻有 kubernetes 1.7 或更高版本的 kube-dns 才支援

3,Service的建立

  一個 Service 在 k8s 中是一個 Rest 物件,和 Pod 類似。 像所有的 Rest 物件一樣, Service 定義可以基於 POST 方式,請求 apiserver 建立新的例項。 例如,假定有一組 Pod,它們對外暴露了 9376 埠,同時還被打上 "app=MyApp" 標籤。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376   #將請求代理到使用 TCP 埠 9376,並且具有標籤 "app=MyApp" 的 Pod 上

  a)ClusterIP型別

  clusterIP 主要在每個 node 節點使用 iptables 或 ipvs,將發向 clusterIP 對應埠的資料,轉發到 kube-proxy 中。然後 kube-proxy 自己內部實現有負載均衡的方法,並可以查詢到這個 service 下對應 pod 的地址和埠,進而把資料轉發給對應的 pod 的地址和埠。 

  主要需要以下幾個元件的協同工作:

  • apiserver:使用者通過 kubectl 命令向 apiserver 傳送建立 service 的命令,apiserver 接收到請求後將資料儲存 到 etcd 中。
  • kube-proxy:k8s 的每個節點中都有一個叫做 kube-porxy 的程式,這個程式負責感知 service,pod 的變化,並將變化的資訊寫入本地的 iptables 或 ipvs 規則中。
  • iptables 或 ipvs:使用 NAT 等技術將 virtual IP 的流量轉至 endpoints 中。

建立Deployment(cluster-deployment.yaml)

apiVersion: apps/v1
kind: Deployment
metadata:
    name: nginx-deployment
    namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-app
  template:
    metadata:
      labels:
        app: nginx-app
    spec:
      containers:
      - name: nginx-container
        image: hub.xcc.com/my-xcc/my-nginx:v1
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 80

建立一個 Service(cluster-svc.yaml)

apiVersion: v1
kind: Service
metadata:
  name: cluster-svc
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: nginx-app
  ports:
  - name: http
    port: 80
    targetPort: 80

執行命令

#先建立deployment
kubectl apply -f cluster-deployment.yaml
kubectl get deployment
#檢視pod及其對應的ip
kubectl get pod -o wide

#建立svc
kubectl apply -f cluster-svc.yaml
kubectl get svc
# 檢視 ipvs規則。可以看見 svc 所在的 ip 地址代理的是上面的三個 pod 的 ip
ipvsadm -Ln

  b)NodePort型別

  如果設定 type 的值為 "NodePort",Kubernetes master 將從給定的配置範圍內(預設:30000-32767)分配埠,每個 Node 將從該埠(每個 Node 上的同一埠)代理到 Service。該埠將通過 Service 的 spec.ports[*].nodePort 欄位被指定。

建立NodePort資源(nodeport-svc.yaml)

apiVersion: v1
kind: Service
metadata:
  name: nodeport-svc
  namespace: default
spec:
  type: NodePort
  selector:
    app: nginx-app
  ports:
  - name: http
    port: 80
    targetPort: 80
    # 繫結到宿主機的31234埠,如果不指定,將隨機分配30000-32767
    nodePort: 31234

執行命令

kubectl apply -f nodeport-svc.yaml
kubectl get svc
# 檢視 ipvs 規則
ipvsadm -Ln

  c)LoadBalancer 型別

  使用支援外部負載均衡器的雲提供商的服務,設定 type 的值為 "LoadBalancer",將為 Service 提供負載均衡器。 負載均衡器是非同步建立的,關於被提供的負載均衡器的資訊將會通過 Service 的 status.loadBalancer 欄位被髮布出去。

示例:(loadbalancer-svc.yaml)

kind: Service
apiVersion: v1
metadata:
  name: loadbalancer-svc
spec:
  selector:
    app: nginx-App
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
      nodePort: 30061
  clusterIP: 10.0.124.225
  loadBalancerIP: 79.41.36.129
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
      - ip: 124.145.67.177

  來自外部負載均衡器的流量將直接打到 backend Pod 上,不過實際它們是如何工作的,這要依賴於雲提供商。 在這些情況下,將根據使用者設定的 loadBalancerIP 來建立負載均衡器。 某些雲提供商允許設定 loadBalancerIP。如果沒有設定 loadBalancerIP,將會給負載均衡器指派一個臨時 IP。 如果設定了 loadBalancerIP,但云提供商並不支援這種特性,那麼設定的 loadBalancerIP 值將會被忽略掉。

  d)ExternalName 型別

  這種型別的 Service 通過返回 CNAME 和它的值,可以將服務對映到 externalName 欄位的內容,例如 www.xcc.com。ExternalName Service 是 Service 的特例,它沒有 selector,也沒有定義任何的埠和Endpoint。相反的,對於執行在叢集外部的服務,它通過返回該外部服務的別名這種方式來提供服務。

示例:(externelname-svc.yaml)

apiVersion: v1
kind: Service
metadata:
  name: externalname-svc
  namespace: default
spec:
  type: ExternalName
  externalName: www.xcc.com

  當查詢主機 externalname-svc.defalut.svc.cluster.local(svc-name.namespace.svc.cluster.local)時,叢集的 DNS 服務將返回一個值 www.xcc.com 的 CNAME 記錄。訪問這個服務的工作方式和其他的相 同,唯一不同的是重定向發生在 DNS 層,而且不會進行代理或轉發。

執行命令

[root@master01 ~]# kubectl apply -f externelname-svc.yaml 
service/externalname-svc created

[root@master01 ~]# kubectl get svc
NAME               TYPE           CLUSTER-IP    EXTERNAL-IP        PORT(S)      AGE
externalname-svc   ExternalName   <none>        www.xixihaha.com   <none>       4s
kubernetes         ClusterIP      10.96.0.1     <none>             443/TCP      10d

[root@master01 ~]# dig -t A externalname-svc.default.svc.cluster.local. @10.244.0.7
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-9.P2.el7 <<>> -t A externalname-svc.default.svc.cluster.local. @10.244.0.7
......
;; ANSWER SECTION:
externalname-svc.default.svc.cluster.local. 30 IN CNAME    www.xcc.com.
......

  e)Headless Service

  一種特殊的 ClusterIP SVC 型別。有時不需要或不想要負載均衡,以及單獨的 Service IP。 遇到這種情況,可以通過指定 Cluster IP 的值為 "None" 來建立 Headless Service(spec.clusterIP: "None")。這個選項允許開發人員自由尋找他們自己的方式,從而降低與 Kubernetes 系統的耦合性。 應用仍然可以使用一種自注冊的模式和介面卡,對其它需要發現機制的系統能夠很容易地基於這個 API 來構建。對這類 Service 並不會分配 Cluster IP,kube-proxy 不會處理它們,而且平臺也不會為它們進行負載均衡和路由。 DNS 如何實現自動配置,依賴於 Service 是否定義了 selector。

示例:(headless-svc.yaml)

apiVersion: v1
kind: Service
metadata:
  name: headless-svc
  namespace: default
spec:
  selector:
    app: nginx-app
  clusterIP: "None"
  ports:
  - port: 80
    targetPort: 80

執行命令

[root@master01 ~]# kubectl apply -f headless-svc.yaml 
service/headless-svc created

# 檢視 svc。可以看見 headless-svc 的 IP 是空的
[root@master01 ~]# kubectl get svc
NAME           TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
headless-svc   ClusterIP   None          <none>        80/TCP    5s
kubernetes     ClusterIP   10.96.0.1     <none>        443/TCP   10d

# 檢視 k8s 元件所在 IP 地址
[root@k8s-master01 ~]# kubectl get pod -n kube-system -o wide 
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-5c98db65d4-4vjp4 1/1 Running 3 20h 10.244.0.7 master01 <none> <none>
coredns-5c98db65d4-d2kxh 1/1 Running 4 20h 10.244.0.6 master01 <none> <none>
......

# 使用 dig 命令檢視 svc 解析到的ip地址。yum install -y bind-utils 安裝 dig 命令
[root@k8s-master01 ~]# dig -t A headless-svc.default.svc.cluster.local. @10.244.0.6

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-9.P2.el7 <<>> -t A headless-svc.default.svc.cluster.local. @10.244.0.14
......

;; ANSWER SECTION:
headless-svc.default.svc.cluster.local.    30 IN A    10.244.2.57
headless-svc.default.svc.cluster.local.    30 IN A    10.244.1.47
headless-svc.default.svc.cluster.local.    30 IN A    10.244.1.46

使用 dig 命令從 codedns 元件檢視到了 headless-svc 所解析到的 IP 地址。有三個 10.244.2.5710.244.1.47 和 10.244.1.46

如果你現在建立一個不使用 spec.clusterIP: "None" 的 svc,使用 codedns 檢視,會發現只解析到了一個 IP 上。

 

相關文章