当前位置: 首页 > news >正文

hyperf对接 项目接入 Jenkins 国内 CI/CD 实践

整体架构 开发者 push/PR ↓ esc to interrupt Gitee 私有仓库 ↓ Webhook Jenkins(自建) ↓ ┌───────────────────────────────┐ │ Stage1: 拉取代码 │ │ Stage2: Composer 安装依赖 │ │ Stage3: 静态分析 + 单元测试 │ │ Stage4: 构建 Docker 镜像 │ │ Stage5: 推送 ACR/Harbor │ │ Stage6: SSH 部署到目标服务器 │ │ Stage7: 通知钉钉/企业微信 │ └───────────────────────────────┘ --- 第一步:安装 Jenkins(Docker 方式,国内加速)1.1docker-compose.yml version:'3.8'services: jenkins: image: jenkins/jenkins:lts-jdk17 container_name: jenkins restart: unless-stopped privileged:trueuser: root ports: -"8080:8080"-"50000:50000"volumes: - /data/jenkins:/var/jenkins_home - /var/run/docker.sock:/var/run/docker.sock# Docker-outside-Docker- /usr/bin/docker:/usr/bin/docker environment: -TZ=Asia/Shanghai -JAVA_OPTS=-Duser.timezone=Asia/Shanghai-Xmx2gdocker-composeup-d# 获取初始密码dockerexecjenkinscat/var/jenkins_home/secrets/initialAdminPassword1.2切换国内插件源(解决插件下载慢) Jenkins 启动后,进入 Manage Jenkins → Plugin Manager → Advanced,将 Update Site URL 替换为清华镜像: https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json 或者直接修改配置文件:# 进入 Jenkins 数据目录sed-i's|https://updates.jenkins.io/update-center.json|https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-ce nter.json|g'\/data/jenkins/hudson.model.UpdateCenter.xml1.3必装插件清单 在 Manage Jenkins → Plugin Manager → Available 搜索安装: ┌─────────────────────┬───────────────────────────────────┐ │ 插件名 │ 用途 │ ├─────────────────────┼───────────────────────────────────┤ │ Gitee │ Gitee Webhook 触发 + 构建状态回写 │ ├─────────────────────┼───────────────────────────────────┤ │ Pipeline │ Jenkinsfile 声明式流水线 │ ├─────────────────────┼───────────────────────────────────┤ │ Git │ 拉取 Git 仓库 │ ├─────────────────────┼───────────────────────────────────┤ │ Publish Over SSH │ SSH 部署到远程服务器 │ ├─────────────────────┼───────────────────────────────────┤ │ Credentials Binding │ 安全注入密钥/密码 │ ├─────────────────────┼───────────────────────────────────┤ │ AnsiColor │ 彩色日志输出 │ ├─────────────────────┼───────────────────────────────────┤ │ DingTalk │ 钉钉通知 │ ├─────────────────────┼───────────────────────────────────┤ │ Docker Pipeline │ Pipeline 内构建推送镜像 │ ├─────────────────────┼───────────────────────────────────┤ │ Workspace Cleanup │ 构建前清理工作区 │ └─────────────────────┴───────────────────────────────────┘ --- 第二步:Jenkins 全局配置2.1配置 Gitee 连接 Manage Jenkins → Configure System → Gitee 配置: - Gitee 链接名称:gitee - Gitee 域名:https://gitee.com - 证书令牌:添加 → Jenkins → 类型选 Gitee API 令牌 → 填入 Gitee 私人令牌2.2配置 SSH 远程服务器(Publish Over SSH) Manage Jenkins → Configure System → Publish over SSH: Name: production-server Hostname:192.168.1.100 Username: deploy Remote Directory: /data/www 私钥粘贴到 Key 字段(Jenkins 服务器的 ~/.ssh/id_rsa 内容)。2.3配置 Credentials(凭据) Manage Jenkins → Credentials → System → Global → Add Credentials: ┌─────────────────┬───────────────────────────────┬─────────────────────┐ │ ID │ 类型 │ 用途 │ ├─────────────────┼───────────────────────────────┼─────────────────────┤ │ gitee-ssh-key │ SSH Username with private key │ 拉取 Gitee 私有仓库 │ ├─────────────────┼───────────────────────────────┼─────────────────────┤ │ composer-auth │ Secret text │ COMPOSER_AUTH JSON │ ├─────────────────┼───────────────────────────────┼─────────────────────┤ │ acr-credentials │ Username/Password │ 推送 Docker 镜像 │ ├─────────────────┼───────────────────────────────┼─────────────────────┤ │ dingtalk-token │ Secret text │ 钉钉机器人 Token │ └─────────────────┴───────────────────────────────┴─────────────────────┘ --- 第三步:Hyperf 项目配置3.1项目根目录添加 Jenkinsfile // Jenkinsfile pipeline{agent any // 环境变量 environment{APP_NAME='hyperf-app'DEPLOY_PATH='/data/www/hyperf-app'PHP_BIN='/usr/local/php/bin/php'COMPOSER_BIN='/usr/local/bin/composer'// Docker 镜像仓库(阿里云 ACR) IMAGE_REGISTRY='registry.cn-hangzhou.aliyuncs.com'IMAGE_NAMESPACE='your-namespace'IMAGE_TAG="${IMAGE_REGISTRY}/${IMAGE_NAMESPACE}/${APP_NAME}:${BUILD_NUMBER}"// 注入 Composer 私有仓库认证 COMPOSER_AUTH=credentials('composer-auth')}// 触发条件 triggers{// Gitee 插件触发(push 到 main/master 分支) GenericTrigger(genericVariables:[[key:'ref', value:'$.ref']], causeString:'Triggered by Gitee push to $ref', token:'your-webhook-token', regexpFilterText:'$ref', regexpFilterExpression:'refs/heads/(main|master|release/.*)')}options{// 保留最近10次构建记录 buildDiscarder(logRotator(numToKeepStr:'10'))// 超时30分钟 timeout(time:30, unit:'MINUTES')// 彩色日志 ansiColor('xterm')// 不允许并发构建 disableConcurrentBuilds()}stages{// ── Stage1: 清理工作区 ────────────────────────────── stage('Prepare'){steps{cleanWs()checkout scmecho"✅ 代码拉取完成,分支:${env.GIT_BRANCH},Commit:${env.GIT_COMMIT[0..7]}"}}// ── Stage2: 安装 Composer 依赖 ───────────────────── stage('Composer Install'){steps{sh'''${PHP_BIN}${COMPOSER_BIN}install\--no-dev\--optimize-autoloader\--no-interaction\--prefer-dist'''}}// ── Stage3: 代码质量检查 ──────────────────────────── stage('Code Quality'){parallel{stage('PHPStan'){steps{sh'''${PHP_BIN}vendor/bin/phpstan analyse\--level=5\--no-progress\app/'''}}stage('Unit Tests'){steps{sh'''${PHP_BIN}vendor/bin/phpunit\--configurationphpunit.xml\--log-junit reports/junit.xml\--coverage-clover reports/coverage.xml'''}post{always{// 发布测试报告 junit'reports/junit.xml'}}}}}// ── Stage4: 生成 Hyperf 注解缓存 ─────────────────── stage('Build Cache'){steps{sh'${PHP_BIN} bin/hyperf.php di:init-proxy'}}// ── Stage5: 构建 Docker 镜像 ──────────────────────── stage('Docker Build'){when{// 只有 main/master/release 分支才构建镜像 anyOf{branch'main'branch'master'branch pattern:'release/.*', comparator:'REGEXP'}}steps{withCredentials([usernamePassword(credentialsId:'acr-credentials', usernameVariable:'ACR_USER', passwordVariable:'ACR_PASS')]){sh'''dockerlogin${IMAGE_REGISTRY}-u${ACR_USER}-p${ACR_PASS}dockerbuild\--build-argAPP_ENV=production\--label"git-commit=${GIT_COMMIT}"\--label"build-number=${BUILD_NUMBER}"\-t${IMAGE_TAG}\-t${IMAGE_REGISTRY}/${IMAGE_NAMESPACE}/${APP_NAME}:latest\.dockerpush${IMAGE_TAG}dockerpush${IMAGE_REGISTRY}/${IMAGE_NAMESPACE}/${APP_NAME}:latestdockerrmi${IMAGE_TAG}||true'''}}}// ── Stage6: 部署到测试环境 ────────────────────────── stage('Deploy Staging'){when{branch'main'}steps{sshPublisher(publishers:[sshPublisherDesc(configName:'staging-server', transfers:[sshTransfer(// 不传文件,只执行远程脚本 execCommand:"""cd/data/www/hyperf-app-staginggitpull origin main /usr/local/php/bin/php /usr/local/bin/composerinstall--no-dev --optimize-autoloader /usr/local/php/bin/php bin/hyperf.php di:init-proxy# 平滑重启 Worker(不断开连接)PID=\$(catruntime/hyperf.pid2>/dev/null)if[-n"\$PID"];thenkill-USR1\$PIDelsesupervisorctl restart hyperf-stagingfi""", execTimeout:120000)], failOnError:true)])}}// ── Stage7: 部署到生产环境(需人工确认)──────────── stage('Deploy Production'){when{branch'master'}input{message"确认部署到生产环境?"ok"确认部署"submitter"admin,ops-team"parameters{choice(name:'DEPLOY_MODE', choices:['rolling','restart'], description:'部署方式')}}steps{sshPublisher(publishers:[sshPublisherDesc(configName:'production-server', transfers:[sshTransfer(execCommand:"""set-ecd/data/www/hyperf-appecho">>> 备份当前版本..."cp-r./data/backup/hyperf-app-\$(date+%Y%m%d%H%M%S)2>/dev/null||trueecho">>> 拉取最新代码..."gitpull origin masterecho">>> 安装依赖..."/usr/local/php/bin/php /usr/local/bin/composerinstall--no-dev --optimize-autoloaderecho">>> 重建注解缓存..."/usr/local/php/bin/php bin/hyperf.php di:init-proxyecho">>> 平滑重启..."PID=\$(catruntime/hyperf.pid2>/dev/null)if["${DEPLOY_MODE}"="rolling"]&&[-n"\$PID"];thenkill-USR1\$PIDecho">>> SIGUSR1 已发送,Worker 平滑重启中..."elsesupervisorctl restart hyperf-appfiecho">>> 部署完成 ✅"""", execTimeout:180000)], failOnError:true)])}}}// ── 构建后通知 ─────────────────────────────────────────── post{success{script{dingTalk(robot:'dingtalk-robot-id', type:'MARKDOWN', title:"✅ 构建成功 -${APP_NAME}", text:["## ✅ 构建成功","- **项目**:${APP_NAME}","- **分支**:${env.GIT_BRANCH}","- **Commit**:${env.GIT_COMMIT[0..7]}","- **构建号**: #${BUILD_NUMBER}","- **耗时**:${currentBuild.durationString}","> [查看构建详情](${BUILD_URL})"])}}failure{script{dingTalk(robot:'dingtalk-robot-id', type:'MARKDOWN', title:"❌ 构建失败 -${APP_NAME}", text:["## ❌ 构建失败","- **项目**:${APP_NAME}","- **分支**:${env.GIT_BRANCH}","- **失败阶段**:${env.STAGE_NAME}","> [查看失败日志](${BUILD_URL}console)"], at:['all'])}}always{// 清理 Docker 悬空镜像sh'docker image prune -f || true'}}}--- 第四步:Gitee 配置 Webhook 在 Gitee 仓库 → 管理 → WebHooks → 添加: URL: http://your-jenkins.com:8080/gitee-project/hyperf-app 密码: your-webhook-token(与 Jenkinsfile 中 token 一致) 触发事件: ✅ Push ✅ Pull Request Jenkins 任务配置中勾选: - 构建触发器 → Gitee webhook 触发构建 - 填写 Webhook 密码 --- 第五步:多分支流水线(推荐) 比单任务更强大,自动发现所有分支和 PR。 New Item → Multibranch Pipeline: // 分支源配置(Jenkins UI 中填写) // Branch Sources → Gitee // Credentials: gitee-ssh-key // Owner: your-org // Repository: hyperf-app // 扫描触发:Gitee Webhook 推送时自动扫描 // Orphaned Item Strategy: 保留3天 分支策略(在 Jenkinsfile 中用 when 控制): ┌───────────┬─────────────────┬──────────────────────┐ │ 分支 │ 触发动作 │ 部署目标 │ ├───────────┼─────────────────┼──────────────────────┤ │ feature/* │ 只跑测试 │ 无 │ ├───────────┼─────────────────┼──────────────────────┤ │ main │ 测试 + 构建镜像 │ 测试环境(自动) │ ├───────────┼─────────────────┼──────────────────────┤ │ master │ 测试 + 构建镜像 │ 生产环境(人工确认) │ ├───────────┼─────────────────┼──────────────────────┤ │ release/* │ 测试 + 构建镜像 │ 预发布环境 │ └───────────┴─────────────────┴──────────────────────┘ --- 第六步:Hyperf 项目适配 phpunit.xml(测试配置)<?xmlversion="1.0"encoding="UTF-8"?><phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"bootstrap="vendor/autoload.php"colors="true"><testsuites><testsuitename="Unit"><directory>test/Unit</directory></testsuite><testsuitename="Feature"><directory>test/Feature</directory></testsuite></testsuites><coverage><include><directory>app</directory></include></coverage><php><envname="APP_ENV"value="testing"/><envname="DB_DRIVER"value="sqlite"/><envname="DB_DATABASE"value=":memory:"/></php></phpunit>phpstan.neon(静态分析) parameters: level:5paths: - app excludePaths: - app/Exception/Handler ignoreErrors: -'#Call to an undefined method Hyperf\\Di\\Container#'.env.testing(CI 测试环境变量)APP_ENV=testingAPP_DEBUG=falseDB_DRIVER=sqliteDB_DATABASE=:memory:REDIS_HOST=127.0.0.1CACHE_DRIVER=array --- 第七步:Jenkins Agent 安装 PHP 环境 如果 Jenkins 运行在 Docker 容器内,需要在容器中安装 PHP + Swoole:# 自定义 Jenkins 镜像FROM jenkins/jenkins:lts-jdk17USERroot# 安装 PHP 8.1 + 必要扩展RUNapt-getupdate&&apt-getinstall-y\php8.1-cli php8.1-mbstring php8.1-xml\php8.1-curl php8.1-zip php8.1-redis\php8.1-pdo php8.1-mysqlgitunzip\&&rm-rf/var/lib/apt/lists/*# 安装 ComposerRUNcurl-sShttps://getcomposer.org/installer|php\&&mvcomposer.phar /usr/local/bin/composer\&&composerconfig-grepo.packagistcomposer\https://mirrors.aliyun.com/composer/# 安装 Swoole(PHPStan/测试不需要,但如果要跑集成测试需要)RUN peclinstallswoole\&&echo"extension=swoole.so">>/etc/php/8.1/cli/php.ini\&&echo"swoole.use_shortname=Off">>/etc/php/8.1/cli/php.iniUSERjenkins --- 关键注意事项 ┌──────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────┐ │ 问题 │ 解决方案 │ ├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────┤ │ Gitee Webhook │ Jenkins 必须有公网 IP 或内网穿透(frp/ngrok) │ │ 国内延迟 │ │ ├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────┤ │ Composer 下载慢 │ 全局配置阿里云镜像:composer config-grepo.packagistcomposer│ │ │ https://mirrors.aliyun.com/composer/ │ ├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────┤ │ 生产部署不中断 │ 用kill-USR1$PID触发 Hyperf Worker 平滑重启 │ ├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────┤ │ 凭据安全 │ 所有密码/Token 存 Jenkins Credentials,Jenkinsfile 中用 credentials()注入,绝不硬编码 │ ├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────┤ │ 并发构建冲突 │ disableConcurrentBuilds()防止同分支并发部署 │ ├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────┤ │ 构建产物清理 │dockerimage prune-f+ buildDiscarder 防止磁盘爆满 │ └──────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────┘
http://www.jsqmd.com/news/698383/

相关文章:

  • LISNR公司的 data-over-sound / ultrasonic proximity
  • 题解:洛谷 P8817 [CSP-S 2022] 假期计划
  • 手把手教你用西门子博途TIA Portal配置康耐视InSight相机Profinet通讯(含GSD文件安装与地址映射)
  • 2026年绿雕:解读文旅景观行业三大核心趋势 - 速递信息
  • 济南乐彩装饰工程:济南环氧地坪 固化地坪哪个公司好 - LYL仔仔
  • Postman便携版:打破Windows开发者的安装枷锁
  • 从OpenAMP到IPCC:拆解多核异构MCU的高效通信链路
  • 2026年贵阳安顺遵义高三初三复读与单科学习规划深度选购指南 - 年度推荐企业名录
  • 2026年4月更新:湖北不锈钢加工行业洗牌,如何甄选靠谱的制造合作伙伴? - 2026年企业推荐榜
  • C++:类中的静态成员函数
  • 媒体发稿全攻略:新闻发布平台怎么选?自媒体十大平台+靠谱媒体发布平台大盘点 - 代码非世界
  • 2026年卫生分切刀具哪个牌子好:行业选型标准与主流品牌适配分析 - 商业小白条
  • 高通410随身WiFi改Debian:从零构建短信转发服务器
  • 机器学习课程排行榜:数据驱动的学习路径推荐
  • 2026年贵阳高三初三复读与艺考文化课培训机构深度横评指南 - 年度推荐企业名录
  • Atmosphere系统架构深度解析:从原理到实践的技术探索
  • 实战排查:服务器日志里惊现‘rcu_sched stall on CPU’警告,我是这样一步步定位到内核模块bug的
  • BilibiliUploader:如何用Python自动化B站视频投稿,提升10倍工作效率
  • 划线机常见问题解答(2026最新专家版) - 速递信息
  • 如何在AMD显卡上轻松训练AI绘画模型:kohya_ss完整配置指南
  • 2025届毕业生推荐的五大降AI率助手推荐榜单
  • 2026年贵阳、遵义、安顺高三初三复读与艺考文化课培训深度指南 - 年度推荐企业名录
  • 从Hello World到指针:用5个实际代码片段,彻底搞懂C语言的核心概念与内存模型
  • 2026年3月国内工业废气处理厂家,工业废气处理效率倍增 - 品牌推荐师
  • 面试官灵魂拷问:RAG检索效果差?四层优化框架助你搞定高薪Offer!
  • 从‘木偶’到‘活人’:用Unity Avatar肌肉与自由度设置,解决角色动画穿模和僵硬问题
  • Mybatis第三章(补充):MyBatis二级缓存必懂---序列化与反序列化(从概念到实操)
  • 保姆级教程:用Python+C++复现SGM立体匹配的视差优化全流程(附代码避坑点)
  • 【STM32】STM32实战笔记:独立看门狗与窗口看门狗的配置与调试(47)
  • 软文发稿平台怎么选?选平台必看:高效、靠谱、性价比的判断方法 - 代码非世界