Nexus 迁移blob到AWS S3

公司的Nexus部署在AWS EC2上, 因为存储了很多内部的Docker image等等制品, 因此导致容量越来越大, 已经扩容到2T了, 鉴于成本的考量, 计划将这些制品迁移到AWS S3上, 后续不光可以通过S3的生命周期管理, 而且可以不用再无限制扩容现有的环境了, 因此记录一下, 以便后续查阅

Nexus 3.x blob 如何从file 迁移至 s3_Makun1995的博客-CSDN博客_nexus 迁移blob

创建现有EC2的AMI

以防对现有生产环境造成不可逆的影响, 因此先创建一个当前环境的AMI, 从AMI拉起一个新实例进行测试操作

创建AMI
填写AMI信息

记住一定unckecked无重启的选项, 避免数据出现异常, 这个过程比较缓慢, 不用傻等, 提前准备其他资源

创建S3存储桶

进入Nexus的blob设置页面, 尝试创建一个类型为S3的blob

Nexus设置页面

观察下图, 可以发现几个必填信息

S3 blob的信息

因此需要创建一个与该EC2同region的S3桶(避免跨Region产生额外费用), 首先查看EC2所属的区域信息

EC2区域信息

创建需求的桶

创建S3桶

创建IAM相关资源

创建IAM Policy

执行最小权限原则1, 为该EC2添加一个Role用于执行S3的操作, Role依赖于Policy, 因此需要先创建Policy

创建IAM Policy

策略中仅需包含必要权限: ListBucket+GetObject+DeleteObject+PutObject

🆙2022-06-02更新

解决Bucket exists but is not owned by you报错

增加权限GetBucketAcl+GetLifecycleConfiguration+PutLifecycleConfiguration

增加额外权限

🆙2022-06-03更新

根据官方手册推荐, 增加权限PutObjectTagging+GetObjectTagging+DeleteObjectTagging

按照官方手册增加额外权限

创建IAM Role

按照如下图示创建即可

创建一个用于EC2的Role
关联上一步创建的Policy
填写名称和描述

创建IAM User(可选操作)

点击展开

按照图示创建即可

添加IAM User信息
附加已有的Policy
保存AKSK信息

一定注意保存AKSK信息, 该信息会在关闭页面后无法显示, 因此一定妥善保存

同步数据到S3

通过AMI拉起新EC2实例(过程略), 为该EC2附加刚刚创建的IAM Role

添加Role

搜索并添加刚刚创建的Role

确定附加IAM Role

使用SSH登录测试EC2, 切换至root用户, 定位到blob的目录

定位到blob目录

执行S3同步命令, 将存储blob的文件同步至S3桶

1
aws --region cn-north-1 s3 sync . s3://gw-prod-nexus/

其中需要指定region信息, 否则可能会提示权限异常哈, 剩下的就是等待同步完成即可

创建测试blob

使用测试EC2启动Nexus服务, 并登录web控制台, 添加一个测试S3的blob, 如下图

创建测试blob

提取S3类型blob的配置信息

Accessing the OrientDB Console – Sonatype Support

  1. 找到并使用官方配置支持工具

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 金风的nexus是以普通用户创建的, 因此需要切换到普通用户进行操作
    su - nexus

    # 设置环境变量
    NEXUS_HOME="/data/nexus"

    # 查看当前JAVA版本
    java -version
    # ----
    # > java version "1.8.0_281"
    # > Java(TM) SE Runtime Environment (build 1.8.0_281-b09)
    # > Java HotSpot(TM) 64-Bit Server VM (build 25.281-b09, mixed mode)
    #.----

    # 打开支持客户端工具
    java -jar ${NEXUS_HOME}/lib/support/nexus-orient-console.jar
    # ----
    # > OrientDB console v.2.2.36 (build d3beb772c02098ceaea89779a7afd4b7305d3788, branch 2.2.x) https://www.orientdb.com
    # > Type 'help' to display all the supported commands.
    # > orientdb>
    # ----

  2. 打开配置文件, 我这里是/data/sonatype-work/nexus3/db/config, 默认的管理员账号密码: admin/admin

    1
    connect plocal:/data/sonatype-work/nexus3/db/config admin admin

    输出信息一览, 提示的warning不用理会, 因为没关闭服务导致

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    OrientDB console v.2.2.36 (build d3beb772c02098ceaea89779a7afd4b7305d3788, branch 2.2.x) https://www.orientdb.com
    Type 'help' to display all the supported commands.
    orientdb>

    orientdb> connect plocal:/data/sonatype-work/nexus3/db/config admin admin

    Connecting to database [plocal:/data/sonatype-work/nexus3/db/config] with user 'admin'...
    2022-06-02 07:58:28:350 WARNI {db=config} Storage 'config' was not closed properly. Will try to recover from write ahead log... [OLocalPaginatedStorage]
    2022-06-02 07:58:28:356 WARNI {db=config} Record OFuzzyCheckpointStartRecord{lsn=LSN{segment=928, position=1166251}} com.orientechnologies.orient.core.storage.impl.local.paginated.wal.OFuzzyCheckpointStartRecord{lsn=null, previousCheckpoint=LSN{segment=928, position=1159588}} will be skipped during data restore [OLocalPaginatedStorage]
    2022-06-02 07:58:28:357 WARNI {db=config} Record com.orientechnologies.orient.core.storage.impl.local.paginated.wal.OFuzzyCheckpointEndRecord{lsn=LSN{segment=928, position=1166291}} will be skipped during data restore [OLocalPaginatedStorage]OK
    orientdb {db=config}>

    查看当前默认blob的配置信息select * from repository_blobstore, 如下输出结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    orientdb {db=config}> select * from repository_blobstore

    +----+-----+--------------------+-------+----+---------------------------------------------------------------------------------------------------+
    |# |@RID |@CLASS |name |type|attributes |
    +----+-----+--------------------+-------+----+---------------------------------------------------------------------------------------------------+
    |0 |#19:0|repository_blobstore|default|File|{file={path=default}} |
    |1 |#20:0|repository_blobstore|test-s3|S3 |{s3={region=cn-north-1, bucket=gw-prod-nexus, prefix=test, expiration=-1}, blobStoreQuotaConfig={}}|
    +----+-----+--------------------+-------+----+---------------------------------------------------------------------------------------------------+

    2 item(s) found. Query executed in 0.009 sec(s).

其中, 测试的S3类型blob, 可以看到的信息如下表, 其中粗体并加*内容为必填项

配置项 备注
region* cn-north-1 区域名称
bucket* gw-prod-nexus 桶名
prefix test 传到S3的文件夹名称
expiration -1 blob内容过期天数, 设置为-1避免执行清理
blobStoreQuotaConfig {} Soft Quota信息

修改默认blob

将Nexus停机后, 先准备S3信息, 需知悉以下内容, 其中粗体并加*内容为必填项

配置项 备注
region* cn-north-1 区域名称
bucket* gw-prod-nexus 桶名
prefix default 传到S3的文件夹名称, 此处设置为default便于后续创建额外blob
expiration -1 blob内容过期天数, 设置为-1避免执行清理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 修改默认类型为S3
update repository_blobstore set type="S3" where name="default"
# ----
# > Updated record(s) '1' in 0.041000 sec(s).
# ----

# 修改S3的属性
update repository_blobstore set attributes.s3={region: 'cn-north-1', bucket: 'gw-prod-nexus', prefix: 'default', expiration: -1} where name = "default"

# ----
# > Updated record(s) '1' in 0.005000 sec(s).
# ----

# 见后文问题, 需增加额外更新语句
update repository_blobstore set attributes.blobStoreQuotaConfig={} where name = "default"


# 查看修改后的结果
select * from repository_blobstore

# ----
# > +----+-----+-----------------+----+-------+---------------------------------------------------------------------------------+
# > |# |@RID |@CLASS |type|name |attributes |
# > +----+-----+-----------------+----+-------+---------------------------------------------------------------------------------+
# > |0 |#19:0|repository_blo...|S3 |default|{s3={bucket=gw-prod-nexus, prefix=default, expiration=-1, region=cn-north-1}, ...|
# > |1 |#20:0|repository_blo...|S3 |test-s3|{s3={region=cn-north-1, bucket=gw-prod-nexus, prefix=test, expiration=-1}, blo...|
# > +----+-----+-----------------+----+-------+---------------------------------------------------------------------------------+
# >
# > 2 item(s) found. Query executed in 0.001 sec(s).
# ----

# 保存退出
exit

验证修改结果

重新启动Nexus服务, 登录web页面查看变更情况

🔧2022-06-02修复: blob无法识别

结果展示
默认blob已经迁移完成

部署生产系统

  1. 为了避免数据出现异常, 建议先将S3数据进行清理, 或者使用新的prefix, 避免生产数据异常

    清空桶数据
  2. 登录生产环境的EC2配置Role并同步S3数据

  3. 停止生产环境的Nexus服务systemctl stop nexus

  4. 再次同步S3数据

  5. 执行以下命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    # 金风的nexus是以普通用户创建的, 因此需要切换到普通用户进行操作
    su - nexus

    # 设置环境变量
    NEXUS_HOME="/data/nexus"

    # 备份配置文件
    cp -r /data/sonatype-work/nexus3/db/config /data/sonatype-work/nexus3/db/config.$(date +%Y%m%d)

    # 查看当前JAVA版本
    java -version

    # 打开支持客户端工具
    java -jar ${NEXUS_HOME}/lib/support/nexus-orient-console.jar

    # 打开配置文件
    connect plocal:/data/sonatype-work/nexus3/db/config admin admin

    # 修改默认类型为S3
    update repository_blobstore set type="S3" where name="default"

    # 将属性清空
    update repository_blobstore set attributes={} where name = "default"

    # 修改S3的属性
    update repository_blobstore set attributes.s3={region: 'cn-north-1', bucket: 'gw-prod-nexus', prefix: 'default', expiration: -1} where name = "default"

    update repository_blobstore set attributes.blobStoreQuotaConfig={} where name = "default"

    # 查看修改后的结果
    select * from repository_blobstore

    # 保存退出
    exit

    # 切回root用户
    exit

  6. 启动生产环境的Nexus服务systemctl start nexus

  7. 登录web界面, 验证blob是否正确识别

    生产Nexus服务正常
  8. 登录Jenkins, 尝试发布验证上传是否正常

    发布正常

错误处理

Bucket exists but is not owned by you.

无法创建blob

创建测试blob的时候出现了这个问题

  1. 怀疑Nexus无法获取该EC2挂载的IAM Role
    1. 尝试添加同样权限的IAM User并填写Authentication下的AKSK信息后仍然报错
    2. 尝试使用最高权限AKSK信息, 问题得到解决

使用CloudTrail排查问题

针对以上解决情况, 打开控制台并导航至CloudTrail Management Console, 查询刚刚输入的最高权限AK信息, 并缩小周期为30m, 如下图

定位请求事件名称

可以发现, 使用到了额外权限:

  • GetBucketAcl
  • DeleteBucketLifecycle
  • GetBucketLifecycle

对应IAM权限控制为:

  • GetBucketAcl
  • GetLifecycleConfiguration
  • PutLifecycleConfiguration

该答案内容已同步至Bucket exists but is not owned by you - Nexus Repository Manager - Sonatype Community

官方手册

🆙2022-06-02更新

Configuring Blob Stores

官方文档有最小权限解释, 需以下权限:

  • s3:PutObject
  • s3:GetObject
  • s3:DeleteObject
  • s3:ListBucket
  • s3:GetLifecycleConfiguration
  • s3:PutLifecycleConfiguration
  • s3:PutObjectTagging
  • s3:GetObjectTagging
  • s3:DeleteObjectTagging
  • s3:GetBucketAcl ( used for problem diagnosis )

com.orientechnologies.orient.core.record.impl.ODocument cannot be cast to java.util.Map

blob报错
系统无法找到blob配置
  1. 初步怀疑博客中语句存在问题

    通过对比测试blob, 貌似缺少一些属性信息, 见下图

    缺少blobStoreQuotaConfig信息

    增加语句

    1
    update repository_blobstore set attributes.blobStoreQuotaConfig={} where name = "default"

    问题解决!

    识别到S3

生产Nexus配置文件属性问题

问题描述

使用如下命令对生产系统实施变更

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 金风的nexus是以普通用户创建的, 因此需要切换到普通用户进行操作
su - nexus

# 设置环境变量
NEXUS_HOME="/data/nexus"

# 备份配置文件
cp -r /data/sonatype-work/nexus3/db/config /data/sonatype-work/nexus3/db/config.$(date +%Y%m%d)

# 查看当前JAVA版本
java -version

# 打开支持客户端工具
java -jar ${NEXUS_HOME}/lib/support/nexus-orient-console.jar

# 打开配置文件
connect plocal:/data/sonatype-work/nexus3/db/config admin admin

# 修改默认类型为S3
update repository_blobstore set type="S3" where name="default"

# 修改S3的属性
update repository_blobstore set attributes.s3={region: 'cn-north-1', bucket: 'gw-prod-nexus', prefix: 'default', expiration: -1} where name = "default"

update repository_blobstore set attributes.blobStoreQuotaConfig={} where name = "default"

# 查看修改后的结果
select * from repository_blobstore

# 保存退出
exit

# 切回root用户
exit

🚨查询结果与预期不符!

属性存在异常!

可以发现仍然存在file={path=default}的属性

  1. 尝试执行如下命令修改属性

    1
    2
    3
    4
    # 更新属性信息
    update repository_blobstore set attributes={s3: {region: 'cn-north-1', bucket: 'gw-prod-nexus', prefix: 'default', expiration: -1}, blobStoreQuotaConfig: {}} where name = "default"
    # 查询修改结果
    select * from repository_blobstore

    覆盖了file属性, 但是仍与预期不符
  2. 再次重新执行更新语句

    1
    2
    3
    4
    5
    6
    update repository_blobstore set attributes.s3={region: 'cn-north-1', bucket: 'gw-prod-nexus', prefix: 'default', expiration: -1} where name = "default"

    update repository_blobstore set attributes.blobStoreQuotaConfig={} where name = "default"

    # 查询修改结果
    select * from repository_blobstore

    结果与预期一致

问题排查

  1. 复现生产系统更新过程中遇到的配置文件问题, 首先创建一个生产环境原始配置文件副本进行测试

    1
    2
    # 创建原始配置文件副本
    cp -r /data/sonatype-work/nexus3/db/config.$(date +%Y%m%d) /data/sonatype-work/nexus3/db/config_for_test

  2. 使用官方给出的OrientDB2客户端链接到原始数据库

    1
    2
    3
    4
    5
    6
    7
    8
    # 设置环境变量
    NEXUS_HOME="/data/nexus"

    # 打开支持客户端工具
    java -jar ${NEXUS_HOME}/lib/support/nexus-orient-console.jar

    # 打开配置文件
    connect plocal:/data/sonatype-work/nexus3/db/config_for_test admin admin

  3. 尝试先对attributes字段值置空

    1
    update repository_blobstore set attributes= NULL where name = "default"

    提示异常, 字段不允许为NULL
  4. 尝试先对attributes字段值置为{}

    1
    update repository_blobstore set attributes={} where name = "default"

    可以设置为空字典
  5. 再次执行更新语句

    1
    2
    3
    4
    5
    6
    update repository_blobstore set attributes.s3={region: 'cn-north-1', bucket: 'gw-prod-nexus', prefix: 'default', expiration: -1} where name = "default"

    update repository_blobstore set attributes.blobStoreQuotaConfig={} where name = "default"

    # 查询修改结果
    select * from repository_blobstore

    达到预期效果

结论

需要在更新语句之前插入置为空字典语句以剔除file属性, 完整更新语句如下

1
2
3
4
5
6
7
8
9
10
# 修改默认类型为S3
update repository_blobstore set type="S3" where name="default"

# 将属性清空
update repository_blobstore set attributes={} where name = "default"

# 修改S3的属性
update repository_blobstore set attributes.s3={region: 'cn-north-1', bucket: 'gw-prod-nexus', prefix: 'default', expiration: -1} where name = "default"

update repository_blobstore set attributes.blobStoreQuotaConfig={} where name = "default"

  1. 关于最小权限原则_百度百科↩︎

  2. OrientDB_百度百科↩︎