容器化项目镜像从构建到清理完整生命周期管理
- 2020 年 3 月 17 日
- 筆記


将应用构建成镜像、将镜像上传到镜像仓库非常简单。通过命令就能解决。镜像仓库巨大爆满如何解决?我们需要在开始使用前就应该设置好镜像的构建策略。(每个公司中管理不一样,具体可根据分支策略决定)。
分支开发策略
- 主干分支 master
- 特性分支 f1 f2
- 版本分支 release

master分支是仓库的主干分支,当我们收到新的需求需要开发的时候。我们会基于master分支创建一个或多个特性分支,进行开发。开发完成后,首先基于master分支创建一个版本分支,然后将一个或多个特性分支合并到版本分支,进行发布。发布完成后将版本分支合并到主干分支。
发布验证:
- 特性分支需要构建发布到特性环境验证。
- 版本分支需要构建发布到UAT/STAG/PROD环境验证。
带来问题:
- 特性分支产生的镜像会很多,而且并不重要可以定时清理掉。
- 版本分支产生相对较少(考虑到版本修复情况),每个版本只有一个镜像。(上线发布完成后清除掉其他)
容器镜像管理规范
命名规范
- 仓库类型
- 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镜像仓库中进行搜索,如果存在则跳过构建直接发布,如果不存在则进行构建步骤再发布。

镜像清理策略
随着镜像越来越多,频繁更新导致Harbor镜像仓库容量很快爆满。
- snapshot仓库:每定时清理几天前的镜像
- release仓库:版本发布完成后,清除版本其他镜像
因为版本分支进行正式环境发布的时候,可能会出现问题。进行修复后镜像仓库中会出现 1.1.1_xxxxxx1,1.1.1_xxxxxx2的镜像标签。假如1.1.1_xxxxxx2是我们正式发布的版本,则发布完成后。清理掉1.1.1_xxxxxx1。
构建容器镜像
编写Dockerfile
FROM nginx:latest COPY dist /usr/share/nginx/html
构建镜像
docker build -t demo-web-app:1.1.1 .
上传镜像
docker push demo-web-app: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 """ } } } } }
构建完成后,运行镜像进行测试
docker run -itd -p 8080:80 --name nginx-server demo/demo-web-app:1.1.1_xxxxxxxx1
测试成功

镜像清理策略
如果我们使用的是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 } } } }