• [docker]-容器镜像管理规范


    导语:记录一下相关规范。以防后续用到时找不到

    容器镜像管理规范

    命名规范

    • 仓库类型
      • snapshot :开发版本仓库
      • release :生产正式版本仓库
    • 仓库命名
      • snapshot : 业务/项目名称-snapshot demo-snapshot
      • release : 业务/项目名称-release demo-release
    • 镜像命名
      • DEV : demo-snapshot/demo-devops-service:branch_commitid
      • PRD: demo-release/demo-devops-service:version_commitid
      • (业务/项目名称)/应用名称/标签
    • 标签命名
      • 分支名_提交ID
      • 版本号_提交ID

    提交ID的意义: 减少重复构建。 每次发布获取当前发布分支中的最后一次提交的id进行匹配,在harbor镜像仓库中进行搜索,如果存在则跳过构建直接发布,如果不存在则进行构建步骤再发布。

    img

    镜像清理策略

    随着镜像越来越多,频繁更新导致Harbor镜像仓库容量很快爆满。

    • snapshot仓库:每定时清理几天前的镜像
    • release仓库:版本发布完成后,清除版本其他镜像

    因为版本分支进行正式环境发布的时候,可能会出现问题。进行修复后镜像仓库中会出现 1.1.1_xxxxxx1,1.1.1_xxxxxx2的镜像标签。假如1.1.1_xxxxxx2是我们正式发布的版本,则发布完成后。清理掉1.1.1_xxxxxx1。

    主要通过以下2条命令获取短位的COMMITID,生成tag

                        GIT_COMMITID= sh(  returnStdout: true, script: 'echo -n `git rev-parse --short HEAD`')
                        Tag="${BRANCH}_${GIT_COMMITID}"
    
    • 1
    • 2

    jenkinsfile 如下

    #!groovy
    
    pipeline {
        agent {
            label "10.10.3.245"
        }
    
        //定义构建参数
        parameters {
           
            
            imageTag(name: 'DOCKER_IMAGE', description: '应用使用的基础镜像',
                image: 'base/lung', filter: '.*', defaultTag: 'ge930',
                 registry: 'http://harbor.deepwise.com', credentialId: '18060a85-ebcf-4aa4-bca7-b267c07497f3', tagOrder: 'NATURAL') 
            gitParameter name: 'BRANCH', type: 'PT_BRANCH_TAG', branchFilter: '(.*\\b)', listSize: '10', defaultValue: '20220531_sp2', description: '代码的分支', selectedValue: 'TOP', sortMode: 'DESCENDING'
            choice(name: 'NameSpace', choices: ['default','dev01','dev02','dev03','dev04','dev05','test01','test02','test03','test04','test05'], description: '请选择需要发布的环境----namespace')
        }
    
        //定义变量
        environment {
            Unzip_Path="/data1/TestPackage/lung/cta/"
            //Tag = "`date '+%Y-%m-%d'-${BUILD_NUMBER}`"
            // 项目名称
            // PROJECT = 'DxAI'
            // 工程名称
            // APP_NAME = "dxPython"
            // 发布环境
            // DEPLOY_ENV = "${params.发布环境}"
            // 仓库地址
            // GIT_REPO = "${params.GIT_REPO}"
            // 仓库分支
            // BRANCH = "${params.BRANCH}"
            // 备份路径
            // BAK_PATH = "/mnt/backup/"
            // 模块路径
            // APP_PATH = "/data1/docker_data/wwwroot/research"
            //默认邮件接收人
            // DEFAULT_EMAIL = ''
            //负责邮件接收人
            // OWNER_EMAIL = ''
            //PL邮件接收人
            // PL_EMAIL = ' '
        }
    
        //来源全局设置配置的变量
    //     tools {
    // //        jdk 'java-8'
    //         maven 'maven-3.6.0'
    //     }
    
        options {
            timestamps()
            //保持构建的最大个数
            buildDiscarder(logRotator(numToKeepStr: '10'))
        }
    
    
    
    
        //执行任务模块
        stages {
            //拉取代码模块
            stage('Checkout weight') {
                steps {
                    //deleteDir()
                    echo "---------------------------------------------------------------------------------"
                    echo "Starting Checkout Code......"
                    echo "---------------------------------------------------------------------------------"
                    script {
                        echo "starting fetchCode from http://xxx.git......"
                        checkout([$class           : 'GitSCM',
                                  branches         : [[name: "${BRANCH}"]],
                                  doGenerateSubmoduleConfigurations: false,
                                  extensions: [[$class: 'CloneOption', noTags: false, reference: '', shallow: false, timeout: 60000],[$class:'CheckoutOption',timeout:60000]],
                                  userRemoteConfigs: [[credentialsId: '81f18226-d36e-4a0e-bd0e-b936843ae77b',
                                                       url          : "http://xxx.git",]]
                        ])
                    }
                }
            }
            stage('Checkout lung') {
                steps {
                    //mkdir lung
    
                    //deleteDir()
                    echo "---------------------------------------------------------------------------------"
                    echo "Starting Checkout Code......"
                    echo "---------------------------------------------------------------------------------"
                    println env.WORKSPACE
    			 //   dir("/data/jenkins/workspace/lung"){
    			 //       sh "sleep 300"
    			 //       sh "echo pwd"
    				//     sh "pwd"
    			 //   }
    
                    script {
                        wrap([$class: 'BuildUser']) {
                        def deploylog="${BUILD_USER} use pipeline  '${JOB_NAME}(${BUILD_NUMBER})' "
                        println deploylog
                        buildName "#${BUILD_NUMBER}-^${BRANCH}^-${BUILD_USER}"
                        HTTPD_LOCATION= sh(  returnStdout: true, script: 'git show -s  |grep -vE "commit|Date" |grep -v "^$"')
                      
                        buildDescription "${HTTPD_LOCATION}" 
                        GIT_COMMITID= sh(  returnStdout: true, script: 'echo -n `git rev-parse --short HEAD`')
                        Tag="${BRANCH}_${GIT_COMMITID}"
                        
                        
                      }
                        echo "starting fetchCode from http://xxx.git......"
                        checkout([$class           : 'GitSCM',
                                  branches         : [[name: "${BRANCH}"]],
                                  doGenerateSubmoduleConfigurations: false,
                                  extensions: [[$class: 'CleanBeforeCheckout', deleteUntrackedNestedRepositories: true],[$class: 'CloneOption', noTags: false, reference: '', shallow: false, timeout: 60000],[$class:'CheckoutOption',timeout:60000],[$class: 'RelativeTargetDirectory', relativeTargetDir: '/data/jenkins/workspace/lung-base']],
                                  userRemoteConfigs: [[credentialsId: '81f18226-d36e-4a0e-bd0e-b936843ae77b',
                                                      url          : "http://xxx.git",]]
                        ])
                    }
                }
            }
    
    
            //打包模块
            stage('Package') {
                steps {
                    echo "---------------------------------------------------------------------------------"
                    echo "Starting Gradlew package ......"
                    echo "---------------------------------------------------------------------------------"
                    script {
                        sh """
                            echo "package"
                            rsync -avP  --exclude=.git /data/jenkins/workspace/lung-base/*  /data/package/lung/ 
                            rsync -avP  --exclude=.git ${WORKSPACE}/*  /data/package/lung/
                            rsync -avP  --exclude=.git /data/jenkins/workspace/aiclassifier  /data/package/
                           """
                    }
                }
            }
    
            stage('build') {
                steps {
                    echo "---------------------------------------------------------------------------------"
                    echo "Starting Publish apk to Remote ......"
                    echo "---------------------------------------------------------------------------------"
                    script {
                        sh """
                            echo Tag ${Tag}
                            echo TestTag $TestTag 
                            cd /data/package/
                            echo base image name: ${DOCKER_IMAGE}
                            echo image name: harbor.deepwise.com/dev/${JOB_NAME}:${Tag}
                            echo FROM harbor.deepwise.com/${DOCKER_IMAGE} > Dockerfile
                            echo COPY lung /root/lung >> Dockerfile
                            echo COPY aiclassifier /root/aiclassifier >> Dockerfile
                            echo harbor.deepwise.com/dev/${JOB_NAME}:${Tag}
                            cat Dockerfile
                            echo "docker build -t harbor.deepwise.com/dev/${JOB_NAME}:${Tag} ."
                            docker build -t harbor.deepwise.com/dev/${JOB_NAME}:${Tag} .
                            
                            docker push harbor.deepwise.com/dev/${JOB_NAME}:${Tag}
                            
                            whoami
                           # echo ssh root@172.16.20.88 "kubectl set image deployment/aiserver-dp  aiserver=harbor.deepwise.com/dev/${JOB_NAME}:${Tag} -n ${NameSpace} "
                           # ssh root@172.16.20.88 "kubectl set image deployment/aiserver-dp  aiserver=harbor.deepwise.com/dev/${JOB_NAME}:${Tag} -n ${NameSpace} "
                        """
                    }
                }
            }
    
            stage('Install') {
                // agent {
                //     label "172.16.40.245"
                // }
                steps {
                    echo "---------------------------------------------------------------------------------"
                    echo "Starting Install Application......"
                    echo "---------------------------------------------------------------------------------"
                    script {
                        sh """
                            echo 123
                           """
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185

    构建容器镜像

    编写Dockerfile

    FROM nginx:latest
    
    COPY dist /usr/share/nginx/html
    
    • 1
    • 2
    • 3

    复制

    构建镜像

    docker build -t demo-web-app:1.1.1 .
    
    • 1

    复制

    上传镜像

    docker push demo-web-app:1.1.1
    
    • 1

    复制

    一个完整的Jenkinsfile

    pipeline {
        agent {node {label "master"}}
        stages {
            stage('WebBuild') {
                steps {
                    script {
                        docker.image('node:10.19.0-alpine').inside('-u 0:0 -v /var/jenkins_home/.npm:/root/.npm') {
    
    
                            sh """
                                id 
                                ls /root/.npm
    
                                ls /root/ -a
                                npm config set unsafe-perm=true
                                npm config list
                                npm config set cache  /root/.npm
                                #npm config set registry https://registry.npm.taobao.org
                                npm config list
                                ls 
                                cd demo && npm install  --unsafe-perm=true && npm run build  && ls -l dist/ && sleep 15 
                            """
                        }
                    }
                }
            }
    
          stage("BuildImage"){
            steps {
              script{
    
    
                sh """
                    #构建镜像
                    cd demo
                    docker build -t demo/demo-web-app:1.1.1_xxxxxxxx1 .
    
                    #docker push demo/demo-web-app:1.1.1_xxxxxxxx1
    
                """
    
              }
            }
          }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    复制

    构建完成后,运行镜像进行测试

    docker run -itd -p 8080:80 --name nginx-server demo/demo-web-app:1.1.1_xxxxxxxx1
    
    • 1

    镜像清理策略

    如果我们使用的是Harbor镜像仓库,我们可以给每个项目管理员授权定时手动清理镜像,我们也可以通过Harbor的接口进行自动化清理。在此举例通过Jenkins自动化清理。

    这里列举了 获取镜像标签和根据标签删除镜像的方法。(注意这个实例仅供参考,在生产请慎用。不是说不能用,而是涉及到删除镜像,如果误删影响很大。)

    #!groovy
    @Library('jenkinslibrary@master') _
    
    def tools = new org.devops.tools()
    
    String registryName = "${env.registryName}"
    String serviceName = "${env.serviceName}"
    String tagName = "${env.tagName}"
    def harborProjects = []
    
    currentBuild.description = "Trigger by ${serviceName} ${tagName}"
    
    
    pipeline {
        agent { node { label "build"} }
    
        stages{
    
            stage("GetHarborTags"){
                steps{
                    timeout(time:5, unit:"MINUTES"){
                        script{
                            tools.PrintMes("获取Harbor仓库中的项目信息","green")
                            println(serviceName)
    
                            try {
                                response = httpRequest authentication: 'harbor-admin,
                                                       url: "https://registry.demo.com/api/repositories/${registryName}/${serviceName}/tags",
                                                       ignoreSslErrors: true
                                //println(response.content)
                                response = readJSON text: """${response.content}"""
    
                            } catch(e){
                                response = ['name':'']
                                println(e)
                                println("Harbor镜像不存在此标签!")
    
                            }
    
                            /*println(tagName)
                            for (tagname in response){
                                //println(response)
                                harborProjects << tagname['name']
                            }
    
                            println(harborProjects)*/
    
                        }
                    }
                }
            }
    
    
    
            stage("DeleteHarborTags"){
                steps{
                    timeout(time:20, unit:"MINUTES"){
                        script{
                            tools.PrintMes("总共找到 ${harborProjects.size()} 个标签","green")
                            sumImageNum = harborProjects.size()
                            for (tag in harborProjects){
                                sumImageNum -= 1
    
                                tools.PrintMes(" ${sumImageNum}  Delete Tags ---> ${registryName} --> ${serviceName} --> ${tag} ","green")
    
                                httpRequest httpMode: 'DELETE',
                                           authentication: 'c016027e-0573-4246-93cf-f4a55b08a86a',
                                           url: "https://registry.demo.com/api/repositories/${registryName}/${serviceName}/tags/${tag}",
                                           ignoreSslErrors: true
    
                                sleep 1
                            }
                        }
                    }
                }
            }
        }
    
    
        post {
            always{
                script{
                    cleanWs notFailBuild: true 
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    参考

    https://cloud.tencent.com/developer/article/1599849

    https://github.com/zeyangli

  • 相关阅读:
    【黄色手套22】7话:预处理和文件操作
    composer 扩展库。助手库文档
    日常Bug排查-改表时读数据不一致
    8.11 可解析AI
    如何实现一个简单易用的 RocketMQ SDK
    京东云开发者|ElasticSearch降本增效常见的方法
    3.primitive主数据类型和引用 认识变量
    Caldera安装及简单使用
    【微信小程序系列:四】前端利用wx.setStorageSync缓存设置有效时间
    java.lang.Object类(equals()方法、toString()方法)
  • 原文地址:https://blog.csdn.net/xujiamin0022016/article/details/126072817