Applying CI/CD With Spring Boot – 用Spring Boot应用CI/CD

最后修改: 2020年 5月 19日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll take a look at the Continuous Integration/Continuous Deployment (CI/CD) process and implement its essential parts.

在本教程中,我们将看一下持续集成/持续部署(CI/CD)过程,并实现其基本部分。

We’ll create a simple Spring Boot application and then push it to the shared Git repository. After that, we’ll build it with a building integration service, create a Docker image, and push it to a Docker repository.

我们将创建一个简单的Spring Boot应用程序,然后将其推送到共享的Git仓库。之后,我们将用一个构建集成服务来构建它,创建一个Docker镜像,并将其推送到Docker仓库中。

In the end, we’ll automatically deploy our application to a PaaS service (Heroku).

最后,我们将自动把我们的应用程序部署到一个PaaS服务(Heroku)。

2. Version Control

2.版本控制

The crucial part of CI/CD is the version control system to manage our code. In addition, we need a repository hosting service that our build and deploy steps will tie into.

CI/CD的关键部分是用来管理我们代码的版本控制系统。此外,我们还需要一个资源库托管服务,我们的构建和部署步骤将与之结合。

Let’s choose Git as the VCS and GitHub as our repository provider as they are the most popular at the moment and free to use.

让我们选择Git作为VCS和GitHub作为我们的版本库提供商,因为它们是目前最流行的,并且可以免费使用。

First, we’ll have to create an account on GitHub.

首先,我们要在GitHub上创建一个账户

Additionally, we should create a Git repository. Let’s name it baeldung-ci-cd-process. Also, let’s pick a public repository since it will allow us to access other services for free. Lastly, let’s initialize our repository with a README.md.

此外,我们应该创建一个 Git 仓库。让我们把它命名为baeldung-ci-cd-process。另外,让我们选择一个公共仓库,因为它将允许我们免费访问其他服务。最后,让我们用README.md来初始化我们的版本库。

Now that our repository has been created, we should clone our project locally. To do so, let’s execute this command on our local computer:

现在我们的版本库已经被创建,我们应该在本地克隆我们的项目。要做到这一点,让我们在本地计算机上执行这个命令。

git clone https://github.com/$USERNAME/baeldung-ci-cd-process.git

This will initialize our project in the directory where we executed the command. At the moment, it should only contain the README.md file.

这将在我们执行命令的目录中初始化我们的项目。目前,它应该只包含README.md文件。

3. Creating the Application

3.创建应用程序

In this section, we’ll create a simple Spring Boot application that will take part in the process. We’ll also use Maven as our build tool.

在这一节中,我们将创建一个简单的Spring Boot应用程序,参与这一过程。我们还将使用Maven作为我们的构建工具。

First, let’s initialize our project in the directory where we have cloned the version control repository.

首先,让我们在克隆了版本控制库的目录中初始化我们的项目。

For instance, we can do that with Spring Initializer, adding the web and actuator modules.

例如,我们可以通过Spring Initializer,添加webactuator模块来实现。

3.1. Creating the Application Manually

3.1.手动创建应用程序

Or, we can add the spring-boot-starter-web and spring-boot-starter-actuator dependencies manually:

或者,我们可以手动添加spring-boot-starter-webspring-boot-starter-actuator依赖项。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

The first is to introduce a REST endpoint and the second, a health check endpoint.

第一个是引入一个REST端点,第二个是健康检查端点。

In addition, let’s add the plugin that’ll allow us to run our application:

此外,让我们添加插件,这将使我们能够运行我们的应用程序。

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

And finally, let’s add a Spring Boot main class:

最后,让我们添加一个Spring Boot主类。

@SpringBootApplication
public class CiCdApplication {

    public static void main(String[] args) {
        SpringApplication.run(CiCdApplication.class, args);
    }
}

3.2. Pushing

3.2.推进

Whether using Spring Initializr or manually creating the project, we’re now ready to commit and push our changes to our repository.

无论是使用Spring Initializr还是手动创建项目,我们现在都可以提交并推送我们的修改到仓库。

Let’s do that with the following commands:

让我们用以下命令来做。

git add .
git commit -m 'Initialize application'
git push

We can now check if our changes exist in the repository.

现在我们可以检查我们的修改是否存在于版本库中。

4. Build Automation

4.建立自动化

Another part of the CI/CD process is a service that will build and test our pushed code.

CI/CD过程的另一部分是一个服务,它将构建和测试我们推送的代码。

We’ll use Travis CI here, but any building service will work as well.

我们将在这里使用Travis CI,但任何建筑服务也可以使用。

4.1. Maven Wrapper

4.1 Maven包装器

Let’s begin by adding a Maven Wrapper to our application. If we’ve used the Spring Initializr, we can skip this part since it’s included by default.

让我们首先在我们的应用程序中添加一个Maven Wrapper。如果我们使用了Spring Initializr,就可以跳过这一部分,因为它是默认包含的。

In the application directory, let’s do:

在应用程序目录中,让我们来做。

mvn -N io.takari:maven:0.7.7:wrapper

This will add Maven wrapper files, including the mvnw and the mvnw.cmd files that can be used instead of Maven.

这将增加Maven包装文件,包括mvnwmvnw.cmd文件,可以代替Maven使用。

While Travis CI has its own Maven, other building services might not. This Maven Wrapper will help us to be prepared for either situation. Also, developers won’t have to install Maven on their machines.

虽然Travis CI有自己的Maven,但其他构建服务可能没有。这个Maven Wrapper将帮助我们为两种情况做好准备。此外,开发者也不必在自己的机器上安装Maven。

4.2. Building Service

4.2.建筑服务

After that, let’s create an account on Travis CI by signing in with our GitHub account. Going forward, we should allow access to our project in GitHub.

之后,让我们通过登录与我们的GitHub账户在Travis CI上创建一个账户。往后,我们应该允许在GitHub中访问我们的项目。

Next, we should create a .travis.yml file which will describe the building process in Travis CI. Most of the building services allow us to create such a file, which is located at our repository’s root.

接下来,我们应该创建一个.travis.yml文件,描述Travis CI的构建过程。大多数构建服务允许我们创建这样一个文件,它位于我们的版本库的根部。

In our case, let’s tell Travis to use Java 11 and the Maven Wrapper to build our application:

在我们的例子中,让我们告诉Travis使用Java 11和Maven Wrapper来构建我们的应用程序。

language: java
jdk:
  - openjdk11
script:
  - ./mvnw clean install

The language property indicates we want to use Java.

language属性表明我们要使用Java。

The jdk property says which Docker image to download from DockerHub, openjdk11 in this case.

jdk属性说的是从DockerHub下载的Docker镜像,openjdk11在这种情况下。

The script property says what command to run – we want to use our Maven wrapper.

script属性说的是运行什么命令–我们想使用我们的Maven包装器。

Lastly, we should push our changes to the repository. The Travis CI should automatically trigger the build.

最后,我们应该把我们的修改推送到版本库。Travis CI应该自动触发构建。

5. Dockerizing

5.码头化

In this section, we’ll build a Docker image with our application and host it on DockerHub as a part of the CD process. It’ll allow us to run it on any machine with ease.

在本节中,我们将用我们的应用程序构建一个Docker镜像,并将其托管在DockerHub上,作为CD过程的一部分。这将使我们能够轻松地在任何机器上运行它。

5.1. Repository for Docker Images

5.1.Docker镜像的存储库

First, we should create a Docker repository for our images.

首先,我们应该为我们的图像创建一个Docker存储库。

Let’s create an account on DockerHub. Also, let’s create the repository for our project by filling out the appropriate fields:

让我们在DockerHub上创建一个账户。同时,让我们为我们的项目创建资源库,填写相应的字段。

  • name: baeldung-ci-cd-process
  • visibility: Public
  • Build setting: GitHub

5.2. Docker Image

5.2 Docker镜像

Now, we’re ready to create a Docker image and push it to DockerHub.

现在,我们准备创建一个Docker镜像并将其推送到DockerHub。

First, let’s add the jib-maven-plugin that will create and push our image with the application to the Docker repository (replace DockerHubUsername with the correct username):

首先,让我们添加 jib-maven-plugin,它将创建并将我们的镜像与应用程序一起推送到Docker仓库(将DockerHubUsername替换为正确的用户名)。

<profile>
    <id>deploy-docker</id>
    <properties>
        <maven.deploy.skip>true</maven.deploy.skip>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>com.google.cloud.tools</groupId>
                <artifactId>jib-maven-plugin</artifactId>
                <version>2.2.0</version>
                <configuration>
                    <to>
                        <image>${DockerHubUsername}/baeldung-ci-cd-process</image>
                        <tags>
                            <tag>${project.version}</tag>
                            <tag>latest</tag>
                        </tags>
                    </to>
                </configuration>
            </plugin>
        </plugins>
    </build>
</profile>

We have added it as part of a Maven profile in order to not trigger it with the default build.

我们将其作为Maven配置文件的一部分,以便不在默认构建中触发它。

Additionally, we have specified two tags for the image. To learn more about the plugin, visit our article about Jib.

此外,我们还为图片指定了两个标签。要了解有关该插件的更多信息,请访问我们的关于Jib的文章

Going forward, let’s adjust our build file (.travis.yml):

继续前进,让我们调整我们的构建文件(.travis.yml)。

before_install:
  - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
  - docker pull openjdk:11-jre-slim-sid

script:
  - ./mvnw clean install
  - ./mvnw deploy jib:build -P deploy-docker

With these changes, the build service will log in to DockerHub before building the application. Additionally, it’ll execute the deploy phase with our profile. During that phase, our application will be pushed as an image to the Docker repository.

有了这些变化,构建服务将在构建应用程序之前登录到DockerHub。此外,它将使用我们的配置文件执行deploy阶段。在这个阶段,我们的应用程序将作为一个镜像推送到Docker仓库。

Lastly, we should define DOCKER_PASSWORD and DOCKER_USERNAME variables in our build service. In Travis CI, these variables can be defined as part of the build settings.

最后,我们应该在构建服务中定义DOCKER_PASSWORDDOCKER_USERNAME变量。在Travis CI中,这些变量可以被定义为构建设置的一部分。

Now, let’s push our changes to the VCS. The build service should automatically trigger the build with our changes.

现在,让我们把我们的修改推送到VCS。构建服务应该自动触发我们的修改进行构建。

We can check if the Docker image has been pushed to the repository by running locally:

我们可以通过本地运行检查Docker镜像是否已经推送到仓库。

docker run -p 8080:8080 -t $DOCKER_USERNAME/baeldung-ci-cd-process

Now, we should be able to access our health check by accessing http://localhost:8080/actuator/health.

现在,我们应该能够通过访问http://localhost:8080/actuator/health.来访问我们的健康检查。

6. Code Analysis

6.代码分析

The next thing we’ll include in our CI/CD process is static code analysis. The main goal of such a process is to ensure the highest code quality. For instance, it could detect that we don’t have enough test cases or that we have some security issues.

我们将在CI/CD过程中包括的下一件事是静态代码分析。这样一个过程的主要目标是确保最高的代码质量。例如,它可以检测到我们没有足够的测试用例,或者我们有一些安全问题。

Let’s integrate with CodeCov, which will inform us about our test coverage.

让我们与CodeCov集成,它将告知我们的测试覆盖率。

Firstly, we should log in to CodeCov with our GitHub profile to establish integration.

首先,我们应该用我们的GitHub配置文件登录CodeCov,以建立整合。

Secondly, we should make changes to our code. Let’s begin by adding the jacoco plugin:

其次,我们应该对我们的代码进行修改。让我们首先添加jacoco插件

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.5</version>
    <executions>
        <execution>
            <id>default-prepare-agent</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

The plugin is responsible for generating test reports that will be used by CodeCov.

该插件负责生成将被CodeCov使用的测试报告。

Next, we should adjust the script section in our build service file (.travis.yml):

接下来,我们应该调整我们的构建服务文件(.travis.yml)中的脚本部分。

script:
  - ./mvnw clean org.jacoco:jacoco-maven-plugin:prepare-agent install
  - ./mvnw deploy jib:build -P deploy-docker

after_success:
  - bash <(curl -s https://codecov.io/bash)

We instructed the jacoco plugin to trigger during the clean install phase. Additionally, we’ve included the after_success section, which will send the report to CodeCov after the build is successful.

我们指示jacoco插件在clean install阶段触发。此外,我们还包括after_success部分,它将在构建成功后将报告发送给CodeCov。

Going forward, we should add a test class to our application. For instance, it could be a test for the main class:

展望未来,我们应该为我们的应用程序添加一个测试类。例如,它可以是一个主类的测试。

@SpringBootTest
class CiCdApplicationIntegrationTest {

    @Test
    public void contextLoads() {

    }
}

Lastly, we should push our changes. The build should be triggered and the report should be generated in our CodeCov profile related to the repository.

最后,我们应该推送我们的修改。构建应该被触发,报告应该在我们的CodeCov配置文件中生成,与版本库相关。

7. Deploying the Application

7.部署应用程序

As the last part of our process, we’ll deploy our application. With a Docker image available for use, we can deploy it on any service. For instance, we could deploy it on cloud-based PaaS or IaaS.

作为我们过程的最后一部分,我们将部署我们的应用程序。有了可供使用的Docker镜像,我们可以将其部署在任何服务上。例如,我们可以把它部署在基于云的PaaS或IaaS上。

Let’s deploy our application to Heroku, which is a PaaS that requires minimal setup.

让我们把我们的应用程序部署到Heroku,这是一个PaaS,需要最少的设置。

First, we should create an account and then log in.

首先,我们应该创建一个账户,然后登录

Next, let’s create the application space in Heroku and name it baeldung-ci-cd-processThe name of the application must be unique, so we might need to use another one.

接下来,让我们在Heroku中创建应用空间并命名为baeldung-ci-cd-process应用程序的名字必须是唯一的,所以我们可能需要使用另一个名字。

We’ll deploy it by integrating Heroku with GitHub, as it’s the simplest solution. However, we could also have written the pipeline that would use our Docker image.

我们将通过整合Heroku与GitHub来部署它,因为这是最简单的解决方案。然而,我们也可以编写使用我们的Docker镜像的管道。

Going forward, we should include the heroku plugin in our pom:

今后,我们应该在pom中包括heroku插件

<profile>
    <id>deploy-heroku</id>
    <properties>
        <maven.deploy.skip>true</maven.deploy.skip>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>com.heroku.sdk</groupId>
                <artifactId>heroku-maven-plugin</artifactId>
                <version>3.0.2</version>
                <configuration>
                    <appName>spring-boot-ci-cd</appName>
                    <processTypes>
                        <web>java $JAVA_OPTS -jar -Dserver.port=$PORT target/${project.build.finalName}.jar</web>
                    </processTypes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</profile>

Like with Docker, we’ve added it as part of the Maven profile. Additionally, we’ve included a startup command in the web section.

与Docker一样,我们将其作为Maven配置文件的一部分。此外,我们在web部分加入了一个启动命令。

Next, we should adjust our build service file (.travis.yml) to deploy the application to Heroku as well:

接下来,我们应该调整我们的构建服务文件(.travis.yml),将应用程序也部署到Heroku。

script:
  - ./mvnw clean install
  - ./mvnw heroku:deploy jib:build -P deploy-heroku,deploy-docker

Additionally, let’s add the Heroku API-KEY in a HEROKU_API_KEY variable in our building service.

此外,让我们在构建服务中的HEROKU_API_KEY变量中添加Heroku API-KEY。

Lastly, let’s commit our changes. The application should be deployed to Heroku after the build has finished.

最后,让我们提交我们的修改。构建完成后,应用程序应该被部署到Heroku。

We can check it by accessing https://baeldung-ci-cd-process.herokuapp.com/actuator/health

我们可以通过访问https://baeldung-ci-cd-process.herokuapp.com/actuator/health来检查。

8. Conclusion

8.结语

In this article, we’ve learned what the essential parts of the CI/CD process are and how to prepare them.

在这篇文章中,我们已经了解了CI/CD过程的基本部分是什么,以及如何准备它们。

First, we prepared a Git repository in GitHub and pushed our application there. Then, we used Travis CI as a build tool to build our application from that repository.

首先,我们在GitHub上准备了一个Git仓库,并将我们的应用程序推送到那里。然后,我们使用Travis CI作为构建工具,从该仓库构建我们的应用程序。

After that, we created a Docker image and pushed it to DockerHub.

之后,我们创建了一个Docker镜像并将其推送到DockerHub。

Next, we added a service that’s responsible for static code analysis.

接下来,我们添加了一个负责静态代码分析的服务。

Lastly, we deployed our application to PaaS and accessed it.

最后,我们将我们的应用程序部署到PaaS,并对其进行访问。

As always, the code for these examples is available over on GitHub.

一如既往,这些例子的代码可在GitHub上获得。