在Ubuntu 22.04上使用GitLab和Jenkins部署CI/CD的完整过程

详细介绍在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配置

安装必要插件
  1. 访问 Jenkins: http://your-server-ip:8080

  2. 安装插件:

    • GitLab Plugin

    • Pipeline

    • Maven Integration

    • Docker Pipeline

    • Blue Ocean

创建Pipeline任务
  1. 新建Item → 选择 Pipeline

  2. 配置:

  3. 创建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配置

  1. 在GitLab项目中

  2. 在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案例包含了从环境搭建到自动化部署的全过程,可以根据实际需求进行调整和扩展。

相关推荐
liux35282 小时前
DevOps 实践指南:GitLab与Jenkins部署
gitlab·jenkins·devops
ζั͡山 ั͡有扶苏 ั͡✾2 小时前
K8s 集群内存压力检测和智能 Pod 驱逐工具
云原生·容器·kubernetes
腾讯数据架构师2 小时前
k8s兼容昆仑芯p800
人工智能·云原生·容器·kubernetes·cube-studio·昆仑芯
CheungChunChiu2 小时前
# Xorg 配置与 modesetting 驱动详解:从设备节点到显示旋转
android·linux·ubuntu·显示·xserver
风一样的男子&2 小时前
kylin桌面版v10安装docker和k8s
docker·kubernetes·kylin
MIXLLRED2 小时前
树莓派4B(ARM架构)的Ubuntu 22.04(Jammy)上安装Intel RealSense SDK和ROS2驱动
arm开发·ubuntu·树莓派·深度相机
努力搬砖的咸鱼2 小时前
为什么需要 Kubernetes
微服务·云原生·容器·kubernetes
阿钱真强道2 小时前
04 ubuntu20下 OpenHarmony-3.0-LTS qemu mps2-an386 运行 liteos_m
linux·嵌入式硬件·ubuntu·harmonyos
程序之大道至简3 小时前
Jenkins不显示阶段视图解决方案
运维·jenkins·阶段视图·stage view