环境准备

  • docker
  • java
  • mvn
  • git

安装好以上环境依赖以后,可以部署一下 portainer ,这是一款docker管理平台。安装以后可以方便后续操作。

部署流程

为了将java程序部署在docker中,需要一下几个步骤:

  1. 打jar包

通过 mvn package 在target目录下生成jar包即可。

  1. 生成镜像

为了生成镜像,我们首先需要一份Dockerfile文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 基础镜像
FROM openjdk:8-jre-slim
# 作者
MAINTAINER forri
# 配置
ENV PARAMS=""
# 时区
ENV TZ=PRC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 添加应用
ADD target/chatgpt-api.jar /chatgpt-api.jar
## 在镜像运行为容器后执行的命令
ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /chatgpt-api.jar $PARAMS"]

有了该文件以后,在项目根目录执行命令:

1
docker build -f ./Dockerfile -t forri/chatgpt-api:1.5 .

随即便可以在本地生成一个镜像,可以通过

1
docker images

命令查看

  1. push到dockerhub仓库

为了push到远端仓库,首先需要在本地登录dockerhub账号:

1
docker login

登录完成以后,执行命令:

1
docker push forri/chatgpt-api:1.5

即可将打包的镜像推送到远端,然后在任何一个包含docker环境的服务器上,随取随用。

  1. 在需要用的地方,拉取镜像后启动
1
2
docker pull forri/chatgpt-api:1.5
docker run -p 8080:8080 --name chatgpt-api -d forri/chatgpt-api:1.5

配置自动化部署

为了实现自动化的容器部署,使用github action能力实现:

  • 提交代码到main分支后,自动触发docker build 打包镜像上传dockerhub
  • ssh虚拟机,自动拉取最新镜像并部署

由于项目中依赖了chatgpt-sdk-java这个包,但是这个包没有上传到maven中央仓库,所以在打包的时候会提示包找不到的错误

本地可以通过手动install来解决,但是如果要用github action,这个方案就行不通了。

两个办法:

  • 将sdk集成到data工程
  • 上传sdk到中央仓库

这里使用第一个方案。

为了验证确实部署了新的代码上容器,新增一个接口用于部署后的验证:

1
127.0.0.1:8091/api/v1/auth/testcicd

准备好代码后,继续下面的操作(代码修改好后记得在本地 mvn package 并且能成功打包,再继续)

操作步骤

  • 注册dockerhub并获得accesstoken,并配置到github action的variables中
  • 获取部署服务器的ssh私钥(注意是私钥),并配置到github action的variables中
  • 将服务器公钥添加到keys文件(注意是公钥)
    1
    cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
    在代码根目录添加文件夹:.github/workflows/maven.yml
    代码内容:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
    # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven

    # This workflow uses actions that are not certified by GitHub.
    # They are provided by a third-party and are governed by
    # separate terms of service, privacy policy, and support
    # documentation.

    name: Java CI with Maven

    on:
    push:
    branches: [ "main" ]

    env:
    REGISTRY: docker.io # 默认为 docker.io,即去 Docker Hub 上找
    IMAGE_NAME: ${{ github.event.repository.name }} # 使用 GitHub Actions 提供的能力,可以自动获取仓库名
    IMAGE_TAG: latest # Docker Image 的 tag,为了方便我直接设置 latest
    PORT: 8091 # 服务要暴露的端口,可以改

    jobs:
    build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Set up JDK 8 # 可以改版本
    uses: actions/setup-java@v3
    with:
    java-version: '8' # 可以改版本
    distribution: 'temurin'
    cache: maven
    - name: Build with Maven
    run: mvn -B package --file pom.xml

    # Login against a Docker registry except on PR
    # https://github.com/docker/login-action
    - name: Log into registry
    if: github.event_name != 'pull_request'
    uses: docker/login-action@v2
    with:
    registry: ${{ env.REGISTRY }}
    username: ${{ secrets.DOCKER_HUB_USER }}
    password: ${{ secrets.DOCKER_HUB_TOKEN }}

    # Build and push Docker image with Buildx (don't push on PR)
    # https://github.com/docker/build-push-action
    - name: Build and push Docker image
    uses: docker/build-push-action@v3
    with:
    context: .
    push: ${{ github.event_name != 'pull_request' }}
    tags: ${{ secrets.DOCKER_HUB_USER }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}

    # 连接到远程服务器
    - name: Connect to server
    uses: webfactory/ssh-agent@v0.4.1
    with:
    ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}

    # 初始化 knownhosts
    - name: Setup knownhosts
    run: ssh-keyscan ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts

    # 触发服务器部署脚本
    - name: Trigger server deployment script
    run: |
    ssh root@${{ secrets.SERVER_HOST }} "docker stop ${{ env.IMAGE_NAME }} || true"
    ssh root@${{ secrets.SERVER_HOST }} "docker rm ${{ env.IMAGE_NAME }} || true"
    ssh root@${{ secrets.SERVER_HOST }} "docker pull ${{ secrets.DOCKER_HUB_USER }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}"
    ssh root@${{ secrets.SERVER_HOST }} "docker run -p ${{ env.PORT }}:${{ env.PORT}} --name ${{ env.IMAGE_NAME }} -d ${{ secrets.DOCKER_HUB_USER }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}"

提交代码,等待流水线执行完成后,到Portainer上检查部署情况,成功!
image
image
image

这样,后续迭代开发只需要在dev分支开发测试完成,然后merge到main分支后提交,即可自动部署到云服务。

参考文档:通过 GitHub Actions 完成 Spring Boot 项目的 CI/CD(基于 Docker)