ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

GitPuk+Arbess组合实现轻量级CICD自动化部署实战

GitPuk+Arbess组合实现轻量级CICD自动化部署实战 做CICD落地最怕的不是工具不会用而是链路里每个环节都在暗处。早些年我做发布靠人肉登录服务器、拉代码、改配置、重启进程一次上线起码折腾半小时一紧张还容易把测试环境的参数带到生产。后来下定决心把自动化部署这事彻底理清试过GitLab CI、Jenkins也写了不少Shell脚本最终在一个自托管项目里把GitPuk和Arbess组合起来整套CICD链路才算真正顺畅。GitPuk负责代码托管和Webhook事件触发Arbess负责流水线编排和发布执行两者用一套YAML配置串起来。我写这篇不是来谈空理论的直接把我实战里的方案选型、环境搭建、流水线配置和踩坑记录都摆出来适合正在为中小团队搭自动化部署、又不想被Kubernetes全家桶绑架的朋友参考。1. 方案选型GitPuk和Arbess在CICD链路里各自承担什么1.1 先想清楚自动化部署要解决的真实问题先说清楚一件事CICD里的CI持续集成和CD持续部署是两件事很多人混在一起。CI管的是“代码合并之后自动验证”重点在测试、编译、静态检查CD管的是“验证通过之后自动上线”重点在构建产物、环境切换、发布策略。你想让整个流程自动化就得先给这两个阶段画好边界否则流水线会越写越乱。我这边遇到的典型场景是一个后端API服务三四个环境开发、测试、预发、生产每次发版都要重复执行十几条命令。人工操作多了最常见的问题就是“我明明没改配置怎么线上挂了”其实是上一个人手滑改了生产参数。自动化部署不是为了省那几分钟是为了把可变操作变成不可变流程把错误率降下来——这是整个方案设计的出发点。所以我在后面所有章节里反复强调同一件事能由机器按固定路径执行的部分绝不要留给人类临场发挥。1.2 GitPuk代码入口与Webhook事件源GitPuk在这套方案里承担的是代码仓库入口的角色。有人问GitHub/GitLab不也能干这事吗能但不符合我们的场景。第一代码要留在自建环境里外部平台那条路直接排除第二团队只有二十来人不需要庞大的委员会式权限体系GitPuk的用户管理和仓库级权限刚好够用第三它的资源占用很小一台2C4G的机器跑GitPuk加Arbess完全没压力。这一点对中小团队太重要了。除了基本的Git操作GitPuk最关键的输出是Webhook事件。你在仓库里配置一个Webhook把push、merge request、tag创建这些事件推给ArbessArbess收到后就开始跑流水线。所以GitPuk不是只管代码它是整套自动化部署的触发器。这里还有一个顺手做掉的动作管理员和开发者的权限分开管理员能配置Webhook和分支保护开发者只能写代码和建PR这是避免有人绕过流程的最基本动作。1.3 Arbess流水线编排与发布执行引擎Arbess是发布引擎负责读流水线配置、调度任务、执行部署。我选它而不是继续用Jenkins核心原因是Arbess把“流水线即代码”贯彻得比Jenkins干净。Jenkins的Job配置在界面里点点点版本化困难换机器还要手动迁移。Arbess这边一条流水线就是一个YAML文件跟代码一起进仓库改配置走MR审计天然留痕。还有一个我当时很看重的点Arbess的任务节点可以独立部署GitPuk挂了不影响已经跑起来的发布任务代码托管和构建部署之间的故障域是隔离的。这个问题听起来不紧急真正遇到Git服务因仓库过大卡住导致Webhook延迟的时候你就知道这种隔离有多香了。后来GitPuk升级数据库时重启了几分钟线上正在跑的发布任务一点没受影响当时就觉得自己没选错。1.4 这套组合的取舍轻量自托管的代价与收益当然这套方案也不是没有成本。Arbess的生态比Jenkins小很多插件需要自己写或者找社区方案但这几年CICD的核心能力拉代码、跑测试、构建镜像、发布它都有原生支持够用。还有一点它不像GitLab CI那样天然绑定在同一个产品里需要你花一点时间把GitPuk的Webhook和Arbess的工作流串起来——这个串联就是后面章节的核心内容。整体选型逻辑总结成一句话用GitPuk做代码入口和事件源用Arbess做流程编排和发布执行两个组件各干各的中间用Webhook和密钥对接。轻量、自托管、配置可版本化是这套方案最大的三个卖点。需要付出的代价是前期得自己把整套环境串起来但串完之后日常维护压力非常小这也是我后来愿意继续在这套组合上投入的原因。2. 环境准备把GitPuk和Arbess跑起来2.1 用Compose快速部署GitPuk环境这块我直接用的是Docker Compose简单、可复现、迁移方便。需要一台Linux机器2C4G起步我实测跑一个小团队完全没有问题。GitPuk的compose文件大概长这样version: 3 services: gitpuk: image: gitpuk/gitpuk:3.2 container_name: gitpuk restart: always ports: - 3000:3000 volumes: - ./gitpuk-data:/var/lib/gitpuk environment: GITPUK_DB: sqlite GITPUK_INIT_TOKEN: change_me_in_production启动后浏览器访问端口3000用初始化Token创建管理员账号。接着创建一个测试仓库比如shop-api配置SSH公钥、保护main分支。这些操作每个Git平台都大同小异关键点是先给Arbess留好Webhook入口后面要用。我建议把管理员初始密码设置成带随机串的强密码保存到密码管理器里别为了省事复制到代码仓库里否则安全防线从第一天就是漏的。2.2 部署Arbess并接入RunnerArbess同样用compose起架构上是Server加Runner两部分。Server负责接收Webhook、管理流水线状态Runner负责真跑任务。如果只有一台机器可以把Runner和Server装在一起小团队够了。生产建议Server独立Runner按环境拆分这个我们后面会讲。下面是当时用的compose配置version: 3 services: arbess-server: image: arbess/server:1.8 ports: - 8080:8080 volumes: - ./arbess-server:/var/lib/arbess arbess-runner-1: image: arbess/runner:1.8 environment: ARBESS_SERVER: http://arbess-server:8080 RUNNER_NAME: runner-dev volumes: - /var/run/docker.sock:/var/run/docker.sock - ./runner-cache:/cacheRunner挂载宿主机docker.sock这样它就能直接在宿主机上创建构建容器实现在容器里调起其他容器这也是常见的Docker-in-Docker替代方案。注意这个权限很大生产环境要控制好Runner所在机器的访问权限别把Runner随便暴露到公网。我后来加了防火墙规则只允许Server这台机器访问Runner的API端口其他来源一律拒绝。2.3 三个必须理解的核心概念Webhook、Pipeline、Deploy Key配置前后要把三个核心概念揉碎Webhook、Pipeline、Deploy Key。我习惯用一个生活化类比Webhook就像快递柜门上贴的感应器快递员一放包裹就响铃Pipeline是柜子内部的处理流程——扫描条码、通知收件人、分配货架Deploy Key就是收件人的取件码没有它流程跑得再漂亮也打不开柜门。Webhook解决的是“怎么知道代码变了”。GitPuk在推送事件发生时会向Arbess的HTTP接口发一个POST请求带上仓库名、分支、提交hash、推送人等信息。Arbess收到后比对仓库存的Secret校验通过就触发对应流水线。这里最容易被忽略的是Secret很多人开始测试时不校验收到的消息随便填一串后面签名校验失败就查半天。Pipeline解决的是“代码变了该做什么”。它由多个Stage组成每个Stage可以串行或并行。我的建议是保持流水线浅平checkout、test两个并行分组、build、deploy四个阶段足够不要试图在一个流程里塞进几十个步骤否则排障难度直线上升。如果某个项目确实需要额外步骤再单独抽一条流水线别什么都往主流程里堆。Deploy Key解决的是“凭据从哪来”。Arbess要访问GitPuk拉代码、要访问目标服务器执行发布必须有SSH Key或Token。所有密钥都放进Arbess的Secret管理流水线里通过引用名称来使用而不是把明文密码写进YAML。这一点后面单独细说是整套方案的安全生命线。3. 流水线配置全拆解从分支策略到密钥管理3.1 分支策略设计什么分支自动部署到哪个环境流水线设计要跟着分支策略走。我们团队用的是GitHub Flow的简化版main是唯一的长生命周期分支所有功能分支从这里切出合并必须通过PR和测试dev是从main按时间周期拉出来的持续部署分支生产发布用Tag触发。具体映射关系如下分支/标签部署环境触发方式是否自动部署main生产基线合并PR时否需人工确认dev测试环境push到dev后自动是refs/tags/v*生产环境创建Tag时是需审批为什么生产不用main直接自动部署因为自动化解决的是操作正确性但“这次要不要上线”是一个业务决策不能交给机器。所以生产链路加一道人工审批而测试环境完全自动化跑得再烂也只是测试环境影响可控。还有一个细节dev分支定期从main同步避免测试环境跟主干差太多不然上线前才发现冲突就是大风控。3.2 一套完整的Pipeline YAML配置示例下面是一套我实际在Arbess上跑的Pipeline配置项目是一个Java后端服务。字段名可能随Arbess版本有差异但关键逻辑是一样的project: shop-api defaults: image: alpine:3.19 workflow: stages: - name: checkout type: git-checkout repo: gitpuk.local/shop-api/backend.git branch: ${TRIGGER_BRANCH} depth: 1 - name: test type: shell image: maven:3.9-eclipse-temurin-17 parallel: - cmd: mvn test -DskipITs - cmd: mvn spotless:check cache: maven-repo timeout: 300 - name: build-image type: docker-build dockerfile: Dockerfile context: ./ image: registry.local/shop-api/backend:${GIT_COMMIT_SHORT} registry: registry.local username: {SECRET:DOCKER_USER} password: {SECRET:DOCKER_PASS} timeout: 600 - name: deploy-test type: remote-exec hosts: - {HOST: TEST_ENV, SECRET: SSH_PRIV_KEY} script: | docker pull registry.local/shop-api/backend:${GIT_COMMIT_SHORT} docker stop backend-old || true docker rm backend-old || true docker run -d --name backend-new -p 8080:8080 \ registry.local/shop-api/backend:${GIT_COMMIT_SHORT} when: branch: dev - name: deploy-prod type: approval when: tag: v* - name: deploy-prod-run type: remote-exec hosts: - {HOST: PROD_ENV, SECRET: SSH_PRIV_KEY} script: | docker login registry.local -u ${SECRET:DOCKER_USER} -p ${SECRET:DOCKER_PASS} docker-compose -f docker-compose.prod.yml up -d when: tag: v*先看checkout。type是git-checkoutrepo填的是GitPuk上的仓库地址branch从${TRIGGER_BRANCH}取。depth:1表示浅克隆只拉最新一次提交目的是把拉代码时间从几十秒压到几秒。注意如果你的构建需要历史提交记录或Submodule浅克隆会坑你这种场景要把depth去掉或带上submodules参数。test阶段用的是Maven容器。parallel下面挂两个任务一个跑单元测试一个跑代码风格检查。这两件事互不干扰并行执行可以把总耗时从两分钟压到一分钟出头。cache字段指向仓库缓存的maven-repo避免每次构建都重新下载一遍依赖。没有缓存的时候冷启动拉依赖可能要十几分钟这个优化非常值得做。超时设300秒超过就杀不心疼。build-image阶段是在Runner机器上调用Docker构建镜像并推送到私有registry。image标签用${GIT_COMMIT_SHORT}也就是commit短哈希registry指定私有仓库地址用户名密码通过{SECRET:DOCKER_USER}引用。这里有两个容易踩的点Dockerfile的context是./表示在流水线工作区根目录构建如果你的Dockerfile不在根目录必须把context指到对应目录否则COPY全都找不到文件。另外私有的registry如果不支持匿名拉取后面所有部署节点都要配置docker login否则镜像在目标服务器上拉不下来。deploy-test阶段有条件分支只有when.branch等于dev时才执行。脚本逻辑很简单拉镜像、停旧容器、删旧容器、起新容器。这个流程看起来粗暴但测试环境完全够用而且每次都是全新容器配置污染少。真正跑生产时我不建议这样直接docker run后面滚动发布一节会专门说清楚。deploy-prod是人工审批节点type为approvalwhen.tag匹配v*才出现。Arbess会让审批人在Web界面上点通过或拒绝通过后继续拒绝则整条流水线标记为失败。deploy-prod-run是真正对生产环境执行的远端命令用docker-compose方式起服务好处是环境变量和网络配置都在一个文件里可重复、可回滚。3.3 关键参数怎么定镜像标签、超时与重试镜像标签我用${GIT_COMMIT_SHORT}也就是commit ID的前8位。为什么不用latest因为latest不可追溯回滚时你不知道线上跑的是哪次提交短哈希几乎唯一还能直接在GitPuk里点开对应commit排查问题非常方便。有些团队习惯用日期加时间但多个分支并发时容易冲突还是短哈希最省心。超时设置测试阶段300秒构建阶段600秒。这些值不是拍脑袋我观察过团队一个月的执行耗时单测平均90秒但周一早晨依赖源慢时会飙到200秒所以给到300秒留有缓冲镜像构建因为要拉基础镜像冷启动约90秒平均360秒600秒足够。如果超过这个时间说明任务基本卡死直接杀掉比等它白白占用Runner资源更划算。重试策略上我让build-image失败自动重试一次主要吸收registry网络抖动test失败不重试因为测试失败是代码问题重试只会掩盖真实情况。这两个策略放在全局配置里所有流水线默认生效。自动重试这里要小心只在幂等且大概率是瞬时故障的环节开启盲目全开会把伪故障滚成真故障。3.4 密钥与权限管理的三个原则第一个原则所有密钥进Secret存储不进代码。我在Arbess里配置了DOCKER_USER、DOCKER_PASS、SSH_PRIV_KEY三组敏感变量。流水线YAML里只写引用不写值。这样做还有一个好处作为平台管理员我可以看每次运行的变量解密日志知道哪个任务在哪个时间用了哪把密钥审计方便。第二个原则密钥按环境隔离。测试环境的SSH私钥不要跟生产环境共用。如果生产服务器被入侵测试密钥不能连上任何生产节点。这个隔离成本不高但很多人贪方便一把钥匙走天下出事就是大麻烦。我在Arbess里把生产部署用的SSH私钥单独放在prod区域流水线只有审批通过才允许读取权限模型越细越稳。第三个细节Webhook Secret单独生成用openssl rand -hex 32长随机串别用什么password123。GitPuk发送请求时用HMAC签名Arbess收到后重新计算验签两边不一致直接拒绝并记日志这是防止有人伪造推送事件的基本防线。这个Secret要同时存在GitPuk的webhook配置和Arbess的项目密钥库中两边必须一模一样。4. 自动化部署的核心环节从push到滚动的完整链路4.1 push之后发生什么一次部署的完整时序把整个链路串起来看开发本地git push到GitPuk的dev分支 → GitPuk校验用户权限、把commit写入仓库 → 触发Webhook事件向Arbess发POST请求 → Arbess校验签名匹配仓库项目找到对应流水线 → Runner拉取代码到工作区 → 执行checkout → 并行跑单元测试和静态检查 → 全部通过后构建Docker镜像并推送到私有registry → 流水线携带Docker标签和SSH凭据登录测试服务器执行remote-exec脚本 → 容器启动健康检查通过Arbess标记整个Pipeline为成功。整个过程从push到测试环境可用我这边实测平均6到8分钟。这个链路里最容易出问题的是Webhook环节。因为网络策略、防火墙、NAT都会影响GitPuk能不能访问到Arbess。我第一次配置时Arbess跑在同一台内网机器GitPuk在另一台Webhook地址写的是公网IP怎么都收不到。后来把所有节点放到同一二层网络里直接填内网地址问题立刻消失。排查网络连通性telnet一下IP和端口是最快的方式别一上来就怀疑配置。4.2 并行、串行与滚动发布节奏流水线里有一种节奏感该并行的并行该串行的串行。checkout是唯一的前置步骤必须最先跑test的两个任务互相独立并行跑build必须在test之后因为产物质量依赖测试结果deploy必须在build之后。这种依赖关系Arbess支持通过stage的depends_on字段定义而实际配置里我没写depends_onArbess默认按声明顺序串行只有显式parallel字段的才并行。理解这一点流水线编排就够用了。部署环节我用滚动发布的基本形态先拉新镜像启动一个新容器做健康检查通过后把流量切到新容器最后移除旧容器。上面的deploy-test脚本简化了流程但生产环境我用docker-compose配合healthcheck或者用编排工具起临时任务。注意一点不要把生产环境的健康检查等同于容器起来了必须等接口真正返回200再切流量否则会出现“新容器一直在重启但客户还访问旧容器”的假健康。4.3 人工审批与一键回滚生产中我加了Approval节点。Arbess支持定义审批人和超时时间审批人必须属于prod-approvers组审批超时24小时自动失败。这样一来“是否可以上线”的决定权始终在人而“上线过程怎么执行”完全自动化两条线不冲突。审批页面上会带上本次发布涉及的分支、commit列表、镜像tag和变更说明审批人不用再翻日志猜内容。回滚策略上我保留最近5个镜像tag不清理同时在生产服务器的部署脚本里固定保留上一次运行的容器作为backup。一旦发布后健康检查报错运维只需要在Arbess里手动触发一个rollback工作流脚本会把流量切回上一次的release版本。实测一次回滚从触发到恢复大约40秒比人肉改配置快一个量级。回滚动作也要留审计日志谁在什么时间回的滚一目了然。4.4 让状态主动通知你消息与监控配置没人愿意一直盯着流水线状态。我在Arbess配置了Webhook通知流水线开始、失败、通过三种事件推送到团队IM机器人。消息包含项目名、分支、提交人、commit链接、失败原因和日志链接。开发push之后不用再问“CI过了没”机器人会直接告诉他。关掉页面、干别的活等消息就行。还有一个被忽略的点通知不是越多越好。我一开始把每个Stage的状态都发一遍一天下来群消息刷屏大家把通知静音了。后来砍到只有三种Pipeline开始、全链路失败、生产发布成功或人工审批待处理。信息量少了反而没人漏看关键消息。偶尔的深夜发布会有单发的预警消息专门给值班运维频率很低但一定要有。5. 踩坑实录常见故障与排查技巧5.1 Webhook不触发先查签名和网络先说Webhook不触发。现象本地推代码GitPuk仓库的Webhook记录显示已发送Arbess那边却没有任何流水线被创建。第一步检查Webhook配置里的Secret跟Arbess项目里存的Secret是否一致不一致时Arbess会直接丢弃请求且不返回错误——这是安全设计但也让排障少了一个线索。第二步查GitPuk发出的请求目标地址是否可达在Arbess服务器上执行curl测试内网或外网通不通。第三步看GitPuk的事件记录里是否带签名头Arbess验签失败会记录日志这通常指向服务端时间不同步。对NTP时间漂移会导致HMAC验签失败这个坑很隐蔽。5.2 构建镜像推不上去的几个真相再说镜像构建推送失败。现象本地docker build正常流水线里却报permission denied。常见原因有三个Runner里配置的registry地址跟真实镜像仓库地址不一致例如写成了http但仓库只接受httpsDockerfile里的本地依赖路径在Runner工作区里不存在因为checkout用了depth:1Submodule内容没拉全还有registry的Token过期这种一般是长期运行导致给凭证加自动刷新策略就解决了。我的建议是build-image这个Stage的registry地址统一存放为配置项不要在每个流水线里手写Dockerfile里的COPY路径必须在Git仓库内否则脱离仓库的构建在Runner上是必失败的。还有一点容易被忽略Runner机器上的Docker版本如果太旧某些Dockerfile指令会直接报错部署时要把Runner的Docker升级到受支持的版本。5.3 部署任务卡死交互、known_hosts和超时部署任务卡死最让人头大。现象remote-exec脚本执行后Arbess一直显示running10分钟不结束。Shell脚本里如果出现交互提示例如docker run时要求输入密码或者SSH执行远程命令时提示确认指纹Runner就会卡在读输入上。解决方法是脚本里所有命令加-f参数或者提前通过ssh-keyscan把host key写入known_hosts任何可能交互的命令都加上非交互标志。另外服务器防火墙也会造成SSH连接建立后hang住建议remote-exec的超时设短一点比如90秒卡住的部署任务自动失败而不是无限占用Runner。5.4 问题速查表症状、原因与处理方向把上面这些经验和团队里遇到过的其他问题整理成一张速查表排障时可以直接对着看症状可能原因处理方向推代码后无流水线Webhook secret不一致、地址不可达、服务器时间漂移检查事件记录、curl连通性、NTP同步checkoout失败仓库名写错、SSH key无权限、分支不存在核对仓库地址、检查Deploy Key、确认分支名test阶段偶发失败Runner资源不足、缓存损坏增加Runner资源、清理缓存目录镜像推送被拒绝registry地址写错、凭证过期、非https核对配置、刷新Token、统一镜像地址部署成功但服务访问异常健康检查没做、容器启动参数错误查看日志、执行curl健康检查、检查端口映射你会发现大部分问题都不是“配置不会写”而是“路径、密钥、网络参数不一致”。所以在搭环境阶段多做一点参数规范后面能省大量排查时间。我把所有环境的IP、端口、仓库地址、机器角色整理成一个环境清单放到团队Wiki里改之前先对表基本能避开一半的坑。5.5 只有实际跑过才会注意到的细节Runner缓存目录默认在宿主机/root/.cache里如果Runner容器以非root运行经常出现缓存写入失败。我在compose里把缓存目录挂到一个专门volume并chown给Runner用户之后再没遇到缓存报错。类似这种问题文档一般不会写只有跑起来才会暴露。Arbess的日志默认UTC国内团队看时间很别扭。可以在启动环境变量里加TZAsia/Shanghai否则你排查问题时看到的“刚刚”和日志时间对不上坑过我好几次。时间是需要尽早统一的配置别等到对日志的时候再改。并发任务撞车也是一个实际问题。同一个仓库的同一分支同时触发两个流水线会产生工作区冲突。我在GitPuk的Webhook上做了过滤同分支如果已有running状态的流水线新push不重复触发而是标记为pending。Arbess配置里有对应开关直接打开能省掉大量排查时间。这套自动化部署链路我从零搭到稳定前后折腾了大概三周中间砍过很多设计。后来再有人问我怎么落地我都会先说把“变”和“不变”分开工具只是把不变的部分自动化剩下那些必须由人判断的部分再多自动化也替不了。
返回列表