Java程序员如何快速理解Kubernetes
我們希望微服務(wù)是可復(fù)制的,可替換的工作節(jié)點(diǎn),這樣可以輕松進(jìn)行升級或降級,同時(shí)無需任何停機(jī)時(shí)間,并花費(fèi)最少代價(jià)的管理。我們可以說我們希望他們成為我們的小黃人(minions)。本文我們將通過一個(gè)簡單的例子來了解Kubernetes可以通過創(chuàng)建和編排一群“小黃人"來為我們做些什么。您可以與本文一起編碼或從此處[1]克隆項(xiàng)目。
先決條件
需要將使用Docker容器化微服務(wù)以便在Kubernetes中運(yùn)行它們。我們將使用Minikube,而不是使用云托管的Kubernetes,以便可以在本地沙箱運(yùn)行。
目的
我們的小黃人軍團(tuán)將是Java微服務(wù)。我們希望軍團(tuán)中有不同類型的工作角色,以便能夠了解Kubernetes可以為我們做些什么。因此,我們的目標(biāo)是讓每個(gè)微服務(wù)都響應(yīng)一個(gè)簡單的http請求,其響應(yīng)如下:
使用ASCII字來表示minion的類型。
構(gòu)建Java Minion服務(wù)
我們可以通過Spring Boot Web應(yīng)用程序來啟動(dòng)我們的微服務(wù),程序使用具有Web啟動(dòng)依賴性的Spring Initializr初始化:
在項(xiàng)目中,創(chuàng)建一個(gè)使用@RestController注釋的Controller來處理請求。使用@RequestMapping(method = GET)來提供響應(yīng)主體。所以首先我們可以這樣做:
@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類型?為此可以使用一個(gè)技巧。創(chuàng)建一個(gè)可以采用我們選擇的任何minion類型的應(yīng)用程序。要做到這一點(diǎn),需要它包含一個(gè)ASCII藝術(shù)字庫。因此,我們創(chuàng)建了一個(gè)名為MinionsLibrary的類,使用@Component注解,在內(nèi)部我們創(chuàng)建了一個(gè)地圖,我們使用此博客[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獲取。
然后告訴微服務(wù)是哪種minion類型。使用Spring應(yīng)用程序的名稱屬性(我們稍后可以使用Docker環(huán)境變量設(shè)置)來執(zhí)行此操作。它還將幫助我們稍后在響應(yīng)中顯示我們的應(yīng)用程序版本,所以現(xiàn)在的Controller變?yōu)?#xff1a;
@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();
????}
}
現(xiàn)在選擇'image'包以匹配應(yīng)用程序名稱,該名稱將是minion類型名稱(例如'單眼小黃人')。
容器化并部署
需要為我們的應(yīng)用程序創(chuàng)建一個(gè)Docker鏡像。我們想在Docker鏡像中構(gòu)建可執(zhí)行的jar,然后在容器啟動(dòng)時(shí)啟動(dòng)Java應(yīng)用程序。可以使用多階段Docker構(gòu)建來完成此任務(wù)。 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'是構(gòu)建JAR,然后jar包被拷貝到基于輕量的openjdk:alpine鏡像的下一階段構(gòu)建。使用JAVA_OPTS參數(shù)來限制程序的內(nèi)存占用(關(guān)于降低內(nèi)存,可以參考該文章[3])。
然后使用命令“docker build . -t minion”構(gòu)建一個(gè)鏡像。
通過創(chuàng)建Kubernetes部署文件來部署它。我們稱之為“minion-army.yml”。這將包含每個(gè)minion類型的條目。這是其中的一個(gè)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”變量會自動(dòng)與spring.application.name屬性匹配,以便此minion服務(wù)成為單眼小黃人類型。有兩個(gè)這種minion類型的實(shí)例(副本)可用,Kubernetes服務(wù)將自動(dòng)將請求路由到其中一個(gè)或另一個(gè)。
該服務(wù)將通過Minikube以端口30080暴露對外提供服務(wù) (對于真正的Kubernetes,該服務(wù)的這一點(diǎn)會有所不同,因?yàn)槲覀兪褂肔oadBalancer而不是NodePort,并且不會限制在minikube端口范圍)。服務(wù)將使用與服務(wù)匹配的Pod來處理它。我們將為每種類型提供一種服務(wù)。
minion類型的部署將創(chuàng)建兩個(gè)Pod。每個(gè)人都是這種類型的工作節(jié)點(diǎn)。
我們可以為每個(gè)minion類型重復(fù)上面的配置,每次增加外部端口號以便使用不同的端口,或者我們可以使用這個(gè)GitHub存儲庫,它還具有其他配置,可以在不停機(jī)的情況下進(jìn)行小型版本升級(如果我們使用Helm,我們可以避免重復(fù),但我們不想添加比我們更多的工具)。
創(chuàng)建軍團(tuán)
首先啟動(dòng)mMinikube:
minikube?start?--memory?4000?--cpus?3
等待它開始,然后將您的Docker registry鏈接到Minikube,并為Minikube構(gòu)建minion圖像:
eval?$(minikube?docker-env)
docker?build?.?-t?minion
然后我們可以部署軍團(tuán):
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
每個(gè)看起來都很像文章開頭的快樂小黃人頁面。
我們可以通過“kubectl get pods”來查看整個(gè)軍隊(duì),或者“minikube dashboard”進(jìn)到Pods頁面:
創(chuàng)造更多的部隊(duì)
我們可以在minikube dashboard的Deployments部分下創(chuàng)建更多特定類型的minions:
一個(gè)小黃人倒下,另一個(gè)替補(bǔ)他的位置
假設(shè)從瀏覽器點(diǎn)擊快樂小黃人服務(wù)時(shí)得到的:
如果殺死“happy-minion-58c9c46d67-j84s9”會發(fā)生什么?可以通過儀表板的Pod部分刪除:
kubectl?delete?pod?happy-minion-58c9c46d67-j84s9
如果你在瀏覽器中點(diǎn)擊刷新幾次(殺死小黃人兵可能需要一點(diǎn)時(shí)間),你會看到該服務(wù)會使用該類型的另一個(gè)小黃人。如果瀏覽Pod部分,您將看到Kubernetes創(chuàng)建了一個(gè)新的Pod來代替您刪除的那個(gè),以保證該部署中有兩個(gè)節(jié)點(diǎn)。
Minion升級
我們還可以為小黃人進(jìn)行滾動(dòng)升級。為此,我們應(yīng)該在minions-army.yml文件的每個(gè)Deployment部分的'spec'部分下面(它可以直接位于同一級別的'replicas'下面):
minReadySeconds:?10
strategy:
???type:?RollingUpdate
???rollingUpdate:
?????maxUnavailable:?1
?????maxSurge:?1
然后將Controller類中的版本更改為0.2,保存它然后執(zhí)行:
docker?build?.?-t?minion:0.2
然后打開minion-army.yml并找到 - 用“0.2”替換所有“最新”,保存更改并執(zhí)行:
kubectl?apply?-f?minion-army.yml?--record
刷新其中一個(gè)minion類型的瀏覽器,以查看版本更改是否與kubectl rollout status部署中看到的內(nèi)容一致,其中是minion類型(例如one-eyed-minion)。
小黃人回滾
要查看已部署的歷史記錄,請執(zhí)行kubectl rollout history deployment?,回滾執(zhí)行 kubectl rollout undo deployment?--to-revision = 1(可能需要一段時(shí)間)。
銷毀軍團(tuán)
用以下方法摧毀軍隊(duì):
kubectl?delete?-f?minion-army.yml
用“minikube stop”停止minikube。
相關(guān)鏈接:
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
?
總結(jié)
以上是生活随笔為你收集整理的Java程序员如何快速理解Kubernetes的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 常见开源分布式存储系统
- 下一篇: Java服务GC参数调优案例