上一篇簡單介紹了一下k8s是什么以及如何使用kubeadm快捷安裝,今兒來聊一下k8s的幾個基礎概念及術語。k8s中的資源都可以使用yaml文件進行描述。(文章內容來源於《kubernetes權威指南 第四版》)

Master
集群控制節點,負責整個集群的管理和控制,負責命令的執行過程,運行着以下四個關鍵進程。
(1)Kubernetes API Service(Kube-apiservice):提供了Http Rest 接口的關鍵服務進程,是K8s里所有資源的CRUD的唯一操作入口,也是集群控制的入口進程。
(2)Kubernetes Controller Manager(Kube-controller-manager):K8s中所有資源對象的自動化控制中心,資源對象的大總管。controller用於監控容器健康狀態,controller manager監控controller的健康狀態。
(3)Kubernetes Scheduler(Kube-scheduler):先做預選,篩選有哪些Node符合,然后做優選最佳的節點。負責資源調度(Pod調度)的進程。
(4)etcd server:保存所有資源對象的數據。當數據發生變化時,etcd 會快速地通知 Kubernetes 相關組件。
Node
工作負載節點,每個Node都會被Master分配工作負載(Docker容器),當某個Node宕機時,其上的工作負載會被Master自動轉移到其他節點上。每個Node節點都運行着以下一組進程。
(1)kubelet:負責Pod對應的容器創建、啟停等任務,同時與Master密切協作,實現集群管理的基本功能。
(2)kube-proxy:實現K8s service的通信和負載均衡機制的重要組件。
(3)Docker Enginer:Docker引擎,負責本機的容器創建和管理工作。
Node節點可在運行時動態增加到集群中,默認情況下kubelet會向Master注冊自己。會定時匯報自身信息,比如Docker版本、CPU、內存、運行哪些Pod等。這樣Master可以熟知Node節點的信息,實現高效均衡的資源調度策略,在指定時間內沒上報,會被Master判斷為失聯,進行工作負載轉移。
~可通過如下命令查看集群中節點
kubectl get nodes

~可通過如下命令查看節點詳細信息
kubectl describe nodes/節點名稱

Pod

根容器Pause,作為業務無關並不易死亡的Pause容器,它的狀態代表了整個容器組的狀態,可以簡單有效判斷容器是否已死。
Pod里多個業務容器共享Pause容器的IP和Volume,簡化了業務容器之間的通信問題,也解決了文件共享問題。
k8s為每個Pod分配了一個唯一的IP地址,簡稱Pod IP,Pod里面的容器可以共享IP,采用虛擬二層網絡技術實現集群內任意兩個Pod之間可以直接進行TCP/IP通信。

Pod有兩種類型:普通Pod和靜態Pod,靜態Pod並不存放在etcd存儲里,而是存放在某個具體的Node里的一個具體文件中,並且只在此Node上運行。普通Pod創建之后就會被放在etcd中存儲,隨后被Master調度到某個Node上並進行綁定,被Node上kubelet進程實例化成一組相關的Docker容器並啟動。默認情況下,Pod某個容器停止 時,k8s會自動檢測並重啟此Pod,如果所在的Node宕機,則會將所有Pod重新調度到其他節點上。
每個Pod都可以對其能使用的服務器上的計算資源設置限額,當前可以設置限額的計算資源有cpu和memory兩種,其中cpu的資源單位以cpu的數量,是一個絕對值而非相對值。
在k8s中,通常以千分之一的CPU配額為最小單位,用m來表示,Memory配額也是一個絕對值,單位時內存字節數。
在K8s中,一個計算資源進行配額限定需要設定以下兩個參數:
Requests:最小申請量,必須滿足此要求。
Limits:最大允許使用量,不能被突破,當容器試圖突破時,會被kill掉然后重啟。
例如:在聲明某個Pod或Service時可以在spec中進行設置
spec: container: - name: db image: mysql imagePullPolixy: IfNotPresent resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m"

Event是一個事件的記錄,記錄了事件的最早發生時間、最后重現時間、重復次數、發起者、類型,以及導致此事件的原因等。
可使用如下命令在資源的詳細描述中看到
kubectl describe 資源類型 資源名稱 例如:
kubectl describe nodes cnode-1

Label
標簽信息,kv鍵值對,可附加到各種資源對象上,例如Node,Pod,Service,RC等,一個資源對象可以定義多個Label,一個Label可以被添加到多個資源上,通常在資源定義時確定,也可在在對象創建后動態添加或刪除。
可通過Label Selector查詢和篩選擁有某些Label的資源對象。當前有 兩種Label Selector的表達式,基於等式和基於集合。
例如:name=redis
env != dev
name in (a,b)
name not in (a,b)
當需要多個實現復雜選擇的時候,可以用逗號分隔,表示And的關系。
Label Selector使用場景:
(1)Kube-controller通過RC上定義的Label Selector來 篩選要監控的Pod副本數量,從而實現Pod副本的數量始終符合預期設定的全自動監控流程。
(2)kube-proxy通過Service的Label Selector來選擇對應的Pod,自動建立起每個Service到對象Pod的請求轉發路由表,從而實現Service的智能負載均衡機制。
(3)Kube-schedule通過Label,並且在Pod定義文件中使用NodeSelector這種標簽調度策略,實現Pod定向調度的特性。
Replication Controller
RC定義了一個期望的場景,聲明某個Pod的數量在任意時刻都符合某個預期值,RC的定義包括如下幾個部分:
(1)Pod期待的副本數
(2)用於篩選目標Pod的Label Selector
(3)當Pod的副本數量小於預期數量時,用創建新Pod的Pod模板創建足夠的Pod
例如:希望一個redis保持3個實例
apiVersion: v1 kind: ReplicationController metadata: name: redis labels: name: redis spec: replicas: 3 selector: name: redis template: metadata: name: redis labels: name: redis spec: containers: - name: redis image: redis imagePullPolicy: IfNotPresent ports: - containerPort: 6379
Master會根據RC定期巡檢系統中存活的目標Pod,並確保目標Pod實例數量剛好等於RC的期望值。
可通過如下命令動態修改RC中的副本值,可使用此命令進行集群的擴容和縮容。
kubectl scale rc rc-name --replicas=4
特性和作用:
(1)通過定義RC實現Pod的創建過程及副本數量的自動控制。
(2)RC里包括完成的Pod定義模板
(3)RC通過Label Selector機制實現對Pod副本的自動控制。
(4)通過改變RC中的Pod副本數量,可以實現對Pod的擴容或縮容功能。
(5)通過改變RC中的鏡像版本,實現Pod的滾動升級功能。
Deployment
常見Pod控制器如下:
控制器名稱作用Deployment聲明式更新控制器,用於發布無狀態應用ReplicaSet副本集控制器,用於對Pod進行副本規模的擴大或者裁剪StatefulSet有狀態副本集,用於發布有狀態應用DaemonSet
在K8s集群每一個Node上運行一個副本,
用於發布監控和日志收集類等應用
Job運行一次性作業任務CronJob運行周期性作業任務
Deploymen內部使用Replica Set實現,Replica Set是下一代的RC,支持使用基於集合的Label Selector,這也是與Replication Controller唯一的區別。
使用場景:
(1)創建一個Deplayment來生成對應的Replica Set並完成副本的創建過程
(2)檢查Deplayment的狀態來看部署動作是否完成
(3)更新Deplayment來創建新的Pod
(4)如果當前Deplayment不穩定,則回滾到一個早前的版本。
(5)掛起或恢復一個Deplayment。
(6)擴展Deployment以應對高負載。
(7)清理不再需要的舊版本ReplicaSets。
Deployment的聲明的api是extensions/v1betal,其他的使用與RC並無區別
apiVersion: extensions/v1betas
StatefulSet
有很多服務是有狀態的,特別是一些中間件集群,例如MySQL集群、MongoDB集群、Akka集 群、ZooKeeper集群等,這些應用集群都有以下幾個共同點,
(1)每個節點都有固定的身份ID,通過這個ID,集群中的成員可 以相互發現並通信。
(2)集群的規模是比較固定的,集群規模不能隨意變動。
(3)集群中的每個節點都是有狀態的,通常會持久化數據到永久 存儲中。
(4)如果磁盤損壞,則集群里的某個節點無法正常運行,集群功 能受損
如果使用RC或Deployment的話,就會發現第一點無法滿足,因為其的Pod名稱都是隨機生成的,並且不固定,重啟之后又是另外一個名、IP等。
StatefulSet可以解決上述問題:
(1)StatefulSet里的每個Pod都有穩定、唯一的網絡標識,可以用來 發現集群內的其他成員。假設StatefulSet的名稱為kafka,那么第1個Pod 叫kafka-0,第2個叫kafka-1,以此類推。
(2) StatefulSet控制的Pod副本的啟停順序是受控的,操作第n個Pod 時,前n-1個Pod已經是運行且准備好的狀態。
(3) 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}.${handless service name}
比如一個3節點的Kafka的StatefulSet集群對應的Headless Service的名 稱為kafka,StatefulSet的名稱為kafka,則StatefulSet里的3個Pod的DNS 名稱分別為kafka-0.kafka、kafka-1.kafka、kafka-3.kafka。
Job
Job與其他Pod控制器不同的是,Job控制的容器僅運行一次,當所有Pod副本結束,Job也就運行結束了,Job生成的副本不能自動重啟,對應Pod的Restart Policy設置為Never。
同時,k8s提供了CronJob,解決某些任務需要定時反復執行的問題。
Horizontal Pod Autoscaler
Pod橫向自動擴容,通過追蹤分析RC控制的所有目標Pod的負載變化情況,來確定是否需要針對性的調整目標Pod的副本數。
HPA可以有兩種方式作為Pod負載的度量指標:
(1)CPUUtilizationPercentage
算術平均值,目標Pod所有副本自身的CPU利用率的一分鍾內的平均值,是當前cpu使用量除於Requests值,如果超過自定義比例,例如80%,則需要動態擴容
例如:聲明一個HPA對name為test的Deployment在cpu利用率達到90%進行擴容,最多為10個
apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: name: test namespace: default spec: maxReplicas: 10 minReplicas: 1 scaleTargetRef: kind: Deployment name: test targetCPUUtilizationPercentage: 90
也可以通過如下命令創建HPA
kubectl autoscale deployment app-name --cpu-percent=90 --min=1 --max=1
(2)應用程序自定義的度量指標,例如TPS或QPS(每秒內的請求數)
Service
K8s中的Service定義了一個服務的訪問入口地址,前端應用可以通過這個入口地址訪問其背后一組由Pod副本組成的集群實例,Service與其后端的Pod副本集群之間是通過Label Selector來實現連接的。

運行在每個Node節點上的kube-proxy進程其實就是一個智能的負載均衡器,負責把對Service的請求轉發到后端的Pod上,並在內部實現負載均衡和會話保持機制,每個Service都被分配了一個全局唯一的虛擬IP,成為Cluster-IP,在Service生命周期中,Cluster IP不會改變,但是Pod實例重啟之后ip就會變,所以Service的Cluster IP就可以解決此問題。
通過如下命令可以查看service的詳細信息
kubelet get svc service-name -o yaml
例如:

在spec.port中,targetPort表示容器所暴露的端口,port是Service提供的虛端口,如果未指定targetPort,則默認targetPort與port一致。
Service提供支持多個Endpoint,在此情況下,每個Endpoint需定義一個名字區分,例如:
apiVersion: v1 kind: Service metadata: name: tomcat-test spec: selector: name: tomcat-test ports: - port: 8080 name: service-port1 - port: 8081 name: service-port2
(1)k8s的服務發現機制
K8S中的Service都有一個唯一的Cluster IP和唯一的名字,通過Add-On增值包的方式引入DNS系統,把服務名作為域名,程序就可以直接使用服務名來建立通信連接了。
(2)外部系統訪問Service
三種IP:
1、Node IP:Node節點的IP
節點物理網卡的IP地址,真實存在的物理網絡,K8s集群之外的節點訪問集群,必須通過Node IP進行通信。
2、Pod IP:Pod的IP
是Docker Engine根據docker0的網橋的IP地址段進行分配的,虛擬的二層網絡,
3、Cluster IP:Service的IP
僅僅作用於Service這個對象,由K8S管理和分配IP地址。無法被PING。只能結合Service Port組成一個具體的通信端口,單獨的Cluster IP不具備TCP/IP通信的基礎。
如果需要集群外的節點訪問集群,解決方案就是使用NodePort,例如:
apiVersion: v1 kind: Service metadata: name: tomcat-service spec: type: NodePort selector: name: tomcat ports: - port: 8080 nodePort: 30001
上例中將spec.type指定為NodePort,然后在ports中指定nodePort,也就是宿主機的端口號。
如果不指定NodePort,K8S會自動分配一個可用端口。
NodePort的實現方式是在K8S集群中的每個Node上為需要外部訪問的Service開啟一個對應的TCP監聽端口,外部系統只需要任意一個Node的IP地址+具體NodePort的端口即可訪問服務。
Volume
Volume是Pod中能夠被多個容器訪問的共享目錄,k8s的Volume定義在Pod上,然后被Pod的多個容器掛載到具體的文件目錄下;與Pod的生命周期相同,與容器的生命周期無關,當容器終止或重啟時,Volume中的數據也不會丟失,支持多種類型的Volume,例如G`lusterFS等文件系統。
使用:
先在Pod上聲明一個Volume,然后在容器中引用該Volume並Mount到容器中的某個目錄上,例如:增加一個名為datavol的Volume,並Mount到容器的/mydata-data目錄上。
spec: volumes: - name: datavol emptyDir: {} containers: - name: test-volume image: tomcat volumeMounts: - mountPath: /mydata-data name: datavol
k8s提供了豐富的volume類型:
(1)emptyDir
在Pod分配到Node時創建的,初始內容為空,無需指定宿主機上對應的目錄文件,K8S自動分配,當Pod從Node上移除時,emptyDir中的數據也會被永久刪除。
用途:
1、臨時空間
2、長時間任務的中間過程CheckPoint的臨時保存目錄
3、一個容器需要從另一個容器中獲取數據的目錄(多容器共享目錄)
volumes: - name: emptyDir emptyDir: {}
(2)hostPath
在Pod上掛載宿主機上的文件或目錄,可用於以下幾個方面:
1、容器生成的日志需要永久保存時
2、需要訪問宿主機上的Docker引擎內部數據結構的容器應用時,可以通過定義hostPath為宿主機/var/lib/docker目錄,使容器內部應用可以直接訪問Docker的文件系統。
需注意:
1、在不同的Node上具有相同配置的Pod可能會因為宿主機上的目錄和文件不同而導致對Volume上目錄和文件的訪問結果不一致。
2、如果使用了資源配額管理,則K8s無法將hostPath在宿主機上使用的資源納入管理。
volumes: - name: hostpath hostpath: path: "/path"
(3)gcePersistentDisk
使用谷歌公有雲提供的永久磁盤(Persistent Disk,PD)存放Volume的數據,PD上的內容會永久保存,當Pod被刪除時,PD只是被卸載(Unmount),不會被刪除,需要先創建一個永久磁盤(PD),才能使用gcePersistentDisk。
限制條件:
Node需要GCE虛擬機
這些虛擬機需要與PD存在與相同的GCE項目和zone中。
通過gcloud命令可創建一個PD
gcloud compute disks create --size=500GB --zone=us-centrall-a my-data-disk
volumes: - name: gcePersistentDiskTest gcePersistentDisk: pdName: my-data-disk fsType: ext4
(4)awsElasticBlockStore
使用亞馬遜公有雲提供的EBS Volume存儲數據,需要先創建一個EBS Volume才能使用
限制條件:
Node節點需要AWS EC2實例
AWS EC2實例需要與EBS Volume存在於相同的region和availability-zone中
EBS只支持單個EC2實例mount一個Volume
volumes: - name: awsElasticBlockStoreTest awsElasticBlockStore: volumeId: aws://<availability-zone>/<volume-id> fsType: ext4
(5)NFS
使用NFS網絡文件系統提供的共享目錄存儲數據時,需在系統中部署一個NFS Server
volumes: - name: nfs nfs: server: nfs服務器地址 path: "/"
Persistent Volume
Persistent Volume(PV)和與之關聯的Persistent Volume Claim(PVC)是一塊網絡存儲,掛接到虛機上的‘網盤’。網絡存儲是相對獨立於計算資源而存在的一種實體資源。
PV是K8s集群中某個網絡存儲中對應的的一塊存儲,與Volume相似,但有以下區別:
(1)PV只能是網絡存儲,不屬於任何Node,但可以在任何Node上訪問。
(2)PV並不是定義在Pod上的,而是獨立於Pod之外定義的。
(3)PV目前只有幾種類型:GCE Persistent Disks、NFS、RBD、iSCSCI、AWS ElasticBlockStore、GlusterFS
例如:定義一個NFS類型的PV
apiVersion: v1 kind: PersistentVolume metadata: name: pvtest spec: capacity: storage: 5Gi accessModes: - ReadWriteOnce nfs: server: nfs地址 path: /path
重點是PV的accessModes屬性,目前有以下幾種類型:
(1)ReadWriteOnce:讀寫權限,並且只能被單個Node掛載
(2)ReadOnlyMany:只讀權限,允許被多個Node掛載
(3)ReadWriteMany:讀寫權限,允許被多個Node掛載
如果某個Pod想申請某種條件的PV,則首先需要定義一個Persistent Volume Claim(PVC)對象
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: myclaim spec: accessModes: - ReadWriteOnce resources: requests: storage: 8Gi
然后,在Pod的Volume定義中引用上述PVC即可
volumes: - name: pvtest persistentVolumeClaim: claimName: myclaim
PV有以下幾種狀態:
(1)Available:空閑狀態
(2)Bound:已經綁定到某個PVC上
(3)Released:對應的PVC已經刪除,但資源還沒有被集群回收
(4)Failed:PV自動回收失敗。
Namespace
用於實現多租戶的資源隔離,通過將集群內部的資源對象“分配”到不同的Namespace中,形成邏輯上分組的不同項目、小組或用戶組,便於不同的分組在共享使用整個集群的資源的同時還能被分別管理。
K8S集群啟動之后,會創建一個名為default的Namespace,可以通過如下命令查看
kubectl get namespace 或 kubectl get ns
如果在資源定義的時候不指定Namespace,則用戶創建的Pod、Service、RC都將會創建到default的Namespace中。
Namespace的定義:
apiVersion: v1 kind: Namespace metadata: name: my-ns
將Pod定義在這個Namespace中
apiVersion: v1 kind: Pod metadata: name: my-pod namespace: my-ns labels: name: my-pod spec: containers: - name: my-pod image: imageName imagePullPolicy: IfNotPresent ports: - containerPort: 8080
使用kubectl get pods是只能看到default命名空間的Pod,如果需要看到其他Namespace的資源,需要指定Namespace。
kubectl get pods --namespace=my-ns 或者 kubectl get pods -n my-ns
當給不同租戶創建一個Namespace實現多租戶的資源隔離時,能結合k8s的資源配額管理,限定不同租戶能占用的資源,例如CPU使用量,內存使用量等。
Annotation
使用key/value鍵值對的形式進行定義,與Label類似,不同的是Label具有嚴格的命名規則,定義的是資源對象的元數據,並用於Label Selector,而Annotation則是任意定義的附加信息,以便於外部工具進行查找,K8s會通過此方式標記資源對象的一些特殊信息。
用Annotation來記錄的信息如下:
(1)build信息,release信息,Docker鏡像信息等,例如時間戳、release id號、鏡像hash值、docker registry地址等
(2)日志庫、監控庫、分析庫等資源庫的信息
(3)程序調試工具信息,例如工具名稱、版本號等
(4)團隊的聯系信息、例如電話號碼、負責人信息等。
ConfigMap
Docker容器提供了兩種方式在運行期間修改配置文件中的參數。
(1)在運行時通過容器的環境變量來傳遞參數;
(2)通過Docker Volume將容器外的配置文件映射到容器內。
大多數情況下使用的是第二種方式,但這種方式需要先在宿主機下創建配置文件進行映射,在分布式情況下,修改多台服務器的某個配置文件,都是比較麻煩的。
所以,ConfigMap就出現了,所有的配置項都是kv類型的,v也可以是某個文件的路徑,例如username=abc,這些配置項可以作為Map中的一個項,整個Map的數據可以被持久化存儲在 Kubernetes的Etcd數據庫中,然后提供API以方便Kubernetes相關組件或 客戶應用CRUD操作這些數據。
k8s提供了一種機制,將存儲在etcd中的 ConfigMap通過Volume映射的方式變成目標Pod內的配置文件,不管目標Pod被調度到哪台服務器上,都會完成自動映射。進一步地,如果 ConfigMap中的key-value數據被修改,則映射到Pod中的“配置文件”也會 隨之自動更新。

===============================
我是Liusy,一個喜歡健身的程序員。
歡迎關注微信公眾號【Liusy01】,一起交流Java技術及健身,獲取更多干貨,領取Java進階干貨,領取最新大廠面試資料,一起成為Java大神。
來都來了,關注一波再溜唄。