如何将Spring Boot应用程序部署到Kubernetes集群中

107 阅读4分钟

如何将Spring Boot应用程序部署到Kubernetes集群上

容器化是将应用的源代码和运行应用所需的依赖关系捆绑在一起。Kubernetes是一个容器编排工具,它使得在一个分布式的服务器系统中运行一个应用程序的多个实例成为可能。因此,Kubernetes简化了云中应用程序的扩展处理。本文介绍了如何构建Spring Boot Docker容器并将其部署到Kubernetes集群中。

前提条件

  • 在你的电脑上安装[JDK]。
  • 在你的计算机上安装[Docker]。
  • 在你的电脑上安装了[Minikube]。

目标

在本教程结束时。

  1. 你应该知道Dockerfile中使用的各种指令以及如何创建Dockerfile。
  2. 你应该有能力创建Kubernetes部署和服务文件。

应用程序设置

我们将使用spring initializr来启动我们的应用程序。

  1. 在你的浏览器上导航到spring initialzr
  2. 输入SpringKubernates 作为应用程序的名称。
  3. 添加spring web,H2, 和Spring data JPA 作为项目依赖。
  4. 保留其他配置为默认值,并点击生成按钮,下载引导的应用程序源代码。
  5. 解压下载的文件,在你喜欢的IDE中打开项目。我使用的是Intelij IDEA

应用数据层

  1. 在根项目包中,创建一个名为Student.java 的新Java文件。
  2. 在上面创建的Student.java 文件中,添加下面的代码片段。
@NoArgsConstructor //adds a constructor with no arguments 
@AllArgsConstructor // adds a constructor with all arguments
@Getter // adds getter methods for all fields
@Setter // adds setter methods for all fields
@Entity // adds JPA annotations / marks this class as an entity
public class Student {
    @Id // adds a primary key
    @GeneratedValue(strategy = GenerationType.AUTO) //Indicates that the Id field is automatically generated
    private Long id;
    private String name;
    private String regNo;
    private String course;
}

在上面的代码片段中,我们创建一个Student 实体,代表数据库中的一个表。

应用资源库层

在根项目包中,创建一个名为StudentRepository.java 的新的Java文件,并添加下面的代码片断。

// Repo class that contains all the methods to interact with the database
public interface StudentRepository extends JpaRepository<Student, Long> {
}

应用控制器层

在根项目包中创建一个名为StudentController.java 的Java文件,并添加下面的代码片段。

@RestController // marks the class as a controller
@RequestMapping("/api/students") // Root path to the API endpoints in this class
public class StudentController {
    private final StudentRepository repository;

    public StudentController(StudentRepository repository) {
        this.repository = repository;
    }

    @GetMapping // Get request that returns all students
    public ResponseEntity<List<Student>> getAll() {
        return new ResponseEntity<>(repository.findAll(), HttpStatus.OK);
    }

    @GetMapping("/{id}") // Get request that returns a specific student with the provided Id
    public ResponseEntity<Student> getStudentById(@PathVariable String id) {
        return new ResponseEntity<>(repository.findById(Long.valueOf(id))
                .orElseThrow(() -> new IllegalStateException("Student with id " + id + " not found")), HttpStatus.OK);
    }

    @PostMapping // Post request that creates a new student in the database
    public ResponseEntity<Student> createStudent(@RequestBody Student student) {
        return new ResponseEntity<>(repository.save(student), HttpStatus.CREATED);
    }

    @DeleteMapping("/{id}") // Delete request that deletes a student in the database
    public ResponseEntity<String> deleteStudent(@PathVariable String id) {
        repository.deleteById(Long.valueOf(id));
        return new ResponseEntity<>("Student deleted", HttpStatus.NO_CONTENT);
    }
}

创建Dockerfile

Dockerfile是一个蓝图,描述了Docker将如何创建镜像。Dockerfile上的指令会按照其编写的顺序执行。

在项目根目录下创建一个名为Dockerfile 的文件,并添加以下代码片断。

FROM adoptopenjdk/openjdk11:jdk-11.0.2.9-slim
WORKDIR /opt
ENV PORT 8080
EXPOSE 8080
COPY target/*.jar /opt/app.jar
ENTRYPOINT exec java $JAVA_OPTS -jar app.jar
  • FROM adoptopenjdk/openjdk11:jdk-11.0.2.9-slim 指令添加 ,作为我们的基础镜像,应用程序将从这里运行。JDK 11
  • WORKDIR /opt 指令将图像中的目录 作为工作目录。/opt
  • ENV PORT 8080 directive创建了一个名为 的环境变量,其值为 。PORT 8080
  • EXPOSE 8080 指令在镜像中暴露了8080端口。
  • COPY target/*.jar /opt/app.jar directive将 文件夹(使用Maven时)或 文件夹(使用Gradle时)中的jar文件复制到镜像中名为 的工作目录。target build app.jar
  • ENTRYPOINT exec java $JAVA_OPTS -jar app.jar 指令执行jar文件并启动Spring Boot应用程序。

构建Docker镜像

现在我们已经创建了Dockerfile,我们可以继续从Dockerfile中创建一个镜像。

在执行下面的命令之前,请确保Docker正在运行。

要建立Docker镜像,请执行下面的命令。

docker build -t spring-boot-test .
  • 上面的命令创建了一个名为spring-boot-test 的Docker镜像。
  • . 表示Dockerfile在当前目录中。

创建Kubernetes部署文件

在项目根目录下,创建一个名为deployment.yaml 的新文件,并添加以下代码片段。

apiVersion: v1 # Kubernetes API version
kind: Service # Kubernetes resource kind we are creating
metadata: # Metadata of the resource kind we are creating
  name: spring-test-service
spec:
  selector:
    app: spring-test-app
  ports:
    - protocol: "TCP"
      port: 8080 # The port that the service is running on in the cluster
      targetPort: 8080 # The port exposed by the service
  type: LoadBalancer # type of the service. LoadBalancer indicates that our service will be external.
---
apiVersion: apps/v1
kind: Deployment # Kubernetes resource kind we are creating
metadata:
  name: spring-test-app
spec:
  selector:
    matchLabels:
      app: spring-test-app
  replicas: 2 # Number of replicas that will be created for this deployment
  template:
    metadata:
      labels:
        app: spring-test-app
    spec:
      containers:
        - name: spring-test-app 
          image: spring-boot-test # Image that will be used to containers in the cluster
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080 # The port that the container is running on in the cluster

上面的代码片断有两个部分,分别由---:

  1. Service - a服务将我们的应用程序暴露在Kubernetes集群之外。它充当负载均衡器,将对我们应用程序的请求分配到集群中运行的应用程序的各个实例。
  2. Deployment - 部署是一个蓝图,用于在集群中创建应用程序的实例。

部署到Kubernetes

现在我们已经创建了Kubernetes部署文件,我们可以把它部署到集群上。执行下面的命令,将应用程序部署到集群上。

kubectl apply -f deployment.yaml

注意:在执行上述命令之前,确保minikube正在运行。通过执行命令minikube start ,启动minikube。

我们可以在Kubernetes仪表盘上检查部署正在运行,没有错误。通过执行命令minikube enable dashboard ,启动Kubernetes仪表盘。

Kubernetes dashboard

我们的应用程序现在已经在Kubernetes集群中成功运行,但我们不能从集群外访问它。为了从集群外部访问我们的应用程序,我们需要公开服务。

执行下面的命令,获得集群中可用服务的列表。

$ kubectl get services
NAME                    TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
flask-test-service      LoadBalancer   10.107.76.196    <pending>     6000:32111/TCP   51d
kubernetes              ClusterIP      10.96.0.1        <none>        443/TCP          76d
mongo-express-service   LoadBalancer   10.98.170.37     <pending>     8081:30000/TCP   30d
mongodb-service         ClusterIP      10.99.13.235     <none>        27017/TCP        31d
spring-test-app         LoadBalancer   10.96.88.167     <pending>     8080:30750/TCP   2d
spring-test-service     LoadBalancer   10.107.183.251   <pending>     8080:30507/TCP   22d

从上面的列表中,我们可以看到我们的服务spring-test-service 正在运行,但外部IP地址是<pending> 。要公开该服务,执行下面的命令。

$ minikube service spring-test-service
❗  Executing "docker container inspect minikube --format={{.State.Status}}" took an unusually long time: 6.215910582s
💡  Restarting the docker service may improve performance.
|-------------|-----------------------|---------------|-----------------------------|
| NAMESPACE   | NAME                  | TARGET PORT   | URL                         |
| ----------- | --------------------- | ------------- | --------------------------- |
| default     | spring-test-service   | 8080          | http://192.168.49.2:8080    |
| ----------- | --------------------- | ------------- | --------------------------- |
🎉  Opening service default/spring-test-service in default browser...

现在我们可以通过minikube生成的URLhttp://192.168.49.2:8080来访问我们的应用程序。

总结

现在你已经学会了如何将Spring Boot应用程序部署到Kubernetes集群中。部署一个Spring Boot应用程序集群,并为它提供一个数据库。