创建一个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文档:
Jenkins的任务配置
启用 构建触发器 > 轮询 SCM (Build Triggers > Poll SCM)
GitLab Web hook配置
新增一个Web hook,URL为
http://jenkins-server/git/notifyCommit?url=git@git-server:user/repository.git
这样,当有新的提交后,Jenkins即会自动触发构建任务。