详细介绍在Ubuntu 22.04上使用GitLab和Jenkins部署CI/CD的完整过程。
环境准备
1. 系统初始化
bash
# 更新系统
sudo apt update && sudo apt upgrade -y
# 安装必要工具
sudo apt install -y curl wget git vim
2. 安装Docker(推荐方式)
bash
# 安装Docker
sudo apt install -y docker.io docker-compose
sudo systemctl enable docker
sudo systemctl start docker
# 将当前用户添加到docker组
sudo usermod -aG docker $USER
第一部分:GitLab安装与配置
1. 使用Docker安装GitLab
bash
# 创建GitLab数据目录
sudo mkdir -p /srv/gitlab/{config,data,logs}
# 创建docker-compose.yml
cat > docker-compose-gitlab.yml << 'EOF'
version: '3.6'
services:
gitlab:
image: gitlab/gitlab-ce:latest
container_name: gitlab
restart: always
hostname: 'gitlab.example.com' # 修改为您的域名或IP
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://192.168.1.100' # 修改为您的IP或域名
gitlab_rails['gitlab_shell_ssh_port'] = 2222
gitlab_rails['smtp_enable'] = false
gitlab_rails['time_zone'] = 'Asia/Shanghai'
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
networks:
- gitlab-network
shm_size: '256m'
networks:
gitlab-network:
driver: bridge
EOF
# 启动GitLab
docker-compose -f docker-compose-gitlab.yml up -d
2. 配置GitLab
bash
# 获取初始root密码
sudo docker exec -it gitlab cat /etc/gitlab/initial_root_password
# 登录GitLab (http://your-server-ip)
# 用户名: root
# 密码: 从上述命令获取
第二部分:Jenkins安装与配置
1. 使用Docker安装Jenkins
bash
# 创建Jenkins数据目录
sudo mkdir -p /srv/jenkins_home
# 设置目录权限
sudo chown -R 1000:1000 /srv/jenkins_home
# 创建docker-compose.yml
cat > docker-compose-jenkins.yml << 'EOF'
version: '3.7'
services:
jenkins:
image: jenkins/jenkins:lts-jdk17
container_name: jenkins
restart: always
user: "1000:1000" # 使用非root用户
ports:
- "8080:8080"
- "50000:50000"
volumes:
- /srv/jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
environment:
- JAVA_OPTS=-Djenkins.install.runSetupWizard=false
networks:
- jenkins-network
networks:
jenkins-network:
driver: bridge
EOF
# 启动Jenkins
docker-compose -f docker-compose-jenkins.yml up -d
# 查看初始管理员密码
sudo cat /srv/jenkins_home/secrets/initialAdminPassword
第三部分:案例实战 - Spring Boot应用CI/CD
1. 准备示例项目
bash
# 在GitLab创建新项目
# 项目名称: spring-boot-demo
# 可见性: Private
# 本地准备Spring Boot项目
mkdir spring-boot-demo && cd spring-boot-demo
# 创建项目结构
cat > pom.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
EOF
# 创建Java源码
mkdir -p src/main/java/com/example/demo
cat > src/main/java/com/example/demo/DemoApplication.java << 'EOF'
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class DemoApplication {
@GetMapping("/")
public String hello() {
return "Hello from Spring Boot CI/CD Pipeline!";
}
@GetMapping("/health")
public String health() {
return "Status: UP";
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
EOF
# 创建测试文件
mkdir -p src/test/java/com/example/demo
cat > src/test/java/com/example/demo/DemoApplicationTest.java << 'EOF'
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@SpringBootTest
@AutoConfigureMockMvc
class DemoApplicationTest {
@Autowired
private MockMvc mockMvc;
@Test
void testHelloEndpoint() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(status().isOk())
.andExpect(content().string("Hello from Spring Boot CI/CD Pipeline!"));
}
@Test
void testHealthEndpoint() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/health"))
.andExpect(status().isOk())
.andExpect(content().string("Status: UP"));
}
}
EOF
# 创建Dockerfile
cat > Dockerfile << 'EOF'
FROM openjdk:17-jdk-slim AS builder
WORKDIR /app
COPY . .
RUN ./mvnw clean package -DskipTests
FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
EOF
# 创建部署脚本
cat > deploy.sh << 'EOF'
#!/bin/bash
# 部署脚本
set -e
APP_NAME="spring-boot-demo"
CONTAINER_NAME="spring-boot-app"
VERSION=${BUILD_NUMBER:-latest}
echo "开始部署版本: $VERSION"
# 停止并删除旧容器
docker stop $CONTAINER_NAME || true
docker rm $CONTAINER_NAME || true
# 运行新容器
docker run -d \
--name $CONTAINER_NAME \
--restart always \
-p 8081:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
$APP_NAME:$VERSION
echo "部署完成!"
docker ps | grep $CONTAINER_NAME
EOF
chmod +x deploy.sh
# 创建.gitlab-ci.yml
cat > .gitlab-ci.yml << 'EOF'
stages:
- build
- test
- package
- deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
cache:
paths:
- .m2/repository/
- target/
build:
stage: build
image: maven:3.8.6-openjdk-17
script:
- mvn clean compile
artifacts:
paths:
- target/
test:
stage: test
image: maven:3.8.6-openjdk-17
script:
- mvn test
coverage: '/Total.*?([0-9]{1,3})%/'
package:
stage: package
image: maven:3.8.6-openjdk-17
script:
- mvn package -DskipTests
artifacts:
paths:
- target/*.jar
deploy:
stage: deploy
image: docker:latest
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_DRIVER: overlay2
script:
- docker build -t spring-boot-demo:$CI_COMMIT_SHORT_SHA .
- docker tag spring-boot-demo:$CI_COMMIT_SHORT_SHA spring-boot-demo:latest
- docker run -d -p 8081:8080 --name spring-boot-app spring-boot-demo:latest
only:
- main
environment:
name: production
url: http://your-server-ip:8081
EOF
2. Jenkins配置
安装必要插件
-
访问 Jenkins: http://your-server-ip:8080
-
安装插件:
-
GitLab Plugin
-
Pipeline
-
Maven Integration
-
Docker Pipeline
-
Blue Ocean
-
创建Pipeline任务
-
新建Item → 选择 Pipeline
-
配置:
-
Pipeline: Pipeline script from SCM
-
SCM: Git
-
Repository URL: http://gitlab-server/root/spring-boot-demo.git
-
Credentials: 添加GitLab凭据
-
Branch Specifier: */main
-
Script Path: Jenkinsfile
-
-
创建Jenkinsfile (在项目根目录)
groovy
Groovy
pipeline {
agent any
tools {
maven 'Maven-3.8'
jdk 'JDK-17'
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'http://gitlab-server/root/spring-boot-demo.git',
credentialsId: 'gitlab-credentials'
}
}
stage('Build') {
steps {
sh 'mvn clean compile'
}
}
stage('Unit Test') {
steps {
sh 'mvn test'
}
post {
always {
junit 'target/surefire-reports/*.xml'
}
}
}
stage('Code Analysis') {
steps {
sh 'mvn checkstyle:checkstyle pmd:pmd'
}
}
stage('Package') {
steps {
sh 'mvn package -DskipTests'
}
post {
success {
archiveArtifacts 'target/*.jar'
}
}
}
stage('Build Docker Image') {
steps {
script {
docker.build("spring-boot-demo:${env.BUILD_ID}")
}
}
}
stage('Deploy to Test') {
steps {
sh '''
docker stop spring-boot-test || true
docker rm spring-boot-test || true
docker run -d --name spring-boot-test -p 8082:8080 spring-boot-demo:${BUILD_ID}
'''
}
}
stage('Integration Test') {
steps {
sh '''
sleep 10 # 等待应用启动
curl -f http://localhost:8082/health || exit 1
'''
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
steps {
sh '''
docker tag spring-boot-demo:${BUILD_ID} spring-boot-demo:latest
docker stop spring-boot-prod || true
docker rm spring-boot-prod || true
docker run -d --name spring-boot-prod -p 8081:8080 --restart always spring-boot-demo:latest
'''
}
}
}
post {
success {
emailext (
subject: "构建成功: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "项目构建成功,请访问: http://your-server-ip:8081",
to: 'admin@example.com'
)
}
failure {
emailext (
subject: "构建失败: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "项目构建失败,请检查日志",
to: 'admin@example.com'
)
}
}
}
3. GitLab Webhook配置
-
在GitLab项目中:
-
Settings → Webhooks
-
Secret Token: (生成并复制)
-
-
在Jenkins中:
-
项目配置 → Build Triggers
-
勾选 "Build when a change is pushed to GitLab"
-
添加Secret Token (与GitLab中相同)
-
第四部分:CI/CD流程测试
1. 推送代码触发构建
bash
# 初始化Git仓库
git init
git add .
git commit -m "Initial commit"
git remote add origin http://gitlab-server/root/spring-boot-demo.git
git push -u origin main
2. 监控流程
bash
# 查看Jenkins构建日志
docker logs -f jenkins
# 查看应用运行状态
docker ps
# 测试应用
curl http://localhost:8081
curl http://localhost:8081/health
第五部分:高级配置
1. 配置Slack通知
bash
# Jenkins安装Slack Notification插件
# Jenkinsfile中添加
post {
success {
slackSend(
color: 'good',
message: "构建成功: ${env.JOB_NAME} - ${env.BUILD_NUMBER}"
)
}
failure {
slackSend(
color: 'danger',
message: "构建失败: ${env.JOB_NAME} - ${env.BUILD_NUMBER}"
)
}
}
2. 配置SonarQube代码质量检查
groovy
Groovy
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('sonarqube-server') {
sh 'mvn sonar:sonar'
}
}
}
3. 使用Kubernetes部署
yaml
bash
# 创建k8s部署文件 deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-boot-app
spec:
replicas: 3
selector:
matchLabels:
app: spring-boot-app
template:
metadata:
labels:
app: spring-boot-app
spec:
containers:
- name: spring-boot-app
image: spring-boot-demo:latest
ports:
- containerPort: 8080
故障排除
常见问题解决
bash
# 1. Jenkins无法连接GitLab
# 检查网络连通性
docker network ls
docker network connect gitlab-network jenkins
# 2. Maven构建缓慢
# 配置阿里云镜像
mkdir -p ~/.m2
cat > ~/.m2/settings.xml << 'EOF'
<settings>
<mirrors>
<mirror>
<id>aliyun</id>
<name>Aliyun Maven Mirror</name>
<url>https://maven.aliyun.com/repository/public</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>
EOF
# 3. Docker权限问题
sudo chmod 666 /var/run/docker.sock
# 4. 查看日志
docker logs gitlab
docker logs jenkins
journalctl -u docker --follow
这个完整的CI/CD案例包含了从环境搭建到自动化部署的全过程,可以根据实际需求进行调整和扩展。