Nexus仓库数据高可用备份与恢复方案(下)
#作者:闫乾苓
文章目录
- Nexus服务中nexus-data 的数据(元数据)备份
- 备份方式一,使用k8s cronjob定时备份nexus数据到minio
- 备份方式一的故障模拟测试
- 备份方式一的故障恢复验证
- 备份方式二,配置实时同步备份
- 备份方式二的故障恢复验证
- 4、注意事项
Nexus服务中nexus-data 的数据(元数据)备份
注意:如果使用CephFS,GlusterFS等分布式文件系统作为nexus的持久化存储,本环节可以忽略。
因本例不具备CephFS,GlusterFS分布式文件存储环境,所以本章节将介绍如何对Nexus存储到 NFS中的数据进行有效的备份和恢复测试。
只要确保从NFS中备份出数据能在nfs-server 故障并有数据损坏时进行有效恢复,就能保证nexus的数据高可用。Blob Store中的数据由minio保障其高可用。
nexus-data 数据目录已经通过NFS storageClass 定义的pvc和pv进行了动态绑定,存储到NFS server中了,需要对这部分数据有效的备份。建议同时使用以下2中备份方式,以确保备份数据的有效性,
因绝大部分的的二级制构件数据都存储到了minio中,只有少部分的的元数据会存储到nexus-data目录,所以执行nexus-data目录数据的全量备份并不会占用大量的磁盘空间。
备份方式一,使用k8s cronjob定时备份nexus数据到minio
优点:
1.备份前停止服务,以确保数据的完整性
2.可根据实际业务需要,制定灵活的备份策略
3.每次均为全量备份
4.提供临时hostpath和minio两重冗余备份机制,增强备份数据的有效性
缺点:
1.备份期间须停止服务,会短暂中断业务
2.不能确保数据完全不丢失,最大的数据丢失间隔通常等于备份任务的时间间隔
CronJob备份策略及流程
本章节介绍使用k8s cronjob对nexus存储在nfs中的数据进行备份,可根据具体的业务数据高可用要求,制定不同的备份策略,比如:滚动备份最近7天的数据,每天1个全量备份
,流程如下:
- cronjob会按照cronjob YAML中设置的时间,生成jobs
- jobs会启动备份容器,启动后容器会挂载nexus服务YAML中定义pvc到容器中
- 容器中执行backup-script.sh脚本, 脚本使用kubect scale命令将replicas设置为0,即关闭nexus服务确保下一步打包备份数据时数据的有效性。
- nexus服务关闭后,使用tar命令打包备份nexus元数据并存放到临时目录
- 打包完成后为减少nexus服务不可用时间,立即使用kubectl scale将replicas设置为1,即启动nexus服务。
- 脚本使用minio的mc命令将tar备份文件上传到minio,以确保备份数据的安全性和有效性。
- 脚本自动删除超过7天的备份文件。
容器中执行nexus扩缩容的账号及权限
因为需要在cronjob定时备份启动的容器中执行nexus服务的关闭和启动(scale 0或1),所以需要配置账号的RBAC权限。
service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:name: nexus-backup-cronjob-serviceaccountnamespace: nexus-2 # 替换为你的命名空间
role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:name: nexus-backup-cronjob-rolenamespace: nexus-2
rules:
- apiGroups: ["apps"]resources: ["deployments"]verbs: ["get", "update", "list"]
- apiGroups: ["apps"]resources: ["deployments/scale"]verbs: ["get", "update", "patch"]
role-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:name: cronjob-rolebindingnamespace: nexus-2 # 替换为你的命名空间
subjects:
- kind: ServiceAccountname: nexus-backup-cronjob-serviceaccountnamespace: nexus-2 # 替换为你的命名空间
roleRef:kind: Rolename: nexus-backup-cronjob-roleapiGroup: rbac.authorization.k8s.io
将YAML应用到系统中:
# kubectl apply -f service-account.yaml -f role.yaml -f role-binding.yaml
serviceaccount/nexus-backup-cronjob-serviceaccount created
role.rbac.authorization.k8s.io/nexus-backup-cronjob-role created
rolebinding.rbac.authorization.k8s.io/cronjob-rolebinding created
备份脚本
因为此脚本是在alpine容器中执行,alpine没有bash环境,所以此脚本首行改成/bin/sh
backup-script.sh
#!/bin/sh# 替换为你的 Deployment 名称和命名空间
DEPLOYMENT_NAME="devops-nexus"
NAMESPACE="nexus-2"
KUBECTL="/root/kubectl"stop_nexus() {
# 获取当前副本数
CURRENT_REPLICAS=$(${KUBECTL} get deployment $DEPLOYMENT_NAME -n $NAMESPACE -o jsonpath='{.spec.replicas}')# 检查当前副本数并执行相应的操作
if [ "$CURRENT_REPLICAS" -eq 1 ]; then# 如果当前副本数为1,则执行缩容到0的操作NEW_REPLICAS=0echo "Scaling $DEPLOYMENT_NAME in $NAMESPACE down from $CURRENT_REPLICAS to $NEW_REPLICAS replicas..."#/usr/local/bin/kubectl scale deployment $DEPLOYMENT_NAME -n $NAMESPACE --replicas=$NEW_REPLICAS${KUBECTL} scale deployment $DEPLOYMENT_NAME -n $NAMESPACE --replicas=$NEW_REPLICASif [ $? -eq 0 ]; thenecho "Scaled $DEPLOYMENT_NAME in $NAMESPACE to $NEW_REPLICAS replicas successfully."elseecho "Failed to scale $DEPLOYMENT_NAME in $NAMESPACE." >&2exit 1fi
else# 如果当前副本数不为1,则给出提示并以非0状态退出echo "Error: Cannot scale down $DEPLOYMENT_NAME in $NAMESPACE because current replicas is $CURRENT_REPLICAS (not 1)." >&2exit 1
fi
}start_nexus() {
# 获取当前副本数
CURRENT_REPLICAS=$(${KUBECTL} get deployment $DEPLOYMENT_NAME -n $NAMESPACE -o jsonpath='{.spec.replicas}')if [ "$CURRENT_REPLICAS" -eq 0 ]; then# 如果需要扩容并且当前副本数为0,则执行扩容到1的操作NEW_REPLICAS=1echo "Scaling $DEPLOYMENT_NAME in $NAMESPACE up from $CURRENT_REPLICAS to $NEW_REPLICAS replicas..."${KUBECTL} scale deployment $DEPLOYMENT_NAME -n $NAMESPACE --replicas=$NEW_REPLICASif [ $? -eq 0 ]; thenecho "Scaled $DEPLOYMENT_NAME in $NAMESPACE to $NEW_REPLICAS replicas successfully."elseecho "Failed to scale $DEPLOYMENT_NAME in $NAMESPACE." >&2exit 1fi
else# 当前副本数不为0,则给出提示(如果需要的话)if [ "$CURRENT_REPLICAS" -ne 0 ]; thenecho "Error: Cannot scale up $DEPLOYMENT_NAME in $NAMESPACE because current replicas is $CURRENT_REPLICAS (not 0)." >&2exit 1fi
fi
}# 定义变量
SOURCE_DIR="/mnt/data"
MINIO_ENDPOINT="http://minio-service.minio.svc.cluster.local:29000"
MINIO_ACCESS_KEY="65WNgqImKSD3c0qX"
MINIO_SECRET_KEY="n7iXpeOECUX3u3OOmzGPmS9M7JT4Y2nd"
MINIO_BUCKET="nexus-data-backup"
DATE=$(date +%Y-%m-%d)
BACKUP_FILE="nexus-data-bak-${DATE}.tar.gz"
LOCAL_BACKUP_DIR="/tmp/backup"
MINIO_BACKUP_PATH="/${BACKUP_FILE}"# 创建本地备份目录(如果不存在)
mkdir -p ${LOCAL_BACKUP_DIR}# 为确保数据的完整性,备份数据前需要将nexus deployment 的replicas 设置为0,即关闭nexus服务。
stop_nexus# 执行备份
tar -czf ${LOCAL_BACKUP_DIR}/${BACKUP_FILE} -C ${SOURCE_DIR} .# 检查备份是否成功,如果失败脚本退出并恢复nexus服务。
if [ $? -ne 0 ]; thenecho "Backup failed " >&2# 即使在执行数据tar 打包失败,在脚本退出前,使用函数将nexus服务恢复start_nexusexit 1
fi# 备份成功,使用函数及时恢复nexus服务
start_nexus# 设置 MinIO 别名
mc alias set minio ${MINIO_ENDPOINT} ${MINIO_ACCESS_KEY} ${MINIO_SECRET_KEY}# 检查别名设置是否成功
if [ $? -ne 0 ]; thenecho "Failed to set MinIO alias" >&2exit 1
fi# 上传备份文件到 MinIO
mc cp ${LOCAL_BACKUP_DIR}/${BACKUP_FILE} minio/${MINIO_BUCKET}/${MINIO_BACKUP_PATH}# 检查上传是否成功
if [ $? -ne 0 ]; thenecho "Upload to MinIO failed" >&2exit 1
fi# 计算7天前的日期(使用GNU date)
# 注意:确保您的系统支持GNU date,否则需要找到替代方法
SEVEN_DAYS_AGO=$(date -d "7 days ago" '+%Y-%m-%d')
SEVEN_DAYS_AGO_BACKUP="nexus-data-bak-${SEVEN_DAYS_AGO}.tar.gz"# 删除7天前的备份文件
mc rm minio/${MINIO_BUCKET}/${SEVEN_DAYS_AGO_BACKUP}
rm -f ${LOCAL_BACKUP_DIR}/${SEVEN_DAYS_AGO_BACKUP}echo "Backup and cleanup completed successfully"
CronJob中使用的images镜像的Dockerfile
Dockerfile
FROM alpine:latest# coreutils 用于提供完成的date命令
RUN apk add --no-cache tar curl coreutils# 安装 MinIO Client (mc)
RUN curl -L https://dl.min.io/client/mc/release/linux-amd64/mc -o /usr/local/bin/mc && \chmod +x /usr/local/bin/mc# 创建备份目录
RUN mkdir -p /mnt/data /tmp/backupWORKDIR /root# 复制备份脚本、kubectl到容器中(用于执行nexus副本数的扩缩容)
COPY backup-script.sh kubectl .# 设置备份脚本为可执行
RUN chmod +x backup-script.sh kubectl# 设置容器启动时执行的命令
CMD ["./backup-script.sh"]
docker_build.sh
#!/bin/bashDATE=`date +%F-%H%M%S`#请自行修改harbo仓库地址
docker build -t 192.168.123.240:8011/yql/nexus-backup-cronjob:v${DATE} .
docker push 192.168.123.240:8011/yql/nexus-backup-cronjob:v${DATE}
CronJob YAML
cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:name: daily-full-backupnamespace: nexus-2
spec:schedule: "35 16 * * *"successfulJobsHistoryLimit: 7 # 保留7个成功的任务历史记录failedJobsHistoryLimit: 1 # 保留1个失败的任务历史记录jobTemplate:spec:template:metadata:labels:app: backupspec:# nodeName: 192.168.123.249 # 可以指定调度的node节点serviceAccountName: nexus-backup-cronjob-serviceaccountcontainers:- name: backup-containerimage: 192.168.123.240:8011/yql/nexus-backup-cronjob:v2024-11-20-163322imagePullPolicy: IfNotPresentvolumeMounts:- mountPath: /mnt/dataname: data-volume- mountPath: /tmp/backupname: backup-volume- mountPath: /etc/localtimename: localtimereadOnly: trueresources:limits:cpu: 1memory: 2Girequests:cpu: 0.5memory: 1GirestartPolicy: OnFailurevolumes:- name: data-volumepersistentVolumeClaim:claimName: nexus-pvc # nexus-data源数据的 PVC 名称- name: backup-volumehostPath:path: /tmp/nexus-data-backup # 指定宿主机上的路径type: DirectoryOrCreate- name: localtimehostPath:path: /etc/localtimetype: File
# kubectl apply -f cronjob.yaml
查看cronjob执行的过程
查看备份数据是否上传到minio
查看pod被调度到的节点临时文件目录是否有备份文件:
查看文件是否上传到minio
备份方式一的故障模拟测试
将正常运行的容器部署的nexus服务的NFS PVC绑定的PV中的数据删除,模拟元数据丢失。
备份方式一的故障恢复验证
使用k8s cronjob定时备份nexus数据到minio的备份包进行恢复。
将从minio中下载的备份包上传到nfs server 临时目录
注意:因nexus 数据丢失,k8s检测到nexus服务异常会自动重启nexus 的pod,nexus会重新初始化产生新的数据文件,此时需要先将pod副本数手动设置为0,停止服务,再将初始化的数据删除,再使用备份包进行数据恢复。
pod被k8s自动重启
手动设置pod副本数为0,关闭nexusf服务。
手动删除pod重启后重新初始化的数据文件
将备份数据包解压到nexus nfs pv目录
将pod副本数恢复成1
web页面查看nexus数据已恢复
备份方式二,配置实时同步备份
在nfs server上安装sersync(sersync是国人基于rsync+inotify-tools开发的文件实时同步工具,github地址:https://github.com/wsgzao/sersync),实时监视nexus nfs pvc中文件的变更,将文件实时同步到安装了rsyncd服务的目的备份服务器。
目的备份服务器安装配置rsyncd服务
检查服务器是否已安装rsync
# which rsync
/usr/bin/rsync
如何没有安装,使用yum 安装
# yum install rsync
创建配置文件 /etc/rsyncd.conf
dport = 873
uid = root
gid = root
use chroot = no
max connetctions = 200
timeout = 100
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
log file = /var/log/rsyncd.log
[backup]
path = /data/nexus-nfs-backup/
ignore errors
read only = false
list = false
hosts allow = 192.168.123.0/24
hosts deny = 0.0.0.0/32
auth users = rsync_backup
secrets file = /etc/rsync.passwor
创建配置文件中secrets file 指定的账号密码文件/etc/rsync.password
账号和密码在一行,中间用冒号分隔。
# cat /etc/rsync.password
rsync_backup:passwd123
需要将/etc/rsync.password 文件权限设置为600
# chmod 600 /etc/rsync.password
# ll /etc/rsync.password
-rw------- 1 root root 23 11月 22 09:28 /etc/rsync.password
创建存放备份数据的目录,与配置文件中path 参数指定一致
# mkdir /data/nexus-nfs-backup
启动服务
# rsync --daemon
查看服务进程及端口号
# ps -ef |grep "rsync --daemon"
root 17991 1 0 11:30 ? 00:00:00 rsync --daemon# netstat -ntlp |grep 873
tcp 0 0 0.0.0.0:873 0.0.0.0:* LISTEN 17991/rsync
tcp6 0 0 :::873 :::* LISTEN 17991/rsync
关闭服务使用kill -9 xxx
关闭服务器后再启动服务器需要手动删除pid文件,否则不能启动,有提示。
# rm -f /var/run/rsyncd.pid
在nfs server安装配置sersync
sersync二进制包下载地址:https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/sersync/sersync2.5.4_64bit_binary_stable_final.tar.gz
解压:
# tar xf sersync2.5.4_64bit_binary_stable_final.tar.gz
# ll
drwxr-xr-x 2 root root 4.0K 10月 26 2011 GNU-Linux-x86# 移动到/opt目录
# mv GNU-Linux-x86/ /opt/sersync
配置文件confxml.xml修改如下:
配置文件中指定的保存密码的文件,只有密码,没有账号:
rsync.pass
passwd123
设置为600权限
# chmod 600 rsync.pass
# ls -l
-rw------- 1 root root 10 11月 22 09:33 rsync.pass
启动服务:
# nohup /opt/sersync/sersync2 -r -d /opt/sersync/confxml.xml > /opt/sersync/rsync.log
在查看数据同步
备份方式二的故障模拟测试
和备份方式一的故障模拟类似,也是将nexus nfs pvc绑定的pv中的数据手动删除,模拟元数据丢失。
备份方式二的故障恢复验证
将nexus pod副本数设置为0,停止nexus服务。
手动删除因k8s检测到nexus pod故障,自动重启pod后重新生成的初始化数据文件
将rsyncd备份服务器上备份数据同步回来到nexus nfs pvc绑定的pv目录
再将pod副本数恢复为1,启动nexus服务
通过web页面查看nexus数据已恢复
4、注意事项
1.nexus服务只部署1个pod副本,其本身的高可用依赖在pod中配置健康检查探针的方式由k8s deployment予以保障,具体配置细节请自行查找相关资料。
2.本方案只针对nexus保存在nfs pvc绑定的pv中的元数据进行高可用保证,二进制构件数据通过Blob Stores 保存在minio分布式文件存储系统,由minio确保二级制构件文件的数据高可用。
3.在出现nexus元数据丢失故障时,请优先使用sersync实时备份的数据进行恢复。如其不能正常恢复,再使用cronjob定时备份的数据进行恢复。使用此备份数据进行恢复,会造成备份间隔时段内的数据丢失。
4.实际生产环境中直接删除所有数据文件的情况应该比较少见,大部分情况应该为部分数据丢失或者损坏,在使用备份数据进行恢复前,请先对现存数据进行备份,以便在数据恢复失败时进行回滚操作。