创建一个CI/CD项目

作者:James Zhu ([email protected])

创建日期:2018-10-26

操作步骤

在新建任务时,任务类型选择 流水线(Pipeline),其它参数根据需要设置。

在流水线定义处,根据实际需求编写 Pipeline script,本文示例如下:

pipeline {
    agent any
    // 环境变量定义
    environment {
        // 生成image的名称
        IMAGE_NAME = 'my-image'
        // 生成器Git地址
        BUILDER_GIT_URL = 'git@git-server:user/repository.git'
        // Git凭据ID
        GIT_CREDENTIALS_ID = 'git-credential-id'
    }
    stages {
        stage('Checkout') {
            steps {
                script {
                    // 切换到临时目录
                    dir(pwd([tmp: true])) {
                        // 获取生成器
                        git branch: 'master', changelog: false, credentialsId: env.GIT_CREDENTIALS_ID, url: env.BUILDER_GIT_URL
                        // 执行脚本初始化配置
                        configVars = load('pipeline.groovy').getConfigVars(env.IMAGE_NAME)
                    }
                    // 根据配置获取代码
                    scmVars = git branch: configVars.GIT_BRANCH, changelog: false, credentialsId: env.GIT_CREDENTIALS_ID, url: configVars.GIT_URL
                    // 将docker build需要的文件复制到WORKSPACE
                    dir(pwd([tmp: true])) {
                        sh "mv build ${env.WORKSPACE}/"
                    }
                    sh 'mv build/Dockerfile .'
                }
            }
        }
        stage('Build') {
            environment {
                // 定义build参数
                BUILD_ARGS = """
                    IMAGE_NAME=${env.IMAGE_NAME}
                    HTTP_HOST=${configVars.HTTP_HOST}
                    SUB_DIR=${configVars.SUB_DIR}
                    VCS_URL=${scmVars.GIT_URL}
                    VCS_REF=${scmVars.GIT_COMMIT}
                    BUILD_DATE=${env.BUILD_TIMESTAMP}
                    BUILD_VERSION=${env.BUILD_ID}
                    CONFIG_TEMPLATE=${configVars.CONFIG_TEMPLATE}
                """
            }
            steps {
                script {
                    // 定义docker registry
                    docker.withRegistry(configVars.DOCKER_REGISTRY) {
                        // docker build
                        image = docker.build(env.IMAGE_NAME, """${sh(
                            returnStdout: true,
                            script: 'echo "$BUILD_ARGS" | xargs -r -n1 echo -n " --build-arg"'
                        )} .""")
                        // 如果不是本地环境,执行docker push
                        if (env.DOCKER_HOST != 'tcp://192.168.99.100:2376') {
                            image.push()
                            image.push(env.BUILD_ID)
                        }
                    }
                    sh 'docker system prune -f'
                }
            }
        }
        stage('Test') {
            steps {
                echo 'Testing (TODO)'
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying (TODO)'
            }
        }
    }
}

这里假设生成器Git是一个由运维维护的Git项目,里面主要包含docker build所需要的文件,或者一些环境配置文件(如数据库配置)。目录大致结构如下:

- build/
  - Dockerfile
  - docker-healthcheck
  - other-files-needed-in-build
- pipeline.groovy
- other-files

pipeline.groovy是一个根据IMAGE_NAME动态加载项目参数的脚本,代码示例如下:

def getConfigVars(String imageName) {
    return [
        GIT_URL: 'git@git-server:user/repository.git',
        GIT_BRANCH: 'master',
        DOCKER_REGISTRY: 'localhost:5000'
    ]
}
return this

当然也可以选择 Pipeline script from SCM,这会集成Git拉取代码相关功能,只是要求把Jenkinsfile放在Git版本库中。这样的好处是环境变量中会自带Git的参数,但是要求开发者都去放一个Jenkinsfile,开发和运维的职责就有点混淆了。

点击 立即构建 即会开始构建任务。

一个Bug

上述脚本在本地Docker Toolbox上可以正常build成功,但是到了服务器上(CentOS 7.2)却失败了,报错如下:

rm -rf /tmp/pear
can't remove '/tmp/pear': Directory not empty

为什么用了rm -rf仍然会报 Directory not empty 的错误呢?这个目录应该也没被其它程序占用。搜索了半天后,发现了一篇文章。原来是 CentOS 7 不支持 overlay2 这个存储驱动,改成 devicemapper 就好了。

官网说明,去修改/etc/docker/daemon.json,结果直接报错。

unable to configure the Docker daemon with file /etc/docker/daemon.json: the following directives are specified both as a flag and in the configuration file: storage-driver: (from flag: overlay2, from file: devicemapper)

原来,在/etc/sysconfig/docker-storage也定义了存储驱动,两者冲突了,于是就直接将该文件改成以下内容,重启docker后就OK了。

DOCKER_STORAGE_OPTIONS="--storage-driver devicemapper "

GitLab push自动触发构建

要实现GitLab push自动触发构建,只需要在2处进行设置,详见Git Plugin文档

  1. Jenkins的任务配置

    启用 构建触发器 > 轮询 SCM (Build Triggers > Poll SCM)

  2. GitLab Web hook配置

    新增一个Web hook,URL为http://jenkins-server/git/notifyCommit?url=git@git-server:user/repository.git

这样,当有新的提交后,Jenkins即会自动触发构建任务。

results matching ""

    No results matching ""