Docker 本身非常適合管理單個容器。但隨着您開始使用越來越多的容器和容器化應用程序,並把它們划分成數百個部分,很可能會導致管理和編排變得非常困難。最終,您需要后退一步,對容器實施分組,以便跨所有容器提供網絡、安全、遙測等服務。於是,Kubernetes 應運而生。
一、簡介
Kubernetes,又稱為 k8s(首字母為 k、首字母與尾字母之間有 8 個字符、尾字母為 s,所以簡稱 k8s)或者簡稱為 “kube” ,是一種可自動實施 Linux 容器操作的開源平台。它可以幫助用戶省去應用容器化過程的許多手動部署和擴展操作。也就是說,您可以將運行 Linux 容器的多組主機聚集在一起,由 Kubernetes 幫助您輕松高效地管理這些集群。而且,這些集群可跨公共雲、私有雲或混合雲部署主機。因此,對於要求快速擴展的雲原生應用而言(例如借助 Apache Kafka 進行的實時數據流處理),Kubernetes 是理想的托管平台。
Kubernetes 最初由 Google 的工程師基於 go 語言開發和設計出來並於2014年6月開源。Google 是最早研發 Linux 容器技術的企業之一,曾公開分享介紹 Google 如何將一切都運行於容器之中(這是 Google 雲服務背后的技術)。Google 每周會啟用超過 20 億個容器——全都由內部平台 Borg 支撐。Borg 是 Kubernetes 的前身,多年來開發 Borg 的經驗教訓成了影響 Kubernetes 中許多技術的主要因素。
趣聞:Kubernetes 徽標的七個輪輻代表着項目最初的名稱“九之七項目”(Project Seven of Nine)。
紅帽是第一批與 Google 合作研發 Kubernetes 的公司之一,作為 Kubernetes 上游項目的第二大貢獻者,我們甚至在這個項目啟動之前就已參與其中。2015 年,Google 將 Kubernetes 項目捐贈給了新成立的雲原生計算基金會。
為什么用k8s
docker很難在不同服務器之間互聯。
真正的生產型應用會涉及多個容器。這些容器必須跨多個服務器主機進行部署。Kubernetes 可以提供所需的編排和管理功能,以便您針對這些工作負載大規模部署容器。借助 Kubernetes 編排功能,您可以構建跨多個容器的應用服務、跨集群調度、擴展這些容器,並長期持續管理這些容器的健康狀況。
Kubernetes 還需要與聯網、存儲、安全性、遙測和其他服務集成整合,以提供全面的容器基礎架構。

當然,這取決於您如何在您的環境中使用容器。Linux 容器中的基本應用將它們視作高效、快速的虛擬機。一旦把它部署到生產環境或擴展為多個應用,您顯然需要許多托管在相同位置的容器來協同提供各種服務。隨着這些容器的累積,您運行環境中容器的數量會急劇增加,復雜度也隨之增長。
Kubernetes 通過將容器分類組成 “容器集” (pod),解決了容器增殖帶來的許多常見問題容器集為分組容器增加了一個抽象層,可幫助您調用工作負載,並為這些容器提供所需的聯網和存儲等服務。Kubernetes 的其它部分可幫助您在這些容器集之間達成負載平衡,同時確保運行正確數量的容器,充分支持您的工作負載。
容器分組:pod,pod下一組多個docker容器,最小操作單元。
如果能正確實施 Kubernetes,再輔以其它開源項目(例如 Atomic 注冊表、Open vSwitch、heapster、OAuth 以及 SELinux),您就能夠輕松編排容器基礎架構的各個部分。
用途
在您生產環境中使用 Kubernetes 的主要優勢在於,它提供了一個便捷有效的平台,讓您可以在物理機和虛擬機集群上調度和運行容器。更廣泛一點說,它可以幫助您在生產環境中,完全實施並依托基於容器的基礎架構運營。由於 Kubernetes 的實質在於實現操作任務自動化,所以您可以將其它應用平台或管理系統分配給您的許多相同任務交給容器來執行。
利用 Kubernetes,您能夠達成以下目標:
- l 跨多台主機進行容器編排。
- l 更加充分地利用硬件,最大程度獲取運行企業應用所需的資源。
- l 有效管控應用部署和更新,並實現自動化操作。
- l 掛載和增加存儲,用於運行有狀態的應用。
- l 快速、按需擴展容器化應用及其資源。
- l 對服務進行聲明式管理,保證所部署的應用始終按照部署的方式運行。
- l 利用自動布局、自動重啟、自動復制以及自動擴展功能,對應用實施狀況檢查和自我修復。
但是,Kubernetes 需要依賴其它項目來全面提供這些經過編排的服務。因此,借助其它開源項目可以幫助您將 Kubernetes 的全部功用發揮出來。這些功能包括:
- 注冊表,通過 Atomic 注冊表或 Docker 注冊表等項目實現。
- 聯網,通過 OpenvSwitch 和智能邊緣路由等項目實現。
- 遙測,通過 heapster、kibana、hawkular 和 elastic 等項目實現。
- 安全性,通過 LDAP、SELinux、RBAC 和 OAUTH 等項目以及多租戶層來實現。
- 自動化,參照 Ansible 手冊進行安裝和集群生命周期管理。
- 服務,可通過自帶預建版常用應用模式的豐富內容目錄來提供。
二、核心概念

Kubernetes 有各類資源對象來描述整個集群的運行狀態(Node、Pod、Replication Controller、Service等都可以看作一種“資源對象”)。這些對象都需要通過調用 kubernetes api 來進行創建、修改、刪除並將其保存在etcd中持久化存儲,可以通過 kubectl 命令工具,也可以直接調用 k8s api,或者使用對象語言的客戶端庫(例如:golang , pythion )。
從這個角度來看,Kubernetes其實是一個高度自動化的資源控制系統,它通過跟蹤對比etcd庫里保存的“資源期望狀態”與當前環境中的“實際資源狀態”的差異來實現自動控制和自動糾錯的高級功能。
每個 kubernetes 對象都會包含兩個關鍵字段:Object Spec 和 Object Status。spec 描述了對象所期望達到的狀態,status 描述了該對象的實際狀態。

2.1 Master
Kubernetes里的Master指的是集群控制節點,每個Kubernetes集群里需要有一個Master節點來負責整個集群的管理和控制,基本上Kubernetes的所有控制命令都發給它,它來負責具體的執行過程,我們后面執行的所有命令基本都是在Master節點上運行的。Master節點通常會占據一個獨立的服務器(高可用部署建議用3台服務器),其主要原因是它太重要了,是整個集群的“首腦”,如果宕機或者不可用,那么對集群內容器應用的管理都將失效。
Master節點上運行着以下一組關鍵進程:
- Kubernetes API Server (kube-apiserver):提供了HTTP Rest接口的關鍵服務進程,是Kubernetes里所有資源的增、刪、改、查等操作的唯一入口,也是集群控制的入口進程。
- Kubernetes Controller Manager (kube-controller-manager):Kubernetes里所有資源對象的自動化控制中心,可以理解為資源對象的“大總管”。
- Kubernetes Scheduler (kube-scheduler):負責資源調度(Pod調度)的進程,相當於公交公司的“調度室”。
另外,在Master節點上還需要啟動一個etcd服務,因為Kubernetes里的所有資源對象的數據全部是保存在etcd中的。

比如,你在 bash中輸入
[root@node01 ~]# kubectl get service
k8s的API Server會對其處理,實際調用了k8s的api,k8s操作的唯一入口
kube-controller-manager負責RC機制,當docker容器出錯關閉后,會自動開啟新的
資源調度器和管家聯系在一起,管家查看etcd中的數據,調用資源調度器去進行資源調度,看哪些工作節點符合要求,下發命令給節點
2.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引擎,負責本機的容器創建和管理工作。
fluentd
Node節點可以在運行期間動態增加到Kubernetes集群中,前提是這個節點上已經正確安裝、配置和啟動了上述關鍵進程,在默認情況下kubelet會向Master注冊自己,這也是Kubernetes推薦的Node管理方式。一旦Node被納入集群管理范圍,kubelet進程就會定時向Master節點匯報自身的情報,例如操作系統、Docker版本、機器的CPU和內存情況,以及當前有哪些Pod在運行等,存在etcd中,這樣Master可以獲知每個Node的資源使用情況,並實現高效均衡等資源調度策略。而某個Node超過指定時間不上報信息時,會被Master判斷為“失聯”,Node的狀態被標記為不可用(Not Ready),隨后Master會觸發“工作負載大轉移”的自動流程。
比如,要運行三台nginx,有一台掛了,該台的所有信息都已存在etcd當中了,controller manager會根據rc機制重啟一台,讓調度器去找可用的資源進行重啟。
2.3. Pod
Pod是Kubernetes的最重要也最基本的概念,如下圖所示是Pod的組成示意圖,我們看到每個Pod都有一個特殊的被成為“根容器”的Pause容器。Pause容器對應的鏡像屬於Kubernetes平台的一部分,除了Pause容器,每個Pod還包含一個或多個緊密相關的用戶業務容器。
Pause是打不死的小強,不運行業務,Pod會檢查該容器有沒有掛

Pod的組成
為什么Kubernetes會設計出一個全新的Pod概念並且Pod有這樣特殊的組成結構?
原因之一:在一組容器作為一個單元的情況下,我們難以對“整體”簡單地進行判斷及有效地進行行動。比如,一個容器死亡了,此時算是整體死亡么?引入業務無關並且不易死亡的Pause容器作為Pod的根容器,以它的狀態代表整體容器組的狀態,就簡單、巧妙地解決了這個難題。
原因之二:Pod里的多個業務容器共享Pause容器的IP,共享Pause容器掛接的Volume,這樣既簡化了密切關聯的業務容器之間的通信問題,也很好地解決了它們之間的文件共享問題。
Kubernetes為每個Pod都分配了唯一的IP地址,稱之為Pod IP,一個Pod里的多個容器共享Pod IP地址。Kubernetes要求底層網絡支持集群內任意兩個Pod之間的TCP/IP直接通信,這通常采用虛擬而層網絡技術來實現,例如Flannel、Open vSwitch等,因此我們需要牢記一點:在Kubernetes里,一個Pod里的容器與另外主機上的Pod容器能夠直接通信。
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重新調度到其他節點上。Pod、容器與Node的關系圖如下圖所示。
Pod、容器與Node的關系
2.4. Label(標簽)
Label是Kubernetes系統中另外一個核心概念。一個Label是一個key=value的鍵值對,其中key與vaue由用戶自己指定。Label可以附加到各種資源對象上,例如Node、Pod、Service、RC等,一個資源對象可以定義任意數量的Label,同一個Label也可以被添加到任意數量的資源對象上去,Label通常在資源對象定義時確定,也可以在對象創建后動態添加或者刪除。
我們可以通過指定的資源對象捆綁一個或多個不同的Label來實現多維度的資源分組管理功能,以便於靈活、方便地進行資源分配、調度、配置、部署等管理工作。例如:部署不同版本的應用到不同的環境中;或者監控和分析應用(日志記錄、監控、告警)等。一些常用等label示例如下。
- 版本標簽:"release" : "stable" , "release" : "canary"...
- 環境標簽:"environment" : "dev" , "environment" : "production"
- 架構標簽:"tier" : "frontend" , "tier" : "backend" , "tier" : "middleware"
- 分區標簽:"partition" : "customerA" , "partition" : "customerB"...
- 質量管控標簽:"track" : "daily" , "track" : "weekly"
Label相當於我們熟悉的“標簽”,給某個資源對象定義一個Label,就相當於給它打了一個標簽,隨后可以通過Label Selector(標簽選擇器)查詢和篩選擁有某些Label的資源對象,Kubernetes通過這種方式實現了類似SQL的簡單又通用的對象查詢機制。
mysql打個標簽,mycat打個標簽,放在一個服務器上跑。
2.5. Replication Controller
RC是Kubernetes系統中的核心概念之一,簡單來說,它其實是定義了一個期望的場景,即聲明某種Pod的副本數量在任意時刻都符合某個預期值,所以RC的定義包括如下幾個部分。
- Pod期待的副本數(replicas)。
- 用於篩選目標Pod的Label Selector。
- 當Pod的副本數量小於預期數量時,用於創建新Pod的Pod模版(template)。
2.6. Deployment
Deployment是Kubernetes v1.2引入的概念,引入的目的是為了更好地解決Pod的編排問題。為此,Deployment在內部使用了Replica Set來實現目的,無論從Deployment的作用與目的,它的YAML定義,還是從它的具體命令行操作來看,我們都可以把它看作RC的一次升級,兩者相似度超過90%。
Deployment相對於RC的一個最大升級是我們隨時知道當前Pod“部署”的進度。實際上由於一個Pod的創建、調度、綁定節點及在目標Node上啟動對應的容器這一完整過程需要一定的時間,所以我們期待系統啟動N個Pod副本的目標狀態,實際上是一個連續變化的“部署過程”導致的最終狀態。
Deployment的典型使用場景有以下幾個。
- 創建一個Deployment對象來生成對應的Replica Set並完成Pod副本的創建過程。
- 檢查Deployment的狀態來看部署動作是否完成(Pod副本的數量是否達到預期的值)。
- 更新Deployment以創建新的Pod(比如鏡像升級)。
- 如果當前Deployment不穩定,則回滾到一個早先的Deployment版本。
- 暫停Deployment以便於一次性修改多個PodTemplateSpec的配置項,之后再恢復Deployment,進行新的發布。
- 擴展Deployment以應對高負載。
- 查看Deployment的狀態,以此作為發布是否成功的指標。
- 清理不再需要的舊版本ReplicaSets。
2.7. StatefulSet
在Kubernetes系統中,Pod的管理對象RC、Deployment、DaemonSet和Job都是面向無狀態的服務。但現實中有很多服務是有狀態的,特別是一些復雜的中間件集群,例如MySQL集群、MongoDB集群、Kafka集群、Zookeeper集群等,這些應用集群有以下一些共同點。
每個節點都有固定的身份ID,通過這個ID,集群中的成員可以相互發現並且通信。
集群的規模是比較固定的,集群規模不能隨意變動。
集群里的每個節點都是有狀態的,通常會持久化數據到永久存儲中。
如果磁盤損壞,則集群里的某個節點無法正常運行,集群功能受損。
如果用RC/Deployment控制Pod副本數的方式來實現上述有狀態的集群,則我們會發現第一點是無法滿足的,因為Pod的名字是隨機產生的,Pod的IP地址也是在運行期才確定且可能有變動的,我們事先無法為每個Pod確定唯一不變的ID,為了能夠在其他節點上恢復某個失敗的節點,這種集群中的Pod需要掛接某種共享存儲,為了解決這個問題,Kubernetes從v1.4版本開始引入了PetSet這個新的資源對象,並且在v1.5版本時更名為StatefulSet,StatefulSet從本質上來說,可以看作Deployment/RC的一個特殊變種,它有如下一些特性。
StatefulSet里的每個Pod都有穩定、唯一的網絡標識,可以用來發現集群內的其他成員。假設StatefulSet的名字叫kafka,那么第一個Pod叫kafak-0,第二個Pod叫kafak-1,以此類推。
StatefulSet控制的Pod副本的啟停順序是受控的,操作第n個Pod時,前n-1個Pod已經時運行且准備好的狀態。
StatefulSet里的Pod采用穩定的持久化存儲卷,通過PV/PVC來實現,刪除Pod時默認不會刪除與StatefulSet相關的存儲卷(為了保證數據的安全)。
StatefulSet除了要與PV卷捆綁使用以存儲Pod的狀態數據,還要與Headless Service配合使用,即在每個StatefulSet的定義中要聲明它屬於哪個Headless Service。Headless Service與普通Service的關鍵區別在於,它沒有Cluster IP,如果解析Headless Service的DNS域名,則返回的是該Service對應的全部Pod的Endpoint列表。StatefulSet在Headless Service的基礎上又為StatefulSet控制的每個Pod實例創建了一個DNS域名,這個域名的格式為:
$(podname).$(headless service name)
比如一個3節點的Kafka的StatefulSet集群,對應的Headless Service的名字為kafka,StatefulSet的名字為kafka,則StatefulSet里面的3個Pod的DNS名稱分別為kafka-0.kafka、kafka-1.kafka、kafka-3.kafka,這些DNS名稱可以直接在集群的配置文件中固定下來。
2.8. Service(服務)
Service也是Kubernetes里的最核心的資源對象之一,Kubernetes里的每個Service其實就是我們經常提起的微服務架構中的一個“微服務”,之前我們所說的Pod、RC等資源對象其實都是為這節所說的“服務”------Kubernetes Service作“嫁衣”的。下圖顯示了Pod、RC與Service的邏輯關系。
Pod、RC與Service的關系
從圖中我們看到,Kubernetes的Service定義了一個服務的訪問入口地址,前端的應用(Pod)通過這個入口地址訪問其背后的一組由Pod副本組成的集群實例,Service與其后端Pod副本集群之間則是通過Label Selector來實現“無縫對接”的。而RC的作用實際上是保證Service的服務能力和服務質量始終處於預期的標准。
2.9. Volume(存儲卷)
Volume是Pod中能夠被多個容器訪問的共享目錄。Kubernetes的Volume概念、用途和目的與Docker的Volume比較類似,但兩者不能等價。首先,Kubernetes中的Volume定義在Pod上,然后被一個Pod里的多個容器掛載到具體的文件目錄下;其次,Kubernetes中的Volume中的數據也不會丟失。最后,Kubernetes支持多種類型的Volume,例如Gluster、Ceph等先進的分布式文件系統。
2.10. Namespace
Namespace(命名空間)是Kubernetes系統中的另一個非常重要的概念,Namespace在很多情況下用於實現多租戶的資源隔離。Nameaspace通過將集群內部的資源對象“分配”到不同的Namespce中,形成邏輯上分組的不同項目、小組或用戶組,便於不同的分組在共享使用整個集群的資源的同時還能被分別管理。
2.11. Annotation(注解)
Annotation與Label類似,也使用key/value鍵值對的形式進行定義。不同的是Label具有嚴格的命名規則,它定義的是Kubernetes對象的元數據(Metadata),並且用於Label Selector。而Annotation則是用戶任意定義的“附加”信息,以便於外部工具進行查找,很多時候,Kubernetes的模塊自身會通過Annotation的方式標記資源對象的特殊信息。
通常來說,用Annotation來記錄的信息如下。
- build信息、release信息、Docker鏡像信息等,例如時間戳、release id號、PR號、鏡像hash值、docker registry地址等。
- 日志庫、監控庫、分析庫等資源庫的地址信息。
- 程序調試工具信息,例如工具、版本號等。
- 團隊等聯系信息,例如電話號碼、負責人名稱、網址等。
2.12. 其他
Kubelet
運行在節點上的服務,可讀取容器清單(container manifest),確保指定的容器啟動並運行。
kubectl
Kubernetes 的命令行配置工具。
上述組件是Kubernetes系統的核心組件,它們共同構成了Kubernetes系統的框架和計算模型。通過對它們進行靈活組合,用戶就可以快速、方便地對容器集群進行配置、創建和管理。除了本章所介紹的核心組件,在Kubernetes系統中還有許多輔助配置的資源對象,例如LimitRange、Resurce。另外,一些系統內部使用的對象Binding、Event等請參考Kubernetes的API文檔。
三、術語
官方把 kubernetes 術語分為 12 個分類:
系統結構、社區、核心對象、擴展、基礎、網絡、操作、安全、存儲、工具、用戶類型、工作負載
由於 k8s 的術語實在太多了,想要全部記住作為新學者還是有點壓力,所以我們課程中只講解一些常用的術語。想要了解更多 kubernetes 術語,請參照官方術語表。
地址:https://kubernetes.io/docs/reference/glossary/?fundamental=true
Pods
在Kubernetes中,最小的管理元素不是一個個獨立的容器,而是Pod,Pod是最小的,管理,創建,計划的最小單元。
Labels
標簽其實就一對 key/value ,被關聯到對象上,比如Pod;標簽的使用我們傾向於能夠標示對象的特殊特點,並且對用戶而言是有意義的(就是一眼就看出了這個Pod是數據庫),但是標簽對內核系統是沒有直接意義的。標簽可以用來划分特定組的對象(比如,所有女的),標簽可以在創建一個對象的時候直接給與,也可以在后期隨時修改,每一個對象可以擁有多個標簽,但是,key值必須是唯一的
"labels": {
"key1" : "value1",
"key2" : "value2"
}
Namespace
Namespace 是對一組資源和對象的抽象集合,比如可以用來將系統內部的對象划分為不同的項目組或用戶組。常見的pods, services, replication controllers和deployments等都是屬於某一個namespace的(默認是default),而node, persistentVolumes等則不屬於任何namespace。
Namespace常用來隔離不同的用戶,比如Kubernetes自帶的服務一般運行在kube-system namespace中。
Replication Controller
Replication Controller 保證了在所有時間內,都有特定數量的Pod副本正在運行,如果太多了,Replication Controller就殺死幾個,如果太少了,Replication Controller會新建幾個,和直接創建的pod不同的是,Replication Controller會替換掉那些刪除的或者被終止的pod,不管刪除的原因是什么(維護阿,更新啊,Replication Controller都不關心)。基於這個理由,我們建議即使是只創建一個pod,我們也要使用Replication Controller。Replication Controller 就像一個進程管理器,監管着不同node上的多個pod,而不是單單監控一個node上的pod,Replication Controller 會委派本地容器來啟動一些節點上服務(Kubelet ,Docker)。
Node
Node是Pod真正運行的主機,可以物理機,也可以是虛擬機。為了管理Pod,每個Node節點上至少要運行container runtime(比如docker或者rkt)、kubelet和kube-proxy服務。
ReplicaSets
ReplicaSet是下一代復本控制器。ReplicaSet和 Replication Controller之間的唯一區別是現在的選擇器支持。Replication Controller只支持基於等式的selector(env=dev或environment!=qa),但ReplicaSet還支持新的,基於集合的selector(version in (v1.0, v2.0)或env notin (dev, qa))。在試用時官方推薦ReplicaSet。
Services
Kubernetes Pod是平凡的,它門會被創建,也會死掉(生老病死),並且他們是不可復活的。 ReplicationControllers動態的創建和銷毀Pods(比如規模擴大或者縮小,或者執行動態更新)。每個pod都由自己的ip,這些IP也隨着時間的變化也不能持續依賴。這樣就引發了一個問題:如果一些Pods(讓我們叫它作后台,后端)提供了一些功能供其它的Pod使用(讓我們叫作前台),在kubernete集群中是如何實現讓這些前台能夠持續的追蹤到這些后台的?
答案是:Service
Kubernete Service 是一個定義了一組Pod的策略的抽象,我們也有時候叫做宏觀服務。這些被服務標記的Pod都是(一般)通過label Selector決定的(下面我們會講到我們為什么需要一個沒有label selector的服務)
舉個例子,我們假設后台是一個圖形處理的后台,並且由3個副本。這些副本是可以相互替代的,並且前台並需要關心使用的哪一個后台Pod,當這個承載前台請求的pod發生變化時,前台並不需要直到這些變化,或者追蹤后台的這些副本,服務是這些去耦
對於Kubernete原生的應用,Kubernete提供了一個簡單的Endpoints API,這個Endpoints api的作用就是當一個服務中的pod發生變化時,Endpoints API隨之變化,對於哪些不是原生的程序,Kubernetes提供了一個基於虛擬IP的網橋的服務,這個服務會將請求轉發到對應的后台pod。
Volumes
容器中的磁盤的生命周期是短暫的,這就帶來了一系列的問題,第一,當一個容器損壞之后,kubelet 會重啟這個容器,但是文件會丟失-這個容器會是一個全新的狀態,第二,當很多容器在同一Pod中運行的時候,很多時候需要數據文件的共享。Kubernete Volume解決了這個問題。
PV/PVC/StorageClass
PersistentVolume(PV)是集群中已由管理員配置的一段網絡存儲。 集群中的資源就像一個節點是一個集群資源。 PV是諸如卷之類的卷插件,但是具有獨立於使用PV的任何單個pod的生命周期。 該API對象捕獲存儲的實現細節,即NFS,iSCSI或雲提供商特定的存儲系統。
PersistentVolumeClaim(PVC)是用戶存儲的請求。 它類似於pod。 Pod消耗節點資源,PVC消耗光伏資源。 莢可以請求特定級別的資源(CPU和內存)。 權利要求可以請求特定的大小和訪問模式(例如,可以一旦讀/寫或只讀許多次安裝)。
雖然PersistentVolumeClaims允許用戶使用抽象存儲資源,但是常見的是,用戶需要具有不同屬性(如性能)的PersistentVolumes,用於不同的問題。 群集管理員需要能夠提供多種不同於PersistentVolumes的PersistentVolumes,而不僅僅是大小和訪問模式,而不會使用戶了解這些卷的實現細節。 對於這些需求,存在StorageClass資源。
StorageClass為管理員提供了一種描述他們提供的存儲的“類”的方法。 不同的類可能映射到服務質量級別,或備份策略,或者由群集管理員確定的任意策略。 Kubernetes本身對於什么類別代表是不言而喻的。 這個概念有時在其他存儲系統中稱為“配置文件”
例子
https://kubernetes.io/docs/user-guide/persistent-volumes/walkthrough/
Deployment
Deployment為Pod和ReplicaSet提供了一個聲明式定義(declarative)方法,用來替代以前的ReplicationController來方便的管理應用。典型的應用場景包括:
l 定義Deployment來創建Pod和ReplicaSet
l 滾動升級和回滾應用
l 擴容和縮容
l 暫停和繼續Deployment
比如一個簡單的nginx應用可以定義為:
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80
擴容:
kubectl scale deployment nginx-deployment --replicas 10
如果集群支持 horizontal pod autoscaling 的話,還可以為Deployment設置自動擴展:
kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80
更新鏡像也比較簡單:
kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
回滾:
kubectl rollout undo deployment/nginx-deployment
Secret
Secret解決了密碼、token、密鑰等敏感數據的配置問題,而不需要把這些敏感數據暴露到鏡像或者Pod Spec中。Secret可以以Volume或者環境變量的方式使用。
Secret有三種類型:
l Service Account:用來訪問Kubernetes API,由Kubernetes自動創建,並且會自動掛載到Pod的/run/secrets/kubernetes.io/serviceaccount目錄中;
l Opaque:base64編碼格式的Secret,用來存儲密碼、密鑰等;
l kubernetes.io/dockerconfigjson:用來存儲私有docker registry的認證信息。
StatefulSet
StatefulSet是為了解決有狀態服務的問題(對應Deployments和ReplicaSets是為無狀態服務而設計),其應用場景包括:
l 穩定的持久化存儲,即Pod重新調度后還是能訪問到相同的持久化數據,基於PVC來實現
l 穩定的網絡標志,即Pod重新調度后其PodName和HostName不變,基於Headless Service(即沒有Cluster IP的Service)來實現
l 有序部署,有序擴展,即Pod是有順序的,在部署或者擴展的時候要依據定義的順序依次依次進行(即從0到N-1,在下一個Pod運行之前所有之前的Pod必須都是Running和Ready狀態),基於init containers來實現
l 有序收縮,有序刪除(即從N-1到0)
從上面的應用場景可以發現,StatefulSet由以下幾個部分組成:
l 用於定義網絡標志(DNS domain)的Headless Service
l 用於創建PersistentVolumes的volumeClaimTemplates
l 定義具體應用的StatefulSet
DaemonSet
DaemonSet保證在每個Node上都運行一個容器副本,常用來部署一些集群的日志、監控或者其他系統管理應用。典型的應用包括:
l 日志收集,比如fluentd,logstash等
l 系統監控,比如Prometheus Node Exporter,collectd,New Relic agent,Ganglia gmond等
l 系統程序,比如kube-proxy, kube-dns, glusterd, ceph等
Service Account
Service account是為了方便Pod里面的進程調用Kubernetes API或其他外部服務而設計的。
CronJob
CronJob即定時任務,就類似於Linux系統的crontab,在指定的時間周期運行指定的任務。在Kubernetes 1.5,使用CronJob需要開啟batch/v2alpha1 API,即–runtime-config=batch/v2alpha1。
Job
Job負責批量處理短暫的一次性任務 (short lived one-off tasks),即僅執行一次的任務,它保證批處理任務的一個或多個Pod成功結束。
Kubernetes支持以下幾種Job:
l 非並行Job:通常創建一個Pod直至其成功結束
l 固定結束次數的Job:設置.spec.completions,創建多個Pod,直到.spec.completions個Pod成功結束
l 帶有工作隊列的並行Job:設置.spec.Parallelism但不設置.spec.completions,當所有Pod結束並且至少一個成功時,Job就認為是成功
Security Context 和 PSP
Security Context的目的是限制不可信容器的行為,保護系統和其他容器不受其影響。
Kubernetes提供了三種配置Security Context的方法:
l Container-level Security Context:僅應用到指定的容器
l Pod-level Security Context:應用到Pod內所有容器以及Volume
l Pod Security Policies(PSP):應用到集群內部所有Pod以及Volume
Pod Security Policies(PSP)是集群級的Pod安全策略,自動為集群內的Pod和Volume設置Security Context。
使用PSP需要API Server開啟extensions/v1beta1/podsecuritypolicy,並且配置PodSecurityPolicyadmission控制器。
Resource Quotas
資源配額(Resource Quotas)是用來限制用戶資源用量的一種機制。
它的工作原理為:
l 資源配額應用在Namespace上,並且每個Namespace最多只能有一個ResourceQuota對象
l 開啟計算資源配額后,創建容器時必須配置計算資源請求或限制(也可以用LimitRange設置默認值)
l 用戶超額后禁止創建新的資源
Network Policy
Network Policy提供了基於策略的網絡控制,用於隔離應用並減少攻擊面。它使用標簽選擇器模擬傳統的分段網絡,並通過策略控制它們之間的流量以及來自外部的流量。
在使用Network Policy之前,需要注意:
l apiserver開啟extensions/v1beta1/networkpolicies
l 網絡插件要支持Network Policy,如Calico、Romana、Weave Net和trireme等
Ingress
通常情況下,service和pod的IP僅可在集群內部訪問。集群外部的請求需要通過負載均衡轉發到service在Node上暴露的NodePort上,然后再由kube-proxy將其轉發給相關的Pod。
而Ingress就是為進入集群的請求提供路由規則的集合,如下圖所示

Ingress可以給service提供集群外部訪問的URL、負載均衡、SSL終止、HTTP路由等。為了配置這些Ingress規則,集群管理員需要部署一個Ingress controller,它監聽Ingress和service的變化,並根據規則配置負載均衡並提供訪問入口。
ThirdPartyResources
ThirdPartyResources是一種無需改變代碼就可以擴展Kubernetes API的機制,可以用來管理自定義對象。
ThirdPartyResources將在v1.7棄用
ThirdPartyResources將在v1.7棄用,並在未來版本中刪除。建議從v1.7開始,遷移到CustomResourceDefinition。
ConfigMap
ConfigMap用於保存配置數據的鍵值對,可以用來保存單個屬性,也可以用來保存配置文件。ConfigMap跟secret很類似,但它可以更方便地處理不包含敏感信息的字符串。
PodPreset
PodPreset用來給指定標簽的Pod注入額外的信息,如環境變量、存儲卷等。這樣,Pod模板就不需要為每個Pod都顯式設置重復的信息。
四、安裝
4.1 K8S 集群架構方案

Kubernetes 集群組件:
l etcd 一個高可用的K/V鍵值對存儲和服務發現系統
l flannel 實現誇主機的容器網絡的通信
l kube-apiserver 提供kubernetes集群的API調用
l kube-controller-manager 確保集群服務
l kube-scheduler 調度容器,分配到Node
l kubelet 在Node節點上按照配置文件中定義的容器規格啟動容器
l kube-proxy 提供網絡代理服務
Kubernetes 集群部署方案
如下是集群部署策略,1個master + 2個node。存儲集群etcd是單點集群(真實環境不推薦此做法,需要集群)。網絡使用的是flannel虛擬二次網絡。

Kubernetes具有完備的集群管理能力:
- 包括多層次的安全防護和准入機制
- 多租戶應用支撐能力
- 透明的服務注冊和服務發現機制
- 內建智能負載均衡器
- 強大的故障發現和自我修復能力
- 服務滾動升級和在線擴容能力
- 可擴展的資源自動調度機制
- 以及多粒度的資源管理能力
同時,kubernetes提供了完善的管理工具,這些工具涵蓋了包括開發、部署測試、運維監控在內的各個環節。
在kubernetes中,service(服務)是分布式集群架構的核心,一個service對象擁有如下關鍵特征:
- 擁有一個唯一指定的名字(比如mysql-service)。
- 擁有一個虛擬IP(Cluster IP、service IP或VIP)和端口號。
- 能夠提供某種遠程服務能力。
- 被映射到了提供這種服務能力的一組容器應用上。
Kubernetes.io開發了一個交互式教程,通過WEB瀏覽器就能使用預先部署好的一個Kubernetes集群,快速體驗kubernetes的功能和應用場景。
鏈接:https://kubernetes.io/docs/tutorials/kubernetes-basics/
K8s官方下載地址:https://github.com/kubernetes

環境准備
| 節點 |
ip 地址 |
操作系統 |
| master |
192.168.100.246 |
CentOS 7.3-x86_64 |
| node1 |
192.168.100.247 |
CentOS 7.3-x86_64 |
| node2 |
192.168.100.248 |
CentOS 7.3-x86_64 |
| harbor |
192.168.100.241 |
CentOS 7.3-x86_64 |
集群詳情
l OS:CentOS Linux release 7.3.1611 (Core) 3.10.0-514.el7.x86_64
l Kubernetes 1.6.0+(最低的版本要求是1.6)
l Docker:建議使用 Docker CE
l Etcd 3.3.10
l Flannel 0.7.1 vxlan或者host-gw 網絡
l TLS 認證通信 (所有組件,如 etcd、kubernetes master 和 node)
l RBAC 授權
l kubelet TLS BootStrapping
l kubedns、dashboard、heapster(influxdb、grafana)、EFK(elasticsearch、fluentd、kibana) 集群插件
l 私有docker鏡像倉庫harbor(請自行部署,harbor提供離線安裝包,直接使用docker-compose啟動即可),不會的請參官文檔:
安裝文檔:https://github.com/goharbor/harbor/blob/master/docs/installation_guide.md
配置 https訪問:https://github.com/goharbor/harbor/blob/master/docs/configure_https.md
環境說明
在下面的步驟中,我們將在三台CentOS系統的物理機上部署具有三個節點的kubernetes1.12.3集群。
角色分配如下:
鏡像倉庫:192.168.100.241,為私有鏡像倉庫,請替換為公共倉庫或你自己的鏡像倉庫地址。
Master:192.168.100.246
Node:192.168.100.247、192.168.100.248
注意:192.168.100.246 這台主機 master 和 node 復用。所有生成證書、執行 kubectl 命令的操作都在這台節點上執行。一旦 node 加入到 kubernetes 集群之后就不需要再登陸node節點了。
4.2 提醒
本文檔介紹使用二進制部署最新 kubernetes v1.12.3 集群的所有步驟,而不是使用 kubeadm 等自動化方式來部署集群。
在部署的過程中,將詳細列出各組件的啟動參數,它們的含義和可能遇到的問題。
部署完成后,你將理解系統各組件的交互原理,進而能快速解決實際問題。
所以本文檔主要適合於那些有一定 kubernetes 基礎,想通過一步步部署的方式來學習和了解系統配置、運行原理的人。
提醒
- 本文檔適用於 CentOS 7.x、Ubuntu 18.x 及以上版本系統
- 由於啟用了 TLS 雙向認證、RBAC 授權等嚴格的安全機制,建議從頭開始部署,而不要從中間開始,否則可能會認證、授權等失敗!
- 部署過程中需要有很多證書的操作,請大家耐心操作,不明白的操作可以參考本書中的其他章節的解釋。
- 該部署操作僅是搭建成了一個可用 kubernetes 集群,而很多地方還需要進行優化,heapster 插件、EFK 插件不一定會用於真實的生產環境中,但是通過部署這些插件,可以讓大家了解到如何部署應用到集群上。
特別提醒
有同學沒有按照本文檔的k8s版本(1.12.3)安裝,自己升級了版本;有些地方k8s升級后官方默認功能做了調整,比如kube-apiserver中的1.14版本不支持Initializers插件等......;由於k8s升級和功能迭代快,所以同學們如果必要請不要自行升級版本,自行升級將可能導致參照文檔安裝提示報錯。
文檔需要根據個人環境修改的地方都用中划線 +【粗體 or 標紅】表示,大家看到這種信息的時候記得修改,務必不能使用文檔相同的信息。。。。。。
4.3 組件版本和配置策略
組件版本
l Kubernetes 1.12.3
l Docker 18.09.0-ce
l Etcd 3.3.10
l Flanneld 0.10.0
l 插件:
m Coredns
m Dashboard
m Heapster (influxdb、grafana)
m Metrics-Server
m EFK (elasticsearch、fluentd、kibana)
l 鏡像倉庫:
m docker registry
m harbor
主要配置策略
kube-apiserver:
l 使用節點本地 nginx 4 層透明代理實現高可用;
l 關閉非安全端口 8080 和匿名訪問;
l 在安全端口 6443 接收 https 請求;
l 嚴格的認證和授權策略 (x509、token、RBAC);
l 開啟 bootstrap token 認證,支持 kubelet TLS bootstrapping;
l 使用 https 訪問 kubelet、etcd,加密通信;
kube-controller-manager:
l 3 節點高可用;
l 關閉非安全端口,在安全端口 10252 接收 https 請求;
l 使用 kubeconfig 訪問 apiserver 的安全端口;
l 自動 approve kubelet 證書簽名請求 (CSR),證書過期后自動輪轉;
l 各 controller 使用自己的 ServiceAccount 訪問 apiserver;
kube-scheduler:
l 3 節點高可用;
l 使用 kubeconfig 訪問 apiserver 的安全端口;
kubelet:
l 使用 kubeadm 動態創建 bootstrap token,而不是在 apiserver 中靜態配置;
l 使用 TLS bootstrap 機制自動生成 client 和 server 證書,過期后自動輪轉;
l 在 KubeletConfiguration 類型的 JSON 文件配置主要參數;
l 關閉只讀端口,在安全端口 10250 接收 https 請求,對請求進行認證和授權,拒絕匿名訪問和非授權訪問;
l 使用 kubeconfig 訪問 apiserver 的安全端口;
kube-proxy:
l 使用 kubeconfig 訪問 apiserver 的安全端口;
l 在 KubeProxyConfiguration 類型的 JSON 文件配置主要參數;
l 使用 ipvs 代理模式;
集群插件:
l DNS:使用功能、性能更好的 coredns;
l Dashboard:支持登錄認證;
l Metric:heapster、metrics-server,使用 https 訪問 kubelet 安全端口;
l Log:Elasticsearch、Fluend、Kibana;
l Registry 鏡像庫:docker-registry、harbor;
harbor私有鏡像倉庫:參考:https://github.com/goharbor/harbor
4.4 系統初始化和全局變量
1. 集群機器
master:192.168.100.246
node1:192.168.100.247
node2:192.168.100.248
請提前在 VirtualBox 中安裝三台基於 centos 7 鏡像的虛擬機,本文檔中的 etcd 集群、master 節點、worker 節點均使用這三台機器。
注意:
- 需要在所有機器上執行本文檔的初始化命令;
- 需要使用具有 root 權限的賬號執行這些命令。
2. 主機名
設置永久主機名稱,然后重新登錄:
hostnamectl set-hostname master # 將 master 替換為當前主機名
l 設置的主機名保存在 /etc/hostname 文件中;
如果 DNS 不支持解析主機名稱,則需要修改每台機器的 /etc/hosts 文件,添加主機名和 IP 的對應關系:
cat >> /etc/hosts <<EOF 192.168.100.246 master 192.168.100.247 node1 192.168.100.248 node2 EOF
3. 添加 docker 賬戶
在每台機器上添加 docker 賬戶:
useradd -m docker
4. 無密碼 ssh 登錄其它節點
如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令,所以需要添加該節點到其它節點的 ssh 信任關系。
設置 master 可以無密碼登錄所有節點的 root 賬戶:
ssh-keygen -t rsa ssh-copy-id root@master ssh-copy-id root@node1 ssh-copy-id root@node2
5. 將可執行文件路徑 /opt/k8s/bin 添加到 PATH 變量中
在每台機器上添加環境變量:
echo 'PATH=/opt/k8s/bin:$PATH' >>/root/.bashrc source /root/.bashrc
6. 安裝依賴包
在每台機器上安裝依賴包:
CentOS:
yum install -y epel-release yum install -y conntrack ntpdate ntp ipvsadm ipset jq iptables curl sysstat libseccomp wget /usr/sbin/modprobe ip_vs
Ubuntu:
apt-get install -y conntrack ipvsadm ntp ipset jq iptables curl sysstat libseccomp /usr/sbin/modprobe ip_vs
l ipvs 依賴 ipset;
l ntp 保證各機器系統時間同步;
7. 關閉防火牆
在每台機器上關閉防火牆,清理防火牆規則,設置默認轉發策略:
systemctl stop firewalld systemctl disable firewalld iptables -F && iptables -X && iptables -F -t nat && iptables -X -t nat iptables -P FORWARD ACCEPT
8. 關閉 swap 分區
如果開啟了 swap 分區,kubelet 會啟動失敗(可以通過將參數 --fail-swap-on 設置為 false 來忽略 swap on),故需要在每台機器上關閉 swap 分區。同時注釋 /etc/fstab 中相應的條目,防止開機自動掛載 swap 分區:
swapoff -a sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
9. 關閉 SELinux
關閉 SELinux,否則后續 K8S 掛載目錄時可能報錯 Permission denied:
setenforce 0 sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
10. 關閉 dnsmasq(可選)
linux 系統開啟了 dnsmasq 后(如 GUI 環境),將系統 DNS Server 設置為 127.0.0.1,這會導致 docker 容器無法解析域名,需要關閉它:
systemctl stop dnsmasq
systemctl disable dnsmasq
11. 加載內核模塊
modprobe ip_vs_rr modprobe br_netfilter
12. 優化內核參數
這一步如果執行有一些報錯不用理會,繼續往下執行。
cat > kubernetes.conf <<EOF net.bridge.bridge-nf-call-iptables=1 net.bridge.bridge-nf-call-ip6tables=1 net.ipv4.ip_forward=1 net.ipv4.tcp_tw_recycle=0 vm.swappiness=0 # 禁止使用 swap 空間,只有當系統 OOM 時才允許使用它 vm.overcommit_memory=1 # 不檢查物理內存是否夠用 vm.panic_on_oom=0 # 開啟 OOM fs.inotify.max_user_instances=8192 fs.inotify.max_user_watches=1048576 fs.file-max=52706963 fs.nr_open=52706963 net.ipv6.conf.all.disable_ipv6=1 net.netfilter.nf_conntrack_max=2310720 EOF cp kubernetes.conf /etc/sysctl.d/kubernetes.conf sysctl -p /etc/sysctl.d/kubernetes.conf
l 必須關閉 tcp_tw_recycle,否則和 NAT 沖突,會導致服務不通;
l 關閉 IPV6,防止觸發 docker BUG;
13. 設置系統時區
# 調整系統 TimeZone timedatectl set-timezone Asia/Shanghai # 將當前的 UTC 時間寫入硬件時鍾 timedatectl set-local-rtc 0 # 重啟依賴於系統時間的服務 systemctl restart rsyslog systemctl restart crond
14. 更新系統時間(可選)
ntpdate cn.pool.ntp.org
15. 關閉無關的服務
systemctl stop postfix && systemctl disable postfix
16. 設置 rsyslogd 和 systemd journal
systemd 的 journald 是 Centos 7 缺省的日志記錄工具,它記錄了所有系統、內核、Service Unit 的日志。
相比 systemd,journald 記錄的日志有如下優勢:
- 可以記錄到內存或文件系統;(默認記錄到內存,對應的位置為 /run/log/jounal)
- 可以限制占用的磁盤空間、保證磁盤剩余空間;
- 可以限制日志文件大小、保存的時間;
journald 默認將日志轉發給 rsyslog,這會導致日志寫了多份,/var/log/messages 中包含了太多無關日志,不方便后續查看,同時也影響系統性能。
mkdir /var/log/journal # 持久化保存日志的目錄 mkdir /etc/systemd/journald.conf.d cat > /etc/systemd/journald.conf.d/99-prophet.conf <<EOF [Journal] # 持久化保存到磁盤 Storage=persistent # 壓縮歷史日志 Compress=yes SyncIntervalSec=5m RateLimitInterval=30s RateLimitBurst=1000 # 最大占用空間 10G SystemMaxUse=10G # 單日志文件最大 200M SystemMaxFileSize=200M # 日志保存時間 2 周 MaxRetentionSec=2week # 不將日志轉發到 syslog ForwardToSyslog=no EOF systemctl restart systemd-journald
17. 創建相關目錄
創建目錄:
mkdir -p /opt/k8s/{bin,work} /etc/{kubernetes,etcd}/cert
18. 升級內核(版本低執行該項)
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm # 安裝完成后檢查 /boot/grub2/grub.cfg 中對應內核 menuentry 中是否包含 initrd16 配置,如果沒有,再安裝一次! yum --enablerepo=elrepo-kernel install -y kernel-lt # 設置開機從新內核啟動 grub2-set-default 0 安裝內核源文件(可選,在升級完內核並重啟機器后執行): # yum erase kernel-headers yum --enablerepo=elrepo-kernel install kernel-lt-devel-$(uname -r) kernel-lt-headers-$(uname -r)
19. 關閉 NUMA(可選)
cp /etc/default/grub{,.bak} vim /etc/default/grub # 在 GRUB_CMDLINE_LINUX 一行添加 `numa=off` 參數,如下所示: diff /etc/default/grub.bak /etc/default/grub 6c6 < GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rhgb quiet" --- > GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rhgb quiet numa=off" 重新生成 grub2 配置文件: cp /boot/grub2/grub.cfg{,.bak} grub2-mkconfig -o /boot/grub2/grub.cfg
20. 檢查系統內核和模塊是否適合運行 docker (僅適用於 linux 系統)
curl https://raw.githubusercontent.com/docker/docker/master/contrib/check-config.sh > check-config.sh bash ./check-config.sh
21. 分發集群環境變量定義腳本(擴容時不需要執行該步驟)
后續的部署步驟將使用 environment.sh文件中定義的全局環境變量,請根據自己的機器、網絡情況修改:
#!/usr/bin/bash # 生成 EncryptionConfig 所需的加密 key export ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64) # 集群各機器 IP 數組 export NODE_IPS=(172.26.106.83 172.26.106.81 172.26.106.82) # 集群各 IP 對應的 主機名數組 export NODE_NAMES=(node01 node02 node03 ) # etcd 集群服務地址列表 export ETCD_ENDPOINTS="https://172.26.106.83:2379,https://172.26.106.81:2379,https://172.26.106.82:2379" # etcd 集群間通信的 IP 和端口 export ETCD_NODES="node01=https://172.26.106.83:2380,node02=https://172.26.106.81:2380,node03=https://172.26.106.82:2380" # kube-apiserver 的反向代理(kube-nginx)地址端口 # export KUBE_APISERVER="https://127.0.0.1:8443" export KUBE_APISERVER="https://172.26.106.83:8443" # 節點間互聯網絡接口名稱 export IFACE="eth0" # etcd 數據目錄 export ETCD_DATA_DIR="/data/k8s/etcd/data" # etcd WAL 目錄,建議是 SSD 磁盤分區,或者和 ETCD_DATA_DIR 不同的磁盤分區 export ETCD_WAL_DIR="/data/k8s/etcd/wal" # k8s 各組件數據目錄 export K8S_DIR="/data/k8s/k8s" # docker 數據目錄 export DOCKER_DIR="/data/k8s/docker" ## 以下參數一般不需要修改 # TLS Bootstrapping 使用的 Token,可以使用命令 head -c 16 /dev/urandom | od -An -t x | tr -d ' ' 生成 BOOTSTRAP_TOKEN="41f7e4ba8b7be874fcff18bf5cf41a7c" # 最好使用 當前未用的網段 來定義服務網段和 Pod 網段 # 服務網段,部署前路由不可達,部署后集群內路由可達(kube-proxy 保證) SERVICE_CIDR="10.254.0.0/16" # Pod 網段,建議 /16 段地址,部署前路由不可達,部署后集群內路由可達(flanneld 保證) CLUSTER_CIDR="172.30.0.0/16" # 服務端口范圍 (NodePort Range) export NODE_PORT_RANGE="30000-32767" # flanneld 網絡配置前綴 export FLANNEL_ETCD_PREFIX="/kubernetes/network" # kubernetes 服務 IP (一般是 SERVICE_CIDR 中第一個IP) export CLUSTER_KUBERNETES_SVC_IP="10.254.0.1" # 集群 DNS 服務 IP (從 SERVICE_CIDR 中預分配) export CLUSTER_DNS_SVC_IP="10.254.0.2" # 集群 DNS 域名(末尾不帶點號) export CLUSTER_DNS_DOMAIN="cluster.local" # 將二進制目錄 /opt/k8s/bin 加到 PATH 中 export PATH=/opt/k8s/bin:$PATH
其中IFACE要去查看網卡信息
[root@node03 ~]# ll /etc/sysconfig/network-scripts/ total 228 -rw-r--r-- 1 root root 38 Nov 29 2018 ifcfg-eth0 -rw-r--r-- 1 root root 254 Jan 3 2018 ifcfg-lo lrwxrwxrwx 1 root root 24 Nov 29 2018 ifdown -> ../../../usr/sbin/ifdown -rwxr-xr-x 1 root root 654 Jan 3 2018 ifdown-bnep -rwxr-xr-x 1 root root 6569 Jan 3 2018 ifdown-eth -rwxr-xr-x 1 root root 781 Jan 3 2018 ifdown-ippp -rwxr-xr-x 1 root root 4540 Jan 3 2018 ifdown-ipv6 lrwxrwxrwx 1 root root 11 Nov 29 2018 ifdown-isdn -> ifdown-ippp -rwxr-xr-x 1 root root 2130 Sep 27 2018 ifdown-post -rwxr-xr-x 1 root root 1068 Jan 3 2018 ifdown-ppp -rwxr-xr-x 1 root root 870 Jan 3 2018 ifdown-routes -rwxr-xr-x 1 root root 1456 Jan 3 2018 ifdown-sit -rwxr-xr-x. 1 root root 1621 Mar 18 2017 ifdown-Team -rwxr-xr-x. 1 root root 1556 Mar 18 2017 ifdown-TeamPort -rwxr-xr-x 1 root root 1462 Jan 3 2018 ifdown-tunnel lrwxrwxrwx 1 root root 22 Nov 29 2018 ifup -> ../../../usr/sbin/ifup -rwxr-xr-x 1 root root 12415 Jan 3 2018 ifup-aliases -rwxr-xr-x 1 root root 910 Jan 3 2018 ifup-bnep -rwxr-xr-x 1 root root 13475 Sep 27 2018 ifup-eth -rwxr-xr-x 1 root root 12075 Jan 3 2018 ifup-ippp -rwxr-xr-x 1 root root 11893 Jan 3 2018 ifup-ipv6 lrwxrwxrwx 1 root root 9 Nov 29 2018 ifup-isdn -> ifup-ippp -rwxr-xr-x 1 root root 650 Jan 3 2018 ifup-plip -rwxr-xr-x 1 root root 1064 Jan 3 2018 ifup-plusb -rwxr-xr-x 1 root root 4997 Sep 27 2018 ifup-post -rwxr-xr-x 1 root root 4154 Jan 3 2018 ifup-ppp -rwxr-xr-x 1 root root 2001 Jan 3 2018 ifup-routes -rwxr-xr-x 1 root root 3303 Jan 3 2018 ifup-sit -rwxr-xr-x. 1 root root 1755 Mar 18 2017 ifup-Team -rwxr-xr-x. 1 root root 1876 Mar 18 2017 ifup-TeamPort -rwxr-xr-x 1 root root 2711 Jan 3 2018 ifup-tunnel -rwxr-xr-x 1 root root 1836 Jan 3 2018 ifup-wireless -rwxr-xr-x 1 root root 5419 Jan 3 2018 init.ipv6-global -rw-r--r-- 1 root root 19948 Jan 3 2018 network-functions -rw-r--r-- 1 root root 31027 Jan 3 2018 network-functions-ipv6 [root@node03 ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0 BOOTPROTO=dhcp ONBOOT=yes
然后,把全局變量定義腳本拷貝到所有節點的 /opt/k8s/bin 目錄:
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp /opt/k8s/bin/environment.sh root@${node_ip}:/opt/k8s/bin/ ssh root@${node_ip} "chmod +x /opt/k8s/bin/*" done
22. 參考
系統內核相關參數參考:https://docs.openshift.com/enterprise/3.2/admin_guide/overcommit.html
4.5 創建 CA 證書和秘鑰
為確保安全,kubernetes 系統各組件需要使用 x509 證書對通信進行加密和認證。
CA (Certificate Authority) 是自簽名的根證書,用來簽名后續創建的其它證書。
本文檔使用 CloudFlare 的 PKI 工具集 cfssl 創建所有證書。
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
1. 安裝 cfssl 工具
sudo mkdir -p /opt/k8s/work/cert && cd /opt/k8s/work wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 mv cfssl_linux-amd64 /opt/k8s/bin/cfssl wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 mv cfssljson_linux-amd64 /opt/k8s/bin/cfssljson wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 mv cfssl-certinfo_linux-amd64 /opt/k8s/bin/cfssl-certinfo chmod +x /opt/k8s/bin/* export PATH=/opt/k8s/bin:$PATH
2. 創建根證書 (CA)
CA 證書是集群所有節點共享的,只需要創建一個 CA 證書,后續創建的所有證書都由它簽名。
3. 創建配置文件
CA 配置文件用於配置根證書的使用場景 (profile) 和具體參數 (usage,過期時間、服務端認證、客戶端認證、加密等),后續在簽名其它證書時需要指定特定場景。
cd /opt/k8s/work/cert cat > ca-config.json <<EOF { "signing": { "default": { "expiry": "87600h" }, "profiles": { "kubernetes": { "usages": [ "signing", "key encipherment", "server auth", "client auth" ], "expiry": "87600h" } } } } EOF
l signing:表示該證書可用於簽名其它證書,生成的 ca.pem 證書中 CA=TRUE;
l server auth:表示 client 可以用該該證書對 server 提供的證書進行驗證;
l client auth:表示 server 可以用該該證書對 client 提供的證書進行驗證;
4. 創建證書簽名請求文件
cd /opt/k8s/work/cert cat > ca-csr.json <<EOF { "CN": "kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "study163" } ] } EOF
l CN:Common Name,kube-apiserver 從證書中提取該字段作為請求的用戶名 (User Name),瀏覽器使用該字段驗證網站是否合法;
l O:Organization,kube-apiserver 從證書中提取該字段作為請求用戶所屬的組 (Group);
l kube-apiserver 將提取的 User、Group 作為 RBAC 授權的用戶標識;
5. 生成 CA 證書和私鑰
cd /opt/k8s/work/cert cfssl gencert -initca ca-csr.json | cfssljson -bare ca ls ca*
6. 分發證書文件
將生成的 CA 證書、秘鑰文件、配置文件拷貝到所有節點的 /etc/kubernetes/cert 目錄下:
cd /opt/k8s/work/cert source /opt/k8s/bin/environment.sh # 導入 NODE_IPS 環境變量 for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "mkdir -p /etc/kubernetes/cert" scp ca*.pem ca-config.json root@${node_ip}:/etc/kubernetes/cert done
7. 參考
各種 CA 證書類型:
https://github.com/kubernetes-incubator/apiserver-builder/blob/master/docs/concepts/auth.md
4.6 部署 kubectl 命令行工具
kubectl 是 kubernetes 集群的命令行管理工具,本小節介紹安裝和配置它的步驟。
kubectl 默認從 ~/.kube/config 文件讀取 kube-apiserver 地址、證書、用戶名等信息,如果沒有配置,執行 kubectl 命令時可能會出錯:
kubectl get pods The connection to the server localhost:8080 was refused - did you specify the right host or port?
注意:
- 如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
- 本文檔只需要部署一次,生成的 kubeconfig 文件是通用的,可以拷貝到需要執行 kubeclt 命令的機器上。
1. 下載和分發 kubectl 命令行工具
下載和解壓:
cd /opt/k8s/work wget https://dl.k8s.io/v1.12.3/kubernetes-client-linux-amd64.tar.gz tar -xzvf kubernetes-client-linux-amd64.tar.gz
分發到所有使用 kubectl 的節點:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp kubernetes/client/bin/kubectl root@${node_ip}:/opt/k8s/bin/ ssh root@${node_ip} "chmod +x /opt/k8s/bin/*" done
2. 創建 admin 證書和私鑰
kubectl 與 apiserver https 安全端口通信,apiserver 對提供的證書進行認證和授權。
kubectl 作為集群的管理工具,需要被授予最高權限。這里創建具有最高權限的 admin 證書。
創建證書簽名請求:
cd /opt/k8s/work/cert cat > admin-csr.json <<EOF { "CN": "admin", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "system:masters", "OU": "study163" } ] } EOF
l 為 system:masters,kube-apiserver 收到該證書后將請求的 Group 設置為 system:masters;
l 預定義的 ClusterRoleBinding cluster-admin 將 Group system:masters 與 Role cluster-admin 綁定,該 Role 授予所有 API的權限;
l 該證書只會被 kubectl 當做 client 證書使用,所以 hosts 字段為空;
生成證書和私鑰:
cd /opt/k8s/work/cert
cfssl gencert -ca=/opt/k8s/work/cert/ca.pem -ca-key=/opt/k8s/work/cert/ca-key.pem -config=/opt/k8s/work/cert/ca-config.json -profile=kubernetes admin-csr.json | cfssljson -bare adminls admin*
3. 創建 kubeconfig 文件
kubeconfig 為 kubectl 的配置文件,包含訪問 apiserver 的所有信息,如 apiserver 地址、CA 證書和自身使用的證書;
cd /opt/k8s/work source /opt/k8s/bin/environment.sh # 設置集群參數 kubectl config set-cluster kubernetes --certificate-authority=/opt/k8s/work/cert/ca.pem --embed-certs=true --server=${KUBE_APISERVER} --kubeconfig=kubectl.kubeconfig # 設置客戶端認證參數 kubectl config set-credentials admin --client-certificate=/opt/k8s/work/cert/admin.pem --client-key=/opt/k8s/work/cert/admin-key.pem --embed-certs=true --kubeconfig=kubectl.kubeconfig # 設置上下文參數 kubectl config set-context kubernetes --cluster=kubernetes --user=admin --kubeconfig=kubectl.kubeconfig # 設置默認上下文 kubectl config use-context kubernetes --kubeconfig=kubectl.kubeconfig
l --certificate-authority:驗證 kube-apiserver 證書的根證書;
l --client-certificate、--client-key:剛生成的 admin 證書和私鑰,連接 kube-apiserver 時使用;
l --embed-certs=true:將 ca.pem 和 admin.pem 證書內容嵌入到生成的 kubectl.kubeconfig 文件中(不加時,寫入的是證書文件路徑);
4. 分發 kubeconfig 文件
分發到所有使用 kubectl 命令的節點:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "mkdir -p ~/.kube" scp kubectl.kubeconfig root@${node_ip}:~/.kube/config done
l 保存到用戶的 ~/.kube/config 文件;
4.7 部署 etcd 集群
etcd 是基於 Raft 的分布式 key-value 存儲系統,由 CoreOS 開發,常用於服務發現、共享配置以及並發控制(如 leader 選舉、分布式鎖等)。kubernetes 使用 etcd 存儲所有運行數據。
本文檔介紹部署一個三節點高可用 etcd 集群的步驟:
l 下載和分發 etcd 二進制文件;
l 創建 etcd 集群各節點的 x509 證書,用於加密客戶端(如 etcdctl) 與 etcd 集群、etcd 集群之間的數據流;
l 創建 etcd 的 systemd unit 文件,配置服務參數;
l 檢查集群工作狀態;
etcd 集群各節點的名稱和 IP 如下:
l master:192.168.100.246
l node1:192.168.100.247
l node2:192.168.100.248
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
1. 下載和分發 etcd 二進制文件
到 https://github.com/coreos/etcd/releases 頁面下載最新版本的發布包:
cd /opt/k8s/work wget https://github.com/coreos/etcd/releases/download/v3.3.10/etcd-v3.3.10-linux-amd64.tar.gz tar -xvf etcd-v3.3.10-linux-amd64.tar.gz
分發二進制文件到集群所有節點:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp etcd-v3.3.10-linux-amd64/etcd* root@${node_ip}:/opt/k8s/bin ssh root@${node_ip} "chmod +x /opt/k8s/bin/*" done
2. 創建 etcd 證書和私鑰
創建證書簽名請求:
cd /opt/k8s/work/cert cat > etcd-csr.json <<EOF { "CN": "etcd", "hosts": [ "127.0.0.1", "192.168.100.246", "192.168.100.247", "192.168.100.248" ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "study163" } ] } EOF
l hosts 字段指定授權使用該證書的 etcd 節點 IP 或域名列表,這里將 etcd 集群的三個節點 IP 都列在其中;
生成證書和私鑰:
cd /opt/k8s/work/cert cfssl gencert -ca=/opt/k8s/work/cert/ca.pem -ca-key=/opt/k8s/work/cert/ca-key.pem -config=/opt/k8s/work/cert/ca-config.json -profile=kubernetes etcd-csr.json | cfssljson -bare etcd ls etcd*pem
分發生成的證書和私鑰到各 etcd 節點:
cd /opt/k8s/work/cert source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "mkdir -p /etc/etcd/cert" scp etcd*.pem root@${node_ip}:/etc/etcd/cert/ done
3. 創建 etcd 的 systemd unit 模板文件
是啟動腳本文件
cd /opt/k8s/work source /opt/k8s/bin/environment.sh cat > etcd.service.template <<EOF [Unit] Description=Etcd Server After=network.target After=network-online.target Wants=network-online.target Documentation=https://github.com/coreos [Service] Type=notify WorkingDirectory=${ETCD_DATA_DIR} ExecStart=/opt/k8s/bin/etcd \\ --data-dir=${ETCD_DATA_DIR} \\ --wal-dir=${ETCD_WAL_DIR} \\ --name=##NODE_NAME## \\ --cert-file=/etc/etcd/cert/etcd.pem \\ --key-file=/etc/etcd/cert/etcd-key.pem \\ --trusted-ca-file=/etc/kubernetes/cert/ca.pem \\ --peer-cert-file=/etc/etcd/cert/etcd.pem \\ --peer-key-file=/etc/etcd/cert/etcd-key.pem \\ --peer-trusted-ca-file=/etc/kubernetes/cert/ca.pem \\ --peer-client-cert-auth \\ --client-cert-auth \\ --listen-peer-urls=https://##NODE_IP##:2380 \\ --initial-advertise-peer-urls=https://##NODE_IP##:2380 \\ --listen-client-urls=https://##NODE_IP##:2379,http://127.0.0.1:2379 \\ --advertise-client-urls=https://##NODE_IP##:2379 \\ --initial-cluster-token=etcd-cluster-0 \\ --initial-cluster=${ETCD_NODES} \\ --initial-cluster-state=new \\ --auto-compaction-mode=periodic \\ --auto-compaction-retention=1 \\ --max-request-bytes=33554432 \\ --quota-backend-bytes=6442450944 \\ --heartbeat-interval=250 \\ --election-timeout=2000 Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
l WorkingDirectory、--data-dir:指定工作目錄和數據目錄為 ${ETCD_DATA_DIR},需在啟動服務前創建這個目錄;
l --wal-dir:指定 wal 目錄,為了提高性能,一般使用 SSD 或者和 --data-dir 不同的磁盤;
l --name:指定節點名稱,當 --initial-cluster-state 值為 new 時,--name 的參數值必須位於 --initial-cluster 列表中;
l --cert-file、--key-file:etcd server 與 client 通信時使用的證書和私鑰;
l --trusted-ca-file:簽名 client 證書的 CA 證書,用於驗證 client 證書;
l --peer-cert-file、--peer-key-file:etcd 與 peer 通信使用的證書和私鑰;
l --peer-trusted-ca-file:簽名 peer 證書的 CA 證書,用於驗證 peer 證書;
好多服務的.service文件在下面目錄
[root@node01 work]# cd /usr/lib/systemd/system
[root@node01 system]# ll
4. 為各節點創建和分發 etcd systemd unit 文件
替換模板文件中的變量,為各節點創建 systemd unit 文件:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for (( i=0; i < 3; i++ )) do sed -e "s/##NODE_NAME##/${NODE_NAMES[i]}/" -e "s/##NODE_IP##/${NODE_IPS[i]}/" etcd.service.template > etcd-${NODE_IPS[i]}.service done ls *.service
l NODE_NAMES 和 NODE_IPS 為相同長度的 bash 數組,分別為節點名稱和對應的 IP;
分發生成的 systemd unit 文件:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp etcd-${node_ip}.service root@${node_ip}:/usr/lib/systemd/system/etcd.service done
l 文件重命名為 etcd.service;
5. 啟動 etcd 服務
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "mkdir -p ${ETCD_DATA_DIR} ${ETCD_WAL_DIR}" ssh root@${node_ip} "systemctl daemon-reload && systemctl enable etcd && systemctl restart etcd " & done
l 必須創建 etcd 數據目錄和工作目錄;
l etcd 進程首次啟動時會等待其它節點的 etcd 加入集群,命令 systemctl start etcd 會卡住一段時間,為正常現象。
6. 檢查啟動結果
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "systemctl status etcd|grep Active" done
確保狀態為 active (running),否則查看日志,確認原因:
journalctl -u etcd
7. 驗證服務狀態
部署完 etcd 集群后,在任一 etc 節點上執行如下命令:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ETCDCTL_API=3 /opt/k8s/bin/etcdctl --endpoints=https://${node_ip}:2379 --cacert=/opt/k8s/work/cert/ca.pem --cert=/etc/etcd/cert/etcd.pem --key=/etc/etcd/cert/etcd-key.pem endpoint health done
預期輸出:
>>> 192.168.100.246
https://192.168.100.246:2379 is healthy: successfully committed proposal: took = 3.5037ms
>>> 192.168.100.247
https://192.168.100.247:2379 is healthy: successfully committed proposal: took = 17.918242ms
>>> 192.168.100.248
https://192.168.100.248:2379 is healthy: successfully committed proposal: took = 10.445815ms
輸出均為 healthy 時表示集群服務正常。
8. 查看當前的 leader
source /opt/k8s/bin/environment.sh ETCDCTL_API=3 /opt/k8s/bin/etcdctl -w table --cacert=/opt/k8s/work/cert/ca.pem \ --cert=/etc/etcd/cert/etcd.pem \ --key=/etc/etcd/cert/etcd-key.pem \ --endpoints=${ETCD_ENDPOINTS} endpoint status
輸出:

可見,當前的 leader 為 172.26.106.83。
4.8 部署 flannel 網絡
kubernetes 要求集群內各節點(包括 master 節點)能通過 Pod 網段互聯互通。flannel 使用 vxlan 技術為各節點創建一個可以互通的 Pod 網絡,使用的端口為 UDP 8472,需要開放該端口(如公有雲 AWS 等)。
flanneld 第一次啟動時,從 etcd 獲取配置的 Pod 網段信息,為本節點分配一個未使用的地址段,然后創建 flannedl.1 網絡接口(也可能是其它名稱,如 flannel1 等)。
flannel 將分配給自己的 Pod 網段信息寫入 /run/flannel/docker 文件,docker 后續使用這個文件中的環境變量設置 docker0 網橋,從而從這個地址段為本節點的所有 Pod 容器分配 IP。
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
1. 下載和分發 flanneld 二進制文件
到 https://github.com/coreos/flannel/releases 頁面下載最新版本的發布包:
cd /opt/k8s/work mkdir flannel wget https://github.com/coreos/flannel/releases/download/v0.10.0/flannel-v0.10.0-linux-amd64.tar.gz tar -xzvf flannel-v0.10.0-linux-amd64.tar.gz -C flannel
分發 flanneld 二進制文件到集群所有節點:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp flannel/{flanneld,mk-docker-opts.sh} root@${node_ip}:/opt/k8s/bin/ ssh root@${node_ip} "chmod +x /opt/k8s/bin/*" done
2. 創建 flannel 證書和私鑰
flannel 從 etcd 集群存取網段分配信息,而 etcd 集群啟用了雙向 x509 證書認證,所以需要為 flanneld 生成證書和私鑰。
創建證書簽名請求:
cd /opt/k8s/work/cert cat > flanneld-csr.json <<EOF { "CN": "flanneld", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "study163" } ] } EOF
l 該證書只會被 kubectl 當做 client 證書使用,所以 hosts 字段為空;
生成證書和私鑰:
cfssl gencert -ca=/opt/k8s/work/cert/ca.pem \ -ca-key=/opt/k8s/work/cert/ca-key.pem \ -config=/opt/k8s/work/cert/ca-config.json \ -profile=kubernetes flanneld-csr.json | cfssljson -bare flanneld ls flannel*pem
將生成的證書和私鑰分發到所有節點(master 和 worker):
cd /opt/k8s/work/cert source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "mkdir -p /etc/flanneld/cert" scp flanneld*.pem root@${node_ip}:/etc/flanneld/cert done
3. 向 etcd 寫入集群 Pod 網段信息
注意:本步驟只需執行一次。
cd /opt/k8s/work source /opt/k8s/bin/environment.sh etcdctl \ --endpoints=${ETCD_ENDPOINTS} \ --ca-file=/opt/k8s/work/cert/ca.pem \ --cert-file=/opt/k8s/work/cert/flanneld.pem \ --key-file=/opt/k8s/work/cert/flanneld-key.pem \ set ${FLANNEL_ETCD_PREFIX}/config '{"Network":"'${CLUSTER_CIDR}'", "SubnetLen": 21, "Backend": {"Type": "vxlan"}}'
l flanneld 當前版本 (v0.10.0) 不支持 etcd v3,故使用 etcd v2 API 寫入配置 key 和網段數據;
l 寫入的 Pod 網段 ${CLUSTER_CIDR} 地址段如 /16 必須小於 SubnetLen,必須與 kube-controller-manager 的 --cluster-cidr 參數值一致;
4. 創建 flanneld 的 systemd unit 文件
cd /opt/k8s/work source /opt/k8s/bin/environment.sh cat > flanneld.service << EOF [Unit] Description=Flanneld overlay address etcd agent After=network.target After=network-online.target Wants=network-online.target After=etcd.service Before=docker.service [Service] Type=notify ExecStart=/opt/k8s/bin/flanneld \\ -etcd-cafile=/etc/kubernetes/cert/ca.pem \\ -etcd-certfile=/etc/flanneld/cert/flanneld.pem \\ -etcd-keyfile=/etc/flanneld/cert/flanneld-key.pem \\ -etcd-endpoints=${ETCD_ENDPOINTS} \\ -etcd-prefix=${FLANNEL_ETCD_PREFIX} \\ -iface=${IFACE} \\ -ip-masq ExecStartPost=/opt/k8s/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker Restart=always RestartSec=5 StartLimitInterval=0 [Install] WantedBy=multi-user.target RequiredBy=docker.service EOF
l mk-docker-opts.sh 腳本將分配給 flanneld 的 Pod 子網網段信息寫入 /run/flannel/docker 文件,后續 docker 啟動時使用這個文件中的環境變量配置 docker0 網橋;
l flanneld 使用系統缺省路由所在的接口與其它節點通信,對於有多個網絡接口(如內網和公網)的節點,可以用 -iface 參數指定通信接口;
l flanneld 運行時需要 root 權限;
l -ip-masq: flanneld 為訪問 Pod 網絡外的流量設置 SNAT 規則,同時將傳遞給 Docker 的變量 --ip-masq(/run/flannel/docker 文件中)設置為 false,這樣 Docker 將不再創建 SNAT 規則; Docker 的 --ip-masq 為 true 時,創建的 SNAT 規則比較“暴力”:將所有本節點 Pod 發起的、訪問非 docker0 接口的請求做 SNAT,這樣訪問其他節點 Pod 的請求來源 IP 會被設置為 flannel.1 接口的 IP,導致目的 Pod 看不到真實的來源 Pod IP。 flanneld 創建的 SNAT 規則比較溫和,只對訪問非 Pod 網段的請求做 SNAT。
5. 分發 flanneld systemd unit 文件到所有節點
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp flanneld.service root@${node_ip}:/usr/lib/systemd/system/ done
6. 啟動 flanneld 服務
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "systemctl daemon-reload && systemctl enable flanneld && systemctl restart flanneld" done
7. 檢查啟動結果
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "systemctl status flanneld|grep Active" done
確保狀態為 active (running),否則查看日志,確認原因:
journalctl -u flanneld
8. 檢查分配給各 flanneld 的 Pod 網段信息
查看集群 Pod 網段(/16):
source /opt/k8s/bin/environment.sh etcdctl \ --endpoints=${ETCD_ENDPOINTS} \ --ca-file=/etc/kubernetes/cert/ca.pem \ --cert-file=/etc/flanneld/cert/flanneld.pem \ --key-file=/etc/flanneld/cert/flanneld-key.pem \ get ${FLANNEL_ETCD_PREFIX}/config
輸出:
{"Network":"172.30.0.0/16", "SubnetLen": 21, "Backend": {"Type": "vxlan"}}
查看已分配的 Pod 子網段列表(/24):
source /opt/k8s/bin/environment.sh etcdctl \ --endpoints=${ETCD_ENDPOINTS} \ --ca-file=/etc/kubernetes/cert/ca.pem \ --cert-file=/etc/flanneld/cert/flanneld.pem \ --key-file=/etc/flanneld/cert/flanneld-key.pem \ ls ${FLANNEL_ETCD_PREFIX}/subnets
輸出(結果是部署情況而定,網段可能與下面不一樣):
/kubernetes/network/subnets/172.30.240.0-21
/kubernetes/network/subnets/172.30.120.0-21
/kubernetes/network/subnets/172.30.192.0-21
查看某一 Pod 網段對應的節點 IP 和 flannel 接口地址:
source /opt/k8s/bin/environment.sh etcdctl \ --endpoints=${ETCD_ENDPOINTS} \ --ca-file=/etc/kubernetes/cert/ca.pem \ --cert-file=/etc/flanneld/cert/flanneld.pem \ --key-file=/etc/flanneld/cert/flanneld-key.pem \ get ${FLANNEL_ETCD_PREFIX}/subnets/172.30.240.0-21
輸出(結果是部署情況而定,網段可能與下面不一樣):
{"PublicIP":"192.168.100.246","BackendType":"vxlan","BackendData":{"VtepMAC":"fa:32:14:34:86:74"}}
l 172.30.240.0/21 被分配給節點 node2(192.168.100.252);
l VtepMAC 為 node2 節點的 flannel.1 網卡 MAC 地址;
9. 檢查節點 flannel 網絡信息
ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:22:0d:33:89:75 brd ff:ff:ff:ff:ff:ff
inet 172.27.137.240/20 brd 172.27.143.255 scope global dynamic eth0
valid_lft 100647283sec preferred_lft 100647283sec
3: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether ce:9c:a9:08:50:03 brd ff:ff:ff:ff:ff:ff
inet 172.30.80.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
l flannel.1 網卡的地址為分配的 Pod 子網段的第一個 IP(.0),且是 /32 的地址;
ip route show |grep flannel.1
172.30.32.0/21 via 172.30.32.0 dev flannel.1 onlink
172.30.184.0/21 via 172.30.184.0 dev flannel.1 onlink
l 到其它節點 Pod 網段請求都被轉發到 flannel.1 網卡;
l flanneld 根據 etcd 中子網段的信息,如 ${FLANNEL_ETCD_PREFIX}/subnets/172.30.80.0-21 ,來決定進請求發送給哪個節點的互聯 IP;
10. 驗證各節點能通過 Pod 網段互通
在各節點上部署 flannel 后,檢查是否創建了 flannel 接口(名稱可能為 flannel0、flannel.0、flannel.1 等):
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh ${node_ip} "/usr/sbin/ip addr show flannel.1|grep -w inet" done
輸出:
>>> 192.168.100.246
inet 172.30.240.0/32 scope global flannel.1
>>> 192.168.100.247
inet 172.30.120.0/32 scope global flannel.1
>>> 192.168.100.248
inet 172.30.192.0/32 scope global flannel.1
在各節點上 ping 所有 flannel 接口 IP,確保能通:
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh ${node_ip} "ping -c 1 172.30.240.0" ssh ${node_ip} "ping -c 1 172.30.120.0" ssh ${node_ip} "ping -c 1 172.30.192.0" done
4.9 kube-apiserver 高可用之 nginx 代理
kuebectl 的配置文件中靜態指定了某個 kube-apiserver IP,如果該 apiserver 實例掛掉,可能引起服務異常。
本章節講解使用 nginx 4 層透明代理功能實現 K8S 節點( master 節點和 worker 節點)高可用訪問 kube-apiserver 的方案。
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
1. 基於 nginx 代理的 kube-apiserver 高可用方案
l 控制節點的 kube-controller-manager、kube-scheduler 是多實例部署,所以只要有一個實例正常,就可以保證高可用;
l 集群內的 Pod 使用域名 kubernetes 訪問 kube-apiserver, kube-dns 會自動解析出多個 kube-apiserver 節點的 IP,所以也是高可用的;
l kubelet、kube-proxy、controller-manager、scheduler 通過本地的 kube-nginx(監聽 127.0.0.1)訪問 kube-apiserver,從而實現 kube-apiserver 的高可用。
l kube-nginx 會對所有 kube-apiserver 實例做健康檢查和負載均衡;
2. 下載和編譯 nginx
下載源碼:
cd /opt/k8s/work wget http://nginx.org/download/nginx-1.15.3.tar.gz tar -xzvf nginx-1.15.3.tar.gz
安裝nginx必須的依賴包
yum -y install gcc openssl-devel pcre-devel zlib-devel
配置編譯參數:
cd /opt/k8s/work/nginx-1.15.3 mkdir /opt/k8s/kube-nginx ./configure --prefix=/opt/k8s/kube-nginx --with-stream --without-http --without-http_uwsgi_module --without-http_scgi_module --without-http_fastcgi_module
l --with-stream:開啟 4 層透明轉發(TCP Proxy)功能;
l --without-xxx:關閉所有其他功能,這樣生成的動態鏈接二進制程序依賴最小;
輸出:
Configuration summary
+ PCRE library is not used
+ OpenSSL library is not used
+ zlib library is not used
nginx path prefix: "/opt/k8s/kube-nginx"
nginx binary file: "/opt/k8s/kube-nginx/sbin/nginx"
nginx modules path: "/opt/k8s/kube-nginx/modules"
nginx configuration prefix: "/opt/k8s/kube-nginx/conf"
nginx configuration file: "/opt/k8s/kube-nginx/conf/nginx.conf"
nginx pid file: "/opt/k8s/kube-nginx/logs/nginx.pid"
nginx error log file: "/opt/k8s/kube-nginx/logs/error.log"
nginx http access log file: "/opt/k8s/kube-nginx/logs/access.log"
nginx http client request body temporary files: "client_body_temp"
nginx http proxy temporary files: "proxy_temp"
編譯和安裝:
cd /opt/k8s/work/nginx-1.15.3 make && make install
3. 驗證編譯的 nginx
cd /opt/k8s/kube-nginx
./sbin/nginx -v
輸出:
nginx version: nginx/1.15.3
查看 nginx 動態鏈接的庫:
ldd ./sbin/nginx
linux-vdso.so.1 => (0x00007ffe11794000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f29eb5d5000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f29eb3b9000) libc.so.6 => /lib64/libc.so.6 (0x00007f29eafeb000) /lib64/ld-linux-x86-64.so.2 (0x00007f29eb7e0000)
由於只開啟了 4 層透明轉發功能,所以除了依賴 libc 等操作系統核心 lib 庫外,沒有對其它 lib 的依賴(如 libz、libssl 等),這樣可以方便部署到各版本操作系統中;
4. 安裝和部署 nginx
配置 nginx,開啟 4 層透明轉發功能:
cat > /opt/k8s/kube-nginx/conf/nginx.conf <<EOF worker_processes 1; events { worker_connections 1024; } stream { upstream backend { hash $remote_addr consistent; server 192.168.100.246:6443 max_fails=3 fail_timeout=30s; server 192.168.100.247:6443 max_fails=3 fail_timeout=30s; server 192.168.100.248:6443 max_fails=3 fail_timeout=30s; } server { listen 8443; proxy_connect_timeout 1s; proxy_pass backend; } } EOF
l 需要根據集群 kube-apiserver 的實際情況,替換 backend 中 server 列表;
5. 配置 systemd unit 文件,啟動服務
配置 kube-nginx systemd unit 文件:
cd /opt/k8s/work cat > kube-nginx.service <<EOF [Unit] Description=kube-apiserver nginx proxy After=network.target After=network-online.target Wants=network-online.target [Service] Type=forking ExecStartPre=/opt/k8s/kube-nginx/sbin/nginx -c /opt/k8s/kube-nginx/conf/nginx.conf -p /opt/k8s/kube-nginx -t ExecStart=/opt/k8s/kube-nginx/sbin/nginx -c /opt/k8s/kube-nginx/conf/nginx.conf -p /opt/k8s/kube-nginx ExecReload=/opt/k8s/kube-nginx/sbin/nginx -c /opt/k8s/kube-nginx/conf/nginx.conf -p /opt/k8s/kube-nginx -s reload PrivateTmp=true Restart=always RestartSec=5 StartLimitInterval=0 LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
啟動 kube-nginx 服務:
cp kube-nginx.service /usr/lib/systemd/system/ systemctl daemon-reload && systemctl enable kube-nginx && systemctl restart kube-nginx
6. 檢查 kube-nginx 服務運行狀態
systemctl status kube-nginx |grep 'Active:'
確保狀態為 active (running),否則查看日志,確認原因:
journalctl -u kube-nginx
4.10 部署 master 節點
kubernetes master 節點運行如下組件:
l kube-apiserver
l kube-scheduler
l kube-controller-manager
l kube-nginx
- kube-scheduler 和 kube-controller-manager 會自動選舉產生一個 leader 實例,其它實例處於阻塞模式,當 leader 掛了后,重新選舉產生新的 leader,從而保證服務可用性;
- kube-apiserver 是無狀態的,需要通過 kube-nginx 進行代理訪問,從而保證服務可用性;
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
1. 安裝和配置 kube-nginx
2. 下載最新版本二進制文件
從 CHANGELOG頁面 下載 server tarball 文件。
cd /opt/k8s/work wget https://dl.k8s.io/v1.12.3/kubernetes-server-linux-amd64.tar.gz tar -xzvf kubernetes-server-linux-amd64.tar.gz cd kubernetes tar -xzvf kubernetes-src.tar.gz
將二進制文件拷貝到所有 master 節點:
cd /opt/k8s/work/kubernetes source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp server/bin/* root@${node_ip}:/opt/k8s/bin/ ssh root@${node_ip} "chmod +x /opt/k8s/bin/*" done
3. apiserver 集群
本章節講解部署一個三實例 kube-apiserver 集群的步驟,它們通過 kube-nginx 進行代理訪問,從而保證服務可用性。
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
3.1 准備工作
注意:下載最新版本的二進制文件、安裝和配置 flanneld 參考:部署master節點
3.2. 創建 kubernetes 證書和私鑰
創建證書簽名請求:
cd /opt/k8s/work/cert source /opt/k8s/bin/environment.sh cat > kubernetes-csr.json <<EOF { "CN": "kubernetes", "hosts": [ "127.0.0.1", "172.26.106.83", "172.26.106.81", "172.26.106.82", "${CLUSTER_KUBERNETES_SVC_IP}", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster", "kubernetes.default.svc.cluster.local" ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "study163" } ] } EOF
l hosts 字段指定授權使用該證書的 IP 或域名列表,這里列出了 VIP 、apiserver 節點 IP、kubernetes 服務 IP 和域名;
l 域名最后字符不能是 .(如不能為 kubernetes.default.svc.cluster.local.),否則解析時失敗,提示: x509: cannot parse dnsName "kubernetes.default.svc.cluster.local.";
l 如果使用非 cluster.local 域名,如 opsnull.com,則需要修改域名列表中的最后兩個域名為:kubernetes.default.svc.opsnull、kubernetes.default.svc.opsnull.com
l kubernetes 服務 IP 是 apiserver 自動創建的,一般是 --service-cluster-ip-range 參數指定的網段的第一個IP,后續可以通過如下命令獲取:
kubectl get svc kubernetes
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.254.0.1 <none> 443/TCP 1d
生成證書和私鑰:
cfssl gencert -ca=/opt/k8s/work/cert/ca.pem \ -ca-key=/opt/k8s/work/cert/ca-key.pem \ -config=/opt/k8s/work/cert/ca-config.json \ -profile=kubernetes kubernetes-csr.json | cfssljson -bare kubernetes ls kubernetes*pem
將生成的證書和私鑰文件拷貝到 master 節點:
cd /opt/k8s/work/cert source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "mkdir -p /etc/kubernetes/cert" scp kubernetes*.pem root@${node_ip}:/etc/kubernetes/cert/ done
3.3 創建加密配置文件
cat > encryption-config.yaml <<EOF kind: EncryptionConfig apiVersion: v1 resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: ${ENCRYPTION_KEY} - identity: {} EOF
將加密配置文件拷貝到 master 節點的 /etc/kubernetes 目錄下:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp encryption-config.yaml root@${node_ip}:/etc/kubernetes/ done
3.4 創建 kube-apiserver systemd unit 模板文件
特別提醒:有同學沒有按照老師的k8s版本安裝,自己升級了版本,有些地方k8s升級后官方默認功能做了調整,比如kube-apiserver中的1.14版本不支持Initializers插件。
cd /opt/k8s/work source /opt/k8s/bin/environment.sh cat > kube-apiserver.service.template <<EOF [Unit] Description=Kubernetes API Server Documentation=https://github.com/GoogleCloudPlatform/kubernetes After=network.target [Service] WorkingDirectory=${K8S_DIR}/kube-apiserver ExecStart=/opt/k8s/bin/kube-apiserver \\ --enable-admission-plugins=Initializers,NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\ --anonymous-auth=false \\ --experimental-encryption-provider-config=/etc/kubernetes/encryption-config.yaml \\ --advertise-address=##NODE_IP## \\ --bind-address=##NODE_IP## \\ --insecure-port=0 \\ --authorization-mode=Node,RBAC \\ --runtime-config=api/all \\ --enable-bootstrap-token-auth \\ --service-cluster-ip-range=${SERVICE_CIDR} \\ --service-node-port-range=${NODE_PORT_RANGE} \\ --tls-cert-file=/etc/kubernetes/cert/kubernetes.pem \\ --tls-private-key-file=/etc/kubernetes/cert/kubernetes-key.pem \\ --client-ca-file=/etc/kubernetes/cert/ca.pem \\ --kubelet-certificate-authority=/etc/kubernetes/cert/ca.pem \\ --kubelet-client-certificate=/etc/kubernetes/cert/kubernetes.pem \\ --kubelet-client-key=/etc/kubernetes/cert/kubernetes-key.pem \\ --kubelet-https=true \\ --service-account-key-file=/etc/kubernetes/cert/ca.pem \\ --etcd-cafile=/etc/kubernetes/cert/ca.pem \\ --etcd-certfile=/etc/kubernetes/cert/kubernetes.pem \\ --etcd-keyfile=/etc/kubernetes/cert/kubernetes-key.pem \\ --etcd-servers=${ETCD_ENDPOINTS} \\ --enable-swagger-ui=true \\ --allow-privileged=true \\ --max-mutating-requests-inflight=2000 \\ --max-requests-inflight=4000 \\ --apiserver-count=3 \\ --audit-log-maxage=30 \\ --audit-log-maxbackup=3 \\ --audit-log-maxsize=100 \\ --audit-log-path=${K8S_DIR}/kube-apiserver/audit.log \\ --event-ttl=168h \\ --logtostderr=true \\ --v=2 Restart=on-failure RestartSec=5 Type=notify LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
l --experimental-encryption-provider-config:啟用加密特性;
l --authorization-mode=Node,RBAC: 開啟 Node 和 RBAC 授權模式,拒絕未授權的請求;
l --enable-admission-plugins:啟用 ServiceAccount 和 NodeRestriction;
l --service-account-key-file:簽名 ServiceAccount Token 的公鑰文件,kube-controller-manager 的 --service-account-private-key-file 指定私鑰文件,兩者配對使用;
l --tls-*-file:指定 apiserver 使用的證書、私鑰和 CA 文件。--client-ca-file 用於驗證 client (kue-controller-manager、kube-scheduler、kubelet、kube-proxy 等)請求所帶的證書;
l --kubelet-client-certificate、--kubelet-client-key:如果指定,則使用 https 訪問 kubelet APIs;需要為證書對應的用戶(上面 kubernetes*.pem 證書的用戶為 kubernetes) 用戶定義 RBAC 規則,否則訪問 kubelet API 時提示未授權;
l --bind-address: 不能為 127.0.0.1,否則外界不能訪問它的安全端口 6443;
l --insecure-port=0:關閉監聽非安全端口(8080);
l --service-cluster-ip-range: 指定 Service Cluster IP 地址段;
l --service-node-port-range: 指定 NodePort 的端口范圍;
l --runtime-config=api/all=true: 啟用所有版本的 APIs,如 autoscaling/v2alpha1;
l --enable-bootstrap-token-auth:啟用 kubelet bootstrap 的 token 認證;
l --apiserver-count=3:指定 apiserver 的實例數量;
3.5 為各節點創建和分發 kube-apiserver systemd unit 文件
替換模板文件中的變量,為各節點創建 systemd unit 文件:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for (( i=0; i < 3; i++ )) do sed -e "s/##NODE_NAME##/${NODE_NAMES[i]}/" -e "s/##NODE_IP##/${NODE_IPS[i]}/" kube-apiserver.service.template > kube-apiserver-${NODE_IPS[i]}.service done ls kube-apiserver*.service
l NODE_NAMES 和 NODE_IPS 為相同長度的 bash 數組,分別為節點名稱和對應的 IP;
分發生成的 systemd unit 文件:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp kube-apiserver-${node_ip}.service root@${node_ip}:/usr/lib/systemd/system/kube-apiserver.service done
l 文件重命名為 kube-apiserver.service;
3.6 啟動 kube-apiserver 服務
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "mkdir -p ${K8S_DIR}/kube-apiserver" ssh root@${node_ip} "systemctl daemon-reload && systemctl enable kube-apiserver && systemctl restart kube-apiserver" done
l 必須創建工作目錄;
3.7 檢查 kube-apiserver 運行狀態
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "systemctl status kube-apiserver |grep 'Active:'" done
確保狀態為 active (running),否則到 master 節點查看日志,確認原因:
journalctl -u kube-apiserver
3.8 打印 kube-apiserver 寫入 etcd 的數據
source /opt/k8s/bin/environment.sh ETCDCTL_API=3 etcdctl \ --endpoints=${ETCD_ENDPOINTS} \ --cacert=/opt/k8s/work/cert/ca.pem \ --cert=/opt/k8s/work/cert/etcd.pem \ --key=/opt/k8s/work/cert/etcd-key.pem \ get /registry/ --prefix --keys-only
3.9 檢查集群信息
kubectl cluster-info
Kubernetes master is running at https://127.0.0.1:8443
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
kubectl get all --all-namespaces
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default service/kubernetes ClusterIP 10.254.0.1 <none> 443/TCP 4m27s
kubectl get componentstatuses
NAME STATUS MESSAGE ERROR
controller-manager Unhealthy Get http://127.0.0.1:10252/healthz: dial tcp 127.0.0.1:10252: connect: connection refused
scheduler Unhealthy Get http://127.0.0.1:10251/healthz: dial tcp 127.0.0.1:10251: connect: connection refused
etcd-2 Healthy {"health":"true"}
etcd-0 Healthy {"health":"true"}
etcd-1 Healthy {"health":"true"}
注意:
- 如果執行 kubectl 命令式時輸出如下錯誤信息,則說明使用的 ~/.kube/config 文件不對,請切換到正確的賬戶后再執行該命令:
The connection to the server localhost:8080 was refused - did you specify the right host or port?
2.執行 kubectl get componentstatuses 命令時,apiserver 默認向 127.0.0.1 發送請求。當 controller-manager、scheduler 以集群模式運行時,有可能和 kube-apiserver 不在一台機器上,這時 controller-manager 或 scheduler 的狀態為 Unhealthy,但實際上它們工作正常。
3.10 檢查 kube-apiserver 監聽的端口
sudo netstat -lnpt|grep kube-api
tcp 0 0 192.168.100.246:6443 0.0.0.0:* LISTEN 51903/kube-apiserve
l 6443: 接收 https 請求的安全端口,對所有請求做認證和授權;
l 由於關閉了非安全端口,故沒有監聽 8080;
3.11 授予 kubernetes 證書訪問 kubelet API 的權限
在執行 kubectl exec、run、logs 等命令時,apiserver 會轉發到 kubelet。這里定義 RBAC 規則,授權 apiserver 調用 kubelet API。
kubectl create clusterrolebinding kube-apiserver:kubelet-apis --clusterrole=system:kubelet-api-admin --user kubernetes
3.12 參考
關於證書域名最后字符不能是 . 的問題,實際和 Go 的版本有關,1.9 不支持這種類型的證書:https://github.com/kubernetes/ingress-nginx/issues/2188
4. controller-manager 集群
本文檔介紹部署高可用 kube-controller-manager 集群的步驟。
該集群包含 3 個節點,啟動后將通過競爭選舉機制產生一個 leader 節點,其它節點為阻塞狀態。當 leader 節點不可用后,剩余節點將再次進行選舉產生新的 leader 節點,從而保證服務的可用性。
為保證通信安全,本文檔先生成 x509 證書和私鑰,kube-controller-manager 在如下兩種情況下使用該證書:
- 與 kube-apiserver 的安全端口通信時;
- 在安全端口(https,10252) 輸出 prometheus 格式的 metrics;
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
4.1 准備工作
下載最新版本的二進制文件、安裝和配置 flanneld 參考:部署master節點
4.2 創建 kube-controller-manager 證書和私鑰
cd /opt/k8s/work/cert cat > kube-controller-manager-csr.json <<EOF { "CN": "system:kube-controller-manager", "key": { "algo": "rsa", "size": 2048 }, "hosts": [ "127.0.0.1", "172.26.106.83", "172.26.106.81", "172.26.106.82" ], "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "system:kube-controller-manager", "OU": "study163" } ] } EOF
l hosts 列表包含所有 kube-controller-manager 節點 IP;
l CN 為 system:kube-controller-manager、O 為 system:kube-controller-manager,kubernetes 內置的 ClusterRoleBindings system:kube-controller-manager 賦予 kube-controller-manager 工作所需的權限。
生成證書和私鑰:
cd /opt/k8s/work/cert cfssl gencert -ca=/opt/k8s/work/cert/ca.pem \ -ca-key=/opt/k8s/work/cert/ca-key.pem \ -config=/opt/k8s/work/cert/ca-config.json \ -profile=kubernetes kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager ls kube-controller-manager*pem
將生成的證書和私鑰分發到所有 master 節點:
cd /opt/k8s/work/cert source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp kube-controller-manager*.pem root@${node_ip}:/etc/kubernetes/cert/ done
4.3 創建和分發 kubeconfig 文件
kubeconfig 文件包含訪問 apiserver 的所有信息,如 apiserver 地址、CA 證書和自身使用的證書;
cd /opt/k8s/work source /opt/k8s/bin/environment.sh kubectl config set-cluster kubernetes \ --certificate-authority=/opt/k8s/work/cert/ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=kube-controller-manager.kubeconfig kubectl config set-credentials system:kube-controller-manager \ --client-certificate=cert/kube-controller-manager.pem \ --client-key=cert/kube-controller-manager-key.pem \ --embed-certs=true \ --kubeconfig=kube-controller-manager.kubeconfig kubectl config set-context system:kube-controller-manager \ --cluster=kubernetes \ --user=system:kube-controller-manager \ --kubeconfig=kube-controller-manager.kubeconfig kubectl config use-context system:kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig
分發 kubeconfig 到所有 master 節點:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp kube-controller-manager.kubeconfig root@${node_ip}:/etc/kubernetes/ done
4.4 創建和分發 kube-controller-manager systemd unit 文件
cd /opt/k8s/work source /opt/k8s/bin/environment.sh cat > kube-controller-manager.service <<EOF [Unit] Description=Kubernetes Controller Manager Documentation=https://github.com/GoogleCloudPlatform/kubernetes [Service] WorkingDirectory=${K8S_DIR}/kube-controller-manager ExecStart=/opt/k8s/bin/kube-controller-manager \\ --port=0 \\ --secure-port=10252 \\ --bind-address=127.0.0.1 \\ --kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\ --authentication-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\ --authorization-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\ --service-cluster-ip-range=${SERVICE_CIDR} \\ --cluster-name=kubernetes \\ --cluster-signing-cert-file=/etc/kubernetes/cert/ca.pem \\ --cluster-signing-key-file=/etc/kubernetes/cert/ca-key.pem \\ --experimental-cluster-signing-duration=8760h \\ --root-ca-file=/etc/kubernetes/cert/ca.pem \\ --service-account-private-key-file=/etc/kubernetes/cert/ca-key.pem \\ --leader-elect=true \\ --controllers=*,bootstrapsigner,tokencleaner \\ --horizontal-pod-autoscaler-use-rest-clients=true \\ --horizontal-pod-autoscaler-sync-period=10s \\ --tls-cert-file=/etc/kubernetes/cert/kube-controller-manager.pem \\ --tls-private-key-file=/etc/kubernetes/cert/kube-controller-manager-key.pem \\ --use-service-account-credentials=true \\ --kube-api-qps=1000 \\ --kube-api-burst=2000 \\ --logtostderr=true \\ --v=2 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF
l --port=0:關閉監聽 http /metrics 的請求,同時 --address 參數無效,--bind-address 參數有效;
l --secure-port=10252、--bind-address=0.0.0.0: 在所有網絡接口監聽 10252 端口的 https /metrics 請求;
l --kubeconfig:指定 kubeconfig 文件路徑,kube-controller-manager 使用它連接和驗證 kube-apiserver;
l --authentication-kubeconfig 和 --authorization-kubeconfig:kube-controller-manager 使用它連接 apiserver,對 client 的請求進行認證和授權。kube-controller-manager 不再使用 --tls-ca-file 對請求 https metrics 的 Client 證書進行校驗。如果沒有配置這兩個 kubeconfig 參數,則 client 連接 kube-controller-manager https 端口的請求會被拒絕(提示權限不足)。
l --cluster-signing-*-file:簽名 TLS Bootstrap 創建的證書;
l --experimental-cluster-signing-duration:指定 TLS Bootstrap 證書的有效期;
l --root-ca-file:放置到容器 ServiceAccount 中的 CA 證書,用來對 kube-apiserver 的證書進行校驗;
l --service-account-private-key-file:簽名 ServiceAccount 中 Token 的私鑰文件,必須和 kube-apiserver 的 --service-account-key-file 指定的公鑰文件配對使用;
l --service-cluster-ip-range :指定 Service Cluster IP 網段,必須和 kube-apiserver 中的同名參數一致;
l --leader-elect=true:集群運行模式,啟用選舉功能;被選為 leader 的節點負責處理工作,其它節點為阻塞狀態;
l --controllers=*,bootstrapsigner,tokencleaner:啟用的控制器列表,tokencleaner 用於自動清理過期的 Bootstrap token;
l --horizontal-pod-autoscaler-*:custom metrics 相關參數,支持 autoscaling/v2alpha1;
l --tls-cert-file、--tls-private-key-file:使用 https 輸出 metrics 時使用的 Server 證書和秘鑰;
l --use-service-account-credentials=true: kube-controller-manager 中各 controller 使用 serviceaccount 訪問 kube-apiserver;
分發 systemd unit 文件到所有 master 節點:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp kube-controller-manager.service root@${node_ip}:/usr/lib/systemd/system/ done
4.5 kube-controller-manager 的權限
ClusteRole: system:kube-controller-manager 的權限很小,只能創建 secret、serviceaccount 等資源對象,各 controller 的權限分散到 ClusterRole system:controller:XXX 中。
需要在 kube-controller-manager 的啟動參數中添加 --use-service-account-credentials=true 參數,這樣 main controller 會為各 controller 創建對應的 ServiceAccount XXX-controller。
內置的 ClusterRoleBinding system:controller:XXX 將賦予各 XXX-controller ServiceAccount 對應的 ClusterRole system:controller:XXX 權限。
另外,--authentication-kubeconfig 和 --authorization-kubeconfig 參數指定的證書需要有創建 "subjectaccessreviews" 的權限,否則提示:
curl --cacert /opt/k8s/work/cert/ca.pem --cert /opt/k8s/work/cert/admin.pem --key /opt/k8s/work/cert/admin-key.pem https://127.0.0.1:10252/metrics
Internal Server Error: "/metrics": subjectaccessreviews.authorization.k8s.io is forbidden: User "system:kube-controller-manager" cannot create resource "subjectaccessreviews" in API group "authorization.k8s.io" at the cluster scope
解決辦法是創建一個 ClusterRoleBinding,賦予相應的權限:
kubectl create clusterrolebinding controller-manager:system:auth-delegator --user system:kube-controller-manager --clusterrole system:auth-delegator
clusterrolebinding.rbac.authorization.k8s.io/controller-manager:system:auth-delegator created
參考:https://github.com/kubernetes/kubeadm/issues/1285
4.6 啟動 kube-controller-manager 服務
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "mkdir -p ${K8S_DIR}/kube-controller-manager" ssh root@${node_ip} "systemctl daemon-reload && systemctl enable kube-controller-manager && systemctl restart kube-controller-manager" done
l 必須創建工作目錄;
4.7 檢查服務運行狀態
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "systemctl status kube-controller-manager|grep Active" done
確保狀態為 active (running),否則查看日志,確認原因:
journalctl -u kube-controller-manager
4.8 查看輸出的 metric
注意:以下命令在 kube-controller-manager 節點上執行。
kube-controller-manager 監聽 10252 端口,接收 https 請求:
sudo netstat -lnpt|grep kube-controll
tcp 0 0 127.0.0.1:10252 0.0.0.0:* LISTEN 55755/kube-controll
curl --cacert /opt/k8s/work/cert/ca.pem --cert /opt/k8s/work/cert/admin.pem --key /opt/k8s/work/cert/admin-key.pem https://127.0.0.1:10252/metrics
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "forbidden: User \"system:anonymous\" cannot get path \"/metrics\"",
"reason": "Forbidden",
"details": {
},
"code": 403
}
將 kube-controller-manager 的日志級別設置為 4 后,可以看到原因是:
journalctl -u kube-controller-manager -f |grep /metrics
2月 22 19:07:28 m7-inf-prod01 kube-controller-manager[1416748]: I0222 19:07:28.003325 1416748 authorization.go:73] Forbidden: "/metrics", Reason: "no RBAC policy matched"
2月 22 19:07:28 m7-inf-prod01 kube-controller-manager[1416748]: I0222 19:07:28.003472 1416748 wrap.go:42] GET /metrics: (2.600519ms) 403 [curl/7.29.0 127.0.0.1:36324]
這是由於沒有部署 metrics-server 的緣故。后續在 metrics-server插件 一文中將介紹部署 metrics-server 的步驟。
參考:https://github.com/kubernetes-incubator/metrics-server/issues/85
4.9 測試 kube-controller-manager 集群的高可用
停掉一個或兩個節點的 kube-controller-manager 服務,觀察其它節點的日志,看是否獲取了 leader 權限。
4.10 查看當前的 leader
kubectl get endpoints kube-controller-manager --namespace=kube-system -o yaml
apiVersion: v1
kind: Endpoints
metadata:
annotations:
control-plane.alpha.kubernetes.io/leader: '{"holderIdentity":"node1_084534e2-6cc4-11e8-a418-5254001f5b65","leaseDurationSeconds":15,"acquireTime":"2018-06-10T15:40:33Z","renewTime":"2018-06-10T16:19:08Z","leaderTransitions":12}'
creationTimestamp: 2018-06-10T13:59:42Z
name: kube-controller-manager
namespace: kube-system
resourceVersion: "4540"
selfLink: /api/v1/namespaces/kube-system/endpoints/kube-controller-manager
uid: 862cc048-6cb6-11e8-96fa-525400ba84c6
可見,當前的 leader 為 master 節點。
4.11 參考
關於 controller 權限和 use-service-account-credentials 參數:
https://github.com/kubernetes/kubernetes/issues/48208
kublet 認證和授權:
https://kubernetes.io/docs/admin/kubelet-authentication-authorization/#kubelet-authorization
5. scheduler 集群
本章節介紹部署高可用 kube-scheduler 集群的步驟。
該集群包含 3 個節點,啟動后將通過競爭選舉機制產生一個 leader 節點,其它節點為阻塞狀態。當 leader 節點不可用后,剩余節點將再次進行選舉產生新的 leader 節點,從而保證服務的可用性。
為保證通信安全,本文檔先生成 x509 證書和私鑰,kube-scheduler 在如下兩種情況下使用該證書:
- 與 kube-apiserver 的安全端口通信;
- 在安全端口(https,10251) 輸出 prometheus 格式的 metrics;
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
5.1 准備工作
下載最新版本的二進制文件、安裝和配置 flanneld 參考:部署master節點
5.2 創建 kube-scheduler 證書和私鑰
創建證書簽名請求:
cd /opt/k8s/work/cert cat > kube-scheduler-csr.json <<EOF { "CN": "system:kube-scheduler", "hosts": [ "127.0.0.1", "192.168.100.246", "192.168.100.247", "192.168.100.248" ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "system:kube-scheduler", "OU": "study163" } ] } EOF
l hosts 列表包含所有 kube-scheduler 節點 IP;
l CN 為 system:kube-scheduler、O 為 system:kube-scheduler,kubernetes 內置的 ClusterRoleBindings system:kube-scheduler 將賦予 kube-scheduler 工作所需的權限。
生成證書和私鑰:
cd /opt/k8s/work/cert cfssl gencert -ca=/opt/k8s/work/cert/ca.pem \ -ca-key=/opt/k8s/work/cert/ca-key.pem \ -config=/opt/k8s/work/cert/ca-config.json \ -profile=kubernetes kube-scheduler-csr.json | cfssljson -bare kube-scheduler ls kube-scheduler*pem
5.3 創建和分發 kubeconfig 文件
kubeconfig 文件包含訪問 apiserver 的所有信息,如 apiserver 地址、CA 證書和自身使用的證書;
cd /opt/k8s/work source /opt/k8s/bin/environment.sh kubectl config set-cluster kubernetes \ --certificate-authority=cert/ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=kube-scheduler.kubeconfig kubectl config set-credentials system:kube-scheduler \ --client-certificate=cert/kube-scheduler.pem \ --client-key=cert/kube-scheduler-key.pem \ --embed-certs=true \ --kubeconfig=kube-scheduler.kubeconfig kubectl config set-context system:kube-scheduler \ --cluster=kubernetes \ --user=system:kube-scheduler \ --kubeconfig=kube-scheduler.kubeconfig kubectl config use-context system:kube-scheduler --kubeconfig=kube-scheduler.kubeconfig
l 上一步創建的證書、私鑰以及 kube-apiserver 地址被寫入到 kubeconfig 文件中;
分發 kubeconfig 到所有 master 節點:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp kube-scheduler.kubeconfig root@${node_ip}:/etc/kubernetes/ done
5.4 創建 kube-scheduler 配置文件
cat <<EOF | sudo tee kube-scheduler.yaml apiVersion: componentconfig/v1alpha1 kind: KubeSchedulerConfiguration clientConnection: kubeconfig: "/etc/kubernetes/kube-scheduler.kubeconfig" leaderElection: leaderElect: true EOF
l --kubeconfig:指定 kubeconfig 文件路徑,kube-scheduler 使用它連接和驗證 kube-apiserver;
l --leader-elect=true:集群運行模式,啟用選舉功能;被選為 leader 的節點負責處理工作,其它節點為阻塞狀態;
分發 kube-scheduler 配置文件到所有 master 節點:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp kube-scheduler.yaml root@${node_ip}:/etc/kubernetes/ done
5.5 創建和分發 kube-scheduler systemd unit 文件
cd /opt/k8s/work cat > kube-scheduler.service <<EOF [Unit] Description=Kubernetes Scheduler Documentation=https://github.com/GoogleCloudPlatform/kubernetes [Service] WorkingDirectory=${K8S_DIR}/kube-scheduler ExecStart=/opt/k8s/bin/kube-scheduler \\ --config=/etc/kubernetes/kube-scheduler.yaml \\ --address=127.0.0.1 \\ --kube-api-qps=100 \\ --logtostderr=true \\ --v=2 Restart=always RestartSec=5 StartLimitInterval=0 [Install] WantedBy=multi-user.target EOF
l --address:在 127.0.0.1:10251 端口接收 http /metrics 請求;kube-scheduler 目前還不支持接收 https 請求;
分發 systemd unit 文件到所有 master 節點:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp kube-scheduler.service root@${node_ip}:/etc/systemd/system/ done
5.6 啟動 kube-scheduler 服務
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "mkdir -p ${K8S_DIR}/kube-scheduler" ssh root@${node_ip} "systemctl daemon-reload && systemctl enable kube-scheduler && systemctl restart kube-scheduler" done
l 必須先創建工作目錄;
5.7 檢查服務運行狀態
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "systemctl status kube-scheduler|grep Active" done
確保狀態為 active (running),否則查看日志,確認原因:
journalctl -u kube-scheduler
5.8 查看輸出的 metric
注意:以下命令在 kube-scheduler 節點上執行。
kube-scheduler 監聽 10251 端口,接收 http 請求:
sudo netstat -lnpt|grep kube-sche
tcp 0 0 127.0.0.1:10251 0.0.0.0:* LISTEN 23783/kube-schedule
curl -s http://127.0.0.1:10251/metrics |head
# HELP apiserver_audit_event_total Counter of audit events generated and sent to the audit backend.
# TYPE apiserver_audit_event_total counter
apiserver_audit_event_total 0
# HELP apiserver_client_certificate_expiration_seconds Distribution of the remaining lifetime on the certificate used to authenticate a request.
# TYPE apiserver_client_certificate_expiration_seconds histogram
apiserver_client_certificate_expiration_seconds_bucket{le="0"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="21600"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="43200"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="86400"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="172800"} 0
5.9 測試 kube-scheduler 集群的高可用
隨便找一個或兩個 master 節點,停掉 kube-scheduler 服務,看其它節點是否獲取了 leader 權限(systemd 日志)。
5.10 查看當前的 leader
kubectl get endpoints kube-scheduler --namespace=kube-system -o yaml
apiVersion: v1
kind: Endpoints
metadata:
annotations:
control-plane.alpha.kubernetes.io/leader: '{"holderIdentity":"k8s-1_1ea20a43-4f8e-11e9-9ac5-000c2955d204","leaseDurationSeconds":15,"acquireTime":"2019-03-26T06:12:24Z","renewTime":"2019-03-26T06:23:54Z","leaderTransitions":0}'
creationTimestamp: 2019-03-26T06:12:24Z
name: kube-scheduler
namespace: kube-system
resourceVersion: "72504"
selfLink: /api/v1/namespaces/kube-system/endpoints/kube-scheduler
uid: 1f4b7394-4f8e-11e9-a869-000c2955d204
可見,當前的 leader 為 master 節點。
4.11 部署 worker 節點
kubernetes work 節點運行如下組件:
l docker
l kubelet
l kube-proxy
l flanneld
l kube-nginx
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
前提條件:
安裝和配置 flanneld
參考 部署flannel網絡
安裝和配置 kube-nginx
安裝依賴包:
ssh root@${node_ip} "yum install -y epel-release" ssh root@${node_ip} "yum install -y conntrack ipvsadm ipset jq iptables curl sysstat libseccomp && /usr/sbin/modprobe ip_vs"
1. 部署 docker 組件
docker 是容器的運行環境,管理它的生命周期。kubelet 通過 Container Runtime Interface (CRI) 與 docker 進行交互。
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
docker 有多種安裝方式:本章節中我們使用最簡單的安裝方式 腳本安裝,使用腳本安裝不需要我們關注依賴運行腳本就能自動安裝。
不過這種方式不安全,而且必須要依賴網絡,不推薦在生產環境中使用;生成環境建議使用二進制腳本安裝。
1.1. 安裝依賴包
參考 部署worker節點
1.2. 下載和分發 docker 安裝文件
使用腳本安裝docker:
curl -fLsS https://get.docker.com/ | sh
等待安裝完成,也可以指定使用 Aliyun 鏡像
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
使用二進制文件安裝docker:
到 https://download.docker.com/linux/static/stable/x86_64/ 頁面下載最新發布包:
cd /opt/k8s/work wget https://download.docker.com/linux/static/stable/x86_64/docker-18.09.0.tgz tar -xvf docker-18.09.0.tgz
分發二進制文件到所有 worker 節點:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp docker/* root@${node_ip}:/opt/k8s/bin/ ssh root@${node_ip} "chmod +x /opt/k8s/bin/*" done
1.3 創建和分發 systemd unit 文件
cd /opt/k8s/work cat > docker.service <<"EOF" [Unit] Description=Docker Application Container Engine Documentation=http://docs.docker.io [Service] WorkingDirectory=##DOCKER_DIR## Environment="PATH=/opt/k8s/bin:/bin:/sbin:/usr/bin:/usr/sbin" EnvironmentFile=-/run/flannel/docker ExecStart=/opt/k8s/bin/dockerd $DOCKER_NETWORK_OPTIONS ExecReload=/bin/kill -s HUP $MAINPID Restart=on-failure RestartSec=5 LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity Delegate=yes KillMode=process [Install] WantedBy=multi-user.target EOF
l EOF 前后有雙引號,這樣 bash 不會替換文檔中的變量,如 $DOCKER_NETWORK_OPTIONS;
l dockerd 運行時會調用其它 docker 命令,如 docker-proxy,所以需要將 docker 命令所在的目錄加到 PATH 環境變量中;
l flanneld 啟動時將網絡配置寫入 /run/flannel/docker 文件中,dockerd 啟動前讀取該文件中的環境變量 DOCKER_NETWORK_OPTIONS ,然后設置 docker0 網橋網段;
l 如果指定了多個 EnvironmentFile 選項,則必須將 /run/flannel/docker 放在最后(確保 docker0 使用 flanneld 生成的 bip 參數);
l docker 需要以 root 用於運行;
l docker 從 1.13 版本開始,可能將 iptables FORWARD chain的默認策略設置為DROP,從而導致 ping 其它 Node 上的 Pod IP 失敗,遇到這種情況時,需要手動設置策略為 ACCEPT:
sudo iptables -P FORWARD ACCEPT
並且把以下命令寫入 /etc/rc.local 文件中,防止節點重啟iptables FORWARD chain的默認策略又還原為DROP
/sbin/iptables -P FORWARD ACCEPT
分發 systemd unit 文件到所有 worker 機器:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh sed -i -e "s|##DOCKER_DIR##|${DOCKER_DIR}|" docker.service for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp docker.service root@${node_ip}:/usr/lib/systemd/system/ done
1.4 配置和分發 docker 配置文件
使用國內的倉庫鏡像服務器以加快 pull image 的速度,同時增加下載的並發數 (需要重啟 dockerd 生效):
cd /opt/k8s/work cat > docker-daemon.json <<EOF { "registry-mirrors": ["https://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn"], "insecure-registries": ["docker02:35000"], "max-concurrent-downloads": 20, "live-restore": true, "max-concurrent-uploads": 10, "debug": true, "data-root": "/data/k8s/docker/data", "exec-root": "/data/k8s/docker/exec", "log-opts": { "max-size": "100m", "max-file": "5" } } EOF
分發 docker 配置文件到所有 work 節點:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "mkdir -p /etc/docker/ ${DOCKER_DIR}/{data,exec}" scp docker-daemon.json root@${node_ip}:/etc/docker/daemon.json done
1.5 啟動 docker 服務
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "systemctl stop firewalld && systemctl disable firewalld" ssh root@${node_ip} "/usr/sbin/iptables -F && /usr/sbin/iptables -X && /usr/sbin/iptables -F -t nat && /usr/sbin/iptables -X -t nat" ssh root@${node_ip} "/usr/sbin/iptables -P FORWARD ACCEPT" ssh root@${node_ip} "systemctl daemon-reload && systemctl enable docker && systemctl restart docker" #ssh root@${node_ip} 'for intf in /sys/devices/virtual/net/docker0/brif/*; do echo 1 > $intf/hairpin_mode; done' ssh root@${node_ip} "sudo sysctl -p /etc/sysctl.d/kubernetes.conf" done
l 關閉 firewalld(centos7)/ufw(ubuntu16.04),否則可能會重復創建 iptables 規則;
l 清理舊的 iptables rules 和 chains 規則;
l 開啟 docker0 網橋下虛擬網卡的 hairpin 模式;
1.6 檢查服務運行狀態
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "systemctl status docker|grep Active" done
確保狀態為 active (running),否則查看日志,確認原因:
journalctl -u docker
1.7 檢查 docker0 網橋
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "/usr/sbin/ip addr show flannel.1 && /usr/sbin/ip addr show docker0" done
確認各 work 節點的 docker0 網橋和 flannel.1 接口的 IP 處於同一個網段中(如下 172.30.112.0/32 位於 172.30.112.1/21 中):
>>> 192.168.100.246
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether fa:32:14:34:86:74 brd ff:ff:ff:ff:ff:ff
inet 172.30.240.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:7f:58:af:18 brd ff:ff:ff:ff:ff:ff
inet 172.30.240.1/21 brd 172.30.247.255 scope global docker0
valid_lft forever preferred_lft forever
2. 部署kubelet組件
kublet 運行在每個 worker 節點上,接收 kube-apiserver 發送的請求,管理 Pod 容器,執行交互式命令,如 exec、run、logs 等。
kublet 啟動時自動向 kube-apiserver 注冊節點信息,內置的 cadvisor 統計和監控節點的資源使用情況。
為確保安全,本文檔只開啟接收 https 請求的安全端口,對請求進行認證和授權,拒絕未授權的訪問(如 apiserver、heapster)。
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
2.1. 下載和分發 kubelet 二進制文件
參考 部署master節點
2.2. 安裝依賴包
參考 部署worker節點
2.3. 創建 kubelet bootstrap kubeconfig 文件
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_name in ${NODE_NAMES[@]} do echo ">>> ${node_name}" # 創建 token export BOOTSTRAP_TOKEN=$(kubeadm token create \ --description kubelet-bootstrap-token \ --groups system:bootstrappers:${node_name} \ --kubeconfig ~/.kube/config) # 設置集群參數 kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/cert/ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig # 設置客戶端認證參數 kubectl config set-credentials kubelet-bootstrap \ --token=${BOOTSTRAP_TOKEN} \ --kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig # 設置上下文參數 kubectl config set-context default \ --cluster=kubernetes \ --user=kubelet-bootstrap \ --kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig # 設置默認上下文 kubectl config use-context default --kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig done
l 證書中寫入 Token 而非證書,證書后續由 kube-controller-manager 創建。
查看 kubeadm 為各節點創建的 token:
kubeadm token list --kubeconfig ~/.kube/config
TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS
33xdd2.b04al5zmia3tnnf8 23h 2019-04-01T14:06:46+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:node2
ch6fro.v9nps3d5z0lcmyrd 23h 2019-04-01T14:06:25+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:master
napqqj.wi1qhq2gjel1dvia 23h 2019-04-01T14:06:35+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:node1
l 創建的 token 有效期為 1 天,超期后將不能再被使用,且會被 kube-controller-manager 的 tokencleaner 清理(如果啟用該 controller 的話);
l kube-apiserver 接收 kubelet 的 bootstrap token 后,將請求的 user 設置為 system:bootstrap:,group 設置為 system:bootstrappers;
查看各 token 關聯的 Secret:
kubectl get secrets -n kube-system|grep bootstrap-token
bootstrap-token-503xx3 bootstrap.kubernetes.io/token 7 40m
bootstrap-token-cjadiv bootstrap.kubernetes.io/token 7 45m
bootstrap-token-iifyk9 bootstrap.kubernetes.io/token 7 40m
2.4. 分發 bootstrap kubeconfig 文件到所有 worker 節點
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_name in ${NODE_NAMES[@]} do echo ">>> ${node_name}" scp kubelet-bootstrap-${node_name}.kubeconfig root@${node_name}:/etc/kubernetes/kubelet-bootstrap.kubeconfig done
2.5. 創建和分發 kubelet 參數配置文件
從 v1.10 開始,kubelet 部分參數需在配置文件中配置,kubelet --help 會提示:
DEPRECATED: This parameter should be set via the config file specified by the Kubelet's --config flag
創建 kubelet 參數配置模板文件:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh cat <<EOF | tee kubelet-config.yaml.template kind: KubeletConfiguration apiVersion: kubelet.config.k8s.io/v1beta1 authentication: anonymous: enabled: false webhook: enabled: true x509: clientCAFile: "/etc/kubernetes/cert/ca.pem" authorization: mode: Webhook clusterDomain: "${CLUSTER_DNS_DOMAIN}" clusterDNS: - "${CLUSTER_DNS_SVC_IP}" podCIDR: "${CLUSTER_CIDR}" maxPods: 220 serializeImagePulls: false hairpinMode: promiscuous-bridge cgroupDriver: cgroupfs runtimeRequestTimeout: "15m" rotateCertificates: true serverTLSBootstrap: true readOnlyPort: 0 port: 10250 address: "##NODE_IP##" EOF
l address:API 監聽地址,不能為 127.0.0.1,否則 kube-apiserver、heapster 等不能調用 kubelet 的 API;
l readOnlyPort=0:關閉只讀端口(默認 10255),等效為未指定;
l authentication.anonymous.enabled:設置為 false,不允許匿名訪問 10250 端口;
l authentication.x509.clientCAFile:指定簽名客戶端證書的 CA 證書,開啟 HTTP 證書認證;
l authentication.webhook.enabled=true:開啟 HTTPs bearer token 認證;
l 對於未通過 x509 證書和 webhook 認證的請求(kube-apiserver 或其他客戶端),將被拒絕,提示 Unauthorized;
l authroization.mode=Webhook:kubelet 使用 SubjectAccessReview API 查詢 kube-apiserver 某 user、group 是否具有操作資源的權限(RBAC);
l featureGates.RotateKubeletClientCertificate、featureGates.RotateKubeletServerCertificate:自動 rotate 證書,證書的有效期取決於 kube-controller-manager 的 --experimental-cluster-signing-duration 參數;
l 需要 root 賬戶運行;
為各節點創建和分發 kubelet 配置文件:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" sed -e "s/##NODE_IP##/${node_ip}/" kubelet-config.yaml.template > kubelet-config-${node_ip}.yaml.template scp kubelet-config-${node_ip}.yaml.template root@${node_ip}:/etc/kubernetes/kubelet-config.yaml done
2.6. 創建和分發 kubelet systemd unit 文件
創建 kubelet systemd unit 文件模板:
cd /opt/k8s/work cat > kubelet.service.template <<EOF [Unit] Description=Kubernetes Kubelet Documentation=https://github.com/GoogleCloudPlatform/kubernetes After=docker.service Requires=docker.service [Service] WorkingDirectory=${K8S_DIR}/kubelet ExecStart=/opt/k8s/bin/kubelet \\ --root-dir=${K8S_DIR}/kubelet \\ --bootstrap-kubeconfig=/etc/kubernetes/kubelet-bootstrap.kubeconfig \\ --cert-dir=/etc/kubernetes/cert \\ --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \\ --config=/etc/kubernetes/kubelet-config.yaml \\ --hostname-override=##NODE_NAME## \\ --pod-infra-container-image=xiaochunping/pause:3.1 --allow-privileged=true \\ --event-qps=0 \\ --kube-api-qps=1000 \\ --kube-api-burst=2000 \\ --registry-qps=0 \\ --image-pull-progress-deadline=30m \\ --logtostderr=true \\ --v=2 Restart=always RestartSec=5 StartLimitInterval=0 [Install] WantedBy=multi-user.target EOF
l 如果設置了 --hostname-override 選項,則 kube-proxy 也需要設置該選項,否則會出現找不到 Node 的情況;
l --bootstrap-kubeconfig:指向 bootstrap kubeconfig 文件,kubelet 使用該文件中的用戶名和 token 向 kube-apiserver 發送 TLS Bootstrapping 請求;
l K8S approve kubelet 的 csr 請求后,在 --cert-dir 目錄創建證書和私鑰文件,然后寫入 --kubeconfig 文件;
l --pod-infra-container-image 不使用 redhat 的 pod-infrastructure:latest 鏡像,它不能回收容器的僵屍;
為各節點創建和分發 kubelet systemd unit 文件:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_name in ${NODE_NAMES[@]} do echo ">>> ${node_name}" sed -e "s/##NODE_NAME##/${node_name}/" kubelet.service.template > kubelet-${node_name}.service scp kubelet-${node_name}.service root@${node_name}:/usr/lib/systemd/system/kubelet.service done
2.7. Bootstrap Token Auth 和授予權限
kublet 啟動時查找配置的 --kubeletconfig 文件是否存在,如果不存在則使用 --bootstrap-kubeconfig 向 kube-apiserver 發送證書簽名請求 (CSR)。
kube-apiserver 收到 CSR 請求后,對其中的 Token 進行認證(事先使用 kubeadm 創建的 token),認證通過后將請求的 user 設置為 system:bootstrap:<TokenID>,group 設置為 system:bootstrappers,這一過程稱為 Bootstrap Token Auth。
默認情況下,這個 user 和 group 沒有創建 CSR 的權限,kubelet 啟動失敗,錯誤日志如下:
sudo journalctl -u kubelet -a |grep -A 2 'certificatesigningrequests'
May 06 06:42:36 k8s-1 kubelet[26986]: F0506 06:42:36.314378 26986 server.go:233] failed to run Kubelet: cannot create certificate signing request: certificatesigningrequests.certificates.k8s.io is forbidden: User "system:bootstrap:lemy40" cannot create certificatesigningrequests.certificates.k8s.io at the cluster scope
May 06 06:42:36 k8s-1 systemd[1]: kubelet.service: Main process exited, code=exited, status=255/n/a
May 06 06:42:36 k8s-1 systemd[1]: kubelet.service: Failed with result 'exit-code'.
解決辦法是:創建一個 clusterrolebinding,將 group system:bootstrappers 和 clusterrole system:node-bootstrapper 綁定:
kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --group=system:bootstrappers
2.8. 啟動 kubelet 服務
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "mkdir -p ${K8S_DIR}/kubelet" ssh root@${node_ip} "/usr/sbin/swapoff -a" ssh root@${node_ip} "systemctl daemon-reload && systemctl enable kubelet && systemctl restart kubelet" done
l 必須創建工作目錄;
l 關閉 swap 分區,否則 kubelet 會啟動失敗;
journalctl -u kubelet |tail
3月 29 15:01:53 k8s-1 systemd[1]: Job kubelet.service/start failed with result 'dependency'.
3月 29 15:03:23 k8s-1 systemd[1]: Dependency failed for Kubernetes Kubelet.
3月 29 15:03:23 k8s-1 systemd[1]: Job kubelet.service/start failed with result 'dependency'.
-- Reboot --
3月 30 18:48:42 k8s-1 systemd[1]: Cannot add dependency job for unit kubelet.service, ignoring: Unit not found.
3月 31 14:41:17 k8s-1 systemd[1]: [/usr/lib/systemd/system/kubelet.service:10] Unknown lvalue '--allow-privileged' in section 'Service'
3月 31 14:41:17 k8s-1 systemd[1]: [/usr/lib/systemd/system/kubelet.service:10] Unknown lvalue '--allow-privileged' in section 'Service'
3月 31 14:41:17 k8s-1 systemd[1]: Started Kubernetes Kubelet.
3月 31 14:41:17 k8s-1 kubelet[29768]: I0331 14:41:17.880649 29768 server.go:408] Version: v1.12.3
3月 31 14:41:17 k8s-1 kubelet[29768]: I0331 14:41:17.881049 29768 plugins.go:99] No cloud provider specified.
kubelet 啟動后使用 --bootstrap-kubeconfig 向 kube-apiserver 發送 CSR 請求,當這個 CSR 被 approve 后,kube-controller-manager 為 kubelet 創建 TLS 客戶端證書、私鑰和 --kubeletconfig 文件。
注意:kube-controller-manager 需要配置 --cluster-signing-cert-file 和 --cluster-signing-key-file 參數,才會為 TLS Bootstrap 創建證書和私鑰。
kubectl get csr
NAME AGE REQUESTOR CONDITION
node-csr-7YSbJGsZGmAH5IrdOmJVcGDrUK3iGtFiDHuRMVdJk5M 22h system:bootstrap:f9isxw Pending
node-csr-IHHUpaUceWdFY7QPRw48ICdr_pyt1VC-KgLM9SfNWE8 23h system:bootstrap:8w1g0a Pending
node-csr-xqKuMm_MsfUGrMiwti1EaaJMsOpWwvlOQIT_N_3a02k 22h system:bootstrap:2b84b8
kubectl get nodes
No resources found.
l 三個 work 節點的 csr 均處於 pending 狀態;
2.9. 檢查服務運行狀態
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "systemctl status kubelet|grep Active" done
確保狀態為 active (running),否則查看日志,確認原因:
journalctl -u kubelet
2.10. approve kubelet CSR 請求
可以手動或自動 approve CSR 請求。推薦使用自動的方式,因為從 v1.8 版本開始,可以自動輪轉approve csr 后生成的證書。
2.10.1. 手動 approve CSR 請求
查看 SCR 列表:
kubectl get csr
NAME AGE REQUESTOR CONDITION
node-csr-7YSbJGsZGmAH5IrdOmJVcGDrUK3iGtFiDHuRMVdJk5M 22h system:bootstrap:f9isxw Pending
node-csr-IHHUpaUceWdFY7QPRw48ICdr_pyt1VC-KgLM9SfNWE8 23h system:bootstrap:8w1g0a Pending
node-csr-xqKuMm_MsfUGrMiwti1EaaJMsOpWwvlOQIT_N_3a02k 22h system:bootstrap:2b84b8
approve SCR:
kubectl certificate approve node-csr-7YSbJGsZGmAH5IrdOmJVcGDrUK3iGtFiDHuRMVdJk5M
certificatesigningrequest.certificates.k8s.io "node-csr-7YSbJGsZGmAH5IrdOmJVcGDrUK3iGtFiDHuRMVdJk5M" approved
查看 Approve 結果:
kubectl describe csr node-csr-7YSbJGsZGmAH5IrdOmJVcGDrUK3iGtFiDHuRMVdJk5M
2.10.2. 自動 approve CSR 請求
創建三個 ClusterRoleBinding,分別用於自動 approve client、renew client、renew server 證書:
cd /opt/k8s/work cat > csr-crb.yaml <<EOF # Approve all CSRs for the group "system:bootstrappers" kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: auto-approve-csrs-for-group subjects: - kind: Group name: system:bootstrappers apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: system:certificates.k8s.io:certificatesigningrequests:nodeclient apiGroup: rbac.authorization.k8s.io --- # To let a node of the group "system:nodes" renew its own credentials kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: node-client-cert-renewal subjects: - kind: Group name: system:nodes apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient apiGroup: rbac.authorization.k8s.io --- # A ClusterRole which instructs the CSR approver to approve a node requesting a # serving cert matching its client cert. kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: approve-node-server-renewal-csr rules: - apiGroups: ["certificates.k8s.io"] resources: ["certificatesigningrequests/selfnodeserver"] verbs: ["create"] --- # To let a node of the group "system:nodes" renew its own server credentials kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: node-server-cert-renewal subjects: - kind: Group name: system:nodes apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: approve-node-server-renewal-csr apiGroup: rbac.authorization.k8s.io EOF
l auto-approve-csrs-for-group:自動 approve node 的第一次 CSR; 注意第一次 CSR 時,請求的 Group 為 system:bootstrappers;
l node-client-cert-renewal:自動 approve node 后續過期的 client 證書,自動生成的證書 Group 為 system:nodes;
l node-server-cert-renewal:自動 approve node 后續過期的 server 證書,自動生成的證書 Group 為 system:nodes;
生效配置:
kubectl apply -f csr-crb.yaml
clusterrolebinding.rbac.authorization.k8s.io/auto-approve-csrs-for-group created
clusterrolebinding.rbac.authorization.k8s.io/node-client-cert-renewal created
clusterrole.rbac.authorization.k8s.io/approve-node-server-renewal-csr created
clusterrolebinding.rbac.authorization.k8s.io/node-server-cert-renewal created
2.11. 查看 kublet 的情況
等待一段時間(1-10 分鍾),三個節點的 CSR 都被自動 approved:
kubectl get csr
NAME AGE REQUESTOR CONDITION
node-csr--BjlTzxB5Y4op_6wYlDKbbQj1NtX-IOBMLmWhkupEWA 4m system:bootstrap:8galm1 Approved,Issued
node-csr-a68FhmUgprTJkaLwnJOLQLOkDQuAviDdBy91ByVtWt0 4m system:bootstrap:4ef7hj Approved,Issued
node-csr-a7DI6d0QjBiPh58IBGYFPUKAZvKs6sfbqlnoc22erRs 4m system:bootstrap:ai162m Approved,Issued
l Pending 的 CSR 用於創建 kubelet server 證書,需要手動 approve,下文介紹。
所有節點均 ready:
kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready <none> 2m58s v1.12.3
node1 Ready <none> 2m54s v1.12.3
node2 Ready <none> 2m56s v1.12.3
kube-controller-manager 為各 node 生成了 kubeconfig 文件和公私鑰:
ls -l /etc/kubernetes/kubelet.kubeconfig
-rw------- 1 root root 2298 3月 28 17:45 /etc/kubernetes/kubelet.kubeconfig
ls -l /etc/kubernetes/cert/|grep kubelet
-rw------- 1 root root 1265 3月 28 17:45 kubelet-client-2019-03-28-17-45-48.pem
lrwxrwxrwx 1 root root 58 3月 28 17:45 kubelet-client-current.pem -> /etc/kubernetes/cert/kubelet-client-2019-03-28-17-45-48.pem
l 沒有自動生成 kubelet server 證書;
2.12. 手動 approve server cert csr
基於安全性考慮,CSR approving controllers 默認不會自動 approve kubelet server 證書簽名請求,需要手動 approve。
kubectl get csr
NAME AGE REQUESTOR CONDITION
csr-42fr8 7m45s system:node:master Pending
csr-5gwjj 7m43s system:node:node2 Pending
csr-klkcp 7m41s system:node:node1 Pending
node-csr-7YSbJGsZGmAH5IrdOmJVcGDrUK3iGtFiDHuRMVdJk5M 23h system:bootstrap:f9isxw Approved,Issued
node-csr-IHHUpaUceWdFY7QPRw48ICdr_pyt1VC-KgLM9SfNWE8 24h system:bootstrap:8w1g0a Approved,Issued
node-csr-xqKuMm_MsfUGrMiwti1EaaJMsOpWwvlOQIT_N_3a02k 23h system:bootstrap:2b84b8 Approved,Issued
kubectl certificate approve csr-42fr8 kubectl certificate approve csr-5gwjj kubectl certificate approve csr-klkcp
certificatesigningrequest.certificates.k8s.io/csr-42fr8 approved
certificatesigningrequest.certificates.k8s.io/csr-5gwjj approved
certificatesigningrequest.certificates.k8s.io/csr-klkcp approved
ls -l /etc/kubernetes/cert/kubelet-*
-rw------- 1 root root 1265 3月 28 17:45 /etc/kubernetes/cert/kubelet-client-2019-03-28-17-45-48.pem
lrwxrwxrwx 1 root root 58 3月 28 17:45 /etc/kubernetes/cert/kubelet-client-current.pem -> /etc/kubernetes/cert/kubelet-client-2019-03-28-17-45-48.pem
-rw------- 1 root root 1301 3月 28 17:58 /etc/kubernetes/cert/kubelet-server-2019-03-28-17-58-23.pem
lrwxrwxrwx 1 root root 58 3月 28 17:58 /etc/kubernetes/cert/kubelet-server-current.pem -> /etc/kubernetes/cert/kubelet-server-2019-03-28-17-58-23.pem
2.13. kubelet 提供的 API 接口
kublet 啟動后監聽多個端口,用於接收 kube-apiserver 或其它組件發送的請求:
sudo netstat -lnpt|grep kubelet
tcp 0 0 192.168.100.246:10250 0.0.0.0:* LISTEN 8958/kubelet
tcp 0 0 127.0.0.1:41848 0.0.0.0:* LISTEN 8958/kubelet
tcp 0 0 127.0.0.1:10248 0.0.0.0:* LISTEN 8958/kubelet
l 10248: healthz http 服務;
l 10250: https API 服務;注意:未開啟只讀端口 10255;
例如執行 kubectl exec -it nginx-ds-5rmws -- sh 命令時,kube-apiserver 會向 kubelet 發送如下請求:
POST /exec/default/nginx-ds-5rmws/my-nginx?command=sh&input=1&output=1&tty=1
kubelet 接收 10250 端口的 https 請求:
l /pods、/runningpods
l /metrics、/metrics/cadvisor、/metrics/probes
l /spec
l /stats、/stats/container
l /logs
l /run/、"/exec/", "/attach/", "/portForward/", "/containerLogs/" 等管理;
詳情參考:https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/server/server.go#L434:3
由於關閉了匿名認證,同時開啟了 webhook 授權,所有訪問 10250 端口 https API 的請求都需要被認證和授權。
預定義的 ClusterRole system:kubelet-api-admin 授予訪問 kubelet 所有 API 的權限(kube-apiserver 使用的 kubernetes 證書 User 授予了該權限):
kubectl describe clusterrole system:kubelet-api-admin
Name: system:kubelet-api-admin
Labels: kubernetes.io/bootstrapping=rbac-defaults
Annotations: rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
nodes/log [] [] [*]
nodes/metrics [] [] [*]
nodes/proxy [] [] [*]
nodes/spec [] [] [*]
nodes/stats [] [] [*]
nodes [] [] [get list watch proxy]
2.14. kublet api 認證和授權
kublet 配置了如下認證參數:
l authentication.anonymous.enabled:設置為 false,不允許匿名訪問 10250 端口;
l authentication.x509.clientCAFile:指定簽名客戶端證書的 CA 證書,開啟 HTTPs 證書認證;
l authentication.webhook.enabled=true:開啟 HTTPs bearer token 認證;
同時配置了如下授權參數:
l authroization.mode=Webhook:開啟 RBAC 授權;
kubelet 收到請求后,使用 clientCAFile 對證書簽名進行認證,或者查詢 bearer token 是否有效。如果兩者都沒通過,則拒絕請求,提示 Unauthorized:
curl -s --cacert /etc/kubernetes/cert/ca.pem https://192.168.100.246:10250/metrics
Unauthorized
curl -s --cacert /etc/kubernetes/cert/ca.pem -H "Authorization: Bearer 123456" https://192.168.100.246:10250/metrics
Unauthorized
通過認證后,kubelet 使用 SubjectAccessReview API 向 kube-apiserver 發送請求,查詢證書或 token 對應的 user、group 是否有操作資源的權限(RBAC);
2.14.1. 證書認證和授權
# 權限不足的證書
sudo curl -s --cacert /etc/kubernetes/cert/ca.pem --cert /etc/kubernetes/cert/kube-controller-manager.pem --key /etc/kubernetes/cert/kube-controller-manager-key.pem https://192.168.100.246:10250/metrics
Forbidden (user=system:kube-controller-manager, verb=get, resource=nodes, subresource=metrics)
# 使用部署 kubectl 命令行工具時創建的、具有最高權限的 admin 證書;
sudo curl -s --cacert /etc/kubernetes/cert/ca.pem --cert /opt/k8s/work/cert/admin.pem --key /opt/k8s/work/cert/admin-key.pem https://192.168.100.246:10250/metrics|head
# HELP apiserver_client_certificate_expiration_seconds Distribution of the remaining lifetime on the certificate used to authenticate a request.
# TYPE apiserver_client_certificate_expiration_seconds histogram
apiserver_client_certificate_expiration_seconds_bucket{le="0"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="21600"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="43200"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="86400"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="172800"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="345600"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="604800"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="2.592e+06"} 0
l --cacert、--cert、--key 的參數值必須是文件路徑,如上面的 ./admin.pem 不能省略 ./,否則返回 401 Unauthorized;
2.14.2. bear token 認證和授權
創建一個 ServiceAccount,將它和 ClusterRole system:kubelet-api-admin 綁定,從而具有調用 kubelet API 的權限:
kubectl create sa kubelet-api-test kubectl create clusterrolebinding kubelet-api-test --clusterrole=system:kubelet-api-admin --serviceaccount=default:kubelet-api-test SECRET=$(kubectl get secrets | grep kubelet-api-test | awk '{print $1}') TOKEN=$(kubectl describe secret ${SECRET} | grep -E '^token' | awk '{print $2}') echo ${TOKEN} curl -s --cacert /etc/kubernetes/cert/ca.pem -H "Authorization: Bearer ${TOKEN}" https://172.26.106.83:10250/metrics|head
# HELP apiserver_client_certificate_expiration_seconds Distribution of the remaining lifetime on the certificate used to authenticate a request.
# TYPE apiserver_client_certificate_expiration_seconds histogram
apiserver_client_certificate_expiration_seconds_bucket{le="0"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="21600"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="43200"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="86400"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="172800"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="345600"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="604800"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="2.592e+06"} 0
2.14.3. cadvisor 和 metrics
cadvisor 統計所在節點各容器的資源(CPU、內存、磁盤、網卡)使用情況,分別在自己的 http web 頁面(4194 端口)和 10250 以 promehteus metrics 的形式輸出。
瀏覽器訪問 :
https://192.168.100.246:10250/metrics 和
https://192.168.100.246:10250/metrics/cadvisor
分別返回 kublet 和 metrics 和 cadvisor 。
注意:
l kublet.config.json 設置 authentication.anonymous.enabled 為 false,不允許匿名證書訪問 10250 的 https 服務;
l 參考 瀏覽器訪問kube-apiserver安全端口,創建和導入相關證書,然后訪問上面的 10250 端口;
2.15. 獲取 kublet 的配置
從 kube-apiserver 獲取各 node 的配置:
# 使用部署 kubectl 命令行工具時創建的、具有最高權限的 admin 證書;
sudo curl -sSL --cacert /etc/kubernetes/cert/ca.pem --cert /opt/k8s/work/cert/admin.pem --key /opt/k8s/work/cert/admin-key.pem ${KUBE_APISERVER}/api/v1/nodes/master/proxy/configz | jq '.kubeletconfig|.kind="KubeletConfiguration"|.apiVersion="kubelet.config.k8s.io/v1beta1"'
{
"syncFrequency": "1m0s",
"fileCheckFrequency": "20s",
"httpCheckFrequency": "20s",
"address": "192.168.100.246",
"port": 10250,
"rotateCertificates": true,
"serverTLSBootstrap": true,
"authentication": {
"x509": {
"clientCAFile": "/etc/kubernetes/cert/ca.pem"
},
"webhook": {
"enabled": true,
"cacheTTL": "2m0s"
},
"anonymous": {
"enabled": false
}
},
"authorization": {
"mode": "Webhook",
"webhook": {
"cacheAuthorizedTTL": "5m0s",
"cacheUnauthorizedTTL": "30s"
}
},
"registryPullQPS": 0,
"registryBurst": 10,
"eventRecordQPS": 0,
"eventBurst": 10,
"enableDebuggingHandlers": true,
"healthzPort": 10248,
"healthzBindAddress": "127.0.0.1",
"oomScoreAdj": -999,
"clusterDomain": "cluster.local",
"clusterDNS": [
"10.254.0.2"
],
"streamingConnectionIdleTimeout": "4h0m0s",
"nodeStatusUpdateFrequency": "10s",
"nodeLeaseDurationSeconds": 40,
"imageMinimumGCAge": "2m0s",
"imageGCHighThresholdPercent": 85,
"imageGCLowThresholdPercent": 80,
"volumeStatsAggPeriod": "1m0s",
"cgroupsPerQOS": true,
"cgroupDriver": "cgroupfs",
"cpuManagerPolicy": "none",
"cpuManagerReconcilePeriod": "10s",
"runtimeRequestTimeout": "15m0s",
"hairpinMode": "promiscuous-bridge",
"maxPods": 220,
"podCIDR": "172.30.0.0/16",
"podPidsLimit": -1,
"resolvConf": "/etc/resolv.conf",
"cpuCFSQuota": true,
"cpuCFSQuotaPeriod": "100ms",
"maxOpenFiles": 1000000,
"contentType": "application/vnd.kubernetes.protobuf",
"kubeAPIQPS": 1000,
"kubeAPIBurst": 2000,
"serializeImagePulls": false,
"evictionHard": {
"imagefs.available": "15%",
"memory.available": "100Mi",
"nodefs.available": "10%",
"nodefs.inodesFree": "5%"
},
"evictionPressureTransitionPeriod": "5m0s",
"enableControllerAttachDetach": true,
"makeIPTablesUtilChains": true,
"iptablesMasqueradeBit": 14,
"iptablesDropBit": 15,
"failSwapOn": true,
"containerLogMaxSize": "10Mi",
"containerLogMaxFiles": 5,
"configMapAndSecretChangeDetectionStrategy": "Watch",
"enforceNodeAllocatable": [
"pods"
],
"kind": "KubeletConfiguration",
"apiVersion": "kubelet.config.k8s.io/v1beta1"
}
或者參考代碼中的注釋:
https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/apis/kubeletconfig/v1beta1/types.go
2.16. 參考
kubelet 認證和授權:
3. 部署 kube-proxy 組件
kube-proxy 運行在所有 worker 節點上,,它監聽 apiserver 中 service 和 Endpoint 的變化情況,創建路由規則來進行服務負載均衡。
本文檔講解部署 kube-proxy 的部署,使用 ipvs 模式。
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
3.1. 下載和分發 kube-proxy 二進制文件
參考 部署master節點
3.2. 安裝依賴包
各節點需要安裝 ipvsadm 和 ipset 命令,加載 ip_vs 內核模塊。
參考 部署worker節點
3.3. 創建 kube-proxy 證書
創建證書簽名請求:
cd /opt/k8s/work/cert cat > kube-proxy-csr.json <<EOF { "CN": "system:kube-proxy", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "study163" } ] } EOF
l CN:指定該證書的 User 為 system:kube-proxy;
l 預定義的 RoleBinding system:node-proxier 將User system:kube-proxy 與 Role system:node-proxier 綁定,該 Role 授予了調用 kube-apiserver Proxy 相關 API 的權限;
l 該證書只會被 kube-proxy 當做 client 證書使用,所以 hosts 字段為空;
生成證書和私鑰:
cd /opt/k8s/work/cert cfssl gencert -ca=/opt/k8s/work/cert/ca.pem \ -ca-key=/opt/k8s/work/cert/ca-key.pem \ -config=/opt/k8s/work/cert/ca-config.json \ -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy ls kube-proxy*
3.4. 創建和分發 kubeconfig 文件
cd /opt/k8s/work source /opt/k8s/bin/environment.sh kubectl config set-cluster kubernetes \ --certificate-authority=/opt/k8s/work/cert/ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=kube-proxy.kubeconfig kubectl config set-credentials kube-proxy \ --client-certificate=/opt/k8s/work/cert/kube-proxy.pem \ --client-key=/opt/k8s/work/cert/kube-proxy-key.pem \ --embed-certs=true \ --kubeconfig=kube-proxy.kubeconfig kubectl config set-context default \ --cluster=kubernetes \ --user=kube-proxy \ --kubeconfig=kube-proxy.kubeconfig kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
l --embed-certs=true:將 ca.pem 和 admin.pem 證書內容嵌入到生成的 kubectl-proxy.kubeconfig 文件中(不加時,寫入的是證書文件路徑);
分發 kubeconfig 文件:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_name in ${NODE_NAMES[@]} do echo ">>> ${node_name}" scp kube-proxy.kubeconfig root@${node_name}:/etc/kubernetes/ done
3.5. 創建 kube-proxy 配置文件
從 v1.10 開始,kube-proxy 部分參數可以配置文件中配置。可以使用 --write-config-to 選項生成該配置文件,或者參考 kubeproxyconfig 的類型定義源文件
創建 kube-proxy config 文件模板:
cd /opt/k8s/work cat <<EOF | tee kube-proxy-config.yaml.template kind: KubeProxyConfiguration apiVersion: kubeproxy.config.k8s.io/v1alpha1 clientConnection: kubeconfig: "/etc/kubernetes/kube-proxy.kubeconfig" bindAddress: ##NODE_IP## clusterCIDR: ${CLUSTER_CIDR} healthzBindAddress: ##NODE_IP##:10256 hostnameOverride: ##NODE_NAME## metricsBindAddress: ##NODE_IP##:10249 mode: "ipvs" EOF
l bindAddress: 監聽地址;
l clientConnection.kubeconfig: 連接 apiserver 的 kubeconfig 文件;
l clusterCIDR: kube-proxy 根據 --cluster-cidr 判斷集群內部和外部流量,指定 --cluster-cidr 或 --masquerade-all 選項后 kube-proxy 才會對訪問 Service IP 的請求做 SNAT;
l hostnameOverride: 參數值必須與 kubelet 的值一致,否則 kube-proxy 啟動后會找不到該 Node,從而不會創建任何 ipvs 規則;
l mode: 使用 ipvs 模式;
為各節點創建和分發 kube-proxy 配置文件:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for (( i=0; i < 3; i++ )) do echo ">>> ${NODE_NAMES[i]}" sed -e "s/##NODE_NAME##/${NODE_NAMES[i]}/" -e "s/##NODE_IP##/${NODE_IPS[i]}/" kube-proxy-config.yaml.template > kube-proxy-config-${NODE_NAMES[i]}.yaml.template scp kube-proxy-config-${NODE_NAMES[i]}.yaml.template root@${NODE_NAMES[i]}:/etc/kubernetes/kube-proxy-config.yaml done
3.6. 創建和分發 kube-proxy systemd unit 文件
cd /opt/k8s/work cat > kube-proxy.service <<EOF [Unit] Description=Kubernetes Kube-Proxy Server Documentation=https://github.com/GoogleCloudPlatform/kubernetes After=network.target [Service] WorkingDirectory=/data/k8s/k8s/kube-proxy ExecStart=/opt/k8s/bin/kube-proxy \\ --config=/etc/kubernetes/kube-proxy-config.yaml \\ --logtostderr=true \\ --v=2 Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
分發 kube-proxy systemd unit 文件:
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_name in ${NODE_NAMES[@]} do echo ">>> ${node_name}" scp kube-proxy.service root@${node_name}:/usr/lib/systemd/system/ done
3.7. 啟動 kube-proxy 服務
cd /opt/k8s/work source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "mkdir -p ${K8S_DIR}/kube-proxy" ssh root@${node_ip} "systemctl daemon-reload && systemctl enable kube-proxy && systemctl restart kube-proxy" done
l 必須先創建工作目錄;
3.8. 檢查啟動結果
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "systemctl status kube-proxy|grep Active" done
確保狀態為 active (running),否則查看日志,確認原因:
journalctl -u kube-proxy
3.9. 查看監聽端口和 metrics
sudo netstat -lnpt|grep kube-prox
tcp 0 0 192.168.100.246:10256 0.0.0.0:* LISTEN 20482/kube-proxy
tcp 0 0 192.168.100.246:10249 0.0.0.0:* LISTEN 20482/kube-proxy
l 10249:http prometheus metrics port;
l 10256:http healthz port;
3.10. 查看 ipvs 路由規則
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh root@${node_ip} "/usr/sbin/ipvsadm -ln" done
預期輸出:
>>> 192.168.100.246
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.254.0.1:443 rr
-> 192.168.100.246:6443 Masq 1 0 0
-> 192.168.100.247:6443 Masq 1 0 0
-> 192.168.100.248:6443 Masq 1 0 0
>>> 192.168.100.247
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.254.0.1:443 rr
-> 192.168.100.246:6443 Masq 1 0 0
-> 192.168.100.247:6443 Masq 1 0 0
-> 192.168.100.248:6443 Masq 1 0 0
>>> 192.168.100.248
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.254.0.1:443 rr
-> 192.168.100.246:6443 Masq 1 0 0
-> 192.168.100.247:6443 Masq 1 0 0
-> 192.168.100.248:6443 Masq 1 0 0
可見將所有到 kubernetes cluster ip 443 端口的請求都轉發到 kube-apiserver 的 6443 端口;
4.12 驗證集群功能
本文檔使用 daemonset 驗證 master 和 worker 節點是否工作正常。
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行,然后遠程分發文件和執行命令。
1. 檢查節點狀態
kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready <none> 4h35m v1.12.3
node1 Ready <none> 4h35m v1.12.3
node2 Ready <none> 4h35m v1.12.3
都為 Ready 時正常。
2. 創建測試文件
cd /opt/k8s/work cat > nginx-ds.yml <<EOF apiVersion: v1 kind: Service metadata: name: nginx-ds labels: app: nginx-ds spec: type: NodePort selector: app: nginx-ds ports: - name: http port: 80 targetPort: 80 --- apiVersion: extensions/v1beta1 kind: DaemonSet metadata: name: nginx-ds labels: addonmanager.kubernetes.io/mode: Reconcile spec: template: metadata: labels: app: nginx-ds spec: containers: - name: my-nginx image: nginx:1.7.9 ports: - containerPort: 80 EOF
3. 執行定義文件
kubectl create -f nginx-ds.yml
4. 檢查各 Node 上的 Pod IP 連通性
kubectl get pods -o wide|grep nginx-ds
nginx-ds-69hp5 1/1 Running 0 2m19s 172.30.8.5 node1 <none>
nginx-ds-rm744 1/1 Running 0 2m19s 172.30.48.7 master <none>
nginx-ds-vpzk2 1/1 Running 0 2m19s 172.30.144.6 node2 <none>
可見,nginx-ds 的 Pod IP 分別是 172.30.8.5、172.30.48.7、172.30.144.6,在所有 Node 上分別 ping 這三個 IP,看是否連通:
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh ${node_ip} "ping -c 1 172.30.8.5" ssh ${node_ip} "ping -c 1 172.30.48.7" ssh ${node_ip} "ping -c 1 172.30.144.6" done
5. 檢查服務 IP 和端口可達性
kubectl get svc |grep nginx-ds
nginx-ds NodePort 10.254.242.170 <none> 80:30437/TCP 5m
可見:
l Service Cluster IP:10.254.242.170
l 服務端口:80
l NodePort 端口:30437
在所有 Node 上 curl Service IP:
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh ${node_ip} "curl 10.254.242.170" done
預期輸出 nginx 歡迎頁面內容。
6. 檢查服務的 NodePort 可達性
在所有 Node 上執行:
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh ${node_ip} "curl ${node_ip}:30437" done
預期輸出 nginx 歡迎頁面內容。
service ip(虛擬ip):
nodeport(虛擬ip):物理ip:nodeport虛擬端口
pod:fuent設置的網段的ip
五、部署集群插件
可視化自動化工具、日志收集
插件是集群的附件組件,豐富和完善了集群的功能。下面章節我們來學習一些常用的插件:
EFK (elasticsearch、fluentd、kibana)
5.1 dns 插件
特別重要:
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh ${node_ip} "docker pull coredns/coredns:1.2.2 && docker tag coredns/coredns:1.2.2 k8s.gcr.io/coredns:1.2.2" done
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行。
5.1.1. 修改配置文件
將下載的 kubernetes-server-linux-amd64.tar.gz 解壓后,再解壓其中的 kubernetes-src.tar.gz 文件。
coredns 對應的目錄是:cluster/addons/dns。
cd /opt/k8s/work/kubernetes/cluster/addons/dns/coredns cp coredns.yaml.base coredns.yaml source /opt/k8s/bin/environment.sh sed -i -e "s/__PILLAR__DNS__DOMAIN__/${CLUSTER_DNS_DOMAIN}/" -e "s/__PILLAR__DNS__SERVER__/${CLUSTER_DNS_SVC_IP}/" coredns.yaml
5.1.2. 插件 coredns
kubectl create -f coredns.yaml
5.1.3. 檢查 coredns 功能
kubectl get all -n kube-system
NAME READY STATUS RESTARTS AGE
pod/coredns-779ffd89bd-v75k9 0/1 ImagePullBackOff 0 4m54s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kube-dns ClusterIP 10.254.0.2 <none> 53/UDP,53/TCP 4m54s
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deployment.apps/coredns 1 1 1 0 4m54s
NAME DESIRED CURRENT READY AGE
replicaset.apps/coredns-779ffd89bd 1 1 0 4m54sa
新建一個 Deployment
cd /opt/k8s/work cat > my-nginx.yaml <<EOF apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-nginx spec: replicas: 2 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: nginx:1.7.9 ports: - containerPort: 80 EOF kubectl create -f my-nginx.yaml
Export 該 Deployment, 生成 my-nginx 服務:
kubectl expose deploy my-nginx
service "my-nginx" exposed
kubectl get services --all-namespaces |grep my-nginx
default my-nginx ClusterIP 10.254.206.78 <none> 80/TCP 7s
創建另一個 Pod,查看 /etc/resolv.conf 是否包含 kubelet 配置的 --cluster-dns 和 --cluster-domain,是否能夠將服務 my-nginx 解析到上面顯示的 Cluster IP 10.254.206.78
cd /opt/k8s/work cat > dnsutils-ds.yml <<EOF apiVersion: v1 kind: Service metadata: name: dnsutils-ds labels: app: dnsutils-ds spec: type: NodePort selector: app: dnsutils-ds ports: - name: http port: 80 targetPort: 80 --- apiVersion: extensions/v1beta1 kind: DaemonSet metadata: name: dnsutils-ds labels: addonmanager.kubernetes.io/mode: Reconcile spec: template: metadata: labels: app: dnsutils-ds spec: containers: - name: my-dnsutils image: tutum/dnsutils:latest command: - sleep - "3600" ports: - containerPort: 80 EOF kubectl create -f dnsutils-ds.yml
查看 pod 節點:
kubectl get pods kubectl exec dnsutils-ds-9rm5v nslookup kubernetes
Server: 10.254.0.2
Address: 10.254.0.2#53
Name: kubernetes.default.svc.cluster.local
Address: 10.254.0.1
kubectl exec dnsutils-ds-9rm5v nslookup www.baidu.com # 解析外部域名時,需要以 . 結尾
Server: 10.254.0.2
Address: 10.254.0.2#53
Non-authoritative answer:
*** Can't find www.baidu.com: No answer
kubectl exec dnsutils-ds-9rm5v nslookup www.baidu.com.
Server: 10.254.0.2
Address: 10.254.0.2#53
Non-authoritative answer:
www.baidu.com canonical name = www.a.shifen.com.
Name: www.a.shifen.com
Address: 61.135.169.125
Name: www.a.shifen.com
Address: 61.135.169.121
kubectl exec dnsutils-ds-9rm5v nslookup my-nginx
Server: 10.254.0.2
Address: 10.254.0.2#53
Name: my-nginx.default.svc.cluster.local
Address: 10.254.229.163
kubectl exec dnsutils-ds-9rm5v nslookup kube-dns.kube-system.svc.cluster
Server: 10.254.0.2
Address: 10.254.0.2#53
Non-authoritative answer:
*** Can't find kube-dns.kube-system.svc.cluster: No answer
kubectl exec dnsutils-ds-9rm5v nslookup kube-dns.kube-system.svc
Server: 10.254.0.2
Address: 10.254.0.2#53
Name: kube-dns.kube-system.svc.cluster.local
Address: 10.254.0.2
kubectl exec dnsutils-ds-9rm5v nslookup kube-dns.kube-system.svc.cluster.local
Server: 10.254.0.2
Address: 10.254.0.2#53
Non-authoritative answer:
*** Can't find kube-dns.kube-system.svc.cluster.local: No answer
kubectl exec dnsutils-ds-9rm5v nslookup kube-dns.kube-system.svc.cluster.local.
Server: 10.254.0.2
Address: 10.254.0.2#53
Name: kube-dns.kube-system.svc.cluster.local
Address: 10.254.0.2
5.2 dashboard 插件
特別重要:
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh ${node_ip} "docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kubernetes-dashboard-amd64:v1.8.3 && docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kubernetes-dashboard-amd64:v1.8.3 k8s.gcr.io/kubernetes-dashboard-amd64:v1.8.3" done
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行。
修改配置文件
將下載的 kubernetes-server-linux-amd64.tar.gz 解壓后,再解壓其中的 kubernetes-src.tar.gz 文件。
dashboard 對應的目錄是:cluster/addons/dashboard。
cd /opt/k8s/work/kubernetes/cluster/addons/dashboard
5.2.1. 修改配置文件
cat dashboard-service.yaml apiVersion: v1 kind: Service metadata: name: kubernetes-dashboard namespace: kube-system labels: k8s-app: kubernetes-dashboard kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile spec: type: NodePort # 增加這一行 selector: k8s-app: kubernetes-dashboard ports: - port: 443 targetPort: 8443
l 指定端口類型為 NodePort,這樣外界可以通過地址 nodeIP:nodePort 訪問 dashboard;
5.2.2. 執行所有定義文件
$ ls *.yaml
dashboard-configmap.yaml dashboard-controller.yaml dashboard-rbac.yaml dashboard-secret.yaml dashboard-service.yaml $ kubectl create -f .
5.2.3. 查看分配的 NodePort
$ kubectl get deployment kubernetes-dashboard -n kube-system
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kubernetes-dashboard 1 1 1 1 2m
$ kubectl --namespace kube-system get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
coredns-77cd44d8df-4fmfc 1/1 Running 0 1h 172.30.200.2 node2 <none>
kubernetes-dashboard-69db8c7745-jtvpj 1/1 Running 0 55s 172.30.112.3 master <none>
$ kubectl get services kubernetes-dashboard -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes-dashboard NodePort 10.254.9.198 <none> 443:32014/TCP 1m
l NodePort 32014 映射到 dashboard pod 443 端口;
dashboard 的 --authentication-mode 支持 token、basic,默認為 token。如果使用 basic,則 kube-apiserver 必須配置 '--authorization-mode=ABAC' 和 '--basic-auth-file' 參數。
5.2.4. 查看 dashboard 支持的命令行參數
kubectl exec --namespace kube-system -it kubernetes-dashboard-659798bd99-x5j6d -- /dashboard --help # kubernetes-dashboard-659798bd99-x5j6d 為 pod 名稱
2019/04/01 13:12:03 Starting overwatch
Usage of /dashboard:
--alsologtostderr log to standard error as well as files
--apiserver-host string The address of the Kubernetes Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8080. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and local discovery is attempted.
--authentication-mode stringSlice Enables authentication options that will be reflected on login screen. Supported values: token, basic. Default: token.Note that basic option should only be used if apiserver has '--authorization-mode=ABAC' and '--basic-auth-file' flags set. (default [token])
--auto-generate-certificates When set to true, Dashboard will automatically generate certificates used to serve HTTPS. Default: false.
--bind-address ip The IP address on which to serve the --secure-port (set to 0.0.0.0 for all interfaces). (default 0.0.0.0)
--default-cert-dir string Directory path containing '--tls-cert-file' and '--tls-key-file' files. Used also when auto-generating certificates flag is set. (default "/certs")
--disable-settings-authorizer When enabled, Dashboard settings page will not require user to be logged in and authorized to access settings page.
--enable-insecure-login When enabled, Dashboard login view will also be shown when Dashboard is not served over HTTPS. Default: false.
--heapster-host string The address of the Heapster Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8082. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and service proxy will be used.
--insecure-bind-address ip The IP address on which to serve the --port (set to 0.0.0.0 for all interfaces). (default 127.0.0.1)
--insecure-port int The port to listen to for incoming HTTP requests. (default 9090)
--kubeconfig string Path to kubeconfig file with authorization and master location information.
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory
--logtostderr log to standard error instead of files
--metric-client-check-period int Time in seconds that defines how often configured metric client health check should be run. Default: 30 seconds. (default 30)
--port int The secure port to listen to for incoming HTTPS requests. (default 8443)
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
--system-banner string When non-empty displays message to Dashboard users. Accepts simple HTML tags. Default: ''.
--system-banner-severity string Severity of system banner. Should be one of 'INFO|WARNING|ERROR'. Default: 'INFO'. (default "INFO")
--tls-cert-file string File containing the default x509 Certificate for HTTPS.
--tls-key-file string File containing the default x509 private key matching --tls-cert-file.
--token-ttl int Expiration time (in seconds) of JWE tokens generated by dashboard. Default: 15 min. 0 - never expires (default 900)
-v, --v Level log level for V logs
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
command terminated with exit code 2
5.2.5. 訪問 dashboard
為了集群安全,從 1.7 開始,dashboard 只允許通過 https 訪問,如果使用 kube proxy 則必須監聽 localhost 或 127.0.0.1,對於 NodePort 沒有這個限制,但是僅建議在開發環境中使用。
對於不滿足這些條件的登錄訪問,在登錄成功后瀏覽器不跳轉,始終停在登錄界面。
參考: https://github.com/kubernetes/dashboard/wiki/Accessing-Dashboard---1.7.X-and-above https://github.com/kubernetes/dashboard/issues/2540
- kubernetes-dashboard 服務暴露了 NodePort,可以使用 https://NodeIP:NodePort 地址訪問 dashboard;
- 通過 kube-apiserver 訪問 dashboard;
- 通過 kubectl proxy 訪問 dashboard:
如果使用了 VirtualBox,需要啟用 VirtualBox 的 ForworadPort 功能將虛機監聽的端口和 Host 的本地端口綁定。
1. 通過 kubectl proxy 訪問 dashboard
啟動代理:
kubectl proxy --address='localhost' --port=8086 --accept-hosts='^*$'
Starting to serve on 127.0.0.1:8086
l --address 必須為 localhost 或 127.0.0.1;
l 需要指定 --accept-hosts 選項,否則瀏覽器訪問 dashboard 頁面時提示 “Unauthorized”;
瀏覽器訪問 URL:
http://47.92.173.105:8086/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy
2.5.2. 通過 kube-apiserver 訪問 dashboard
獲取集群服務地址列表:
kubectl cluster-info
Kubernetes master is running at https://192.168.100.246:8443
CoreDNS is running at https://192.168.100.246:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
kubernetes-dashboard is running at https://192.168.100.246:8443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
l 由於 apiserver 通過本地的 kube-nginx 做了代理,所以上面顯示的 127.0.0.1:8443 為本地的 kube-nginx 的 IP 和 Port,瀏覽器訪問時需要替換為 kube-apiserver 實際監聽的 IP 和端口,如 192.168.100.246:6443;
l 必須通過 kube-apiserver 的安全端口(https)訪問 dashbaord,訪問時瀏覽器需要使用自定義證書,否則會被 kube-apiserver 拒絕訪問。
l 創建和導入自定義證書的步驟,參考:瀏覽器訪問kube-apiserver安全端口
瀏覽器訪問 URL:
https://192.168.100.246:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy 對於 virtuabox 做了端口映射:
http://127.0.0.1:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/
5.2.6. 創建登錄 dashboard 的 token 和 kubeconfig 配置文件
上面提到,Dashboard 默認只支持 token 認證,所以如果使用 KubeConfig 文件,需要在該文件中指定 token,不支持使用 client 證書認證。
5.2.7. 創建登錄 token
kubectl create sa dashboard-admin -n kube-system kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin ADMIN_SECRET=$(kubectl get secrets -n kube-system | grep dashboard-admin | awk '{print $1}') DASHBOARD_LOGIN_TOKEN=$(kubectl describe secret -n kube-system ${ADMIN_SECRET} | grep -E '^token' | awk '{print $2}') echo ${DASHBOARD_LOGIN_TOKEN}
eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtYWRtaW4tdG9rZW4tOHE4bWoiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkLWFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiMWU4NmM2ZmMtNTZkNy0xMWU5LWE5Y2EtMDAwYzI5NWJhMDYzIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZC1hZG1pbiJ9.CKm0cnBGmKvW19TdLWprxHqSAMYClIDRxfvMagA4_J_ogi2QGXQGWVcd9n0rV_QL_s98E1I3A2399aK-FJTBAw5ZyrwiyNVYYa8aSS3ROI7zyKr1xcubGCgvdKGv1WETTPjH6xcYGXhtdR0P6MReuqft6wX0ZucjmlaRtMjpEba_N4YmLG_YOI-qjziAqgE1vv9NU6DjWM8Enyh6cN6CzeI3qtEkt25PPFfE3BsGVMbpnkGyBgcJC-HKd_VafAxSYaG1xICxgDGPWc5PiMIq7sQSFbX4wZAnBIozFG8IBtzgxrFhSdaJS_MJROm5gYZf5mRezAJf-QRovX-e2xcfkQ
使用輸出的 token 登錄 Dashboard。
5.2.8. 創建使用 token 的 KubeConfig 文件
cd /opt/k8s/work source /opt/k8s/bin/environment.sh # 設置集群參數 kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/cert/ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=dashboard.kubeconfig # 設置客戶端認證參數,使用上面創建的 Token kubectl config set-credentials dashboard_user \ --token=${DASHBOARD_LOGIN_TOKEN} \ --kubeconfig=dashboard.kubeconfig # 設置上下文參數 kubectl config set-context default \ --cluster=kubernetes \ --user=dashboard_user \ --kubeconfig=dashboard.kubeconfig # 設置默認上下文 kubectl config use-context default --kubeconfig=dashboard.kubeconfig
用生成的 dashboard.kubeconfig 登錄 Dashboard。
由於缺少 Heapster 插件,當前 dashboard 不能展示 Pod、Nodes 的 CPU、內存等統計數據和圖表;
5.3 heapster 插件
特別重要:
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" ssh ${node_ip} "docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/heapster-grafana-amd64:v4.4.3 && docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/heapster-grafana-amd64:v4.4.3 gcr.io/google_containers/heapster-grafana-amd64:v4.4.3" ssh ${node_ip} "docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/heapster-amd64:v1.5.3 && docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/heapster-amd64:v1.5.3 gcr.io/google_containers/heapster-amd64:v1.5.3" ssh ${node_ip} "docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/heapster-influxdb-amd64:v1.3.3 && docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/heapster-influxdb-amd64:v1.3.3 gcr.io/google_containers/heapster-influxdb-amd64:v1.3.3" done
Heapster是一個收集者,將每個Node上的cAdvisor的數據進行匯總,然后導到第三方工具(如InfluxDB)。
Heapster 是通過調用 kubelet 的 http API 來獲取 cAdvisor 的 metrics 數據的。
由於 kublet 只在 10250 端口接收 https 請求,故需要修改 heapster 的 deployment 配置。同時,需要賦予 kube-system:heapster ServiceAccount 調用 kubelet API 的權限。
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行。
5.3.1. 下載 heapster 文件
到 heapster release 頁面 下載最新版本的 heapster
cd /opt/k8s/work wget https://github.com/kubernetes/heapster/archive/v1.5.4.tar.gz tar -xzvf v1.5.4.tar.gz mv v1.5.4.tar.gz heapster-1.5.4.tar.gz
官方文件目錄: heapster-1.5.4/deploy/kube-config/influxdb
5.3.2. 修改配置
cd heapster-1.5.4/deploy/kube-config/influxdb cp grafana.yaml{,.orig} diff grafana.yaml.orig grafana.yaml
67c67
< # type: NodePort
---
> type: NodePort
l 開啟 NodePort;
cp heapster.yaml{,.orig} diff heapster.yaml.orig heapster.yaml
< - --source=kubernetes:https://kubernetes.default
---
> - --source=kubernetes:https://kubernetes.default?kubeletHttps=true&kubeletPort=10250
l 由於 kubelet 只在 10250 監聽 https 請求,故添加相關參數;
5.3.3. 執行所有定義文件
$ cd /opt/k8s/work/heapster-1.5.4/deploy/kube-config/influxdb $ ls *.yaml grafana.yaml heapster.yaml influxdb.yaml $ kubectl create -f . $ cd ../rbac/ $ cp heapster-rbac.yaml{,.orig} $ diff heapster-rbac.yaml.orig heapster-rbac.yaml 12a13,26 > --- > kind: ClusterRoleBinding > apiVersion: rbac.authorization.k8s.io/v1beta1 > metadata: > name: heapster-kubelet-api > roleRef: > apiGroup: rbac.authorization.k8s.io > kind: ClusterRole > name: system:kubelet-api-admin > subjects: > - kind: ServiceAccount > name: heapster > namespace: kube-system $ kubectl create -f heapster-rbac.yaml
l 將 serviceAccount kube-system:heapster 與 ClusterRole system:kubelet-api-admin 綁定,授予它調用 kubelet API 的權限;
如果不修改,默認的 ClusterRole system:heapster 權限不足:
E1128 10:00:05.010716 1 manager.go:101] Error in scraping containers from kubelet:192.168.100.246:10250: failed to get all container stats from Kubelet URL "https://192.168.100.246:10250/stats/container/": request failed - "403 Forbidden", response: "Forbidden (user=system:serviceaccount:kube-system:heapster, verb=create, resource=nodes, subresource=stats)" E1128 10:00:05.018556 1 manager.go:101] Error in scraping containers from kubelet:172.27.128.149:10250: failed to get all container stats from Kubelet URL "https:// 192.168.100.247:10250/stats/container/": request failed - "403 Forbidden", response: "Forbidden (user=system:serviceaccount:kube-system:heapster, verb=create, resource=nodes, subresource=stats)" E1128 10:00:05.022664 1 manager.go:101] Error in scraping containers from kubelet:172.27.128.148:10250: failed to get all container stats from Kubelet URL "https:// 192.168.100.248:10250/stats/container/": request failed - "403 Forbidden", response: "Forbidden (user=system:serviceaccount:kube-system:heapster, verb=create, resource=nodes, subresource=stats)" W1128 10:00:25.000467 1 manager.go:152] Failed to get all responses in time (got 0/3)
5.3.4. 檢查執行結果
$ kubectl get pods -n kube-system | grep -E 'heapster|monitoring'
heapster-56c9dc749-j7hvz 1/1 Running 0 1m
monitoring-grafana-c797777db-lnwnc 1/1 Running 0 1m
monitoring-influxdb-cf9d95766-5wd28 1/1 Running 0 1m
檢查 kubernets dashboard 界面,可以正確顯示各 Nodes、Pods 的 CPU、內存、負載等統計數據和圖表:
5.3.5. 訪問 grafana
- 通過 kube-apiserver 訪問:
獲取 monitoring-grafana 服務 URL:
kubectl cluster-info
Kubernetes master is running at https://192.168.100.246:8443
Heapster is running at https://192.168.100.246:8443/api/v1/namespaces/kube-system/services/heapster/proxy
CoreDNS is running at https://192.168.100.246:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
kubernetes-dashboard is running at https://192.168.100.246:8443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy
monitoring-grafana is running at https://192.168.100.246:8443/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
monitoring-influxdb is running at https://192.168.100.246:8443/api/v1/namespaces/kube-system/services/monitoring-influxdb/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
瀏覽器訪問 URL:
https://192.168.100.246:8443/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
對於 virtuabox 做了端口映射:
http://127.0.0.1:8080/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
- 通過 kubectl proxy 訪問:
創建代理
kubectl proxy --address='192.168.100.246' --port=8086 --accept-hosts='^*$'
Starting to serve on 172.27.129.150:8086
瀏覽器訪問 URL:
http://192.168.100.246:8086/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy/?orgId=1
對於 virtuabox 做了端口映射:
http://127.0.0.1:8086/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy/?orgId=1
- 通過 NodePort 訪問:
kubectl get svc -n kube-system|grep -E 'monitoring|heapster'
heapster ClusterIP 10.254.199.65 <none> 80/TCP 3m
monitoring-grafana NodePort 10.254.116.161 <none> 80:31470/TCP 3m
monitoring-influxdb ClusterIP 10.254.250.185 <none> 8086/TCP 3m
l grafana 監聽 NodePort 31470;
瀏覽器訪問 URL:http://192.168.100.246:31470/?orgId=1
5.3.6. 參考
5.4 metrics-server 插件
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行。
5.4.1. 創建 metrics-server 使用的證書
cd /opt/k8s/work/cert cat > metrics-server-csr.json <<EOF { "CN": "aggregator", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "study163" } ] } EOF
l 注意: CN 名稱為 aggregator,需要與 kube-apiserver 的 --requestheader-allowed-names 參數配置一致;
生成 metrics-server 證書和私鑰:
cfssl gencert -ca=/etc/kubernetes/cert/ca.pem \ -ca-key=/etc/kubernetes/cert/ca-key.pem \ -config=/etc/kubernetes/cert/ca-config.json \ -profile=kubernetes metrics-server-csr.json | cfssljson -bare metrics-server
將生成的證書和私鑰文件拷貝到 kube-apiserver 節點:
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]} do echo ">>> ${node_ip}" scp metrics-server*.pem root@${node_ip}:/etc/kubernetes/cert/ done
5.4.2. 修改 kubernetes 控制平面組件的配置以支持 metrics-server
5.4.2.1. kube-apiserver
添加如下配置參數:
--requestheader-client-ca-file=/etc/kubernetes/cert/ca.pem
--requestheader-allowed-names=""
--requestheader-extra-headers-prefix="X-Remote-Extra-"
--requestheader-group-headers=X-Remote-Group
--requestheader-username-headers=X-Remote-User
--proxy-client-cert-file=/etc/kubernetes/cert/metrics-server.pem
--proxy-client-key-file=/etc/kubernetes/cert/metrics-server-key.pem
--runtime-config=api/all=true
l --requestheader-XXX、--proxy-client-XXX 是 kube-apiserver 的 aggregator layer 相關的配置參數,metrics-server & HPA 需要使用;
l --requestheader-client-ca-file:用於簽名 --proxy-client-cert-file 和 --proxy-client-key-file 指定的證書;在啟用了 metric aggregator 時使用;
l 如果 --requestheader-allowed-names 不為空,則--proxy-client-cert-file 證書的 CN 必須位於 allowed-names 中,默認為 aggregator;
如果 kube-apiserver 機器沒有運行 kube-proxy,則還需要添加 --enable-aggregator-routing=true 參數;
關於 --requestheader-XXX 相關參數,參考:
https://github.com/kubernetes-incubator/apiserver-builder/blob/master/docs/concepts/auth.md
https://docs.bitnami.com/kubernetes/how-to/configure-autoscaling-custom-metrics/
注意:requestheader-client-ca-file 指定的 CA 證書,必須具有 client auth and server auth;
5.4.2.2. kube-controllr-manager
添加如下配置參數(從 v1.12 開始,該選項默認為 true,不需要再添加):
--horizontal-pod-autoscaler-use-rest-clients=true
用於配置 HPA 控制器使用 REST 客戶端獲取 metrics 數據。
5.4.3. 整體架構
暫缺
5.4.4. 修改插件配置文件配置文件
metrics-server 插件位於 kubernetes 的 cluster/addons/metrics-server/ 目錄下。
修改 metrics-server-deployment 文件:
$ cp metrics-server-deployment.yaml{,.orig} $ diff metrics-server-deployment.yaml.orig metrics-server-deployment.yaml
51c51
< image: mirrorgooglecontainers/metrics-server-amd64:v0.2.1
---
> image: k8s.gcr.io/metrics-server-amd64:v0.2.1
54c54
< - --source=kubernetes.summary_api:''
---
> - --source=kubernetes.summary_api:https://kubernetes.default?kubeletHttps=true&kubeletPort=10250
60c60
< image: siriuszg/addon-resizer:1.8.1
---
> image: k8s.gcr.io/addon-resizer:1.8.1
l metrics-server 的參數格式與 heapster 類似。由於 kubelet 只在 10250 監聽 https 請求,故添加相關參數;
授予 kube-system:metrics-server ServiceAccount 訪問 kubelet API 的權限:
$ cat auth-kubelet.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: metrics-server:system:kubelet-api-admin
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:kubelet-api-admin
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
l 新建一個 ClusterRoleBindings 定義文件,授予相關權限;
5.4.5. 創建 metrics-server
$ cd /opt/k8s/kubernetes/cluster/addons/metrics-server $ ls -l *.yaml
-rw-rw-r-- 1 k8s k8s 398 Jun 5 07:17 auth-delegator.yaml
-rw-rw-r-- 1 k8s k8s 404 Jun 16 18:02 auth-kubelet.yaml
-rw-rw-r-- 1 k8s k8s 419 Jun 5 07:17 auth-reader.yaml
-rw-rw-r-- 1 k8s k8s 393 Jun 5 07:17 metrics-apiservice.yaml
-rw-rw-r-- 1 k8s k8s 2640 Jun 16 17:54 metrics-server-deployment.yaml
-rw-rw-r-- 1 k8s k8s 336 Jun 5 07:17 metrics-server-service.yaml
-rw-rw-r-- 1 k8s k8s 801 Jun 5 07:17 resource-reader.yaml
$ kubectl create -f .
5.4.6. 查看運行情況
$ kubectl get pods -n kube-system |grep metrics-server metrics-server-v0.2.1-7486f5bd67-v95q2 2/2 Running 0 45s $ kubectl get svc -n kube-system|grep metrics-server metrics-server ClusterIP 10.254.115.120 <none> 443/TCP 1m
5.4.7. 查看 metrcs-server 輸出的 metrics
metrics-server 輸出的 APIs:
- 通過 kube-apiserver 或 kubectl proxy 訪問:
https://192.168.100.246:6443/apis/metrics.k8s.io/v1beta1/nodes https://192.168.100.246:6443/apis/metrics.k8s.io/v1beta1/nodes/ https://192.168.100.246:6443/apis/metrics.k8s.io/v1beta1/pods https://192.168.100.246:6443/apis/metrics.k8s.io/v1beta1/namespace//pods/
- 直接使用 kubectl 命令訪問:
kubectl get --raw apis/metrics.k8s.io/v1beta1/nodes kubectl get --raw apis/metrics.k8s.io/v1beta1/pods kubectl get --raw apis/metrics.k8s.io/v1beta1/nodes/ kubectl get --raw apis/metrics.k8s.io/v1beta1/namespace//pods/
$ kubectl get --raw "/apis/metrics.k8s.io/v1beta1" | jq .
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "metrics.k8s.io/v1beta1",
"resources": [
{
"name": "nodes",
"singularName": "",
"namespaced": false,
"kind": "NodeMetrics",
"verbs": [
"get",
"list"
]
},
{
"name": "pods",
"singularName": "",
"namespaced": true,
"kind": "PodMetrics",
"verbs": [
"get",
"list"
]
}
]
}
$ kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes" | jq .
{
"kind": "NodeMetricsList",
"apiVersion": "metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/metrics.k8s.io/v1beta1/nodes"
},
"items": [
{
"metadata": {
"name": "node2",
"selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/node2",
"creationTimestamp": "2018-06-16T10:24:03Z"
},
"timestamp": "2018-06-16T10:23:00Z",
"window": "1m0s",
"usage": {
"cpu": "133m",
"memory": "1115728Ki"
}
},
{
"metadata": {
"name": "master",
"selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/master",
"creationTimestamp": "2018-06-16T10:24:03Z"
},
"timestamp": "2018-06-16T10:23:00Z",
"window": "1m0s",
"usage": {
"cpu": "221m",
"memory": "6799908Ki"
}
},
{
"metadata": {
"name": "node1",
"selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/node1",
"creationTimestamp": "2018-06-16T10:24:03Z"
},
"timestamp": "2018-06-16T10:23:00Z",
"window": "1m0s",
"usage": {
"cpu": "76m",
"memory": "1130180Ki"
}
}
]
}
l /apis/metrics.k8s.io/v1beta1/nodes 和 /apis/metrics.k8s.io/v1beta1/pods 返回的 usage 包含 CPU 和 Memory;
5. 5 EFK 插件
EFK 對應的目錄:kubernetes/cluster/addons/fluentd-elasticsearch
$ cd /opt/k8s/work/kubernetes/cluster/addons/fluentd-elasticsearch $ ls *.yaml es-service.yaml es-statefulset.yaml fluentd-es-configmap.yaml fluentd-es-ds.yaml kibana-deployment.yaml kibana-service.yaml
注意:如果沒有特殊指明,本文檔的所有操作均在 master 節點上執行。
5.5.1. 拉取鏡像
注意:因為elk插件使用的是谷歌鏡像,由於眾所周知的原因我們無法使用,只能通過先拉去對於的其他公開鏡像然后改名。或者直接修改yml文件中定義的谷歌鏡像名(推薦使用第一種)。
source /opt/k8s/bin/environment.sh for node_ip in ${NODE_IPS[@]}; do echo ">>> ${node_ip}"; ssh ${node_ip} "docker pull xiaochunping/elasticsearch:v6.2.5 && docker tag xiaochunping/elasticsearch:v6.2.5 k8s.gcr.io/elasticsearch:v6.2.5"; ssh ${node_ip} "docker pull xiaochunping/fluentd-elasticsearch:v2.2.0 && docker tag xiaochunping/fluentd-elasticsearch:v2.2.0 k8s.gcr.io/fluentd-elasticsearch:v2.2.0"; done
命令只需要在master節點上執行即可。
5.5.2. 給 Node 設置標簽
DaemonSet fluentd-es 只會調度到設置了標簽 beta.kubernetes.io/fluentd-ds-ready=true 的 Node,需要在期望運行 fluentd 的 Node 上設置該標簽;
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready <none> 3d v1.10.4
node1 Ready <none> 3d v1.10.4
node2 Ready <none> 3d v1.10.4
$ kubectl label nodes node2 beta.kubernetes.io/fluentd-ds-ready=true
node "node2" labeled
5.5.3. 執行定義文件
$ cd /opt/k8s/kubernetes/cluster/addons/fluentd-elasticsearch $ ls *.yaml es-service.yaml es-statefulset.yaml fluentd-es-configmap.yaml fluentd-es-ds.yaml kibana-deployment.yaml kibana-service.yaml $ kubectl create -f .
5.5.4. 檢查執行結果
$ kubectl get pods -n kube-system -o wide|grep -E 'elasticsearch|fluentd|kibana'
elasticsearch-logging-0 1/1 Running 0 5m 172.30.81.7 master
elasticsearch-logging-1 1/1 Running 0 2m 172.30.39.8 node2
fluentd-es-v2.0.4-hntfp 1/1 Running 0 5m 172.30.39.6 node2
kibana-logging-7445dc9757-pvpcv 1/1 Running 0 5m 172.30.39.7 node2
$ kubectl get service -n kube-system|grep -E 'elasticsearch|kibana'
elasticsearch-logging ClusterIP 10.254.50.198 <none> 9200/TCP 5m
kibana-logging ClusterIP 10.254.255.190 <none> 5601/TCP 5m
kibana Pod 第一次啟動時會用**較長時間(0-20分鍾)**來優化和 Cache 狀態頁面,可以 tailf 該 Pod 的日志觀察進度:
$ kubectl logs kibana-logging-7445dc9757-pvpcv -n kube-system -f
{"type":"log","@timestamp":"2018-06-16T11:36:18Z","tags":["info","optimize"],"pid":1,"message":"Optimizing and caching bundles for graph, ml, kibana, stateSessionStorageRedirect, timelion and status_page. This may take a few minutes"}
{"type":"log","@timestamp":"2018-06-16T11:40:03Z","tags":["info","optimize"],"pid":1,"message":"Optimization of bundles for graph, ml, kibana, stateSessionStorageRedirect, timelion and status_page complete in 224.57 seconds"}
注意:只有當的 Kibana pod 啟動完成后,才能查看 kibana dashboard,否則會提示 refuse。
5.5.5. 訪問 kibana
- 通過 kube-apiserver 訪問:
$ kubectl cluster-info|grep -E 'Elasticsearch|Kibana'
Elasticsearch is running at https://192.168.100.246:6443/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy
Kibana is running at https://192.168.100.246:6443/api/v1/namespaces/kube-system/services/kibana-logging/proxy
瀏覽器訪問 URL:
https://192.168.100.246:6443/api/v1/namespaces/kube-system/services/kibana-logging/proxy
對於 virtuabox 做了端口映射:
http://127.0.0.1:8080/api/v1/namespaces/kube-system/services/kibana-logging/proxy
- 通過 kubectl proxy 訪問:
創建代理
$ kubectl proxy --address='192.168.100.246' --port=8086 --accept-hosts='^*$' Starting to serve on 172.27.129.150:8086
瀏覽器訪問 URL:
http://192.168.100.246:8086/api/v1/namespaces/kube-system/services/kibana-logging/proxy
對於 virtuabox 做了端口映射:
http://127.0.0.1:8086/api/v1/namespaces/kube-system/services/kibana-logging/proxy
在 Settings -> Indices 頁面創建一個 index(相當於 mysql 中的一個 database),選中 Index contains time-based events,使用默認的 logstash-* pattern,點擊 Create ;
創建 Index 后,稍等幾分鍾就可以在 Discover 菜單下看到 ElasticSearch logging 中匯聚的日志;
六、結合k8s的系統整體監控方案、業務部屬系統
七、xx公司的探索和實踐
