ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

从 Jenkins 迁移到 Gitea Actions:轻量级 CI/CD 流水线实践指南

从 Jenkins 迁移到 Gitea Actions:轻量级 CI/CD 流水线实践指南 1. 为什么我决定放弃 Jenkins先说结论Jenkins 不是不好而是太重了。我用了五年多 Jenkins从 2.x 一路升上来插件装了几十个Master 节点从 2G 内存加到 8G还是动不动就卡。每次构建高峰期队列里排着任务看着页面转圈真想砸键盘。疫情前那批项目都是用 Jenkins 扛下来的但说实话它给我的感觉越来越像一台老式机床——功能强大但维护成本已经超过了它带来的收益。我放弃 Jenkins 的直接导火索有三件事。第一插件依赖地狱。某个安全插件需要升级结果连带要升级其他六个插件其中两个又和现有插件冲突最后花半天时间解决依赖还不如重新搭一套。第二配置即代码做不彻底。虽然 Jenkins 有 Jenkinsfile但真正用过的人都知道语法别扭调试困难写出的流水线难以复用。第三界面操作反馈太差排错基本靠日志图形化只停留在“能看”的程度谈不上“好用”。后来我调研了一圈替代方案GitLab CI、GitHub Actions、Drone、Gitea Actions 都试过最终在一个内部工具链迁移项目里彻底用 Gitea Actions 替换了 Jenkins过程比预想中平滑太多。我意识到不是只有 Jenkins 能做 CI/CD很多新生代工具已经把“开箱即用”做到了极致。这篇我就拿 Gitea Actions 作为例子讲讲为什么它值得作为 Jenkins 的替代品以及怎么把任务迁过去。如果你也是那种“每天被 Jenkins 维护折磨、只想快点把代码跑起来”的开发者这篇文章应该能帮你少走不少弯路。下面我按自己的迁移过程来写尽量把能复现的步骤、能避开的坑都交代清楚。2. 替代工具 Gitea Actions 是什么凭什么更好2.1 它不是另一个 Jenkins而是一套轻量 CI/CD 引擎Gitea 本身是一个轻量级 Git 托管服务界面清爽安装简单一个二进制文件就能跑起来。它从 1.19 版本开始内置了 Gitea Actions底层兼容 GitHub Actions 的工作流语法。换句话说你不需要再额外装一个独立的 CI 服务器只要把代码托管到 Gitea然后在仓库里增加一个.gitea/workflows配置文件就能触发构建、测试、部署。这套逻辑和 Jenkins 有本质区别。Jenkins 的构建任务是靠插件一个个把能力“拼”出来的而 Gitea Actions 用的是“工作流 动作”的概念所有任务都由一个指令性的 YAML 文件描述执行器去容器里跑任务运行环境天然隔离参数传递也更直观。对于不熟悉 Jenkins 的同事来说学习 Gitea Actions 只需要理解三个词on触发条件、jobs任务列表、steps执行步骤。2.2 完全图形化操作但是不逼你用图形有人说“完全图形化操作”是亮点其实我的体会是“该有的图形界面都有该保留的文件版本控制也保留了”。Gitea 的仓库页面可以直接看到 Actions 标签页每次提交的执行状态、日志输出、耗时统计都清清楚楚点击就能展开看每一条命令的输出。相比 Jenkins 的“构建历史 控制台输出”这种分离式体验Gitea Actions 把“提交、触发、执行、日志”串在一条时间线上定位问题非常顺手。但它没有放弃 YAML 工作流。这两者并不矛盾——你可以在网页上维护工作流文件、查看运行结果也可以把工作流文件当作代码一样提交、审查、回滚。这种“文件即配置 可视化观察”的组合让我这种习惯命令行的人觉得严谨也让不太接触命令行的测试、运维同事能直接在网页上追踪进度。Jenkins 的那种“Job 配置存在服务端数据库里换台服务器就要重新导配置”的玩法在 Gitea Actions 面前显得格外笨重。2.3 学习成本到底低在哪我用 Jenkins 教会一个新人看懂流水线通常要讲两个星期包括节点、构建器、发布器、触发器、凭据、插件管理这些概念。而在 Gitea Actions 这边新人只要会看 GitHub Actions 的文档基本就能上手。因为工作流文件是声明式的每个step就是一个独立的命令或者一个uses引用的现成动作没有“插件中心-项目关联”这种中间层。举一个最直观的例子。在 Jenkins 里如果你要发一封邮件通知需要安装 Email Extension 插件、配置 SMTP、在系统设置里填账号、在 Job 里添加 post-build action、再引用构建参数。在 Gitea Actions 里你只需要在steps后面写一个uses: dawidd6/action-send-mailv3把收件人和 SMTP 信息作为with参数传进去三行搞定。这个差距就是所谓的“学习成本更低”。2.4 免费且没有授权陷阱Gitea 是开源软件Gitea Actions 是内置功能不存在“基础功能免费高级功能收费”的套路。Jenkins 本身也开源但它的“开源”意味着你需要自己维护插件、自己处理兼容性、自己搭建高可用。而 Gitea 作为一个完整产品安装升级都有现成方案社区版没有隐藏限制。对我们中小团队来说这不仅是省钱更是省心——不用去数人头看授权也不用担心哪一天突然收到销售电话。3. 从 Jenkins 迁移到 Gitea Actions环境准备与安装3.1 安装 Gitea 本体两条路一条比一条简单如果只是个人项目最省事的方式是直接下载官方二进制文件运行。Gitea 会自己管理 SQLite 数据库默认端口 3000启动后浏览器打开http://服务器IP:3000就能进入安装向导。安装向导会问你数据库类型、站点名称、管理员账号填完即用。这个过程撑死五分钟比 Jenkins 的 war 包加 Tomcat 部署轻太多。如果是团队使用我推荐用 Docker Compose 部署一套gitea gitea-runner。一个docker-compose.yml包含 Gitea 服务和 runner 服务两分钟启动。这里有一个关键配置Runner 必须通过GITEA_RUNNER_REG_TOKEN注册到 Gitea 实例这个 token 在 Gitea 管理后台的“管理动作”页面可以生成。注册完成后Runner 会自动轮询任务不需要额外守护进程管理。version: 3 services: gitea: image: gitea/gitea:latest ports: - 3000:3000 volumes: - /srv/gitea:/data runner: image: gitea/act_runner:latest depends_on: - gitea environment: - GITEA_INSTANCE_URLhttp://gitea:3000 - GITEA_RUNNER_REG_TOKEN你的注册令牌 volumes: - /var/run/docker.sock:/var/run/docker.sock - /srv/gitea-runner/data:/data注意runner挂载了 Docker socket这一步是为了让 Runner 能够通过 Docker 启动临时容器来执行每一个 Job 步骤。如果你不想给 Runner 这么高的权限也可以选择使用宿主机的 shell 执行模式但那样就失去了隔离性不推荐。挂载 socket 后要保证 socket 文件的权限是660并且跑 runner 的进程在docker组里否则会出现权限错误。3.2 汉化和基础设置别在这个环节浪费太多时间很多人在网上搜“jenkins汉化”怎么搞装插件换语言包。Gitea 这边不需要这么痛苦它本身就支持多语言安装向导里直接选“简体中文”整个管理后台就是中文界面。Actions 的运行日志是纯英文的这个没法汉化但日志内容本来也是命令输出和 Jenkins 一模一样不影响使用。安装完成后我建议立刻在“设置 - 动作”里把“默认工作流权限”改为“读取仓库内容”同时开启“允许创建公开工作流 Fork”。这样能避免新手误操作导致的工作流权限过大问题。Gitea 的权限模型比 Jenkins 更接近 Git 仓库权限没有 Jenkins 那种“全局凭据”的复杂层级日常管理压力小得多。3.3 为迁移准备的仓库结构调整从 Jenkins 迁移不要直接拷贝 Job 配置那样会拖泥带水。我做的第一步是梳理原有 Jenkins Job 的三种类型构建类、部署类、清理类。构建类对应 Gitea Actions 的push触发工作流部署类对应workflow_dispatch手动触发或tag触发清理类直接删除交给 Git 分支自动清理机制搞定。梳理完毕后把每个 Jenkins Job 里的核心选择在.gitea/workflows/下重写为 YAML 文件。Jenkins 里用插件实现的“读取 SSH 私钥”“执行远程脚本”“上传到服务器”这些能力在 Gitea Actions 里分别对应“配置 Secrets”“使用 ssh 动作”“使用 scp 动作”。你会发现每一条逻辑都更透明因为每一步都是显式的容器执行不像 Jenkins 插件内部帮你做了很多隐藏操作。4. 核心实操用 Gitea Actions 写一条自动化部署流水线4.1 第一个工作流文件别一上来就搞复杂我建议第一步只做一个“打印环境变量”的任务用于验证 Runner 和基本语法。在仓库根目录创建.gitea/workflows/demo.ymlname: Demo Workflow on: push: branches: [ main ] jobs: echo: runs-on: ubuntu-latest steps: - name: 打印当前分支 run: echo 当前分支是 ${GITHUB_REF} - name: 查看工作目录 run: ls -la推上去之后仓库的 Actions 标签页会立刻出现一条记录。如果这一步能跑通说明从代码推送到 Runner 执行的全链路是正常的。接下来再慢慢加构建步骤不要第一版就写 50 行复杂逻辑否则排错困难。注意这里有个细节Gitea Actions 兼容 GitHub Actions 的GITHUB_REF、GITHUB_SHA、GITHUB_WORKSPACE这些变量。网上搜到的一些 jenkins 可用环境变量列表在 Gitea 里不能直接用但 Gitea 也提供了自己对等的变量集官方文档里叫“Gitea Actions 内置环境变量”。例如GITEA_REPOSITORY_NAME、GITEA_ACTOR等等。用的时候建议去看一遍官方文档免得照搬 GitHub 的流水线时踩坑。4.2 构建 Node.js 项目并缓存依赖下面这个例子是很多项目都能套用的拉代码、安装依赖、跑测试、构建产物、上传产物。在 Jenkins 里这个流程需要配置 NodeJS 插件、构建工具、归档产物插件在 Gitea Actions 里流程是name: Node Build on: push: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - name: 检出代码 uses: actions/checkoutv4 - name: 设置 Node 环境 uses: actions/setup-nodev4 with: node-version: 20 - name: 缓存 npm 依赖 uses: actions/cachev4 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles(package-lock.json) }} - name: 安装依赖 run: npm ci - name: 运行测试 run: npm test - name: 构建 run: npm run build - name: 上传产物 uses: actions/upload-artifactv4 with: name: dist path: dist这个流水线里actions/checkout是官方动作相当于 Jenkins 里的“从 Git 拉源码”actions/cache处理缓存避免了每次重复下载 node_modules。最关键的是这里所有动作都不需要提前安装插件Runner 会自动从 GitHub 或 Gitea 的动作市场拉取对应版本。如果你在内网环境可以设置动作镜像仓库这个后面常见问题里会讲。4.3 容器内使用 Docker 命令解决“Jenkins 容器内无法用 Docker”的老问题很多人在 Jenkins 里遇到一个经典问题Jenkins 运行在 Docker 容器里但容器内没有 Docker 客户端无法执行docker build。网上的常见解法是挂载宿主机的/var/run/docker.sock到 Jenkins 容器里再安装 Docker 客户端也就是俗称的 DinD 方案。这个方案能用但安全性堪忧而且版本升级经常会触发 socket 权限不匹配。换成 Gitea Actions这件事反而简单了。Gitea Runner 本身就在宿主机上或者挂在 Docker socket 上它的工作模式就是为每个 Job 启动一个新的 Docker 容器所以工作流里的dockerCLI 是天然可用的。你只需要在运行环境里选择runs-on: ubuntu-latest这个标签会被 Runner 解析为使用一个标准 Ubuntu 容器容器内自带 Docker CLI 并且连接宿主机的 Docker socket。然后用docker命令构建镜像完全绕开了 Jenkins 那种“插件装不上客户端”的尴尬。- name: 构建镜像 run: docker build -t myapp:latest . - name: 推送到镜像仓库 run: docker push registry.example.com/myapp:latest如果你不想在 Workflow 里写死 Docker 命令也可以直接使用docker/build-push-action这个官方动作把context和file参数传进去它会帮你做构建和推送。4.4 自动部署到服务器替代 Jenkins 的 Publish Over SSHJenkins 部署通常需要装 Publish Over SSH 插件然后在系统设置里维护多个 SSH Server 配置。Gitea Actions 的做法是在仓库的 Secrets 里存放目标服务器的私钥和连接信息然后在工作流里使用ssh动作实现远程命令。这里我用的是easingthemes/ssh-deploy做一个演示但更通用的是appleboy/scp-action和appleboy/ssh-action。先是推送文件- name: 上传构建产物到服务器 uses: appleboy/scp-actionv0.1.7 with: host: ${{ secrets.DEPLOY_HOST }} username: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_KEY }} source: dist/* target: /var/www/myapp strip_components: 1然后是远程重启服务- name: 执行远程部署脚本 uses: appleboy/ssh-actionv1.0.3 with: host: ${{ secrets.DEPLOY_HOST }} username: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_KEY }} script: | cd /var/www/myapp cp -r dist /opt/myapp systemctl restart myapp这个模式的好处是没有了 Jenkins 插件 SSH 配置的全局耦合每个仓库通过 Secrets 声明自己的部署凭据权限边界清晰。而且 Secrets 在日志中会打码相对安全。需要说明的是把私钥放在 CI 平台的 Secrets 里是常规做法但如果你的服务器禁止密码登录推荐再对密钥设置一个 passphrase然后在工作流里用sshpass或提前加载的方式解锁否则 Runner 读取密钥时会受限制。4.5 手动触发与定时任务Jenkins 有的它都有Jenkins 的 Build Trigger 可以设置定时构建、远程触发、参数化构建。Gitea Actions 对应的方式分别是schedule事件、workflow_dispatch事件和repository_dispatch事件。以手动触发为例在工作流里加on: workflow_dispatch: inputs: environment: description: 部署环境 required: true default: staging type: choice options: - staging - production这样在仓库的 Actions 页面会出现一个“Run workflow”按钮点击后可以选择环境参数再执行。Jenkins 的参数化构建功能在这里变得更加规范因为参数会被工作流里的inputs自动转成环境变量使用起来比 Jenkins 的字符串参数安全得多。定时任务用标准 cron 语法支持时区配置。例如每天早上 8 点执行一次on: schedule: - cron: 0 0 * * *需要注意 Gitea 默认的时区可能不是东八区建议在管理后台或者服务器环境变量里统一成TZAsia/Shanghai否则定时触发的时间会比预期晚 8 小时这种坑排查起来非常隐蔽。5. 常见问题与排查技巧实录5.1 Runner 不执行任务卡在等待状态这是第一次配置最容易遇到的问题。可能的原因有三个Runner 注册 token 不正确、Runner 进程没有正确连接到 Gitea 实例、仓库的工作流分支不合规。排查方法也简单。先看 Runner 日志。如果是用 Docker Compose 部署执行docker logs runner日志里会显示注册状态。如果看到registration token is not valid说明 token 过期了去后台重新生成一个再注册。如果日志显示连接成功但任务一直排队检查工作流文件里的runs-on标签。默认标签是ubuntu-latest而 Runner 注册时的默认标签可能也是这个但如果你的 Runner 是通过自行配置启动的标签不匹配就会导致任务永远无法调度。解决办法是在 Runner 的配置文件中将标签改为ubuntu-latest或者在工作流里改成与 Runner 相同的标签。5.2 工作流文件修改了但没触发Gitea Actions 的触发条件默认监听分支推送但是你修改的.gitea/workflows文件如果不在触发条件指定的分支上比如你在dev分支改了文件但工作流的on.push.branches只写了main那自然不会跑。这是声明式流水线最容易搞混的地方。正确理解是文件本身属于哪个分支就只影响那个分支的触发逻辑。如果你希望任何分支推送都执行可以去掉branches限制或者使用push: paths精确控制。另外如果你在网页上直接通过 Gitea 的编辑器修改工作流文件Gitea 会自动创建一条提交这个提交也是会触发工作流的。但如果你用git push --force强推并且工作流文件被删除Gitea Actions 默认会阻止删除分支触发的 Job这是安全策略属于正常行为。5.3 内网环境拉取动作很慢甚至失败经常有人问 Jenkins 插件下载慢怎么办网上有各种各样的插件加速器。Gitea Actions 同样需要拉取动作和容器镜像如果服务器在京外或内网默认从 GitHub 拉取 actions 会很慢。解决办法有两个层次。第一层给 Runner 配置代理或者换成国内的动作镜像。Gitea 官方支持在 Runner 配置中指定动作仓库的地址例如把ACTIONS_REPO设置为某个内网镜像仓库。第二层如果你用的动作是actions/checkout、actions/setup-node这类官方动作可以把动作仓库复制到自己的 Gitea 实例上然后在工作流里把uses的地址改成你内网 Gitea 的地址。流水线文件是文本批量替换一下即可。容器镜像的加速方式不同需要修改 Runner 所在宿主机的 Docker 镜像源在/etc/docker/daemon.json里配置 registry mirrors。这一步和 Jenkins 加速插件下载不是一个思路别混淆。5.4 环境变量传参容易踩的坑Gitea Actions 支持在env里定义环境变量也可以读取 Gitea 的内置变量。但有一个坑内置变量是以下划线 大写形式比如GITEA_REPOSITORY_NAME而不是REPOSITORY_NAME。我刚迁移时直接把 GitHub Actions 里的GITHUB_REPOSITORY拿来用结果发现值为空。规避办法是在工作流开头加一个步骤先打印所有环境变量确认实际可用的变量名再写业务逻辑。另一个常见问题是 Secrets 的使用。在with或env里引用 Secrets 时必须使用${{ secrets.XXX }}的上下文语法直接写$SECRETS_XXX是不会被识别的。而且在run的 shell 命令里引用 Secrets 时要注意不要打印到日志中。虽然 Gitea 会默认打码但如果把 Secret 拼进 URL 或者文件名中打码机制可能会失效建议使用临时文件传递。5.5 并行与依赖Jenkins 的 pipeline 和 Gitea 的 jobsJenkins 的流水线支持parallel并行执行Gitea Actions 同样通过jobs并行支持多个任务同时跑。默认情况下工作流里定义的所有jobs会并行执行没有依赖关系。需要串行时用needs字段指定前置任务。jobs: test: runs-on: ubuntu-latest steps: - run: npm test deploy: needs: test runs-on: ubuntu-latest steps: - run: npm run deploy注意needs是 job 级别的不是 step 级别。如果你希望同一个 job 里两个步骤互相等待只需要按顺序写在steps列表里即可。这个模型比 Jenkins 的 stage 更直观因为每个 job 都是独立容器环境完全隔离避免了 Jenkins 节点上残留文件导致的“灵异问题”。5.6 批量处理仓库从 Jenkins 多 Job 迁移的高效路径如果你维护了十几个 Jenkins Job逐个手工重写工作流会非常耗时。我的经验是写一个简单的脚本把 Job 配置里常见的“执行 shell 脚本”部分提取出来然后生成对应的工作流骨架。因为每个 Job 的目标都是“拉代码、构建、发布”只是细节不同可以用模板引擎生成。我用的模板是 Jinja2定义好基类将参数变量填充进去一次生成十几个 YAML 文件。但这里有一个重要提醒不要盲目保留 Jenkins 里的一些 Shell 脚本逻辑。比如 Jenkins 里为了在不同节点间传递产物经常把构建结果写到固定目录再通过插件归档。而在 Gitea Actions 里更好的做法是每一个 Job 都重新拉代码、重新构建利用缓存加速或者将构建产物上传为 artifact 供后续 Job 下载。这看起来重复了但实际上更可靠。我的一个项目把两个大模块的构建从串行改为并行后整体构建时间从 28 分钟降到了 12 分钟这就是迁移带来的直接收益。6. 我的实际体验与建议从决定放弃 Jenkins 到彻底迁移完成我一共花了一周时间其中前三天都在熟悉 Gitea Actions 的语法后四天做实际迁移和调试。如果用一句话总结体验那就是复杂性被系统性移除了。我不需要再关心插件版本、节点标签、系统配置这些和业务无关的东西只需要聚焦在“流水线里要跑什么命令”。我最喜欢的两个细节一个是可以直接在仓库页面上编辑工作流文件并且浏览器里能看到语法校验另一个是每次提交的状态都显示在提交记录旁边不用跳转页面就能判断构建是否通过。这种把“代码仓库”和“CI/CD 状态”融合在一起的设计才是新时代工具该有的样子。如果你团队里还有人不熟悉 YAML建议先用可视化仓库里的“动作”功能找到现成的工作流模板修改使用。Gitea 提供了不少示例模板比如 Node.js、Golang、Python 这些主流项目点几下就能生成。把这些模板研究明白后再按自己的需求增删步骤。最后再分享一个迁移后的运营技巧把.gitea/workflows目录当作普通代码来管理每次修改都要经过 Pull Request 审查。这能避免有人不小心把生产环境的密钥写进工作流文件。我还加了一个保护规则要求所有工作流文件的修改必须由 CI 管理员审批否则不能合并到主分支。这一点比 Jenkins 的全局安全配置好用得多因为粒度刚好是仓库级别。现在团队里已经有几个项目开始自己写工作流了我不需要再像以前那样每天帮他们调整 Jenkins Job。这就是工具选对之后获得的“时间复利”。如果你手头正好有个用了几年、改起来就想哭的 Jenkins 实例不妨用周末时间搭一个 Gitea 试试把一条最简单的流水线迁过去感受一下区别。反正试错成本很低——一个二进制文件、一个 Runner 容器加起来占用不到 500 兆内存实在不行再切回去也不亏。
返回列表