最近一直在研究基于Kubernetes和SpringBoot的微服務(wù)架構(gòu),在研究過程中瘫析,逐漸意識(shí)到砌梆,一個(gè)優(yōu)秀的微服務(wù)架構(gòu)在最大化地做到高內(nèi)聚、松耦合的同時(shí)颁股,也必須要求架構(gòu)內(nèi)的微服務(wù)基于一定的規(guī)范進(jìn)行設(shè)計(jì)么库。符合這些規(guī)范的微服務(wù),才是是體系內(nèi)的“優(yōu)秀公民”甘有,只有體系內(nèi)的都是“優(yōu)秀公民”诉儒,才能保障微服務(wù)架構(gòu)的健康發(fā)展。
針對(duì)這一設(shè)計(jì)理念亏掀,我決定寫幾篇博文忱反,來定義一下我認(rèn)為的“優(yōu)秀公民”泛释,給后續(xù)搭建微服務(wù)提供一些范例。
SpringBoot由于其自帶Servlet容器温算,可以獨(dú)立運(yùn)行怜校,并且配置簡(jiǎn)單,容易上手注竿,最重要的是基于JavaEE平臺(tái)茄茁,使得SpringBoot非常適合開發(fā)微服務(wù)。所以本文決定以一個(gè)簡(jiǎn)單的SpringBoot應(yīng)用為例巩割,來演示一下裙顽,如何把SpringBoot以微服務(wù)的形式部署到Kubernetes集群里。
閱讀本文您需要具備基本的Java宣谈、Docker和Kubernetes知識(shí)愈犹。
SpringBoot Hello World
首先,我們先創(chuàng)建一個(gè)最簡(jiǎn)單的SpringBoot應(yīng)用闻丑。項(xiàng)目的源碼結(jié)構(gòu)如下:
src
--main
--java
--hello
-Application.java
-HelloController.java
pom.xml
HelloController.java是一個(gè)SpringMVC的Controller漩怎,內(nèi)容如下:
package hello;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
public class HelloController {
@RequestMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
}
可以看到我們以注解的方式聲明了一個(gè)RestController,然后將URI(/)映射到index方法里嗦嗡,這樣勋锤,只要有URI為“/”的請(qǐng)求,就會(huì)返回“Greetings from Spring Boot!”酸钦。
Application.java是程序的入口怪得,內(nèi)容如下:
package hello;
import java.util.Arrays;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
};
}
}
Application類里面包含兩個(gè)方法,main方法是整個(gè)程序的入口卑硫,另外commandLineRunner方法被注解成了@Bean,程序在啟動(dòng)的時(shí)候蚕断,這個(gè)方法會(huì)被自動(dòng)執(zhí)行欢伏,將整個(gè)Application Context里面所有的bean都打印出來,這個(gè)方法是便于各位了解SpringBoot底層原理的亿乳,在生產(chǎn)環(huán)境中可以刪除硝拧。
最后是根目錄的Maven pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-spring-boot</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
SpringBoot提供了spring-boot-starter-parent、spring-boot-starter-web以及spring-boot-maven-plugin葛假,這樣就大大簡(jiǎn)化了SpringBoot項(xiàng)目的Maven配置障陶,基于“約定優(yōu)于配置”的理念,可以讓開發(fā)人員快速上手聊训,輕松開發(fā)出SpringBoot應(yīng)用抱究。
開發(fā)完成后,在項(xiàng)目根目錄執(zhí)行:
mvn package
如果一切正常的話带斑,會(huì)在target目錄里生成一個(gè)名為gs-spring-boot-0.1.0.jar的文件鼓寺。我們可以這樣運(yùn)行這個(gè)jar:
cd target
java -jar ./gs-spring-boot-0.1.0.jar
在瀏覽器上訪問:http://localhost:8080勋拟,可以看到如下輸出:
這樣,我們就完成了一個(gè)基本的SpringBoot應(yīng)用妈候。
構(gòu)建Docker鏡像
接下來敢靡,我們來構(gòu)建一個(gè)Docker鏡像,用于運(yùn)行剛才我們開發(fā)的SpringBoot應(yīng)用苦银。
首先啸胧,我們要構(gòu)建一個(gè)基礎(chǔ)鏡像,這個(gè)鏡像包含了簡(jiǎn)單的操作系統(tǒng)幔虏,JDK環(huán)境等等纺念。我們沒有直接使用dockerhub上的Java8基礎(chǔ)鏡像,而是基于opensuse的基礎(chǔ)鏡像所计,之后在上面安裝OpenJDK柠辞,原因是我們公司在生產(chǎn)環(huán)境使用SUSE Linux,運(yùn)維人員對(duì)SUSE更熟悉主胧,這也反映出在文章開始我提到的叭首,一個(gè)微服務(wù)框架內(nèi)的“優(yōu)秀公民”要為了符合規(guī)范,做一些妥協(xié)踪栋,這樣整個(gè)微服務(wù)體系才可以健康發(fā)展焙格。
廢話少說,直接上代碼夷都。構(gòu)建基礎(chǔ)鏡像的Dockerfile如下:
FROM opensuse:latest
MAINTAINER "Feng Di <fengdidi@gmail.com>"
LABEL description="Base Image Java 8"
RUN zypper -n update && zypper -n install java-1_8_0-openjdk && mkdir /app
之后我們打成名為opensuse-java8的docker鏡像:
docker build -t opensuse-java8:latest .
接下來眷唉,構(gòu)建應(yīng)用鏡像的Dockerfile如下:
FROM opensuse-java8:latest
MAINTAINER "Feng Di <fengdidi@gmail.com>"
LABEL description="Spring Boot Image"
WORKDIR /app
COPY gs-spring-boot-0.1.0.jar /app/app.jar
EXPOSE 8080
CMD java -jar /app/app.jar
注意構(gòu)建這個(gè)鏡像時(shí),需要將上一章編譯出的gs-spring-boot-0.1.0.jar放到于上面Dockerfile相同目錄下囤官。
docker build -t springbootdemo:latest .
在Kubernetes上運(yùn)行
這里我們使用Minikube在自己的開發(fā)機(jī)上跑一個(gè)K8S集群冬阳。
- minikube的詳細(xì)信息請(qǐng)移步:https://kubernetes.io/docs/getting-started-guides/minikube/
- minikube在Mac上安裝需要提前安裝VirtualBox和Kubectl,
VirtualBox安裝介質(zhì)可在官網(wǎng)上下載:https://www.virtualbox.org/wiki/Downloads - 用于某些原因党饮,minikube在國(guó)內(nèi)運(yùn)行需要做一些改造肝陪,具體請(qǐng)移步:https://www.cnblogs.com/sxwen/p/8421188.html
安裝好minikube后,運(yùn)行下面命令啟動(dòng)minikube:
minikube start
由于minikube里面的docker daemon與本機(jī)的不相同刑顺,這里我們需要先將剛才構(gòu)建的鏡像上傳到minikube的docker daemon里面氯窍。首先將剛才構(gòu)建的鏡像導(dǎo)出到一個(gè)tar包里:
docker save springbootdemo:latest > springbootdemo.tar
之后切換到minikube的docker daemon并執(zhí)行導(dǎo)入:
eval $(minikube docker-env)
docker load < springbootdemo.tar
創(chuàng)建一個(gè)名為springbootdemo.yaml的文件,這個(gè)文件容器編排的腳本
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: springbootdemo
spec:
replicas: 2 # tells deployment to run 2 pods matching the template
template: # create pods using pod definition in this template
metadata:
labels:
app: springbootdemo
spec:
containers:
- name: springbootdemo
image: springbootdemo:latest
imagePullPolicy: Never #just for minikube,do not use this in production!!!
ports:
- containerPort: 8080
注意這里因?yàn)橐猰inikube使用本地構(gòu)建的鏡像蹲堂,所以我在編排腳本里面加入了這一行:“imagePullPolicy: Never ”狼讨,意思是說讓kubernetes在創(chuàng)建pod的時(shí)候不從dockerhub上pull鏡像,而是直接使用本地的鏡像柒竞,這個(gè)設(shè)置僅用于開發(fā)測(cè)試政供,這個(gè)在生產(chǎn)環(huán)境上千萬不要這么用。
接下來,我們創(chuàng)建這個(gè)deployment鲫骗,然后將這個(gè)服務(wù)暴露出去:
kubectl create -f springbootdemo.yaml
kubectl expose deployment springbootdemo --type="LoadBalancer"
minikube service springbootservice --url
此時(shí)我們執(zhí)行“kubectl get pods”犬耻,如果剛才創(chuàng)建的pod正常啟動(dòng)了,我們可以執(zhí)行如下命令查看這個(gè)服務(wù)對(duì)外暴露的IP和端口:
minikube service springbootservice --url
我這里的輸出為:http://192.168.99.100:30009/
在瀏覽器上輸入上述地址执泰,可以看到我們的服務(wù)跑起來了:
服務(wù)擴(kuò)容
在服務(wù)的運(yùn)行過程中枕磁,有時(shí)我們可能會(huì)面臨服務(wù)的消費(fèi)者過多,對(duì)服務(wù)產(chǎn)生很大壓力的場(chǎng)景术吝。這個(gè)時(shí)候计济,我們就需要對(duì)這個(gè)服務(wù)做彈性擴(kuò)容。
現(xiàn)在運(yùn)行如下命令:
kubectl get pods
我們可以看到排苍,現(xiàn)在springbootdemo這個(gè)服務(wù)沦寂,有兩個(gè)實(shí)例在運(yùn)行,下面我將它擴(kuò)充到4個(gè):
kubectl scale deployment springbootdemo --replicas=4
再次執(zhí)行“kubectl get pods”淘衙,可以看到服務(wù)成功擴(kuò)充到了4個(gè)節(jié)點(diǎn)传藏。
總結(jié)
以上就是一個(gè)基于kubernetes和springboot的簡(jiǎn)單實(shí)例,在這個(gè)實(shí)例中彤守,我們將一個(gè)springboot的應(yīng)用以微服務(wù)的形式部署到了kubernetes集群中毯侦,并實(shí)現(xiàn)了對(duì)外暴露、對(duì)外服務(wù)具垫。當(dāng)然這僅僅是一個(gè)“Hello World”實(shí)例侈离,kubernetes和springboot都是非常成熟非常優(yōu)雅的框架,我相信將這兩個(gè)技術(shù)結(jié)合使用構(gòu)建微服務(wù)是一個(gè)不錯(cuò)的選擇筝蚕。