Node,Pod,Replication Controller,Service等都是k8s中的一種“資源對象”,都可以通過工具kubectl執行增、刪、改的管理操作。其配置結果是保存在etcd中。k8s就像是一個自動化的資源控制系統,通過對etcd庫中保存的資源期望狀態和實際資源狀態進行差異對比,來發現、控制和進行“糾錯”。
1、k8s集羣管理角色——Master
每個k8s集羣裏需要一個Master節點來負責整個集羣的管理和控制,通常要佔據一個獨立的服務器,從高可用角度考慮則建議使用3臺服務器。
Master節點上運行着以下關鍵進程或服務:
k8s API Server,提供了HTTP Rest接口的關鍵服務進程,是集羣中所有資源增、刪、改操作的唯一入口,也是控制集羣的唯一入口。
k8s Controller Manager,是集羣中所有資源對象的運行指揮中心。
k8s Scheduler,是負責調度Pod資源的進程。
etcd服務,提供集羣中所有資源對象配置的持久化。
2、k8s集羣管理角色——Node
Node是k8s集羣中的工作負載節點,當某個Node當機時,其上的工作負載會被Master自動轉移到其他節點上去。
Node節點上運行着以下關鍵進程或服務:
Kublete,負責Pod對應的容器的創建、啓停管理,與Master節點協作,實現集羣管理的基本功能。
kube-proxy,是提供k8s的通信與負載均衡功能的重要組件。
Docker Engine,docker引擎。
Node節點可以動態地加入到k8s集羣中,在這個過程中kublete找到Master完成信息註冊。在加入集羣后,kublete進程就定時向Master節點發送操作系統、Docker版本、CPU、內存的統計信息,以及當前有哪些Pods在運行。當某個Node超時未發送以上信息時,會被Master判定爲“失聯”(Not Ready),隨即觸發工作負載轉移的流程。
查看集羣中有哪些Nodes:
[root@gqtest ~]# kubectl get nodes
NAME STATUS AGE
127.0.0.1 Ready 3d
查看某個Node的詳細信息:
[root@gqtest ~]# kubectl describe node "127.0.0.1"
Name: 127.0.0.1
Role:
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/hostname=127.0.0.1
Taints: <none>
CreationTimestamp: Sat, 17 Feb 2018 22:14:43 +0800
Phase:
Conditions:
Type Status LastHeartbeatTime LastTransitionTime Reason Message
---- ------ ----------------- ------------------ ------ -------
OutOfDisk False Wed, 21 Feb 2018 10:40:50 +0800 Sat, 17 Feb 2018 22:14:43 +0800 KubeletHasSufficientDisk kubelet has sufficient disk space available
MemoryPressure False Wed, 21 Feb 2018 10:40:50 +0800 Sat, 17 Feb 2018 22:14:43 +0800 KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False Wed, 21 Feb 2018 10:40:50 +0800 Sat, 17 Feb 2018 22:14:43 +0800 KubeletHasNoDiskPressure kubelet has no disk pressure
Ready True Wed, 21 Feb 2018 10:40:50 +0800 Wed, 21 Feb 2018 10:40:00 +0800 KubeletReady kubelet is posting ready status
Addresses: 127.0.0.1,127.0.0.1,127.0.0.1
Capacity:
alpha.kubernetes.io/nvidia-gpu: 0
cpu: 2
memory: 1532144Ki
pods: 110
Allocatable:
alpha.kubernetes.io/nvidia-gpu: 0
cpu: 2
memory: 1532144Ki
pods: 110
System Info:
Machine ID: 3be0a8ad023f4dd0b530ddcaeecf83cd
System UUID: E351A3F3-7D82-4C97-A174-297F8526DDBD
Boot ID: d40f85db-90fe-4904-92cc-847b6f136058
Kernel Version: 3.10.0-693.17.1.el7.x86_64
OS Image: CentOS Linux 7 (Core)
Operating System: linux
Architecture: amd64
Container Runtime Version: docker://1.12.6
Kubelet Version: v1.5.2
Kube-Proxy Version: v1.5.2
ExternalID: 127.0.0.1
Non-terminated Pods: (3 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
--------- ---- ------------ ---------- --------------- -------------
default mysql-mn49n 0 (0%) 0 (0%) 0 (0%) 0 (0%)
default myweb-k6fp0 0 (0%) 0 (0%) 0 (0%) 0 (0%)
default myweb-m9nv9 0 (0%) 0 (0%) 0 (0%) 0 (0%)
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.
CPU Requests CPU Limits Memory Requests Memory Limits
------------ ---------- --------------- -------------
0 (0%) 0 (0%) 0 (0%) 0 (0%)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
1m 1m 1 {kubelet 127.0.0.1} Normal Starting Starting kubelet.
1m 1m 1 {kubelet 127.0.0.1} Warning ImageGCFailed unable to find data for container /
1m 1m 1 {kubelet 127.0.0.1} Normal NodeHasSufficientDisk Node 127.0.0.1 status is now: NodeHasSufficientDisk
1m 1m 1 {kubelet 127.0.0.1} Normal NodeHasSufficientMemory Node 127.0.0.1 status is now: NodeHasSufficientMemory
1m 1m 1 {kubelet 127.0.0.1} Normal NodeHasNoDiskPressure Node 127.0.0.1 status is now: NodeHasNoDiskPressure
1m 1m 1 {kubelet 127.0.0.1} Warning Rebooted Node 127.0.0.1 has been rebooted, boot id: d40f85db-90fe-4904-92cc-847b6f136058
1m 1m 1 {kubelet 127.0.0.1} Normal NodeNotReady Node 127.0.0.1 status is now: NodeNotReady
58s 58s 1 {kubelet 127.0.0.1} Normal NodeReady Node 127.0.0.1 status is now: NodeReady
1m 55s 2 {kubelet 127.0.0.1} Warning MissingClusterDNS kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "myweb-k6fp0_default(3ac7a156-1414-11e8-9692-0800275f8277)". Falling back to DNSDefault policy.
1m 53s 2 {kubelet 127.0.0.1} Warning MissingClusterDNS kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "myweb-m9nv9_default(3ac7b4c7-1414-11e8-9692-0800275f8277)". Falling back to DNSDefault policy.
1m 48s 2 {kubelet 127.0.0.1} Warning MissingClusterDNS kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "mysql-mn49n_default(cee70dc4-140e-11e8-9692-0800275f8277)". Falling back to DNSDefault policy.
3、k8s集羣最小資源調度管理單位——Pod
一般情況下,Pod是Kubernetes創建或部署的最小/最簡單的基本單位,一個Pod代表集羣上正在運行的一個進程。
注:在單個Pod中共同管理多個容器是一個相對高級的用法,應該只有在容器緊密耦合的特殊實例中使用此模式。
Kubernetes中的Pod使用可分兩種主要方式:
Pod中運行一個容器。“one-container-per-Pod”模式是Kubernetes最常見的用法。在這種情況下,你可以將Pod視爲單個封裝的容器,但是Kubernetes是直接管理Pod而不是容器。
Pods中運行多個需要一起工作的容器。Pod可以封裝緊密耦合的應用,它們需要由多個容器組成,它們之間能夠共享資源,這些容器可以形成一個單一的內部service單位。
每個Pod都是運行應用的單個實例,如果需要水平擴展應用(例如,運行多個實例),則應該使用多個Pods,每個實例一個Pod。在Kubernetes中,這樣通常稱爲Replication。Replication的Pod通常由Controller創建和管理。
Pods提供兩種共享資源:網絡和存儲。
網絡,每個Pod被分配一個獨立的IP地址,Pod中的每個容器共享網絡命名空間,包括IP地址和網絡端口。Pod內的容器可以使用localhost相互通信。當Pod中的容器與Pod 外部通信時,他們必須協調如何使用共享網絡資源(如端口)。k8s要求底層網絡支持集羣內任意兩個Pod之間的TCP/IP直接通信,這一般是採用虛擬二層網絡技術來實現的,如Open vSwitch。
存儲,Pod可以指定一組共享存儲volumes。Pod中的所有容器都可以訪問共享volumes,允許這些容器共享數據。volumes 還用於Pod中的數據持久化,以防其中一個容器需要重新啓動而丟失數據。
Pod的幾個關鍵知識點:
很少會直接在kubernetes中創建單個Pod。因爲Pod的生命週期是短暫的,用後即焚的實體。當Pod被創建後(不論是由你直接創建還是被其他Controller),都會被Kuberentes調度到集羣的Node上。直到Pod的進程終止、被刪掉、因爲缺少資源而被驅逐、或者Node故障之前這個Pod都會一直保持在那個Node上。
Pod不會自愈。如果Pod運行的Node故障,或者是調度器本身故障,這個Pod就會被刪除。同樣的,如果Pod所在Node缺少資源或者Pod處於維護狀態,Pod也會被驅逐。Kubernetes使用更高級的稱爲Controller的抽象層,來管理Pod實例。雖然可以直接使用Pod,但是在Kubernetes中通常是使用Controller來管理Pod。
Controller可以創建和管理多個Pod,提供副本管理、滾動升級和集羣級別的自愈能力。例如,如果一個Node故障,Controller就能自動將該節點上的Pod調度到其他健康的Node上。通常,Controller會用你提供的Pod Template來創建相應的Pod。
Pod都會包含一個特殊的“根容器”,一個或多個業務容器。當根容器“死亡”了時,代表該Pod整個容器組的不可用。
Endpoint,Pod的IP加上Pod裏的容器端口(containerPort)稱爲一個Endpoint,代表着此Pod裏的一個服務進程的對外通信地址。
Pod Event事件,可以使用kubectl describe pod xxxx查看一個Pod的詳細信息,其中包括了該Pod所發生的每個事件的信息。Event事件信息對於故障排查很有幫助。
Pod的類型:
普通Pod
靜態Pod,不受etcd的配置信息管控,直接存放在某個Node上且只在該Node上運行。
Pod的資源限額:
CPU限額,資源限制的單位爲cpu Core的數量,該資源是以絕對值計算的。k8s將1/1000的CPU Core的資源定義爲可供分配的最小CPU資源單位,稱爲m。通常一個容器的CPU配額被定義爲100~300m,即佔用0.1~0.3個CPU Core。因爲該配額是一個資源的絕對值,所以無論是在一個只有2 CPU Cores的機器上,還是在一個有48CPU Cores的機器上,100m所代表的CPU使用量都是一樣的。
內存限額,內存的資源限制是以節節爲計量單位,內存的配額也是按資源的絕對值進行分配的。
k8s中配置資源限額的方法:
Requests設置,一個資源的最小申請量,是系統必須滿足的一個用量;
Limits設置,該資源的最大允許使用量,不能被突破,當容器試圖突破該參數限制時,可能會被k8s Kii並重啓。
一般是結合使用以上兩個參數去定義一個資源的配額,如下所示。
spec:
containers:
- name: db
image: mysql
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
4、Label和Label Selector
Label是一個key/value的鍵值對,可以附加到各種資源對象上。
一個資源對象可以定義任意數量的Label,同一個Label也可以被添加到任意數量的資源對象上去。
通常是在資源對象定義時確定Label,也支持在對象創建後動態添加或刪除。
Label和Lable Selector構成了k8s系統中最核心的一個應用模型,可以對被管理對象進行精細分組,以實現整個集羣的高可用。
通過給指定的資源對象捆綁一個或多個不同的Label來實現多維度的資源分組管理。一些示例標籤如下:
"release" : "stable", "release" : "canary"
"environment" : "dev","environment" : "qa","environment" : "production"
"tier" : "frontend","tier" : "backend","tier" : "cache"
"partition" : "customerA", "partition" : "customerB"
"track" : "daily", "track" : "weekly"
Label Selector可以查詢和篩選擁有某些Label的資源對象。
目前支持兩種選擇器:equality-based(基於平等)和set-based(基於集合)的。前者採用等式的表達式,後者採用集合操作的表達式匹配標籤。
標籤選擇器可以由逗號分隔的多個requirements 組成。在多重需求的情況下,必須滿足所有要求,因此逗號分隔符作爲AND邏輯運算符。
equality-based示例:
name = redis-slave
env != production
set-based示例:
name in (redis-master, redis-slave)
name not in (frontend)
Label Selector set-based篩選功能僅在以下新出現的管理對象中得到了支持:
Deployment
ReplicaSet
DaemonSet
Job
Label Selector的幾個重要使用場景:
kube-controller進程,通過資源對象RC上定義的Label Selector來篩選要監控和管理的Pod副本的數量。
kube-proxy進程,通過Service的Lable Selector來選擇對應的Pod,建立起每個Service到對應Pod的請求轉發路由表,進而實現Service的智能負載均衡機制。
kube-scheduler進程,通過爲某些Node定義特定的Label,然後在Pod定義文件中使用NodeSelector進行篩選,進而實現Pod的定向調度功能。
5、Replication Controller與Replica Set
ReplicationController(簡稱RC),定義了一個期望的場景,即聲明某種Pod的副本數量在任意時刻都符合某個預期值。通過RC,k8s實現了用戶應用集羣的高可用性,同時大減少了系統管理員在傳統IT環境中需要完成的許多手工運維工作(如主機監控、應用監控和故障恢復等)。
ReplicationController是早期k8s版本中主要使用的一項技術,在較新版本中,RC的功能已經逐漸被功能更強大的ReplicaSet和Deployment的組合所替代。
因爲ReplicationController、ReplicaSet和Deployment之間存在着巨大的相似性,所以仍然有了解和掌握RC知識和使用方法的必要。
RC會包含以下幾個部分:
Pod期待的副本數;
用於篩選目標Pod的Label Selector;
當Pod的副本數量小於預期時,用於創建新Pod的Pod模板。
修改RC的副本數量可以實現Pod的動態縮放:
$ kubectl scale rc redis-slave --replicas=3
關於刪除RC:
刪除RC並不會影響通過該RC已經創建好的Pod;
爲了刪除使用該RC創建的Pods,可以設置replicas爲0,然後更新該RC;
使用kubectl delete命令刪除RC及其所有pod。
關於ReplicaSet的知識:
由於Replication Controller與k8s代碼中的模塊Replication Controller同名,所以在k8s v1.2時就升級成了另外一個新的工具Replica Set。二者之間唯一的區別是,Replica Set支持了基於集合的Label Selector功能。
ReplicaSet是支持新的set-based選擇器要求的下一代ReplicationController 。它主要用作Deployment協調pod創建、刪除和更新。雖然ReplicaSets可以獨立使用,但它主要被 Deployments用作pod 機制的創建、刪除和更新,建議使用Deployment而不是直接使用ReplicaSets。
ReplicaSet的使用示例(部分):
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: frontend
spec:
replicas: 3
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels:
app: guestbook
tier: frontend
spec:
RC(Replica Set)的一些特性:
在大多數情況下,我們通過定義一個RC實現Pod的創建過程及副本數量的自動控制。
RC裏包括完整的Pod定義模板。
RC通過Label Selector機制實現對Pod副本的自動控制。
通過改變RC裏的Pod副本數量,可以實現Pod的擴容或縮容功能。
通過改變RC裏Pod模板中的鏡像版本,可以實現Pod的滾動升級功能。
6、Deployment
Deployment爲Pod和Replica Set(升級版的 Replication Controller)提供聲明式更新。
注意:您不該手動管理由 Deployment 創建的 Replica Set,否則您就篡越了 Deployment controller 的職責!
Deployment的典型的用例如下:
創建一個Deployment對象來生成對應的ReplicaSet,並完成Pod副本的創建過程。
檢查Deployment的狀態來查看部署動作是否完成,Pod副本的數量是否達到預期的值。
更新Deployment以創建新的Pod,通過修改Pod-Template-Spec字段來聲明Pod的新狀態。這會創建一個新的ReplicaSet,Deployment會按照控制的速率將pod從舊的ReplicaSet移動到新的ReplicaSet中。
如果當前狀態不穩定,則回滾到一個早先的Deployment版本。
暫停Deployment,以便於一次性修改多個PodTemplateSpec的配置項,然後再恢復Deployment,進行新的發佈。
擴容Deployment以滿足更高的負載。
查看Deployment 的狀態,以此作爲發佈是否成功的指標。
清除舊的不必要的 ReplicaSets。
(1)創建 Deployment
下面是一個 Deployment 示例,它創建了一個 ReplicaSet 來啓動3個 nginx pod。
下載示例文件nginx-deployment.yaml並執行命令:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
執行創建命令:
[root@gqtest ~]# kubectl create -f nginx-deployment.yaml --record
deployment "nginx-deployment" created
注:--record選項可以記錄當前命令執行記錄,便於以後查看一個deployment revision中執行了哪些命令。
[root@gqtest ~]# kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 1 1 1 0 2m
看到available數量仍爲0,繼續查看一下該deployment的詳細情況:
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Wed, 21 Feb 2018 17:02:45 +0800
Labels: app=nginx
Selector: app=nginx
Replicas: 1 updated | 1 total | 0 available | 1 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-4087004473 (1/1 replicas created)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
3m 3m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-4087004473 to 1
上面輸出信息沒有異常,再查看一次該deployment的狀態,如下所示,available的實例數量已經是1,說明經過一個創建過程(往往會因爲要在線下載docker image鏡像而需要等待一段時間),已經成功創建:
[root@gqtest ~]# kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 1 1 1 1 4m
查看Replica Set信息,可以到到ReplicaSet名稱是在Deployment後添加一串數字:
[root@gqtest ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-4087004473 1 1 1 4m
繼續查看pod的狀態,如下所示:
[root@gqtest ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-mn49n 1/1 Running 1 3d
myweb-k6fp0 1/1 Running 1 3d
myweb-m9nv9 1/1 Running 1 3d
nginx-deployment-4087004473-xdxhn 1/1 Running 0 7m
(2)更新Deployment
Deployment的更新(rollout)當且僅當Deployment的pod template中的label發生更新或者鏡像發生更改時,纔會被觸發。像Deployment擴容,不會觸發rollout事件。
假如我們現在想要讓 nginx pod 使用nginx:1.9.1的鏡像來代替原來的nginx:1.7.9的鏡像。
[root@gqtest ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
deployment "nginx-deployment" image updated
或者可以使用edit命令來編輯 Deployment,修改 .spec.template.spec.containers[0].image ,將nginx:1.7.9 改寫成 nginx:1.9.1。
[root@gqtest ~]# kubectl edit deployment/nginx-deployment
查看更新結果:
[root@gqtest ~]# kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 0 of 1 updated replicas are available...
[root@gqtest ~]# kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 1 1 1 0 18m
[root@gqtest ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-3599678771 1 1 0 4m
nginx-deployment-4087004473 0 0 0 19m
從上面的輸出信息中看到,正在創建一個新的ReplicaSet的過程中,同時已經將舊的縮容到了0個replica。
查看pod狀態時,已經看不到舊的pod了:
[root@gqtest ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-mn49n 1/1 Running 1 3d
myweb-k6fp0 1/1 Running 1 3d
myweb-m9nv9 1/1 Running 1 3d
nginx-deployment-3599678771-chzwr 1/1 Running 0 6m
再次執行查看Deployment更新結果的命令,顯示已經成功完成:
[root@gqtest ~]# kubectl rollout status deployment/nginx-deployment
deployment "nginx-deployment" successfully rolled out
[root@gqtest ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-3599678771 1 1 1 8m
nginx-deployment-4087004473 0 0 0 23m
最後查看一下該Deployment任務的詳細信息:
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Wed, 21 Feb 2018 17:02:45 +0800
Labels: app=nginx
Selector: app=nginx
Replicas: 1 updated | 1 total | 1 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-3599678771 (1/1 replicas created)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
25m 25m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-4087004473 to 1
10m 10m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-3599678771 to 1
10m 10m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-4087004473 to 0
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 都創建完成後纔開始改變航道。
Label selector 更新:
上面的演示是基於image文件發生了更改的條件下的一個Deployment更新的樣例。通常不鼓勵更新 label selector,雖然技術上可行,但關聯影響較多。
(3)回退Deployment
可以通過設置.spec.revisonHistoryLimit項來指定 deployment 最多保留多少 revision 歷史記錄。默認的會保留所有的 revision;如果將該項設置爲0,Deployment就不允許回退了。
只要 Deployment 的 rollout 被觸發就會創建一個 revision。也就是說當且僅當 Deployment 的 Pod template(如.spec.template)被更改,例如更新template 中的 label 和容器鏡像時,就會創建出一個新的 revision。
我們先故意執行一個有錯誤的Deployment任務:
[root@gqtest ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.99
deployment "nginx-deployment" image updated
我們故障寫了一個不存在的鏡像文件版本,Deployment rollout任務會補卡住,如下所示:
[root@gqtest ~]# kubectl rollout status deployments nginx-deployment
Waiting for rollout to finish: 0 of 1 updated replicas are available...
可以看到pod的狀態處於"ImagePullBackOff"的錯誤狀態:
[root@gqtest ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-3599678771 0 0 0 23m
nginx-deployment-4087004473 0 0 0 38m
nginx-deployment-538426637 1 1 0 3m
[root@gqtest ~]# kubectl rollout status deployments nginx-deployment
Waiting for rollout to finish: 0 of 1 updated replicas are available...
^C[root@gqtest ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-mn49n 1/1 Running 1 3d
myweb-k6fp0 1/1 Running 1 3d
myweb-m9nv9 1/1 Running 1 3d
nginx-deployment-538426637-g55n4 0/1 ImagePullBackOff 0 3m
注:Deployment controller會自動停止壞的 rollout,並停止擴容新的 ReplicaSet。
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Wed, 21 Feb 2018 17:02:45 +0800
Labels: app=nginx
Selector: app=nginx
Replicas: 1 updated | 1 total | 0 available | 1 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-538426637 (1/1 replicas created)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
40m 40m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-4087004473 to 1
25m 25m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-3599678771 to 1
25m 25m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-4087004473 to 0
5m 5m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-538426637 to 1
5m 5m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-3599678771 to 0
我們需要回退到穩定版本的Deployment revision。
先查看一下Deployment的revision記錄:
[root@gqtest ~]# kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION CHANGE-CAUSE
1 kubectl create -f nginx-deployment.yaml --record
2 kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
3 kubectl set image deployment/nginx-deployment nginx=nginx:1.99
注:因爲我們創建 Deployment 的時候使用了--recored參數可以記錄命令,我們可以很方便的查看每次 revision 的變化。
查看單個revision 的詳細信息:
[root@gqtest ~]# kubectl rollout history deployment/nginx-deployment --revision=2
deployments "nginx-deployment" with revision #2
Labels: app=nginx
pod-template-hash=3599678771
Annotations: kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
Containers:
nginx:
Image: nginx:1.9.1
Port: 80/TCP
Volume Mounts: <none>
Environment Variables: <none>
No volumes.
回退到歷史版本
回退到上一個版本:
[root@gqtest ~]# kubectl rollout undo deployment/nginx-deployment
deployment "nginx-deployment" rolled back
回退到一個指定的版本:
# kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment "nginx-deployment" rolled back
查看下回退結果:
[root@gqtest ~]# kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 1 1 1 1 46m
查看下回退版本所產生的事件記錄:
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Wed, 21 Feb 2018 17:02:45 +0800
Labels: app=nginx
Selector: app=nginx
Replicas: 1 updated | 1 total | 1 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-3599678771 (1/1 replicas created)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
46m 46m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-4087004473 to 1
32m 32m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-4087004473 to 0
11m 11m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-538426637 to 1
11m 11m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-3599678771 to 0
32m 2m 2 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-3599678771 to 1
2m 2m 1 {deployment-controller } Normal DeploymentRollback Rolled back deployment "nginx-deployment" to revision 2
2m 2m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-538426637 to 0
可以通過設置.spec.revisonHistoryLimit項來指定 deployment 最多保留多少 revision 歷史記錄。默認的會保留所有的 revision;如果將該項設置爲0,Deployment就不允許回退了。
(4)Deployment 擴容
使用以下命令將nginx-deployment擴容爲3副本:
[root@gqtest ~]# kubectl scale deployment nginx-deployment --replicas 3
deployment "nginx-deployment" scaled
[root@gqtest ~]# kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 51m
[root@gqtest ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-3599678771 3 3 3 36m
nginx-deployment-4087004473 0 0 0 51m
nginx-deployment-538426637 0 0 0 16m
[root@gqtest ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-mn49n 1/1 Running 1 3d
myweb-k6fp0 1/1 Running 1 3d
myweb-m9nv9 1/1 Running 1 3d
nginx-deployment-3599678771-hz7l6 1/1 Running 0 7m
nginx-deployment-3599678771-j136m 1/1 Running 0 1m
nginx-deployment-3599678771-kqwpf 1/1 Running 0 1m
查看該Deployment的事件記錄:
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Wed, 21 Feb 2018 17:02:45 +0800
Labels: app=nginx
Selector: app=nginx
Replicas: 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-3599678771 (3/3 replicas created)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
52m 52m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-4087004473 to 1
37m 37m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-4087004473 to 0
17m 17m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-538426637 to 1
17m 17m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-3599678771 to 0
37m 7m 2 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-3599678771 to 1
7m 7m 1 {deployment-controller } Normal DeploymentRollback Rolled back deployment "nginx-deployment" to revision 2
7m 7m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-538426637 to 0
1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-3599678771 to 3
注:假如集羣中啓用了horizontal pod autoscaling,您可以給 Deployment 設置一個 autoscaler,基於當前 Pod的 CPU 利用率選擇最少和最多的 Pod 數。
關於Deployment的比例擴容:
RollingUpdate Deployment 支持同時運行一個應用的多個版本。或者 autoscaler 擴 容 RollingUpdate Deployment 的時候,正在中途的 rollout(進行中或者已經暫停的),爲了降低風險,Deployment controller 將會平衡已存在的活動中的 ReplicaSet(有 Pod 的 ReplicaSet)和新加入的 replica。這被稱爲比例擴容。
(5)暫停和恢復Deployment
可以在發出一次或多次更新前暫停一個 Deployment,然後再恢復它。這樣您就能多次暫停和恢復 Deployment,在此期間進行一些修復工作,而不會發出不必要的 rollout。
[root@gqtest ~]# kubectl rollout pause deployment/nginx-deployment
deployment "nginx-deployment" paused
[root@gqtest ~]# kubectl set image deploy/nginx-deployment nginx=nginx:1.9.2
deployment "nginx-deployment" image updated
即使執行了上面的鏡像更改命令,也沒有觸發任何Deployment rollout的事件。而且可以繼續做更多的修改,例如:
[root@gqtest ~]# kubectl set resources deployment nginx-deployment -c=nginx --limits=cpu=200m,memory=256Mi
deployment "nginx-deployment" resource requirements updated
注:-c選項的作用是指定容器唯一標識名
當各種變更都準備妥當後,我們恢復該deployment:
[root@gqtest ~]# kubectl rollout resume deploy nginx-deployment
deployment "nginx-deployment" resumed
查看一下deployment處理結果,使用-w選項(watch)跟蹤輸出如下所示:
[root@gqtest ~]# kubectl get rs -w
NAME DESIRED CURRENT READY AGE
nginx-deployment-27997383 2 2 0 2m
nginx-deployment-3599678771 2 2 2 1h
nginx-deployment-4087004473 0 0 0 1h
nginx-deployment-538426637 0 0 0 59m
NAME DESIRED CURRENT READY AGE
nginx-deployment-27997383 2 2 1 4m
nginx-deployment-3599678771 1 2 2 1h
nginx-deployment-3599678771 1 2 2 1h
nginx-deployment-27997383 3 2 1 4m
nginx-deployment-3599678771 1 1 1 1h
nginx-deployment-27997383 3 2 1 4m
nginx-deployment-27997383 3 3 1 4m
nginx-deployment-27997383 3 3 2 5m
nginx-deployment-3599678771 0 1 1 1h
nginx-deployment-3599678771 0 1 1 1h
nginx-deployment-3599678771 0 0 0 1h
可以看到已經成功完成了合併了多次更新內容後的deployment任務:
[root@gqtest ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-27997383 3 3 3 13m
nginx-deployment-3599678771 0 0 0 1h
nginx-deployment-4087004473 0 0 0 1h
nginx-deployment-538426637 0 0 0 1h
(6)Deployment 狀態
Deployment 在生命週期中有多種狀態。在創建一個新的 ReplicaSet 的時候它可以是 progressing 狀態, complete 狀態,或者 fail to progress 狀態。
進行中的 Deployment
Kubernetes 將執行過下列任務之一的 Deployment 標記爲 progressing 狀態:
Deployment 正在創建新的ReplicaSet過程中。
Deployment 正在擴容一個已有的 ReplicaSet。
Deployment 正在縮容一個已有的 ReplicaSet。
有新的可用的 pod 出現。
可以使用kubectl rollout status命令監控 Deployment 的進度。
完成的 Deployment
Kubernetes 將包括以下特性的 Deployment 標記爲 complete 狀態:
Deployment 最小可用。最小可用意味着 Deployment 的可用 replica 個數等於或者超過 Deployment 策略中的期望個數。
所有與該 Deployment 相關的replica都被更新到了指定版本,也就說更新完成。
該 Deployment 中沒有舊的 Pod 存在。
可以用kubectl rollout status命令查看 Deployment 是否完成。如果 rollout 成功完成,kubectl rollout status將返回一個0值的 Exit Code。
[root@gqtest ~]# kubectl rollout status deploy/nginx-deployment
deployment "nginx-deployment" successfully rolled out
[root@gqtest ~]# echo $?
0
版本記錄的清理策略
可以設置 Deployment 中的 .spec.revisionHistoryLimit 項來指定保留多少舊的 ReplicaSet。 餘下的將在後臺被當作垃圾收集。默認的,所有的 revision 歷史就都會被保留。在未來的版本中,將會更改爲2。
注意: 將該值設置爲0,將導致所有的 Deployment 歷史記錄都會被清除,該 Deployment 就無法再回退了。
(7)金絲雀 Deployment
如果想要使用 Deployment 對部分用戶或服務器發佈 release,可以創建多個 Deployment,每個 Deployment 對應一個 release。
(8)編寫 Deployment Spec
在所有的 Kubernetes 配置中,Deployment 也需要apiVersion,kind和metadata這些配置項。
Deployment也需要 .spec section.
Pod Template
.spec.template 是 .spec中唯一要求的字段。
.spec.template 是 pod template. 它跟 Pod有一模一樣的schema,除了它是嵌套的並且不需要apiVersion 和 kind字段。
爲了劃分Pod的範圍,Deployment中的pod template必須指定適當的label(不要跟其他controller重複了)和適當的重啓策略。
.spec.template.spec.restartPolicy 可以設置爲 Always , 如果不指定的話這就是默認配置。
Replicas
.spec.replicas 是可以選字段,指定期望的pod數量,默認是1。
Selector
.spec.selector是可選字段,用來指定 label selector ,圈定Deployment管理的pod範圍。
如果被指定, .spec.selector 必須匹配 .spec.template.metadata.labels,否則它將被API拒絕。如果 .spec.selector 沒有被指定, .spec.selector.matchLabels 默認是 .spec.template.metadata.labels。
在Pod的template跟.spec.template不同或者數量超過了.spec.replicas規定的數量的情況下,Deployment會殺掉label跟selector不同的Pod。
注意: 不應該再創建其他label跟這個selector匹配的pod,或者通過其他Deployment,或者通過其他Controller,例如ReplicaSet和ReplicationController。否則該Deployment會被把它們當成都是自己創建的。Kubernetes不會阻止這麼做。如果有多個controller使用了重複的selector,controller們就會互相打架並導致不正確的行爲。
策略
.spec.strategy 指定新的Pod替換舊的Pod的策略。 .spec.strategy.type 可以是"Recreate"或者是 "RollingUpdate"(按比例更新)。"RollingUpdate"是默認值。
Recreate Deployment,.spec.strategy.type==Recreate時,在創建出新的Pod之前會先殺掉所有已存在的Pod。
Rolling Update Deployment,.spec.strategy.type==RollingUpdate時,Deployment使用rolling update 的方式更新Pod 。可以指定maxUnavailable 和 maxSurge 來控制 rolling update 進程。
Max Unavailable,.spec.strategy.rollingUpdate.maxUnavailable 是可選配置項,用來指定在升級過程中不可用Pod的最大數量。該值可以是一個絕對值(例如5),也可以是期望Pod數量的百分比(例如10%)。通過計算百分比的絕對值向下取整。例如,該值設置成30%,啓動rolling update後舊的ReplicatSet將會立即縮容到期望的Pod數量的70%。新的Pod ready後,隨着新的ReplicaSet的擴容,舊的ReplicaSet會進一步縮容,確保在升級的所有時刻可以用的Pod數量至少是期望Pod數量的70%。
Max Surge,.spec.strategy.rollingUpdate.maxSurge 是可選配置項,用來指定可以超過期望的Pod數量的最大個數。該值可以是一個絕對值(例如5)或者是期望的Pod數量的百分比(例如10%)。當MaxUnavailable爲0時該值不可以爲0。通過百分比計算的絕對值向上取整。默認值是1。例如,該值設置成30%,啓動rolling update後新的ReplicatSet將會立即擴容,新老Pod的總數不能超過期望的Pod數量的130%。舊的Pod被殺掉後,新的ReplicaSet將繼續擴容,舊的ReplicaSet會進一步縮容,確保在升級的所有時刻所有的Pod數量和不會超過期望Pod數量的130%。
Progress Deadline Seconds
.spec.progressDeadlineSeconds 是可選配置項,用來指定在系統報告Deployment的failed progressing——表現爲resource的狀態中type=Progressing、Status=False、 Reason=ProgressDeadlineExceeded前可以等待的Deployment進行的秒數。Deployment controller會繼續重試該Deployment。未來,在實現了自動回滾後, deployment controller在觀察到這種狀態時就會自動回滾。
如果設置該參數,該值必須大於 .spec.minReadySeconds。
Min Ready Seconds
.spec.minReadySeconds是一個可選配置項,用來指定沒有任何容器crash的Pod並被認爲是可用狀態的最小秒數。默認是0(Pod在ready後就會被認爲是可用狀態)。
Rollback To
.spec.rollbackTo 是一個可以選配置項,用來配置Deployment回退的配置。設置該參數將觸發回退操作,每次回退完成後,該值就會被清除。
Revision
.spec.rollbackTo.revision是一個可選配置項,用來指定回退到的revision。默認是0,意味着回退到歷史中最老的revision。
Revision History Limit
Deployment revision history存儲在它控制的ReplicaSets中。
.spec.revisionHistoryLimit 是一個可選配置項,用來指定可以保留的舊的ReplicaSet數量。該理想值取決於心Deployment的頻率和穩定性。如果該值沒有設置的話,默認所有舊的Replicaset或會被保留,將資源存儲在etcd中,是用kubectl get rs查看輸出。每個Deployment的該配置都保存在ReplicaSet中,然而,一旦刪除了舊的RepelicaSet,該Deployment就無法再回退到那個revison了。
如果將該值設置爲0,所有具有0個replica的ReplicaSet都會被刪除。在這種情況下,新的Deployment rollout無法撤銷,因爲revision history都被清理掉了。
Paused
.spec.paused是可以可選配置項,boolean值。用來指定暫停和恢復Deployment。Paused和沒有paused的Deployment之間的唯一區別就是,所有對paused deployment中的PodTemplateSpec的修改都不會觸發新的rollout。
Deployment被創建之後默認是非paused。
7、Horizontal Pod Autoscaler
橫向自動擴容功能,簡稱HPA,也是k8s系統中的一種資源對象。在v1.1版本中首次發佈,在v1.2版本中升級爲穩定版。在v1.6版本之前,僅支持使用CPU負載作爲是否擴容的判定條件;自v1.6版本開始提供了根據應用自定義指標進行自動擴容和縮容的功能,不過目前仍爲實驗性質。
可以通過yaml文件定義一個HPA對象,或者直接使用命令創建一個HPA對象。
yaml文件定義的樣例:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
namespace: default
spec:
maxReplicas: 10
minReplicas: 1
scaleTargetRef:
kink: Deployment
name: php-apache
targetCPUUtilizationPercentage: 90
注:當名爲php-apache的deployment的Pods副本的CPU使用率超過90%時,會觸發自動擴容行爲。但擴容或縮容都必須滿足的約束條件是Pod的副本數量要在1~10之間。
以上爲命令行方式創建一個HPA:
[root@gqtest ~]# kubectl autoscale deployment nginx-deployment --min=1 --max=5 --cpu-percent=80
deployment "nginx-deployment" autoscaled
8、StatefulSet
StatefulSet(有狀態系統服務設計)在k8s v1.5中引入,在Kubernetes 1.7中還是beta特性。
現實中很多服務是有狀態的,如MySQL集羣、kafka集羣、ZooKeeper集羣等,這些應用集羣有以下特點:
每個節點都有固定的身份ID,通過這個ID,集羣中的成員可以相互發現和通信;
集羣的規模是相對固定的,且不能隨意變動;
集羣裏每個節點都是有狀態的,通常會持久化數據到永久存儲中;
如果磁盤損壞導致集羣裏某個節點無法正常運行,則集羣功能會部分受損;
StatefulSet是Deployment/RC的一個特殊變種,有如下特性:
StatefulSet裏每個Pod都有穩定、唯一的網絡標識,可以用來發現集羣內其他成員。假設StatefulSet名字叫kafka,那麼第1個Pod會命名爲kafka-0,第2個Pod叫kafka-1,以此類推。
StatefulSet控制的Pod副本的啓停順序是受控的,操作第n個Pod時,前n-1個Pod已經是運行且準備好的狀態。
StatefulSet裏的Pod採用穩定的持久化存儲卷,通過PV/PVC實現,刪除Pod時默認不會刪除與StatefulSet相關的存儲卷。
StatefulSet需要與Headless Service配合使用,需要在每個StatefulSet的定義中聲明它屬於哪個Headless Service。
Headless Service沒有Cluster IP,當解析Headless Service的DNS域名時,得到的是該Service對應的全部Pod的Endpoint列表。StatefulSet在Headless Service的基礎上,又爲受Headless Service控制的每個Pod實例創建了一個DNS域名,格式爲:$(podname).$(headless service name)。
樣例:一個3節點的kafka的StatefulSet集羣
該應用集羣的headless service名稱定義爲kafka
該應用集羣的StatefulSet名字爲kafka,則StatefulSet裏的3個Pod的名稱分別爲:kafka-0,kafka-1,kafka-2
該應用集羣的StatefulSet中3個Pod的DNS名稱應該是:kafka-0.kafka,kafka-1.kafka,kafka-2.kafka
可以在該應用集羣的配置文件中直接使用上述DNS名稱來代表相應的Pod。
9、Service
(1)Service的概念
k8s的Service定義了一個服務的訪問入口地址,前端的應用通過這個入口地址訪問其背後的一組由Pod副本組成的集羣實例。Service與其後端Pod副本集羣之間則是通過Label Selector來實現對接的。RC的作用相當於是保證Service的服務能力和服務質量始終處於預期的標準。一個 Service 在 Kubernetes 中是一個 REST 對象。
Service 定義可以基於 POST 方式,請求 apiserver 創建新的實例。 例如,假定有一組 Pod,它們對外暴露了 9376 端口,同時還被打上 "app=MyApp" 標籤。
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
上述配置將創建一個名稱爲 “my-service” 的 Service 對象,它會將請求代理到使用 TCP 端口 9376,並且具有標籤 "app=MyApp" 的 Pod 上。 這個 Service 將被指派一個 IP 地址(通常稱爲 “Cluster IP”)。 Service 能夠將一個接收端口映射到任意的 targetPort。 默認情況下,targetPort 將被設置爲與 port 字段相同的值。Kubernetes Service 能夠支持 TCP 和 UDP 協議,默認 TCP 協議。
使用以下命令查看Service的yaml定義文件:
# kubectl get svc my-service -o yaml
(2)沒有 selector 的 Service
Servcie 抽象了該如何訪問 Kubernetes Pod,但也能夠抽象其它類型的 backend,例如:
希望在生產環境中使用外部的數據庫集羣。
希望服務指向另一個 Namespace 中或其它集羣中的服務。
正在將工作負載同時轉移到 Kubernetes 集羣和運行在 Kubernetes 集羣之外的 backend。
定義沒有 selector 的 Service :
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
由於這個 Service 沒有 selector,就不會創建相關的 Endpoints 對象。可以手動將 Service 映射到指定的 Endpoints:
kind: Endpoints
apiVersion: v1
metadata:
name: my-service
subsets:
- addresses:
- ip: 1.2.3.4
ports:
- port: 9376
注意:Endpoint IP 地址不能是 loopback(127.0.0.0/8)、 link-local(169.254.0.0/16)、或者 link-local 多播(224.0.0.0/24)。
訪問沒有 selector 的 Service,與有 selector 的 Service 的原理相同。請求將被路由到用戶定義的 Endpoint(該示例中爲 1.2.3.4:9376)。
ExternalName Service 是 Service 的特例,它沒有 selector,也沒有定義任何的端口和 Endpoint。 相反地,對於運行在集羣外部的服務,它通過返回該外部服務的別名這種方式來提供服務。
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
當查詢主機 my-service.prod.svc.CLUSTER時,集羣的 DNS 服務將返回一個值爲 my.database.example.com 的 CNAME 記錄。 訪問這個服務的工作方式與其它的相同,唯一不同的是重定向發生在 DNS 層,而且不會進行代理或轉發。 如果後續決定要將數據庫遷移到 Kubernetes 集羣中,可以啓動對應的 Pod,增加合適的 Selector 或 Endpoint,修改 Service 的 type。
(3)VIP 和 Service 代理
運行在每個Node上的kube-proxy進程其實就是一個智能的軟件負載均衡器,它會負責把對Service的請求轉發到後端的某個Pod實例上並在內部實現服務的負載均衡與會話保持機制。Service不是共用一個負載均衡器的IP,而是被分配了一個全局唯一的虛擬IP地址,稱爲Cluster IP。在Service的整個生命週期內,它的Cluster IP不會改變。 kube-proxy 負責爲 Service 實現了一種 VIP(虛擬 IP)的形式,而不是 ExternalName 的形式。在k8s v1.2版本之前默認使用userspace提供vip代理服務,從 Kubernetes v1.2 起,默認是使用 iptables 代理。
iptables 代理模式
這種模式,kube-proxy 會監視 Kubernetes master 對 Service 對象和 Endpoints 對象的添加和移除。 對每個 Service,它會創建相關 iptables 規則,從而捕獲到達該 Service 的 clusterIP(虛擬 IP)和端口的請求,進而將請求重定向到 Service 的一組 backend 中的某個上面。 對於每個 Endpoints 對象,它也會創建 iptables 規則,這個規則會選擇一個 backend Pod。默認的策略是,隨機選擇一個 backend。 實現基於客戶端 IP 的會話親和性,可以將 service.spec.sessionAffinity 的值設置爲 "ClientIP" (默認值爲 "None")。
和 userspace 代理類似,網絡返回的結果是,任何到達 Service 的 IP:Port 的請求,都會被代理到一個合適的 backend,不需要客戶端知道關於 Kubernetes、Service、或 Pod 的任何信息。 這應該比 userspace 代理更快、更可靠。然而,不像 userspace 代理,如果初始選擇的 Pod 沒有響應,iptables 代理能夠自動地重試另一個 Pod,所以它需要依賴 readiness probes。
(4)多端口 Service
很多 Service 需要暴露多個端口。對於這種情況,Kubernetes 支持在 Service 對象中定義多個端口。 當使用多個端口時,必須給出所有的端口的名稱,這樣 Endpoint 就不會產生歧義,例如:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
(5)選擇自己的 IP 地址
在 Service 創建的請求中,可以通過設置 spec.clusterIP 字段來指定自己的集羣 IP 地址。 比如,希望替換一個已經已存在的 DNS 條目,或者遺留系統已經配置了一個固定的 IP 且很難重新配置。 用戶選擇的 IP 地址必須合法,並且這個 IP 地址在 service-cluster-ip-range CIDR 範圍內,這對 API Server 來說是通過一個標識來指定的。 如果 IP 地址不合法,API Server 會返回 HTTP 狀態碼 422,表示值不合法。
(6)服務發現
Kubernetes 支持2種基本的服務發現模式 —— 環境變量和 DNS。
環境變量
當 Pod 運行在 Node 上,kubelet 會爲每個活躍的 Service 添加一組環境變量。 它同時支持 Docker links兼容 變量、簡單的 {SVCNAME}_SERVICE_HOST 和 {SVCNAME}_SERVICE_PORT 變量,這裏 Service 的名稱需大寫,橫線被轉換成下劃線。
舉個例子,一個名稱爲 "redis-master" 的 Service 暴露了 TCP 端口 6379,同時給它分配了 Cluster IP 地址 10.0.0.11,這個 Service 生成了如下環境變量:
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
這意味着需要有順序的要求 —— Pod 想要訪問的任何 Service 必須在 Pod 自己之前被創建,否則這些環境變量就不會被賦值。DNS 並沒有這個限制。
DNS
一個強烈推薦的集羣插件 是 DNS 服務器。 DNS 服務器監視着創建新 Service 的 Kubernetes API,從而爲每一個 Service 創建一組 DNS 記錄。 如果整個集羣的 DNS 一直被啓用,那麼所有的 Pod 應該能夠自動對 Service 進行名稱解析。
例如,有一個名稱爲 "my-service" 的 Service,它在 Kubernetes 集羣中名爲 "my-ns" 的 Namespace 中,爲 "my-service.my-ns" 創建了一條 DNS 記錄。 在名稱爲 "my-ns" 的 Namespace 中的 Pod 應該能夠簡單地通過名稱查詢找到 "my-service"。 在另一個 Namespace 中的 Pod 必須限定名稱爲 "my-service.my-ns"。 這些名稱查詢的結果是 Cluster IP。
Kubernetes 也支持對端口名稱的 DNS SRV(Service)記錄。 如果名稱爲 "my-service.my-ns" 的 Service 有一個名爲 "http" 的 TCP 端口,可以對 "_http._tcp.my-service.my-ns" 執行 DNS SRV 查詢,得到 "http" 的端口號。
Kubernetes DNS 服務器是唯一的一種能夠訪問 ExternalName 類型的 Service 的方式。 更多信息可以查看DNS Pod 和 Service。
Kubernetes 從 1.3 版本起, DNS 是內置的服務,通過插件管理器 集羣插件 自動被啓動。Kubernetes DNS 在集羣中調度 DNS Pod 和 Service ,配置 kubelet 以通知個別容器使用 DNS Service 的 IP 解析 DNS 名字。
(7)Headless Service
有時不需要或不想要負載均衡,以及單獨的 Service IP。 遇到這種情況,可以通過指定 Cluster IP(spec.clusterIP)的值爲 "None" 來創建 Headless Service。
這個選項允許開發人員自由尋找他們自己的方式,從而降低與 Kubernetes 系統的耦合性。 應用仍然可以使用一種自注冊的模式和適配器,對其它需要發現機制的系統能夠很容易地基於這個 API 來構建。
對這類 Service 並不會分配 Cluster IP,kube-proxy 不會處理它們,而且平臺也不會爲它們進行負載均衡和路由。 DNS 如何實現自動配置,依賴於 Service 是否定義了 selector。
配置 Selector
對定義了 selector 的 Headless Service,Endpoint 控制器在 API 中創建了 Endpoints 記錄,並且修改 DNS 配置返回 A 記錄(地址),通過這個地址直接到達 Service 的後端 Pod上。
不配置 Selector
對沒有定義 selector 的 Headless Service,Endpoint 控制器不會創建 Endpoints 記錄。
(8)發佈服務 —— type類型
對一些應用希望通過外部(Kubernetes 集羣外部)IP 地址暴露 Service。
Kubernetes ServiceTypes 允許指定一個需要的類型的 Service,默認是 ClusterIP 類型。
Type 的取值以及行爲如下:
ClusterIP:通過集羣的內部 IP 暴露服務,選擇該值,服務只能夠在集羣內部可以訪問,這也是默認的 ServiceType。
NodePort:通過每個 Node 上的 IP 和靜態端口(NodePort)暴露服務。NodePort 服務會路由到 ClusterIP 服務,這個 ClusterIP 服務會自動創建。通過請求 <NodeIP>:<NodePort>,可以從集羣的外部訪問一個 NodePort 服務。
LoadBalancer:使用雲提供商的負載局衡器,可以向外部暴露服務。外部的負載均衡器可以路由到 NodePort 服務和 ClusterIP 服務。
ExternalName:通過返回 CNAME 和它的值,可以將服務映射到 externalName 字段的內容(例如, foo.bar.example.com)。 沒有任何類型代理被創建,這隻有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。
k8s中有3種IP地址:
Node IP: Node節點的IP地址,這是集羣中每個節點的物理網卡的IP地址;
Pod IP: Pod的IP地址,這是Docker Engine根據docker0網橋的IP地址段進行分配的,通常是一個虛擬的二層網絡;
Cluster IP:Service 的IP地址,這也是一個虛擬的IP,但它更像是一個“僞造”的IP地址,因爲它沒有一個實體網絡對象,所以無法響應ping命令。它只能結合Service Port組成一個具體的通信服務端口,單獨的Cluster IP不具備TCP/IP通信的基礎。在k8s集羣之內,Node IP網、Pod IP網與Cluster IP網之間的通信採用的是k8s自己設計的一種編程實現的特殊的路由規則,不同於常見的IP路由實現。
10、Volume
(1)Volume概要知識
默認情況下容器中的磁盤文件是非持久化的,對於運行在容器中的應用來說面臨兩個問題,第一:當容器掛掉kubelet將重啓啓動它時,文件將會丟失;第二:當Pod中同時運行多個容器,容器之間需要共享文件時。Kubernetes的Volume解決了這兩個問題。
Kubernetes Volume具有明確的生命週期,與pod相同。因此,Volume的生命週期比Pod中運行的任何容器要持久,在容器重新啓動時能可以保留數據,當然,當Pod被刪除不存在時,Volume也將消失。
Kubernetes支持許多類型的Volume,Pod可以同時使用任意類型/數量的Volume。
要使用Volume,pod需要指定Volume的類型和內容(spec.volumes字段),和映射到容器的位置(spec.containers.volumeMounts字段)。
Volume的配置樣例:
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
volumes:
- name: datavol
emptyDir: {}
containers:
- name: tomcat-demo
image: tomcat
volumeMounts:
- mountPath: /mydata-data
name: datavol
imagePullPolicy: IfNotPresent
更多的Volume類型配置樣例可以參見:https://github.com/kubernetes/kubernetes/tree/master/examples/volumes
(2)Volume 類型
Kubernetes支持Volume類型有:
emptyDir
hostPath
gcePersistentDisk
awsElasticBlockStore
nfs
iscsi
fc (fibre channel)
flocker
glusterfs
rbd
cephfs
gitRepo
secret
persistentVolumeClaim
downwardAPI
projected
azureFileVolume
azureDisk
vsphereVolume
Quobyte
PortworxVolume
ScaleIO
StorageOS
local
(3)emptyDir
使用emptyDir時,當Pod分配到Node上時,將會創建一個emptyDir Volume,它的內容爲空,並且只要Node上的Pod一直運行,Volume就會一直存在。當Pod(不管任何原因)從Node上被刪除時,emptyDir也同時會刪除,存儲的數據也將永久刪除。emptyDir Volume的存儲介質(Disk,SSD等)取決於kubelet根目錄(如/var/lib/kubelet)所處文件系統的存儲介質。不限制emptyDir或hostPath Volume使用的空間大小,不對容器或Pod的資源隔離。
注:刪除容器不影響emptyDir。
emptyDir的一些用途:
臨時空間
一個容器需要從另一個容器中獲取數據的目錄
示例:
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: gcr.io/google_containers/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
(4)hostPath
hostPath允許掛載Node上的文件系統到Pod裏面去。如果Pod需要使用Node上的文件,可以使用hostPath。
通常有以下兩種使用場景:
容器應用程序生成的日誌文件需要永久保存時,可以使用宿主機的文件系統進行存儲。
需要訪問宿主機上Docker引擎內部數據結構的容器應用時,可以通過定義hostPath爲宿主機/var/lib/docker目錄,使容器內部應用可以直接訪問Docker的文件系統。
使用hostPath的注意事項:
在不同的Node上具有相同配置的Pod,可能會因爲宿主機上的目錄和文件不同而導致對Volume上目錄和文件的訪問結果不一致。
如果使用了資源配額管理,則k8s無法將hostPath在宿主機上使用的資源納入管理。
hostPath使用示例:
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: gcr.io/google_containers/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-volume
volumes:
- name: test-volume
hostPath:
# directory location on host
path: /data
(5)NFS
Kubernetes中通過簡單地配置就可以掛載NFS到Pod中,而NFS中的數據是可以永久保存的,同時NFS支持同時寫操作。Pod被刪除時,Volume被卸載,內容被保留。這就意味着NFS能夠允許我們提前對數據進行處理,而且這些數據可以在Pod之間相互傳遞。
NFS使用示例(PV):nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs
spec:
capacity:
storage: 1Mi
accessModes:
- ReadWriteMany
nfs:
# FIXME: use the right IP
server: 10.244.1.4
path: "/exports"
11、Persistent Volume(PV)
PersistentVolume子系統爲用戶和管理員提供了一個API,它抽象瞭如何提供存儲以及如何使用它的細節。 爲此,我們引入兩個新的API資源:PersistentVolume和PersistentVolumeClaim。
PersistentVolume(PV)是管理員設置的羣集中的一部分存儲。 它就像集羣中的一個資源,就像一個節點是集羣資源一樣。 PV是類似Volumes的插件,但具有獨立於Pod的生命週期。 此API對象捕獲存儲實現的細節,即NFS,iSCSI或特定於雲提供程序的存儲系統。
PersistentVolumeClaim(PVC)是用戶存儲的請求。 它與Pod相似。 Pod消耗節點資源,PVC消耗PV資源。 Pod可以請求特定級別的資源(CPU和內存)。 PVC聲明可以請求特定的存儲空間大小和訪問模式(例如,可以一次讀/寫或多次只讀)。
PV可以理解成是k8s集羣中的某個網絡存儲中對應的一塊存儲資源,它與Volume很類似,但有以下區別:
PV只能是網絡存儲,不屬於任何Node,但可以在每個Node上訪問;
PV不是定義是Pod上的,而是獨立定義的;
PV目前支持的存儲類型包括:gcePersistentDisk、AWSElasticBlockStore、AzureFile、AzureDisk、FC、Flocker、NFS、iSCSI、RBD、CephFS、Cinder、GlusterFS、Vsphere Volume、Quobyte Volumes、VMware Photon、Portworx Volumes、ScaleIO Volumes和HostPath(僅支持單機測試使用)。
比較重要的PV的accessModes屬性:
ReadWriteOnce: 讀寫權限,只能被單個Node掛載;
ReadOnlyMany: 只讀權限,允許被多個Node掛載;
ReadWriteMany: 讀寫權限,允許被多個Node掛載;
PV的幾種狀態:
Available
Bound
Released: 對應的PVC已經刪除,但資源還沒有被集羣回收。
Failed:PV自動回收失敗。
PV/PVC使用示例:nfs-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: my-nfs
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
注:PV的示例參見上一段落的NFS使用示例。
然後,在Pod的Volume定義中引用上述PVC即可:
volumes:
- name: mypvc
persistentVolumeClaim:
claimName: my-nfs
12、Namespace
Kubernetes可以使用Namespaces(命名空間)創建多個虛擬集羣。當團隊或項目中具有許多用戶時,可以考慮使用Namespace來實現多租戶的資源隔離。
Namespace爲名稱提供了一個範圍。資源的Names在Namespace中具有唯一性。
Namespace是一種將集羣資源劃分爲多個用途(通過 resource quota)的方法。
在未來的Kubernetes版本中,默認情況下,相同Namespace中的對象將具有相同的訪問控制策略。
k8s集羣啓動後,會創建一個名爲"default"的Namespace,如果不特別指明一個資源對象的Namespace屬性,則用戶創建的資源對象都會使用默認的"default"。
大多數Kubernetes資源(例如pod、services、replication controllers或其他)都在某些Namespace中,但Namespace資源本身並不在Namespace中。而低級別資源(如Node和persistentVolumes)不在任何Namespace中。Events是一個例外:它們可能有也可能沒有Namespace,具體取決於Events的對象。
創建Namespace
(1) 命令行直接創建
$ kubectl create namespace new-namespace
(2) 通過文件創建
$ cat my-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: new-namespace
$ kubectl create -f ./my-namespace.yaml
注意:命名空間名稱滿足正則表達式[a-z0-9]([-a-z0-9]*[a-z0-9])?,最大長度爲63位
刪除Namespace
$ kubectl delete namespaces new-namespace
注意:
刪除一個namespace會自動刪除所有屬於該namespace的資源。
default和kube-system命名空間不可刪除。
PersistentVolumes是不屬於任何namespace的,但PersistentVolumeClaim是屬於某個特定namespace的。
Events是否屬於namespace取決於產生events的對象。
查看 Namespace
使用以下命令列出羣集中的當前的Namespace:
$ kubectl get namespaces
NAME STATUS AGE
default Active 1d
kube-system Active 1d
Kubernetes從兩個初始的Namespace開始:
default
kube-system 由Kubernetes系統創建的對象的Namespace
將一個資源對象放入名爲"development"的命名空間裏:
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: development
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
name: busybox
設置請求的名稱空間
要臨時設置Request的Namespace,請使用--namespace 標誌。
例如:
$ kubectl --namespace=<insert-namespace-name-here> run nginx --image=nginx
$ kubectl --namespace=<insert-namespace-name-here> get pods
注:如果不加--namespace參數,kubectl get將僅顯示屬於"default"命名空間的資源對象。
可以使用kubectl命令將創建的Namespace可以永久保存在context中。
$ kubectl config set-context $(kubectl config current-context) --namespace=<insert-namespace-name-here>
# Validate it
$ kubectl config view | grep namespace:
13、Annotation(註解)
Annotation與Label類似,也使用key/value鍵值對的形式定義。可以使用Kubernetes Annotations將任何非標識metadata附加到對象。客戶端(如工具和庫)可以檢索此metadata。
可以使用Labels或Annotations將元數據附加到Kubernetes對象。標籤可用於選擇對象並查找滿足某些條件的對象集合。相比之下,Annotations不用於標識和選擇對象。Annotations中的元數據可以是small 或large,structured 或unstructured,並且可以包括標籤不允許使用的字符。
Annotations就如標籤一樣,也是由key/value組成:
"annotations": {
"key1" : "value1",
"key2" : "value2"
}
以下是在Annotations中記錄信息的一些例子:
構建、發佈的鏡像信息,如時間戳,發行ID,git分支,PR編號,鏡像hashes和注Registry地址。
一些日誌記錄、監視、分析或audit repositories。
一些工具信息:例如,名稱、版本和構建信息。
用戶或工具/系統來源信息,例如來自其他生態系統組件對象的URL。
負責人電話/座機,或一些信息目錄。
注意:Annotations不會被Kubernetes直接使用,其主要目的是方便用戶閱讀查找。
參考1:《Kubernetes權威指南——從Docker到Kubernetes實踐全接觸》第1章。
參考2:Kubernetes中文社區 | 中文文檔
轉自:https://blog.csdn.net/watermelonbig/article/details/79346524?utm_source=copy