1. 为什么需要Jenkins+Gitlab+Docker的CI/CD组合
在当代软件开发中,持续集成和持续交付(CI/CD)已经成为团队协作的标配。这套组合之所以被广泛采用,是因为它解决了开发流程中的几个关键痛点:
-
环境一致性难题:Docker容器化技术让"在我机器上能跑"这个经典借口成为历史。通过容器镜像,我们可以确保从开发到测试再到生产,所有环节的环境完全一致。
-
自动化构建与测试:Jenkins作为老牌自动化服务器,能够监听代码仓库变化,自动触发构建流程,运行单元测试和集成测试,大幅减少人工干预。
-
代码版本控制与协作:Gitlab不仅提供Git仓库管理,还内置了Issue跟踪、代码审查等协作功能,是团队开发的枢纽。
我曾在多个项目中实践这套技术栈,最深刻的体会是:当三者配合恰当时,代码从提交到部署的整个流程可以完全自动化,开发人员只需关注业务逻辑实现。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与组件安装
2.1 Docker安装与配置
Docker是整套流程的基础,建议在所有节点上统一安装。以下是CentOS 7下的安装步骤:
bash复制# 卸载旧版本
sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
# 安装必要工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# 设置稳定版仓库
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 安装Docker引擎
sudo yum install -y docker-ce docker-ce-cli containerd.io
# 启动Docker并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker
# 验证安装
sudo docker run hello-world
注意:如果遇到"virtualization support not detected"错误,需要进入BIOS启用VT-x/AMD-V虚拟化支持。对于Windows用户,还需要启用WSL2功能。
配置国内镜像加速(解决拉取镜像慢的问题):
bash复制sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
2.2 Gitlab安装与基础配置
对于中小团队,使用Docker运行Gitlab是最便捷的方式:
bash复制# 创建数据目录
sudo mkdir -p /srv/gitlab/{config,data,logs}
# 运行Gitlab容器
sudo docker run --detach \
--hostname gitlab.example.com \
--publish 443:443 --publish 80:80 --publish 22:22 \
--name gitlab \
--restart always \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ee:latest
首次访问需要设置root密码(访问http://服务器IP)。关键配置项位于/srv/gitlab/config/gitlab.rb:
ruby复制external_url 'http://gitlab.example.com'
gitlab_rails['gitlab_shell_ssh_port'] = 22
nginx['listen_port'] = 80
配置完成后执行:
bash复制sudo docker exec -it gitlab gitlab-ctl reconfigure
2.3 Jenkins安装与插件配置
同样使用Docker部署Jenkins:
bash复制# 创建数据卷
sudo mkdir -p /var/jenkins_home
sudo chown 1000:1000 /var/jenkins_home
# 运行Jenkins容器
sudo docker run -d \
-p 8080:8080 -p 50000:50000 \
-v /var/jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
--name jenkins \
jenkins/jenkins:lts
首次访问需要解锁Jenkins(查看初始密码):
bash复制sudo docker logs jenkins
安装必要插件:
- GitLab Plugin(Gitlab集成)
- Docker Pipeline(Docker支持)
- Blue Ocean(新版UI)
- Pipeline(流水线支持)
经验:插件安装时选择清华源可大幅加速下载。在Jenkins管理界面 -> 插件管理 -> 高级 -> 更新站点,替换URL为:https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
3. 集成配置与流水线设计
3.1 Gitlab与Jenkins的SSH密钥对接
实现自动化拉取代码的关键是配置SSH密钥认证:
bash复制# 在Jenkins容器内生成密钥
sudo docker exec -it jenkins bash
ssh-keygen -t rsa -b 4096 -C "jenkins@example.com"
cat ~/.ssh/id_rsa.pub
将公钥添加到Gitlab:
- 登录Gitlab -> 用户设置 -> SSH Keys
- 粘贴公钥内容
验证连接:
bash复制ssh -T git@gitlab.example.com
3.2 创建Jenkinsfile定义流水线
在项目根目录创建Jenkinsfile,这是CI/CD的核心定义文件。一个典型的Java项目流水线示例:
groovy复制pipeline {
agent any
environment {
DOCKER_REGISTRY = "registry.example.com"
PROJECT_NAME = "my-spring-app"
VERSION = "${env.BUILD_NUMBER}"
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
credentialsId: 'gitlab-ssh-key',
url: 'git@gitlab.example.com:mygroup/myproject.git'
}
}
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('Test') {
steps {
sh 'mvn test'
junit '**/target/surefire-reports/*.xml'
}
}
stage('Docker Build') {
steps {
script {
docker.build("${DOCKER_REGISTRY}/${PROJECT_NAME}:${VERSION}")
}
}
}
stage('Deploy') {
steps {
script {
docker.withRegistry('https://${DOCKER_REGISTRY}', 'docker-hub-cred') {
docker.image("${DOCKER_REGISTRY}/${PROJECT_NAME}:${VERSION}").push()
}
sshagent(['deploy-server-key']) {
sh """
ssh user@production-server "
docker pull ${DOCKER_REGISTRY}/${PROJECT_NAME}:${VERSION} && \
docker stop ${PROJECT_NAME} || true && \
docker rm ${PROJECT_NAME} || true && \
docker run -d --name ${PROJECT_NAME} \
-p 8080:8080 \
${DOCKER_REGISTRY}/${PROJECT_NAME}:${VERSION}
"
"""
}
}
}
}
}
}
3.3 Gitlab Webhook配置
实现代码推送自动触发Jenkins构建:
- 在Gitlab项目 -> Settings -> Webhooks
- 添加URL:
http://jenkins-server:8080/project/my-project - 触发事件选择"Push events"和"Merge request events"
- 添加Secret Token(需与Jenkins侧一致)
在Jenkins项目配置中:
- 勾选"Build when a change is pushed to GitLab"
- 设置相同的Secret Token
- 配置过滤规则(如只监听main分支)
4. 高级配置与优化技巧
4.1 使用Docker Compose管理服务
将各个组件统一用docker-compose.yml管理:
yaml复制version: '3'
services:
gitlab:
image: gitlab/gitlab-ee:latest
container_name: gitlab
restart: always
hostname: gitlab.example.com
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://gitlab.example.com'
gitlab_rails['gitlab_shell_ssh_port'] = 2222
ports:
- "80:80"
- "443:443"
- "2222:22"
volumes:
- /srv/gitlab/config:/etc/gitlab
- /srv/gitlab/logs:/var/log/gitlab
- /srv/gitlab/data:/var/opt/gitlab
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
restart: always
ports:
- "8080:8080"
- "50000:50000"
volumes:
- /var/jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
environment:
- JAVA_OPTS=-Djenkins.install.runSetupWizard=false
registry:
image: registry:2
container_name: registry
restart: always
ports:
- "5000:5000"
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
- /data/registry:/data
4.2 Jenkins Agent动态扩展
对于大型项目,可以使用Docker动态创建Jenkins Agent:
-
安装Docker插件并配置Cloud:
- 管理Jenkins -> 节点管理 -> 配置Cloud
- 添加Docker Cloud
- 设置Docker Host URI(如unix:///var/run/docker.sock)
-
定义Agent模板:
groovy复制label 'docker-agent' dockerTemplate( image 'jenkins/agent:latest', label 'docker-agent', volumes ['/var/run/docker.sock:/var/run/docker.sock'] ) -
在Jenkinsfile中指定agent:
groovy复制pipeline { agent { docker { image 'maven:3.8.4-jdk-11' args '-v $HOME/.m2:/root/.m2' } } // 其余阶段... }
4.3 安全加固措施
-
Gitlab安全:
- 定期更新补丁(特别是高危漏洞修复)
- 配置适当的项目可见性
- 启用双因素认证
- 定期备份(/srv/gitlab/data目录)
-
Jenkins安全:
- 使用Role-based Authorization Strategy插件管理权限
- 禁用匿名访问
- 定期清理工作空间和构建记录
-
Docker安全:
- 避免容器以root用户运行
- 使用只读文件系统(--read-only)
- 限制资源使用(-m, --cpus)
5. 常见问题排查
5.1 "Login failed. Check API token or Gitlab version"
这个错误通常发生在Jenkins与Gitlab集成时:
- 确认使用的Gitlab API token有足够权限
- 检查Gitlab版本是否与插件兼容
- 尝试改用SSH方式认证
5.2 Docker构建时权限不足
当Jenkins容器内执行Docker命令时可能遇到权限问题:
bash复制# 将jenkins用户加入docker组
sudo usermod -aG docker jenkins
# 在容器启动时传递用户ID
docker run -u root ... # 临时解决方案
更好的做法是绑定Docker socket时设置正确权限:
bash复制docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):/usr/bin/docker ...
5.3 Pipeline执行卡住无响应
可能原因和解决方案:
- Agent离线:检查节点状态,确保有可用Agent
- 资源不足:增加Docker内存分配(建议至少4GB)
- 网络问题:检查容器间网络连通性
- 死锁:在Jenkins管理界面终止卡住的构建
5.4 Gitlab Runner与Jenkins的抉择
虽然Gitlab也提供CI功能(Gitlab CI/CD),但Jenkins的优势在于:
- 更丰富的插件生态系统
- 支持更复杂的流水线编排
- 可以集成非Gitlab代码仓库
- 更灵活的执行环境配置
建议场景:
- 如果团队已经深度使用Gitlab且需求简单 → Gitlab CI
- 需要多仓库、复杂流程或已有Jenkins经验 → Jenkins
6. 实战案例:Spring Boot项目完整流程
以一个实际的Spring Boot项目为例,演示完整CI/CD流程:
-
项目结构准备:
code复制my-spring-app/ ├── src/ ├── Dockerfile ├── Jenkinsfile ├── pom.xml -
Dockerfile内容:
dockerfile复制FROM openjdk:11-jre-slim WORKDIR /app COPY target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","app.jar"] -
Jenkins流水线优化:
groovy复制pipeline { agent any options { timeout(time: 30, unit: 'MINUTES') buildDiscarder(logRotator(numToKeepStr: '10')) } stages { stage('Checkout') { steps { checkout scm } } stage('Build & Test') { steps { sh 'mvn clean verify' junit '**/target/surefire-reports/*.xml' archiveArtifacts 'target/*.jar' } } stage('SonarQube Analysis') { when { branch 'main' } steps { withSonarQubeEnv('sonar-server') { sh 'mvn sonar:sonar' } } } stage('Docker Build') { when { branch 'main' } steps { script { def image = docker.build("my-registry/my-spring-app:${env.BUILD_NUMBER}") docker.withRegistry('https://my-registry', 'docker-creds') { image.push() } } } } stage('Deploy to Staging') { when { branch 'main' } steps { sshagent(['staging-server']) { sh ''' ssh ubuntu@staging " docker pull my-registry/my-spring-app:${BUILD_NUMBER} && \ docker tag my-registry/my-spring-app:${BUILD_NUMBER} my-spring-app:latest && \ docker-compose -f /opt/app/docker-compose.yml up -d " ''' } } } } post { always { cleanWs() } failure { emailext body: '构建失败: ${BUILD_URL}', subject: '构建失败: ${JOB_NAME} - ${BUILD_NUMBER}', to: 'dev-team@example.com' } } } -
部署后验证:
- 自动化健康检查:
groovy复制stage('Health Check') { steps { retry(3) { sh ''' curl --fail http://staging-server:8080/actuator/health || exit 1 ''' } } } - 金丝雀发布策略:
groovy复制stage('Canary Release') { steps { script { // 先部署5%流量 sh 'kubectl set image deployment/my-app *=my-registry/my-spring-app:${env.BUILD_NUMBER}' sh 'kubectl scale deployment/my-app --replicas=1' // 监控5分钟 sleep time: 5, unit: 'MINUTES' // 无异常则全量发布 sh 'kubectl scale deployment/my-app --replicas=5' } } }
- 自动化健康检查:
这套组合在实践中表现出的最大优势是灵活性。我曾在一个微服务项目中管理20+个服务的CI/CD流程,通过合理的Jenkins共享库设计和Docker Compose编排,将部署时间从原来的2小时缩短到15分钟。关键点在于:
- 标准化所有服务的构建流程
- 利用Docker网络实现服务间隔离与通信
- 通过Gitlab的分支策略控制不同环境的部署流程
