Skip to content

Github Action Docker 配置

概述

该配置将允许我们推送 tag 后, 编译 Docker 镜像并上传至 Github 的 Docker 镜像仓库中。

可选的是:

  • 在打包开始和结束时都会发送飞书通知.
  • 自动部署到服务器

关于如何创建镜像,请参考 Docker 配置

Github 仓库配置

飞书通知配置

在 Github 的仓库设置中配置 Action secrets,名称为 FEI_SHU_URL,值为飞书机器人的地址,形如 https://open.feishu.cn/open-apis/bot/v2/hook/xxx

secrets配置飞书参数

部署服务器配置

如上节所示,再添加如下三个 secrets:

  1. SERVER_HOST 服务器地址 IP
  2. SERVER_USERNAME 服务器用户名
  3. SERVER_PASSWORD 服务器密码

Github Action 配置

在仓库根目录创建 .github/workflows/release.yml。 (文件名可以自定义,但是必须放在 .github/workflows 目录下)

请务必检查 TODO 标记的地方,根据自己的需求修改,其中最重要的就是打包镜像相关的配置。

  1. dockerfile 镜像的 Dockerfile 路径
  2. image 镜像名,为防止冲突,建议使用 github.repository 变量作为前缀
  3. container 容器名
  4. port 容器端口号,格式为 宿主机端口号:容器端口号

DANGER

image,container 和 port 三个变量必须在服务器上唯一,创建部署前请通过监控页面检查是否已经存在。

yml
name: Release

# 限定推送 tag 形式为 x.y.z 的时候触发
on:
  push:
    tags:
      - "*.*.*"

env:
  registry: ghcr.io
  registry-username: ${{ github.actor }}
  registry-password: ${{ secrets.GITHUB_TOKEN }}
  # TODO: 确保此处的 secrets 在 GitHub 仓库中都存在且无误
  server-host: ${{ secrets.SERVER_HOST }}
  server-username: ${{ secrets.SERVER_USERNAME }}
  server-password: ${{ secrets.SERVER_PASSWORD }}
  node-env: production

concurrency:
  group: ${{ github.workflow }}
  cancel-in-progress: true

jobs:
  build-and-push-image:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      # TODO: 修改此处的dockerfile路径,镜像名,容器名和端口号。可以根据需要添加多个镜像。
      matrix:
        include:
          - dockerfile: ./Dockerfile
            image: ${{ github.repository }}/starter-react
            container: starter-react-demo
            port: 5001:80

    permissions:
      contents: write
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      # TODO: 移除这个 step,如果你不需要 Feishu 通知
      - name: Create notification
        id: notification-before
        uses: actions/github-script@v6
        if: always()
        env:
          GITHUB_REPOSITORY: ${{ toJson(github.repository) }}
          GITHUB_HEAD_COMMIT: ${{ toJson(github.event.head_commit) }}
          NOW_TAG: ${{ github.ref_name }}
        with:
          script: |
            const repository = process.env.GITHUB_REPOSITORY
            const commit = JSON.parse(process.env.GITHUB_HEAD_COMMIT);
            const authorName = commit.author.name;
            const message = commit.message;
            const url = commit.url;
            return {"msg_type":"text","content":{"text":`镜像开始构建:\n- 仓库: ${repository}\n- 标签: ${process.env.NOW_TAG}\n- 提交作者: ${authorName}\n- 任务url: ${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`}}

      # TODO: 移除这个 step,如果你不需要 Feishu 通知
      - name: Send Feishu notification
        if: always()
        run: |
          curl -X POST -H "Content-Type: application/json" -d ${{ toJson(steps.notification-before.outputs.result) }} ${{ secrets.FEI_SHU_URL }}

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to the Container registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.registry }}
          username: ${{ env.registry-username }}
          password: ${{ env.registry-password }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.registry }}/${{ matrix.image }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        id: docker_build
        with:
          context: .
          file: ${{ matrix.dockerfile }}
          push: true
          tags: ${{ steps.meta.outputs.tags }}

      # TODO: 移除这个 step,如果你不需要 Feishu 通知
      - name: Create notification
        id: notification
        uses: actions/github-script@v6
        if: always()
        env:
          GITHUB_REPOSITORY: ${{ toJson(github.repository) }}
          GITHUB_HEAD_COMMIT: ${{ toJson(github.event.head_commit) }}
          CONCLUSION: ${{ steps.docker_build.conclusion }}
          IMAGE: ${{ toJson(steps.meta.outputs.tags) }}
          NOW_TAG: ${{ github.ref_name }}
        with:
          script: |
            const conclusion = process.env.CONCLUSION;
            const repository = process.env.GITHUB_REPOSITORY;
            const commit = JSON.parse(process.env.GITHUB_HEAD_COMMIT);
            const authorName = commit.author.name;
            const message = commit.message;
            const url = commit.url;
            const image = process.env.IMAGE;
            return {"msg_type":"text","content":{"text":`${conclusion == 'success' ? "镜像构建成功" : "镜像构建失败"}:\n- 仓库: ${repository}\n- 标签: ${process.env.NOW_TAG}\n- 提交作者: ${authorName}\n- 任务url: ${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}\n- 镜像url: ${JSON.parse(image).split('\n')[0]}`}}

      # TODO: 移除这个 step,如果你不需要 Feishu 通知
      - name: Send Feishu notification
        if: always()
        run: |
          curl -X POST -H "Content-Type: application/json" -d ${{ toJson(steps.notification.outputs.result) }} ${{ secrets.FEI_SHU_URL }}

      # TODO: 移除这个 step,如果你不需要自动部署到服务器
      - name: Deploy to Server
        uses: appleboy/ssh-action@v0.1.10
        id: deploy_to_server
        with:
          host: ${{ env.server-host }}
          username: ${{ env.server-username }}
          password: ${{ env.server-password }}
          script: |
            echo "开始登录"
            docker login -u ${{ env.registry-username }} -p ${{ env.registry-password }} ${{ env.registry }}

            echo "开始获取当前运行信息"
            container_name="${{ matrix.container }}"
            image_id=$(docker inspect --format='{{.Image}}' $container_name 2>/dev/null)
            if [ -z "$image_id" ]; then
                echo "容器 $container_name 不存在"
            else
                image_tag=$(docker image inspect --format='{{index .RepoTags 0}}' $image_id | cut -d ":" -f 2)
                echo "容器 $container_name 的当前镜像标签版本号为: $image_tag"
            fi

            echo "开始拉取镜像,新版本号为: ${{ github.ref_name }}"
            docker pull ${{ env.registry }}/${{ matrix.image }}:${{ github.ref_name }}
            echo "开始停止并删除旧容器"
            docker stop ${{ matrix.container }} || true && docker rm ${{ matrix.container }} || true
            echo "开始删除旧镜像"
            docker rmi ${{ env.registry }}/${{ matrix.image }}:${image_tag} || true
            echo "开始启动新容器"
            docker run --env NODE_ENV=${{ env.node-env }} -d -p ${{ matrix.port }} --name ${{ matrix.container }} ${{ env.registry }}/${{ matrix.image }}:${{ github.ref_name }}

      # TODO: 移除这个 step,如果你不需要 Feishu 通知
      - name: Create notification
        id: notification_after
        uses: actions/github-script@v6
        if: always()
        env:
          GITHUB_REPOSITORY: ${{ toJson(github.repository) }}
          GITHUB_HEAD_COMMIT: ${{ toJson(github.event.head_commit) }}
          CONCLUSION: ${{ steps.deploy_to_server.conclusion }}
          IMAGE: ${{ toJson(steps.meta.outputs.tags) }}
          NOW_TAG: ${{ github.ref_name }}
          HOST: ${{ env.server-host }}
          PORT: ${{ matrix.port }}
        with:
          script: |
            const conclusion = process.env.CONCLUSION;
            const repository = process.env.GITHUB_REPOSITORY;
            const commit = JSON.parse(process.env.GITHUB_HEAD_COMMIT);
            const authorName = commit.author.name;
            const message = commit.message;
            const url = commit.url;
            const image = process.env.IMAGE;
            const websiteUrl = `http://${process.env.HOST}:${process.env.PORT.split(':')[0]}`;
            return {"msg_type":"text","content":{"text":`${conclusion == 'success' ? "部署到服务器成功" : "部署到服务器失败"}:\n- 仓库: ${repository}\n- 标签: ${process.env.NOW_TAG}\n- 提交作者: ${authorName}\n- 任务url: ${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}\n- 镜像url: ${JSON.parse(image).split('\n')[0]}\n- 网站url: ${websiteUrl}`}}

      # TODO: 移除这个 step,如果你不需要 Feishu 通知
      - name: Send Feishu notification
        if: always()
        run: |
          curl -X POST -H "Content-Type: application/json" -d ${{ toJson(steps.notification_after.outputs.result) }} ${{ secrets.FEI_SHU_URL }}

配置更新版本脚本

安装依赖

bash
ni -Dw changelogen bumpp

添加更新脚本到 package.json

json
{
  "scripts": {
    "changelog": "changelogen --output && prettier --write CHANGELOG.md && git add CHANGELOG.md",
    "release": "bumpp --all --tag=\"%s\" --execute \"pnpm changelog\""
  }
}

确保 package.json 中的 version 字段存在(例如 0.0.0),然后运行 pnpm release 即可。