容器化項目鏡像從構建到清理完整生命周期管理
- 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 } } } }