Kubernetes 實戰 —— 02. 開始使用 Kubernetes 和 Docker

滿賦諸機發表於2021-03-07

建立、執行及共享容器映象 P23

執行容器 P24

執行 P24

可以執行 Docker 客戶端可執行檔案來執行各種 Docker 命令。例如:可以試著從 Docker Hub 的公共映象倉庫拉取、執行映象。 Docker Hub 中有許多隨時可用的常見映象,其中就包括 busybox ,可以用來執行簡單的命令,例如: echo "Hello world"P24

docker run busybox echo "Hello world"

原理 P25

圖 2.1 在一個基於 busybox 映象的容器中執行 echo Hello world

執行 docker run 命令後: P25

  1. Docker 檢查 busybox:lastest 映象是否存在於本機。如果不存在,則會自動從 Docker 映象中心拉取映象
  2. Docker 基於 busybox:lastest 映象建立一個容器並在容器中執行命令 echo "Hello world"

執行映象 P25

docker run <image>
docker run <image>:<tag>

Docker 支援同一映象的多個版本,每個版本必須有唯一的 tag 名,當引用映象沒有顯式地指定 tag 時, Docker 會預設指定 tag 為 latestP25

建立應用 P26
const http = require('http');
const os = require('os');

console.log("Kubia server starting...");

var handler = function(request, response) {
  console.log("Received request from " + request.connection.remoteAddress);
  response.writeHead(200);
  response.end("You've hit " + os.hostname() + "\n");
};

var www = http.createServer(handler);
www.listen(8080);

這個應用會接收 HTTP 請求並響應應用執行的伺服器真實主機名(非宿主機名),當其部署在 Kubernetes 上並進行伸縮時(水平伸縮,複製應用到多個節點),可以發現 HTTP 請求切換到了應用的不同例項上。 P26

建立 Dockerfile P27
FROM node:7
ADD app.js /app.js
ENTRYPOINT ["node", "app.js"]
  • FROM 行定義了映象的起始內容(構建所基於的基礎映象) P27
  • ADD 行把 app.js 檔案從本地新增到映象的根目錄,並保持檔名為 app.js P27
  • ENTRYPOINT 行定義了當映象被執行時需要執行的命令 P27
構建容器映象 P27
docker build -t kubia .

上述命令會基於當前目錄下的 Dockerfile 檔案中的指令建立一個名為 kubia 的映象。 P27

圖 2.2 基於 Dockerfile 構建一個新的容器映象

映象是如何構建的 P28

構建過程不是由 Docker 客戶端執行的,而是將整個目錄的檔案上傳到 Docker 守護程式並在那裡進行的。如果在一臺非 Linux 作業系統中使用 Docker ,客戶端執行在宿主機作業系統上,而守護程式執行在一個虛擬機器內。 P28

提示: 不要在構建目錄中包含任何不需要的檔案,這樣會減慢構建的速度,尤其是 Docker 守護程式執行在一個遠端機器的時候。 P28

映象分層 P28

映象不是一個大的二進位制塊,而是由許多層組成的,不同映象可能會共享分層,這樣會讓儲存和傳輸變得更加高效。 P28

構建映象時, Dockerfile 中每一條單獨的指令都會建立一個新層,最後一層會被標記為指定的映象名。 P29

圖 2.3 容器映象是由多層組成的,每一層可以被不同的映象複用

可以通過 docker images 檢視本地儲存的映象。

執行容器映象 P30
docker run --name kubia-container -p 8080:8080 -d kubia

上述命令會基於 kubia 映象建立一個叫 kubia-container 的新容器。 -d 表示這個容器與命令列分離,意味著在後臺執行。 -p 8080:8080 表示本機上的 8080 埠會對映到容器內的 8080 埠,所以可以通過 localhost:8080 訪問這個應用。 P30

列出所有執行中的容器 P30

docker ps 會列印出每一個容器的 ID 和名稱、容器執行所使用的映象、容器中執行程式碼命令、建立時間、以及當前狀態。 P30

  • -a: 檢視所有容器,包括執行中的和已停止的

獲取更多的容器資訊 P30

docker inspect kubia-container 可以檢視包含容器底層資訊的長 JSON 。 P31

探索執行容器的內部 P31

在已有的容器內部執行 shell P31

docker exec -ti kubia-container bash

上述命令會在已有的 kubia-container 容器內部執行 bash 。 bash 程式會和主容器程式擁有相同的名稱空間,可以用於檢視 Node.js 和應用是如何在容器裡執行的。 P31

  • -t: 分配一個偽終端 (TTY) ,可以顯示命令提示符
  • -i: 確保標準輸入流保持開放,可以在 shell 中輸入命令

進入容器對於除錯容器內執行的應用來說非常有用。出錯時,需要做的第一件事是檢視應用執行的系統的真實狀態。應用不僅擁有獨立的檔案系統,還有程式、使用者、主機名和網路介面。 P32

停止和刪除容器 P32
  • docker stop kubia-container: 停止容器,容器停止後仍然存在 P32
  • docker rm kubia-container: 刪除容器,所有內容都會被刪除並且無法再次啟動 P32
向映象倉庫推送映象 P33

使用附加標籤標註映象 P33

docker tag kubia idealism/kubia

上述命令不會重新命名標籤,而是給同一個映象建立一個額外的標籤。 kubiaidealism/kubia 指向同一個映象 ID ,實際上是同一個映象的兩個標籤。 P33

推送映象 P33

docker push idealism/kubia 可以將本地的映象推送到 idealism 名下的 kubia

在不同機器上執行映象 P33

推送完成之後,就可以通過 docker run --name kubia-container -p 8080:8080 -d kubia 在任何機器上執行同一個映象了。

配置 Kubernetes 叢集 P34

用 Minikube 執行一個本地三節點 Kubernetes 叢集

Minikube 是一個構建單節點叢集的工具,對於測試 Kubernetes 和本地開發應用都非常有用。 P35

  • minikube start --nodes 4: 啟動一個四節點的 Kubernetes 叢集,包含一個主節點和三個工作節點。
  • kuibectl cluster-info: 展示叢集資訊
  • minikube ssh: 登入到 Minikube VM 從內部訪問,可以檢視在節點上執行的程式
  • kubectl get nodes: 列出叢集的節點
  • kubectl describe node minikube: 檢視指定節點的更多資訊

使用 kubectl get nodes 檢視當前叢集的節點資訊如下:

NAME           STATUS   ROLES    AGE   VERSION
minikube       Ready    master   56m   v1.18.2
minikube-m02   Ready    <none>   13m   v1.18.2
minikube-m03   Ready    <none>   11m   v1.18.2
minikube-m04   Ready    <none>   10m   v1.18.2

可以發現有三個節點的角色是 <none> ,需要手動將它們的 ROLES 設定為 worker ,執行如下命令即可:

kubectl label node minikube-m02 node-role.kubernetes.io/worker=worker
kubectl label node minikube-m03 node-role.kubernetes.io/worker=worker
kubectl label node minikube-m04 node-role.kubernetes.io/worker=worker

圖 2.4 如何與三節點 Kubernetes 叢集進行互動

在 Kubernetes 上執行第一個應用 P40

部署 Node.js 應用 P40
kubectl create deployment kubia --image=idealism/kubia

上述命令無須配置檔案即可建立一個名為 kubia 的 development (可以使用 kubectl get developments 檢視),同時自動建立一個名稱字首為 kubia 的 pod (可以使用 kubectl get pods 檢視)。 P40

介紹 pod P41

Kubernetes 不直接處理單個容器,它使用多個共存容器的理念,這組容器叫作 pod 。 P41

一個 pod 是一組緊密相關的容器,它們總是一起執行在同一個工作節點上,以及同一個 Linux 名稱空間中。每個 pod 就像一個獨立的邏輯機器,擁有自己的 IP 、主機名、程式等,執行一個獨立的應用程式。應用程式可以是單個程式,執行在單個容器中,也可以是一個主應用程式和其他支援程式,每個程式都在自己的容器中執行。一個 pod 的所有容器都執行在同一個邏輯機器上,而其他 pod 中的容器,即使執行在同一個工作節點上,也是執行在不同的邏輯機器上。 P41

圖 2.5 容器、 pod 及物理工作節點之間的關係

列出 pod P41

  • kubectl get pods: 檢視 pod 的重要資訊: pod 名稱、 READY 、狀態、重啟次數和 AGE
  • kubectl describe pod <pod-name>: 檢視指定 pod 的詳細資訊

幕後發生的事情 P42

圖 2.6 在 Kubernetes 中執行 kubia 容器映象

上圖顯示了在 Kubernetes 中執行容器映象所必須的兩個步驟: P42

  1. 構建映象並將其推送到 Docker Hub
  2. 執行 kubectl 命令
    1. 命令向 Kubernetes API 伺服器傳送一個 REST HTTP 請求
    2. 建立一個新的 pod ,排程器將其排程到一個工作節點上
    3. Kubelet 看到 pod 被排程到節點上,就告知 Docker 從映象中心中拉取指定的映象,因為本地沒有該映象
    4. 下載映象後, Docker 建立並執行容器

排程 (scheduling): 將 pod 分配給一個節點。 pod 會立即執行,而不是將要執行。 P43

訪問 Web 應用 P43

通過 kubectl describe pod <pod-name> 可以看到 pod 自己的 IP ,不過這個地址只能從叢集內部訪問。通過建立一個 LoadBalancer 型別的服務,可以建立一個外部的負載均衡,通過負載均衡的公共 IP 就可以訪問 pod 。 P43

建立一個服務物件 P43

kubectl expose deployment kubia --type=LoadBalancer --name kubia-http --port=8080: 對外暴露之前建立的 Pod P43

大多數資源都有縮寫: P43

  • po: pod 的縮寫
  • svc: service 的縮寫
  • rc: replicationcontroller 的縮寫 (目前已 不推薦使用該控制器
  • deploy: deployment 的縮寫
  • rs: replicaset 的縮寫

由於我們使用的是 minikube ,所以沒有整合 LoadBalancer ,執行完上述命令後,使用 kubectl get services 會得到如下結果:

NAME         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
kubernetes   ClusterIP      10.96.0.1      <none>        443/TCP          37m
kubia-http   LoadBalancer   10.100.74.254  <pending>     8080:32342/TCP   5m52s

可以發現 LoadBalancerEXTERNAL-IP 一直處於 <pending>,我們可以使用 minikube 自帶的 minikube tunnel 命令可以完成暴露,執行完後,可得如下結果:

NAME         TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)          AGE
kubernetes   ClusterIP      10.96.0.1      <none>         443/TCP          39m
kubia-http   LoadBalancer   10.100.74.254  10.100.74.254  8080:32342/TCP   7m56s

列出服務 P44

expose 命令的輸出是一個名為 kubia-http 的服務。服務是類似於 pod 和 node 的物件,因此可以通過 kubectl get services 命令檢視新建立的服務物件。

使用外部 IP 訪問服務 P44

curl 10.100.74.254:8080
# 輸出如下:
# You've hit kubia-5b6b94f7f8-h6dbr

可以發現應用將 pod 名稱作為它的主機名。 P45

使用 minikube 為叢集新增節點可能會導致新增的節點無法訪問(在這裡耗時幾小時終於搞明白了,還是要多讀文件 ),可以按照文件中進行設定;當然也有可能是等待的時間不夠,設定等操作還沒有結束(按照前面的配置了後,還是無法訪問,以為哪裡操作有問題,各種操作順序都試了還是無法訪問,最後等待了近 10 min 就自動好了。。。)

系統的邏輯部分 P45

Deployment 、 pod 和服務是符合組合在一起的 P45

Kubernetes 的基本構建是 pod 。但是,我們前面並沒有真的直接建立出任何 pod 。通過執行 kubectl create deployment 命令,建立了一個 Deployment 控制器,它用於建立 pod 例項。為了使該 pod 能夠從叢集外部訪問,需要讓 Kubernetes 將該 Deployment 管理的所有 pod 由一個服務對外暴露。 P45

圖 2.7 由 Deployment 、 pod 和服務組成的系統

pod 和它的容器 P45

系統中最重要的元件是 pod ,一個 pod 中可以包含任意數量的容器。 pod 有自己獨立的私有 IP 地址和主機名。 P46

Deployment 的角色 P46

Deployment 控制器確保始終存在一個執行中的 pod 例項。通常, Deployment 用於複製 pod (即建立 pod 的多個副本)並讓它們保持執行。如果 pod 因為任何原因消失了,那麼 Deployment 將建立一個新的 pod 來替換消失的 pod 。 P46

為什麼需要服務 P46

系統的第三個元件是 kubia-http 服務。 pod 的存在是短暫的,一個 pod 可能會在任何時候消失(1. 所在節點發生故障; 2. 有人刪除了 pod ; 3. pod 被從一個健康的節點剔除了),此時消失的 pod 將被 Deployment 替換為新的 pod 。新舊 pod 具有不同的 IP 地址。所以需要一個服務來解決不斷變化的 pod IP 地址的問題,以及在一個固定的 IP 和埠對上對外暴露多個 pod 。 P46

服務表示一組或多組提供相同服務的 pod 的靜態地址。到達服務 IP 和埠的請求將被轉發到屬於該服務的一個容器的 IP 和埠。 P46

水平伸縮 P46
kubectl get deployments
# 輸出如下:
# NAME    READY   UP-TO-DATE   AVAILABLE   AGE
# kubia   1/1     1            1           49m

使用 kubectl get 可以檢視所有型別的資源,上述命令檢視所有的 Deployment ,目前僅有一個名為 kubia 的控制器,總共需要 1 個 pod ,目前已有 1 個 pod 已準備好, 1 個 pod 已是最新版本, 1 個 pod 可用。

增加期望的副本數 P47

kubectl scale deployment kubia --replicas=3
# 輸出如下:
# deployment.apps/kubia scaled

上述命令告訴 Kubernetes 需要確保 pod 始終有三個例項在執行。這是 Kubernetes 最基本的原則之一。不告訴 Kubernetes 應該執行什麼操作,而是宣告性地改變系統的期望狀態,並讓 Kubernetes 檢查當前的狀態是否與期望的狀態一致。 P47

打到服務上到請求會打到所有到三個 pod 上 P47

curl 10.100.74.254:8080
# 由於存在三個 pod ,所以有以下三種輸出
# You've hit kubia-5b6b94f7f8-h6dbr
# You've hit kubia-5b6b94f7f8-wwbdh
# You've hit kubia-5b6b94f7f8-zzxks

當 pod 有多個例項時 Kubernetes 服務作為負載均衡擋在多個 pod 前面,請求隨機地打到不同的 pod 。 P48

圖 2.8 由同一 Deployment 管理並通過服務 IP 和埠暴露的 pod 的三個例項

檢視應用執行在哪個節點上 P49
kubectl get pods -o wide
# 輸出如下:
# NAME                     READY   STATUS    RESTARTS   AGE   IP           NODE           ...
# kubia-5b6b94f7f8-h6dbr   1/1     Running   0          12m   10.244.3.2   minikube-m04   ...
# kubia-5b6b94f7f8-wwbdh   1/1     Running   0          12m   10.244.2.2   minikube-m03   ...
# kubia-5b6b94f7f8-zzxks   1/1     Running   0          55m   10.244.1.2   minikube-m02   ...

-o wide: 顯示 pod 的 IP 和所執行的節點,以及其他部分資訊。 P49

介紹 Kubernetes dashboard P50

訪問 Minikube 的 dashboard P51

minikube dashboard

以上命令會啟動 dashboard ,並輸出可訪問的網址供瀏覽器中開啟。 P51

實際操作時,發現這個命令會一直卡在 Verifying proxy health ... 這一行,隨後會返回 503 錯誤,目前 github 上已有類似的 issue 。我按照討論中的方法發現, kubernetes-dashboard namespace 下的兩個 pod 都成功啟動了,並且可以通過 kubectl port-forward -n kubernetes-dashboard pod/kubernetes-dashboard-696dbcc666-gbgwl 9090:9090 將埠對映到本地並能成功訪問。

本文首發於公眾號:滿賦諸機(點選檢視原文) 開源在 GitHub :reading-notes/kubernetes-in-action

相關文章