当前位置:首页 > 文章中心 > 正文内容

使用Jenkins更新Openshift上的应用

dgx6663个月前 (09-14)文章中心20

前言

本篇介绍了一种利用Jenkins Pipeline脚本更新Openshift容器云平台上已部署应用的方式,主要涉及如下知识:

  • 如何利用Openshift在Jenkins的插件更新部署并获取状态
  • 如何编写Jenkins Pipeline脚本
  • 如何为Pipeline脚本建立动态参数、动态步骤
  • 如何让Pipeline步骤并行执行
  • 如何从容器镜像库获取镜像Tag列表

以下为运行效果示意,如感兴趣可继续阅读。

说明

本例中使用的Jenkins运行于Openshift平台上,可能是Openshift插件可以正常使用的条件。

本例中脚本对镜像Tag个格式有具体要求,详见下文。

如何更新

了解K8S和Openshift的同学应该知道,我们把StatefulSet、Deployment、DeploymentConfig里容器镜像替换掉,就会触发部署。

在以下Groovy方法中,调用了Openshift在Jenkins的插件的selector方法,找到部署对象,然后修改目标容器的镜像,从而触发更新。

def patchImage(appConfig) {
    def p = openshift.selector("${appConfig.deploymentType}/${appConfig.deploymentName}").object()
    def originalImage = p.spec.template.spec.containers[0].image
    def targetImage = "image-registry.openshift-image-registry.svc:5000/${openshift.project()}/${appConfig.imageStreamName}:${appConfig.imageTag}"
    p.spec.template.spec.containers[0].image = targetImage
    openshift.apply(p)
    if (targetImage != originalImage) {
        echo "${appConfig.deploymentType}/${appConfig.deploymentName} 镜像已由 ${originalImage} 更新为: ${openshift.project()}/${appConfig.imageStreamName}:${appConfig.imageTag},将自动触发Rollout"
    } else {
        echo "${appConfig.deploymentType}/${appConfig.deploymentName} 镜像未改变: ${originalImage},将强制rollout。"
        openshift.selector("${appConfig.deploymentType}/${appConfig.deploymentName}").rollout().latest()
    }
}

如何判断更新状态

Deployment在Rollout的过程中,我们可以通过openshift插件获取拿到最新的部署版本,并判断其是否更新完成。

由于DeploymentConfig(简写为dc)和StatefulSet(简写为sts)有些不同,所以,我们分开进行判断。

对于dc,主要通过比较desiredReplicas(期望副本数)和readyReplicas(就绪副本数)来判断是否完成了更新。

if (appConfig.deploymentType == "dc") {
            def latestDeploymentVersion = openshift.selector(appConfig.deploymentType, appConfig.deploymentName).object().status.latestVersion
            def rc = openshift.selector('rc', "${appConfig.deploymentName}-${latestDeploymentVersion}")
            timeout (time: 30, unit: 'MINUTES') {
                rc.untilEach(1){
                    def rcMap = it.object()
                    def desiredReplicas = rcMap.metadata.annotations['kubectl.kubernetes.io/desired-replicas']
                    if (desiredReplicas == null) {
                        desiredReplicas = rcMap.status.replicas
                    }
                    def readyReplicas = rcMap.status.readyReplicas == null ? 0 : rcMap.status.readyReplicas
                    echo "desired replicas: ${desiredReplicas}, readyReplicas: ${readyReplicas}"
                    
                    if (waitEachCopy) {
                        return desiredReplicas != null && (desiredReplicas.equals(readyReplicas))
                    } else {
                        return desiredReplicas != null && readyReplicas >=1
                    }
                }
            }
        }

对于sts,方式是判断每个容器均Ready。

if (appConfig.deploymentType == "sts") {
            def latestStsVersion = openshift.selector(appConfig.deploymentType, appConfig.deploymentName).object().status.updateRevision
            def stsPods = openshift.selector('pods', [ 'controller-revision-hash': latestStsVersion ])
            timeout (time: 30, unit: 'MINUTES') {
                stsPods.untilEach(1){
                    if (waitEachCopy) {
                        return it.object().status.containerStatuses.every {
                            echo "pod container ready: ${it.ready}"
                            it.ready
                        }
                    } else {                                    
                        return it.object().status.containerStatuses.any {
                            echo "pod container ready: ${it.ready}"
                            it.ready
                        }
                    }
                }
            }
        }

如何将镜像导入内部镜像库

在更新部署对象的镜像之前,我们可以先把容器镜像由外部镜像库导入到Openshift内部镜像库,可以多留存一份镜像,同时利于工作节点快速获取镜像(未验证)。

这里用到了openshit插件的raw方法,可以执行原生oc命令,所以导入镜像实际使用的命令为oc image mirror。不知什么原因,这里经常会报Unknown Blob错误,多试几次就好了。

在同步镜像之前,需要登录源和目标镜像仓库,使用的也是oc命令registry login。

Closure createImportImageStep(appConfig) {
    return {
        echo "当前集群:${openshift.cluster()} 当前项目: ${openshift.project()}"

        echo "登录内部镜像库..."
        openshift.raw("registry login --registry=${internalRegistryAddress} --insecure=true --skip-check=true")

        echo "登录外部镜像库..."
        openshift.raw("registry login --registry ${externalRegistryAddress} --auth-basic=YourUsername:YourPassword --insecure=true")

        echo "导入镜像到内部镜像库..."
        if (!appConfig.imageTag) {
            echo "${appConfig.app} 因无目标镜像跳过镜像导入"
            return
        }                        
        retry(5) {
            importDockerImage(appConfig)
        }      
    }
}

def importDockerImage(appConfig) {    
    echo "开始导入镜像:istag/${appConfig.imageStreamName}:${appConfig.imageTag}"
    def p = openshift.selector("istag/${appConfig.imageStreamName}:${appConfig.imageTag}")
    if (p.exists() && importImageForce) {
        p.delete()
        echo "imagestreamtag/${appConfig.imageStreamName}:${appConfig.imageTag} deleted."
    }
    if (!p.exists() || importImageForce) {
        openshift.raw("image mirror ${externalRegistryAddress}/${appConfig.imagePath}/${appConfig.imageName}:${appConfig.imageTag} ${internalRegistryAddress}/${openshift.project()}/${appConfig.imageStreamName}:${appConfig.imageTag} --insecure=true")
    }
    echo "完成导入镜像:istag/${appConfig.imageStreamName}:${appConfig.imageTag}"
}

如何列举镜像Tag

为了方便运维同事通过界面选择要更新的应用和Tag,需要Pipeline脚本从外部Docker镜像库读取各应用镜像的Tag。

这里脚本的核心点是调用了镜像库的HTTP API。


//访问镜像库api获取镜像Tag
def getDockerTags(appConfig) {
    def url = "http://${externalRegistryAddress}/v2/${appConfig.imagePath}/${appConfig.imageName}/tags/list"
    def list = getDockerImageTags(url)
    list
}

// 分为3步,获得json文本,转换为json对象,从对象获得所有Tag
def getDockerImageTags(url) {
    def myjson = getUrl(url)
    def json = jsonParse(myjson);
    def tags = json.tags
    tags
}

//通过curl访问镜像库API,将结果保存为result.json文件,然后返回文件内容
def getUrl(url) {
    sh(returnStdout: true, script: "curl -s --max-time 15 --retry 3 --retry-delay 5  ${url} 2>&1 | tee result.json")
    def data = readFile('result.json').trim()
    data
}

// 将镜像Tag倒序排列后,取前5个
def getLatestTag(appConfig) {
    def tags = getDockerTags(appConfig)
    if (tags == null || tags.size() == 0) {
        return null
    }
    def sortedTags = sortReverse(tags)
    def topTags = sortedTags.size() > 5 ? new ArrayList(sortedTags.subList(0,4)) : sortedTags
    topTags
}

// 对镜像Tag按倒序排列,要求镜像Tag遵循一定格式,如2.1.0-20211105
@NonCPS
def sortReverse(list) {
    list.sort{a,b ->
        def ay = a.split("[.-]")
        def by = b.split("[.-]")
        for (int i = 0; i < ay.length; i++) {
            def ai = ay[i].isInteger() ? ay[i].toInteger() : ay[i];
            def bi = by[i].isInteger() ? by[i].toInteger() : by[i];
            if (bi.compareTo(ai) == 0) {
                continue
            }
            return bi.compareTo(ai)
        }
    }
}

// 将json文本转换为json对象
@NonCPS
def jsonParse(json) {
    new groovy.json.JsonSlurperClassic().parseText(json)
}

如何生成选型参数

为Jenkins Pipeline创建选项参数,方法是创建一个ChoiceParameterDefinition的数组就可以了。

//生成镜像Tag选择器
def getTagChoiceParamter() {
    def tags = []
    tagSet.each { entry -> tags.add(entry.key) }
    def sortedTags = sortReverse(tags)
    
    def parameters = []
    def para = new ChoiceParameterDefinition("latestTag", sortedTags, sortedTags[0], "最新版本")
    parameters.add para
    parameters
}

//这个是镜像选择的stage 片段
    stage('选择版本') {
        def tagSelectorInputTimeout = false
        try {
            timeout(time: 180, unit: 'SECONDS') {
                tagSelectorInput = input(
                        id: 'tagInput', ok: '继续', message: '请选择要更新到的镜像Tag:', parameters: tagParameters
                )
            }
        } catch(err) {
            def user = err.getCauses()[0].getUser()
            if('SYSTEM' == user.toString()) {
                tagSelectorInputTimeout = true
            } else {
                tagSelectorInput = null
                echo "镜像Tag选择被用户中止: [${user}]"
            }
        }
    }

验证身份

能够访问Pipeline,说明已经当前用户已经登录Jenkins了。为了多一道安全控制,需要用户在Openshift界面或通过命令oc whoami -t获取一个Token,在正式运行Pipeline部署步骤之前验证一下身份。

这里主要使用了Openshift插件的withCluster、withCredentials和raw三个方法,通过执行oc status -v命令验证其身份。

stage('登录Openshift') {
        timeout(time: 180, unit: 'SECONDS') {
            myToken = input(
                    id: 'myTokenInput', ok: '继续', message: '请输入访问Openshift的Token', parameters: [password(defaultValue: '', description: 'OCP访问令牌,可以在登录openshift后,通过执行 oc whoami -t 获得', name: 'Token for Openshift')]
            )
        }
        openshift.withCluster() {
            openshift.withCredentials(myToken) {
                def status = openshift.raw('status', '-v')
                echo "登录成功. 集群是:${openshift.cluster()} 默认项目: ${openshift.project()} 流水线版本:${pipelineVersion}"
            }
        }
    }

应用元数据

应用的列表和应用之间的依赖关系,以及各应用部署对象的类型和名称、镜像库路径等等,通过在脚本定义字段实现。


@Field def final myConfigJson = '''
[
      {
      "app": "activity",	//应用
      "imagePath": "xxx-group/release/yyy", //应用镜像在镜像库的路径
      "imageName": "activity", //应用镜像的名称
      "imageStreamName": "activity", //应用镜像在内部镜像库的名称
      "deploymentType": "dc", //应用的部署类型,dc即DeploymentConfig
      "deploymentName": "activity", //应用的Deployment或StatefulSet的名称
      "selected": false, //是否默认选中,如果经常更新该应用,可默认选中
      "dependencies": [
      ], //依赖的其它应用,可以控制更新顺序
      "status": "new",  //更新状态控制,这里都是new,其它由脚本控制
      "imageTag": "", //目标镜像Tag,在运行中赋值
      "group": ["积分活动"] //应用组,方便一次性选择多个应用,当前脚本以无此功能
    },
    {
      "app": "point",
      "imagePath": "xxx-group/release/yyy",
      "imageName": "point",
      "imageStreamName": "point",
      "deploymentType": "dc",
      "deploymentName": "point",
      "selected": false,
      "dependencies": [
      ],
      "status": "new",
      "imageTag": "",
      "group": ["积分活动"]
    }
]
'''

完整脚本

完整的Pipeline脚本如下,为方便理解,加了一些注释。为方便选择应用,选择应用的方式几经修改,部分方法可能已经不再使用了。使用该脚本在Jenkins建立Pipeline类型的Job即可,不要选择“Use Groovy Sandbox”。可能因安全原因,脚本变更后,每次都需要Jenkins管理员授权。

import groovy.transform.Field

@Field def final pipelineVersion = "20210813" //此Pipeline的版本,用于发生问题时调查依据
@Field def final envType = "" // 环境类型 ".test" 为测试环境,""为生产环境,方便在测试环境和生产环境同时使用
@Field def final externalRegistryAddress = "docker-app.nexus${envType}.ccc" //外部镜像库域名
@Field def final internalRegistryAddress = "default-route-openshift-image-registry.apps.ocp${envType}.ccc" //内部镜像库域名

@Field def ocp_project = 'yyy-prod' //针对哪个Openshift Project

//应用元数据
@Field def final myConfigJson = '''
[
      {
      "app": "activity",	//应用
      "imagePath": "xxx-group/release/yyy", //应用镜像在镜像库的路径
      "imageName": "activity", //应用镜像的名称
      "imageStreamName": "activity", //应用镜像在内部镜像库的名称
      "deploymentType": "dc", //应用的部署类型,dc即DeploymentConfig
      "deploymentName": "activity", //应用的Deployment或StatefulSet的名称
      "selected": false, //是否默认选中,如果经常更新该应用,可默认选中
      "dependencies": [
      ], //依赖的其它应用,可以控制更新顺序
      "status": "new",  //更新状态控制,这里都是new,其它由脚本控制
      "imageTag": "", //目标镜像Tag,在运行中赋值
      "group": ["积分活动"] //应用组,方便一次性选择多个应用,当前脚本以无此功能
    },
    {
      "app": "point",
      "imagePath": "xxx-group/release/yyy",
      "imageName": "point",
      "imageStreamName": "point",
      "deploymentType": "dc",
      "deploymentName": "point",
      "selected": false,
      "dependencies": [
      ],
      "status": "new",
      "imageTag": "",
      "group": ["积分活动"]
    }
]
'''
@Field def myConfig;

//Openshift Token,二次验证身份用
@Field def myToken

@Field def tagSet = [:] //镜像Tag的Map,key为镜像Tag,value为应用列表
@Field def rolloutGroupSelector //应用组选择器,此版本已废弃
@Field def rolloutAppSelector //应用选择器
@Field def tagParameters //镜像Tag参数

//运行选项
@Field def waitEachCopy = true //等待每个副本更新完成
@Field def waitDepen = true //等待依赖应用更新完成
@Field def importImageForce = false //当镜像已存在于内部镜像库时,强制导入

@Field def stepsForImportImages = [:] //导入镜像的Pipeline步骤,用于并行执行
@Field def stepsForUpdateApp = [:] //更新应用的Pipeline步骤,用于并行执行


node {
    stage('登录Openshift') {
        timeout(time: 180, unit: 'SECONDS') {
            myToken = input(
                    id: 'myTokenInput', ok: '继续', message: '请输入访问Openshift的Token', parameters: [password(defaultValue: '', description: 'OCP访问令牌,可以在登录openshift后,通过执行 oc whoami -t 获得', name: 'Token for Openshift')]
            )
        }
        openshift.withCluster() {
            openshift.withCredentials(myToken) {
                def status = openshift.raw('status', '-v')
                echo "登录成功. 集群是:${openshift.cluster()} 默认项目: ${openshift.project()} 流水线版本:${pipelineVersion}"
            }
        }

        myConfig = jsonParse(myConfigJson)
        getTagSet(myConfig)
        tagParameters = getTagChoiceParamter()
    }
    stage('选择版本') {
        def tagSelectorInputTimeout = false
        try {
            timeout(time: 180, unit: 'SECONDS') {
                tagSelectorInput = input(
                        id: 'tagInput', ok: '继续', message: '请选择要更新到的镜像Tag:', parameters: tagParameters
                )
            }
        } catch(err) {
            def user = err.getCauses()[0].getUser()
            if('SYSTEM' == user.toString()) {
                tagSelectorInputTimeout = true
            } else {
                tagSelectorInput = null
                echo "镜像Tag选择被用户中止: [${user}]"
            }
        }

        if (tagSelectorInputTimeout) {
            currentBuild.result = 'FAILURE'
            error "选择镜像Tag超时"
        } else if (tagSelectorInput == null) {
            currentBuild.result = 'FAILURE'
            error "选择镜像错误,可能被用户中止"
        } else {
            echo "成功选择镜像标签"
        }    
    
    }
    stage('选择应用') {
        def selectedTag = tagSelectorInput
        def rolloutAppSelectorTimeout = false     
        try {
            def paras = []
            def appSelectedFollowTag = tagSet.get(selectedTag)
            for (i=0; i < appSelectedFollowTag.size(); i++) {
                paras.add booleanParam(defaultValue: true, description: '', name: appSelectedFollowTag[i].app)
            }

            timeout(time: 180, unit: 'SECONDS') {
                rolloutAppSelector = input(id: 'rolloutAppSelectorInput', ok: '继续', message: '请选择要更新的应用', parameters: paras)
            }
        } catch(err) {
            def user = err.getCauses()[0].getUser()
            if('SYSTEM' == user.toString()) {
                rolloutAppSelectorTimeout = true
            } else {
                rolloutAppSelector = null
                echo "Aborted by: [${user}]"
            }
        }
        if (rolloutAppSelectorTimeout) {
            currentBuild.result = 'FAILURE'
            error "选择待更新应用超时。"
        } else if (rolloutAppSelector == null) {
            currentBuild.result = 'FAILURE'
            error "选择待更新应用可能被用户中止了。"
        } else {
            echo "选择待更新应用成功。"
            rolloutAppSelector.findAll{key, value -> value == true}.each { entry ->
                echo "应用 ${entry} 已选择。"
            }            
            def apps = getSelectedApp()
            for (int i = 0; i < apps.size(); i++) {                
                def appConfig = apps[i]
                appConfig.imageTag = selectedTag
            }            
        }
    }

    stage ('设置选项') {
        def optionsSelectorInputTimeout = false
        try {
            timeout(time: 180, unit: 'SECONDS') {
                runOptions = input(id: 'runOptionsInput', ok: '继续', message: '请选择更新选项', parameters: [
                        booleanParam(defaultValue: true, description: '等待应用的每个副本都更新完成?', name: "waitEachCopy"),
                        booleanParam(defaultValue: true, description: '等待被依赖应用更新完成?', name: "waitDepen"),
                        booleanParam(defaultValue: false, description: '当镜像已存在时,强制重新导入?', name: "importImageForce")
                ])
            }
        } catch(err) {
            def user = err.getCauses()[0].getUser()
            if('SYSTEM' == user.toString()) {
                optionsSelectorInputTimeout = true
            } else {
                runOptions = null
                echo "选择更新选项被用户中止: [${user}]"
            }
        }

        if (optionsSelectorInputTimeout) {
            currentBuild.result = 'FAILURE'
            error "选择更新选项超时"
        } else if (runOptions == null) {
            currentBuild.result = 'FAILURE'
            error "选择更新选项错误,可能被用户中止"
        } else {
            waitEachCopy = runOptions.waitEachCopy.value
            waitDepen = runOptions.waitDepen.value
            importImageForce = runOptions.importImageForce.value
            echo "成功选择更新选项"
        }
    }

    stage ('确认') {
        def apps = "";
        sortAppSelectorByDependency().each { appConfig ->            
            wait = waitFinished(appConfig) ? "-等待" : ""
            apps += "${appConfig.deploymentType}/${appConfig.deploymentName}:${appConfig.imageTag}${wait}\n"
        }
        def waitType = ""
        if (waitDepen) {
            waitType += "\n在更新前,等待被依赖的应用都更新完成。"
        } else {
            waitType += "\n直接更新,不等待被依赖的应用更新完成。"
        }
        if (waitEachCopy) {
            waitType += "\n等待所有副本都更新完成"
        }
        else {
            waitType += "\n只要有一个副本更新完成即可"
        }


        def message = """将更新${ocp_project}的如下应用:
${apps}
等待方式:${waitType}

目标镜像、更新顺序及等待方式如上。

强制刷新镜像: ${importImageForce}

请确认以上内容,并选择继续或中止?
"""

        input (message: "将更新${ocp_project}的如下应用", ok: '确认并继续', parameters: [ 
          string(name: 'confirmAppsToUpdate', description: message, defaultValue: "---不用输入---")]
        )
    }

    stage ('导入镜像') {
        openshift.withCluster() {
            openshift.withCredentials(myToken) {
                openshift.withProject(ocp_project) {         
                    rolloutAppSelector.findAll{key, value -> value == true}.each { key, value ->
                        def appConfig = myConfig.find{item -> item.app == key}
                        stepsForImportImages["导入镜像:${appConfig.app}"] = createImportImageStep(appConfig)
                    }
                    stepsForImportImages["failFast"] = true
                    parallel stepsForImportImages                   
                }
            }
        }
    }
    stage ('更新应用') {
        openshift.withCluster() {
            openshift.withCredentials(myToken) {
                openshift.withProject(ocp_project) {
                    sortAppSelectorByDependency().each { appConfig ->
                        stepsForUpdateApp["更新应用:${appConfig.app}"] = createUpdateAppStep(appConfig)
                    }
                    parallel stepsForUpdateApp
                }
            }
        }
    }
}

def getAppGroups() {
    def groups = [:]
    groups.put("全部", new HashSet<String>())
    for (i=0; i < myConfig.size(); i++) {
        def curApp = myConfig[i]
        groups["全部"].add(curApp.app)
        
        def group = curApp.group
        if (group) {
            group.each {
                if (!groups.containsKey(it)) {
                    groups.put(it, new HashSet<String>())
                }                
                groups[it].add(curApp.app)                
            }
        }
    }
    groups
}

def getSelectedAppFollowGroup() {    
    def selectedApps = new HashSet<String>()
    rolloutGroupSelector.findAll{key, value ->
        value == true
    }.each { key, value ->
        configs = myConfig.findAll{config -> "全部".equals(key) || (config.group && config.group.contains(key))}
        if (configs) {
            configs.each { config -> selectedApps.add(config.app) }            
        }
    }
    selectedApps
}

Closure createImportImageStep(appConfig) {
    return {
        echo "当前集群:${openshift.cluster()} 当前项目: ${openshift.project()}"

        echo "登录内部镜像库..."
        openshift.raw("registry login --registry=${internalRegistryAddress} --insecure=true --skip-check=true")

        echo "登录外部镜像库..."
        openshift.raw("registry login --registry ${externalRegistryAddress} --auth-basic=YourUsername:YourPassword --insecure=true")

        echo "导入镜像到内部镜像库..."
        if (!appConfig.imageTag) {
            echo "${appConfig.app} 因无目标镜像跳过镜像导入"
            return
        }                        
        retry(5) {
            importDockerImage(appConfig)
        }      
    }
}


Closure createUpdateAppStep(appConfig) {
    return {        
        if (!appConfig.imageTag) {
            echo "${appConfig.app} 因无目标镜像跳过更新"
            appConfig.status = "skiped"
            return
        }
        
        if (waitFinished(appConfig)) {                        
            def selectedApps = getSelectedApp()                    
            timeout (time: 60, unit: 'MINUTES') {
                waitUntil(initialRecurrencePeriod: 30000, quiet: false) {
                    appConfig.dependencies.every {depAppName ->
                        def depApp = selectedApps.find{item -> item.app.equals(depAppName)}
                        def depFinished = depApp == null || depApp.status.equals("updated") || depApp.status.equals("skiped")
                        if (depApp) {
                            echo "${appConfig.app}依赖的${depApp.app}当前状态是:${depApp.status}"
                        }
                        depFinished
                    }
                }
            }
        }
        
        patchImage(appConfig)
        
        if (appConfig.deploymentType == "dc") {
            def latestDeploymentVersion = openshift.selector(appConfig.deploymentType, appConfig.deploymentName).object().status.latestVersion
            def rc = openshift.selector('rc', "${appConfig.deploymentName}-${latestDeploymentVersion}")
            timeout (time: 30, unit: 'MINUTES') {
                rc.untilEach(1){
                    def rcMap = it.object()
                    def desiredReplicas = rcMap.metadata.annotations['kubectl.kubernetes.io/desired-replicas']
                    if (desiredReplicas == null) {
                        desiredReplicas = rcMap.status.replicas
                    }
                    def readyReplicas = rcMap.status.readyReplicas == null ? 0 : rcMap.status.readyReplicas
                    echo "desired replicas: ${desiredReplicas}, readyReplicas: ${readyReplicas}"
                    
                    if (waitEachCopy) {
                        return desiredReplicas != null && (desiredReplicas.equals(readyReplicas))
                    } else {
                        return desiredReplicas != null && readyReplicas >=1
                    }
                }
            }
        } else if (appConfig.deploymentType == "sts") {
            def latestStsVersion = openshift.selector(appConfig.deploymentType, appConfig.deploymentName).object().status.updateRevision
            def stsPods = openshift.selector('pods', [ 'controller-revision-hash': latestStsVersion ])
            timeout (time: 30, unit: 'MINUTES') {
                stsPods.untilEach(1){
                    if (waitEachCopy) {
                        return it.object().status.containerStatuses.every {
                            echo "pod container ready: ${it.ready}"
                            it.ready
                        }
                    } else {                                    
                        return it.object().status.containerStatuses.any {
                            echo "pod container ready: ${it.ready}"
                            it.ready
                        }
                    }
                }
            }
        }
        
        appConfig.status = "updated"
        echo "${appConfig.deploymentType}/${appConfig.deploymentName} rollout success." 
    }
}

def waitFinished(app) {
    def hasDependency = app.dependencies != null && app.dependencies.size() > 0
    waitDepen && hasDependency
}

def sortAppSelectorByDependency() {
    def apps = getSelectedApp()
    sortByDependency(apps)
}

def getSelectedApp() {
    def selectedApps = []
    rolloutAppSelector.findAll{key, value ->
        value == true
    }.each { key, value ->
        config = myConfig.find{item -> item.app == key}
        if (config != null) {
            selectedApps.add(config)
        }
    }
    selectedApps
}

@NonCPS
def sortByDependency(list) {
    list.sort {a,b ->
        b.dependencies.contains(a.app) ? -1 : a.dependencies.contains(b.app) ? 1 : 0
    }
}


def patchImage(appConfig) {
    def p = openshift.selector("${appConfig.deploymentType}/${appConfig.deploymentName}").object()
    def originalImage = p.spec.template.spec.containers[0].image
    def targetImage = "image-registry.openshift-image-registry.svc:5000/${openshift.project()}/${appConfig.imageStreamName}:${appConfig.imageTag}"
    p.spec.template.spec.containers[0].image = targetImage
    openshift.apply(p)
    if (targetImage != originalImage) {
        echo "${appConfig.deploymentType}/${appConfig.deploymentName} 镜像已由 ${originalImage} 更新为: ${openshift.project()}/${appConfig.imageStreamName}:${appConfig.imageTag},将自动触发Rollout"
    } else {
        echo "${appConfig.deploymentType}/${appConfig.deploymentName} 镜像未改变: ${originalImage},将强制rollout。"
        openshift.selector("${appConfig.deploymentType}/${appConfig.deploymentName}").rollout().latest()
    }
}

def importDockerImage(appConfig) {    
    echo "开始导入镜像:istag/${appConfig.imageStreamName}:${appConfig.imageTag}"
    def p = openshift.selector("istag/${appConfig.imageStreamName}:${appConfig.imageTag}")
    if (p.exists() && importImageForce) {
        p.delete()
        echo "imagestreamtag/${appConfig.imageStreamName}:${appConfig.imageTag} deleted."
    }
    if (!p.exists() || importImageForce) {
        openshift.raw("image mirror ${externalRegistryAddress}/${appConfig.imagePath}/${appConfig.imageName}:${appConfig.imageTag} ${internalRegistryAddress}/${openshift.project()}/${appConfig.imageStreamName}:${appConfig.imageTag} --insecure=true")
    }
    echo "完成导入镜像:istag/${appConfig.imageStreamName}:${appConfig.imageTag}"
}

def mergeTagList(source, target) {
    def result = []
    if (source != null) {
        result.addAll(source)
    }
    if (target != null) {
        if (result.size() == 0) {
            result.addAll(target)
        } else {
            def tmp = []
            for (i = 0; i < target.size(); i++) {
                if (result.contains(target[i])) {
                    tmp.add(target[i])
                }
            }
            result = tmp
        }
    }
    result
}

def getTagParameters() {
    def parameters = []
    def apps = getSelectedApp()
    for (i=0; i < apps.size(); i++) {
        def para = getAppTagChoiceParamter(apps[i])
        if (para != null) {
            parameters.add para
        }
    }
    parameters
}

def getAppTagChoiceParamter(appConfig) {
    def topTags = getLatestTag(appConfig)
    if (topTags == null || topTags.size() == 0) {
        return null
    }
    def choiceParamter = new ChoiceParameterDefinition(appConfig.app, topTags, topTags[0], "${appConfig.app}");
    choiceParamter
}

def getTagChoiceParamter() {
    def tags = []
    tagSet.each { entry -> tags.add(entry.key) }
    def sortedTags = sortReverse(tags)
    
    def parameters = []
    def para = new ChoiceParameterDefinition("latestTag", sortedTags, sortedTags[0], "最新版本")
    parameters.add para
    parameters
}

def getTagSet(configs) {
    for (i=0; i < configs.size(); i++) {
        def curApp = configs[i]
        def latestTags = getLatestTag(curApp)
        if (latestTags == null || latestTags.size() ==0) {
            continue
        }
        for (int j=0; j < latestTags.size(); j++) {
          def tag = latestTags[j]
          if (!tagSet.containsKey(tag)) {
              tagSet.put(tag, new HashSet())
          }
          tagSet[tag].add(curApp)
        }
    }
}

def getLatestTag(appConfig) {
    def tags = getDockerTags(appConfig)
    if (tags == null || tags.size() == 0) {
        return null
    }
    def sortedTags = sortReverse(tags)
    def topTags = sortedTags.size() > 5 ? new ArrayList(sortedTags.subList(0,4)) : sortedTags
    topTags
}

def getDockerTags(appConfig) {
    def url = "http://${externalRegistryAddress}/v2/${appConfig.imagePath}/${appConfig.imageName}/tags/list"
    def list = getDockerImageTags(url)
    list
}

@NonCPS
def sortReverse(list) {
    list.sort{a,b ->
        def ay = a.split("[.-]")
        def by = b.split("[.-]")
        for (int i = 0; i < ay.length; i++) {
            def ai = ay[i].isInteger() ? ay[i].toInteger() : ay[i];
            def bi = by[i].isInteger() ? by[i].toInteger() : by[i];
            if (bi.compareTo(ai) == 0) {
                continue
            }
            return bi.compareTo(ai)
        }
    }
}

def getDockerImageTags(url) {
    def myjson = getUrl(url)
    def json = jsonParse(myjson);
    def tags = json.tags
    tags
}

@NonCPS
def jsonParse(json) {
    new groovy.json.JsonSlurperClassic().parseText(json)
}

def getUrl(url) {
    sh(returnStdout: true, script: "curl -s --max-time 15 --retry 3 --retry-delay 5  ${url} 2>&1 | tee result.json")
    def data = readFile('result.json').trim()
    data
}

结语

本例这种方式实际为迁就部分运维同事,因部分运维同事习惯于使用WebLogic Admin Portal在界面中通过勾选操作来更新应用,故通过Jenkins Pipeline来尽量模拟这一过程。个人认为这不是一个正常的路子,还是应该通过脚本来操作,并以自动化的方式运行。

扫描二维码推送至手机访问。

版权声明:本文由第六芝士网发布,如需转载请注明出处。

本文链接:http://www.dgx666.com/post/4221.html

标签: winstepnexus
分享给朋友:

“使用Jenkins更新Openshift上的应用” 的相关文章

楼上邻居弹琴跑调到“离谱”,楼下的操作亮了

俗话说“远亲不如近邻”邻里间的互动有时候可谓非常有趣近日,山东菏泽“致楼上邻居的一封信”在网上走红一位热心又专业的邻居老王写信对邻居练钢琴提出了一些“指导意见”指出了两首曲子中的错误还附上了自己精心准备的乐谱谁还没有一个练琴的邻居了网友们纷纷分享了自己或者邻居的“陪练”经历这封“钢琴指导”信张贴后...

CAD2007怎么安装?CAD2007中文版的安装教程

CAD2007是很多从事建筑设计,尤其是结构设计最经常使用的一款专业绘图软件。不少网友下载了CAD2007安装包之后,却不会安装CAD2007,因为他下载的CAD2007的英文版,而一些网友下载了CAD2007中文版的网友,却因为不是在NET2.0以上的安装环境而安装失败。今天,小编就给大家演示一下...

Visio 2010中文版软件下载及安装教程

下载地址见公众号内1.鼠标右键【Visio2010 (64bit)】压缩包选择【解压到Visio2010 (64bit)】2.打开解压后的文件夹,鼠标右键【Setup】,选择【以管理员身份运行】3.勾选【我接受此协议的条款】,点击【继续】4.点击【自定义】5.①点击【文件位置】;②点击【浏览】选择软...

win进程弹出Microsoft Visual C++ Runtime Library的解决办法

很多软件通过设置自己的异常捕获函数,捕获未处理的异常,生成报告或者日志(例如生成mini-dump文件),达到Release版本下追踪Bug的目的。但是,到了VS2005(即VC8),Microsoft对CRT(C运行时库)的一些与安全相关的代码做了些改动,典型的,例如增加了对缓冲溢出的检查。新CR...

inc-by-one之高级漏洞利用技术

Author:Netfairy0x00 前言什么是inc-by-one?比如有这样的一条指令:inc dword ptr[eax+8],这条指令执行的效果是使eax+8地址处的值加1,类似于c语言*(eax+8) = *(eax+8) +1,如果我们可以控制eax的值,那么这就是一个inc-by-o...

2010版本office软件激活工具

很多人的office软件没有激活就使用不到很多功能购买正版又需要很多钱,去网上下载又容易中病毒下载到垃圾软件流氓插件等等所以我今天给大家带来破解工具先打开我的工具双击打开然后直接点击,无需解压直接双击就可以使用要注意的是很多win10用户在安装这个的时候微软自带的安全软件就会拦截此文件所以在安装之前...