Currently, if you manually deploy a Spring Boot application, you usually upload a JAR package locally, upload it to the server via FTP, and restart the application. It’s too much of a hassle to deploy. If you can just commit code directly to the code base, automate testing, and test it by deploying the application, which is continuous integration, you can save a lot of time to build a better product.

Current continuous integration services include Travis CI, the pioneer of Github, Tencent’s Coding, and Jenkins.

Github Actions is a continuous integration and continuous deployment service provided by Github.

1. Github Actions

Creating the Workflow File

Github Actions As the name implies, you must first select your Github project and find the Actions feature, as shown in the following image:

On this page, we can see the official suggested workflow, as well as a large number of templates to choose from for deployment to different platforms and languages. Here we will look at the official default configuration first, and we can explore the rest after we are familiar with the process.

When we click Create workflow, we will create a YML file in the current repository. Github /workflows directory. This is our workflow configuration file, where we can define the trigger rule, compile phase, deploy phase, and so on.

Basic fields in this configuration file:

  1. Name: Indicates the workflow name

  2. On: triggers the rule. Specific events such as push, PR, etc., or timed execution or when external events occur, such as branch creation, etc., refer to events that trigger the workflow.

  3. Jobs.*. Runs-on: specifies the VM environment where the job runs, for example, Ubuntu-latest.

  4. Jobs.*. Steps: Specify the running steps of each job, such as the JDK version, compilation and packaging stages.

  5. Jobs.*.steps.run: command or action that the step runs.

    Each step can execute one or more commands (actions) in turn, such as MVN package and SSH-deploy.

Github Marketplace provides a wide range of Actions, such as SSH, FTP, etc., that can be easily used directly.

Note that a repository can have multiple workflow files with arbitrary names, and GitHub automatically runs a. Yml file whenever it finds one in the. GitHub/Workflows directory.

The environment variable

Find Secrets in Settings in Github and click Create Key, as shown in the picture:

GitHub Settings apply to the default environment variables for each step in a workflow run. Environment variables are case sensitive and can be created, read, and modified by commands run in an operation or step.

Create a custom Runner

The jobs.*. Runs-on field in the configuration file above refers to the virtual machine environment provided by the official system, that is, our job runs on the official system server. Can we add our server?

We can add servers to a single repository. To add a self-managed runner to a user repository, you must be the repository owner. For an organizational repository, you must be an organization owner or have the permissions of the repository administrator.

On the Github project’s home page, click Actions in Settings.

Click on the top right to add your own hosted Runner.

We can see that the corresponding download, configuration and use methods are provided according to different operating systems. After completing the operation according to the steps, we can see our customized runner on the list.

2. Use Github Actions + Docker to deploy the Spring Boot application

Creating the Workflow File

Name: Deploy with docker on: push: # branches: [master] pull_request: branches: [master] Jobs: compile: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up JDK 11 uses: actions/setup-java@v2 with: java-version: - name: Dependies Cache uses: actions/cache@v2 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{runner. OS}} - maven - # compiler package - name: Build with maven run: MVN package-dmaven.test. skip=true # Login to Docker Hub uses: Docker /login-action@v1 with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - name: Set up Docker Buildx id: Buildx uses: Docker /setup-buildx-action@v1 # build and push to central repository docker_build uses: docker/build-push-action@v2 with: context: ./ file: ./Dockerfile push: true tags: ${{secrets.DOCKER_HUB_USERNAME}}/imageName:latest # SSH uses: fifsky/ssh-action@master with: command: | sh start.sh host: ${{ secrets.HOST }} user: ${{ secrets.USER }} key: ${{ secrets.PRIVATE_KEY}}Copy the code

Configuring environment Variables

Docker Hub project configuration

If you are using Docker in Github Actions, refer to the official documentation. Here we introduce how to create Docker project, create pipeline, optimize pipeline, push mirror version and so on.

In the sample project, the following two configuration items are used:

  1. DOCKER_HUB_USERNAME: indicates the user name of the account

  2. DOCKER_HUB_ACCESS_TOKEN: An authorized Token that is created in user Settings.

In addition to using Docker, you can also use FTP Actions to upload jar packages to the specified directory of the server, and then directly run through jar-jar app.jar.

Configure an SSH key pair

SSH configuration items include HOST, USER, private key PRIVATE_KEY, HOST, and USER.

[root@host ~]$ssh-keygen <== Generating a public/private RSA key pair. Enter file in which to save the key (/root/.ssh/id_rsa): <== create directory '/root/.ssh'. Enter passphrase (empty for no passphrase): <== Enter the key lock code or press Enter to leave Enter same passphrase again: <== Enter the key lock code again Your identification has been saved in /root/.ssh/id_rsa. <== Your public key has been saved in / root /. SSH/id_rsa. Pub. < = = public key, The key fingerprint is: 0 f: d3: e7:1 a: 1 c: bd: 5 c: 03: f1:19: f1:22: df: 9 b: cc: 08 root @ hostCopy the code

Once you have created the public and private keys, you also need to place the public key in authorized_keys of the server so that the connection can be made correctly.

[root@host ~]$ cd .ssh
[root@host .ssh]$ cat id_rsa.pub >> authorized_keys
Copy the code

Why SSH?

We generally have two ways to connect to the server, one is to log in through the password, but the password will be cracked, very insecure, so you can use SSH to log in.

SSH generally has a pair of public and private keys, the server connection steps:

  1. The client sends a request to the server using a public key.
  2. The server verifies that the public key sent by the client is in the local public key list. If it is not, the server rejects the connection. If it is in the list, the server encrypts a random string through the public key and sends it to the client.
  3. The client decrypts the decrypted string using the private key and sends the decrypted string to the server. The server verifies whether the decrypted string matches. If it matches, the connection succeeds.

create Dockerfile

Since the use of Docker, then first need to create a Dockerfile file in the project root directory, and then create an image through Dockerfile.

FROM fabric8/ Java -alpine-openjdk11-jre RUN rm -f /etc/localtime \ &&ln -sv The/usr/share/zoneinfo/Asia/Shanghai/etc/localtime \ && echo "Asia/Shanghai" > / etc/timezone # copies the current directory of the jar package to docker container/directory # ADD target/app.jar /app/app.jar # ADD target/app.jar /app/app.jarCopy the code

Alpine is chosen for the base image because it’s so small it’s almost negligible, and Distroless or Busybox is also available.

In addition to this base image already integrated with the JDK, you can also install the JDK on a pure Alpine image, which is also very small and very fast on push and pull images.

More references: Cloud Native Lab’s Alpine vs Distroless vs Busybox

So, when we submit code to Github, the pipeline creates an image from the Dockerfile file and pushes the image, as shown in the pipeline configuration:

Docker_build uses: docker/build-push-action@v2 with: context:./ file: ./Dockerfile push: true tags: ${{ secrets.DOCKER_HUB_USERNAME }}/imageName:latestCopy the code

pull image && restart container

Through the above configuration, we can successfully push the image to the central repository of Docker Hub, so there is no next step that we need to go to the server to pull the latest image, then close the previous container, and start the new container based on the latest image.

# after push, use SSH connection server executing scripts - name: SSH USES: fifsky/SSH - action @ master with: the command: | sh start. Sh host: ${{ secrets.HOST }} user: ${{ secrets.USER }} key: ${{ secrets.PRIVATE_KEY}}Copy the code

So, we SSH to the server to execute the restart script.

#! /bin/bash docker pull username/app:latest docker rm -f containerName||true&&docker run --name=appName -d -p 8080:8080 username/app:latest docker image prune -afCopy the code

At this point, we can achieve continuous integration without manually uploading jar packages and performing various naming.

Reference 3.

  • GitHub Actions tutorial
  • website