基於Kubernetes和Springboot構建微服務

最近一直在研究基於Kubernetes和SpringBoot的微服務架構,在研究過程中,逐漸意識到,一個優秀的微服務架構在最大化地做到高內聚、松耦合的同時,也必須要求架構內的微服務基於一定的規範進行設計。符合這些規範的微服務,纔是是體系內的“優秀公民”,只有體系內的都是“優秀公民”,才能保障微服務架構的健康發展。
針對這一設計理念,我決定寫幾篇博文,來定義一下我認爲的“優秀公民”,給後續搭建微服務提供一些範例。
SpringBoot由於其自帶Servlet容器,可以獨立運行,並且配置簡單,容易上手,最重要的是基於JavaEE平臺,使得SpringBoot非常適合開發微服務。所以本文決定以一個簡單的SpringBoot應用爲例,來演示一下,如何把SpringBoot以微服務的形式部署到Kubernetes集羣裏。
閱讀本文您需要具備基本的Java、Docker和Kubernetes知識。

SpringBoot Hello World

首先,我們先創建一個最簡單的SpringBoot應用。項目的源碼結構如下:

 

src
  --main
    --java
      --hello
        -Application.java
        -HelloController.java
pom.xml

HelloController.java是一個SpringMVC的Controller,內容如下:

 

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!";
    }

}

可以看到我們以註解的方式聲明瞭一個RestController,然後將URI(/)映射到index方法裏,這樣,只要有URI爲“/”的請求,就會返回“Greetings from Spring Boot!”。

Application.java是程序的入口,內容如下:

 

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類裏面包含兩個方法,main方法是整個程序的入口,另外commandLineRunner方法被註解成了@Bean,程序在啓動的時候,這個方法會被自動執行,將整個Application Context裏面所有的bean都打印出來,這個方法是便於各位瞭解SpringBoot底層原理的,在生產環境中可以刪除。

最後是根目錄的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,這樣就大大簡化了SpringBoot項目的Maven配置,基於“約定優於配置”的理念,可以讓開發人員快速上手,輕鬆開發出SpringBoot應用。

開發完成後,在項目根目錄執行:

 

mvn package

如果一切正常的話,會在target目錄裏生成一個名爲gs-spring-boot-0.1.0.jar的文件。我們可以這樣運行這個jar:

 

cd target
java -jar ./gs-spring-boot-0.1.0.jar

在瀏覽器上訪問:http://localhost:8080,可以看到如下輸出:

springboot.png

 

這樣,我們就完成了一個基本的SpringBoot應用。

構建Docker鏡像

接下來,我們來構建一個Docker鏡像,用於運行剛纔我們開發的SpringBoot應用。
首先,我們要構建一個基礎鏡像,這個鏡像包含了簡單的操作系統,JDK環境等等。我們沒有直接使用dockerhub上的Java8基礎鏡像,而是基於opensuse的基礎鏡像,之後在上面安裝OpenJDK,原因是我們公司在生產環境使用SUSE Linux,運維人員對SUSE更熟悉,這也反映出在文章開始我提到的,一個微服務框架內的“優秀公民”要爲了符合規範,做一些妥協,這樣整個微服務體系纔可以健康發展。
廢話少說,直接上代碼。構建基礎鏡像的Dockerfile如下:

 

FROM opensuse:latest
MAINTAINER "Feng Di <[email protected]>"
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 .

接下來,構建應用鏡像的Dockerfile如下:

 

FROM opensuse-java8:latest
MAINTAINER "Feng Di <[email protected]>"
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

注意構建這個鏡像時,需要將上一章編譯出的gs-spring-boot-0.1.0.jar放到於上面Dockerfile相同目錄下。

 

docker build -t springbootdemo:latest .

在Kubernetes上運行

這裏我們使用Minikube在自己的開發機上跑一個K8S集羣。

安裝好minikube後,運行下面命令啓動minikube:

 

minikube start

由於minikube裏面的docker daemon與本機的不相同,這裏我們需要先將剛纔構建的鏡像上傳到minikube的docker daemon裏面。首先將剛纔構建的鏡像導出到一個tar包裏:

 

docker save springbootdemo:latest > springbootdemo.tar

之後切換到minikube的docker daemon並執行導入:

 

eval $(minikube docker-env)
docker load < springbootdemo.tar

創建一個名爲springbootdemo.yaml的文件,這個文件容器編排的腳本

 

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

注意這裏因爲要minikube使用本地構建的鏡像,所以我在編排腳本里面加入了這一行:“imagePullPolicy: Never ”,意思是說讓kubernetes在創建pod的時候不從dockerhub上pull鏡像,而是直接使用本地的鏡像,這個設置僅用於開發測試,這個在生產環境上千萬不要這麼用。

接下來,我們創建這個deployment,然後將這個服務暴露出去:

 

kubectl create -f springbootdemo.yaml
kubectl expose deployment springbootdemo --type="LoadBalancer"
minikube service springbootservice --url

此時我們執行“kubectl get pods”,如果剛纔創建的pod正常啓動了,我們可以執行如下命令查看這個服務對外暴露的IP和端口:

 

minikube service springbootservice --url

我這裏的輸出爲:http://192.168.99.100:30009/
在瀏覽器上輸入上述地址,可以看到我們的服務跑起來了:

 

minikube.png

服務擴容

在服務的運行過程中,有時我們可能會面臨服務的消費者過多,對服務產生很大壓力的場景。這個時候,我們就需要對這個服務做彈性擴容。
現在運行如下命令:

 

kubectl get pods

我們可以看到,現在springbootdemo這個服務,有兩個實例在運行,下面我將它擴充到4個:

 

kubectl scale deployment springbootdemo --replicas=4

再次執行“kubectl get pods”,可以看到服務成功擴充到了4個節點。

總結

以上就是一個基於kubernetes和springboot的簡單實例,在這個實例中,我們將一個springboot的應用以微服務的形式部署到了kubernetes集羣中,並實現了對外暴露、對外服務。當然這僅僅是一個“Hello World”實例,kubernetes和springboot都是非常成熟非常優雅的框架,我相信將這兩個技術結合使用構建微服務是一個不錯的選擇。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章