目錄
- Kubernetes 是什么
- 為什么要用 Kubernetes
- Kubernetes 概念
- Master
- Node
- Pod
- Label
- Replication Controller
- Deployment
- Horizontal Pod Autoscaler
- StatefulSet
- Service
- 服務發現機制
- Job
- Volume
- Persistent Volume
- Namespace
- Configmap
Kubernetes 是什么
Kubernetes是一個開放的開發平臺、完備的分布式系統支撐平臺,具有完備的集群管理能力。
對現有的編程語言、編程框架、中間件沒有任何侵入性。它讓我們不必費心于負載均衡器的選型和部署實施,不必考慮服務治理,不必頭疼于服務監控和故障處理;讓開發更專注于業務本身,而且其強大的自動化機制,使得后期的運維難度和成本大幅降低。
- 多層次的安全防護和準入機制
- 多租戶應用支撐能力
- 透明的服務注冊和服務發現機制
- 內建的智能負載均衡器
- 強大的故障發現和自我修復能力
- 服務滾動升級和在線擴容能力
- 可擴展的資源自動調度機制
- 多粒度的資源配額管理能力
Kubernetes 是一個全新的基于容器技術的分布式架構解決方案,Service 是分布式集群架構的核心,關鍵特征有:
- 擁有唯一 指定的名稱(如 redis-server)
- 擁有一個虛擬 IP(Cluster IP、Service IP 或 VIP)和端口號
- 能夠提供遠程服務能力
- 被映射到提供這種服務能力的一組容器應用上、
Service 的服務進程都基于 Socket 通信方式對外提供服務,或者是實現了某個具體業務的特定 TCP Server 進程。每個服務進程都有一個獨立的 Endpoint(IP + Port),但是 Kubernetes 能夠通過 Service(虛擬 Cluster IP + Service Port)連接到指定的 Service。
容器提供了強大的隔離功能。k8s 設計了 Pod 對象,將每個服務進程都包裝到相應的 Pod 中,使其成為在 Pod 中運行的一個容器。為了建立 Service 和 Pod 的關聯關系,k8s 給每個 Pod 貼上一個標簽(Label),解決了 Service 和 Pod 的關聯問題。
比如給運行MySQL的Pod貼上name=mysql標簽
MySQL Service的標簽選擇器的選擇條件為name=mysql,意為該Service要作用于所有包含name=mysql Label的Pod
- Pod 運行在節點(Node)的環境中,節點可以是物理機或虛擬機,通常一個節點上可以運行幾百個 Pod;
- 每個 Pod 中都運行著一個特殊的 Pause 容器,其他容器為業務容器,共享 Pause 容器的網絡棧和 Volume 掛在卷,這樣它們之間的通信和數據交換更為高效。在設計時我們可以充分利用這一特性將一組密切相關的服務進程放入同一個 Pod 中。
- 最后,并不是每個 Pod 和它里面運行的容器都能被映射到一個 Service 上,只有提供服務的那組 Pod 才會被映射為一個服務。
Node 作為集群中的工作節點,運行真正的應用程序,在 Node 上 k8s 管理的最小運行單元是 Pod。在 Node 上運行著 k8s 的 kubelet、kube-proxy 服務進程,負責 Pod 的創建、啟動、監控、銷毀,以及實現軟件模式的負載均衡器。
在 k8s 集群中,只需為需要擴容的 Service 關聯的 Pod 創建一個RC(Replication Controller,定義 Pod 模板)?,服務擴容以至服務升級都迎刃而解。在一個RC定義文件中包括以下3個關鍵信息:
- 目標Pod的定義
- 目標Pod需要運行的副本數量(Replicas)?
- 要監控的目標Pod的標簽
為什么要用 Kubernetes
- 微服務架構的核心是將一個巨大的單體應用分解為很多小的互相連接的微服務,一個微服務可能由多個實例副本支撐,副本的數量可以隨著系統的負荷變化進行調整
- 在 k8s 的架構方案中完全屏蔽了底層網絡的細節,基于 Service 的虛擬 IP 地址(Cluster IP)的設計思路讓架構與底層的硬件拓撲無關,我們無須改變運行期的配置文件,就能將系統從現有的物理機環境無縫遷移到公有云上
- k8s 內在的服務彈性擴容機制可以讓我們輕松應對突發流量
Kubernetes 概念
以下簡要介紹 k8s 中重要的資源對象。
Master
Master 是集群控制節點,每個集群中都需要一個 Master 負責整個集群的管理和控制,基本上 k8s 的所有控制指令都發給它來執行。Master 很重要,通常會占據一個獨立的服務器(HA: 3臺),如果它宕機或不可用,對集群內容器應用的管理都將失效。
Master上的關鍵進程:
- Kubernetes API Server(kube-apiserver)?:提供了HTTP Rest接口的關鍵服務進程,是Kubernetes里所有資源的增、刪、改、查等操作的唯一入口,也是集群控制的入口進程。
- Kubernetes Controller Manager(kube-controller-manager)?:Kubernetes里所有資源對象的自動化控制中心,可以將其理解為資源對象的“大總管”?。
- Kubernetes Scheduler(kube-scheduler)?:負責資源調度(Pod調度)的進程,相當于公交公司的“調度室”?。
另外,Master 上通常還需要部署 etcd 服務,所有資源對象的數據都被保存在 etcd 中。
Node
Node 是 k8s 集群中的工作負載節點,每個 Node 都會被 Master 分配一些工作負載(Docker容器)?,當某個 Node 宕機時,其上的工作負載會被 Master 自動轉移到其他節點上。
Node 上的關鍵進程:
- kubelet:負責 Pod 對應的容器的創建、啟停等任務,同時與 Master 密切協作,實現集群管理的基本功能
- kube-proxy:實現 Kubernetes Service 的通信與負載均衡機制的重要組件
- Docker Engine(docker)?:Docker 引擎,負責本機的容器創建和管理工作
Node 可以在運行期間動態增加到 k8s 集群中:
- 默認情況下 kubelet 會向 Master 注冊自己,這也是 k8s 推薦的Node管理方式
- 一旦 Node 被納入集群管理范圍,kubelet進程就會定時向 Master 匯報自身的情報,例如操作系統、Docker 版本、機器的 CPU 和內存情況,以及當前有哪些 Pod 在運行等,這樣 Master 就可以獲知每個 Node 的資源使用情況,并實現高效均衡的資源調度策略
- 某個 Node 在超過指定時間不上報信息時,會被Master判定為“失聯”?,Node 的狀態被標記為不可用(Not Ready)?,隨后Master會觸發“工作負載大轉移”的自動流程
kubectl get nodeskubectl desscribe node k8s-node-1
Pod
Pod 是 k8s 最重要的基本概念,每個 Pod 都有一個特殊的被稱為“根容器”的 Pause 容器。Pause 容器對應的鏡像屬于 k8s 平臺的一部分,除了 Pause 容器,每個 Pod 還包含一個或多個緊密相關的用戶業務容器。
- 引入業務無關并且不易死亡的Pause容器作為Pod的根容器,以它的狀態代表整個容器組的狀態,對“整體”進行判斷及有效地行動。
- Pod里的多個業務容器共享Pause容器的IP,共享Pause容器掛接的Volume,這樣既簡化了密切關聯的業務容器之間的通信問題,也很好地解決了它們之間的文件共享問題
Kubernetes為每個Pod都分配了唯一的IP地址,稱之為Pod IP,一個Pod里的多個容器共享Pod IP地址。Kubernetes要求底層網絡支持集群內任意兩個Pod之間的TCP/IP直接通信,這通常采用虛擬二層網絡技術來實現,在Kubernetes里,一個Pod里的容器與另外主機上的Pod容器能夠直接通信。
Label
Label(標簽)是 k8s 系統中另外一個核心概念。一個 Label 是一個key=value的鍵值對,其中 key 與 value 由用戶自己指定。Label 可以被附加到各種資源對象上,例如 Node、Pod、Service、RC 等,一個資源對象可以定義任意數量的Label,同一個 Label 也可以被添加到任意數量的資源對象上。Label 通常在資源對象定義時確定,也可以在對象創建后動態添加或者刪除。
可以通過Label Selector(標簽選擇器)查詢和篩選擁有某些Label的資源對象,Kubernetes通過這種方式實現了類似SQL的簡單又通用的對象查詢機制。
Label Selector可以被類比為SQL語句中的where查詢條件,例如,name=redis-slave這個Label Selector作用于Pod時,可以被類比為select * from pod where pod’s name =‘redis-slave’這樣的語句。
Label Selector 在 k8s 中的場景:
- kube-controller 進程通過在資源對象RC上定義的Label Selector來篩選要監控的Pod副本數量,使Pod副本數量始終符合預期設定的全自動控制流程
- kube-proxy 進程通過 Service 的 Label Selector 來選擇對應的 Pod,自動建立每個 Service 到對應 Pod 的請求轉發路由表,從而實現Service 的智能負載均衡機制
- 通過對某些 Node 定義特定的Label,并且在 Pod 定義文件中使用 NodeSelector 這種標簽調度策略,kube-scheduler 進程可以實現 Pod 定向調度的特性
Replication Controller
RC是 k8s 系統中的核心概念之一,它其實定義了一個期望的場景,即聲明某種 Pod 的副本數量在任意時刻都符合某個預期值,所以 RC 的定義包括如下幾個部分:
- Pod 期待的副本數量
- 用于篩選目標 Pod 的 Label Selector
- 當 Pod 的副本數量小于預期數量時,用于創建新 Pod 的 Pod 模板
Master 上的 Controller Manager 會定期巡檢系統中當前存活的目標 Pod,并確保目標 Pod 實例的數量剛好等于此 RC 的期望值,如果有過多的 Pod 副本在運行,系統就會停掉一些 Pod,否則系統會再自動創建一些 Pod。可以說,通過 RC,k8s 實現了用戶應用集群的高可用性,并且大大減少了系統管理員在傳統IT環境中需要完成的許多手工運維工作(如主機監控腳本、應用監控腳本、故障恢復腳本等)?。
k8s 1.2中,升級為另外一個新概念——Replica Set。Replica Set 與 RC當前的唯一區別是,Replica Sets 支持基于集合 的Label selector(Set-based selector)?,而 RC只 支持基于等式的 Label Selector(equality-based selector)?,這使得 Replica Set 的功能更強
Replica Set 很少單獨使用,它主要被 Deployment 這個更高層的資源對象所使用,從而形成一整套Pod創建、刪除、更新的編排機制。
RC (Replica Set) 特性與作用:
- 通過定義一個 RC 實現 Pod 的創建以及副本數量的自動控制
- 在 RC 里包括完整的 Pod 定義模板
- RC 通過 Label Selector 機制實現對 Pod 副本的自動控制
- 通過改變 RC 里的 Pod 副本數量,實現 Pod 的擴縮容
- 通過改變 RC 里的 Pod 模板中的鏡像版本,實現 Pod 的滾動升級
Deployment
k8s 在1.2版本中引入的新概念,用于更好地解決Pod的編排問題。Deployment 在內部使用了 Replica Set 來實現目的,無論從 Deployment的作用與目的、YAML定義,還是從它的具體命令行操作來看,我們都可以把它看作 RC 的一次升級,兩者的相似度超過 90%。
Deployment 相對于 RC 的一個最大升級是我們可以隨時知道當前 Pod “部署”的進度。
Deployment 使用場景:
- 創建一個 Deployment 對象來生成對用的 Replica Set 并完成 Pod 副本的創建
- 檢查 Deployment 的狀態來看部署動作是否完成
- 更新 Deployment 來創建新的 Pod(比如鏡像升級)
- 如果當前 Deployment 不穩定,可以回滾之前的版本
- 暫停 Deployment 來一次性修改多個 PodTemplate 的配置項,然后回復 Deployment 重新發布
- 擴展 Deployment 來 應對高負載
- 查看 Deployment 狀態,是否發布成功
- 清理不再需要的 舊版本 Replica Set
Horizontal Pod Autoscaler
k8s 1.1中首次發布重量級新特性——Horizontal Pod Autoscaling(Pod橫向自動擴容,HPA)?。
HPA 與之前的 RC、Deployment 一樣,也屬于一種 k8s 資源對象。通過追蹤分析指定 RC 控制的所有目標 Pod 的負載變化情況,來確定是否需要有針對性地調整目標 Pod 的副本數量,這是 HPA 的實現原理。當前,HPA有以下兩種方式作為Pod負載的度量指標:
- CPUUtilizationPercentage
- 應用程序自定義的度量指標,比如服務在每秒內的相應請求數(TPS或QPS)?
StatefulSet
在 k8s 系統中,Pod 的管理對象 RC、Deployment、DaemonSet 和 Job 都面向無狀態的服務。但現實中有很多服務是有狀態的,特別是一些復雜的中間件集群,例如 MySQL 集群、MongoDB 集群、ZooKeeper 集群等,這些應用集群有4個共同點:
- 每個節點都有固定的身份ID,通過這個ID,集群中的成員可以相互發現并通信
- 集群的規模是比較固定的,集群規模不能隨意變動
- 集群中的每個節點都是有狀態的,通常會持久化數據到永久存儲中
- 如果磁盤損壞,則集群里的某個節點無法正常運行,集群功能受損
StatefulSet 從本質上來說,可以看作 Deployment/RC 的一個特殊變種,它有如下特性:
- StatefulSet 中的每個 Pod 都有穩定、唯一的網絡標識,可以用來發現集群內的其他成員。比如 StatefulSet 的名稱為 kafka,那么第1個Pod 叫 kafka-0,第2個叫 kafka-1,以此類推
- StatefulSet 控制的 Pod 副本的啟停順序是受控的,操作第n個 Pod 時,前n-1個 Pod 已經是運行且準備好的狀態
- StatefulSet 里的 Pod 采用穩定的持久化存儲卷,通過 PV 或 PVC 實現,刪除 Pod 時默認不會刪除與 StatefulSet 相關的存儲卷
Service
Service 服務也是 k8s 里的核心資源對象之一,k8s 里的每個 Service 其實就是我們經常提起的微服務架構中的一個微服務,之前講解 Pod、RC 等資源對象其實都是為講解 k8s Service 做鋪墊。
k8s 的 Service 定義了一個服務的訪問入口地址,前端的應用(Pod)通過這個入口地址訪問其背后的一組由 Pod 副本組成的集群實例,Service 與其后端 Pod 副本集群之間則是通過 Label Selector 來實現對接的。RC 的作用實際上就是保證 Service 的服務能力和服務質量符合預期。
運行在每個 Node上 的 kube-proxy 進程其實就是一個智能的軟件負載均衡器,負責把對 Service 的請求轉發到后端的某個 Pod 實例上,并在內部實現服務的負載均衡與會話保持機制。但 k8s 發明了一種很巧妙又影響深遠的設計:Service 沒有共用一個負載均衡器的IP地址,每個 Service 都被分配了一個全局唯一的虛擬 IP 地址,這個虛擬I P 被稱為 Cluster IP。這樣一來,每個服務就變成了具備唯一 IP 地址的通信節點,服務調用就變成了最基礎的 TCP 網絡通信問題。
Service 一旦被創建,k8s 就會自動為它分配一個可用的 Cluster IP,而且在 Service 的整個生命周期內,它的 Cluster IP 不會發生改變。于是,服務發現這個棘手的問題在 k8s 的架構里也得以輕松解決:只要用 Service 的 Name 與 Service 的 Cluster IP 地址做一個 DNS 域名映射即可完美解決問題。
# 創建 service
kubectl create -f tomcat-server.yaml
# 查詢 endpoints
kubectl get endpoints
# 查詢 service cluster ip
kubectl get svc tomcat-service -o yaml
服務發現機制
每個 k8s 中的 Service 都有唯一的 Cluster IP 及唯一的名稱,而名稱是由開發者自己定義的,部署時也沒必要改變,所以完全可以被固定在配置中。
最早時 k8s 采用了 Linux 環境變量解決這個問題,即每個 Service 都生成一些對應的 Linux 環境變量(ENV)?,并在每個 Pod 的容器啟動時自動注入這些環境變量。每個 Service 的 IP 地址及端口都有標準的命名規范,遵循這個命名規范,就可以通過代碼訪問系統環境變量來得到所需的信息,實現服務調用。
現在,k8s 上的大部分應用都已經采用了 DNS 這種新興的服務發現機制。
k8s 中的3中 IP:
- Node IP:Node的IP地址
- Pod IP:Pod的IP地址
- Cluster IP:Service的IP地址
Node IP 是 k8s 集群中每個節點的物理網卡的 IP 地址,是一個真實存在的物理網絡,所有屬于這個網絡的服務器都能通過這個網絡直接通信,不管其中是否有部分節點不屬于這個 k8s 集群。這也表明在 k8s 集群之外的節點訪問 k8s 集群之內的某個節點或者 TCP/IP 服務時,都必須通過Node IP通信.
Pod IP是每個 Pod 的 IP 地址,它是 Docker Engine 根據 docker0 網橋的 IP 地址段進行分配的,通常是一個虛擬的二層網絡,k8s 中要求位于不同 Node 上的 Pod 都能夠彼此直接通信,所以 k8s 里一個 Pod 里的容器訪問另外一個 Pod 里的容器時,就是通過 Pod IP 所在的虛擬二層網絡進行通信的,而真實的 TCP/IP 流量是通過 Node IP 所在的物理網卡流出的。
Service的Cluster IP,它也是一種虛擬的IP,但更像一個“偽造”的IP網絡:
- Cluster IP 僅僅作用于Kubernetes Service這個對象,并由 k8s 管理和分配IP地址(來源于Cluster IP地址池)?
- Cluster IP 無法被 Ping,因為沒有一個“實體網絡對象”來響應
- Cluster IP 只能結合 Service Port 組成一個具體的通信端口,單獨的 Cluster IP 不具備 TCP/IP 通信的基礎,并且它們屬于 k8s 集群這樣一個封閉的空間,集群外的節點如果要訪問這個通信端口,則需要做一些額外的工作
- 在 k8s 集群內,Node IP 網、Pod IP 網與 Cluster IP 網之間的通信,采用的是 k8s 自己設計的一種編程方式的特殊路由規則,與我們熟知的IP路由有很大的不同
Service 的Cluster IP 屬于 k8s 集群內部的地址,無法在集群外部直接使用這個地址。 那用戶怎么訪問 Web 端服務呢?
采用 NodePort 是解決上述問題的最直接、有效的常見做法。如 http://:port/。NodePort 的實現方式是在 k8s 集群里的每個Node上都為需要外部訪問的 Service 開啟一個對應的 TCP 監聽端口,外部系統只要用任意一個 Node 的 IP 地址+具體的 NodePort 端口號即可訪問此服務。
但 NodePort 還沒有完全解決外部訪問 Service 的所有問題,比如負載均衡問題。假如在我們的集群中有10個 Node,則此時最好有一個負載均衡器,外部的請求只需訪問此負載均衡器的 IP 地址,由負載均衡器負責轉發流量到后面某個 Node 的 NodePort 上。
Load balancer 組件獨立于 k8s 集群之外,通常是一個硬件的負載均衡器,或者是以軟件方式實現的,例如 HAProxy 或者 Nginx。對于每個Service,我們通常需要配置一個對應的 Load balancer 實例來轉發流量到后端的 Node 上,這的確增加了工作量及出錯的概率。于是 k8s 提供了自動化的解決方案,只要把 Service 的 type=NodePort 改為 type=LoadBalancer,k8s 就會自動創建一個對應的 Load balancer 實例并返回它的 IP 地址供外部客戶端使用。
Job
批處理任務通常并行(或者串行)啟動多個計算進程去處理一批工作項(work item)?,在處理完成后,整個批處理任務結束。
k8s 1.2版本開始,支持批處理類型的應用,我們可以通過 k8s Job 這種新的資源對象定義并啟動一個批處理任務 Job。與RC、Deployment、ReplicaSet、DaemonSet類似,Job 也控制一組Pod容器。
與 RC、Deployment、ReplicaSet、DaemonSet 類似,Job 也控制一組 Pod 容器。Job 也是一種特殊的 Pod 副本自動控制器,同時 Job 控制 Pod 副本與 RC 等控制器的工作機制有差別:
- Job 所控制的 Pod 副本是短暫運行的,可以將其視為一組 Docker 容器,其中的每個 Docker 容器都僅僅運行一次。當Job控制的所有Pod副本都運行結束時,對應的 Job 也就結束了。Job 在實現方式上與 RC 等副本控制器不同,Job 生成的 Pod 副本是不能自動重啟的,對應Pod 副本的 RestartPoliy 都被設置為 Never
- k8s 在1.5版本之后又提供了類似 crontab 的定時任務——CronJob,解決了某些批處理任務需要定時反復執行的問題
- Job 所控制的 Pod 副本的工作模式能夠多實例并行計算,以 TensorFlow 框架為例,可以將一個機器學習的計算任務分布到10臺機器上,在每臺機器上都運行一個 worker 執行計算任務,這很適合通過 Job 生成10個 Pod 副本同時啟動運算
Volume
Volume(存儲卷)是 Pod 中能夠被多個容器訪問的共享目錄。k8s 的 Volume 概念、用途和目的與 Docker 的 Volume 比較類似,但兩者不能等價。
- k8s 的 Volume 被定義在 Pod 上,被一個 Pod 里的多個容器掛載到具體 的文件目錄下
- k8s 的 Volume 與 Pod 的生命周期相同,但與容器的生命周期不想關,當容器終止或重啟時,Volume 的數據不會丟失
- 多種類型的 Volume:GlusterFS、Ceph等分布式文件系統
Persistent Volume
Volume 是被定義在 Pod 上的,屬于計算資源的一部分,而實際上,網絡存儲是相對獨立于計算資源而存在的一種實體資源。
在使用虛擬機的情況下,我們通常會先定義一個網絡存儲,然后從中劃出一個“網盤”并掛接到虛擬機上。Persistent Volume(PV)和與之相關聯的 Persistent Volume Claim(PVC)也起到了類似的作用。
PV 可以被理解成 k8s 集群中的某個網絡存儲對應的一塊存儲,它與 Volume 類似,但有以下區別:
- PV 只能是網絡存儲,不屬于任何 Node,但可以在每個 Node 的訪問
- PV 并不是被定義在 Pod 上,而是獨立于 Pod 之外定義的
- PV 目前支持的類型有:gcePersistentDisk、AWSElasticBlockStore、AzureFile、AzureDisk、NFS?、CephFS、Cinder、GlusterFS、HostPath(僅供單機測試)等等?。
如果某個 Pod 想申請某種類型的 PV,則首先需要定義一個 PersistentVolumeClaim 對象,然后,在Pod的Volume定義中引用上述PVC即可。PV 是有狀態的對象,它的狀態有以下幾種:
- Available:空閑狀態
- Bound:已經綁定到某個PVC上
- Released:對應的PVC已經被刪除,但資源還沒有被集群收回
- Failed:PV自動回收失敗。
Namespace
Namespace 是 k8s 一個非常重要的概念,它在很多情況下用于實現多租戶的資源隔離。Namespace 通過將集群內部的資源對象“分配”到不同的 Namespace 中,形成邏輯上分組的不同項目、小組或用戶組,便于不同的分組在共享使用整個集群的資源的同時還能被分別管理。
k8s 集群在啟動后會創建一個名為 default 的 Namespace,通過 kubectl 可以查看:
kubectl get namespaces
如果不特別指明 Namespace,則用戶創建的 Pod、RC、Service 都將被系統創建到這個默認的名為 default 的 Namespace 中。一旦創建了Namespace,我們在創建資源對象時就可以指定這個資源對象屬于哪個 Namespace。
如果 Pod 加入了某個 Namespace,通過 kubectl get命令無法顯示,這是因為如果不加參數,則 kubectl get 命令將僅顯示屬于 default 命名空間的資源對象。
可以在 kubectl 命令中加入–namespace參數來查看某個命名空間中的對象:
kubectl get pods --namespace=dev
當給每個租戶創建一個 Namespace 來實現多租戶的資源隔離時,還能結合 k8s 的資源配額管理,限定不同租戶能占用的資源,例如CPU 使用量、內存使用量等。
Configmap
先從Docker說起。我們知道,Docker通過將程序、依賴庫、數據及配置文件“打包固化”到一個不變的鏡像文件中的做法,解決了應用的部署的難題,但這同時帶來了棘手的問題,即配置文件中的參數在運行期如何修改的問題?。
我們不可能在啟動 Docker 容器后再修改容器里的配置文件,然后用新的配置文件重啟容器里的用戶主進程。為了解決這個問題,Docker 提供了兩種方式:
- 在運行時通過容器的環境變量來傳遞參數;
- 通過Docker Volume將容器外的配置文件映射到容器內。
這兩種方式都有其優勢和缺點,在大多數情況下,后一種方式更合適我們的系統,因為大多數應用通常從一個或多個配置文件中讀取參數。但這種方式也有明顯的缺陷:我們必須在目標主機上先創建好對應的配置文件,然后才能映射到容器里。
上述缺陷在分布式情況下變得更為嚴重,因為無論采用哪種方式,寫入(修改)多臺服務器上的某個指定文件,并確保這些文件保持一致,都是一個很難完成的目標。此外,在大多數情況下,我們都希望能集中管理系統的配置參數,而不是管理一堆配置文件。針對上述問題,k8s 給出了實現:
- 首先,把所有的配置項都當作key-value字符串,當然 value 可以來自某個文本文件,比如配置項 password=123456、user=root、host=192.168.8.4 用于表示連接 FTP 服務器的配置參數。這些配置項可以作為 Map 表中的一個項,整個 Map 的數據可以被持久化存儲在k8s 的 Etcd 數據庫中,然后提供 API 以方便 k8s 相關組件或客戶應用 CRUD 操作這些數據,上述專門用來保存配置參數的 Map 就是k8s ConfigMap資源對象。
- 接下來,k8s 提供了一種內建機制,將存儲在 etcd 中的 ConfigMap 通過 Volume 映射的方式變成目標 Pod 內的配置文件,不管目標 Pod被調度到哪臺服務器上,都會完成自動映射。進一步地,如果 ConfigMap 中的 key-value 數據被修改,則映射到 Pod 中的“配置文件”也會隨之自動更新。所以,k8s ConfigMap 就成了分布式系統中最為簡單(使用方法簡單,但背后實現比較復雜)且對應用無侵入的配置中心。