注:節選自《Kubernetes權威指南》,主要對經常使用的Kubernetes系統運維操做和技巧進行詳細說明。
一、Node的隔離和恢復node
apiVersion: v1 kind: Node metadata: name: kubernetes-minion1 labels: kubernetes.io/hostname: kubernetes-minion1 spec: unschedulable: true
而後,經過kubectl replace命令完成對Node狀態的修改:redis
$ kubectl replace -f unschedule_node.yaml nodes/kubernetes-minion1
查看Node的狀態,能夠觀察到在Node的狀態中增長了一項SchedulingDisabled:算法
$ kubectl get nodes NAME LABELS STATUS kubernetes-minion1 kubernetes.io/hostname=kubernetes-minion1 Ready, SchedulingDisabled
對於後續建立的Pod,系統將不會再向該Node進行調度。數據庫
另外一種方法是不使用配置文件,直接使用kubectl patch命令完成:api
$ kubectl patch node kubernetes-minion1 -p '{"spec":{"unschedulable":true}}'
須要注意的是,將某個Node脫離調度範圍時,在其上運行的Pod並不會自動中止,管理員須要手動中止在該Node上運行的Pod。 服務器
一樣,若是須要將某個Node從新歸入集羣調度範圍,則將unschedulable設置爲false,再次執行kubectl replace或kubectl patch命令就能恢復系統對該Node的調度。網絡
在實際生產系統中會常常遇到服務器容量不足的狀況,這時就須要購買新的服務器,而後將應用系統進行水平擴展來完成對系統的擴容。架構
在Kubernetes集羣中,對於一個新Node的加入是很是簡單的。能夠在Node節點上安裝Docker、Kubelet和kube-proxy服務,而後將Kubelet和kube-proxy的啓動參數中的Master URL指定爲當前Kubernetes集羣Master的地址,最後啓動這些服務。基於Kubelet的自動註冊機制,新的Node將會自動加入現有的Kubernetes集羣中,如圖1所示。 運維
圖1 新節點自動註冊完成擴容ide
Kubernetes Master在接受了新Node的註冊以後,會自動將其歸入當前集羣的調度範圍內,在以後建立容器時,就能夠向新的Node進行調度了。
經過這種機制,Kubernetes實現了集羣的擴容。
在實際生產系統中,咱們常常會遇到某個服務須要擴容的場景,也可能會遇到因爲資源緊張或者工做負載下降而須要減小服務實例數的場景。此時咱們能夠利用命令kubectl scale rc來完成這些任務。以redis-slave RC爲例,已定義的最初副本數量爲2,經過執行下面的命令將redis-slave RC控制的Pod副本數量從初始的2更新爲3:
$ kubectl scale rc redis-slave --replicas=3 scaled
執行kubectl get pods命令來驗證Pod的副本數量增長到3:
$ kubectl get pods NAME READY STATUS RESTARTS AGE redis-slave-4na2n 1/1 Running 0 1h redis-slave-92u3k 1/1 Running 0 1h redis-slave-palab 1/1 Running 0 2m
將--replicas設置爲比當前Pod副本數量更小的數字,系統將會「殺掉」一些運行中的Pod,便可實現應用集羣縮容:
$ kubectl scale rc redis-slave --replicas=1 scaled $ kubectl get pods NAME READY STATUS RESTARTS AGE redis-slave-4na2n 1/1 Running 0 1h
Label(標籤)做爲用戶可靈活定義的對象屬性,在已建立的對象上,仍然能夠隨時經過kubectl label命令對其進行增長、修改、刪除等操做。
例如,咱們要給已建立的Pod「redis-master-bobr0」添加一個標籤role=backend:
$ kubectl label pod redis-master-bobr0 role=backend
查看該Pod的Label:
$ kubectl get pods -Lrole NAME READY STATUS RESTARTS AGE ROLE redis-master-bobr0 1/1 Running 0 3m backend
刪除一個Label,只需在命令行最後指定Label的key名並與一個減號相連便可:
$ kubectl label pod redis-master-bobr0 role-
修改一個Label的值,須要加上--overwrite參數:
$ kubectl label pod redis-master-bobr0 role=master --overwrite
咱們知道,Kubernetes的Scheduler服務(kube-scheduler進程)負責實現Pod的調度,整個調度過程經過執行一系列複雜的算法最終爲每一個Pod計算出一個最佳的目標節點,這一過程是自動完成的,咱們沒法知道Pod最終會被調度到哪一個節點上。有時咱們可能須要將Pod調度到一個指定的Node上,此時,咱們能夠經過Node的標籤(Label)和Pod的nodeSelector屬性相匹配,來達到上述目的。
首先,咱們能夠經過kubectl label命令給目標Node打上一個特定的標籤,下面是此命令的完整用法:
kubectl label nodes <node-name> <label-key>=<label-value>
這裏,咱們爲kubernetes-minion1節點打上一個zone=north的標籤,代表它是「北方」的一個節點:
$ kubectl label nodes kubernetes-minion1 zone=north NAME LABELS STATUS kubernetes-minion1 kubernetes.io/hostname=kubernetes-minion1,zone=north Ready
上述命令行操做也能夠經過修改資源定義文件的方式,並執行kubectl replace -f xxx.yaml命令來完成。
而後,在Pod的配置文件中加入nodeSelector定義,以redis-master-controller.yaml爲例:
apiVersion: v1 kind: ReplicationController metadata: name: redis-master labels: name: redis-master spec: replicas: 1 selector: name: redis-master template: metadata: labels: name: redis-master spec: containers: - name: master image: kubeguide/redis-master ports: - containerPort: 6379 nodeSelector: zone: north
運行kubectl create -f命令建立Pod,scheduler就會將該Pod調度到擁有zone=north標籤的Node上去。
使用kubectl get pods -o wide命令能夠驗證Pod所在的Node:
# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE NODE redis-master-f0rqj 1/1 Running 0 19s kubernetes-minion1
若是咱們給多個Node都定義了相同的標籤(例如zone=north),則scheduler將會根據調度算法從這組Node中挑選一個可用的Node進行Pod調度。
這種基於Node標籤的調度方式靈活性很高,好比咱們能夠把一組Node分別貼上「開發環境」「測試驗證環境」「用戶驗收環境」這三組標籤中的一種,此時一個Kubernetes集羣就承載了3個環境,這將大大提升開發效率。
須要注意的是,若是咱們指定了Pod的nodeSelector條件,且集羣中不存在包含相應標籤的Node時,即便還有其餘可供調度的Node,這個Pod也最終會調度失敗。
當集羣中的某個服務須要升級時,咱們須要中止目前與該服務相關的全部Pod,而後從新拉取鏡像並啓動。若是集羣規模比較大,則這個工做就變成了一個挑戰,並且先所有中止而後逐步升級的方式會致使較長時間的服務不可用。Kubernetes提供了rolling-update(滾動升級)功能來解決上述問題。
滾動升級經過執行kubectl rolling-update命令一鍵完成,該命令建立了一個新的RC,而後自動控制舊的RC中的Pod副本數量逐漸減小到0,同時新的RC中的Pod副本數量從0逐步增長到目標值,最終實現了Pod的升級。須要注意的是,系統要求新的RC須要與舊的RC在相同的命名空間(Namespace)內,即不能把別人的資產偷偷轉移到自家名下。
以redis-master爲例,假設當前運行的redis-master Pod是1.0版本,則如今須要升級到2.0版本。
建立redis-master-controller-v2.yaml的配置文件以下:
apiVersion: v1 kind: ReplicationController metadata: name: redis-master-v2 labels: name: redis-master version: v2 spec: replicas: 1 selector: name: redis-master version: v2 template: metadata: labels: name: redis-master version: v2 spec: containers: - name: master image: kubeguide/redis-master:2.0 ports: - containerPort: 6379
在配置文件中有幾處須要注意:
(1)RC的名字(name)不能與舊的RC的名字相同;
(2)在selector中應至少有一個Label與舊的RC的Label不一樣,以標識其爲新的RC。
本例中新增了一個名爲version的Label,以與舊的RC進行區分。
運行kubectl rolling-update命令完成Pod的滾動升級:
kubectl rolling-update redis-master -f redis-master-controller-v2.yaml
Kubectl的執行過程以下:
Creating redis-master-v2 At beginning of loop: redis-master replicas: 2, redis-master-v2 replicas: 1 Updating redis-master replicas: 2, redis-master-v2 replicas: 1 At end of loop: redis-master replicas: 2, redis-master-v2 replicas: 1 At beginning of loop: redis-master replicas: 1, redis-master-v2 replicas: 2 Updating redis-master replicas: 1, redis-master-v2 replicas: 2 At end of loop: redis-master replicas: 1, redis-master-v2 replicas: 2 At beginning of loop: redis-master replicas: 0, redis-master-v2 replicas: 3 Updating redis-master replicas: 0, redis-master-v2 replicas: 3 At end of loop: redis-master replicas: 0, redis-master-v2 replicas: 3 Update succeeded. Deleting redis-master redis-master-v2
等全部新的Pod啓動完成後,舊的Pod也被所有銷燬,這樣就完成了容器集羣的更新。
另外一種方法是不使用配置文件,直接用kubectl rolling-update命令,加上--image參數指定新版鏡像名稱來完成Pod的滾動升級:
kubectl rolling-update redis-master --image=redis-master:2.0
與使用配置文件的方式不一樣,執行的結果是舊的RC被刪除,新的RC仍將使用舊的RC的名字。
Kubectl的執行過程以下:
Creating redis-master-ea866a5d2c08588c3375b86fb253db75 At beginning of loop: redis-master replicas: 2, redis-master-ea866a5d2c08588c 3375b86fb253db75 replicas: 1 Updating redis-master replicas: 2, redis-master-ea866a5d2c08588c3375b86fb253db 75 replicas: 1 At end of loop: redis-master replicas: 2, redis-master-ea866a5d2c08588c3375b86fb 253db75 replicas: 1 At beginning of loop: redis-master replicas: 1, redis-master-ea866a5d2c08588c 3375b86fb253db75 replicas: 2 Updating redis-master replicas: 1, redis-master-ea866a5d2c08588c3375b86fb 253db75 replicas: 2 At end of loop: redis-master replicas: 1, redis-master-ea866a5d2c08588c3375b86fb 253db75 replicas: 2 At beginning of loop: redis-master replicas: 0, redis-master-ea866a5d2c08588c 3375b86fb253db75 replicas: 3 Updating redis-master replicas: 0, redis-master-ea866a5d2c08588c3375b86fb253db 75 replicas: 3 At end of loop: redis-master replicas: 0, redis-master-ea866a5d2c08588c3375b86fb 253db75 replicas: 3 Update succeeded. Deleting old controller: redis-master Renaming redis-master-ea866a5d2c08588c3375b86fb253db75 to redis-master redis-master
能夠看到,Kubectl經過新建一個新版本Pod,停掉一箇舊版本Pod,逐步迭代來完成整個RC的更新。
更新完成後,查看RC:
$ kubectl get rc CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS redis-master master kubeguide/redis-master:2.0 deployment= ea866a5d2c08588c3375b86fb253db75,name=redis-master,version=v1 3
能夠看到,Kubectl給RC增長了一個key爲「deployment」的Label(這個key的名字可經過--deployment-label-key參數進行修改),Label的值是RC的內容進行Hash計算後的值,至關於簽名,這樣就能很方便地比較RC裏的Image名字及其餘信息是否發生了變化,它的具體做用能夠參見第6章的源碼分析。
若是在更新過程當中發現配置有誤,則用戶能夠中斷更新操做,並經過執行Kubectl rolling-update –rollback完成Pod版本的回滾:
$ kubectl rolling-update redis-master --image=kubeguide/redis-master:2.0 --rollback Found existing update in progress (redis-master-fefd9752aa5883ca4d53013a7b 583967), resuming. Found desired replicas.Continuing update with existing controller redis-master. At beginning of loop: redis-master-fefd9752aa5883ca4d53013a7b583967 replicas: 0, redis-master replicas: 3 Updating redis-master-fefd9752aa5883ca4d53013a7b583967 replicas: 0, redis-master replicas: 3 At end of loop: redis-master-fefd9752aa5883ca4d53013a7b583967 replicas: 0, redis-master replicas: 3 Update succeeded. Deleting redis-master-fefd9752aa5883ca4d53013a7b583967 redis-master
到此,能夠看到Pod恢復到更新前的版本了。
Kubernetes做爲容器應用的管理中心,經過對Pod的數量進行監控,而且根據主機或容器失效的狀態將新的Pod調度到其餘Node上,實現了應用層的高可用性。針對Kubernetes集羣,高可用性還應包含如下兩個層面的考慮:etcd數據存儲的高可用性和Kubernetes Master組件的高可用性。
**7.1 etcd高可用性方案
**
etcd在整個Kubernetes集羣中處於中心數據庫的地位,爲保證Kubernetes集羣的高可用性,首先須要保證數據庫不是單故障點。一方面,etcd須要以集羣的方式進行部署,以實現etcd數據存儲的冗餘、備份與高可用性;另外一方面,etcd存儲的數據自己也應考慮使用可靠的存儲設備。
etcd集羣的部署可使用靜態配置,也能夠經過etcd提供的REST API在運行時動態添加、修改或刪除集羣中的成員。本節將對etcd集羣的靜態配置進行說明。關於動態修改的操做方法請參考etcd官方文檔的說明。
首先,規劃一個至少3臺服務器(節點)的etcd集羣,在每臺服務器上安裝好etcd。
部署一個由3臺服務器組成的etcd集羣,其配置如表1所示,其集羣部署實例如圖2所示。
表1 etcd集羣的配置
而後修改每臺服務器上etcd的配置文件/etc/etcd/etcd.conf。
以etcd1爲建立集羣的實例,須要將其ETCD_INITIAL_CLUSTER_STATE設置爲「new」。etcd1的完整配置以下:
# [member] ETCD_NAME=etcd1 #etcd實例名稱 ETCD_DATA_DIR="/var/lib/etcd/etcd1" #etcd數據保存目錄 ETCD_LISTEN_PEER_URLS="http://10.0.0.1:2380" #集羣內部通訊使用的URL ETCD_LISTEN_CLIENT_URLS="http://10.0.0.1:2379" #供外部客戶端使用的URL …… #[cluster] ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.0.0.1:2380" #廣播給集羣內其餘成員使用的URL ETCD_INITIAL_CLUSTER="etcd1=http://10.0.0.1:2380,etcd2=http://10.0.0.2:2380, etcd3=http://10.0.0.3:2380" #初始集羣成員列表 ETCD_INITIAL_CLUSTER_STATE="new" #初始集羣狀態,new爲新建集羣 ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" #集羣名稱 ETCD_ADVERTISE_CLIENT_URLS="http://10.0.0.1:2379" #廣播給外部客戶端使用的URL
啓動etcd1服務器上的etcd服務:
$ systemctl restart etcd
啓動完成後,就建立了一個名爲etcd-cluster的集羣。
etcd2和etcd3爲加入etcd-cluster集羣的實例,須要將其ETCD_INITIAL_CLUSTER_STATE設置爲「exist」。etcd2的完整配置以下(etcd3的配置略):
# [member] ETCD_NAME=etcd2 #etcd實例名稱 ETCD_DATA_DIR="/var/lib/etcd/etcd2" #etcd數據保存目錄 ETCD_LISTEN_PEER_URLS="http://10.0.0.2:2380" #集羣內部通訊使用的URL ETCD_LISTEN_CLIENT_URLS="http://10.0.0.2:2379" #供外部客戶端使用的URL …… #[cluster] ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.0.0.2:2380" #廣播給集羣內其餘成員使用的URL ETCD_INITIAL_CLUSTER="etcd1=http://10.0.0.1:2380,etcd2=http://10.0.0.2:2380,etcd3=http://10.0.0.3:2380" #初始集羣成員列表 ETCD_INITIAL_CLUSTER_STATE="exist" # existing表示加入已存在的集羣 ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" #集羣名稱 ETCD_ADVERTISE_CLIENT_URLS="http://10.0.0.2:2379" #廣播給外部客戶端使用的URL
啓動etcd2和etcd3服務器上的etcd服務:
$ systemctl restart etcd
啓動完成後,在任意etcd節點執行etcdctl cluster-health命令來查詢集羣的運行狀態:
$ etcdctl cluster-health cluster is healthy member ce2a822cea30bfca is healthy member acda82ba1cf790fc is healthy member eba209cd0012cd2 is healthy
在任意etcd節點上執行etcdctl member list命令來查詢集羣的成員列表:
$ etcdctl member list ce2a822cea30bfca: name=default peerURLs=http://10.0.0.1:2380,http://10.0.0.1: 7001 clientURLs=http://10.0.0.1:2379,http://10.0.0.1:4001 acda82ba1cf790fc: name=default peerURLs=http://10.0.0.2:2380,http://10.0.0.2: 7001 clientURLs=http://10.0.0.2:2379,http://10.0.0.2:4001 eba209cd40012cd2: name=default peerURLs=http://10.0.0.3:2380,http://10.0.0.3: 7001 clientURLs=http://10.0.0.3:2379,http://10.0.0.3:4001
至此,一個etcd集羣就建立成功了。
以kube-apiserver爲例,將訪問etcd集羣的參數設置爲:
--etcd-servers=http://10.0.0.1:4001,http://10.0.0.2:4001,http://10.0.0.3:4001
在etcd集羣成功啓動以後,若是須要對集羣成員進行修改,則請參考官方文檔的詳細說明:點擊此處6
對於etcd中須要保存的數據的可靠性,能夠考慮使用RAID磁盤陣列、高性能存儲設備、NFS網絡文件系統,或者使用雲服務商提供的網盤系統等來實現。
7.2 Kubernetes Master組件的高可用性方案
在Kubernetes體系中,Master服務扮演着總控中心的角色,主要的三個服務kube-apiserver、kube-controller-mansger和kube-scheduler經過不斷與工做節點上的Kubelet和kube-proxy進行通訊來維護整個集羣的健康工做狀態。若是Master的服務沒法訪問到某個Node,則會將該Node標記爲不可用,再也不向其調度新建的Pod。但對Master自身則須要進行額外的監控,使Master不成爲集羣的單故障點,因此對Master服務也須要進行高可用方式的部署。
以Master的kube-apiserver、kube-controller-mansger和kube-scheduler三個服務做爲一個部署單元,相似於etcd集羣的典型部署配置。使用至少三臺服務器安裝Master服務,而且使用Active-Standby-Standby模式,保證任什麼時候候總有一套Master可以正常工做。
全部工做節點上的Kubelet和kube-proxy服務則須要訪問Master集羣的統一訪問入口地址,例如可使用pacemaker等工具來實現。圖3展現了一種典型的部署方式。
圖3 Kubernetes Master高可用部署架構