我們希望微服務是可復制的,可替換的工作節點,這樣可以輕松進行升級或降級,同時無需任何停機時間,并花費最少代價的管理。我們可以說我們希望他們成為我們的小黃人(minions)。本文我們將通過一個簡單的例子來了解Kubernetes可以通過創建和編排一群“小黃人"來為我們做些什么。您可以與本文一起編碼或從此處[1]克隆項目。
先決條件
需要將使用Docker容器化微服務以便在Kubernetes中運行它們。我們將使用Minikube,而不是使用云托管的Kubernetes,以便可以在本地沙箱運行。
目的
我們的小黃人軍團將是Java微服務。我們希望軍團中有不同類型的工作角色,以便能夠了解Kubernetes可以為我們做些什么。因此,我們的目標是讓每個微服務都響應一個簡單的http請求,其響應如下:
使用ASCII字來表示minion的類型。
構建Java Minion服務
我們可以通過Spring Boot Web應用程序來啟動我們的微服務,程序使用具有Web啟動依賴性的Spring Initializr初始化:
在項目中,創建一個使用@RestController注釋的Controller來處理請求。使用@RequestMapping(method = GET)來提供響應主體。所以首先我們可以這樣做:
@RequestMapping(?method=GET)
@ResponseBody
public?String?minion()?throws?UnknownHostException?{
???StringBuilder?stringBuilder?=?new?StringBuilder();
???stringBuilder.append("Host:?").append(InetAddress.getLocalHost().getHostName()).append("<br/>");
???return?stringBuilder.toString();
}
但這并不能完全滿足需求。我們可以輸出ASCII字,但選擇哪種minion類型?為此可以使用一個技巧。創建一個可以采用我們選擇的任何minion類型的應用程序。要做到這一點,需要它包含一個ASCII藝術字庫。因此,我們創建了一個名為MinionsLibrary的類,使用@Component注解,在內部我們創建了一個地圖,我們使用此博客[2]中的一些minions初始化:
@Component
public?class?MinionsLibrary?{
????private?Map<String,String>?map?=?new?HashMap<>();
????public?MinionsLibrary(){
??????map.put("one-eyed-minion",<COPY-PASTE?MINION?ASCII?ART?HERE>);
??????map.put("two-eyed-minion",<COPY-PASTE?MINION?ASCII?ART?HERE>);
??????map.put("sad-minion",<COPY-PASTE?MINION?ASCII?ART?HERE>);
??????map.put("happy-minion",<COPY-PASTE?MINION?ASCII?ART?HERE>);
????}
}
或者你可以從https://github.com/ryandawsonuk/minions/tree/master/src/main/java/org/minions/demo獲取。
然后告訴微服務是哪種minion類型。使用Spring應用程序的名稱屬性(我們稍后可以使用Docker環境變量設置)來執行此操作。它還將幫助我們稍后在響應中顯示我們的應用程序版本,所以現在的Controller變為:
@RestController
public?class?Controller?{
????private?final?String?version?=?"0.1";
????private?MinionsLibrary?minionsLibrary;
????@Value("${spring.application.name}")
????private?String?appName;
????public?Controller(MinionsLibrary?minionsLibrary){
????????this.minionsLibrary=minionsLibrary;
????}
????@RequestMapping(?method=GET)
????@ResponseBody
????public?String?minion()?throws?UnknownHostException?{
????????StringBuilder?stringBuilder?=?new?StringBuilder();
????????stringBuilder.append("Host:?").append(InetAddress.getLocalHost().getHostName()).append("<br/>");
????????stringBuilder.append("Minion?Type:?").append(appName).append("<br/>");
????????stringBuilder.append("IP:?").append(InetAddress.getLocalHost().getHostAddress()).append("<br/>");
????????stringBuilder.append("Version:?").append(version).append("<br/>");
????????stringBuilder.append(minionsLibrary.getMinion(appName));
????????return?stringBuilder.toString();
????}
}
現在選擇'image'包以匹配應用程序名稱,該名稱將是minion類型名稱(例如'單眼小黃人')。
容器化并部署
需要為我們的應用程序創建一個Docker鏡像。我們想在Docker鏡像中構建可執行的jar,然后在容器啟動時啟動Java應用程序。可以使用多階段Docker構建來完成此任務。 Dockerfile是:
FROM?maven:3.5-jdk-8?as?BUILDMINION
COPY?src?/usr/src/myapp/src
COPY?pom.xml?/usr/src/myapp
RUN?mvn?-f?/usr/src/myapp/pom.xml?clean?package?-DskipTests
FROM?openjdk:alpine
COPY?--from=BUILDMINION?/usr/src/myapp/target/*.jar?/maven/
CMD?java?$JAVA_OPTS?-jar?maven/*.jar
從開始到'FROM openjdk:alpine'是構建JAR,然后jar包被拷貝到基于輕量的openjdk:alpine鏡像的下一階段構建。使用JAVA_OPTS參數來限制程序的內存占用(關于降低內存,可以參考該文章[3])。
然后使用命令“docker build . -t minion”構建一個鏡像。
通過創建Kubernetes部署文件來部署它。我們稱之為“minion-army.yml”。這將包含每個minion類型的條目。這是其中的一個minion類型:
apiVersion:?apps/v1beta1
kind:?Deployment
metadata:
?name:?one-eyed-minion
labels:
???serviceType:?one-eyed-minion
spec:
?replicas:?2
template:
???metadata:
?????name:?one-eyed-minion
?????labels:
???????serviceType:?one-eyed-minion
???spec:
?????containers:
???????-?name:?one-eyed-minion
?????????image:?minion:latest
?????????imagePullPolicy:?Never
?????????ports:
?????????-?containerPort:?8080
?????????env:
?????????-?name:?JAVA_OPTS
???????????value:?-Xmx64m?-Xms64m
?????????-?name:?SPRING_APPLICATION_NAME
???????????value:?"one-eyed-minion"
---
apiVersion:?v1
kind:?Service
metadata:
?name:?one-eyed-minion-entrypoint
namespace:?default
spec:
?selector:
???serviceType:?one-eyed-minion
ports:
???-?port:?8080
?????targetPort:?8080
?????nodePort:?30080
type:?NodePort
請注意,“SPRING_APPLICATION_NAME”變量會自動與spring.application.name屬性匹配,以便此minion服務成為單眼小黃人類型。有兩個這種minion類型的實例(副本)可用,Kubernetes服務將自動將請求路由到其中一個或另一個。
該服務將通過Minikube以端口30080暴露對外提供服務 (對于真正的Kubernetes,該服務的這一點會有所不同,因為我們使用LoadBalancer而不是NodePort,并且不會限制在minikube端口范圍)。服務將使用與服務匹配的Pod來處理它。我們將為每種類型提供一種服務。
minion類型的部署將創建兩個Pod。每個人都是這種類型的工作節點。
我們可以為每個minion類型重復上面的配置,每次增加外部端口號以便使用不同的端口,或者我們可以使用這個GitHub存儲庫,它還具有其他配置,可以在不停機的情況下進行小型版本升級(如果我們使用Helm,我們可以避免重復,但我們不想添加比我們更多的工具)。
創建軍團
首先啟動mMinikube:
minikube?start?--memory?4000?--cpus?3
等待它開始,然后將您的Docker registry鏈接到Minikube,并為Minikube構建minion圖像:
eval?$(minikube?docker-env)
docker?build?.?-t?minion
然后我們可以部署軍團:
kubectl?create?-f?minion-army.yml
并看到類型:
open?http://$(minikube?ip):30080
open?http://$(minikube?ip):30081
open?http://$(minikube?ip):30082
open?http://$(minikube?ip):30083
每個看起來都很像文章開頭的快樂小黃人頁面。
我們可以通過“kubectl get pods”來查看整個軍隊,或者“minikube dashboard”進到Pods頁面:
創造更多的部隊
我們可以在minikube dashboard的Deployments部分下創建更多特定類型的minions:
一個小黃人倒下,另一個替補他的位置
假設從瀏覽器點擊快樂小黃人服務時得到的:
如果殺死“happy-minion-58c9c46d67-j84s9”會發生什么?可以通過儀表板的Pod部分刪除:
kubectl?delete?pod?happy-minion-58c9c46d67-j84s9
如果你在瀏覽器中點擊刷新幾次(殺死小黃人兵可能需要一點時間),你會看到該服務會使用該類型的另一個小黃人。如果瀏覽Pod部分,您將看到Kubernetes創建了一個新的Pod來代替您刪除的那個,以保證該部署中有兩個節點。
Minion升級
我們還可以為小黃人進行滾動升級。為此,我們應該在minions-army.yml文件的每個Deployment部分的'spec'部分下面(它可以直接位于同一級別的'replicas'下面):
minReadySeconds:?10
strategy:
???type:?RollingUpdate
???rollingUpdate:
?????maxUnavailable:?1
?????maxSurge:?1
然后將Controller類中的版本更改為0.2,保存它然后執行:
docker?build?.?-t?minion:0.2
然后打開minion-army.yml并找到 - 用“0.2”替換所有“最新”,保存更改并執行:
kubectl?apply?-f?minion-army.yml?--record
刷新其中一個minion類型的瀏覽器,以查看版本更改是否與kubectl rollout status部署中看到的內容一致,其中是minion類型(例如one-eyed-minion)。
小黃人回滾
要查看已部署的歷史記錄,請執行kubectl rollout history deployment?,回滾執行 kubectl rollout undo deployment?--to-revision = 1(可能需要一段時間)。
銷毀軍團
用以下方法摧毀軍隊:
kubectl?delete?-f?minion-army.yml
用“minikube stop”停止minikube。
相關鏈接:
https://github.com/ryandawsonuk/minions/blob/master/minion-army.yml
http://textart4u.blogspot.co.uk/2013/08/minions-emoticons-text-art-for-facebook.html
https://dzone.com/articles/how-to-decrease-jvm-memory-consumption-in-docker-u
?