Kubernetes 入門

自動化容器化應用程式和服務的部署、擴展和管理

Du Spirit
Java Magazine 翻譯系列
21 min readMay 15, 2024

--

Translated from “Getting Started with Kubernetes”, Jesse Butler, Java Magazine May/June 2019. Copyright Oracle Corporation.

如今,單體應用程式被視為一種反模式。對於多數使用案例,雲端是首選的布署平台。這兩項變化遠遠不僅是在別人的電腦上啟動虛擬機器,有效利用雲端的力量與擴展性,意味著要離開過去的單體架構,採用新的架構與開發實踐。

微服務架構是當前在雲端交付應用程式與服務的標準 [譯註:提醒一下,這是 2019 年的文章,微服務在多年後是否還是首選,個人覺得是未定論],應用程式被拆成鬆散耦合的獨立程序,有個自的職責。採用此架構,團隊可更獨立的工作 —— 推動各自的功能 —— 不用與整個組織的路線圖綁住。此外,獨立的軟體元件,測試可以簡化,更流暢地布署。

採用微服務帶來一系列的挑戰。建立與布署幾個服務到虛擬機器是第一步,但你如何管理完整的軟體生命週期?這個需求驅動容器的採用,使用容器解決微服務的幾個問題如下:

  • 應用程式與主機環境脫鉤,提供更好的可攜性。
  • 容器輕量且相對透明,提升可擴展性。
  • 軟體與其相依打包在一起。

鑒於這些優點,容器是打包和布署微服務的絕佳選擇,但容器不是魔術,本質上,它們仍是軟體,你需要一個方法大規模地布署、管理和維護你的容器。曾經,開發人員只有一個單體需要監控與維護,現在,可能有數十甚至上百個服務,這時輪到 Kubernetes 上場。

Kubernetes 是個開源平台用於自動化布署、擴產和管理容器化的應用程式及服務。它是為了解決 Google 在布署和管理大量容器遇到的挑戰而生的,開源並捐贈給 Cloud Native Computing Foundation (CNCF),該基金會促進雲端原生運算的生態系,並成為史上最快速發展的開源專案之一,Kubernetes 目前有超過 2,300 名貢獻者,並被大大小小的公司採用,包含 Fortune 100 半數的公司。

開始使用 Kubernetes

如何開始?Kubernetes 有一個龐大的生態系,許多支援的專案圍繞著它。這場景可能讓人卻步,尋求簡單問題的答案可能讓你困惑,容易讓你覺得遠遠落後。然而,開始的幾步是簡單的,然後隨著你的需要,進一步探索。在這文章中,我將示範如何:

  • 設定 Docker 和 Kubernetes 的本地開發環境
  • 用 Helidon 建立簡單的 Java 微服務
  • 用 Docker 將微服務變成容器映像檔
  • 在本地 Kubernetes 叢集中布署微服務
  • 在叢集中擴展與縮減微服務

你需要在本地安裝一些工具來進行本教學:

  • Docker 18.02 或更新版本
  • Kubernetes 1.7.4 或更新版本
  • JDK 8 或更新版本
  • Maven 3.5 或更新版本

你可以在 macOS、Linux 或 Windows 上安裝這些需求的最新版本。

如果你還沒有安裝 Docker,參閱《Getting Started with Docker guide》按照你的平台指示進行安裝,你需要熟悉 Docker 的基礎操作。

你可使用任何已有的 Kubernetes 叢集,但本文接下來使用 Minikube,Minikube 在本地虛擬機器上執行單一節點的 Kubernetes,足夠讓你開始使用,如果你沒有安裝 Minikube,參閱Minikube 安裝文件

在 Kubernetes 中,服務是個抽象層,定義存取一個 pod 或一組 pods 的方式。

現在,你的 Docker 已經準備好,並且能 Minikube 啟動本地 Kubernetes 叢集,你需要一個範例微服務進行操作,受 Java Magazine 三四月號微服務框架關於 Helidon 文章的啟發 [譯註:這期介紹了很多新框架,想試試 Spring 以外的框架,可以參考這期的文章,過去雙月刊的每一期都有一個主題,個人是蠻喜歡的,現在 Java Magazine 沒有雙月刊的概念了,文章的更新更快但少了點雜誌的味道],在這教學裡,使用 Helidon 建立一個簡單的微服務。

建立一個基本的微服務

要快速使用 Helidon,使用 Helidon 快速入門的 Maven 原型建立新專案,以下步驟替你建立並啟動一個簡單的入門專案:

$ mvn archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.archetypes \
-DarchetypeArtifactId=helidon-quickstart-se \
-DarchetypeVersion=1.0.1 \
-DgroupId=io.helidon.examples \
-DartifactId=helidon-quickstart-se \
-Dpackage=io.helidon.examples.quickstart.se

移動到 helidon-quickstart-se 目錄,然後建置服務:

$ cd helidon-quickstart-se
$ mvn package

就這樣,現在,你有個可運行的微服務範例,這專案會為你的範例微服務建置一個應用程式 JAR 檔案,執行它並確認一切運作正常:

$ java -jar ./target/helidon-quickstart-se.jar
[DEBUG] (main) Using Console logging
2019.03.20 12:52:46 INFO io.helidon.webserver.NettyWebServer Thread[nioEventLoopGroup-2-1,10,main]: Channel '@default'
started: [id: 0xbdfca94d, L:/0:0:0:0:0:0:0:0:8080]
WEB server is up! http://localhost:8080/greet

在另一個終端使用 curl 測試服務的功能:

$ curl -X GET http://localhost:8080/greet
{"message":"Hello World!"}
$ curl -X GET http://localhost:8080/greet/Mary
{"message":"Hello Mary!"}
$ curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Hola"}' /
http://localhost:8080/greet/greeting
$ curl -X GET http://localhost:8080/greet/Maria
{"message":"Hola Maria!"}

這不是一個有趣的服務,但可作為建立容器的範例,在繼續 Kubernetes 之前,先為你的微服務建立 Docker 映像檔。Helidon 有提供範例 Dockerfile,可透過以下方式簡單地建立映像檔:

docker build -t helidon-quickstart-se target

現在,你可以看 Kubernetes 能為你做什麼了。

邁向 Kubernetes

有 Docker,你可以建立容器應像檔,建立容器,管理本地的映像檔與容器。當需要布署容器到正式環境並規模化,Kubernetes 就派上用場了。

在 Kubernetes 中,容器被布署在稱為 pods 的相關容器組中。一個 pod 是一個布署單位,可能包含一個或多個容器。例如,一個 pod 可能包含兩個容器:一個運行網頁伺服器,一個為伺服器提供日誌服務。稍後,你將為你的微服務建立一個 pod,只有一個容器,即你的 helidon-quickstart-se 映像檔的實體。

我們感謝讀者 Deepak Vohra 對這段的更正。

Kubernetes 的一個角色,是確保你的應用服務正常運行,你透過定義一個 deployment [譯註:和 pod 一樣,Kubernetes 專屬的術語不翻譯],描述什麼該被執行與監控,Kubernetes 會監控一個 deployment 中,pods 的健康狀態,當它們失敗或沒有反應,必要時,會重新布署 pods。

讓我們從在本地 Kubernetes 叢集測試一個簡單的範例開始。首先,啟動本地的叢集,使用 minikube start 指令,這指令提供運行的狀態,會等待完成的訊息後再繼續。

$ minikube start
minikube v0.35.0 on darwin (amd64)
...
Done! Thank you for using minikube!

Minikube 的安裝中包含 kubectl,是與 Kubernetes 互動的主要介面,和 Docker 用戶端類似,是個多用途的強大工具,提供與叢集及布署其中的容器進行全方面的互動。

kubectl create 建立一個帶有預先準備的 hello world 範例的 deployment,接著,使用 kubectl get pods 顯示你的 deployment 與其中的 pods,你會看到類似以下的內容:

$ kubectl create deployment hello-node \
--image=gcr.io/hello-minikube-zero-install/hello
deployment.apps/hello-node created
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
hello-node 0/1 1 0 27s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-node-64c578bdf8-5b7jm 1/1 Running 0 10m

預設情況下,Kubernetes 會為一個 pod 配置一個內部 IP 地址,只許叢集內存取,為了讓外部可存取在 pod 裡運行的容器,你需要將 pod 公開成一個 service [譯註:一樣,保留專有名詞不翻譯]。在 Kubernetes 中,service 一個抽象層,定義存取單一個 pod 或是一組 pods 的方法。

建立一個簡單的 service:LoadBalancer,這允許外部透過負載平衡器存取 service。

[編按:為符合頁寬,輸出的部分欄位被忽略了,後續的輸出也是如此。]

$ kubectl expose deployment hello-node --type=LoadBalancer --port=8080 
service/hello-node exposed
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
hello-node LoadBalancer 10.104.108.47 <pending> 8080:30631/TCP
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP
</none></pending>

在雲端供應商或其他 Kubernetes 託管平台上,這動作會分配一個負載平衡器,其 IP 地址會顯示在 EXTERNAL-IP 欄中,在 Minikube,使用 service 指令,會開啟瀏覽器並展示 service 正在運作中:

$ minikube service hello-node

當你的測試完成後,可以卸載並清除這些東西,要做到這點,請用 kubectl delete 指令:

$ kubectl delete service hello-node
service "hello-node" deleted
$ kubectl delete deployment hello-node
deployment.extensions "hello-node" deleted

現在,你有一個本地的 Kubernetes 叢集,也知道它如何運作,該是把你自己的映像檔放到裡面運行了。

布署到 Kubernetes

前一節,你用 kubectl create 的參數建立一個簡單的 deployment。通常,你需要更詳細地描述 deployment,為此,你可以給 kubectl 一個 YAML 檔案。這一步讓你能定義 Kubernetes deployments 的各個面向,用 YAML 定義 deployment 的另一個好處是這些文件能與你的原始碼一起版本控管。

Helidon 的起始專案,在 target/app.yaml 中,包含 deployment 和 service 的樣板配置,用你喜好的編輯器打開這個檔案,看一下裡面的內容:

$ cat target/app.yaml
kind: Service
apiVersion: v1
metadata:
name: helidon-quickstart-se
labels:
app: helidon-quickstart-se
spec:
type: NodePort
selector:
app: helidon-quickstart-se
ports:
- port: 8080
targetPort: 8080
name: http
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: helidon-quickstart-se
spec:
replicas: 1
template:
metadata:
labels:
app: helidon-quickstart-se
version: v1
spec:
containers:
- name: helidon-quickstart-se
image: helidon-quickstart-se
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
---

起初,這內容看起來像是一堆 metadata,但仔細閱讀,你會看到它定義一個 deployment,要包含一個有 helidon-quickstart-se 容器的 pod,以及定義一個 service,讓外部可以存取。注意,service 使用 app 這個名字與 deployment 的標籤 app 一樣。

注意,deployment 規範中,映像檔名稱使用本地的映像檔名稱:helidon-quickstart-se。當你布署到 Kubernetes 時,它會優先從本地尋找該映像檔。你使用本地映像檔,在你本地機器上執行的 Docker 內運行。但問題是 Minikube 是在虛擬機器中運行自己的 Docker,稍早建立的映像檔在那裡是找不到的。

Minikube 有個方便的解法:docker-env 指令。這指令會顯示終端的環境變數,指示 Docker 客戶端使用 Minikube 運行的 Docker 伺服器。在 Linux、UNIX 或 Mac 上,用 eval 呼叫該指令 (請參考文件取得 Windows 上等價的操作),會將變數設定到你目前終端的環境變數中。

$ eval $(minikube docker-env)

現在,當你執行 docker,它將連接到 Minikube 虛擬機器中的 Docker,注意,這配置只在你目前的終端有效,不是一個永久的修改。

現在和之前一樣,你可以建立映像檔,但這次,會在 Minikube 的虛擬機器裡建置,映像檔也會存於其中:

$ docker build -t helidon-quickstart-se target
Sending build context to Docker daemon 5.877MB
Step 1/5 : FROM openjdk:8-jre-slim
8-jre-slim: Pulling from library/openjdk
f7e2b70d04ae: Pull complete
05d40fc3cf34: Pull complete
b235bdb95dc9: Pull complete
9a9ecf5ba38f: Pull complete
91327716c461: Pull complete
Digest: sha256:...
Status: Downloaded newer image for openjdk:8-jre-slim
---> bafe4a0f3a02
Step 2/5 : RUN mkdir /app
---> Running in ec2d3dad6e73
Removing intermediate container ec2d3dad6e73
---> a091fb56d8c5
Step 3/5 : COPY libs /app/libs
---> a8a9ec8475ac
Step 4/5 : COPY helidon-quickstart-se.jar /app
---> b49c72bbfa4c
Step 5/5 : CMD ["java", "-jar", "/app/helidon-quickstart-se.jar"]
---> Running in 4a332d65a10d
Removing intermediate container 4a332d65a10d
---> 248aaf1a5246
Successfully built 248aaf1a5246
Successfully tagged helidon-quickstart-se:latest

當映像檔在本地的 Kubernetes 叢集中建置完成,你可建立你的 deployment 和 service:

$ kubectl create -f target/app.yaml
service/helidon-quickstart-se created
deployment.extensions/helidon-quickstart-se created

$ kubectl get pods
NAME READY STATUS RESTARTS
helidon-quickstart-se-786bd599ff-n874p 1/1 Running 0
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
helidon-quick... NodePort 10.100.20.26 <none> 8080:31803/TCP
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP
</none></none>

在測試你新布署的 Kubernetes service 前,有一件事要注意,稍早,你建立的 service 類型是 LoadBalancer。由於配置在一個負載平衡器,你能用外部 IP 位置存取 pod,這提供外部存取在你叢集中運行的 service 的其中一種類型,另一個類型是這個 service 使用的 NodePort,一個 node port 將 service 公開到一個叢集中所有節點都映射過的通訊埠上。

Minikube 再次提供一個方便的指令,你可以用 service 指令,這次,加上 -- url 參數取得能存取 service 的網址。使用這指令然後用回傳的網址測試你的 service:

$ minikube service helidon-quickstart-se –url
http://192.168.99.101:31803
$ curl -X GET http://192.168.99.101:31803/greet
{"message":"Hello World!"}

你現在已經布署一個微服務到 Kubernetes,太棒了,在你離開要用容器做些驚人之舉前,讓我們再瞭解一些基礎。

關心進行中的事物

當你用命令列啟動你的 service,你會看到網頁伺服器啟動的輸出,這輸出同樣會被容器捕捉,可以透過 kubectl logs 指令查看。如果事情不如預期地運作,這是首先檢查問題的好地方。

$ kubectl logs helidon-quickstart-se-786bd599ff-n874p
[DEBUG] (main) Using Console logging
2019.03.23 01:00:53 INFO io.helidon.webserver.NettyWebServer
Thread[nioEventLoopGroup-2-1,10,main]: Channel '@default'
started: [id: 0x9f01de18, L:/0.0.0.0:8080]
WEB server is up! http://localhost:8080/greet

當你用本地叢集進行布署,這指令特別有用。在大規模情況下,有許多方式可以收集、儲存和利用日誌資料。開源專案或全自動化的商業解決方案都有,線上搜尋能找到許多選項。

按需擴展

假設你的 service 是應用程式的一環,負責對使用者說 Hello,你的團隊預期明天會有個尖峰用量,因此,你將擴展你的 service 以處理更多使用者的請求。擴展或縮減 deployments 的數量是 Kubernetes 的核心功能。要擴展,只需簡單地在 deployment 規範中指定副本的數量,然後用 kubectl apply 套用變更。

現在,編輯你的 target/app.yaml 檔案,然後套用變更。先將副本的數量從 1 增加到 5。

$ grep replicas target/app.yaml
replicas: 5
$ kubectl apply -f target/app.yaml
service/helidon-quickstart-se unchanged
deployment.extensions/helidon-quickstart-se configured

你應該會看之前只有一個,現在有布署五個 pods,注意,因為 Kubernetes 的配置是宣告式的,只有提交變動的部分到叢集中,因此,四個新的 pods 被加到 deployment 中。

$ kubectl get pods

NAME READY STATUS RESTARTS
helidon-quickstart-se-786bd599ff-5gm29 1/1 Running 0
helidon-quickstart-se-786bd599ff-fkg8g 1/1 Running 0
helidon-quickstart-se-786bd599ff-g7945 1/1 Running 0
helidon-quickstart-se-786bd599ff-h6c5n 1/1 Running 0
helidon-quickstart-se-786bd599ff-n874p 1/1 Running 0

同樣,你可輕易縮減 deployment,再次編輯你的 deployment 規範,然後套用變更:

$ grep replicas target/app.yaml
replicas: 2
$ kubectl apply -f target/app.yaml
service/helidon-quickstart-se unchanged
deployment.extensions/helidon-quickstart-se configured
$ kubectl get pods
NAME READY STATUS RESTARTS
helidon-quickstart-se-786bd599ff-h6c5n 1/1 Running 0
helidon-quickstart-se-786bd599ff-n874p 1/1 Running 0

Kubernetes 有更進階的擴展功能,例如 Horizontal Pod Autoscaler,以及雲端託管平台提供的按需擴展功能。

後續

雖然超出本文範圍,現在,有許多 Kubernetes 進階功能你可以去掌握,Kubernetes 官方文件是很好的起點。

在完成對 service 的操作後,你可以用 kubectl delete,將它從你的叢集中移除:

$ kubectl delete service helidon-quickstart-se
service "helidon-quickstart-se" deleted
$ kubectl delete deployment helidon-quickstart-se
deployment.extensions "helidon-quickstart-se" deleted

除了豐富的文件外 [譯註:確實豐富,但個人是覺得很雜亂,不算是好理解的文件],有幾個免費的線上培訓選項可以利用。Kubernetes 社群歡迎且樂於助人。此外,Kubernetes 的 Slack 頻道是學習、交流與支援的絕佳來源。在 GitHub 專案,總是歡迎問題與提交 pull requests。

結論

容器為布署微服務提供的強大抽象,允許解耦你的服務與主機環境,讓它可攜與可擴展,如你在這簡短的介紹中所見,Kubernetes 可大規模管理容器。

當然,這些範例只是開始,深入研究文件,學習更多進階概念,歡迎來到 Kubernetes 開發!

譯者的告白

總算是把當初選的,有關 Java 和雲端的文章都翻譯完了,後續會不會再有相關的翻譯就再看吧!前面有提到 2019 的三四月刊,有介紹許多針對微服務或是 RESTful 開發的新框架,但老實說,個人覺得差異不大,經過這麼多年,Spring framework 也做了許多調整,目前依舊是主流,所以應該不會去翻譯那些介紹框架的文章。

--

--