Kubernetes 實戰 —— 02. 開始使用 Kubernetes 和 Docker
- 2021 年 3 月 7 日
- 筆記
- Kubernetes, Kubernetes 實戰
創建、運行及共享容器鏡像 P23
運行容器 P24
運行 P24
可以運行 Docker 客戶端可執行文件來執行各種 Docker 命令。例如:可以試著從 Docker Hub 的公共鏡像倉庫拉取、運行鏡像。 Docker Hub 中有許多隨時可用的常見鏡像,其中就包括 busybox
,可以用來運行簡單的命令,例如: echo "Hello world"
。 P24
docker run busybox echo "Hello world"
原理 P25
執行 docker run
命令後: P25
- Docker 檢查
busybox:lastest
鏡像是否存在於本機。如果不存在,則會自動從 Docker 鏡像中心拉取鏡像 - Docker 基於
busybox:lastest
鏡像創建一個容器並在容器中運行命令echo "Hello world"
運行鏡像 P25
docker run <image>
docker run <image>:<tag>
Docker 支援同一鏡像的多個版本,每個版本必須有唯一的 tag 名,當引用鏡像沒有顯式地指定 tag 時, Docker 會默認指定 tag 為 latest
。 P25
創建應用 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.jsP27
ENTRYPOINT
行定義了當鏡像被運行時需要執行的命令P27
構建容器鏡像 P27
docker build -t kubia .
上述命令會基於當前目錄下的 Dockerfile 文件中的指令創建一個名為 kubia
的鏡像。 P27
鏡像是如何構建的 P28
構建過程不是由 Docker 客戶端運行的,而是將整個目錄的文件上傳到 Docker 守護進程並在那裡進行的。如果在一台非 Linux 作業系統中使用 Docker ,客戶端運行在宿主機作業系統上,而守護進程運行在一個虛擬機內。 P28
提示: 不要在構建目錄中包含任何不需要的文件,這樣會減慢構建的速度,尤其是 Docker 守護進程運行在一個遠端機器的時候。 P28
鏡像分層 P28
鏡像不是一個大的二進位塊,而是由許多層組成的,不同鏡像可能會共享分層,這樣會讓存儲和傳輸變得更加高效。 P28
構建鏡像時, Dockerfile 中每一條單獨的指令都會創建一個新層,最後一層會被標記為指定的鏡像名。 P29
可以通過 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
上述命令不會重命名標籤,而是給同一個鏡像創建一個額外的標籤。 kubia
和 idealism/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
在 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
列出 pod P41
kubectl get pods
: 查看 pod 的重要資訊: pod 名稱、 READY 、狀態、重啟次數和 AGEkubectl describe pod <pod-name>
: 查看指定 pod 的詳細資訊
幕後發生的事情 P42
上圖顯示了在 Kubernetes 中運行容器鏡像所必須的兩個步驟: P42
- 構建鏡像並將其推送到 Docker Hub
- 運行 kubectl 命令
- 命令向 Kubernetes API 伺服器發送一個 REST HTTP 請求
- 創建一個新的 pod ,調度器將其調度到一個工作節點上
- Kubelet 看到 pod 被調度到節點上,就告知 Docker 從鏡像中心中拉取指定的鏡像,因為本地沒有該鏡像
- 下載鏡像後, 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
可以發現 LoadBalancer
的 EXTERNAL-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
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
查看應用運行在哪個節點上 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