导语:记录一下相关规范。以防后续用到时找不到
容器镜像管理规范
命名规范
- 仓库类型
- 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。
主要通过以下2条命令获取短位的COMMITID,生成tag
GIT_COMMITID= sh( returnStdout: true, script: 'echo -n `git rev-parse --short HEAD`')
Tag="${BRANCH}_${GIT_COMMITID}"
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
"""
}
}
}
}
}
构建容器镜像
编写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
}
}
}
}
参考
https://cloud.tencent.com/developer/article/1599849
https://github.com/zeyangli