本文主要记录自己这两天使用gitea实现把前后端完成打包,构建镜像,以及上传到镜像仓库中实现多地部署。

前置知识

CICD其实已经有过一篇文章介绍了,所以这里不再赘述。

什么是软件仓库?

软件仓库(Software Repository)是一个集中存储、管理和分发软件及其相关文件(如源代码、二进制文件、文档等)的系统或平台。它类似于一个专门存放软件的“图书馆”或“仓库”,旨在简化软件的获取、安装、更新和维护过程。
例如我们常常使用到的一些jar包,npm包或者docker镜像都可以存储在其中,并提供给他人使用。

Gitea同样也有对应的Package Registry用来支持软件仓库的功能。详见gitea文档
我要存储的便是docker镜像,也就是Container,对于存储这类镜像会有很多选择,包括dockerhub,Harbor,阿里私有镜像仓库等或者干脆自己搭建也可以。各家有各家的优势,我选择gitea仅仅是因为我是在本地部署的,我本人访问可以达到最优速率,体验大大增加。也省去了一些不必要的麻烦。

与他差不多的就是Github Package Registry(也提供免费服务,但是吧,我作为新手,肯定还是现在自己这玩明白了再去别地儿玩)。

docker镜像简单介绍

我本来只是会玩docker,导入各种docker镜像,搭建docker网络,以及配置相关的服务。但其实我从未自己构建过镜像。今天尝试一番,也算是揭开神秘面纱了。
docker image本身就像是一个副本,用于启动各种容器,一个镜像,根据传入的参数不同,启动起来的容器也会有一点小区别。但他的内里没变,大体还是这么个东西在这。
然后构建镜像的话,一般需要完成你的项目后,编写一个相对应的dockerfile文件,可以直接让ai帮你生成,然后在执行docker build -t <你的镜像名字> .
这条命令指的是,基于你当前的目录,他会自动读取是否有dockerfile文件,有的话,就会根据这个文件和对应的目录生成一个镜像,接着就可以用docker images查看到了。若要将其导出为一个压缩包也可以通过docker save 镜像id > image.tar实现相应的功能。或者你如我一般把他推送到一个镜像仓库中,便可以方便的使用了。

正式工作

先随便写一个前后端项目,注意前端要与后端相关联,可以访问到后端。

然后分别编写两者的dockerfile文件
我把我的两个文件示例如下:

  1. 前端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 构建阶段
    FROM node:22 AS build
    WORKDIR /app
    COPY package*.json ./
    RUN npm ci
    COPY . .
    RUN npm run build

    # 生产阶段
    FROM nginx:alpine
    COPY --from=build /app/dist /usr/share/nginx/html
    # 复制nginx配置
    COPY nginx.conf /etc/nginx/nginx.conf
    EXPOSE 5555
    CMD ["nginx", "-g", "daemon off;"]

  2. 后端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    FROM python:3.11-slim
    WORKDIR /app

    # 安装依赖
    RUN pip install --no-cache-dir fastapi==0.115.8 uvicorn[standard]==0.34.3 python-multipart

    # 复制代码
    COPY . .

    # 暴露端口
    EXPOSE 5000

    # 启动命令
    CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "5000"]

其中需要注意的是,你在前端中配置好的nginx.conf多半需要代理将访问的api都转发到后端。因此你在编写运行的docker-compose.yml文件时,要记得后端服务的名字与nginx配置里的名字保持一致,这样就可以将整个前后端项目组合成一个。

接下来就是重头戏,如何通过gitea action实现自动打包构建和上传。

  1. 先构建.gitea/workflow/docker-build-push.yml文件,文件名你们自己拟定,要在这个目录下,他就可以正确执行action。

  2. 展示代码

    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
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    name: Build and Push Docker Images

    on:
    push:
    branches: [ main, develop ]
    tags: [ 'v*' ]
    pull_request:
    branches: [ main, develop ]

    jobs:
    build-and-push:
    runs-on: ubuntu-latest
    strategy:
    matrix:
    service: [frontend, backend]

    steps:
    - name: Checkout code
    uses: actions/checkout@v4
    with:
    fetch-depth: 0 # 获取完整的提交历史用于标签生成


    - name: Login to Registry
    uses: docker/login-action@v3
    with:
    registry: gitea.zfxt.top
    username: ${{ vars.REGISTRY_USERNAME }}
    password: ${{ secrets.REGISTRY_PASSWORD }}

    # 为后端服务生成元数据
    - name: Extract metadata for backend
    if: matrix.service == 'backend'
    id: meta-backend
    uses: docker/metadata-action@v5
    with:
    images: gitea.zfxt.top/zfxt/backend
    tags: |
    type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
    type=sha,prefix=,suffix=,enable=true
    type=ref,event=branch
    type=ref,event=pr
    type=semver,pattern={{version}}
    type=semver,pattern={{major}}.{{minor}}
    type=semver,pattern={{major}}

    # 为前端服务生成元数据
    - name: Extract metadata for frontend
    if: matrix.service == 'frontend'
    id: meta-frontend
    uses: docker/metadata-action@v5
    with:
    images: gitea.zfxt.top/zfxt/frontend
    tags: |
    type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
    type=sha,prefix=,suffix=,enable=true
    type=ref,event=branch
    type=ref,event=pr
    type=semver,pattern={{version}}
    type=semver,pattern={{major}}.{{minor}}
    type=semver,pattern={{major}}

    # 构建和推送后端镜像
    - name: Build and push backend image
    if: matrix.service == 'backend'
    uses: docker/build-push-action@v6
    with:
    context: ./backend
    file: ./backend/dockerfile
    push: true
    tags: ${{ steps.meta-backend.outputs.tags }}
    labels: ${{ steps.meta-backend.outputs.labels }}

    # 构建和推送前端镜像
    - name: Build and push frontend image
    if: matrix.service == 'frontend'
    uses: docker/build-push-action@v6
    with:
    context: ./front
    file: ./front/dockerfile
    push: true
    tags: ${{ steps.meta-frontend.outputs.tags }}
    labels: ${{ steps.meta-frontend.outputs.labels }}
  3. 逐段解释

    1
    2
    3
    4
    5
    6
    on:
    push:
    branches: [ main, develop ]
    tags: [ 'v*' ]
    pull_request:
    branches: [ main, develop ]

    作用​​:定义工作流触发场景

    • ​分支推送​​:当代码推送到 main或 develop分支时触发
    • ​标签推送​​:当打上 v*格式的版本标签(如 v1.0.0)时触发
    • Pull Request​​:当向 main或 develop分支提交 PR 时触发
    1
    2
    3
    4
    5
    6
    jobs:
    build-and-push:
    runs-on: ubuntu-latest
    strategy:
    matrix:
    service: [frontend, backend]

    作用​​:定义并行构建任务

    • ​​runs-on​​:使用最新 Ubuntu 环境
    • ​matrix​​:并行构建frontendbackend两个服务
    1
    2
    3
    4
    - name: Checkout code
    uses: actions/checkout@v4
    with:
    fetch-depth: 0 # 获取完整提交历史

    作用​​:拉取仓库代码

    1
    2
    3
    4
    5
    6
    - name: Login to Registry
    uses: docker/login-action@v3
    with:
    registry: gitea.zfxt.top
    username: ${{ vars.REGISTRY_USERNAME }}
    password: ${{ secrets.REGISTRY_PASSWORD }}

    作用​​:登录私有 Docker 仓库

    • ​​secrets​​:需在 GitHub 仓库的 Settings > Action中预先配置
    • vars: 同上
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    - name: Extract metadata for backend
    if: matrix.service == 'backend'
    id: meta-backend
    uses: docker/metadata-action@v5
    with:
    images: gitea.zfxt.top/zfxt/backend
    tags: |
    type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
    type=sha,prefix=,suffix=,enable=true
    type=ref,event=branch
    type=ref,event=pr
    type=semver,pattern={{version}}
    type=semver,pattern={{major}}.{{minor}}
    type=semver,pattern={{major}}

    ​​作用​​:为后端服务生成多维度镜像标签

    ​​标签类型​​:

    • latest:仅主分支构建时添加
    • sha:基于提交哈希的唯一标识
    • branch/pr:分支/PR 名称
    • semver:语义化版本(需项目支持版本管理)
    • ​原因​​:提供多种标签便于追踪和管理镜像
    1
    2
    3
    4
    5
    6
    7
    8
    9
    - name: Build and push backend image
    if: matrix.service == 'backend'
    uses: docker/build-push-action@v6
    with:
    context: ./backend
    file: ./backend/dockerfile
    push: true
    tags: ${{ steps.meta-backend.outputs.tags }}
    labels: ${{ steps.meta-backend.outputs.labels }}

    作用​​:构建后端镜像并推送

    • ​​context​​:Dockerfile 所在目录
    • file​​:Dockerfile 文件名
    • tags/labels​​:复用元数据生成的标签

以上差不多就完成了,但是!我真的遇到好些问题,最后归纳一下,

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

这个插件!!!用不了,用了就要报EOF的错误,可以说我目前和多平台无缘了。害(我忙活了一天才揪出来这个bug,其他nginx代理,校验方式,gitea历史遗留问题都试过了)。结果发现他这个新版的buildx可能用不了。呜呜呜。难过死我了,

最后总结

上传完成后,可以看到我的软件包界面多了几个可以直接使用的包
Gitea实现Docker镜像打包上传全流程-2025-09-04-16-26-02
差不多也就结束了,主要是那个bug太烦人了。啊啊啊。