Kubernetes 入門
自動化容器化應用程式和服務的部署、擴展和管理
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 也做了許多調整,目前依舊是主流,所以應該不會去翻譯那些介紹框架的文章。