Kubernetes有狀態應用管理——PetSet


目錄貼:Kubernetes學習系列

1、介紹

  在Kubernetes中,大多數的Pod管理都是基於無狀態、一次性的理念。例如Replication Controller,它只是簡單的保證可提供服務的Pod數量。如果一個Pod被認定為不健康的,Kubernetes就會以對待牲畜的態度對待這個Pod——刪掉、重建。相比於牲畜應用,PetSet(寵物應用),是由一組有狀態的Pod組成,每個Pod有自己特殊且不可改變的ID,且每個Pod中都有自己獨一無二、不能刪除的數據。

  眾所周知,相比於無狀態應用的管理,有狀態應用的管理是非常困難的。有狀態的應用需要固定的ID、有自己內部可不見的通信邏輯、特別容器出現劇烈波動等。傳統意義上,對有狀態應用的管理一般思路都是:固定機器、靜態IP、持久化存儲等。Kubernetes利用PetSet這個資源,弱化有狀態Pet與具體物理設施之間的關聯。一個PetSet能夠保證在任意時刻,都有固定數量的Pet在運行,且每個Pet都有自己唯一的身份。一個“有身份”的Pet指的是該Pet中的Pod包含如下特性:

    1)        靜態存儲;

    2)        有固定的主機名,且DNS可尋址(穩定的網絡身份,這是通過一種叫 Headless Service 的特殊Service來實現的。 和普通Service相比,Headless Service沒有Cluster IP,用於為一個集群內部的每個成員提供一個唯一的DNS名字,用於集群內部成員之間通信 。);

    3)        一個有序的index(比如PetSet的名字叫mysql,那么第一個啟起來的Pet就叫mysql-0,第二個叫mysql-1,如此下去。當一個Pet down掉后,新創建的Pet會被賦予跟原來Pet一樣的名字,通過這個名字就能匹配到原來的存儲,實現狀態保存。)

  應用舉例:

    1)        數據庫應用,如Mysql、PostgreSQL,需要一個固定的ID(用於數據同步)以及外掛一塊NFS Volume(持久化存儲)。

    2)        集群軟件,如zookeeper、Etcd,需要固定的成員關系。

2、使用限制

    1)        1.4新加功能,1.3及之前版本不可用;

    2)        DNS,要求使用1.4或1.4之后的DNS插件,1.4之前的插件只能解析Service對應的IP,無法解析Pod(HostName)對應的域名;

    3)        日常運維,對於PetSet,唯一能夠更改的就是replicas;

    4)        需要持久化數據卷(PV,若為nfs這種無法通過調用API來創建存儲的網絡存儲,數據卷要在創建PetSet之前靜態創建;若為aws-ebs、vSphere、openstack Cinder這種可以通過API調用來動態創建存儲的虛擬存儲,數據卷除了可以通過靜態的方式創建以外,還可以通過StorageClass進行動態創建。需要注意的是,動態創建出來的PV,默認的回收策略是delete,及在刪除數據的同時,還會把虛擬存儲卷刪除);

    5)        刪除或縮容PetSet不會刪除對應的持久化數據卷,這么做是處於數據安全性的考慮;

    6)        只能通過手動的方式升級PetSet。

3、PetSet示例

  以下示例演示了創建兩個PV,創建兩個PetSet的過程。在PetSet創建完成之后,驗證了其hostName及直接Pod訪問。

3.1 創建PV

  創建PV的時候,我兩個PV選擇了同一個NFS server,創建也是成功的。Kubernetes在創建PV的時候並不會校驗NFS server是否存在,是否能夠連接成功,也不會校驗storage設置的大小是否真實。所以說,PV的創建需要管理員在一開始就設置好,依賴人為的校驗。

[root@k8s-master pv]# cat nfs-pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0001
spec:
  capacity:
    storage: 5Gi 
  accessModes:
    - ReadWriteMany 
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: "/data/disk1"
    server: 192.168.20.47 
    readOnly: false
[root@k8s-master pv]# cat nfs-pv2.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0002
spec:
  capacity:
    storage: 5Gi 
  accessModes:
    - ReadWriteMany 
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: "/data/disk1"
    server: 192.168.20.47 
    readOnly: false
[root@k8s-master pv]# kubectl create -f nfs-pv.yaml 
persistentvolume "pv0001" created
[root@k8s-master pv]# kubectl create -f nfs-pv2.yaml 
persistentvolume "pv0002" created
[root@k8s-master pv]# kubectl get pv
NAME      CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS      CLAIM     REASON    AGE
pv0001    5Gi        RWX           Recycle         Available                       8s
pv0002    5Gi        RWX           Recycle         Available                       5s

3.2 創建PetSet

  創建PetSet的時候需要先創建一個“headless”的Service,即service顯示的將ClusterIP設置為none。而用戶可以通過直接訪問PetSet中的Pod的IP(通過Pod的HostName解析得到),來訪問后端的服務的。Kubernetes在1.4之后的dns插件之上才支持這種類型的DNS解析

[root@k8s-master pv]# cd ../petset/
[root@k8s-master petset]# cat test-petset.yaml 
# A headless service to create DNS records
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  # *.nginx.default.svc.cluster.local
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1alpha1
kind: PetSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
      annotations:
        pod.alpha.kubernetes.io/initialized: "true"
    spec:
      terminationGracePeriodSeconds: 0
      containers:
      - name: nginx
        image: gcr.io/google_containers/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes:
      - ReadWriteMany 
      resources:
        requests:
          storage: 1Gi
[root@k8s-master petset]# kubectl create -f test-petset.yaml 
service "nginx" created
petset "web" created 
[root@k8s-master petset]# kubectl get petset 
NAME      DESIRED   CURRENT   AGE
web       2         2         11s
[root@k8s-master petset]# kubectl get pod
NAME      READY     STATUS    RESTARTS   AGE
web-0       1/1       Running   0          16s
web-1       1/1       Running   0          12s
[root@k8s-master petset]# kubectl get pv
NAME  CAPACITY  ACCESSMODES RECLAIMPOLICY STATUS CLAIM  REASON    AGE
pv0001    5Gi  RWX   Recycle  Bound     default/www-web-0             1m
pv0002    5Gi  RWX   Recycle  Bound     default/www-web-1             1m
[root@k8s-master petset]# kubectl get pvc
NAME        STATUS    VOLUME    CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pv0001    5Gi        RWX           22s
www-web-1   Bound     pv0002    5Gi        RWX           22s
[root@k8s-master petset]# kubectl exec -ti web-0 /bin/bash
root@web-0:/# cd /usr/share/nginx/html
root@web-0:/usr/share/nginx/html# ls
root@web-0:/usr/share/nginx/html# touch 1.out
root@web-0:/usr/share/nginx/html# exit
exit
[root@k8s-master petset]# ssh 192.168.20.47 #登錄到NFS主機
root@192.168.20.47's password: 
Last login: Tue Mar 28 11:54:58 2017 from 10.0.251.145
[root@localhost ~]# echo "123456">> /data/disk1/1.out 
[root@localhost ~]# exit
登出
Connection to 192.168.20.47 closed.
[root@k8s-master petset]# kubectl exec -ti web-0 /bin/bash
root@web-0:/# cat /usr/share/nginx/html/1.out 
123456
root@web-0:/# exit
exit
[root@k8s-master petset]# kubectl exec -ti web-1 /bin/bash
root@web-1:/# cat /usr/share/nginx/html/1.out 
123456
root@web-1:/# exit
exit
[root@k8s-master petset]#

3.3 驗證域名解析

  檢查PetSet中的Pod的hostName

[root@k8s-master ~]# for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1

  檢查通過hostName解析IP

[root@k8s-master dns14]# kubectl get svc nginx 
NAME      CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx     None         <none>        80/TCP    5h
[root@k8s-master dns14]# kubectl describe svc nginx
Name:            nginx
Namespace:        default
Labels:            app=nginx
Selector:        app=nginx
Type:            ClusterIP
IP:            None
Port:            web    80/TCP
Endpoints:        10.0.28.3:80,10.0.82.6:80
Session Affinity:    None
No events.
[root@k8s-master ~]# kubectl get pod -o wide
NAME     READY     STATUS    RESTARTS   AGE       IP          NODE
web-0      1/1       Running   0          42m       10.0.28.3   k8s-node-1
web-1      1/1       Running   0          42m       10.0.82.6   k8s-node-4
[root@k8s-master ~]# kubectl exec -i -t frontend-service-1988680557-xuysd /bin/bash #進入集群中的一個pod
[root@frontend-service-1988680557-xuysd /]# nslookup web-0.nginx
Server:        10.254.10.2
Address:    10.254.10.2#53

Name:    web-0.nginx.default.svc.cluster.local
Address: 10.0.28.3

[root@frontend-service-1988680557-xuysd /]# nslookup web-1.nginx
Server:        10.254.10.2
Address:    10.254.10.2#53

Name:    web-1.nginx.default.svc.cluster.local
Address: 10.0.82.6

<680557-xuysd /]# nslookup web-1.nginx.default.svc.cluster.local  #通常情況下,直接解析web-1.nginx即可得到對應的IP,但再一些容器內發現只有解析全部的名稱“web-1.nginx.default.svc.cluster.local”才能得到IP,這個地方需要研究下區別在哪。
Server:        10.254.10.2
Address:    10.254.10.2#53

Non-authoritative answer:
Name:    web-1.nginx.default.svc.cluster.local
Address: 10.0.82.6

3.4 驗證Pet重建

#為web-0添加一個index頁面,內容為它自己的HostName,注意該目錄我們將其外掛到了PV之上,是一個NFS路徑
[root@k8s-master dns14]# kubectl exec web-0 -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html'
#在其它Pod上獲取web-0的歡迎頁
[root@k8s-master dns14]# kubectl exec -it web-1 -- curl web-0.nginx
web-0
[root@k8s-master dns14]# kubectl exec -it web-0 -- curl web-0.nginx
web-0
#查詢web-0的存活時間
[root@k8s-master dns14]# kubectl get pod | grep web
web-0                               1/1       Running   0          5h
web-1                               1/1       Running   0          5h
[root@k8s-master dns14]# kubectl delete pod web-0  #刪除web-0
pod "web-0" deleted
[root@k8s-master dns14]# kubectl get pod | grep web  #可以看到,web-0被刪除后幾乎立即又被重建了
web-0             1/1       Running   0          3s
web-1             1/1       Running   0          5h
[root@k8s-master dns14]# kubectl get pod | grep web
web-0            1/1       Running   0          5s
web-1            1/1       Running   0          5h
#查看web-0中的歡迎頁是否還在
[root@k8s-master dns14]# kubectl exec -it web-0 -- curl web-0.nginx
web-0
[root@k8s-master dns14]# kubectl exec -it web-1 -- curl web-0.nginx
web-0

4、運維

4.1 Pet互相發現

  通常,Pets需要互相知道對方的存在,在之前的示例中,我們演示了操作員“告訴”一個Pet,它有多少個同伴,但這顯然是不夠的。一種方法是,在Pod內部調用Kubectl的api來獲取該Pet對應的PetSet中的其他成員,但並不推薦這么做。這樣做的話,就會使得你的Pod可以反過來操控外部的組件。另一種方法是通過DNS解析,利用工具nslookup工具可以將nginx(上文定義的headless service)中的所有endPoint都查找出來。具體見下:

# apt-get update && apt-get install -y dnsutils
...
#  nslookup -type=srv nginx
Server:        10.254.10.2
Address:    10.254.10.2#53

nginx.default.svc.cluster.local    service = 10 50 0 web-1.nginx.default.svc.cluster.local.
nginx.default.svc.cluster.local    service = 10 50 0 web-0.nginx.default.svc.cluster.local.
# nslookup web-1.nginx.default.svc.cluster.local
Server:        10.254.10.2
Address:    10.254.10.2#53

Name:    web-1.nginx.default.svc.cluster.local
Address: 10.0.82.6

# nslookup web-0.nginx.default.svc.cluster.local
Server:        10.254.10.2
Address:    10.254.10.2#53

Name:    web-0.nginx.default.svc.cluster.local
Address: 10.0.28.3

4.2 更新及擴縮容

  如之前使用限制(3)中講的,對於PetSet,Kubernetes能幫我們自動做的僅有“replicas”,即副本數量。對於擴充副本數量來說,Kubernetes每次會按照順序,一個個的創建Pod,且在前一個沒有running或ready之前,不會創建下一個;對於縮減副本數量來說,Kubernetes每次會按照順序,一個個的刪除Pod。且在前一個沒有真正的shutdown之前,不會刪除下一個。

  需要注意的是,縮容時,雖然刪除了一些Pod,但Pod對於的持久化存儲PVC—PV是不會被刪除的。例如,我們一開始創建了3個Pet,pod-0、pod-1、pod-2,掛載了pvc-0——pv-0、pvc-1——pv-1、pvc-2——pv-2,在縮容到2個副本的時候,最后一個pod-2會被刪除,但pvc-2——pv-2則不會被刪除,里面的數據還是安全的。PV的最終刪除就像它一開始創建一樣,是由管理員統一管理的。

4.3 鏡像更新

  有時需要更新鏡像到新的版本,那該如何操作呢?雖然Kubernetes沒有給我們提供一些自動更新整個Pet集群的功能,但通過它提供的edit和set image功能也基本上夠我們用的了。更新Pet中的鏡像功能示例如下:

[root@k8s-master ~]# kubectl get po web-0 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
gcr.io/google_containers/nginx-slim:0.8
[root@k8s-master ~]# kubectl exec -i -t frontend-service-1988680557-xuysd /bin/bash #進入集群中的一個pod
[root@frontend-service-1988680557-xuysd /]# nslookup web-0.nginx
Server:        10.254.10.2
Address:    10.254.10.2#53

Name:    web-0.nginx.default.svc.cluster.local
Address: 10.0.28.3

[root@k8s-master ~]# kubectl edit petset/web  #執行之后就像打開一個vi界面,修改對應的鏡像名稱或版本,保存退出
petset "web" edited
#也可以用set image的方式進行更改
[root@k8s-master ~]#  kubectl set image petset/web nginx=gcr.io/google_containers/nginx-slim:0.7
petset "web" image updated 
[root@k8s-master ~]# kubectl delete po web-0  #手動刪除第一個Pod,PetSet會自動給我們再起一個
pod "web-0" deleted
[root@k8s-master ~]# kubectl get po web-0 -o wide  #查看Pod的IP已經更改了
NAME      READY     STATUS    RESTARTS   AGE       IP          NODE
web-0     1/1       Running   0          34s       10.0.62.4   k8s-node-2
[root@k8s-master ~]# kubectl get po web-0 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
gcr.io/google_containers/nginx-slim:0.7  #查看鏡像版本信息,版本也已經更改了

之后可以一個個的將PetSet中的Pod刪除,PetSet會自動按照新版本的鏡像幫我們啟動起來。

5、后續新功能

  官方給出后續將會逐步推出的新功能如下:

    1)        數據安全與本地數據存儲(目前僅支持網絡共享存儲卷,在IO高的情況下網絡存儲卷可能會出現性能瓶頸,后續Kubernetes將會推出支持本地存儲的PetSet)

    2)        豐富通知實踐

    3)        公網的網絡身份

    4)        廣域網集群發布(跨多個可用區、region、雲服務提供商)

    5)        更多的自動運維手段(包括鏡像升級等)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM