Skip to content

Jenkins 持续集成与持续部署

Jenkins 简介

Jenkins 是一个开源的自动化服务器,用于持续集成(CI)和持续部署(CD)。它可以帮助开发团队自动化构建、测试和部署软件,提高开发效率和代码质量。

安装 Jenkins

使用 Docker 安装(推荐)

bash
# 拉取 Jenkins 镜像
docker pull jenkins/jenkins:lts

# 运行 Jenkins 容器
docker run -d -p 8080:8080 -p 50000:50000 \
  -v jenkins_home:/var/jenkins_home \
  --name jenkins \
  jenkins/jenkins:lts

本地安装

Windows

  1. 下载 Jenkins Windows 安装包
  2. 运行安装程序
  3. 访问 http://localhost:8080

macOS

使用 Homebrew:

bash
brew install jenkins
brew services start jenkins

Linux (Ubuntu)

bash
# 添加 Jenkins 仓库
wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'

# 安装 Jenkins
sudo apt update
sudo apt install openjdk-11-jdk
sudo apt install jenkins

# 启动 Jenkins
sudo systemctl start jenkins
sudo systemctl enable jenkins

初始设置

  1. 打开浏览器访问 http://localhost:8080
  2. 获取初始密码:
    bash
    # Docker 安装
    docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
    
    # 本地安装
    sudo cat /var/lib/jenkins/secrets/initialAdminPassword
  3. 安装推荐的插件
  4. 创建管理员账户

Jenkins 基本概念

任务 (Job)

Jenkins 中的任务定义了构建、测试和部署的流程。主要任务类型包括:

  • Freestyle Project:自由风格项目,可以配置各种构建步骤
  • Pipeline:流水线项目,使用代码定义构建流程
  • Multi-configuration Project:多配置项目,可以在不同环境下构建
  • GitHub Organization:GitHub 组织项目,自动管理仓库

构建触发器

构建触发器决定何时自动运行构建:

  • Build periodically:定期构建
  • Poll SCM:定期检查源代码变化
  • GitHub hook trigger:GitHub 推送时触发
  • Build after other projects are built:在其他项目构建后构建

构建环境

构建环境定义构建运行的条件:

  • Delete workspace before build starts:构建前清理工作空间
  • Use secret text(s) or file(s):使用密钥
  • Add timestamps to the Console Output:添加时间戳

Jenkins Pipeline

Declarative Pipeline 基本结构

groovy
pipeline {
    agent any
    
    environment {
        NODE_ENV = 'production'
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Install Dependencies') {
            steps {
                sh 'npm install'
            }
        }
        
        stage('Build') {
            steps {
                sh 'npm run build'
            }
        }
        
        stage('Test') {
            steps {
                sh 'npm test'
            }
        }
        
        stage('Deploy') {
            steps {
                sh 'npm run deploy'
            }
        }
    }
    
    post {
        always {
            echo 'Pipeline finished'
        }
        success {
            echo 'Pipeline succeeded!'
        }
        failure {
            echo 'Pipeline failed!'
        }
    }
}

Scripted Pipeline 基本结构

groovy
node {
    try {
        stage('Checkout') {
            checkout scm
        }
        
        stage('Install Dependencies') {
            sh 'npm install'
        }
        
        stage('Build') {
            sh 'npm run build'
        }
        
        stage('Test') {
            sh 'npm test'
        }
        
        stage('Deploy') {
            sh 'npm run deploy'
        }
        
        echo 'Pipeline succeeded!'
    } catch (e) {
        echo 'Pipeline failed!'
        currentBuild.result = 'FAILED'
        throw e
    }
}

常用插件

基础插件

  • Git Plugin:Git 版本控制支持
  • GitHub Integration:GitHub 集成
  • Build Timeout:构建超时控制
  • Workspace Cleanup:工作空间清理

构建与部署插件

  • Node.js Plugin:Node.js 支持
  • Docker Plugin:Docker 集成
  • Deploy to container Plugin:容器部署
  • Publish Over SSH:SSH 部署

通知插件

  • Email Extension:邮件通知
  • Slack Notification:Slack 通知
  • ** DingTalk**:钉钉通知

实战案例

前端项目 CI/CD 流程

groovy
pipeline {
    agent any
    
    stages {
        stage('Checkout') {
            steps {
                checkout([
                    $class: 'GitSCM',
                    branches: [[name: '*/main']],
                    userRemoteConfigs: [[url: 'https://github.com/username/your-project.git']]
                ])
            }
        }
        
        stage('Setup Node.js') {
            steps {
                nodejs(nodeJSInstallationName: 'Node 16') {
                    sh 'node --version'
                    sh 'npm --version'
                }
            }
        }
        
        stage('Install Dependencies') {
            steps {
                sh 'npm ci'
            }
        }
        
        stage('Lint') {
            steps {
                sh 'npm run lint'
            }
        }
        
        stage('Test') {
            steps {
                sh 'npm test'
            }
            post {
                always {
                    junit 'test-results.xml'
                }
            }
        }
        
        stage('Build') {
            steps {
                sh 'npm run build'
            }
        }
        
        stage('Deploy') {
            when {
                branch 'main'
            }
            steps {
                sshPublisher(
                    publishers: [
                        sshPublisherDesc(
                            configName: 'production-server',
                            transfers: [
                                sshTransfer(
                                    sourceFiles: 'dist/**',
                                    removePrefix: 'dist',
                                    remoteDirectory: '/var/www/html',
                                    cleanRemote: false
                                )
                            ]
                        )
                    ]
                )
            }
        }
    }
    
    post {
        failure {
            mail to: 'team@example.com',
            subject: "Build Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
            body: "Check console output at ${env.BUILD_URL}"
        }
    }
}

后端项目 CI/CD 流程

groovy
pipeline {
    agent any
    
    environment {
        DOCKER_IMAGE = 'username/backend-app'
        DOCKER_TAG = "${env.BUILD_NUMBER}"
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Build Docker Image') {
            steps {
                script {
                    docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
                }
            }
        }
        
        stage('Run Tests') {
            steps {
                sh 'docker run --rm ${DOCKER_IMAGE}:${DOCKER_TAG} npm test'
            }
        }
        
        stage('Push to Registry') {
            when {
                branch 'main'
            }
            steps {
                script {
                    docker.withRegistry('https://registry.hub.docker.com', 'docker-hub-credentials') {
                        docker.image("${DOCKER_IMAGE}:${DOCKER_TAG}").push()
                        docker.image("${DOCKER_IMAGE}:${DOCKER_TAG}").push('latest')
                    }
                }
            }
        }
        
        stage('Deploy to Production') {
            when {
                branch 'main'
            }
            steps {
                sh """
                    ssh production-server 'docker pull ${DOCKER_IMAGE}:${DOCKER_TAG}'
                    ssh production-server 'docker stop backend-app || true'
                    ssh production-server 'docker rm backend-app || true'
                    ssh production-server 'docker run -d --name backend-app -p 3000:3000 ${DOCKER_IMAGE}:${DOCKER_TAG}'
                """
            }
        }
    }
}

最佳实践

  1. 使用 Pipeline as Code:将 Jenkinsfile 存储在代码仓库中,版本控制构建流程
  2. 环境变量管理:使用 Jenkins 凭据管理敏感信息
  3. 并行执行:使用 parallel 关键字并行执行不相关的构建步骤
  4. 构建缓存:合理利用构建缓存减少构建时间
  5. 资源清理:确保构建完成后清理临时资源
  6. 监控与通知:设置适当的监控和通知机制

参考资料