前面将所有的步骤及细节,全部放在了一个代码文件中,Jenkins支持使用load
从其他文件导入函数来执行,这样,可以将部分公共内容(如编译打包或者发布)提取到一个文件中,然后运行流水线时只需要导入一下这个函数即可.
基础
固定格式
def function_name () {
println "hello world!"
}
return this;
def
: 定义一个函数
function_name
: 函数名称
()
:如果需要传参则在此处描写
{}
:代码段
return this;
:返回一个对象
使用函数
新增文件build-docker.groovy
def buildDocker(dockerImage,DOCKER_PASSWORD,tag){
sh "docker build -t ${dockerImage}:task_${tag} ."
sh "docker login -u 528909316 -p \'${DOCKER_PASSWORD}\'"
sh "docker push ${dockerImage}:task_${tag}"
sh ''' sed -i "s#DOCKERIMAGE#${dockerImage}#g" deploy.yml'''
}
return this;
新增文件deploy.groovy
def deploy(ref){
println "deploy function parameter: ${ref}"
if ("${ref}" == "refs/heads/main") {
println "update formal platform"
kubernetesDeploy configs: 'deploy.yml', kubeconfigId: "ae92d8dc-053e-409e-ae1b-f6e3f3bbb9f4"
} else if ("${ref}" == "refs/heads/test") {
println "update test platform"
kubernetesDeploy configs: 'deploy-test.yml', kubeconfigId: 'ae92d8dc-053e-409e-ae1b-f6e3f3bbb9f4'
} else {
println "update unknown platform"
}
}
return this;
新增的两个文件在gitlab新建仓库存放,位于main分支,下面主文件会用到仓库地址来拉取函数文件
修改后的jenkins.groocy文件
// 直接在文件中定义的 作用域为全局
def GITLAB_SSH_URL = "ssh://git@20.88.9.34:222/my_group/one_project.git"
def BUILD_IMAGE = "528909316/jenkins"
// 预先声明一个变量 名称叫做tag 此处声明后作用域为整个文件内
def tag = "${ref}".split("/")[-1]
podTemplate(
cloud: 'kubernetes',
containers: [
containerTemplate(name: 'code', image: "${BUILD_IMAGE}:jenkinscode_v1",, command: "sleep 99d", ttyEnabled: true, alwaysPullImage: true),
containerTemplate(name: 'build', image: "${BUILD_IMAGE}:pyinstaller_v1", command: "sleep 99d", ttyEnabled: true, alwaysPullImage: true),
containerTemplate(name: 'docker', image: "${BUILD_IMAGE}:rundocker_v1", command: "sleep 99d", ttyEnabled: true, alwaysPullImage: true)],
volumes: [hostPathVolume(hostPath: '/var/run/docker.sock', mountPath: '/var/run/docker.sock')]
) {
// 当前函数下的每个stage中都可调用此函数
def GITLAB_ID = "d17b1091-fa2d-4310-8a4d-0b1d7f823ea9"
node(POD_LABEL) {
// 首先拉取相关的函数文件 此处单独为其创建了一个仓库
// url为仓库地址 main为分支
git branch: "main", credentialsId: "${GITLAB_ID}", url: "ssh://git@20.88.9.34:222/my_group/jenkins-function.git"
// 仓库中有两个文件 deploy.groovy和build-docker.groovy文件
// def 新对象 = load "文件名或文件地址" //实例化一个对象
def deploy = load "deploy.groovy"
def buildDocker = load "build-docker.groovy"
stage('Before build execution'){
// 更新gitlab状态
updateGitlabCommitStatus name: 'jenkins', state: 'running'
// 判断分支 如果是pipeline则使用main代码
if ("${ref}" == "refs/heads/pipeline") {
tag = "main"
}
}
stage('pull code') {
// 克隆代码 根据变量 tag作为拉取分支
git branch: "${tag}", credentialsId: "${GITLAB_ID}", url: "${GITLAB_SSH_URL}"
echo "The first stage end"
}
stage('build code') {
container('build') {
stage('Build a python project') {
// 编译为文件
sh '''
python -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flask
pyinstaller -F cpu.py
'''
}
}
echo "The second stage end."
}
stage("build image") {
// 某个stage中定义的 作用域当前函数中,只能在当前stage中或者其子stage中调用
def dockerImage = '528909316/jenkins'
container("docker") {
stage("build image") {
// 某个stage中定义的 作用域当前函数中,只能在当前stage中或者其子stage中调用
def DOCKER_PASSWORD = "*********"
// 调用新生成的对象的.buildDocker函数,同时传递三个参数
buildDocker.buildDocker("${dockerImage}","${DOCKER_PASSWORD}","${tag}")
}
}
echo "push ${dockerImage} end."
}
stage("deploy image") {
// 调用实例化的deploy对象的deploy函数,传递一个参数
deploy.deploy("${ref}")
}
// 返回给gitlab一个信号 表示流水线成功
updateGitlabCommitStatus name: 'one_project', state: 'success'
}
}
需要注意的是,load的时候默认是在jnlp容器执行的.即便指定了container也是无效的,jnlp是Jenkins默认创建的工作节点容器,没有特殊指定的话任务都会在这个容器中运行(唯独load例外).
也可以将这些代码文件放进一个新的pod里,但是那样需要移动一下位置,同一个pod内所有容器的/home/jenkins/agent
目录都是同一个(类似于nfs的挂载目录),放在这个目录下,jnlp容器中即可正常访问到这个文件.
将所有步骤提取到函数中
完成了一个步骤后,接下来将所有内容全部提取为一个个的函数文件,这样如果是Python代码我可以直接调用Python build的文件,如果是Java则调用Java build文件,实现更好的通用性.
规划:
创建一个仓库叫做
jenkins-function
,单独存放提取出的代码文件
创建一个仓库叫做one_project
,用于模拟代码仓库,存放代码片段.
jenkins-function仓库目录层级
.
└── jenkins-function
├── before_build.groovy
├── build-docker.groovy
├── deploy.groovy
├── pull_code.groovy
├── python_build.groovy
└── README.md
before_build.groovy
def before_build(branch)
{
// 判断分支 如果是pipeline则使用main代码
println "brfore build function."
}
return this;
build-docker.groovy
def buildDocker(dockerImage,tag){
// def DOCKER_PASSWORD = "MyNewPass4!"
def DOCKER_PASSWORD = "MyNewPass4!"
sh "docker build -t ${dockerImage}:task_${tag} ."
sh "docker login -u 528909316 -p \'${DOCKER_PASSWORD}\'"
sh "docker push ${dockerImage}:task_${tag}"
// sh ''' sed -i "s#DOCKERIMAGE#${dockerImage}#g" pipeline/deploy.yml'''
}
return this;
deploy.groovy
def deploy(ref,filePath,id,dockerImage){
println "deploy function parameter: ${ref}"
// 替换全局id版本
sh("sed -i 's@DEPLOY_VERSION@${id}@g' ${filePath}")
sh("sed -i 's@DOCKERIMAGE@${dockerImage}@g' ${filePath}")
if ("${ref}" == "refs/heads/main") {
println "update formal platform"
sh ("sed -i 's#NAMESPACE#prod#g' ${filePath}")
kubernetesDeploy configs: "${filePath}", kubeconfigId: "ae92d8dc-053e-409e-ae1b-f6e3f3bbb9f4"
} else if ("${ref}" == "refs/heads/test") {
println "update test platform"
sh ("sed -i 's#NAMESPACE#test#g' ${filePath}")
kubernetesDeploy configs: "${filePath}", kubeconfigId: 'ae92d8dc-053e-409e-ae1b-f6e3f3bbb9f4'
} else {
println "update unknown platform"
}
sh("cat ${filePath}")
}
return this;
pull_code.groovy
def pull_code(tag,GITLAB_ID,GITLAB_SSH_URL)
{
git branch: "${tag}", credentialsId: "${GITLAB_ID}", url: "${GITLAB_SSH_URL}"
}
python_build.groovy
def build(fileName){
sh '''
python -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flask
'''
sh "pyinstaller -F ${fileName}"
}
return this;
one_project仓库内容
单独创建分支pipeline
,存放代码构建文件
jenkins.groovy文件内容:
// 直接在文件中定义的 作用域为全局
def GITLAB_SSH_URL = "ssh://git@20.88.9.34:222/my_group/one_project.git"
def BUILD_IMAGE = "528909316/jenkins"
// 预先声明一个变量 名称叫做tag 此处声明后作用域为整个文件内
// static Integer tag
def tag = "${branch}".split("/")[-1]
// def tag = "${branch}".split("/")[-1]
podTemplate(
cloud: 'kubernetes',
containers: [
containerTemplate(name: 'code', image: "${BUILD_IMAGE}:jenkinscode_v1",, command: "sleep 99d", ttyEnabled: true, alwaysPullImage: true),
containerTemplate(name: 'build', image: "${BUILD_IMAGE}:pyinstaller_v1", command: "sleep 99d", ttyEnabled: true, alwaysPullImage: true),
containerTemplate(name: 'docker', image: "${BUILD_IMAGE}:rundocker_v1", command: "sleep 99d", ttyEnabled: true, alwaysPullImage: true)],
volumes: [hostPathVolume(hostPath: '/var/run/docker.sock', mountPath: '/var/run/docker.sock')]
) {
// 当前函数下的每个stage中都可调用此函数
def GITLAB_ID = "d17b1091-fa2d-4310-8a4d-0b1d7f823ea9"
def filePath = "pipeline/deploy.yml"
node(POD_LABEL) {
git branch: "main", credentialsId: "${GITLAB_ID}", url: "ssh://git@20.88.9.34:222/my_group/jenkins-function.git"
def deploy = load "deploy.groovy"
def buildDocker = load "build-docker.groovy"
def build = load "python_build.groovy"
def pull_code = load "pull_code.groovy"
def before_build = load "before_build.groovy"
def dockerImage = '528909316/jenkins'
stage('Before build execution'){
// 更新gitlab状态
updateGitlabCommitStatus name: 'one_project', state: 'running'
before_build.before_build("${branch}")
// 更新tag
if ("${branch}" == "refs/heads/pipeline") {
tag = "main"
}
}
stage('pull code') {
// 克隆代码 根据变量 tag作为拉取分支
pull_code.pull_code("${tag}","${GITLAB_ID}","${GITLAB_SSH_URL}")
// git branch: "${tag}", credentialsId: "${GITLAB_ID}", url: "${GITLAB_SSH_URL}"
echo "The first stage end"
}
stage('build code') {
container('build') {
stage('Build a python project') {
build.build("cpu.py")
}
}
echo "The second stage end."
}
stage("build image") {
container("docker") {
stage("build image") {
// 某个stage中定义的 作用域当前函数中,只能在当前stage中或者其子stage中调用
buildDocker.buildDocker("${dockerImage}","${tag}")
}
}
echo "push ${dockerImage} end."
}
stage("deploy image") {
deploy.deploy("${branch}","${filePath}","${id}","${dockerImage}")
}
// 返回给gitlab一个信号 表示流水线成功
updateGitlabCommitStatus name: 'one_project', state: 'success'
}
}
main分支:
目录结构
.
├── cpu.py
├── Dockerfile
├── pipeline
│ └── deploy.yml
└── README.md
cpu.py
from flask import Flask
from time import strftime
app = Flask(__name__)
@app.route("/")
def main():
print("[{}] start calculate task.".format(strftime("%Y-%m-%d %H:%M:%S")))
number = 0
for i in range(0,100000000):
number+=i
print("[{}] calculate task end,return results.".format(strftime("%Y-%m-%d %H:%M:%S")))
return {"key":number}
if __name__ == '__main__':
app.run(host='0.0.0.0',port="8000")
Dockerfile
FROM python:3.6.15-slim
ADD dist/cpu cpu
CMD ["./cpu"]
pipeline/deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: python-cpu
namespace: NAMESPACE
spec:
selector:
matchLabels:
run: python-cpu
replicas: 3
template:
metadata:
labels:
run: python-cpu
version: DEPLOY_VERSION
spec:
containers:
- name: python-cpu
imagePullPolicy: Always
image: DOCKERIMAGE:task_main
ports:
- containerPort: 8000
resources:
limits:
cpu: 500m
requests:
cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
name: python-cpu
labels:
run: python-cpu
spec:
ports:
- port: 8000
selector:
run: python-cpu
type: LoadBalancer
jenkins 触发构建配置