docker - 为 nexus 存储库中的 docker 图像设置自定义清理
问题描述
我一直在使用清理策略处理存储在我们的 nexus 存储库中的 docker 图像。这些对基本行为有好处,在每天(或每小时或我们想要的)运行的任务中配置,如下所示:
- 第一个任务将是管理员类型 - 使用相关策略清理存储库
- 2nd of type Docker - 删除未使用的清单和图像
- Admin类型的第 3 个- 紧凑型 blob 存储
清理策略有一个正则表达式,以避免删除以某种方式标记的某个图像(例如: build-latest),以及最后一次下载的时间(例如: 5 天)。
现在这有助于每 X 天删除一次映像,但只要不存在其他映像,就需要保留一些映像,即如果唯一存在的映像是build-99,请不要删除它,这是我仅使用策略无法做到的。
对于我想要实现的目标,repo 的外观如何:
my-repository
只是一个文件夹名称,默认情况下采用存储库名称,它只是为了演示。
那么您如何管理呢?
注意:有关此处所做操作的信息可以在不同的 SO 帖子或 github 中找到
解决方案
使用每天自动运行的 groovy 脚本,我能够做到这一点。该脚本设置在Admin - Execute script的任务中,默认情况下在 nexus 较新版本中禁用,我在 FAQ 部分中的Scripting Nexus Repository Manager 3以及如何确定 Nexus 3 Data Directory 的位置解决了这个问题。
该脚本基于来自不同地方的文档、问题和代码(例如: StorageTxImpl.java是您可以找到获取/删除资产、组件等的方法的地方)。它也受到这些的启发 Using the Nexus3 API how do I get a list of artifacts in a repository , NEXUS-14837 and Nexus 3 Groovy Script development environment setup
剧本:
该脚本必须在第二个任务之前运行(即等于第一个,之前或之后都没有关系)。这些策略也不再需要,因此它们不再分配给存储库。
它是如何工作或做什么的:
- 获取存储库
- 获取 repo 的组件
- 按名称对它们进行分组(例如: repository/my-repository/some-project/service-A)
- 对于每个服务循环其组件并获取其资产
- 按资产过滤资产
last_downloaded
并仅保留与最近 3 个不匹配的资产,例如 - 删除与资产相关的组件(nexus
deleteComponent(cp)
在内部删除资产及其 blob)
注意:我看到脚本可以参数化,但在我的情况下不需要
注意:这可以更新为循环所有存储库,但我只需要一个
import org.sonatype.nexus.repository.storage.Asset
import org.sonatype.nexus.repository.storage.Query
import org.sonatype.nexus.repository.storage.StorageFacet
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import org.sonatype.nexus.repository.Repository
class RepositoryProcessor {
private final log
private final repository
private final String repoName = 'my-repository'
private final String[] ignoreVersions = ['build-latest']
private final int processIfSizeGt = 3
private final int delAllButMostRecentNImages = 2
RepositoryProcessor(log, repository) {
this.log = log
this.repository = repository
}
void processRepository() {
def repo = repository.repositoryManager.get(repoName)
log.debug("found repository: {}", repo)
// will use default of sonatype
// https://github.com/sonatype/nexus-public/blob/master/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageFacetImpl.java
StorageFacet storageFacet = repo.facet(StorageFacet)
log.debug("initiated storage facet: {}", storageFacet.toString())
// tx of type https://github.com/sonatype/nexus-public/blob/master/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/StorageTxImpl.java $$EnhancerByGuice ??
def transaction = storageFacet.txSupplier().get()
log.debug("initiated transaction instance: {}", transaction.toString())
try {
transaction.begin()
log.info("asset count {}", transaction.countAssets(Query.builder().build(), [repo]))
log.info("components count {}", transaction.countComponents(Query.builder().build(), [repo]))
// queried db is orientdb, syntax is adapted to it
def components = transaction.findComponents(Query.builder()
// .where("NOT (name LIKE '%service-A%')")
// .and("NOT (name LIKE '%service-B%')")
.build(), [repo])
// cp and cpt refers to component
// group by name eg: repository/my-repository/some-project/service-A
def groupedCps = components.groupBy{ it.name() }.collect()
// fetch assets for each cp
// and set them in maps to delete the old ones
groupedCps.each{ cpEntry ->
// process only if its greater than the minimum amount of images per service
if (cpEntry.value.size > processIfSizeGt) {
// single component processing (i.e this would be done for each service)
def cpMap = [:] // map with key eq id
def cpAssetsMap = [:] // map of cp assets where key eq cp id
// process service cpts
cpEntry.value.each { cp ->
// cp id of type https://github.com/sonatype/nexus-public/blob/master/components/nexus-orient/src/main/java/org/sonatype/nexus/orient/entity/AttachedEntityId.java
def cpId = cp.entityMetadata.id.identity
// asset of type: https://github.com/sonatype/nexus-public/blob/master/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/storage/Asset.java
def cpAssets = transaction.browseAssets(cp).collect()
// document of type https://github.com/joansmith1/orientdb/blob/master/core/src/main/java/com/orientechnologies/orient/core/record/impl/ODocument.java
// _fields of type: https://github.com/joansmith1/orientdb/blob/master/core/src/main/java/com/orientechnologies/orient/core/record/impl/ODocumentEntry.java
// any field is of type ODocumentEntry.java
// append to map if it does not belong to the ignored versions
if (!(cp.entityMetadata.document._fields.version.value in ignoreVersions)) {
cpMap.put(cpId, cp)
cpAssetsMap.put(cpId, cpAssets)
}
}
// log info about the affected folder/service
log.info("cp map size: {}, versions: {}",
cpMap.values().size(),
cpMap.values().entityMetadata.document._fields.version.value)
// order desc by last_downloaded (default is asc)
log.debug("cp map assets of size: {}", cpAssetsMap.values().size())
def sortedFilteredList = cpAssetsMap.values()
.sort { it.entityMetadata.document._fields.last_downloaded?.value }
.reverse(true)
.drop(delAllButMostRecentNImages)
// list of cp ids from the assets that going to be deleted
def sortedAssetsCps = sortedFilteredList.entityMetadata.document._fields.component?.value?.flatten()
log.info("cp map assets size after filtering {}", sortedFilteredList.size())
// this will print the cps ids to delete
log.debug("elements to delete : sorted assets cps list {}", sortedAssetsCps)
// deleting components and their assets
cpMap.findAll { it.key in sortedAssetsCps }
.each { entry ->
log.info("deleting cp version {}", entry.value.entityMetadata.document._fields.version?.value)
// this will call delete asset internally, and by default will delete blob
transaction.deleteComponent(entry.value)
}
}
}
transaction.commit();
} catch (Exception e) {
log.warn("transaction failed {}", e.toString())
transaction.rollback()
} finally {
transaction.close();
}
}
}
new RepositoryProcessor(log, repository).processRepository()
推荐阅读
- excel - Formula based on a chart
- amazon-s3 - ng-file-upload - 文件大小限制
- selenium - 无头模式会出错,而非无头模式不会
- python - PyTorch:@weak_script_method 装饰器做什么?
- php - PHP:HTML 到 PHP 多维关联数组
- node.js - googleAuth不是构造函数,google-api版本问题
- dart - Flutter 使用列表选择填充列表视图(向下钻取)
- google-chrome - 寻找旧版 Chrome Dev 的文件
- php - 对渴望加载的关系结果进行分页
- python - Python,Pandas——如何根据高于某个值的多列条件搜索行?