資料
背景
資源和對象
Kubernetes 中的所有內容都被抽象為“資源”,如 Pod、Service、Node 等都是資源。“對象”就是“資源”的實例,是持久化的實體。如某個具體的 Pod、某個具體的 Node。Kubernetes 使用這些實體去表示整個集群的狀態。
對象的創建、刪除、修改都是通過 “Kubernetes API”,也就是 “Api Server” 組件提供的 API 接口,這些是 RESTful 風格的 Api,與 k8s 的“萬物皆對象”理念相符。命令行工具 “kubectl”,實際上也是調用 kubernetes api。
K8s 中的資源類別有很多種,kubectl 可以通過配置文件來創建這些 “對象”,配置文件更像是描述對象“屬性”的文件,配置文件格式可以是 “JSON” 或 “YAML”,常用 “YAML”。
ps: 本文不會刻意說 “對象” 這一詞,對於某個具體的 Pod、Node,有時候可能也說成是 “資源”,這其實也是合理的,對象是資源的實例,所以對象也是資源,不必要鑽牛角尖,理解意思即可。
對象規約(Spec)和狀態(Status)
對象是用來完成一些任務的,是持久的,是有目的性的,因此 kubernetes 創建一個對象后,將持續地工作以確保對象存在。當然,kubernetes 並不只是維持對象的存在這么簡單,kubernetes 還管理着對象的方方面面。每個Kubernetes對象包含兩個嵌套的對象字段,它們負責管理對象的配置,他們分別是 “spec” 和 “status” 。
- “spec” 是 “規約”、“規格” 的意思,spec 是必需的,它描述了對象的期望狀態(Desired State)—— 希望對象所具有的特征。當創建 Kubernetes 對象時,必須提供對象的規約,用來描述該對象的期望狀態,以及關於對象的一些基本信息(例如名稱)。以 Pod 為例,如下是一個簡單的創建 Pod 的 yaml 文件模板:
apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: - name: myapp-container image: busybox command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
上面的模板描述了一個對象,對象的類型是 “Pod”,對象名為 “myapp-pod”,包含一個 “app: myapp” 標簽。“spec” 指定了該 Pod 對象的特征——對象包含一個名為 “myapp-container” 的容器,容器根據 “busybox” 鏡像生成,容器運行的命令是 “ ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600'] ”。
除 “spec” 字段外,在創建一個Pod及Pod的控制器對象時,還要像上面模板所示一樣,還要有這三個字段:
“apiVersion”——“創建該對象所使用的 Kubernetes API 的版本”;
“kind”——“想要創建的對象的類型”;
“metadata”——“幫助識別對象唯一性的數據,包括一個 name 字符串、UID 和可選的 namespace。”
- status 描述了對象的 實際狀態(Actual State) ,它是由 Kubernetes 系統提供和更新的。在任何時刻,Kubernetes 控制面一直努力地管理着對象的實際狀態以與期望狀態相匹配。
對象的常用屬性
name和uid
k8s 中每個對象都有 uid ,uid 是 k8s 自動為對象生成的,可以唯一標識該對象的字符串。uid 是用於給 k8s 本身管理對象的標識,對人類來說其可讀性太差,因此對象還具有另一個屬性 —— 可讀性相對更好的,有一定意義的 “名稱” 。在創建對象時可以給對象指定名稱。不屬於同一類別的對象可以有相同的名稱,而同一種類型的對象,想要賦予相同的名稱,則需要用到 namespace。
NameSpace
很多地方都有 namespace 的概念,如 Linux 中的 namespace 、C++ 中的 namespace 等等。 C++ 用 namespace 來解決命名沖突(兩個不同的 namespace 可以存在同名的類),k8s 中 的 namespace 也有這樣的作用——不同的 namespace 中可以存在同名的資源對象(即使這些對象是相同的類型)。然而,k8s 中的 namespace,更多的是為了提高資源的管理效率。
在 k8s 中, namespace (命名空間) 也稱為虛擬集群。namespace 用來在邏輯上對資源進行分組(是的,namespace 用來給資源分組,namespace 本身也是資源...)。怎樣個分組法?譬如,一個 k8s 集群上部署兩個應用——一個是商城,叫 sMall;另一個是視頻類應用,叫 iQipa。那么可以創建兩個 namespace,然后給前者所有資源分配名為 “sMall” 的 namespace,后者分配 “iQipa”,這樣,兩個不同的項目各自管理自己的資源,互不干擾。
可以用 “kubectl get” 命令羅列在集群中存在的某一類資源的對象,例如想要獲取 “sMall” 的所有 pod,可以輸入命令:
kubectl get pods --namespace=sMall
對應的,如果想要獲取 iQipa 的 service,則輸入命令
kubectl get services --namespace=iQipa
k8s 集群搭建好后,會創建一個名為 "default" 的 namespace,當沒有為資源明確指定 namespace 時,會默認分配該 namespace。kubernetes 的組件,分配的是名為 “kube-system” 的 namespace。
命名空間為名稱提供了一個范圍。對象的名稱需要在命名空間內是唯一的,但不能跨命名空間。命名空間不能相互嵌套,每個 Kubernetes 對象只能在一個命名空間中。大多數 kubernetes 資源的對象(例如 Pod、Service、副本控制器等)都位於某些命名空間中。但是命名空間資源本身並不在命名空間中。而且底層資源,例如 nodes 和持久化卷不屬於任何命名空間。可以使用命令查看哪些資源在 namespace 中,哪些不在:
# 列出在命名空間中的資源
kubectl api-resources --namespaced=true
# 列出不在命名空間中的資源
kubectl api-resources --namespaced=false
更多資料:
label和selector
label (標簽)是附加到 Kubernetes 對象(比如 Pods)上的鍵值對,用於區分對象(比如Pod、Service)。 label 旨在用於指定對用戶有意義且相關的對象的標識屬性,但不直接對核心系統有語義含義。 label 可以用於組織和選擇對象的子集。label 可以在創建時附加到對象,隨后可以隨時添加和修改。可以像 namespace 一樣,使用 label 來獲取某類對象,但 label 可以與 selector 一起配合使用,用表達式對條件加以限制,實現更精確、更靈活的資源查找。
label 與 selector 配合,可以實現對象的“關聯”,“Pod 控制器” 與 Pod 是相關聯的 —— “Pod 控制器”依賴於 Pod,可以給 Pod 設置 label,然后給“控制器”設置對應的 selector,這就實現了對象的關聯。
每個對象都可以定義一組鍵/值標簽。每個鍵對於給定對象必須是唯一的。如:
"metadata": {
"labels": {
"app1" : "iQipa",
"app2" : "sMall"
}
}
更多資料:
對象信息的獲取
“Kubectl” 這個命令行工具提供了與 k8s 交互的接口,可以通過 kubectl 來創建、更改、查看、刪除對象,這里只稍微講解下對象信息的獲取,其它放到 Kubectl 有關的文章中再介紹。要查看當前 k8s 集群中存在的某一類資源,可以使用 kubectl 的 “get” 選項:
kubectl get <resource_type> --namespace=<namespace>
參數說明:
-
<resource_type> 就是資源的類別,可以是 pod、node 或其它所有類別的名稱。這里可以輸入資源的單數形式、復數形式,比如 “pod” 還可以寫成 “pods”;對於稍微長一點的資源名,還可以使用縮寫,比如 “service”,可以輸入其縮寫 “svc”。
-
<namespace> <namespace>后面的 “=” 可有可無。namespace 是對象所在的 namespace,namespace 可以看做是對象的分組的組名,下文會簡單介紹。k8s 集群初始化后會創建名為 “default” 的 namespace,在創建對象時如果沒有指定 namespace,默認使用這個 namespace。如果在使用 “kubectl get” 時不指定 “--namespace”這個參數,獲取的是 “default” namespace 下的對象。 另外 “--namespace” 可以簡寫成 “-n”,如果想獲取所有 namespace 下的對象,則可以寫成 “--all-namespaces”。
幾個示例:
- 查看集群中存在的所有 namespace
kubectl get namespace - 查看當前集群下所有存在的 node ( node 不在 namespace 中)
kubectl get node - 查看當前存在的所有 pod
kubectl get pods --all-namespaces - 查看“kube-system”下所有的service
kubectl get svc -n kube-system
“get” 選項只是羅列出資源對象而已,如果想要查看對象的具體信息,需要用 “describe”
kubectl describe <resource_type> <object_name> --namespace=<namespace>
參數說明:
-
<resource_type> 跟上面一樣,可以寫單復數形式、簡寫。<namespace> 也是一樣的,可以寫成 “-n”,但有一點需要注意,不能寫成 “--all-namespaces”,因為如果不同 namespace 有兩個同名同類別的對象,那么就出現歧義了,“--all-namespaces” 對 “kubectl describe” 是沒有意義的,所以不要這樣寫。
-
<object_name> 就是對象的名稱,也就是 “kubectl get” 命令顯示的結果。
示例:
- 獲取名為 “kube-apiserver”,namespace 為 “kube-system” 的 pod 的信息
kubectl describe pod kube-apiserver -n kube-system - 獲取名為 “hello” 的 node 的信息
kubectl describe node hello
小 Tips: 可以用 “kubectl explain” 來查看某類資源的作用,如 “kubectl explain pod”,不過顯示的信息都是一兩句話帶過。。不然我也懶得寫這篇。。(;′⌒`)
更多資料:
資源種類
接下來開始正式介紹 k8s 的各種資源。
Pod
Pod 是最小的可部署的 Kubernetes 對象模型。Pod 表示集群上正在運行的進程。一個 Pod 由一個或多個容器組成,Pod 中容器共享存儲和網絡,在同一台 Docker 主機上運行。在 kubernetes 中,若要運行一個容器,則必須先創建 pod,讓容器在 pod 中運行,可以把 Pod 看成是容器的運行環境。
Docker 是 Kubernetes Pod 中最常用的容器運行時,但 Pod 也能支持其他的容器運行時,如 rtk。
Kubernetes 集群中的 Pod 可被用於以下兩個主要用途:
-
運行單個容器的 Pod。”每個 Pod 一個容器”模型是最常見的 Kubernetes 用例;在這種情況下,可以將 Pod 看作單個容器的包裝器,並且 Kubernetes 直接管理 Pod,而不是容器。
-
運行多個協同工作的容器的 Pod。 Pod 可能封裝由多個緊密耦合且需要共享資源的共處容器組成的應用程序。 這些位於同一位置的容器可能形成單個內聚的服務單元——一個容器將文件從共享卷提供給公眾,而另一個單獨的“掛斗”容器則刷新或更新這些文件。 Pod 將這些容器和存儲資源打包為一個可管理的實體。
上面講對象的“規約”時已經提及 Pod 的模板文件,這里不再贅述。
Pod是容器的運行環境,而我們的應用程序是部署在容器里的,對於k8s的初學者,可以先把Pod簡單地看作為應用的運行容器,把Pod看作是應用在k8s上部署的最小單位。可以簡單粗暴的說“Pod就是用來部署應用的,Pod就是用來裝應用的。”
Pod是不能被外網直接訪問的,所以不要想着只要把應用部署到Pod就可以直接用,要想在外網訪問Pod需要用到service,下文會介紹。
更多資料:
Controller(控制器)
當 Pod 被創建出來,Pod 會被調度到集群中的節點上運行,Pod 會在該節點上一直保持運行狀態,直到進程終止、Pod 對象被刪除、Pod 因節點資源不足而被驅逐或者節點失效為止。Pod 並不會自愈,當節點失效,或者調度 Pod 的這一操作失敗了,Pod 就該被刪除。如此,單單用 Pod 來部署應用,是不穩定不安全的。
Kubernetes 使用更高級的資源對象 “控制器” 來實現對Pod的管理。控制器可以為您創建和管理多個 Pod,管理副本和上線,並在集群范圍內提供自修復能力。 例如,如果一個節點失敗,控制器可以在不同的節點上調度一樣的替身來自動替換 Pod。
先引入“副本”的概念——一個Pod可以被復制成多份,每一份可被稱之為一個“副本”,這些“副本”除了一些描述性的信息(Pod的名字、uid等)不一樣以外,其它信息都是一樣的,譬如Pod內部的容器、容器數量、容器里面運行的應用等的這些信息都是一樣的,這些副本提供同樣的功能。Pod 的“控制器”通常包含一個名為 “replicas” 的屬性。“replicas”屬性則指定了特定Pod的副本的數量,當當前集群中該Pod的數量與該屬性指定的值不一致時,k8s會采取一些策略去使得當前狀態滿足配置的要求。
k8s的一些Pod“控制器”可以提供Pod的“滾動更新”功能,如果你的應用升級了,譬如原來應用是v1版本,現在的版本是v2,那么可以通過僅僅一條命令或一份配置文件,讓k8s來自動地滾動更新應用。k8s會刪除一個v1的Pod,然后新建一個v2的Pod……這樣反復交替操作,直至所有v1Pod被v2Pod代替,這樣就實現了不停機的應用滾動更新。k8s會保存應用的更新記錄,在需要“回滾降級”時,同樣可以通過僅僅一條命令或者一個配置文件實現。
因此,雖然可以直接使用 Pod,可以直接創建Pod,但在 Kubernetes 中,更為常見的是使用控制器創建和管理 Pod。在部署應用時,“控制器” 是比簡單的 Pod 更好的選擇。
下面開始介紹 Pod 的各種控制器,注意,這些控制器也是 k8s 中的資源,也可以創建對象。
ReplicationController
ReplicationController 簡寫 “RC” 或 “RCS”。譯作“副本控制器”,“Replication” 就是“復制”、“副本”的意思。ReplicationController 確保在任何時候都有特定數量的 pod 副本處於運行狀態。 換句話說,ReplicationController 確保一個 pod 或一組同類的 pod 總是可用的。
當 pods 數量過多時,ReplicationController 會終止多余的 pods。當 pods 數量太少時,ReplicationController 將會啟動新的 pods。 與手動創建的 pod 不同,由 ReplicationController 創建的 pods 在失敗、被刪除或被終止時會被自動替換。
下面給出一份生成 ReplicationController 對象的配置文件示例:
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx
spec:
replicas: 3
selector:
app: nginx
template:
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
來看下配置文件,該文件描述了一個資源對象,它有4個字段,分別是 “apiVersion”、“kind”、“metadata”、“spec”。上面講對象與“規約”時講過,創建一個 Pod 及 Pod控制器 對象時,這四個字段是必不可少的。kind 說明這是一個 “ReplicationController” 類型的對象,metadata 說明對象名稱是 “nginx”。spec 指定了這個對象的特征,關注 spec 這個字段:
-
template
在 spec 中 template 這個字段是必需的,其實這就是一個 Pod 的模板,只不過這個模板是嵌套進來的,不需要 kind 和 apiVersion 字段。再來看下這是怎樣一個 Pod —— metadata 部分描述了 Pod 的名稱 “nginx”,還為這個 Pod 分配了名為 “app: nginx” 的標簽。spec 指定了該 Pod 由 “nginx” 鏡像構建而成,容器開啟了“80”端口。 -
replicas
replica就是“復制品”、“副本”的意思,“replicas: 3”就是開啟三個 Pod 副本,並維護這些副本,當 Pod 出錯了,就把 Pod 刪掉然后創建一個新的 Pod,使得 Pod 的數量始終維持在3個,這充分提高了部署的應用的高可用性。當然,在 k8s 運行過程中某個瞬間,ReplicationController 管理的 Pod 由於種種原因,可能比設置的 replicas 的數量多,也可能少,但 ReplicationController 會采取合適的策略盡量維持 Pod 的數量。 -
selector
通過指定 ReplicationController 的 selector 和 Pod 的 “app: nginx” 標簽,可以實現 ReplicationController 和 Pod 的綁定關系,ReplicationController 就是僅僅管理這些附帶 “app: nginx” 標簽的 Pod 而不干涉其它的 Pod。不是由 ReplicationController 本身創建的,但包含 “app: nginx” 的 Pod,也由其管理。
更多資料:
ReplicaSet
ReplicaSet 簡寫 “RS”,是 “Replication Controller” 的升級版。和 “ReplicationController” 一樣用於確保任何給定時間指定的Pod副本數量,並提供聲明式更新等功能。
RC與RS唯一區別就是lable selectore支持不同,RS支持新的基於集合的標簽,RC僅支持基於等式的標簽。
Deployment
Deployment是一個更高層次的API對象,他管理ReplicaSets和Pod,並提供聲明式更新等功能。
官方建議使用Deployment管理ReplicaSets,而不是直接使用ReplicaSets,這就意味着可能永遠不需要直接操作ReplicaSet對象。
StatefulSet
StatefulSet適合持久性的應用程序,有唯一的網絡標識符(IP),持久存儲,有序的部署、擴展、刪除和滾動更新。
DaemonSet
Daemon 一詞應該不陌生的,Daemon 進程(守護進程)、Demon 程序(守護程序)。顧名思義,DaemonSet 一般是用來部署一些特殊應用的,譬如日志應用等有“守護”意義的應用。
DaemonSet確保所有(或一些)節點運行同一個Pod(當然這里不是指“同一個”,而是和副本一樣的概念,當然不可能多個節點運行“同一個”Pod,它又不是量子態)。當節點加入kubernetes集群中,Pod會被調度到該節點上運行,當節點從集群中移除時,DaemonSet的Pod會被刪除。刪除DaemonSet會清理它所有創建的Pod。
Job
一次性任務,運行完成后Pod銷毀,不再重新啟動新容器。
CronJob
CronJob 是在 Job 基礎上加上了定時功能。
HorizontalPodAutoscaling
更多資料:
------------------------以上都是 Pod 的 Controller---------------------
Service
“Service” 簡寫 “svc”。如上文提到的,Pod不能直接提供給外網訪問,而是應該使用service。Service就是把Pod暴露出來提供服務,Service才是真正的“服務”,它的中文名就叫“服務”。o( ̄︶ ̄)o
按照百度上找到的更專業的的說法,可以說Service是一個應用服務的抽象,定義了Pod邏輯集合和訪問這個Pod集合的策略。Service代理Pod集合,對外表現為一個訪問入口,訪問該入口的請求將經過負載均衡,轉發到后端Pod中的容器。
Pod只是運行應用的容器,只是簡單地提供某種“功能”,不能稱之為“服務”。而service不一樣,service是對Pod訪問方式的抽象,service利用Pod的“功能”,利用“Pod”的能動性,通過路由轉發和負載均衡,對外提供真正意義上的“服務”。
k8s使用service還有一個原因。一般而言,k8s每創建一個新的Pod,它的ip地址都是不一樣的(當然,你可以設置固定ip,但這樣並不妥當,程序員都喜歡“解耦”,而這么做顯然是與該理念背道而馳了),如果一個Pod因為某些問題掛了,k8s創建了一個新的Pod,這時ip地址不一樣了,那它還怎么提供功能?這時,Service意義就出來了,一個Service與特定的一個或者一組Pod掛鈎,即使Pod掛掉了,k8s又創建了新的特定的Pod,Service仍然與這個新的Pod掛鈎,這樣,Pod的ip不一樣了,哪怕端口也不一樣了,仍然能通過Service來獲取Pod所提供的服務。
Service是如何保持這種與特定Pod綁定的關系的呢?那就是“Label”和“Label Selector”,可以給Pod分配特定的Label,然后配置Service,通過“Lable Selector”選擇具有這些特定“Label”的Pod來接受請求、提供服務。
默認情況下,在創建service對象的時候,如果不指定service的訪問方式,service被配置為ClusterIp模式,在該模式下,只有集群內部的網絡才能訪問到該service,要想真正的實現外網訪問service,需要把service訪問方式配置為NodePort或其它。
更多資料:
以下內容待更…………
Volume
數據卷,共享Pod中容器使用的數據。用來放持久化的數據,比如數據庫數據。
Secret
用來存放一些隱秘的數據,見不得人的數據,比如證書、token、密碼等,可以像 Volume 一樣被一個 Pod 引用(掛載)。
ConfigMap
用來放配置,與 Secret 是類似的,只是ConfigMap放的是明文的數據,Secret 是密文存放。
Ingress
Ingress 可以提供外網訪問 Service 的能力。可以把某個請求地址映射、路由到特定的 service。ingress 需要配合 ingress controller 一起使用才能發揮作用,ingress只是相當於路由規則的集合而已,真正實現路由功能的,是 Ingress Controller,ingress controller 和其它 k8s 組件一樣,也是在Pod中運行。
更多資料:
ThirdPartyResource
ServiceAccount
訪問Service的Account
