从零构建Jenkins+GitLab自动化流水线:实战避坑指南
每次代码提交后手动打包部署的时代该结束了。如果你还在为测试环境频繁出问题而加班,或是每次发布都要重复执行十几条命令,这篇文章就是为你准备的。我们将用最简技术栈(Jenkins+GitLab+Shell)搭建一条完整的自动化流水线,过程中遇到的每个坑点都会标注解决方案。
1. 环境准备:别在第一步就踩坑
在开始之前,我们需要准备三台基础服务器(实际开发中可以用一台机器完成所有角色):
- 代码仓库服务器:GitLab Community Edition(建议4核8G配置)
- 构建服务器:Jenkins LTS版本(建议4核8G配置)
- 应用服务器:运行Java应用的测试环境(建议2核4G配置)
1.1 基础设施检查清单
安装前的这些检查能节省你80%的后续问题:
# 所有服务器都需要执行 sudo apt update && sudo apt upgrade -y # Ubuntu/Debian sudo yum update -y # CentOS/RHEL # 检查关键依赖 java -version # 需要JDK8或11 git --version docker --version # 可选但推荐最容易忽略的三个权限问题:
- Jenkins用户需要被加入docker用户组(如果使用容器)
- GitLab Runner用户需要有项目目录的写权限
- 应用服务器的SSH密钥需要提前配置好
提示:生产环境建议使用独立的服务账户而非root权限操作
2. 工具链配置:避开那些"理所当然"的默认值
2.1 Jenkins的"正确打开方式"
官方提供的安装方式往往不适合国内环境:
# 替换Jenkins官方源为清华镜像 sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \ https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat-stable/jenkins.io.key echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \ https://mirrors.tuna.tsinghua.edu.cn/jenkins/debian-stable binary/" | \ sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null安装后必须立即调整的配置项:
| 配置项 | 推荐值 | 原因 |
|---|---|---|
| 执行器数量 | CPU核心数×2 | 避免构建任务排队 |
| JVM内存 | -Xmx4g | 默认512m会导致OOM |
| 插件更新站点 | 国内镜像源 | 加速插件安装 |
2.2 GitLab Runner的特殊配置
大多数教程不会告诉你的注册细节:
# 使用shell executor而不是docker sudo gitlab-runner register \ --non-interactive \ --url "https://your.gitlab.domain" \ --registration-token "PROJECT_REGISTRATION_TOKEN" \ --executor "shell" \ --description "dev-runner" \ --tag-list "dev,shell" \ --run-untagged="true"配置完成后需要手动修改/etc/gitlab-runner/config.toml:
concurrent = 4 check_interval = 0 [[runners]] executor = "shell" shell = "bash" environment = ["M2_HOME=/opt/maven", "JAVA_HOME=/opt/java"]3. 流水线设计:从提交到部署的完整链路
3.1 一个Spring Boot项目的.gitlab-ci.yml范例
stages: - build - test - deploy variables: MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" cache: paths: - .m2/repository/ - target/ build-job: stage: build script: - mvn clean package -DskipTests artifacts: paths: - target/*.jar only: - main unit-test: stage: test script: - mvn test except: - tags deploy-dev: stage: deploy script: - scp target/*.jar user@dev-server:/opt/app/ - ssh user@dev-server "systemctl restart myapp" only: - main3.2 Jenkinsfile的进阶写法
pipeline { agent any environment { DEPLOY_ENV = 'dev' ARTIFACT_NAME = "app-${env.BUILD_NUMBER}.jar" } stages { stage('Checkout') { steps { git branch: 'main', url: 'git@gitlab.example.com:mygroup/myproject.git' } } stage('Build') { steps { sh 'mvn clean package' archiveArtifacts artifacts: 'target/*.jar', fingerprint: true } } stage('Deploy') { when { expression { env.DEPLOY_ENV == 'dev' } } steps { sshPublisher( publishers: [ sshPublisherDesc( configName: 'dev-server', transfers: [ sshTransfer( sourceFiles: 'target/*.jar', removePrefix: 'target', remoteDirectory: '/opt/app', execCommand: 'systemctl restart myapp' ) ] ) ] ) } } } post { failure { emailext body: '构建失败: ${BUILD_URL}', subject: '构建失败: ${JOB_NAME}', to: 'dev-team@example.com' } } }4. 避坑大全:那些让你熬夜的问题解决方案
4.1 网络问题排错表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| Git克隆超时 | 防火墙阻断22端口 | 改用HTTPS协议或配置SSH代理 |
| Maven下载失败 | 中央仓库连接慢 | 配置阿里云镜像仓库 |
| SSH连接被拒绝 | 密钥未正确配置 | 使用ssh -v查看详细日志 |
4.2 典型错误日志分析
案例1:Permission denied (publickey)
ERROR: Error cloning remote repo 'origin' Failed to connect to repository : Command "git fetch -f --progress --prune -- origin +refs/heads/*:refs/remotes/origin/*" returned status code 128: stdout: stderr: Permission denied (publickey). fatal: Could not read from remote repository.解决方法:
- 确认Jenkins用户有权限读取SSH密钥
- 在Jenkins中添加GitLab的SSH密钥凭证
- 测试连接:
sudo -u jenkins ssh -T git@gitlab.example.com
案例2:No space left on device
java.io.IOException: No space left on device at java.io.FileOutputStream.writeBytes(Native Method) at java.io.FileOutputStream.write(FileOutputStream.java:326)解决方法:
- 增加Jenkins工作目录磁盘空间
- 配置定期清理旧构建:
pipeline { options { buildDiscarder(logRotator(numToKeepStr: '10')) } }5. 进阶技巧:让流水线更智能
5.1 条件触发部署
stage('Deploy to Prod') { when { allOf { branch 'main' expression { return params.DEPLOY_PROD == true } } } steps { timeout(time: 15, unit: 'MINUTES') { input message: '确认部署到生产环境?', ok: '确认' } sh './deploy-prod.sh' } }5.2 多环境配置管理
使用config插件管理不同环境的配置:
# config/dev.properties server.port=8080 logging.level.root=DEBUG # config/prod.properties server.port=80 logging.level.root=INFO在Jenkinsfile中动态选择配置:
environment { CONFIG_FILE = "config/${env.DEPLOY_ENV}.properties" } steps { sh "cp ${CONFIG_FILE} src/main/resources/application.properties" }6. 监控与优化:看不见的部分更重要
安装Pipeline: GitHub插件后,可以在Jenkinsfile中添加质量门禁:
stage('Quality Gate') { steps { withSonarQubeEnv('sonar-server') { sh 'mvn sonar:sonar' } timeout(time: 5, unit: 'MINUTES') { waitForQualityGate abortPipeline: true } } }构建时长优化策略:
- 并行执行测试:
stage('Test') { parallel { stage('Unit Test') { steps { sh 'mvn test' } } stage('Integration Test') { steps { sh 'mvn verify -DskipUnitTests' } } } }- 使用构建缓存:
# Dockerfile示例 FROM maven:3.8.6-jdk-11 as build COPY pom.xml . RUN mvn dependency:go-offline COPY src/ ./src/ RUN mvn package