原文連接node
將以Redis爲例子,學習使用Headless Service與StatefulSet完成Redis集羣的建立。 以實現一個無單點故障、高可用、可動態擴展的Redis集羣。nginx
Headless服務是一種特殊的服務,其clusterIP值爲None。 這樣設置在運行時不會被分配ClusterIP,而在訪問過程當中,將會返回包含了其label指定的所有Pod列表,而後客戶端程序能夠自定義如何處理這個Pod列表。git
一般狀況下,Service若是有一個集羣IP,則在DNS查找Service時會返回該IP的記錄。 可是若是Service不須要集羣IP,則DNS將會返回Pod的IP列表。github
當前擁有一個Deployment資源,它管理了3個帶有 app: nginx 標籤的副本。redis
apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: publishNotReadyAddresses: true # 是否發佈未就緒的Pod ports: - port: 80 targetPort: http clusterIP: None # Headless服務須要clusterIP爲None selector: app: nginx 複製代碼
經過DNS發現Pod。 查看該服務在DNS上的對應記錄,會發現有對應的三個Pod的IP。json
$ nslookup nginx.default.svc.cluster.local 10.96.0.10 Server: 10.96.0.10 Address: 10.96.0.10#53 Name: nginx.default.svc.cluster.local Address: 172.40.0.2 Name: nginx.default.svc.cluster.local Address: 172.32.0.3 Name: nginx.default.svc.cluster.local Address: 172.32.0.4 複製代碼
另外會發現,這個Service上使用了一個 publishNotReadyAddresses
的屬性。api
正常狀況下Pod只有就緒後才能被DNS解析。而publishNotReadyAddresses爲true時,即便Pod未到就緒狀態,也能被DNS所解析。 Pod的就緒狀態由Pod的就緒探針決定。bash
在Kubernetes中RC、Deployment、ReplicaSet、DaemonSet等等都是面向無狀態服務的。 他們管理的Pod的IP、名字等都是隨機的,而在一些狀況下,這是不可行的。markdown
StatefulSet顧名思義因此有狀態的集合。它主要是爲了解決有狀態服務的問題。 與Deployment相似,StatefulSet管理了基於相同容器定義的一組Pod。網絡
但和Deployment不一樣的是,StatefulSet爲它們的每一個Pod維護了一個固定的ID。 這些Pod是基於相同的聲明來建立的,可是不能相互替換,不管怎麼調度,每一個Pod都有一個永久不變的ID。
type StatefulSet struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` // StatefulSet定義 Spec StatefulSetSpec `json:"spec,omitempty"` // StatefulSet狀態,可過期 Status StatefulSetStatus `json:"status,omitempty"` } type StatefulSetSpec struct { // 副本數量 Replicas *int32 `json:"replicas,omitempty"` // Pod的選擇器 Selector *metav1.LabelSelector `json:"selector"` // Pod模板 Template v1.PodTemplateSpec `json:"template"` // 一組存儲卷申請模板 // StatefulSet會參考模板爲每一個Pod分配一個專屬的存儲卷。 // 此字段的每一個模板項必須在Pod模板的容器配置中至少有一個匹配的volumeMount(名稱同樣便可)。 // 在模板中卷在同名稱的狀況下,該字段對應的卷優先於其餘任何卷。 VolumeClaimTemplates []v1.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty"` // 簡單的理解爲Headless Service名稱,用來爲StatefulSet提供可靠的網絡標識,須要在StatefulSet存在以前就存在 // Pod須要在建立後得到一個DNS/hostnames,格式爲podName.serviceName.default.svc.cluster.local // podName則由StatefulSet進行管理 ServiceName string `json:"serviceName"` // podManagementPolicy控制Pod再建立和擴/縮容時的方案。 // 該字段有兩個值OrderedReady(默認)和Parallel。 // OrderedReady在Pod建立時名字由0開始依次遞增,例如pod-0、pod-1。控制器會依次建立每一個Pod。 // 在縮容狀況下,Pod會按照名字從大到小依次刪除。 // Parallet會一次性建立/刪除全部的Pod,而不會等待上一個完成。 PodManagementPolicy PodManagementPolicyType `json:"podManagementPolicy,omitempty"` // Pod更新策略 UpdateStrategy StatefulSetUpdateStrategy `json:"updateStrategy,omitempty"` // 保存的歷史版本數量,用於回滾Pod,默認值爲10 RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty"` } // PodManagementPolicyType定義了在StatefulSet中Pod的建立規則 type PodManagementPolicyType string const ( // 按照嚴格的順序依次處理擴/縮容的狀況。同一時間最多隻處理一個Pod,處理完一個再處理下一個。 OrderedReadyPodManagement PodManagementPolicyType = "OrderedReady" // 同時處理全部的Pod。 ParallelPodManagement PodManagementPolicyType = "Parallel" ) 複製代碼
使用StatefulSet須要使用 Headless Service
與 PersistentVolume
。
Headless服務用來幫助StatefulSet識別Pod的網絡標識。
PersistenVolumn負責實現持久化存儲,例以下面例子中的redis node id。
搭建過程不展開了,網上不少。
持久存儲卷鬚要使用網絡文件服務來支持。NFS搭建起來比較簡單,因此使用了NFS。
因爲我有的集羣節點是分佈在兩個不一樣機房的物理機,因此設置了IP1和IP2兩個權限。
$ cat /etc/exports # /etc/exports: the access control list for filesystems which may be exported # to NFS clients. See exports(5). # # Example for NFSv2 and NFSv3: # /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check) # # Example for NFSv4: # /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check) # /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check) # /var/nfs/redis/pv1 IP1/0(rw,all_squash) IP2/0(rw,insecure,all_squash) /var/nfs/redis/pv2 IP1/0(rw,all_squash) IP2/0(rw,insecure,all_squash) /var/nfs/redis/pv3 IP1/0(rw,all_squash) IP2/0(rw,insecure,all_squash) /var/nfs/redis/pv4 IP1/0(rw,all_squash) IP2/0(rw,insecure,all_squash) /var/nfs/redis/pv5 IP1/0(rw,all_squash) IP2/0(rw,insecure,all_squash) /var/nfs/redis/pv6 IP1/0(rw,all_squash) IP2/0(rw,insecure,all_squash) 複製代碼
最後重啓nfs服務
爲方便收斂集羣中redis的配置。建立文件redis.conf,並建立一個ConfigMap。
appendonly yes
cluster-enabled yes
cluster-config-file /var/redis/node.conf
cluster-node-timeout 5000
dir /var/redis
port 6379
複製代碼
$ kubectl describe cm redis-conf
Name: redis-conf
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
redis.conf:
----
appendonly yes
cluster-enabled yes
cluster-config-file /var/redis/node.conf
cluster-node-timeout 5000
dir /var/redis
port 6379
Events: <none>
複製代碼
建立持久存儲卷資源,目前對這一起不是很瞭解,只知道配置中將NFS中對應的path掛載到節點上用做持久存儲。
apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv1 # pv1 pv2 ... pv6 spec: capacity: storage: 200M accessModes: - ReadWriteMany nfs: server: NFS_SERVER path: /var/nfs/redis/pv1 # pv1 pv2 ... pv 6 --- ...省略 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv6 spec: capacity: storage: 200M accessModes: - ReadWriteMany nfs: server: NFS_SERVER path: /var/nfs/redis/pv6 複製代碼
建立無頭服務,向StatefulSet提供Pod的列表。
apiVersion: v1 kind: Service metadata: name: redis-service labels: app: redis spec: ports: - name: redis-port port: 6379 clusterIP: None selector: app: redis appCluster: redis-cluster 複製代碼
建立有狀態集合資源。
apiVersion: apps/v1 kind: StatefulSet metadata: name: redis-app spec: serviceName: redis-service replicas: 6 selector: matchLabels: app: redis template: metadata: labels: app: redis appCluster: redis-cluster spec: containers: - name: redis image: redis command: - "redis-server" args: - "/etc/redis/redis.conf" - "--protected-mode no" ports: - name: redis containerPort: 6379 protocol: "TCP" volumeMounts: - name: "redis-conf" mountPath: "/etc/redis" - name: "redis-data" mountPath: "/var/redis" volumes: - name: "redis-conf" configMap: name: "redis-conf" items: - key: "redis.conf" path: "redis.conf" volumeClaimTemplates: - metadata: name: redis-data spec: accessModes: ["ReadWriteMany"] resources: requests: storage: 200M 複製代碼
建立一個Redis集羣的管理Pod,並依次將全部的Redis節點加入集羣。
$ redis-cli --cluster create \ 172.32.0.3:6379 \ 172.32.0.4:6379 \ 172.32.0.5:6379 \ 172.40.0.2:6379 \ 172.40.0.3:6379 \ 172.40.0.4:6379 \ --cluster-replicas 1 >>> Performing hash slots allocation on 6 nodes... (省略大量信息...) 複製代碼
在初始化完成以後,即可以經過 redis-cli -c -h redis-service
來鏈接Redis集羣了。
另外我也嘗試重啓了使某個master節點,觀察到了其slave變爲了master,而當原來的master從新上線後,變爲了slave。
須要注意的是,若是沒有使用 PersistentVolume
資源或者其餘支持持久化存儲的手段,則在某個Pod被殺死以後,其中Redis的節點信息便會丟失。 雖而後面會新建一個Pod,可是新的Pod會新建立一份節點數據,也不會自動加入到集羣中,集羣中Redis節點便會少一個。