ARTICLE DETAIL

资讯详情

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

Gitee WebHook 实现个人项目自动部署:从手动 scp 到 “push 即部署“ 全流程实录(含 8 个坑)

Gitee WebHook 实现个人项目自动部署:从手动 scp 到 “push 即部署“ 全流程实录(含 8 个坑) 个人项目TypeScript Node Docker部署在阿里云 ECS每次更新都要ssh上去git pulldocker compose build手都拉出茧了。于是折腾了一套 “push 即部署”把过程和踩过的坑记下来给同样被手动部署折磨的朋友一个参考。一、先搞清楚CI/CD 到底是什么概念含义本次做到了吗CI持续集成每次提交自动构建、测试、静态检查部分有构建没跑测试CD持续部署代码通过检查后自动发布到生产环境做到了git push即部署触发方式WebHook实时/ 轮询有延迟/ 定时WebHook 实时触发严格说本次搭的是一个“极简 CD”没有独立的 CI 流水线靠 Git 平台的 WebHook 事件通知触发服务器端构建。对个人项目完全够用也是学习 CI/CD 最直观的入门路径。二、方案选型为什么是 Gitee WebHooks方案原理优点缺点GitHub Actions SSHpush 触发云端流水线SSH 到服务器执行部署生态大、可视化国内访问不稳本方案最初选的它最后弃用Gitee WebHooks本次采用push 时 Gitee 发 HTTP 通知到服务器服务器执行部署国内快、零门槛、实时要自己写接收端几十行 Python 搞定cron 轮询服务器定时git fetch比对零组件、最稳最长延迟 N 分钟非实时核心思路构建放在服务器上做个人 2C2G 小机器 swap 扛得住Gitee 只扮演触发源。这样省掉了镜像仓库、云端 runner 等一堆组件。三、架构总览校验 Tokengit fetch reset --hard本地 git pushGitee WebHookwebhook_server.py :9000deploy.shDocker 容器重建组件技术职责触发源Gitee WebHookpush 时 POST 到服务器带X-Gitee-Token签名头接收端webhook_server.pyPython 标准库零依赖校验 Token / 事件类型后台执行部署脚本执行器deploy.shgit fetch reset --hard origin/main→docker compose up -d --build宿主systemdUseradmin开机自启、崩溃自拉、日志统一审计/var/log/webhook-deploy.log每次部署全过程的输出四、实施步骤可直接抄1. 服务器项目目录 git 化# 服务器上假设项目在 /opt/navigate此前是 tar 包部署cd/opt/navigatecp.env.prod /tmp/.env.prod.bak# 先备份密钥文件sudochown-Radmin:admin /opt/navigate# 目录归属当前部署用户关键gitinitgitremoteaddorigin gitgitee.com:你的账号/你的仓库.gitgitfetch origingitreset--hardorigin/main# 工作区与远端完全一致cp/tmp/.env.prod.bak .env.prodchmod600.env.prod服务器拉代码用 SSH 密钥ssh-keygen -t ed25519生成后公钥加到 Gitee → 设置 → SSH 公钥再ssh-keyscan gitee.com ~/.ssh/known_hosts预存指纹。2. 部署脚本deploy.sh#!/bin/bashset-euopipefailexportGIT_SSH_COMMANDssh -o StrictHostKeyCheckingaccept-newcd/opt/navigateecho git sync ($(date%F %T))gitfetch origingitreset--hardorigin/main# 服务器是纯跟随角色硬对齐远端echo rebuild restart containersdockercompose --env-file .env.prod-fdocker-compose.prod.yml up-d--buildecho container statusdockercompose --env-file .env.prod-fdocker-compose.prod.ymlps3. webhook 接收端webhook_server.pyPython 标准库40 行importargparse,hmac,logging,socketserver,subprocess,threading,timefromhttp.serverimportBaseHTTPRequestHandler,HTTPServer# Python 3.7 才内置 ThreadingHTTPServer这里兼容 3.6 旧系统classThreadingHTTPServer(socketserver.ThreadingMixIn,HTTPServer):daemon_threadsTrueDEPLOY_SCRIPT/opt/navigate/scripts/deploy.shLOG_FILE/var/log/webhook-deploy.logclassHandler(BaseHTTPRequestHandler):secretdefdo_POST(self):ifself.path!/hooks/deploy:self.send_error(404);returntokenself.headers.get(X-Gitee-Token,)ifnothmac.compare_digest(token,self.secret):logging.warning(reject: bad token from %s,self.client_address[0])self.send_error(401);returnifself.headers.get(X-Gitee-Event,)!Push Hook:self.send_error(400);returnthreading.Thread(targetself._run_deploy,daemonTrue).start()self.send_response(200)self.end_headers()self.wfile.write(b{status:deploy started})def_run_deploy(self):try:withopen(LOG_FILE,a,encodingutf-8)asf:f.write(\n deploy triggered %s \n%time.strftime(%F %T))subprocess.run([bash,DEPLOY_SCRIPT],stdoutf,stderrsubprocess.STDOUT)f.write( done %s \n%time.strftime(%F %T))exceptExceptionase:withopen(LOG_FILE,a,encodingutf-8)asf:f.write( ERROR: %s \n%e)if__name____main__:apargparse.ArgumentParser()ap.add_argument(--port,typeint,default9000)ap.add_argument(--secret,requiredTrue)argsap.parse_args()Handler.secretargs.secret logging.basicConfig(levellogging.INFO,format%(asctime)s %(message)s)ThreadingHTTPServer((0.0.0.0,args.port),Handler).serve_forever()4. systemd 托管开机自启 崩溃自拉# /etc/systemd/system/webhook.service [Unit] DescriptionGitee WebHook receiver (python) Afternetwork.target docker.service [Service] Typesimple Useradmin # 与仓库属主一致避开 git 权限坑 ExecStart/usr/bin/python3 /opt/navigate/scripts/webhook_server.py --port 9000 --secret 你的密码 Restartalways RestartSec3 [Install] WantedBymulti-user.targetsudosystemctl daemon-reloadsudosystemctlenable--nowwebhooksudosystemctl status webhook5. 放行端口阿里云安全组ECS 控制台 → 实例 → 安全组 → 入方向TCP 9000源0.0.0.0/0系统防火墙若开启sudo firewall-cmd --permanent --add-port9000/tcp sudo firewall-cmd --reload6. Gitee 配置 WebHook仓库 → 管理 → WebHooks 管理 → 添加字段值URLhttp://服务器公网IP:9000/hooks/deploy密码与 systemd 里--secret完全一致事件勾选 Push7. 端到端验证# 本地gitpush gitee main# 服务器tail-n40/var/log/webhook-deploy.log# 看到 triggered → git sync → rebuild → done五、踩坑实录重点8 个坑#坑现象原因解决1下载静默失败curl -sL下载 GitHub 二进制mv报文件不存在国内网络拿到的是错误页-sL静默模式不报错弃用 GitHub 下载改 Python 标准库零依赖方案2checkout 权限拒绝git checkout报Permission deniedtar 部署遗留文件属主是 root当前用户无写目录权限sudo chown -R admin:admin /opt/navigate一劳永逸3Python 版本太老ImportError: cannot import name ThreadingHTTPServerCentOS 7 自带 Python 3.6该类 3.7 才有用ThreadingMixIn HTTPServer自定义兼容类4git 拒绝操作fatal: detected dubious ownershipsystemd 默认 root 跑仓库属主却是 adminsystemd 加Useradmin身份统一5公网超时本机127.0.0.1通公网 IPConnection timed out阿里云安全组默认 DROP静默丢包控制台放行 9000本地测公网 IP才验得了链路6永远无法快进Not possible to fast-forward远端 main 被 force push 重写本地分叉服务器改为git fetch reset --hard纯跟随角色7脚本突然不可执行触发后无任何输出reset --hard会把执行位重写为 644接收端用[bash, script]显式执行不依赖执行位8静默失败日志只有deploy triggered没下文子进程异常被线程吞掉加 try/except任何错误写进日志六、日常使用gitpush gitee main# 部署tail-n40/var/log/webhook-deploy.log# 查部署日志七、写在最后三条可迁移的经验身份一致 权限修补服务器上谁来跑systemd User、文件归谁chown、密钥归谁SSH key三件事对齐能省掉大半权限报错。错误要可见自动化链路每一环都要留日志systemd journald 部署日志文件没反应比报错难查十倍。网络排障有顺序本机 → 公网 IP → 云安全组 → 系统防火墙一层层测timed out几乎都是安全组 DROPrefused才是服务没起。如果对你有帮助欢迎点赞收藏。有问题评论区见。
返回列表