本文发布于Cylon的收藏册,转载请著名原文链接~
本文将介绍 Harbor 从 v1.10.7 升级到 v2.10.0,以及如何将 Harbor 从 v2.10 回滚到 v1.10.7。
升级条件
- Linux服务器
- 4 个 CPU 和 8 GB 内存(强要求),100G可用空间(跨多版本时存放备份文件以及镜像文件,这部分要求)
- Docker-compose > 1.19.0+
- 备份现有的 Harbor /data/database 目录
本次升级主要是使用了 harbor 内置的数据库,所以升级步骤比较容易。
官方升级路线
harbor 的升级,是不能跨很多版本进行升级,官方对此有详细说明 [1] ,可以看到路线为:
1.10.0 [1] => 2.4.0 [2] => 2.6.0 [3] => 2.8.0 [4] => 2.10.0 [5]
模拟升级步骤
github release 页下载对应的安装包
解压
# 命令主要为将harbor压缩包内文件解压到指定目录中,由于 harbor 解压后文件名无论版本如何都为“harbor”
$ mkdir ./harbor-v1.10 && tar -xf harbor-offline-installer-v1.10.0.tgz -C ./harbor-v1.10 --strip-components 1
备份默认的配置文件(仅限于 v1.10.x,v2.x均为 harbor.tmpl)
cp harbor.yml harbor.yaml.backup
清除注释
grep -Ev '^#|^$|^\s*(#|//)' harbor.yml.tmpl > harbor.yml.clean_annotation
修改一些默认配置
\cp -a harbor.yml.clean_annotation harbor.yml
# 如果需要则关闭https
sed -i '/https:/,+3s/.*/# &/' harbor.yml
# 替换默认harbor郁闷
sed -i "s@hostname: reg.mydomain.com@hostname: img.test.com@g" harbor.yml
# 修改默认目录
sed -i "s@data_volume: /data@data_volume: /data/harbor@g" harbor.yml
启动服务
./install.sh
升级步骤
在升级前首先要缕清升级的内容,官方在升级时存在两个步骤,配置文件升级与数据库 schema 升级;并且需要知道升级的路线图,这里是从 1.10.0 升级至本文撰写时最新版本 2.10.0,所以查看官方升级路线为 1.10.0 => 2.4.0 => 2.6.0 => 2.8.0 => 2.10.0。总结升级所需变更如下:
- harbor的配置文件升级
- 数据库 schema 升级,由 harbor-core 组件自动完成
- 升级路线:1.10.0 => 2.4.0 => 2.6.0 => 2.8.0 => 2.10.0
备份当前 harbor 版本
备份当前版本是为了如果需要回滚的话,可以快速的回滚到所需的版本
cd harbor
docker-compose down
备份 Harbor 的当前文件,以便您可以在必要时回滚到当前版本。
备份数据库文件
我们知道了,升级主要是对数据库 schema 进行reschema,Harbor 的每次新版本发布时新的功能及对老功能、代码的重构都会导致数据库模型的变更,因此几乎每次升级都需要升级数据库模式。配置文件数据,是指 Harbor 组件的配置文件,在部分新功能或者新的组件出现时,都需要在配置文件中新增其参数;在老功能、组件重构或者废弃时,也会对配置文件进行更新。
cp -r /data/database /my_backup_dir/
reschema的工作是由 harbor-core 完成的,所以我们只需要备份即可,当新版本在启动时,第一次会 reschema,这个步骤的时间会随着 harbor 的使用量而增加,这里数据库目录为 13G,1.10.7 => 2.4.0 时间大概在20分钟左右。
harbor的配置文件升级
harbor 配置的升级是需要手动执行的,命令是包含在 offline 安装包中,被包含在 “goharbor/prepare:v2.x.0” 镜像中。用户可以在 Harbor 的离线安装包中找到它,也可以在 Docker Hub 上获取,官方给出升级指南中的命令如下
# 1.10
docker run -it --rm -v ./harbor.yml:/harbor-migration/harbor-cfg/harbor.yml goharbor/harbor-migrator:v1.10.0 --cfg up
# 2.4
# 后的yaml文件必须是旧版本的
# 这步骤是将旧的 harbor.yaml 配置文件升级到新版本
# 升级后旧版本的配置文件就没有了,如果需要需要自行备份
# docker run -it --rm -v /:/hostfs goharbor/prepare:[tag] migrate -i ${path to harbor.yml}
docker run -it --rm -v /:/hostfs goharbor/prepare:v2.4.0 migrate -i ./harbor.yml
“-v /:/hostfs” 是将主机的根目录 “/” 挂载到容器中的 “/hostfs” 目录中。因为命令是运行在容器中的,而文件是在宿主机上的,为了能在容器中访问到指定的文件,需要这样挂载,之后 prepare 会对 “/hostfs” 这个文件做特殊处理,使得在容器里也能访问主机上的指定文件。
”-i“ 是指定旧版本的 harbor 配置文件
migrate 命令有如下3个参数。
–input(缩写形式为“-i”):是输入文件的绝对路径,也就是需要升级的原配置文件。
–output(缩写形式为“-o”):是输出文件的绝对路径,也是升级后的配置文件,是可选参数,如果取默认值,则升级后的文件会被写回输入文件中。
–target(缩写形式为“-t”):是目标版本,也就是打算升级到的版本,也是可选参数,如果取默认值,则版本为此工具发布时所支持的最新版本。
这里我们可以使用如下命令
docker run -v :/hostfs goharbor/prepare:v2.4.0 migrate -i home/harbor/upgrade/harbor.yml
升级成功会有如下输出
migrating to version 2.0.0
migrating to version 2.1.0
migrating to version 2.2.0
migrating to version 2.3.0
migrating to version 2.4.0
Written new values to home/harbor/upgrade/harbor.yml
启动新服务
在新版本 harbor 目录中,运行 ./install.sh
脚本来安装新的 Harbor 实例,这里会导入离线安装包,生成配置文件,启动服务等操作
替换 docker-compose 文件
docker-compose 的生成是在 prepare 脚本中执行的,可以看出,是调用的 prepare 镜像
# Run prepare script
docker run --rm -v $input_dir:/input \
-v $data_path:/data \
-v $harbor_prepare_path:/compose_location \
-v $config_dir:/config \
-v /:/hostfs \
--privileged \
goharbor/prepare:dev prepare $@
这种情况下,如果我们需要自定义的 docker-compose.yaml 就可以挂在到对应目录即可,模板文件可以在 photon/prepare/templates/docker_compose 处下载进行替换。
替换后,使用 sed 命令,替换 prepare 脚本中的启动命令即可。
sed -i "/-v \/:\/hostfs/a \\\t\\t -v /root/docker_compose/docker-compose.yml.jinjia.${version}:/usr/src/app/templates/docker_compose/dockercompose.yml.jinjia \\\\" ./prepare
批量升级脚本
#!/bin/bash
# 根据当前版本和目标版本选择对应的下载地址
declare -A harbor_versions=(
["v1.10.0"]="https://github.com/goharbor/harbor/releases/download/v1.10.0/harbor-offline-installer-v1.10.0.tgz"
["v2.4.0"]="https://github.com/goharbor/harbor/releases/download/v2.4.0/harbor-offline-installer-v2.4.0.tgz"
["v2.6.1"]="https://github.com/goharbor/harbor/releases/download/v2.6.1/harbor-offline-installer-v2.6.1.tgz"
["v2.8.0"]="https://github.com/goharbor/harbor/releases/download/v2.8.0/harbor-offline-installer-v2.8.0.tgz"
["v2.10.0"]="https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz"
)
#
# Set Colors
#
bold=$(tput bold)
underline=$(tput sgr 0 1)
reset=$(tput sgr0)
red=$(tput setaf 1)
green=$(tput setaf 76)
white=$(tput setaf 7)
tan=$(tput setaf 202)
blue=$(tput setaf 25)
#
# Headers and Logging
#
underline() { printf "${underline}${bold}%s${reset}\n" "$@"
}
h1() { printf "\n${underline}${bold}${blue}%s${reset}\n" "$@"
}
h2() { printf "\n${underline}${bold}${white}%s${reset}\n" "$@"
}
debug() { printf "${white}%s${reset}\n" "$@"
}
info() { printf "${white}➜ %s${reset}\n" "$@"
}
success() { printf "${green}✔ %s${reset}\n" "$@"
}
error() { printf "${red}✖ %s${reset}\n" "$@"
}
warn() { printf "${tan}➜ %s${reset}\n" "$@"
}
bold() { printf "${bold}%s${reset}\n" "$@"
}
note() { printf "\n${underline}${bold}${blue}Note:${reset} ${blue}%s${reset}\n" "$@"
}
set -e
# 设置根目录和工作目录
ROOT_DIR=$(cd $(dirname $0); pwd)
export ROOT_DIR
# 设置步骤变量
usage()
{
cat <<EOF
Usage: ${CMD} [ OPTION ]
Commands:
Some commands take arguments or -h for usage.
-d download harbor rolling dependencies offline installer package
-u upgrade harbor to latest
-r rollback to old version
-h this message
EOF
return 0
}
compare_versions()
{
local current_version=$1
local target_version=$2
if [[ $current_version == v* ]]; then
current_version=${current_version#v}
fi
if [[ $target_version == v* ]]; then
target_version=${target_version#v}
fi
if [[ $current_version == "$target_version" ]]; then
return 2
fi
IFS='.' read -ra current_version_parts <<< "$current_version"
IFS='.' read -ra target_version_parts <<< "$target_version"
for (( i=0; i<${#current_version_parts[@]}; i++ )); do
if (( ${target_version_parts[$i]} > ${current_version_parts[$i]} )); then
return 0
elif (( ${target_version_parts[$i]} < ${current_version_parts[$i]} )); then
return 1
fi
done
return 2
}
set_env()
{
# 设置工作目录
WORK_DIR="${ROOT_DIR}/_work"
# 获取当前版本和目标版本
read -p "Please input current version [1.10.0]: " CURRENT_VERSION
CURRENT_VERSION=${CURRENT_VERSION:-v1.10.0}
if [[ ${CURRENT_VERSION:0:1} != "v" ]]; then
CURRENT_VERSION="v${CURRENT_VERSION}"
fi
read -p "Please input target version [2.10.0]: " TARGET_VERSION
TARGET_VERSION=${TARGET_VERSION:-v2.10.0}
if [[ ${TARGET_VERSION:0:1} != "v" ]]; then
TARGET_VERSION="v${TARGET_VERSION}"
fi
read -p "Please input harbor path [/root/harbor-v1.10]: " CURR_HARBOR_PATH
CURR_HARBOR_PATH=${CURR_HARBOR_PATH:-/root/harbor-v1.10}
read -p "Please input harbor database [/data/harbor]: " DATA_DIR
DATA_DIR=${DATA_DIR:-/data/harbor}
# 设置备份目录
BACKUP_DIR="${ROOT_DIR}/harbor_backup"
# 当前版本的备份路径
CURRENT_VERSION_BACKUP_PATH="$BACKUP_DIR/${CURRENT_VERSION}"
export WORK_DIR CURRENT_VERSION TARGET_VERSION CURR_HARBOR_PATH DATA_DIR BACKUP_DIR CURRENT_VERSION_BACKUP_PATH
versions=""
sorted_versions=""
export versions sorted_versions
}
set_env_rollback()
{
# 设置工作目录
WORK_DIR="${ROOT_DIR}/_work"
# 获取当前版本和目标版本
read -p "Please input current version [2.10.0]: " CURRENT_VERSION
CURRENT_VERSION=${CURRENT_VERSION:-v2.10.0}
if [[ ${CURRENT_VERSION:0:1} != "v" ]]; then
CURRENT_VERSION="v${CURRENT_VERSION}"
fi
read -p "Please input target version [1.10.0]: " TARGET_VERSION
TARGET_VERSION=${TARGET_VERSION:-v1.10.0}
if [[ ${TARGET_VERSION:0:1} != "v" ]]; then
TARGET_VERSION="v${TARGET_VERSION}"
fi
read -p "Please input old harbor dirname [harbor-v1.10.0]: " CURR_HARBOR_PATH
CURR_HARBOR_PATH=${CURR_HARBOR_PATH:-harbor-v1.10.0}
read -p "Please input harbor database [/data/harbor]: " DATA_DIR
DATA_DIR=${DATA_DIR:-/data/harbor}
# 设置备份目录
BACKUP_DIR="${ROOT_DIR}/harbor_rollback_backup"
# 当前版本的备份路径
CURRENT_VERSION_BACKUP_PATH="$BACKUP_DIR/${CURRENT_VERSION}"
export WORK_DIR CURRENT_VERSION TARGET_VERSION CURR_HARBOR_PATH DATA_DIR BACKUP_DIR CURRENT_VERSION_BACKUP_PATH
}
initialize_workspace()
{
# 创建工作目录(如果不存在)
[ -d ${WORK_DIR} ] || mkdir -pv ${WORK_DIR}
# 创建备份目录(如果不存在)
[ -d ${BACKUP_DIR} ] || mkdir -pv ${BACKUP_DIR}
}
clean_annotation()
{
find ${WORK_DIR}/${version}/ \
-name "harbor.yml.*" \
! -name "harbor.yml.clean_annotation" \
! -name "harbor.yml.tmpl" -type f -exec \
grep -Ev '^#|^$|^\s*(#|//)' {} + > ${WORK_DIR}/${version}/harbor.yml.clean_annotation
}
replace_configuration()
{
cp -a ${WORK_DIR}/${version}/harbor.yml.clean_annotation ${WORK_DIR}/${version}/harbor.yml
sed -i "s@hostname: reg.mydomain.com@hostname: your-harbor-domain.com@g" ${WORK_DIR}/${version}/harbor.yml
sed -i "s@data_volume: /data@data_volume: ${DATA_DIR}@g" harbor.yml
}
compare_versions()
{
local current_version=$1
local target_version=$2
if [[ $current_version == v* ]]; then
current_version=${current_version#v}
fi
if [[ $target_version == v* ]]; then
target_version=${target_version#v}
fi
if [[ $current_version == $target_version ]]; then
return 2
fi
IFS='.' read -ra current_version_parts <<< "$current_version"
IFS='.' read -ra target_version_parts <<< "$target_version"
for (( i=0; i<${#current_version_parts[@]}; i++ )); do
if (( ${target_version_parts[$i]} > ${current_version_parts[$i]} )); then
return 0
elif (( ${target_version_parts[$i]} < ${current_version_parts[$i]} )); then
return 1
fi
done
return 2
}
check_version_number()
{
set +e
# 校验版本号
compare_versions "$version" "${CURRENT_VERSION}"
if [ $? -eq 0 ]; then
warn "${CURRENT_VERSION} Greater than ${version}."
continue
fi
compare_versions "$version" "$CURRENT_VERSION"
if [ $? -eq 2 ]; then
warn "${TARGET_VERSION} equal ${version}."
continue
fi
set -e
}
swtich_version()
{
CURRENT_VERSION=${version:-$CURRENT_VERSION}
# 当前版本的备份路径
CURRENT_VERSION_BACKUP_PATH="${BACKUP_DIR}/${CURRENT_VERSION}"
# 当前harbor的启动路径
CURR_HARBOR_PATH="${WORK_DIR}/${CURRENT_VERSION}"
export CURRENT_VERSION CURRENT_VERSION_BACKUP_PATH CURR_HARBOR_PATH
}
pause()
{
success "Press enter to continue..."
read -r
}
sorted_version()
{
# 获取harbor版本列表
versions=("${!harbor_versions[@]}")
# 对版本列表进行排序
sorted_versions=($(printf '%s\n' "${versions[@]}" | sort -V))
export versions sorted_versions
}
initial_backup_dir()
{
h2 "[Backup initialization]: Starting backup ${CURRENT_VERSION} ..."
# 创建备份目录(如果不存在)
[ -d ${CURRENT_VERSION_BACKUP_PATH} ] || mkdir -pv ${CURRENT_VERSION_BACKUP_PATH}
[ -d ${CURRENT_VERSION_BACKUP_PATH}"_database" ] || mkdir -pv ${CURRENT_VERSION_BACKUP_PATH}"_database"
[ -d ${CURRENT_VERSION_BACKUP_PATH}"_redis" ] || mkdir -pv ${CURRENT_VERSION_BACKUP_PATH}"_redis"
note "backup dir is: ${CURRENT_VERSION_BACKUP_PATH} is checked."
note "backup database dir ${CURRENT_VERSION_BACKUP_PATH}_database is checked."
}
backup_current_version()
{
h2 "[Backup progess]: starting backup harbor ${CURRENT_VERSION} ..."
# 备份harbor
cp -r ${CURR_HARBOR_PATH} ${CURRENT_VERSION_BACKUP_PATH}/
# 备份harbor数据目录的database
cp -Rpf ${DATA_DIR}/database ${CURRENT_VERSION_BACKUP_PATH}"_database"
cp -Rpf ${DATA_DIR}/redis ${CURRENT_VERSION_BACKUP_PATH}"_redis"
# 备份 harbor.yml 防止升级被覆盖从而无法回滚
cp ${CURR_HARBOR_PATH}/harbor.yml ${CURRENT_VERSION_BACKUP_PATH}/harbor.yml.$(date +%F)
note "harbor ${CURRENT_VERSION} is backup completed, in ${CURRENT_VERSION_BACKUP_PATH}"
}
stop_old_harbor_progress()
{
# 停止旧版容器组
h2 "[Progress stop]: stopping ${CURRENT_VERSION} ..."
cd ${CURR_HARBOR_PATH} && docker-compose down && cd ${ROOT_DIR} && note "harbor ${CURRENT_VERSION} is stopped ..."
}
upgrade_configfile()
{
h2 "[Upgrade]: upgrade ${CURRENT_VERSION} to ${version} ..."
docker run -v /:/hostfs goharbor/prepare:${version} migrate -i ${CURRENT_VERSION_BACKUP_PATH}/harbor.yml.$(date +%F) -o ${WORK_DIR}/${version}/harbor.yml
note "harbor config version is ${version}"
}
rollback()
{
step=0
h2 "[Step $step]: set up rollback env ..."; let step+=1
set_env_rollback
# 停止容器
h2 "[Progress stop]: stopping ${CURRENT_VERSION} ..."
cd ${WORK_DIR}/${CURRENT_VERSION}/ && docker-compose down && cd ${ROOT_DIR} && note "harbor ${CURRENT_VERSION} is stopped ..."
# 备份当前版本
initial_backup_dir
backup_current_version
FILE_NAME=${WORK_DIR}/${CURRENT_VERSION}
# 检查工作目录是否存在已下载文件
if [ ! -d ${FILE_NAME} ]; then
error "${CURRENT_VERSION} not found"
exit 1
fi
h2 "[Step $step]: starting switch harbor ${TARGET_VERSION} ..." ; let step+=1
rm -fr ${DATA_DIR}/database && rm -fr ${DATA_DIR}/redis && \
cp -Rpf "${ROOT_DIR}/harbor_backup/${TARGET_VERSION}_database/database" ${DATA_DIR}/database
cp -Rpf "${ROOT_DIR}/harbor_backup/${TARGET_VERSION}_redis/redis" ${DATA_DIR}/redis
note "Switch database to ${TARGET_VERSION} is completed."
sleep $((RANDOM % 6 + 10))
h2 "[Step $step]: starting harbor ${TARGET_VERSION} ..."
cd "${ROOT_DIR}/harbor_backup/${TARGET_VERSION}/${CURR_HARBOR_PATH}" && ./install.sh
success $"----Harbor ${CURRENT_VERSION} to ${TARGET_VERSION} has been rollback.----"
}
download()
{
sorted_version
# 更新harbor
for version in "${sorted_versions[@]}"; do
DOWNLOAD_URL=${harbor_versions[$version]}
FILE_NAME=$(basename $DOWNLOAD_URL)
FOLDER_NAME=${FILE_NAME%.*}
ARCHIVE_FILE="${ROOT_DIR}/$FILE_NAME"
if [ -f ${ARCHIVE_FILE} ]; then
warn "${FILE_NAME} existed, skip download.."
continue
fi
note "[Downloader]: ${harbor_versions[$version]}"
wget "${harbor_versions[$version]}"
done
}
rotate_upgrade_harbor_versions()
{
# 获取harbor版本列表
sorted_version
# 更新harbor
for version in "${sorted_versions[@]}"; do
# 向下传递变量
export version
h2 "[Install ${version}]: starting upgrade ..."
# 检查更新是否合法
# 如果当前版本等于要更新的版本,则不更新
check_version_number
# 停止旧版本的服务
stop_old_harbor_progress
# 备份当前版本 harbor
initial_backup_dir
backup_current_version
h2 "[install checking]: Starting install checking ..."
# 检查offline安装包是否下载
DOWNLOAD_URL=${harbor_versions[$version]}
FILE_NAME=$(basename $DOWNLOAD_URL)
FOLDER_NAME=${FILE_NAME%.*}
ARCHIVE_FILE="${ROOT_DIR}/$FILE_NAME"
# 检查工作目录是否存在已下载文件
if [ ! -f ${ARCHIVE_FILE} ]; then
error "Offline installer ${FILE_NAME} not found,Please download first ${DOWNLOAD_URL}"
exit 1
fi
# 创建对应版本的工作目录(如果不存在)
[ -d "${WORK_DIR}"/"${version}" ] || mkdir -pv "${WORK_DIR}"/"${version}"
note "install checked"
h2 "[Uncompress]: starting uncompress harbor offline installer ..."
# 解压对应版本安装包
mkdir -pv "${WORK_DIR}"/"${version}" && tar -xf "${ROOT_DIR}"/"${FILE_NAME}" -C "${WORK_DIR}"/"${version}"/ --strip-components 1
note "harbor ${version}: ${WORK_DIR}/${version}"
# 导入镜像
set +e
h2 "[Load image]: starting load harbor ${version} ..."
cd "${WORK_DIR}/${version}" && docker image load -i harbor."${version}".tar.gz
note "harbor image loaded"
set -e
h2 "[Replace configration]: start replace default config file ..."
# 清除 harbor.yml 中的注释
clean_annotation
# 替换为所需的config
replace_configuration
note "replaced"
# 更新操作
upgrade_configfile
h2 "[Installer]: start install harbor ${version} ..."
# 如果使用了定制化 docker-compose 则开启这行
# 使用准备好的模板来更换容器内部的模板
# 这样保证了可以随意定制 docker-compose的文件
cd "${WORK_DIR}/${version}" && sed -i "/-v \/:\/hostfs/a \\\t\\t -v /root/docker_compose/docker-compose.yml.jinjia.${version}:/usr/src/app/templates/docker_compose/dockercompose.yml.jinjia \\\\" ./prepare
cd "${WORK_DIR}/${version}" && ./install.sh
note "${version} installed"
success $"----Harbor ${CURRENT_VERSION} to ${version} has been upgraded.----"
# 切换变量,把这次更新好的版本作为下次要更新的旧版本进行传递
swtich_version
pause
done
# 完成升级
success $"----Harbor ${TARGET_VERSION} has been installed and started successfully.----"
}
upgrade()
{
step=0
h2 "[Step $step]: set up env ..."; (( step+1 ))
set_env
# 初始化目录
h2 "[Step $step]: initailizaion workspace ..."; (( step+1 ))
initialize_workspace
# 滚动版本更新harbor
h2 "[Step $step]: Starting upgrade gradually ..."; (( step+1 ))
rotate_upgrade_harbor_versions
}
MAIN(){
if [ $# -eq 0 ]; then
warn "Non option ..."
usage
exit 1
fi
while getopts "duhr" option; do
case ${option} in
d)
download
R=$?
;;
u)
upgrade
R=$?
;;
r)
rollback
R=$?
;;
h)
usage
R=$?
;;
\?)
usage
;;
esac
done
exit ${R}
}
MAIN ${@}
Reference
[1] Upgrade Harbor and Migrate Data - v1.10.0
[2] Upgrade Harbor and Migrate Data - v2.4.0
[3] Upgrade Harbor and Migrate Data - v2.6.0
[4] Upgrade Harbor and Migrate Data - v2.8.0
[5] Upgrade Harbor and Migrate Data - v2.10.0
[6] Prepare 脚本
[6] Upgrade Harbor from v1.10.7 to v2.4.0 then 2.6.0
本文发布于Cylon的收藏册,转载请著名原文链接~
链接:https://www.oomkill.com/2024/03/upgrade-harbor/
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」 许可协议进行许可。