建立、執行及共享容器映象 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