做者 | 聲東 阿里雲售後技術專家linux
導讀:不知道你們有沒有意識到一個現實:大部分時候,咱們已經不像之前同樣,經過命令行,或者可視窗口來使用一個系統了。git
如今咱們上微博、或者網購,操做的其實不是眼前這臺設備,而是一個又一個集羣。一般,這樣的集羣擁有成百上千個節點,每一個節點是一臺物理機或虛擬機。集羣通常遠離用戶,坐落在數據中心。爲了讓這些節點互相協做,對外提供一致且高效的服務,集羣須要操做系統。Kubernetes 就是這樣的操做系統。github
比較 Kubernetes 和單機操做系統,Kubernetes 至關於內核,它負責集羣軟硬件資源管理,並對外提供統一的入口,用戶能夠經過這個入口來使用集羣,和集羣溝通。web
而運行在集羣之上的程序,與普通程序有很大的不一樣。這樣的程序,是「關在籠子裏」的程序。它們從被製做,到被部署,再到被使用,都不尋常。咱們只有深挖根源,才能理解其本質。算法
咱們使用 go 語言寫了一個簡單的 web 服務器程序 app.go,這個程序監聽在 2580 這個端口。經過 http 協議訪問這個服務的根路徑,服務會返回 "This is a small app for kubernetes..." 字符串。json
package main import ( "github.com/gorilla/mux" "log" "net/http" ) func about(w http.ResponseWriter, r *http.Request) { w.Write([]byte("This is a small app for kubernetes...\n")) } func main() { r := mux.NewRouter() r.HandleFunc("/", about) log.Fatal(http.ListenAndServe("0.0.0.0:2580", r)) }
使用 go build 命令編譯這個程序,產生 app 可執行文件。這是一個普通的可執行文件,它在操做系統裏運行,會依賴系統裏的庫文件。小程序
# ldd app linux-vdso.so.1 => (0x00007ffd1f7a3000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f554fd4a000) libc.so.6 => /lib64/libc.so.6 (0x00007f554f97d000) /lib64/ld-linux-x86-64.so.2 (0x00007f554ff66000)
爲了讓這個程序不依賴於操做系統自身的庫文件,咱們須要製做容器鏡像,即隔離的運行環境。Dockerfile 是製做容器鏡像的「菜譜」。咱們的菜譜就只有兩個步驟,下載一個 centos 的基礎鏡像,把 app 這個可執行文件放到鏡像中 /usr/local/bin 目錄中去。centos
FROM centos ADD app /usr/local/bin
製做好的鏡像存再本地,咱們須要把這個鏡像上傳到鏡像倉庫裏去。這裏的鏡像倉庫,至關於應用商店。咱們使用阿里雲的鏡像倉庫,上傳以後鏡像地址是:api
registry.cn-hangzhou.aliyuncs.com/kube-easy/app:latest
鏡像地址能夠拆分紅四個部分:倉庫地址/命名空間/鏡像名稱:鏡像版本。顯然,鏡像上邊的鏡像,在阿里雲杭州鏡像倉庫,使用的命名空間是 kube-easy,鏡像名:版本是 app:latest。至此,咱們有了一個能夠在 Kubernetes 集羣上運行的、「關在籠子裏」的小程序。瀏覽器
Kubernetes 做爲操做系統,和普通的操做系統同樣,有 API 的概念。有了 API,集羣就有了入口;有了 API,咱們使用集羣,才能得其門而入。Kubernetes 的 API 被實現爲運行在集羣節點上的組件 API Server。這個組件是典型的 web 服務器程序,經過對外暴露 http(s) 接口來提供服務。
這裏咱們建立一個阿里雲 Kubernetes 集羣。登陸集羣管理頁面,咱們能夠看到 API Server 的公網入口。
API Server 內網鏈接端點: https://xx.xxx.xxx.xxx:6443
阿里雲 Kubernetes 集羣 API Server 組件,使用基於 CA 簽名的雙向數字證書認證來保證客戶端與 api server 之間的安全通訊。這句話很繞口,對於初學者不太好理解,咱們來深刻解釋一下。
從概念上來說,數字證書是用來驗證網絡通訊參與者的一個文件。這和學校頒發給學生的畢業證書相似。在學校和學生之間,學校是可信第三方 CA,而學生是通訊參與者。若是社會廣泛信任一個學校的聲譽的話,那麼這個學校頒發的畢業證書,也會獲得社會承認。參與者證書和 CA 證書能夠類比畢業證和學校的辦學許可證。
這裏咱們有兩類參與者,CA 和普通參與者;與此對應,咱們有兩種證書,CA 證書和參與者證書;另外咱們還有兩種關係,證書籤發關係以及信任關係。這兩種關係相當重要。
咱們先看簽發關係。以下圖,咱們有兩張 CA 證書,三個參與者證書。
其中最上邊的 CA 證書,簽發了兩張證書,一張是中間的 CA 證書,另外一張是右邊的參與者證書;中間的 CA 證書,簽發了下邊兩張參與者證書。這六張證書以簽發關係爲聯繫,造成了樹狀的證書籤發關係圖。
然而,證書以及簽發關係自己,並不能保證可信的通訊能夠在參與者之間進行。以上圖爲例,假設最右邊的參與者是一個網站,最左邊的參與者是一個瀏覽器,瀏覽器相信網站的數據,不是由於網站有證書,也不是由於網站的證書是 CA 簽發的,而是由於瀏覽器相信最上邊的 CA,也就是信任關係。
理解了 CA(證書),參與者(證書),簽發關係,以及信任關係以後,咱們回過頭來看「基於 CA 簽名的雙向數字證書認證」。客戶端和 API Server 做爲通訊的普通參與者,各有一張證書。而這兩張證書,都是由 CA 簽發,咱們簡單稱它們爲集羣 CA 和客戶端 CA。客戶端信任集羣 CA,因此它信任擁有集羣 CA 簽發證書的 API Server;反過來 API Server 須要信任客戶端 CA,它才願意與客戶端通訊。
阿里雲 Kubernetes 集羣,集羣 CA 證書,和客戶端 CA 證書,實現上實際上是一張證書,因此咱們有這樣的關係圖。
登陸集羣管理控制檯,咱們能夠拿到 KubeConfig 文件。這個文件包括了客戶端證書,集羣 CA 證書,以及其餘。證書使用 base64 編碼,因此咱們可使用 base64 工具解碼證書,並使用 openssl 查看證書文本。
Certificate: Data: Version: 3 (0x2) Serial Number: 787224 (0xc0318) Signature Algorithm: sha256WithRSAEncryption Issuer: O=c0256a3b8e4b948bb9c21e66b0e1d9a72, OU=default, CN=c0256a3b8e4b948bb9c21e66b0e1d9a72 Validity Not Before: Nov 29 06:03:00 2018 GMT Not After : Nov 28 06:08:39 2021 GMT Subject: O=system:users, OU=, CN=252771643302762862
Certificate: Data: Version: 3 (0x2) Serial Number: 787224 (0xc0318) Signature Algorithm: sha256WithRSAEncryption Issuer: O=c0256a3b8e4b948bb9c21e66b0e1d9a72, OU=default, CN=c0256a3b8e4b948bb9c21e66b0e1d9a72 Validity Not Before: Nov 29 06:03:00 2018 GMT Not After : Nov 28 06:08:39 2021 GMT Subject: O=system:users, OU=, CN=252771643302762862
Certificate: Data: Version: 3 (0x2) Serial Number: 2184578451551960857 (0x1e512e86fcba3f19) Signature Algorithm: sha256WithRSAEncryption Issuer: O=c0256a3b8e4b948bb9c21e66b0e1d9a72, OU=default, CN=c0256a3b8e4b948bb9c21e66b0e1d9a72 Validity Not Before: Nov 29 03:59:00 2018 GMT Not After : Nov 29 04:14:23 2019 GMT Subject: CN=kube-apiserver
Certificate: Data: Version: 3 (0x2) Serial Number: 786974 (0xc021e) Signature Algorithm: sha256WithRSAEncryption Issuer: C=CN, ST=ZheJiang, L=HangZhou, O=Alibaba, OU=ACS, CN=root Validity Not Before: Nov 29 03:59:00 2018 GMT Not After : Nov 24 04:04:00 2038 GMT Subject: O=c0256a3b8e4b948bb9c21e66b0e1d9a72, OU=default, CN=c0256a3b8e4b948bb9c21e66b0e1d9a72
理解了原理以後,咱們能夠作一個簡單的測試:以證書做爲參數,使用 curl 訪問 api server,並獲得預期結果。
# curl --cert ./client.crt --cacert ./ca.crt --key ./client.key https://xx.xx.xx.xxx:6443/api/ { "kind": "APIVersions", "versions": [ "v1" ], "serverAddressByClientCIDRs": [ { "clientCIDR": "0.0.0.0/0", "serverAddress": "192.168.0.222:6443" } ] }
如開始所講,Kubernetes 是管理集羣多個節點的操做系統。這些節點在集羣中的角色,卻沒必要徹底同樣。Kubernetes 集羣有兩種節點:master 節點和 worker 節點。
這種角色的區分,實際上就是一種分工:master 負責整個集羣的管理,其上運行的以集羣管理組件爲主,這些組件包括實現集羣入口的 api server;而 worker 節點主要負責承載普通任務。
在 Kubernetes 集羣中,任務被定義爲 pod 這個概念。pod 是集羣可承載任務的原子單元,pod 被翻譯成容器組,實際上是意譯,由於一個 pod 實際上封裝了多個容器化的應用。原則上來說,被封裝在一個 pod 裏邊的容器,應該是存在至關程度的耦合關係。
調度算法須要解決的問題,是替 pod 選擇一個溫馨的「居所」,讓 pod 所定義的任務能夠在這個節點上順利地完成。
爲了實現「擇優而居」的目標,Kubernetes 集羣調度算法採用了兩步走的策略:
下面咱們使用文章開始的時候製做的鏡像,建立一個 pod,並經過日誌來具體分析一下,這個 pod 怎麼樣被調度到某一個集羣節點。
首先,咱們建立 pod 的配置文件,配置文件格式是 json。這個配置文件有三個地方比較關鍵,分別是鏡像地址,命令以及容器的端口。
{ "apiVersion": "v1", "kind": "Pod", "metadata": { "name": "app" }, "spec": { "containers": [ { "name": "app", "image": "registry.cn-hangzhou.aliyuncs.com/kube-easy/app:latest", "command": [ "app" ], "ports": [ { "containerPort": 2580 } ] } ] } }
集羣調度算法被實現爲運行在 master 節點上的系統組件,這一點和 api server 相似。其對應的進程名是 kube-scheduler。kube-scheduler 支持多個級別的日誌輸出,但社區並無提供詳細的日誌級別說明文檔。查看調度算法對節點進行篩選、打分的過程,咱們須要把日誌級別提升到 10,即加入參數 --v=10。
kube-scheduler --address=127.0.0.1 --kubeconfig=/etc/kubernetes/scheduler.conf --leader-elect=true --v=10
使用 curl,以證書和 pod 配置文件等做爲參數,經過 POST 請求訪問 api server 的接口,咱們能夠在集羣裏建立對應的 pod。
# curl -X POST -H 'Content-Type: application/json;charset=utf-8' --cert ./client.crt --cacert ./ca.crt --key ./client.key https://47.110.197.238:6443/api/v1/namespaces/default/pods -d@app.json
預選是 Kubernetes 調度的第一步,這一步要作的事情,是根據預先定義的規則,把不符合條件的節點過濾掉。不一樣版本的 Kubernetes 所實現的預選規則有很大的不一樣,但基本的趨勢,是預選規則會愈來愈豐富。
比較常見的兩個預選規則是 PodFitsResourcesPred 和 PodFitsHostPortsPred。前一個規則用來判斷,一個節點上的剩餘資源,是否是可以知足 pod 的需求;然後一個規則,檢查一個節點上某一個端口是否是已經被其餘 pod 所使用了。
下圖是調度算法在處理測試 pod 的時候,輸出的預選規則的日誌。這段日誌記錄了預選規則 CheckVolumeBindingPred 的執行狀況。某些類型的存儲卷(PV),只能掛載到一個節點上,這個規則能夠過濾掉不知足 pod 對 PV 需求的節點。
從 app 的編排文件裏能夠看到,pod 對存儲卷並無什麼需求,因此這個條件並無過濾掉節點。
調度算法的第二個階段是優選階段。這個階段,kube-scheduler 會根據節點可用資源及其餘一些規則,給剩餘節點打分。
目前,CPU 和內存是調度算法考量的兩種主要資源,但考量的方式並非簡單的,剩餘 CPU、內存資源越多,得分就越高。
日誌記錄了兩種計算方式:LeastResourceAllocation 和 BalancedResourceAllocation。
這兩種方式,一種傾向於選出資源使用率較低的節點,第二種但願選出兩種資源使用比例接近的節點。這兩種方式有一些矛盾,最終依靠必定的權重來平衡這兩個因素。
除了資源以外,優選算法會考慮其餘一些因素,好比 pod 與節點的親和性,或者若是一個服務有多個相同 pod 組成的狀況下,多個 pod 在不一樣節點上的分散程度,這是保證高可用的一種策略。
最後,調度算法會給全部的得分項乘以它們的權重,而後求和獲得每一個節點最終的得分。由於測試集羣使用的是默認調度算法,而默認調度算法把日誌中出現的得分項所對應的權重,都設置成了 1,因此若是按日誌裏有記錄得分項來計算,最終三個節點的得分應該是 29,28 和 29。
之因此會出現日誌輸出的得分和咱們本身計算的得分不符的狀況,是由於日誌並無輸出全部的得分項,猜想漏掉的策略應該是 NodePreferAvoidPodsPriority,這個策略的權重是 10000,每一個節點得分 10,因此才得出最終日誌輸出的結果。
在本文中,咱們以一個簡單的容器化 web 程序爲例,着重分析了客戶端怎麼樣經過 Kubernetes 集羣 API Server 認證,以及容器應用怎麼樣被分派到合適節點這兩件事情。
在分析過程當中,咱們棄用了一些便利的工具,好比 kubectl,或者控制檯。咱們用了一些更接近底層的小實驗,好比拆解 KubeConfig 文件,再好比分析調度器日誌來分析認證和調度算法的運做原理。但願這些對你們進一步理解 Kubernetes 集羣有所幫助。
「阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的技術圈。」