k8s已經成為了絕對熱門的技術,一個上點規模的公司,如果不搞k8s,都不好意思出去見人。安裝k8s要突破種種網路阻礙,但更大的阻礙還在後面。。。

我發現,很多k8s的文章,根本不說人話,包括那要命的官網。

要弄明白k8s的細節,需要知道k8s是個什麼東西。它的主要功能,就是容器的排程——也就是把部署例項,根據

整體資源

的使用狀況,部署到

任何

地方。先不要扯別的,那會擾亂視線,增加複雜性。

注意

任何

這兩個字,預示著你並不能夠透過常規的IP、埠方式訪問部署的例項。複雜性由此而生。

我們學k8s,就要看它要排程哪些資源。以傳統的感覺來看,無非就是

cpu

記憶體

網路

io

等。在瞭解怎麼對這些資源排程之前,先要搞懂什麼叫Pod,這可是k8s的核心概念之一。

搞不懂Pod,就沒法玩k8s。

本文的腦圖,可以在這裡在線檢視:

http://

mind。xjjdog。cn/mind/clo

ud-k8s

1. Pod

pod是k8s排程的最小單元,包含一個或者多個容器(這裡的容器你可以暫時認為是docker)。

Pod擁有一個唯一的IP地址,在包含多個容器的時候,依然是擁有一個IP地址,它是怎麼辦到的呢?

xjjdog

之前寫過兩篇Docker原理的文章,指出其中兩個使用到的底層技術,就是

namespace

cgroup

,k8s在使用多個容器的時候,用到的就是

共享namespace

,這樣Pod裡的容器就可以透過localhost通訊了,就像兩個程序一樣。同理的,Pod 可以掛載多個共享的儲存卷(Volume),這時內部的各個容器就可以訪問共享的 Volume 進行資料的讀寫。

k8s主要概念大梳理!

一些邊車

(Sidecar)

,以及存活探針等,也是以容器的形式,存在於Pod中的。所以Pod是一個大雜燴,它取代了docker容器的一部分工作(這既是

Pause

容器的職責),比如建立一些共享的

net namespace

等。

那如何表示、宣告一個Pod呢?又如何指定這些容器與Pod的關係呢?k8s選用了

yaml

這種配置方式,初衷是避免過度的API設計。

很好,這又引入了另外一個問題,那就是yml檔案的膨脹。所有的k8s運維,都有過被yml檔案給支配的恐懼。

沒有銀彈,只不過把問題轉移到另外一個場景罷了。

宣告一個Pod,就是寫yml檔案。一個Pod的yml樣例,可能長得像下面這樣。

apiVersion: v1 #本版號

kind: Service #建立的資源型別

metadata: #元資料必選

namespace: bcmall #繫結名稱空間

name: bcmall-srv #Service資源名稱

spec: #定義詳細資訊

type: NodePort #型別

selector: #標籤選擇器

app: container-bcmall-pod

ports: #定義埠

- port: 8080 #port 指定server埠,此埠用於叢集內部訪問

targetPort: 80 #繫結pod埠

nodePort: 14000 #將server 埠對映到Node節點的埠,用於外網訪問

protocol: TCP #埠協議

注意

kind

這個選項,這將是k8s概念膨脹的噩夢!k8s的各種配置,基本上都是圍著這裡轉。哦對了,要讓這些yml檔案生效,你需要用到kubectl 命令,就像這樣。

kubectl create -f 。/bcmall。yaml

訪問一個Pod,可以透過它的IP,也可以透過內部的域名(這時候就需要CoreDNS)。當這麼用的時候,其實Pod的表現,就相當於一臺普通的機器,裡面的容器就是一堆程序而已。

2. 探針和鉤子

一個Pod被排程之後,就要進行初始化。初始化肯定是得有一個反饋的,否則都不知道最終有沒有啟動成功。這些健康檢查的功能,叫做探針(Probe),一個比較怪異的英文名詞。

常見的有livenessProbe、readinessProbe、startupProbe等三種探針。

livenessProbe

有點像

心跳

,如果判定不線上了,就會把它幹掉;readinessProbe一般表示

就緒

狀態,也比較像心跳,證明你的服務在正常跑著;startupProbe用於判斷容器是否已經啟動好,避免一些超時等,比如你的JVM啟動完畢了,才能對外提供服務。

k8s主要概念大梳理!

一般,花費120s startupProbe的啟動實踐,每隔5s檢測一下livenessProbe,每隔10s檢測一下readinessProbe,是常用的操作。

這些資訊,也是在yml中配置的,具體的配置層次如何,這裡不羅嗦,您就查文件去吧。

再說一下鉤子(Hook)。主要有

PostStart

PreStop

兩種。PostStart 可以在容器啟動之後就執行,PreStop 則在容器被終止之前被執行。這沒什麼神奇的,就是執行一些shell指令碼而已,只不過比較常用,就提升到了關鍵字的級別。

k8s主要概念大梳理!

我們來看看它長什麼樣子。由於這些配置檔案大同小異,後面就不再貼這樣的程式碼了。

apiVersion: v1

kind: Pod

metadata:

labels:

test: liveness

name: liveness-exec

spec:

containers:

- name: liveness

image: k8s。gcr。io/busybox

args:

- /bin/sh

- -c

- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600

livenessProbe:

exec:

command:

- cat

- /tmp/healthy

initialDelaySeconds: 5

periodSeconds: 5

3. 高可用引起的名詞爆炸

上面說到,yaml的複雜性,是由於kind的種類多所引起的。首先我們要接觸的,就是

ReplicaSet

我們需要多個副本,才能做高可用。

原因很簡單,一個Pod就相當於一臺機器,當掉之後,就無法提供服務了,這是哪門子的高可用?所以對等的Pod,要有多份才行。

ReplicaSet

簡稱

RS

,可以讓你的Pod數量一直保持在某個水平。但它在操作起來還是有點麻煩了,所以一般使用更加高階的

Deployment

。Deployment可以實現一些滾動升級的需求,但前提是你需要在

spec。template。metadata。labels

中設定了相應的鍵值對。

k8s的一些過濾工作,都是透過labels來實現的。這其實是一種非常折衷的做法,因為它本身並沒有做一些類似於sql查詢之類的工作,就只能在這一堆map的鍵值對上做文章。比如這樣:

kubectl get pod -n demo -l app=nginx,version=v1

是不是很魔幻的寫法?不要緊,習慣了就好了。

這些yml配置,通常都是一環套一環的,還會有交叉引用,所以也會有優先順序。高階的kind會直接順帶把低階的kind一起建立了,屬於一個級聯關係。

一切不過是yml檔案包了一層而已。

好了,我們要接觸下一個kind:

service

了。

為什麼需要Service?因為我們上面建立的Pod,哪怕是

Deployment

建立的Pod,你訪問它都要費些功夫。雖然Pod有IP,但如果它重啟了,或者銷燬了,IP就會動態變化。因為Pod是被排程的,它並不知道自己會被排程到哪一臺機器。

Service這個東西,就是要提供一種非IP的訪問途徑,使得不論Pod在哪裡,我們都能訪問的到它。

如圖所示,透過Labels的過濾,可以把多個Pod歸結為一類,然後以某種型別對外暴露服務。Service說白了也是一個組合後的東西。

k8s主要概念大梳理!

對外訪問的型別,這裡要著重說明一下,因為它重要,所以要敲黑板。主要有4種:

ClusterIP 建立一個虛擬的IP,唯一且不可修改。所有訪問該IP的請求,都將被iptables轉發到後端。這是預設的行為,就是一個coredns的外掛

NodePort 提供一個靜態埠(NodePort)來暴露服務,主要使用的技術是

NAT

LoadBalancer LoadBalancer主要用於做外部的服務發現,即暴露給叢集外部的訪問

ExternalName 使用較少,感興趣的可以自行了解

但是等等。k8s是如何實現跨主機的Pod相互訪問的呢?

在單個Node上的Pod相互訪問可以理解,直接透過docker0網橋分配的IP,就可相互訪問。

那k8s的底層網路是真麼設定的呢?答案可能令人沮喪。k8s本身並不負責網路管理,也不為容器提供具體的網路設定,它是透過CNI(容器網路介面)來實現的。在不同的Node之上,不同的Pod訪問就費了點勁,這正是CNI的工作。常用的CNI外掛有:Flannel、Calico、Canal、Weave。

沒錯,又是一堆名詞,而且各個都很難搞。

網路方面是k8s最複雜的知識點,框架也奇多,後面的文章會專門進行介紹。

4. 內部元件

在開啟更多的Kind之前,我們來看一下k8s的內部元件。

下面這張圖,就是官網的一張圖片,說明了k8s的一系列必要的元件。其中,

etcd

根本就不是這個體系裡的,但k8s的一些持久化狀態,需要有個地方存,就引入了這麼一個元件,用來儲存配置資訊。

k8s主要概念大梳理!

其中左半部分,是k8s自身的元件;右半部分,就在每個Node(也就是物理機)上的守護程序。它們的作用如下:

kube-apiserver 提供Rest介面,屬於k8s的靈魂,所有的認證、授權、訪問控制、服務發現等功能,都透過它來暴露

kube-scheduler 一看就是個排程元件,實際上它的作用也是這樣。它會監聽未排程的 Pod,實現你指定的目標

kube-controller-manager 負責維護整個k8s叢集的狀態。注意是k8s叢集的狀態,它不管Pod

kubelet 這是個守護程序,用來和apiserver通訊,彙報自己Node的狀態;一些排程命令,也是透過kubelet來接收執行任務

kube-proxy kube-proxy其實就是管理service的訪問入口,包括叢集內Pod到Service的訪問和叢集外訪問service。我們上面提到的四種模式,就是透過proxy進行轉發的

這些元件的職責,已經是非常非常清楚了。難點還是在多種Kind概念上。

5. 更多概念

k8s主要概念大梳理!

圖中的這些概念,本質上都是在

Pod

之上,又包了一層。層次越高,功能越抽象,依賴的配置也越多。下面將挑主要的進行介紹。

StatefulSet Deployment部署後的例項,它的id都是隨機的,比如

bcmall-deployment-5d45f98bd9

,它是無狀態的。與此對用的是StatefulSet,生成的例項名稱是類似

bcmall-deployment-1

這樣的。它具備固定的網路標記,比如主機名,域名等,可以按照順序來部署和擴充套件,非常適合類似MySQL這樣的例項部署

DaemonSet 用於確保叢集中的每一個節點只執行特定的pod副本,通常用於實現系統級後臺任務

configMap和Secret 顧名思義,就是做配置用的,因為容器或多或少會需要外部傳入一些環境變數。可以用來實現業務配置的統一管理, 允許將配置檔案與映象檔案分離,以使容器化的應用程式具有可移植性

PV和PVC 業務執行就需要儲存,可以透過PV進行定義。PV的生命週期獨立於Pod的生命週期,就是一段網路儲存;PVC是使用者對於儲存的需求:Pod消耗節點資源,PVC消耗PV資源,PVC和PV是一一對應的。沒錯,它們都是透過yml檔案宣告的

**StorageClass ** 可以實現動態PV,是更進一步的封裝

Job 只要完成就立即退出,不需要重啟或重建

Cronjob 週期性任務控制,不需要持續後臺執行

CRD

6. 資源限制

很好,我們終於要聊一點資源限制方面的內容了。k8s的資源限制,仍然是透過

cgroup

來實現的。

k8s提供了

requests

limits

兩種型別引數對資源進行預分配和使用限制。

不要被這兩個詞給迷惑了。requests就相當於JVM引數中的

-Xms

,limits就相當於

-Xmx

。所以,如果你類比著把這兩個值設定成一樣的,是一種最佳的實踐方式。

只是它的設定有點怪異:

requests:

memory: “64Mi”

cpu: “250m”

limits:

memory: “128Mi”

cpu: “500m”

記憶體的單位是

Mi

,而cpu的單位是

m

,要多彆扭有多彆扭,但它是有原因的。

m

毫核

的意思。比如,我們的作業系統有4核,把它乘以1000,那就是總CPU資源是4000毫核。如果你想要你的應用最多佔用

1/4

核,那就設定成

250m

再來看記憶體,Mi是MiB的意思,我也搞不懂為啥不用MB,而使用Mi,可能是想讓你印象深刻吧(MB和MiB還真不一樣)。

若記憶體使用超出限制,會引發系統的OOM機制,但CPU不會,頂多會影響系統執行罷了。

k8s還提供了叫做

LimitRange

ResourceQuota

的Kind,用來限定CPU和Memory的申請範圍,功能更加高階。

7. 叢集搭建工具

k8s主要概念大梳理!

k8s的叢集搭建,常見的有kind,minikube,kubeadm,rancher2等。其中rancher2可以說是相對來說比較容易的方式。它考慮了一些網路不通的因素,有一些推薦的代理選項,對於新手來說,拿來嚐嚐鮮還是很好用的。

但在正常的使用中,還是推薦你使用kubeadm這個工具,這是官方維護和推薦的搭建工具,並做到了向下相容,按照官方的指引,只需要

kubeadm init

就能搭建完畢。至於什麼監控、日誌等,反倒是好處理的了。

k8s最麻煩的有三點:

yml檔案概念爆炸

網路方案多樣、複雜

許可權、證書配置繁瑣

搞懂了這三個方面,可以說玩轉k8s就沒問題了。

k8s主要概念大梳理!