kubernetes 中的大部分概念如 Node,Pod,Replication Controller,Sercice等都可以看作一種"資源對象",幾乎所有的資源對象都可以通過kubernetes提供的kubectl工具(或者API編程調用)執行增,刪,改,查等操作並將其保存在etcd中持久化存儲。kubernets其實是一個高度自動化的資源控制系統,它通過跟蹤對比etcd庫裏保存的"資源期望狀態"與當前環境中的"實際資源狀態"的差異來實現自動控制和自動糾錯的高級功能
1.master
kubernetes 裏的master指的是集羣控制節點,每個kubernetes集羣裏需要一個master節點來負責整個集羣的管理和控制,基本上kubernetes所有的控制命令都是發給它,它來負責具體的執行過程,所有的執行命令都是在master節點上運行,master節點通常會佔據一個獨x86服務器(虛擬機),一個主要的原因是它太重要了,它是整個集羣的"首腦",如果它宕機或者不可用,我們所有的控制命令都將失效
Master節點運行的進程
Kubernetes API Server(kube-apiserver),提供了HTTP Rest 接口的關鍵服務進程,是Kubernetes 裏所有資源的增,刪,改,查等操作的唯一入口,也是集羣控制的入口進程
Kubernetes Controller Manager(kube-controller-manager),Kubernetes裏所有資源對象的自動化控制中心,可以理解爲資源對象的"大總管"
Kubernetes Seheduler(kube-scheduler),負責資源調度(pod調度)的進程,相當於公交公司的"調度室"
在master節點上還需啓動一個etcd Server進程,因爲Kubernetes 裏的所有資源對象的數據保存在etcd中
2.Node
除了Master,Kubernetes集羣中的其他機器被稱爲Node節點,比較早的版本中也被稱爲Minion.與master一樣,Node節點可以是一臺物理主機,也可以是一臺虛擬機,Node節點纔是Kubernetes集羣中的工作負載節點,每個Node都會被Master分配一些工作負載(Docker容器),當某個Node宕機時,其上的工作負載會被Master自動轉移到其他節點上
每個Node節點上都運行着一下一組關鍵進程
kubelet: 負責pod對應的容器的創建,啓停等任務,同時與Master節點密切協作,實現集羣管理的基本功能
kube-proxy: 實現Kubernetes Service的通信與負載均衡機制的重要組件。
Docker Engine (docker):Docker 引擎,負責本機的容器創建和管理工作
Node 節點可以在運行期間動態增加到Kubernetes集羣中,前提是這個節點上已經正確安裝,配置和啓動了上述關鍵進程,在默認情況下kubelet會向Master 註冊自己,這也是Kubernetes推薦的Node管理方式,一旦Node被納入集羣管理範圍,kubelet進程就會定時向Master節點彙報自身情況.例如操作系統,Docker版本,機器的CPU和內存情況,以及之前有哪些Pod在運行等這樣Master可以獲知每個Node的資源使用情況,並實現高效均衡的資源調度策略。而某個Node超過指定時間不上報信息時,會被Master判定爲"失聯",Node的狀態被標記爲不可用(Not Ready),隨後Master會觸發"工作負載大轉移"的自動流程
2.1 查看集羣中的節點
[root@server ~]# kubectl get node
NAME STATUS AGE
127.0.0.1 Ready 5d
2.2 查看節點詳細信息
[root@server ~]# 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: Thu, 12 Sep 2019 16:02:18 +0800
Phase:
Conditions:
Type Status LastHeartbeatTime LastTransitionTime Reason Message
---- ------ ----------------- ------------------ ------ -------
OutOfDisk False Wed, 18 Sep 2019 00:05:37 +0800 Thu, 12 Sep 2019 16:02:18 +0800 KubeletHasSufficientDisk kubelet has sufficient disk space available
MemoryPressure False Wed, 18 Sep 2019 00:05:37 +0800 Thu, 12 Sep 2019 16:02:18 +0800 KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False Wed, 18 Sep 2019 00:05:37 +0800 Thu, 12 Sep 2019 16:02:18 +0800 KubeletHasNoDiskPressure kubelet has no disk pressure
Ready True Wed, 18 Sep 2019 00:05:37 +0800 Mon, 16 Sep 2019 14:15:37 +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: 1
memory: 995896Ki
pods: 110
Allocatable:
alpha.kubernetes.io/nvidia-gpu: 0
cpu: 1
memory: 995896Ki
pods: 110
System Info:
Machine ID: 7503d765072b4bdb9696f7807539358b
System UUID: 89FD4D56-2F9A-E512-92B4-042FEAF4CAAA
Boot ID: d7de7e4d-d385-483c-8f30-7755bc244bcc
Kernel Version: 3.10.0-957.el7.x86_64
OS Image: CentOS Linux 7 (Core)
Operating System: linux
Architecture: amd64
Container Runtime Version: docker://1.13.1
Kubelet Version: v1.5.2
Kube-Proxy Version: v1.5.2
ExternalID: 127.0.0.1
Non-terminated Pods: (2 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
--------- ---- ------------ ---------- --------------- -------------
default mysql-cc20c 0 (0%) 0 (0%) 0 (0%) 0 (0%)
default myweb-vg6hk 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%)
No events.
關鍵信息如下:
Node基本信息: 名稱,標籤,創建時間等
Node當前運行狀態,Node啓動以後會做一系列的自檢工作,比如磁盤是否滿了,如果滿了就標註OutOfDisk=True,否則繼續檢查內存是否不足(MemoryPressure=True),最後一切正常,就切換爲Ready狀態(Ready=True),這種情況表示Node處於健康狀態,可以在其上創建新的pod
Node的主機地址與主機名
Node上的資源總量:描述Node可用的系統資源,包括CPU,內存數量,最大可調度Pod數量等,注意到目前Kubernetes已經實驗性的支持GPU資源分配了(alpha.kubernetes.io/nvidia-gpu=0)
Node 可分配資源量:描述Node當前可用於分配的資源量。
主機系統信息:包括主機的唯一表示UUID,Linux kernel 版本號,操作系統類型與版本,Kubernetes 版本號,kubelet與kube-proxy的版本號等
當前正在運行的Pod列表概要信息。
已分配的資源使用概要信息,例如資源申請的最低,最大允許使用量佔系統總量的百分比
Node相關的Event信息
3. Pod
Pod 是Kubernetes的最重要也最基本的概念,每個Pod都有一個特殊的被稱爲"根容器"的Pause容器。Pause容器對應的鏡像屬於Kubernetes平臺的一部分,除了Pause容器,每個Pod還包含一個或多個緊密相關的用戶業務容器
pod的概念和結構
在一組容器作爲一個單元的情況下,我們難以對"整體"簡單地進行判斷急有效的進行行動。引入業務無關並且不易死亡的Pause容器作爲根容器,以它的狀態代表整個容器組的狀態,就簡單,巧妙的解決了這個難題
pod裏多個業務容器共享Pause容器的ip,共享Pause容器掛接的Volume,這樣即簡化了親切關聯的業務容器之間的通信問題,也很好的解決了它們之間的文件共享問題
Kubernetes 爲每個Pod都分配了唯一的IP地址,稱之爲Pod IP,一個Pod裏的多個容器共享Pod IP 地址,Kubernetes,要求底層網絡支持集羣內任意兩個Pod之間的TCP/IP直接通信,這通常採用虛擬二層網絡技術來實現,例如Flannel,Openvswitch等,在kubernetes裏,一個pod裏的容器與另外主機上的Pod容器能夠直接通信
https://blog.csdn.net/wanchaopeng/article/details/103372099
Pod其實有兩種類型:普通的Pod及靜態Pod(static pod),後者比較特殊,它並不存放在kubernetes的etcd存儲裏,而是存放在某個具體的Node上的一個具體文件中,並且只在此Node上啓動運行,而普通的Pod一旦創建,就會被放入到etcd存儲中,隨後會被Kubernetes Master調度到某個具體的Node 上運行並綁定(Binding)隨後該Pod被對應的Node上的kubelet進程實例化成一組相關的Docker容器並啓動起來,在默認情況下,當pod裏的某個容器停止時,Kubernetes會被檢測到這個問題並且重新啓動這個Pod(重啓Pod裏的所有容器),如果Pod所在的Node宕機,則會將這個Node上的所有Pod重新調度到其他節點上
Kubernetes 裏的所有的資源對象都可以採用yaml或者JSON格式的文件來定義或描述
apiVersion: v1
kind: Pod
metadata:
name: myweb
labels:
name: myweb
spec:
containers:
- name: myweb
image: kubeguide/tomcat-app:v1
ports:
- containerPort: 8080
env:
- name: MYSQL_SERVICE_HOST
value: '172.17.0.2'
- name: MYSQL_SERVICE_PORT
value: '3306'
kind 爲Pod表明這是一個Pod的定義,metadata裏的name屬性爲Pod的名字,metadata裏還能定義資源對象的標籤(Label),這裏生命myweb擁有一個name=myweb的標籤(Label),Pod裏所包含的容器組的定義則在spec一節中聲明,這裏定義了一個名字爲myweb,對應鏡像爲kubeguide/tomcat-app:v1的容器,該容器注入了名爲MYSQL_SERVER_HOST='mysql'和MYSQL_SERVER_PORT='3306'的環境變量(env關鍵字),並且在8080端口(containerPort)上啓動容器進程。Pod的IP加上這裏的容器端口(containerPort),就組成了一個新的概念Endpoint的情況,比如當我們把Tomcat定義爲一個Pod的時候,可以對外暴露管理端口與服務端口這兩個Endpoint
Docker Volume在kubernetes裏也有對應的概念---Pod Volume,後者有一些拓展,比如可以用分佈式文件系統GlusterFS實現後端存儲功能;Pod Volume是定義在Pod之上,然後被各個容器掛載到自己的文件系統中的
每個pod都可以對其能使用的服務器上的計算資源設置限額,當前可以設置限額的計算資源有CPU和memory兩種,其中cpu的資源單位爲CPU(Core)的數量,是一個絕對值而非絕對值
一個cpu的配額對於絕大多數容器來說是相當大的一個資源配額了,所以,在kubernetes裏,通常以千分之一的CPU配額爲最小單位,以m來表示。通常一個容器的CPU配額被定義爲100~300m,即佔用0.1~0.3個CPU。由於CPU配額是一個絕對值,所以無論在擁有一個Core的機器上,還是擁有54個Core的機器上,100m這個配額所代表的CPU的使用量都是一樣的,與CPU配額類似,Memory配額也是一個絕對值,它的單位是內存字節數
在kubernetes裏,一個計算資源進行配額限定需要設定以下兩個參數
Requests: 該資源的最小申請量,系統必須滿足條件
Limits: 該資源最大允許使用的量,不能被突破,當容器試圖使用超過這個量的資源時,可能會被Kubernetes Kill 並重啓
通常我們會把Request 設置爲一個比較小的數值,符合容器時的工作負載情況下的資源需求,而把Limit設置爲峯值負載情況下資源佔用的最大量,比如下面的這短定義,表明MySQL容器申請最少0.25個CPU及64Mib內存,在運行過程中MySQL容器所能使用的配額爲0.5個CPU及128Mib內存
spec:
containers: #Pod內容器定義的部分
- name: mysql #容器的名稱
image: mysql:5.7 #容器對應的Docker Image
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
4.label(標籤)
Label是Kubernetes系統中另外一個核心概念,一個是Label是一個key=value的鍵值對,其中key與value由用戶自己指定。Label可以附加到各種資源對象上,例如Node,Pod,Service,RC等,一個資源對象可以定義任意數量的Label,同一個Label也可以添加到任意數量的資源對象上去,Label通常在資源對象定義時確定,也可以在對象創建後動態添加或者刪除
我們可以通過給指定的資源對象捆綁一個或多個不同的Label來實現多維度的資源分組管理功能,以便於靈活,方便地進行資源分配,調度,配置,部署等管理工作。例如:部署不同版本的應用到不同的環境中;或者監控和分析應用(日誌記錄,監控,告警)等,Label示例如下
版本標籤: "release":"stable","release":"canary"
環境標籤: "environment":"dev","environment":"qa","environment":"production"
架構標籤: "tire":"frontend","tire":"backend","tier":"minddleware"
分區標籤: "partition":"customerA","partition":"customerB"...
質量管控標籤: "track":"daily","track":"weekly"
Label相當於我們熟悉的"標籤",給某個資源對象定義的一個Label,就相當於給它打了一個標籤,隨後可以通過Label Selector(標籤選擇器)查詢和篩選擁有某些Label的資源對象,Kubernetes通過這種方式實現了類似SQL的簡單又通用的對象查詢機制
Label Selector可以被類比爲SQL語句中的where查詢條件,例如,name=redis-slave這個Label Selector作用於Pod時,可以類比爲select * from pod's name = 'redis-slave'這樣的語句,當前又兩種Label Selector 的表達式: 基於等式的(Equality-based)和基於集合的(Set-based),前者採用"等式類"的表達式匹配標籤,下面是一些具體的例子
name=redis-slave: 匹配所有具有標籤name=redis-slave的資源對象
env !=production: 匹配所有不具有標籤env=production的資源對象,比如env=test就是滿足此田間的標籤之一。
而後者使用幾個曹祖的表達式匹配標籤,例子如下
name in (redis-master,redis-slave):匹配所有具有標籤name=redis-master 或者 name=redis-slave的資源對象
name not in (php-frontend): 匹配所有不具有標籤name=php-frontend的資源對象。
可以通過多個Label Selector表達式的組合實現複雜的條件選擇,多個表達式之間用","進行分隔即可,幾個條件之間的"AND"的關係,即同事滿足多個條件,如下
name=redis-slave,env!=production
name notin (php-frontend),env!=production
Label Selector在Kubernetes 中的重要使用場景如下
1. kube-controller 進程通過資源對象RC上定義的Label Selector來篩選要監控的Pod副本的數量,從而實現Pod副本的數量始終符合預期設定的全自動控制流程。
2.kube-proxy進程通過Service的Label Selector來選擇對應的Pod,自動建立起每個Service到對應Pod的請求轉發路由表,從而實現Service的智能負載均衡機制
3.通過對某些Node定義特定的Label,並且在Pod定義文件中使用NodeSelector這種標籤調度策略,kube-scheduler進程可以實現Pod"定向調度"的特性
前列中使用name=XXX的Label Selector。Pod定義了3個Label:release,env和role,不同的Pod定義了不同的Label值,我們設置"role=frontend"的Label Selector,則會選取到Node 1和Node 2上的Pod
而設置"release=beta"的Label Selector,則會選取到Node 2和Node 3上的Pod
總結:
使用Label可以給對象創建多組標籤,Label和Label Selector 共同構成了Kubernetes系統中最核心的應用模型,使得被管理對象能夠精細的分組管理,同事實現了整個集羣的高可用性
5. Replication Controller (RC)
Replication Colltroller(以後簡稱RC)深入描述
RC是Kubernetes 系統中的核心概念之一,簡單來說,它其實是定義了一個期望的場景,即聲明某種Pod的副本數量在人任意時刻都符合某個預期值,RC定義如下
1.Pod期待的副本數(replicas)
2.用於篩選目標Pod的Label Selector
3.當Pod的副本數量小於預期數量的時候,用於創建新Pod的Pod模板(template)
如下RC例子,即確保擁有tier=frontend標籤的這個Pod(運行Tomcat容器)在整個Kubernetes集羣中始終只有一個副本
apiVersion: v1
kind: ReplicationController
metadata:
name: frontend
spec:
replicas: 1
selector:
tier: frontend
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
contaainers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
當我們定義了一個RC並提交到Kubernetes集羣中以後,Master節點上的Controller Manager組件就得到通知,定期巡檢系統中當前存活的目標Pod,並確保目標Pod實例的數量剛好等於此RC的期望值,如果有過多的Pod副本在運行,系統就會停掉一些Pod,否則系統就會再自動創建一些Pod,可以說,通過RC,kubernetes實現了用戶應用集羣的高可用性
下面我們以3個Node節點的集羣爲例,說明Kubernetes如何通過RC來實現Pod副本數量自動控制的機制,假如我們的Rc裏定義redis-slave這個需要保持3個副本,系統將可能在其中兩個Node上創建Pod,
1.兩個Node上創建redis-slave Pod的情形
假設Node2上的Pod2意外終止,根據Rc定義的replicas數量2,Kubernetes將會自動創建並啓動一個新的Pod,以保證整個集羣中始終有兩個redis-slave pod在運行
2.系統可能選擇Node1或者Node3 來創建一個新的Pod
此外在運行時,我們可以通過修改Rc的副本數量,來實現Pod的動態縮放(Scaling)功能,這個可以通過執行Kuberctl scale命令來一鍵完成:
kubectl scale rc redis-slave --replicas=3
scaling的執行結果如下
注意:
刪除Rc並不會影響通過該Rc已創建好的Pod,爲了刪除所有Pod,可以設置replicas的值爲0,然後更新該RC,另外kubectl提供了stop和delete命令來一次性刪除Rc和rc控制的全部Pod
當我們應用升級時,通常會通過Build一個新的Docker鏡像,並用新的鏡像版本來替代舊的版本的方式達到目的,在系統升級過程中,我們希望是平滑的方式,比如當前系統中10個對應舊版本的Pod,最佳的方法是舊版本的Pod每次停止一個,同事創建一個新版本的Pod,在整個升級過程中,此消彼長,而運行中的Pod數量始終是10個,一段時間後,當所有的pod都已經是最新版本的時候,升級完成,通過RC機制,Kubernetes很容易就實現了這種高級實用的特性,被稱爲"滾動升級"
由於Replication Controller與Kubernetes代碼中的模塊Replication Controller同名,同時這個詞也無法準確表達它的本意,所以在Kubernetes 1.2 的時候它升級成另外一個新的概念---Replica Set,官方解釋爲"下一代的RC",它與RC當前存在的唯一區別是Replica Sets支持基於集合的Label selector(Set-based selector),而RC只支持基於等式的Label Selector(equality-base selector),這使得Replica Set的功能更強,下面是等價於之前Rc例子的Replica Set的定義
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: frontend
spec:
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
........
kubectl命令行工具適用於RC的絕大部分命令都同樣適用於Replica Set,此外,當前我們很少單獨使用Replica Set,它主要被Deployment這個逢高層的資源對象使用,從而形成一整套Pod創建,刪除,更新的編排機制。當我們使用Deployment時,無須關心它是如何創建和維護Replica Set的,這一切是自動發生的
Replica Set 與 Deployment 這兩個重要資源對象逐步替換了之前的RCd的作用。是Kubernetes 1.3裏Pod自動擴容(伸縮)這個告警功能實現的基礎,也將繼續在Kubernetes 未來的版本中發揮重要的作用
總結關於RC (Replica Set)的一些特性和作用
1. 在大多數情況下,我們通過定義一個RC實現Pod的創建過程及副本數量的自動控制
2. RC裏包括完整的Pod的定義模板
3. RC通過Label Selector 機制實現對Pod副本的自動控制
4. 通過改變RC裏的Pod 副本數量,可以實現Pod的擴容或縮容功能
5. 通過該百年RC裏Pod模板中的鏡像版本,可以實現Pod的滾動升級功能
6.Deployment
Deployment 是Kubernetes 1.2 引入的概念,引入的目的是爲了更好的解決Pod 的編排問題,爲此,Deployment在內部使用了Replica Set來實現母的,無論從Deployment 的作用與目的,它的YAM定義,還是從它的具體命令行操作來看,我們都可以把它看作RC的一次升級,兩者的相似度超過90%
Deployment相對於RC的一個最大升級是我們可以隨時知道當前Pod"部署"的進度。實際上由於一個Pod的創建,調度,綁定節點及在目標Node上啓動對應的容器這一完整過程需要一定的時間,所以我們期待系統啓動N個Pod副本的目標狀態,實際上是一個連續變化的"部署過程"導致的最終狀態
Deployment的典型使用場景
1. 創建一個Deployment對象來生成對應的Replica Set並完成Pod副本的創建過程
2. 檢查Deployment的狀態來看部署動作是否完成(Pod副本的數量是否達到預期的值)
3. 更新呢Deployment以創建新的Pod(比如鏡像升級)
4. 如果當前Deployment不穩定,則回滾到一個早先的Deployment版本
5. 掛起或者恢復一個Deployment
Deployment的定義與Replica Set的定義很類似,除了API聲明與Kind類型等有所區別:
apiVersion: extensions/v1beta1 apiVersion: v1
kind: Deployment kind: ReplicaSet
metadata: metadata:
name: nginx-deployment name: nginx-repset
例子:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 1
selector:
matchLabels:
tier: frontend
matchExpressions:
- { key: tier, operator: In, values: [frontend] }
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
創建Deployment
[root@server /data/kubernetes]# kubectl create -f tomcat-deployment.yaml
deployment "frontend" created
查看Deployment 信息
[root@server /data/kubernetes]# kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
frontend 1 1 1 1 59m
上述輸出數量解釋:
DESIRED: Pod副本數量的期望值,即Deployment裏定義的Replica.
CURRENT: 當前Replica 的值,實際上是Deployment所創建的Replica Set裏的Replica值,這個值不斷增加,直接達到DESIRED爲止,表明整個部署過程完成
UP-TO-DATE: 最新版本的Pod的副本數量,用於指示在滾動升級的過程中,有多少個Pod副本已經成功升級
AVAILABLE:當前集羣中可用的Pod的副本數量,即集羣中當前存活的Pod數量
查看對應的Replica Set,
[root@server /data/kubernetes]# kubectl get rs
NAME DESIRED CURRENT READY AGE
frontend-141477217 1 1 1 1h
[root@server /data/kubernetes]# kubectl describe deployments
Name: frontend
Namespace: default
CreationTimestamp: Wed, 18 Sep 2019 19:54:45 +0800
Labels: app=app-demo
tier=frontend
Selector: tier=frontend,tier in (frontend)
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: frontend-141477217 (1/1 replicas created)
No events.
7.Horizontal Pod AUTOSCALER(HPA)
Horizontal Pod AUTOSCALER 簡稱HPA,意思是Pod橫向擴容,與之前RC,Deployment一樣,屬於Kubernetes 資源對象,通過追蹤分析rc控制的所有目標POD的負載變化情況,來確定是否需要針對性地調整目標Pod的副本數,這是HPA實現原理
HPA有兩種方式作爲pod的度量指標
CPUUtilizationPercentage
應用程序自定義的度量指標,比如服務在每秒內的相應請求數(TPS或QPS)
CPUUtilizationPercentage是一個算術平均值,即目標Pod所有副本自身的CPU利用率的平均值。一個Pod自身的CPU利用率是該Pod當前CPU的使用量除以它的Pod Request的值,比如我們定義一個Pod的Pod Request爲0.4,而當前Pod的CPU使用量爲0.2,則它的CPU使用率爲50%,如此一來,我們就可以算出來一個RC控制的所有Pod副本的CPU利用率的算術平均值了,如果某一時刻CPUUtilizationPercentage 的值超過80%,則以爲着當前的Pod副本數很可能不足以支撐接下來更多的請求,需要進行動態擴容,而當請求高峯時段過去後,Pod的CPU利用率又會降下來,此時對應的POd副本數應該自動減少到一個合理的水平
CPUUtilizationPercentage計算過程中使用到的Pod的CPU使用量通常是1分鐘內的平均值,目前通過查詢Heapster擴展組件來得到這個值,所以需要安裝部署Heapster,這樣一來便增加了系統的複雜度和實施HPA特性的複雜度,因此,未來的計劃是Kubernetes自身實現一個基礎行能數據採集模板,從而更好地支持HPA和其他需要用到基礎性能數據的功能模板,
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
namespace: default
spec:
maxReplicas: 10
minReplicas: 1
scaleTargetRef:
kind: Deployment
name: php-apache
targetCPUUtilizationPercentage: 90
根據上面的定義,我們可以知道這個HPA控制的目標對象爲一個名叫php-apache的Deployment的Pod副本CPUtiliationpercentage的值超過90%時會觸發自動動態擴容行爲,擴容或縮容時必須滿足的一個約束條件是Pod的副本數要介於1與10之間
除了可以通過直接定義yaml文件並調用kuberctrl create 的命令來創建一個HPA資源對象的方式,我們還能通過下面的簡單命令行直接創建等價的HPA對象
kubectl autoscale deployment php-apache --cpu-percent=90 --min=1 --max=10
8.service(服務)
1.概述
Service 也是Kubernetes 裏的最核心的資源對象之一,Kubernetes裏的每個Service其實就是我們經常提起的爲服務架構中的一個"微服務",之前我們所說的Pod,RC等資源對象其實都是爲"服務"--kubernets Service 服務
kubernetes的Service定義了一個服務的訪問入口地址,前端應用(Pod)通過這個入口地址訪問其背後的一組由Pod副本組成的集羣實例,Service與其後端Pod副本集羣之間則是通過Label Selector來實現"無縫對接"的。而RC的作用實際上是保證Service的服務能力和服務質量始終處於預期的標準
通過分析,識別並見面系統中的所有服務爲微服務---Kubernetes Service,最終我們系統由多個提供不同業務能力而又彼此對的微服務單元組成,服務之間通過TCP/IP進行通信,從而形成了我們強大而靈活的彈性網絡,擁有了清大的分佈式能力,彈性拓展你呢後來,容錯能力,與此同時,我們的程序架構也變得簡單和直觀許多
既然每個Pod都會分配一個單獨的IP地址,而且每個Pod都提供一個獨立的Endpoint(Pod IP+ContainerPort)以被客戶端訪問,現在多個Pod副本組成一個集羣來提供服務,被客戶端訪問,一般是部署一個負載均衡器(軟件或硬件),這組Pod開啓一個對外的服務端口如8000端口,並且將這些Pod的Endpoint列表加入8000端口的轉發列表中,客戶端就可以通過負載均衡器的對外ip地址+服務端口來訪問次服務,而客戶端的請求被轉發到那個Pod,則由負載均衡器的算法決定
Kubernetes也循環了上述常規做法,運行在每個Node上的kube-proxy進程其實就是一個智能的軟件負載器,它負責把對Service的請求轉發到後端的某個Pod實例上,並在內部實現服務的負載均衡與會話保持機制,但Kubernetes發明了一種很巧妙又影響深遠的設計: Service 不是共用一個負載均衡器的IP地址,而是每個Service分配了一個全局唯一的虛擬IP地址,這個虛擬IP被稱爲Cluster IP,這樣一來,這個服務就變成了具備唯一IP地址的通信節點,服務調用就變成了最基礎的TCP網絡通信問題
Pod的Endpoint地址會隨着Pod的銷燬和重新創建而發生改變,因爲新的Pod的ip地址與之前舊Pod的不同,而Service 一旦創建,Kubernetes 就會自動爲它分配一個可用的Cluster IP,而且在Service的整個生命週期內,它的Cluster IP不會發生改變。於是,服務發現這個棘手的問題在Kubernetes的架構裏也得以輕鬆解決: 只要用Service的Name與Service的Cluster IP地址做一個DNS域名映射即可解決
tomcat-service.yaml
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
ports:
- port: 8080
selector:
tier: frontend
kubectl create -f tomcat-service.yaml
查看service的Endpoint
[root@server /data/tomcat]# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.1.52:6443 7d
mysql 172.17.0.2:3306 3d
myweb 172.17.0.3:8080 2d
tomcat-service 172.17.0.4:8080 28s
tomcat-server分配的Cluster ip
[root@server /data/tomcat]# kubectl get svc tomcat-service -o yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: 2019-09-19T09:18:30Z
name: tomcat-service
namespace: default
resourceVersion: "688768"
selfLink: /api/v1/namespaces/default/services/tomcat-service
uid: 72300d76-dabe-11e9-a54c-000c29f4caaa
spec:
clusterIP: 10.254.192.228
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
tier: frontend
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
在spec.ports 的定義中,targetPort 屬性用來確定提供該服務的容器所暴露(EXPOSE)的端口號,即具體業務進程在容器內的tagetPort,上提供TCP/IP介入;而Port屬性則定義了Service的虛端口號,即具默認targetPort與port相同
很多服務存在多個端口的問題,通常一個端口提供業務服務,另外一個端口提供管理服務,比如Mycat,Codis等常見中間件.Kubernetes Service支持多個Endpoint,在存在多個Endpoint的情況下,要求每個Endpoint定義一個名字來區分,
例子如下:
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
ports:
- port: 8080
name: service-port
- port: 8005
name: shutdown-port
selector:
tier: frontend
2.Kubernetes的服務發現機制
每個Kubernetes中的Service都有一個唯一的Cluster IP 以及唯一的名字,而名字是由開發者自己定義的,部署的時候也沒必要改變,完全固定在配置中。通過Service的名字得到對應的Cluster IP
考慮到環境變量的方式獲取Service的IP與端口的方式不太方便,後來Kubernetes通過Add-on增值包的方式引入了DNS系統,把服務名作爲DNS域名,這樣一來,程序就可以直接使用服務名來建立通信鏈接了。目前Kubernetes上的大部分應用採用了DNS服務發現機制
3. 外部系統訪問Service的問題
Kubernetes三種ip
Node IP: Node 節點的IP地址
Pod IP: Pod的IP地址
Cluster IP: Service 的ip地址
首先,Node IP 是Kubernetes集羣中每個節點的物理網卡的IP地址,這是一個真實存在的物理網絡,所有屬於這個網絡的服務器之間都能通過這個網絡直接通信,不管它們中是否有部分節點不屬於這個Kubernetes集羣,這也表明了Kubernetes集羣之外的節點訪問Kubernetes集羣之內的某個節點或者TCP/IP服務的時候,必須通過Node IP 通信
其次,Pod IP 是每個Pod的IP地址,它是Docker Engine根據docker0 網橋的IP地址段進行分配的,通常是一個虛擬的二層網絡,Kubernetes要求位於不同的Node上的Pod 能夠彼此直接通信,所以Kubernetes 裏一個Pod裏的容器訪問另外一個Pod的容器,就是通過Pod IP 所在虛擬二層網絡進行通信的,而真實的TCP/IP流量則是通過Node IP所在的物理網卡流出的
Service 的Cluster IP,它是一個虛擬的IP
Cluster IP 僅僅作用於Kubernetes Service這個對象,並由Kubernetes管理和分配IP地址(來源於Cluster IP地址池)
Cluster IP 無法被Ping,因爲沒有一個"實體網絡對象"來響應
Cluster IP 只能結合Service Port組成一個具體的通信端口,單獨的Cluster IP 不具備TCP/IP通信的基礎,並且屬於 Kubernetes集羣這樣一個封閉的空間,集羣之外的節點如果要訪問這個通信端口,則需要做一些額外的工作在Kubernetes集羣之內,Node IP網,Pod IP網之間的通信,採用的是Kubernetes 自己涉及的一種編程方式的特殊路由規則,與我們數值的IP由有很大的不同
總結: Service的Cluster IP屬於Kubernetes集羣內部的地址,無法在集羣外部使用這個地址。
採用NodePort解決此問題的最直接,最有效,最常用的做法。如下定義擴展如下(後面加井號)
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
type: NodePort ##
ports:
- port: 8080
nodePort: 31002 ##
selector:
tier: frontend
其中,nodePort:31002這個屬性表明我們手動指定tomcat-service的NodePort爲31002,否則Kubernetes會自動分配一個可用的端口,(http://nodePordIP:31002)
NidePort的實現方式是在Kubernetes集羣裏的每個Node上爲需要外部風聞的Service開啓一個對應的TCP建通端口,外部系統只要用任意一個Node的IP地址+具體的IP地址端口號即可訪問此服務,在任意Node上運行netstat命令,即可看到NodePort端口被監聽
netstat -tnlp |grep 31002
但NodePort 還沒完全解決外部訪問Service的所有問題,比如負載問題,假如集羣中有10個Node,則此時最好有一個負載均衡器,外部的請求只需訪問此負載均衡的IP地址由負載均衡器負責轉發流量到後面某個Node的NodePort上
如上圖中的Load balancer組件獨立於Kubernetes集羣之外,通常是一個硬件的負載均衡器,或者是以軟件方式實現的,例如HAProxy或者N。對於每個Service,我們通常需要配置一個對應的Load balancer 實例來轉發流量到後端的Node上,這樣增加了工作量及出錯的概率,因此Kubernetes 提供了自動化的解決方案,如果我們的集羣運行在谷歌的GCE公有云上,那麼只要我們把Service的type=NodePort改爲type = LoadBalancer,此時Kubernetes會自動創建一個對應的Load balancer實例並返回它的IP地址供外部客戶端使用,
9. Volume(存儲卷)
Volume是Pod中能夠被多個容器訪問的共享目錄。Kubernetes的Volume概念,用途和目的與Docker 的Volume 比較類似,單兩者不能等價。首先,Kubernetes中的Volume定義的Pod上,然後被一個Pod裏的多個容器掛載到具體的文件目錄下;其次,Kubernetes中的Volume與Pod的生命週期相同,但與容器的生命週期不相關,當容器終止或者啓動時,Volume中的數據也不會丟失,最後,Kubernetes支持多種類型的Volume,例如GlusterFS,Ceph等先進的分佈式文件系統
Volume的使用也比較簡單,在大多數情況下,我們現在Pod上聲明一個Volumn,然後在容器裏引用該Volume並Mount到容器裏的某個目錄上,舉例說,我們要給之前的Tomcat Pod增加一個名字爲dataVol的Volume,並且Mount到容器的/mydata-data目錄上,則只要對Pod的定義文件修改如下(加#)
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
除了可以讓一個Pod裏的多個容器共享文件,讓容器的數據寫到宿主機的磁盤上或者寫文件到網絡存儲中,Kubernetes 的Volume還擴展出了一種非常有實用價值的功能,即容器配置文件集中化定義與管理,這是通過ConfigMap這個新的資源對象來實現的
Kubernetes的Volume類型
1.emptyDir
一個emptyDir Volume是在Pod分配到Node時創建的,從它的名稱可以看出,它的初始內容爲空,並且無須指定宿主機上對應的目錄文件,因爲這是Kubernetes自動分配的一個目錄,當Pod從Node上移除時,emptyDir中的數據會被永久刪除,emptyDir如下
1.1 臨時空間,例如用於某些程序運行時所需的臨時目錄,且無需永久保留。
1.2 長時間任務的中間過程CheckPoint的臨時保存目錄
1.3 一個容器需要從另外一個容器中獲取數據的目錄(多容器共享目錄)
目前,用戶無法控制emptyDir使用的介質種類。如果kubelet的配置是使用硬盤,那麼所有emptyDir都將創建在該硬盤上。Pod在將來可以設置emptyDir是位於硬盤,固態硬盤上還是基於內存的tmpfs上,上面的例子採用了emptyDir類的Volume.
2. hostPath
hostPath爲在Pod上掛載宿主機上的文件或目錄,它通常可以用於一下幾個方面。
2.1 容器應用程序生成的日誌文件需要永久保存時,可以使用宿主機的高速文件系統進行存儲
2.2 容器訪問宿主機上Docker引擎內部數據結構的容器應用時,可以通過定義的hostPath爲宿主機/var/lib/docker 目錄,使容器內部應用可以直接訪問Docker的文件系統。
使用池中類型的Volume時,注意以下幾點
1.1 在不同的Node上具有相同配置的Pod可能會因爲宿主機上的目錄和文件不同而導致對Volume上目錄和文件的訪問結果不一致
1.2 如果使用了資源配額管理,則Kubernetes 無法將hostPath在宿主機上使用的資源納入管理
下面例子中使用宿主機的/data目錄定義了一個hostPath類型的Volume;
volumes:
- name: "persistent-storage"
hostPath:
path: "/data"
3. gcePersistentDisk
使用這種類型的Volume表示使用谷歌公有云提供的永久磁盤(Persistent Disk, PD)存放Volume的數據,它與EmptyDir不同,PD上的內容會被永久保存,當Pod被刪除時,PD只是被卸載(Unmout)但不會被刪除。需要注意額是你需要先創建一個永久磁盤(PD),才能使用gecPersistentDisk
限制條件如下
1. Node(運行kubelet的節點)需要是GCE虛擬機
2. 這些虛擬機需要與PD存在於相同的GCE項目中和Zone中
通過gcloud命令即可創建一個PD
gcloud compute disks create --size=500GB --zone=us-centrall-a my-data-disk
定義gcePersistentDisk 類型的Volume的實例如下;
volumes:
- name: test-volume
# This GCE PD must already exist.
gcePersistentDisk:
pdName: my-data-disk
fsType: ext4
4. awkElasticBlockStore
與GCE類似,該類型的Volume使用亞馬遜共有云提供的EBS Volume存儲數據,需要先創建一個EBS Volume才能使用awsElasticBlockStore.
限制如下:
1,Node (運行kubelet的節點)需要AWS EC2實例
2,這些AWS EC2實例需要與EBS volume 存在於相同的region和availability-zone中
3,EBS只支持單個EC2實例mount一個volume.
通過awk ec2 create-volume命令可以創建一個EBS volume
aws ec2 create-volume --availability-zone eu-west-la --size 10 --volume-type gp2
定義awsElasticBlockStore 類型的Volume的示例如下
volumes:
- name: test-volume
# This AWS EBS volume must already exist.
awkElasticBlockStore:
volumeID: aws://<availability-zone>/<volume-id>
fsType: ext4
5.NFS
使用NFS網絡文件系統提供的共享目錄存儲數據時,我們需要在系統中部署一個NFS Server.定義NFS類型的Volume的示例如下:
volumes:
- name: nfs
nfs:
# 改爲你的NFS服務器地址
server: nfs-server.localhost
path: "/"
6. 其他類型的Volume
1. iscsi: 使用iSCSI存儲設備上的目錄掛載到Pod中
2. flocker: 使用Flocker來管理存儲卷
3. glusterfs: 使用開源GlusterFS網絡文件系統的目錄掛載到Pod中
4. rbd:使用Linux塊設備共享存儲(Rados Block Device)掛載到Pod中
5. gitRepo: 通過掛載一個空目錄,並從GIT庫clone 一個git repository以供Pod使用
6. secret: 一個secret volume用於爲Pod提供加密的信息,聶可以將定義在Kubernetes中的secret直接掛載爲文件讓Pod訪問。secret volume 是通過tmfs(內存文件系統)實現的,所以這種類型的volume總是不會持久化的
10, Persistent Volume
之前我們提到的Volume是定義在Pod上的,屬於“計算資源”的一部分,而實際上,“網絡存儲”是相對獨立於“計算資源”而存在的一種實體資源。比如在使用虛機的情況下,我們通常會定義一個網絡存儲,然後從中劃出一個”網盤“並連接到虛擬機上。Persistent Volume(簡稱PV)和與之相關聯的Persistent Volume Claim(簡稱PVC)也起到了類似的作用
PV可以理解成Kubernetes集羣中的某個網絡存儲中對應的一塊存儲,它與Volume很類似,區別如下
1. PV只能是網絡存儲,不屬於任何Node,但可以在每個Node上訪問。
2. pv並不是定義的Pod上的,而是獨立於Pod之外定義
3. PV目前只有幾種類型: GCE Persistent Disks, NFS , RBD , iSCSCI , AWs, ElasticBlockStore,GlusterFS等
下面是一個NFS類型PV的一個yaml定義文件,聲明瞭需要5Gi的存儲空間
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
nfs:
path: /somepath
server: 172.17.0.2
比較重要的是PV的accessModes屬性,類型如下
1. ReadWriteOnce: 讀寫權限,並且只能被單個Node掛載
2. ReadOnlyMany: 只讀權限,允許被多個Node掛載
3. ReadWriteMany: 讀寫權限,魚汛被多個Node掛載
如果某個Pod想申請某種條件的PV,則首先需要定義一個PersistentVolumeClaim(PVC)對象:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
然後,在Pod的Volume定義中引用上述PVC即可:
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
PV的狀態,PV是由狀態的對象,有以下集中狀態。
1. Available: 空閒狀態
2. Bound: 已經綁定到某個PVC上。
3. Released: 對應的PVC已經刪除,但資源還沒有被集羣收回。
4. Failed: PV自動回收失敗
11. Namespace(命名空間)
Namespace(命名空間)是Kubernetes系統中的另外一個非常重要的概念,Namespace在很多情況下用於實現多租戶的資源隔離。Namespace 通過將集羣內部的資源對象“分配”到不同的Namespace中,形成邏輯上分組的不同項目,小組或用戶組,便於不同的分組在共享使用整個集羣的資源的同事還能被分別管理。
Kubernetes集羣自動啓動後,會創建一個名爲"default"的Namespace,通過kubectl可以查看到:
[root@server /data/volume]# kubectl get namespace
NAME STATUS AGE
default Active 30d
kube-system Active 30d
接下來,如果不特別知名Namespace,則用戶創建的Pod,RC,Service都將被系統創建到這個默認的名爲default的Namespace中。
Namespace的定義很簡單。如果所示的yaml定義了名爲development的Namespace
apiVersion: v1
kind: Namespace
metadata:
name: development
一旦創建了Namespace,我們在創建資源對象時就可以指定這個資源對象屬於那個Namespace.如下,我們定義了一個名爲busybox的Pod,放入development這個Namespace裏:
apiVersion: v1
kind: Pod
netadata:
name: busybox
namespace: development
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
name: busybox
此時,使用kubectl get 命令無法查看將無法顯示
因爲不加參數,則kubectl get 命令將僅顯示屬於"default"命名空間的資源對象。
可以在kubectl命令中加入--namespace參數來查某個命名空間的對象:
kubectl get pods --namespace=development
當我們給每個租戶創建一個Namespace來實現多租戶的資源隔離時,還能結合Kubernetes的資源配額管理,限定不同租戶能佔用的資源,例如CPU使用量,內存使用量等。
12. Annotation (註解)
Annotation與Label類似,也使用key/value鍵值對的形式來進行定義。不同的是Label具有嚴格的命名規則,它定義的是Kubernetes對象的元數據(Metadata),並且用於Label Selector。而Annotation則是用戶任意定義的"附加"信息。以便於外部工具進行查找,很多時候,Kubernetes的模塊自身會通過Annotation的方式標記資源對象的一些特殊信息
Annotation信息如下
1.build信息,release信息,Docker鏡像信息,例如時間戳,release id號,PR號,鏡像hash值,docker registry地址等
2. 日誌庫,監控庫,分析庫等資源庫的地址信息
3. 程序調試工具信息,例如工具名稱,版本號等
4. 團隊的聯繫信息,例如電話號碼,負責人名稱,網址等。
13.小結
如上是Kubernetes系統的核心組件,共同構成Kubernetes系統核心的框架和計算模型。通過對他們進行靈活組合,用戶就可以快速,方便的對容器集羣進行配置,創建和管理。