• Nexus-获取Repositories / Blobstores实际用量


    获取基于文件的Blob的Repositories / Blobstores存储大小

    此方法使用groovy脚本获取Repositories / Blobstores的实际使用大小。

    通过Nexus Repository Manager控制台,创建自定义groovy脚本的Admin - Execute scripte任务。

    1.Nexus 3.21.2及以上版本开启Admin - Execute script任务

    Admin - Execute scripte任务,在nexus 3.21.2版本及以上默认是关闭的(由于安全性考虑)。所以需要自己通过配置文件参数开启。nexus 3.21.1及以下的版本,默认是开启的,且不可关闭。

    1.1修改配置文件

    配置文件:{data-dir}/etc/nexus.properties

    nexus.scripts.allowCreation=true

    1.2重启nexus生效

    重启期间nexus会不可用,需要停机窗口或者业务闲时重启。否则会产生业务影响。

    {data-dir}/bin/nexus restart

    2.创建任务获取Repositories / Blobstores存储大小

    2.1创建任务

    登陆Administration > System > Tasks > Create task > Admin - Execute Script

    Groovy脚本可以通过REPOSITORY_WHITELIST或REPOSITORY_BLACKLIST控制要获取的仓库的白名单或黑名单。

    Language:groovy

    Task frequency:Manual为手动执行

    2.2手动执行task

    任务执行后nexus日志会出现以下日志,包括结果输出位置。

    1. *SYSTEM Script47 - Blob Storage scan STARTED.
    2. *SYSTEM Script47 - Scanning /home/nexus/sonatype-work/nexus3/blobs/default
    3. *SYSTEM Script47 - Scanning /opt/nexus/test2
    4. *SYSTEM Script47 - Scanning /home/nexus/sonatype-work/nexus3/blobs/test1
    5. *SYSTEM Script47 - Blob Storage scan ENDED. Report at /home/nexus/sonatype-work/nexus3/tmp/repoSizes-20181213-104154.json

    输出结果类似如下内容。下面的输出显示了两个 blob 存储,每个存储都有一个存储库:

    1. {
    2. "blobstore1": {
    3. "repositories": {
    4. "repositoryA": {
    5. "reclaimableBytes": 0,
    6. "totalBytes": 4173387
    7. }
    8. },
    9. "totalBlobStoreBytes": 4173387,
    10. "totalReclaimableBytes": 0,
    11. "totalRepoNameMissingCount": 0
    12. },
    13. "blobstore2": {
    14. "repositories": {
    15. "repositoryB": {
    16. "reclaimableBytes": 0,
    17. "totalBytes": 1397598
    18. }
    19. },
    20. "totalBlobStoreBytes": 1397598,
    21. "totalReclaimableBytes": 0,
    22. "totalRepoNameMissingCount": 0
    23. }
    24. }

    对于每个存储库,totalbytes表示正在使用多少空间, reclaimableBytes表示通过运行Compact Blob Store维护任务可以回收多少空间。

    对于每个 blob 存储,所有存储库条目都被聚合。totalRepoNameMissingCount将显示 Blob 存储中有多少资产与不再存在的存储库相关联。

    经测试,此脚本需要顺序执行。也就是说,创建多个task执行这个脚本的话,多个task也是顺序执行。需要等前一个task计算完所有blob的repository大小后,才开始计算下一个task。

    附官方此groovy脚本内容 nx-blob-repo-space-report-20220510.groovy

    1. /*
    2. * Sonatype Nexus (TM) Open Source Version
    3. * Copyright (c) 2008-present Sonatype, Inc.
    4. * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
    5. *
    6. * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
    7. * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
    8. *
    9. * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
    10. * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
    11. * Eclipse Foundation. All other trademarks are the property of their respective owners.
    12. */
    13. /*
    14. * Utility script that scans blobstores and reads the asset properties files within to summarize which repositories
    15. * are using the blob store, and how much space each is consuming and how much space could potentially be reclaimed by
    16. * running a compact blobstore task.
    17. *
    18. * The script retrieves the blobstore locations from the Nexus system and also all defined repositories.
    19. *
    20. * It is possible to specify a whitelist of repository names *OR* a blacklist (whitelist takes priority)
    21. * If a whitelist is provided, only those repositories whitelisted will be included.
    22. * If a blacklist is provided (and no whitelist), any repositories that are blacklisted will be omitted.
    23. *
    24. * Any empty repositories are also included.
    25. *
    26. * The script tabulates both the total size, and the size that could be reclaimed by performing a compact blob store
    27. * task.
    28. *
    29. * Script was developed to run as an 'Execute Script' task within Nexus Repository Manager.
    30. *
    31. * ==== CHANGE LOG ====
    32. * May 10, 2022
    33. * - fix Windows path matching
    34. * May 9, 2022
    35. * - fix for proper rethrow handling exception caused by failed properties file processing
    36. * - reduce heap memory by not putting all paths to blob properties files into List object
    37. * - improve file path matcher patterns to exclude non .properties files and only include files under ./content
    38. */
    39. /* ---------------- BEGIN CONFIGURABLE SECTION -------------*
    40. * Whitelist - a list of repository names that should be the only items included.
    41. *
    42. * For example: REPOSITORY_WHITELIST = ['maven-central', 'npm-hosted']
    43. */
    44. REPOSITORY_WHITELIST = []
    45. /* Blacklist - a list of repository names that should not be included.
    46. * This will only apply if REPOSITORY_WHITELIST is not set
    47. *
    48. * For example: REPOSITORY_BLACKLIST = ['maven-central', 'npm-hosted']
    49. */
    50. REPOSITORY_BLACKLIST = []
    51. /* ---------------- END CONFIGURABLE SECTION ---------------*/
    52. import groovy.json.JsonOutput
    53. import java.nio.file.FileSystems
    54. import java.nio.file.Path
    55. import java.nio.file.PathMatcher
    56. import java.text.SimpleDateFormat
    57. import org.slf4j.LoggerFactory
    58. import org.sonatype.nexus.common.app.ApplicationDirectories
    59. import org.sonatype.nexus.internal.app.ApplicationDirectoriesImpl
    60. import static groovy.io.FileType.FILES
    61. def log = LoggerFactory.getLogger(this.class)
    62. ApplicationDirectories applicationDirectories =
    63. (ApplicationDirectories)container.lookup(ApplicationDirectoriesImpl.class.name)
    64. Map blobStoreDirectories = [:]
    65. hasWhitelist = REPOSITORY_WHITELIST.size() > 0
    66. hasBlacklist = !hasWhitelist && REPOSITORY_BLACKLIST.size() > 0
    67. String SEP = FileSystems.getDefault().getSeparator()
    68. if ('\\' == SEP) {
    69. SEP = "${SEP}${SEP}" // escape back slashes on windows so path matchers work correctly
    70. log.info("Treating file system as using Windows path separators.")
    71. }
    72. def EXCLUDE_PATTERNS = "glob:{" +
    73. "**${SEP}metadata.properties," +
    74. "**${SEP}*metrics.properties," +
    75. "**${SEP}*.bytes," +
    76. "**${SEP}tmp*," +
    77. "**${SEP}*deletions.index," +
    78. "**${SEP}*.DS_Store}"
    79. log.info("Global Blobstore exclude patterns: {}", EXCLUDE_PATTERNS)
    80. PathMatcher EXCLUDE_MATCHER = FileSystems.getDefault().getPathMatcher(EXCLUDE_PATTERNS)
    81. //Default location of results is the Nexus temporary directory
    82. File resultsFileLocation = applicationDirectories.getTemporaryDirectory()
    83. Map blobStatCollection = [:].withDefault { 0 }
    84. class BlobStatistics
    85. {
    86. int totalRepoNameMissingCount = 0
    87. long totalBlobStoreBytes = 0
    88. long totalReclaimableBytes = 0
    89. Map repositories = [:]
    90. }
    91. class RepoStatistics {
    92. long totalBytes = 0
    93. long reclaimableBytes = 0
    94. }
    95. def collectMetrics(final BlobStatistics blobstat, Set unmapped,
    96. final Properties properties, final File propertiesFile) {
    97. def repo = properties.'@Bucket.repo-name'
    98. if(repo == null && properties.'@BlobStore.direct-path') {
    99. repo = 'SYSTEM:direct-path'
    100. }
    101. if(repo == null) {
    102. // unexpected - log the unexpected condition
    103. if(blobstat.totalRepoNameMissingCount <= 50){
    104. log.warn('Repository name missing from {} : {}', propertiesFile.absolutePath, properties)
    105. log.info('full details: {}', properties)
    106. }
    107. blobstat.totalRepoNameMissingCount++
    108. } else {
    109. if (!blobstat.repositories.containsKey(repo)) {
    110. if (!unmapped.contains(repo)) {
    111. if (!repo.equals('SYSTEM:direct-path')) {
    112. log.info('Found unknown repository in {}: {}', propertiesFile.absolutePath, repo)
    113. }
    114. blobstat.repositories.put(repo as String, new RepoStatistics())
    115. }
    116. }
    117. if (blobstat.repositories.containsKey(repo)) {
    118. blobstat.repositories."$repo".totalBytes += (properties.size as long)
    119. if (!repo.equals('SYSTEM:direct-path')) {
    120. blobstat.totalBlobStoreBytes += (properties.size as long)
    121. }
    122. if (properties.'deleted') {
    123. blobstat.repositories."$repo".reclaimableBytes += (properties.size as long)
    124. if (!repo.equals('SYSTEM:direct-path')) {
    125. blobstat.totalReclaimableBytes += (properties.size as long)
    126. }
    127. }
    128. }
    129. }
    130. }
    131. def passesWhiteBlackList(final String name) {
    132. if (hasWhitelist) {
    133. return REPOSITORY_WHITELIST.contains(name)
    134. }
    135. if (hasBlacklist) {
    136. return !REPOSITORY_BLACKLIST.contains(name)
    137. }
    138. return true
    139. }
    140. Map> storeRepositoryLookup = [:].withDefault { [:] }
    141. repository.getRepositoryManager().browse().each { repo ->
    142. def blobStoreName = repo.properties.configuration.attributes.storage.blobStoreName
    143. storeRepositoryLookup.get(blobStoreName).put(repo.name, passesWhiteBlackList(repo.name))
    144. }
    145. blobStore.getBlobStoreManager().browse().each { blobstore ->
    146. //check that this blobstore is not a group (3.15.0+)
    147. if (blobstore.getProperties().getOrDefault('groupable',true)) {
    148. //S3 stores currently cannot be analysed via this script, so ignore (3.12.0+)
    149. if (blobstore.getProperties().get("blobStoreConfiguration").type == "S3") {
    150. log.info("Ignoring blobstore {} as it is using S3",
    151. blobstore.getProperties().get("blobStoreConfiguration").name);
    152. }
    153. else {
    154. try {
    155. blobstoreName = blobstore.getProperties().get("blobStoreConfiguration").name
    156. blobStoreDirectories[blobstoreName] = blobstore.getProperties().get("absoluteBlobDir").toFile()
    157. }
    158. catch (Exception ex) {
    159. log.warn('Unable to add blobstore {} of type {}: {}',
    160. blobstore.getProperties().get("blobStoreConfiguration").name,
    161. blobstore.getProperties().get("blobStoreConfiguration").type, ex.getMessage())
    162. log.info('details: {}', blobstore.getProperties())
    163. }
    164. }
    165. }
    166. else {
    167. log.info("Ignoring blobstore {} as it is a group store",
    168. blobstore.getProperties().get("blobStoreConfiguration").name);
    169. }
    170. }
    171. log.info('Blob Storage scan STARTED.')
    172. blobStoreDirectories.each { blobStore ->
    173. Path contentDir = blobStore.value.toPath().resolve('content')
    174. log.info('Scanning blobstore {}, root dir {}, content dir {}', blobStore.key, blobStore.value.absolutePath, contentDir)
    175. BlobStatistics blobStat = new BlobStatistics()
    176. Set unmapped = new HashSet<>()
    177. storeRepositoryLookup[blobStore.value.getName()].each { key, value ->
    178. if (value) {
    179. blobStat.repositories.put(key, new RepoStatistics())
    180. } else {
    181. unmapped.add(key)
    182. }
    183. }
    184. def blobstoreDir = new File(blobStore.value.path)
    185. def includePattern = "glob:**${SEP}${blobstoreDir.getName()}${SEP}content${SEP}**${SEP}*.properties"
    186. PathMatcher INCLUDE_MATCHER = FileSystems.getDefault().getPathMatcher(includePattern)
    187. log.info("Looking for blob properties files matching: ${includePattern}")
    188. contentDir.eachFileRecurse(FILES) { p ->
    189. if (!EXCLUDE_MATCHER.matches(p) && INCLUDE_MATCHER.matches(p) ) {
    190. File propertiesFile = p.toFile()
    191. def properties = new Properties()
    192. try {
    193. propertiesFile.withInputStream { is ->
    194. properties.load(is)
    195. }
    196. } catch (FileNotFoundException ex) {
    197. log.warn("File not found '{}', skipping", propertiesFile.getCanonicalPath())
    198. } catch (Exception e) {
    199. log.error('Unable to process {}', propertiesFile.getAbsolutePath(), e)
    200. throw e
    201. }
    202. collectMetrics(blobStat, unmapped, properties, propertiesFile)
    203. }
    204. }
    205. blobStatCollection.put(blobStore.value.getName(), blobStat)
    206. }
    207. blobStatCollection.each() { blobStoreName, blobStat ->
    208. RepoStatistics directPath = blobStat.repositories.remove('SYSTEM:direct-path')
    209. if (directPath!=null) {
    210. log.info("Direct-Path size in blobstore {}: {} - reclaimable: {}", blobStoreName, directPath.totalBytes, directPath.reclaimableBytes)
    211. }
    212. }
    213. def filename = "repoSizes-${new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date())}.json"
    214. File resultsFile = new File(resultsFileLocation, filename)
    215. resultsFile.withWriter { Writer writer ->
    216. writer << JsonOutput.prettyPrint(JsonOutput
    217. .toJson(blobStatCollection
    218. .findAll {a, b -> b.repositories.size() > 0}
    219. .toSorted {a, b -> b.value.totalBlobStoreBytes <=> a.value.totalBlobStoreBytes}))
    220. }
    221. log.info('Blob Storage scan ENDED. Report at {}', resultsFile.absolutePath)

    参考文档

    1.Nexus 3.21.2及以上版本开启Admin - Execute script任务

    https://support.sonatype.com/hc/en-us/articles/360045220393https://support.sonatype.com/hc/en-us/articles/360045220393

    2.获取基于文件的Blob的Repositories / Blobstores存储大小

    https://support.sonatype.com/hc/en-us/articles/115009519847-Investigating-Blobstore-Space-Usage?_ga=2.232065503.1144877504.1662517207-784654775.1661480883#ListingtheSizeofFileBasedBlobsinRepositories/Blobstoreshttps://support.sonatype.com/hc/en-us/articles/115009519847-Investigating-Blobstore-Space-Usage?_ga=2.232065503.1144877504.1662517207-784654775.1661480883#ListingtheSizeofFileBasedBlobsinRepositories/Blobstores

  • 相关阅读:
    ElasticSearch 04 -- 集群
    DHCP原理和配置
    TSINGSEE青犀智能分析网关V4有限空间作业监护AI算法介绍及应用
    C++基础知识记录
    git -- 清除本地分支以及删除远程分支
    FPGA+ARM异核架构,基于米尔MYC-JX8MMA7核心板的全自动血细胞分析仪
    【论文解读系列】NER方向:LatticeLSTM (ACL2018)
    基于Python实现多项式拟合正弦函数
    Matlab中 * 与 .* 的区别
    【java学习—八】==操作符与equals方法(2)
  • 原文地址:https://blog.csdn.net/qq522044637/article/details/126779944