# 手动部署

# 单机部署

# 系统环境

硬件平台:x64_64/amd64

操作系统:almalinux 8.8/9.2

硬件配置:2核CPU、4G内存、100G磁盘、20T的NFS共享盘

软件配置:docker 24.0.6、docker-compose 2.21.0、harbor 2.9.0/2.9.1

部署程序:二进制包

# 相关下载地址

docker

https://github.com/moby/moby/releases (opens new window)

docker-compose

https://github.com/docker/compose/releases (opens new window)

harbor

https://github.com/goharbor/harbor/releases (opens new window)

arm64平台架构参考

  1. # https://dhexx.cn/news/show-4454220.html?action=onClick

# 设置主机名映射

提前在 DNS 服务器上添加 m.oso.plus 的 A 记录。当然,没有 DNS 服务器时,可使用本地解析即可。

echo "192.168.100.69 harbor r.k8s.lan" >> /etc/hosts

# 挂载 nfs 共享

本环境使用 NAS 机上提供的 NFS 共享存储。

如果要部署NFS服务端,可参考以下文档:https://www.yuque.com/kemp/it/tpn6n4yhr2ham7i7 (opens new window)

yum install -y nfs-utils

mkdir -p /data/{docker,harbor}

mount -t nfs 192.168.100.254:/volume3/k8s/docker /data/docker
mount -t nfs 192.168.100.254:/volume3/k8s/harbor /data/harbor

# NFS 服务端无需密码登录时
echo "192.168.100.254:/volume3/k8s/docker /data/docker nfs defaults,_netdev 0 0" >>/etc/fstab
echo "192.168.100.254:/volume3/k8s/harbor /data/harbor nfs defaults,_netdev 0 0" >>/etc/fstab

# NFS 服务端要求密码登录时
echo "192.168.100.254:/volume3/k8s/docker /data/docker nfs defaults,_netdev,username=admin,password=110110 0 0" >>/etc/fstab
echo "192.168.100.254:/volume3/k8s/harbor /data/harbor nfs defaults,_netdev,username=admin,password=110110 0 0" >>/etc/fstab

# 配置证书

# 证书颁发机构证书
openssl genrsa -out ca.key 4096
# CA证书
openssl req -x509 -new -nodes -sha512 -days 3650 -subj "/C=CN/ST=Guizhou/L=Guiyang/O=Example/OU=Personal/CN=r.oso.plus" -key ca.key -out ca.crt

# 服务器证书私钥
openssl genrsa -out r.oso.plus.key 4096
# 证书签名请求 (CSR)
openssl req -sha512 -new -subj "/C=CN/ST=Guizhou/L=Guiyang/O=Example/OU=Personal/CN=r.oso.plus" -key r.oso.plus.key -out r.oso.plus.csr

# x509 v3扩展文件 DNS.2=registry.k8s.lan
cat > v3.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1=r.oso.plus
DNS.2=h.k8s.lan
DNS.3=hostname
EOF

# 使用 v3.ext 给 harbor 主机生成证书
openssl x509 -req -sha512 -days 3650 -extfile v3.ext -CA ca.crt -CAkey ca.key -CAcreateserial -in r.oso.plus.csr -out r.oso.plus.crt

# 复制证书到 harbor 主机的 certs 目录下
mkdir -p /data/harbor/certs
cp r.oso.plus.crt /data/harbor/certs
cp r.oso.plus.key /data/harbor/certs

# 转换 r.oso.plus.crt 为 r.oso.plus.key.cert 供 docker 使用
openssl x509 -inform PEM -in r.oso.plus.crt -out r.oso.plus.cert

# 将服务器证书、密钥、CA文件复制到 harbor 主机上的 docker 证书目录下(需要提前创建)
mkdir -p /etc/docker/certs.d/r.oso.plus/
cp r.oso.plus.cert /etc/docker/certs.d/r.oso.plus/
cp r.oso.plus.key /etc/docker/certs.d/r.oso.plus/
cp ca.crt /etc/docker/certs.d/r.oso.plus/

# 如果先安装了 docker 则重启使配置生效
systemctl daemon-reload && systemctl restart docker

# 尝试使用权威证书替换自签名证书

# https://www.cnblogs.com/Rcsec/p/8479728.html
# https://www.cnblogs.com/punchlinux/p/16499966.html

# https://letsencrypt.org/zh-cn
# https://www.sslforfree.com
# https://decoder.link/csr_generator
# https://www.sslchecker.com/csr/self_signed

# https://keymanager.org

# 证书颁发机构证书
openssl genrsa -out ca.key 4096
# CA证书
openssl req -x509 -new -nodes -sha512 -days 3650 -subj "/C=CN/ST=Guizhou/L=Guiyang/O=Example/OU=Personal/CN=r.k8s.lan" -key ca.key -out ca.crt

# 服务器证书私钥
openssl genrsa -out r.k8s.lan.key 4096
# 证书签名请求 (CSR)
openssl req -sha512 -new -subj "/C=CN/ST=Guizhou/L=Guiyang/O=Example/OU=Personal/CN=r.k8s.lan" -key r.k8s.lan.key -out r.k8s.lan.csr

# x509 v3扩展文件 DNS.2=registry.k8s.lan 百度了一下 好像 DNS.1=r.oso.plus 之类的还可以使用通配符 例如 DNS.1=*.oso.plus
cat > v3.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1=r.oso.plus
DNS.2=registry.oso.plus
DNS.3=r.k8s.lan
DNS.4=harbor
EOF

# 使用 v3.ext 给 harbor 主机生成证书
openssl x509 -req -sha512 -days 3650 -extfile v3.ext -CA ca.crt -CAkey ca.key -CAcreateserial -in r.k8s.lan.csr -out r.k8s.lan.crt

# 复制证书到 harbor 主机的 certs 目录下
mkdir -p /data/harbor/certs
cp r.k8s.lan.crt /data/harbor/certs
cp r.k8s.lan.key /data/harbor/certs

# 转换 r.k8s.lan.crt 为 r.k8s.lan.key.cert 供 docker 使用
openssl x509 -inform PEM -in r.k8s.lan.crt -out r.k8s.lan.cert

# 将服务器证书、密钥、CA文件复制到 harbor 主机上的 docker 证书目录下(需要提前创建)
mkdir -p /etc/docker/certs.d/r.k8s.lan/
cp r.k8s.lan.cert /etc/docker/certs.d/r.k8s.lan/
cp r.k8s.lan.key /etc/docker/certs.d/r.k8s.lan/
cp ca.crt /etc/docker/certs.d/r.k8s.lan/

mkdir -p /etc/docker/certs.d/oso.plus
cp oso.plus.cer /etc/docker/certs.d/oso.plus/
cp oso.plus.key /etc/docker/certs.d/oso.plus/
cp ca.cer /etc/docker/certs.d/oso.plus/

# 如果先安装了 docker 则重启使配置生效
systemctl daemon-reload && systemctl restart docker

cat > harbor.yml << 'EOF'
hostname: r.oso.plus
http:
  port: 80
https:
  port: 443
  certificate: /data/harbor/certs/oso.plus.crt
  private_key: /data/harbor/certs/oso.plus.key
harbor_admin_password: i4Seeyon
database:
  password: root123
  max_idle_conns: 100
  max_open_conns: 900
  conn_max_lifetime: 5m
  conn_max_idle_time: 0
data_volume: /data/harbor
storage_service:
  # ca_bundle:
  s3:
    accesskey: k8s
    secretkey: i4Seeyon
    region: gy-by-1
    regionendpoint: http://m.oso.plus:9000
    bucket: harbor
    encrypt: false
    # keyid: mykeyid
    secure: false
    v4auth: true
    chunksize: 5242880
    multipartcopychunksize: 33554432
    multipartcopymaxconcurrency: 100
    multipartcopythresholdsize: 33554432
    rootdirectory: /
  redirect:
    disable: true
trivy:
  ignore_unfixed: false
  skip_update: false
  offline_scan: false
  security_check: vuln
  insecure: false
jobservice:
  max_job_workers: 10
  job_loggers:
    - STD_OUTPUT
    - FILE
  logger_sweeper_duration: 1 #days
notification:
  webhook_job_max_retry: 3
  webhook_job_http_client_timeout: 3 #seconds
log:
  level: info
  local:
    rotate_count: 50
    rotate_size: 200M
    location: /apps/harbor/logs
_version: 2.9.0
proxy:
  http_proxy:
  https_proxy:
  no_proxy:
  components:
    - core
    - jobservice
    - trivy
upload_purging:
  enabled: true
  age: 168h
  interval: 24h
  dryrun: false
cache:
  enabled: false
  expire_hours: 24
EOF

# 安装 docker

本文采用在线更新源进行安装。离线环境,可下载静态二进制文件安装,也可以下载rpm包进行安装。

# harbor 版本要求:docker-ce 17.06.0+ | docker-compose 1.18+
yum install -y git xz yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sed -i 's+https://download.docker.com+https://mirrors.tuna.tsinghua.edu.cn/docker-ce+' /etc/yum.repos.d/docker-ce.repo

yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 使用局域网软件源
# yum-config-manager --add-repo https://repos.oso.plus/docker-ce/linux/centos/docker-ce.repo
# sed -i 's#download.docker.com#repos.oso.plus/docker-ce#g' /etc/yum.repos.d/docker-ce.repo
# yum install -y docker-ce docker-ce-cli containerd.io

# 如果是通过在线 rpm 安装 新版本不用单独下了
# wget https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64
# cp docker-compose-linux-x86_64 /usr/local/bin/docker-compose
# chmod 755 /usr/local/bin/docker-compose

cat > /etc/docker/daemon.json << 'EOF'
{
    "insecure-registries": [
        "r.oso.plus",
        "registry.oso.plus"
    ],
    "exec-opts": [
        "native.cgroupdriver=systemd"
    ],
    "registry-mirrors": [
        "https://r.oso.plus",
        "https://registry.docker-cn.com",
        "https://hub-mirror.c.163.com",
        "https://docker.mirrors.ustc.edu.cn"
    ],
    "data-root": "/data/docker",
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "100m"
    }
}
EOF

systemctl enable docker --now

# 按需在 /etc/profile 添加环境变量
# export PATH=/usr/libexec/docker/cli-plugins:$PATH

# 安装 harbor

修改配置文件后重新安装 harbor。也就是需要重新执行 install.sh 脚本。

# https://zhuanlan.zhihu.com/p/560060937

mkdir /apps
# wget https://github.com/goharbor/harbor/releases/download/v2.9.0/harbor-offline-installer-v2.9.0.tgz
tar -xf harbor-offline-installer-v2.9.0.tgz -C /apps
cd /apps/harbor && ./install.sh
# cp harbor.yml.tmpl harbor.yml

# 这里设置后端存储为 minio 不要注释掉 data_volume: /data/harbor。 
# data_volume 和 storage_service 可以共存。
# data_volume 用于指定 Harbor 的数据存储路径,包括数据库、配置文件、日志等。这是 Harbor 运行所必需的,无论您使用哪种后端存储服务。
# storage_service 用于配置 Harbor 镜像存储的后端服务。无论您是否启用 data_volume,都可以配置和使用 storage_service。

cat > harbor.yml << 'EOF'
hostname: r.oso.plus
http:
  port: 80
https:
  port: 443
  certificate: /data/harbor/certs/oso.plus.cer
  private_key: /data/harbor/certs/oso.plus.key
harbor_admin_password: i4Seeyon
database:
  password: root123
  max_idle_conns: 100
  max_open_conns: 900
  conn_max_lifetime: 5m
  conn_max_idle_time: 0
data_volume: /data/harbor
storage_service:
  s3:
    accesskey: k8s
    secretkey: i4Seeyon
    region: gy-by-1
    regionendpoint: http://m.oso.plus:9000
    bucket: harbor
    encrypt: false
    secure: false
    skipverify: true
    v4auth: true
    chunksize: 5242880
    multipartcopychunksize: 33554432
    multipartcopymaxconcurrency: 100
    multipartcopythresholdsize: 33554432
    rootdirectory: /
  redirect:
    disable: true
trivy:
  ignore_unfixed: false
  skip_update: false
  offline_scan: false
  security_check: vuln
  insecure: false
jobservice:
  max_job_workers: 10
  job_loggers:
    - STD_OUTPUT
    - FILE
  logger_sweeper_duration: 1 #days
notification:
  webhook_job_max_retry: 3
  webhook_job_http_client_timeout: 3 #seconds
log:
  level: info
  local:
    rotate_count: 50
    rotate_size: 200M
    location: /apps/harbor/logs
_version: 2.9.0
proxy:
  http_proxy:
  https_proxy:
  no_proxy:
  components:
    - core
    - jobservice
    - trivy
upload_purging:
  enabled: true
  age: 168h
  interval: 24h
  dryrun: false
cache:
  enabled: false
  expire_hours: 24
EOF

# 启用 https
./prepare

# 启动
docker-compose up -d
# 查看容器状态
docker-compose ps
# 关闭
docker-compose stop
# docker-compose down

# 创建服务控制文件
# 新版本使用 docker-compose 而不是 docker compose
cat > /etc/systemd/system/harbor.service << 'EOF'
[Unit]
Description=Harbor Cloud Native Registry
Documentation=https://goharbor.io
After=docker.service
Requires=docker.service

[Service]
Type=simple
Restart=on-failure
RestartSec=5
ExecStart=docker compose -f /apps/harbor/docker-compose.yml up
ExecStop=docker compose -f /apps/harbor/docker-compose.yml down -v
ExecStopPost=docker compose -f /apps/harbor/docker-compose.yml rm -f

[Install]
WantedBy=multi-user.target
EOF

# 启动且加入开机自启
systemctl daemon-reload && systemctl enable --now harbor
systemctl status harbor

当minio启用https访问时,需要注意以下三个参数:

# 官方参考:<https://distribution.github.io/distribution/storage-drivers/s3/>
regionendpoint: <https://192.168.100.240:9000>
secure: true
skipverify: true
data_volume: /data/harbor/data
storage_service:
  # ca_bundle: /apps/minio/certs/CAs/ca.crt
  s3:
    accesskey: harbor
    secretkey: i4Seeyon
    region: gz-gy-1
    regionendpoint: https://192.168.100.240:9000
    bucket: harbor
    encrypt: false
    secure: true
    skipverify: true
    v4auth: true
    chunksize: 5242880
    multipartcopychunksize: 33554432
    multipartcopymaxconcurrency: 100
    multipartcopythresholdsize: 33554432
    rootdirectory: /
  redirect:
    disable: true
storage_service:
#   # ca_bundle is the path to the custom root ca certificate, which will be injected into the truststore
#   # of registry's and chart repository's containers.  This is usually needed when the user hosts a internal storage with self signed certificate.
#   ca_bundle:
# storage backend, default is filesystem, options include filesystem, azure, gcs, s3, swift and oss
# for more info about this configuration please refer https://docs.docker.com/registry/configuration/
s3:
accesskey: root
secretkey: miniopwd
region: us-east-1
regionendpoint: http://example.com:9000
bucket: harbor-registry
encrypt: true
secure: true
v4auth: true
chunksize: 5242880
rootdirectory: /
# set disable to true when you want to disable registry redirect
redirect:
disabled: true

# 测试验证

# 默认帐号密码 已被修改为 i4Seeyon
# admin/Harbor12345

# 命令行登陆
# docker login r.k8s.lan

# 界面登陆 可以使用 IP 地址
# https://r.k8s.lan

# 卸载 harbor

cd /apps/harbor
docker-compose down
rm -rf /data/harbor/*
rm -rf /data/docker/*
rm -rf common

# harbor.yml 参数解释

hostname:指定要部署的Harbor的主机IP或域名,这是访问Horbor Portal和注册服务的地址。例如:192.168.31.100或者reg.domain.com,注册的服务可以从外部访问,因此不要使用127.0.0.1、0.0.0.0这种ip指定为hostname

Http、Https:不要再生产环境中使用Http,仅在没有连接在外部的机器上使用
 子参数:
 port:http 80(默认)、https 443(默认)端口号
 certificate:SSL证书路径
 private_key:SSL key路径

internal_tls:内部环境是否使用tsl进行交流
 子参数:
 enabled:true or false
 dir:证书和密钥文件路径

harbor_admin_password:为 Harbor 系统管理员设置初始密码。 该密码仅在Harbor 首次启动时使用。 在后续登录时,将忽略此设置,并在 Harbor Portal 中设置管理员密码。 默认用户名和密码为 admin 和 Harbor12345

database:使用本地数据库或者外部数据库
 子参数:
 password:数据库密码
 max_idle_conns:空闲连接池的最大连接数,默认为50,不配置为2
 max_open_conns:到数据库的最大打开连接数, Harbor 数据库的最大连接数,默认值为 100,不配置,则值为 0

data_volume:主机上存储 Harbor 数据的位置。 即使删除或重新创建 Harbor 的容器,该数据也保持不变。 可以选择配置外部存储,在这种情况下禁用此选项并启用 storage_service。 默认为 /data

trivy:是一种适用于 CI 的简单而全面的容器漏洞扫描程序。软件漏洞是指软件或操作系统中存在的故障、缺陷或弱点。Trivy 检测操作系统包(Alpine、RHEL、CentOS等)和应用程序依赖(Bundler、Composer、npm、yarn等)的漏洞
 子参数:
 ignore_unfixed:true仅显示修复的漏洞,默认为false
 skip_update:您可能希望在测试或 CI/CD 环境中启用此标志以避免 GitHub 速率限制问题。 如果启用了该标志,您必须手动下载 trivy-offline.tar.gz 存档,提取 trivy.db 和 metadata.json 文件并将它们安装在 /home/scanner/.cache/trivy/db/trivy 中。 容器中的数据库路径。 默认值为 false
 insecure:将该标志设置为 true 以跳过验证注册证书。 默认值为 false
 github_token:设置令牌下载Trivy DB GitHub的访问。Trivy DB由Trivy从GitHub的发布页面下载。 来自 GitHub 的匿名下载受到每小时 60 个请求的限制

jobservice:工作服务。子参数:max_job_workers:对于每个镜像复制作业,工作人员将存储库的所有标签同步到远程目标。 增加这个数字允许系统中更多的并发复制作业。 但是由于每个worker都会消耗一定的网络/CPU/IO资源,所以根据主机的硬件资源来设置这个属性的值。 默认值为 10

notification:子参数:webhook_job_max_retry :设置 web hook 作业的最大重试次数。 默认值为 10。

chart:子参数:absolute_url:设置图表以使用绝对 URL

log:配置日志记录。 Harbor 使用 rsyslog 来收集每个容器的日志
 子参数:
 level:设置日志等级,debug、info、warning、error、fatal,默认为info
 local:设置日志保留参数
 external_endpoint:启用此选项可将日志转发到系统日志服务器

proxy:
 子参数:
 http_proxy:配置HTTP代理,例如http://my.proxy.com:3128
 https_proxy:配置HTTPS代理,例如http://my.proxy.com:3128
 no_proxy:配置何时不使用代理,例如 127.0.0.1、localhost、registry

external_url:启用此选项以使用外部代理。 启用后,不再使用主机名,对应着nginx所设置的代理后访问地址

storage_service:默认情况下,Harbor 将图像和图表存储在您的本地文件系统上。 在生产环境中,可能使用另一个存储后端而不是本地文件系统。 下面列出的参数是注册表的配置。
 子参数:
 ca_bundle:自定义根 CA 证书的路径,该证书被注入到注册表和图表存储库容器的信任库中。 如果内部存储使用自签名证书,则需要这样做。
 filesystem:默认为文件系统,但您可以设置 azure、gcs、s3、swift 和 oss。 有关如何配置其他后端的信息,请参阅下面的配置存储后端。 设置 maxthreads 以限制外部提供程序的线程数。 默认值为 100。
 redirect:禁用注册表重定向时,将 disable 设置为 true

external_database:如果禁用本地数据库选项,则配置外部数据库设置。 目前,Harbor 仅支持 PostgreSQL 数据库。 需要为 Harbor 核心、Notary 服务器和 Notary 签名者创建三个数据库。 这些表是在 Harbor 启动时自动生成的。
 子参数:harbor(host、port、db_name....)、notary_signer(host、port、db_name....)、notary_server(host、port、db_name....)

external_redis:配置外部redis实例
 子参数:host、passwort、sentinel_master_set、registry_db_index

metric:配置将 Harbor 实例指标暴露给指定的端口和路径
 子参数:enabled(启用该功能)port、path

pull时TLS证书问题

症状
[root@k8s-m1 scripts]# ctr i pull --user admin:i4Seeyon r.oso.plus/library/nginx:1.24.0
INFO[0000] trying next host                              error="failed to do request: Head \"https://r.oso.plus/v2/library/nginx/manifests/1.24.0\": tls: failed to verify certificate: x509: certificate signed by unknown authority" host=r.oso.plus
ctr: failed to resolve reference "r.oso.plus/library/nginx:1.24.0": failed to do request: Head "https://r.oso.plus/v2/library/nginx/manifests/1.24.0": tls: failed to verify certificate: x509: certificate signed by unknown authority

解决 --skip-verify, -k Skip SSL certificate validation
ctr i pull --user admin:i4Seeyon -k r.oso.plus/library/nginx:1.24.0
ctr i pull -k r.oso.plus/library/nginx:1.24.0

# 推拉测试

在安装有 containerd 的机子上执行测试。

# 拉取 - 来自 docker.io
ctr i pull --platform amd64 --platform arm64 docker.io/library/nginx:1.24.0
# 标记 - 修改本地镜像的tag
ctr i tag docker.io/library/nginx:1.24.0 r.oso.plus/library/nginx:1.24.0
# 推送 - 到 harbor 私仓
ctr i push --user admin:i4Seeyon -k r.oso.plus/library/nginx:1.24.0
# 查看 - 本地镜像
ctr i ls
# 删除 - 从 docker.io 拉取到本地的 nginx:1.24.0 镜像
ctr i rm r.oso.plus/library/nginx:1.24.0
ctr i rm docker.io/library/nginx:1.24.0
# 再次查看
ctr i ls
# 拉取 - 从 harbor 私仓拉取 需要登录则使用以下语句 -k 跳过 TLS 验证
ctr i pull --user admin:i4Seeyon -k r.oso.plus/library/nginx:1.24.0
# 拉取 - 从 harbor 私仓拉取 不需要登录则使用以下语句 -k 跳过 TLS 验证
ctr i pull -k r.oso.plus/library/nginx:1.24.0

# 拉取 - 到 k8s 集群的 k8s.io 名字空间中
ctr -n=k8s.io i pull -k r.oso.plus/library/nginx:1.24.0
ctr -n=k8s.io i ls | grep nginx

docker tag SOURCE_IMAGE[:TAG] r.oso.plus/test/REPOSITORY[:TAG]
docker push r.oso.plus/test/REPOSITORY[:TAG]

docker pull --platform amd64 docker.io/library/nginx:1.24.0
docker tag docker.io/library/nginx:1.24.0 r.oso.plus/test/nginx:1.24.0
docker push r.oso.plus/test/nginx:1.24.0

docker pull --platform amd64 alpine:3.18.4
docker pull --platform arm64 alpine:3.18.4

podman pull quay.io/curl/curl/curl:8.3.0
docker pull quay.io/curl/curl

# 这个重新标记后再上传会报错:ctr: content digest sha256:3d37375e11f1f459074bbe8a5d2d0a30c72f5cc5e2b963ab139e8c14f94263e7: not found
ctr i pull quay.io/curl/curl:latest -V

# 下面这三个OK 添加 --all-platforms 或 --platform amd64 等参数
ctr i pull --all-platforms quay.io/curl/curl:latest -V
ctr i tag quay.io/curl/curl:latest r.oso.plus/library/curl:latest
ctr i push --user admin:i4Seeyon -k r.oso.plus/library/curl:latest
nerdctl inspect r.oso.plus/library/curl:latest

ctr i pull --user admin:i4Seeyon -k r.oso.plus/library/curl:latest

containerd run --rm -d --name my-nginx r.oso.plus/library/nginx:1.24.0
harbor部署时后端存储为本地文件系统存储,后需要改为minio存储。是不是需要重新执行install.sh

是的,如果你已经部署了 Harbor 并且之前使用的是本地文件系统存储,现在想要改为使用 MinIO 作为后端存储,你需要重新执行 install.sh 脚本。
在 Harbor 的部署过程中,存储后端的配置是在安装时确定的,并且会影响到 Harbor 的数据存储方式。因此,要将存储后端从本地文件系统更改为 MinIO,你需要更新配置文件并重新执行安装脚本。
在进行更改之前,请确保你已经备份了 Harbor 的数据和配置文件,以防止数据丢失或配置错误。
以下是更改 Harbor 存储后端为 MinIO 的一般步骤:
停止 Harbor 服务:首先,停止当前运行的 Harbor 服务,以确保数据的一致性和完整性。
备份数据和配置文件:在进行更改之前,务必备份 Harbor 的数据和配置文件,以防止数据丢失或配置错误。
更新配置文件:编辑 harbor.yaml 配置文件,将存储后端的相关配置更改为 MinIO。根据你的具体需求,设置 MinIO 的访问密钥、桶名等参数。
执行安装脚本:重新执行 install.sh 脚本,以应用新的配置并重新部署 Harbor 服务。这将会更新 Harbor 的存储后端为 MinIO。
验证更改:等待安装完成后,验证 Harbor 是否成功使用 MinIO 作为后端存储。可以通过查看日志、检查 MinIO 桶的状态以及上传和下载镜像来验证。
[root@dev scripts]# ctr i tag quay.io/curl/curl:latest r.k8s.lan/library/curl:latest
r.k8s.lan/library/curl:latest
[root@dev scripts]# ctr i push r.k8s.lan/library/curl:latest
index-sha256:eb0face86650cd48716c57514a7c74197bd543501e70edde6263aec0eb57561e:    waiting        |--------------------------------------| 
manifest-sha256:3ef599c9b25b8f086b7d38ef48c05056bf1a43bbe43cea3a672e8f16e0694186: waiting        |--------------------------------------| 
manifest-sha256:e5b70dbb695510428a496515795ae906b1955a5c575cd7a87e2ab024ae4ed666: waiting        |--------------------------------------| 
manifest-sha256:2e3d86d1105699eb0ec7d4434c19754d6993bdb911daed986d17d19b7e808b35: waiting        |--------------------------------------| 
manifest-sha256:3d37375e11f1f459074bbe8a5d2d0a30c72f5cc5e2b963ab139e8c14f94263e7: waiting        |--------------------------------------| 
manifest-sha256:7d93b841407df7a79be084d694744e2433564e5adf0a140070e03b3b62bf011c: waiting        |--------------------------------------| 
elapsed: 0.1 s                                                                    total:   0.0 B (0.0 B/s)                                         
ctr: content digest sha256:3d37375e11f1f459074bbe8a5d2d0a30c72f5cc5e2b963ab139e8c14f94263e7: not found

# 配置docker使用harbor私有仓库

待补充

# 配置containerd使用harbor私有仓库

参考文档

https://github.com/containerd/cri/blob/master/docs/registry.md (opens new window)

按照以下配置修改之后,拉取镜像时,使用 crictr 进行拉取和推送 则不会报错:tls: failed to verify certificate: x509: certificate signed by unknown authority

但使用 ctr 命令进行 pull/push 时不得行,只能使用 --user 和 -k 参数规避。

当 insecure_skip_verify = true 时,不再需要设置 [plugins."io.containerd.grpc.v1.cri".registry.configs."r.oso.plus".auth]。

# 在文件 /etc/containerd/config.toml 中进行调整 一般是 160 行上下
vim /etc/containerd/config.toml
    [plugins."io.containerd.grpc.v1.cri".registry]
      config_path = ""

      [plugins."io.containerd.grpc.v1.cri".registry.auths]

      [plugins."io.containerd.grpc.v1.cri".registry.configs]
        [plugins."io.containerd.grpc.v1.cri".registry.configs."r.oso.plus".tls]
          insecure_skip_verify = true # 是否跳过证书认证
          ca_file = "/etc/containerd/certs.d/oso.plus/ca.cer" # CA 证书
          cert_file = "/etc/containerd/certs.d/oso.plus/oso.plus.cer" # harbor 证书
          key_file = "/etc/containerd/certs.d/oso.plus/oso.plus.key" # harbor 私钥
        [plugins."io.containerd.grpc.v1.cri".registry.configs."r.oso.plus".auth] # harbor 认证的账号密码 配置
          username = "admin" # harbor 用户
          password = "i4Seeyon" # harbor 密码
          # auth = ""
          # identitytoken = ""
  
      [plugins."io.containerd.grpc.v1.cri".registry.headers]

      [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
        [plugin."io.containerd.grpc.v1.cri".registry.mirrors."r.oso.plus"]
          endpoint = ["https://r.oso.plus"] # harbor 自建私有仓库

    [plugins."io.containerd.grpc.v1.cri".x509_key_pair_streaming]
      tls_cert_file = ""
      tls_key_file = ""

# 将离线tar镜像文件导入到harbor私有仓库

大致过程如下:

1、首先将需要导入到私仓的 tar 镜像文件传到任意一台安装有 containerd 或 docker 的主机上,或使用已从其他仓库拉取到本地的镜像。这里使用 console 这台主机为例。

2、使用镜像导入命令 import 到 console 主机中。

3、重现 tag 镜像。主要是修改仓库地址。

4、推送重新 tag 后的镜像到私仓。

5、删除 console 主机上的同名镜像。

6、拉取私仓的镜像进行验证是否可用。

# 导入tar离线镜像文件

# 以部署 K8S 高可用集群所需的镜像 + calico 网络插件 为例
list=(
  coredns-v1.10.1.tgz
  etcd-3.5.9-0.tgz
  kube-apiserver.tar
  kube-controller-manager.tar
  kube-proxy.tar
  kube-scheduler.tar
  pause-3.9.tgz
  calico-cni.tar
  calico-node.tar
  calico-kube-controllers.tar
)
for x in "${list[@]}"; do ctr i import --platform amd64 "${x}"; done

# 检查导入结果

# 然后使用 ctr i ls 命令查看导入结果且记下对应镜像的 tag 标记
ctr i ls | awk 'NR>1 {print $1}'
docker.io/calico/cni:v3.26.1
docker.io/calico/kube-controllers:v3.26.1
docker.io/calico/node:v3.26.1
registry.k8s.io/coredns/coredns:v1.10.1
registry.k8s.io/etcd:3.5.9-0
registry.k8s.io/kube-apiserver-amd64:v1.28.2
registry.k8s.io/kube-controller-manager-amd64:v1.28.2
registry.k8s.io/kube-proxy-amd64:v1.28.2
registry.k8s.io/kube-scheduler-amd64:v1.28.2
registry.k8s.io/pause:3.9

# 根据检查结果给镜像重新打tag

# 注意需要在 私仓创建对应的项目
ctr i tag docker.io/calico/cni:v3.26.1 r.oso.plus/calico/cni:v3.26.1
ctr i tag docker.io/calico/cni:v3.26.1 r.oso.plus/calico/cni:v3.26.1
ctr i tag docker.io/calico/kube-controllers:v3.26.1 r.oso.plus/calico/kube-controllers:v3.26.1
ctr i tag docker.io/calico/node:v3.26.1 r.oso.plus/calico/node:v3.26.1
ctr i tag registry.k8s.io/coredns/coredns:v1.10.1 r.oso.plus/coredns/coredns:v1.10.1
ctr i tag registry.k8s.io/etcd:3.5.9-0 r.oso.plus/etcd:3.5.9-0
ctr i tag registry.k8s.io/kube-apiserver-amd64:v1.28.2 r.oso.plus/kube-apiserver-amd64:v1.28.2
ctr i tag registry.k8s.io/kube-controller-manager-amd64:v1.28.2 r.oso.plus/kube-controller-manager-amd64:v1.28.2
ctr i tag registry.k8s.io/kube-proxy-amd64:v1.28.2 r.oso.plus/kube-proxy-amd64:v1.28.2
ctr i tag registry.k8s.io/kube-scheduler-amd64:v1.28.2 r.oso.plus/kube-scheduler-amd64:v1.28.2
ctr i tag registry.k8s.io/pause:3.9 r.oso.plus/pause:3.9

以下是自动生成 tag、push、pull 的相关命令的 bash 脚本:auto-generation.sh。可根据需求自行调整。

#!/usr/bin/env bash

# 以下为 shell 自动处理命令 此命令会对所有能查询到的镜像自动生成 rm|tag|push|pull 的命令
# pull/push 的参数 --platform 后面跟随平台架构 x86_64=amd64 | aarch64 = arm64
# 生成之后 可自行调整 是否添加 名字空间 之类的。如:-n k8s.io

# 私仓地址
harborAddr="r.oso.plus"

# 用户名/密码
# user="$1"; pwd="$2"
user="yfc"; pwd="i4Seeyon"

# 名字空间 预留
# ns="k8s.io"

# 平台架构
archType="amd64"

# 本地镜像列表
i="$(ctr i ls | awk 'NR>1 {print $1}')"

# make sure user and pwd are defined
if [ -z "${user}" ]; then echo "user variable not defined"; exit 1; fi
if [ -z "${pwd}" ]; then echo "password variable not defined"; exit 1; fi
if [ -z "${i}" ]; then echo "image list is empty"; exit 1; fi
if [ -z "${harborAddr}" ]; then echo "harbor addr is empty"; exit 1; fi

tagFile="ctr-image-tag.sh"
pushFile="ctr-image-push.sh"
pullFile="ctr-image-pull.sh"
rmFile="ctr-image-rm.sh"

list=("${tagFile}" "${pushFile}" "${pullFile}" "${rmFile}")
for x in "${list[@]}"; do touch "${x}" && true >"${x}"; done

echo

# tag
echo "the image tag command is as follows"
while read -r line; do
    a="${line%%/*}"
    # a="${line%/*}"
    case "${a}" in
        "docker.io" )
            b="ctr i tag ${line} ${line/docker.io/r.oso.plus}"
            ;;
        "registry.k8s.io" )
            b="ctr i tag ${line} ${line/registry.k8s.io/r.oso.plus\/k8s}"
            ;;
        "registry.cn-hangzhou.aliyuncs.com" )
        	  b="ctr i tag ${line} ${line/registry.cn-hangzhou.aliyuncs.com\/google_containers/r.oso.plus\/k8s}"
            ;;
        "${harborAddr}" )
            continue
            ;;
    esac
    if [ "${b}" != "" ]; then echo "${b}"; fi
done <<< "${i}" | tee -a "${tagFile}"
echo

# push
echo "the image push command is as follows"
while read -r line; do
    a="${line%%/*}"
    # a="${line%/*}"
    case "${a}" in
        "docker.io" )
            c="ctr i push --platform ${archType} --user ${user}:${pwd} -k ${line/${a}/r.oso.plus}"
            ;;
        "registry.k8s.io" )
            c="ctr i push --platform ${archType} --user ${user}:${pwd} -k ${line/${a}/r.oso.plus\/k8s}"
            ;;
        "registry.cn-hangzhou.aliyuncs.com" )
        	  c="ctr i push --platform ${archType} --user ${user}:${pwd} -k ${line/registry.cn-hangzhou.aliyuncs.com\/google_containers/r.oso.plus\/k8s}"
            ;;
        "${harborAddr}" )
            continue
            ;;
    esac
    if [ "${c}" != "" ]; then echo "${c}"; fi
done <<< "${i}" | tee -a "${pushFile}"
echo

# pull
echo "the image pull command is as follows"
while read -r line; do
    a="${line%%/*}"
    # a="${line%/*}"
    case "${a}" in
        "docker.io" )
            d="ctr i pull --platform ${archType} --user ${user}:${pwd} -k ${line/${a}/r.oso.plus}"
            ;;
        "registry.k8s.io" )
            d="ctr i pull --platform ${archType} --user ${user}:${pwd} -k ${line/${a}/r.oso.plus\/k8s}"
            ;;
        "registry.cn-hangzhou.aliyuncs.com" )
        	  d="ctr i pull --platform ${archType} --user ${user}:${pwd} -k ${line/registry.cn-hangzhou.aliyuncs.com\/google_containers/r.oso.plus\/k8s}"
            ;;
        "${harborAddr}" )
            continue
            ;;
    esac
    if [ "${d}" != "" ]; then echo "${d}"; fi
done <<< "${i}" | tee -a "${pullFile}"
echo

# rm
echo "the image delete command is as follows"
while read -r line; do
    a="${line%%/*}"
    # a="${line%/*}"
    e="ctr i rm ${line}"
    if [ "${e}" != "" ]; then echo "${e}"; fi
done <<< "${i}" | tee -a "${rmFile}"
echo

while read -r line; do
    a="${line%%/*}"
    # a="${line%/*}"
    case "${a}" in
        "docker.io" )
            f="ctr i rm ${line/${a}/r.oso.plus}"
            ;;
        "registry.k8s.io" )
            f="ctr i rm ${line/${a}/r.oso.plus\/k8s}"
            ;;
        "registry.cn-hangzhou.aliyuncs.com" )
            f="ctr i rm ${line/registry.cn-hangzhou.aliyuncs.com\/google_containers/r.oso.plus\/k8s}"
            ;;
        "${harborAddr}" )
            continue
            ;;
    esac
    if [ "${f}" != "" ]; then echo "${f}"; fi
done <<< "${i}" | tee -a "${rmFile}"
echo

# 向私仓 push 镜像进行验证

检查或调整上面脚本自动创建的三个文件无误后。执行 push.list 文件即可

bash push.list

# 从私仓 pull 镜像进行验证

先执行以下命令,自动生成用于删除所有镜像的命令。

# rm
i="$(ctr i ls | awk 'NR>1 {print $1}')"
while read -r line; do if [ "${line}" != "" ]; then echo "ctr i rm ${line}"; fi; done <<< "${i}" | tee rm.list

确认文件内容无误后,执行删除。

务必注意这个命令会将所有镜像都列举出来进行删除。

bash rm.list

然后执行 pull.list 文件即可。

bash pull.list

# 镜像验证

以上步骤都没什么异常之后,可以尝试启动镜像容器进行验证。

# Harbor成员类型说明

项目管理员(Project Admin):管理项目成员,删除项目,管理项目级的策略,读写、删除Artifact及项目中的其他资源。

项目维护人员(Master):管理项目级的策略,读写、删除 Artifact及项目中的其他资源。注意:在 Harbor 2.0的后续版本中,该角色的英文名将改为maintainer,中文翻译不变。

开发者(Developer): 读写Artifact及项目中的其他资源。

访客(Guest): 对Artifact及项目中的其他资源有读权限。

受限访客(Limited Guest):仅用于拉取Artifact,对项目中的其他资源如操作日志(Log)没有读权限。

总之,在Harbor中,不同的成员角色具有不同的权限,因此可以根据成员的需求和访问权限分配相应的角色。

# 将K8S集群所需镜像存放在harbor私仓


# 拉取镜像 - 全平台
ctr image pull --all-platforms registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.28.2
ctr image pull --all-platforms registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.28.2
ctr image pull --all-platforms registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.28.2
ctr image pull --all-platforms registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.28.2
ctr image pull --all-platforms registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9
ctr image pull --all-platforms registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.9-0
ctr image pull --all-platforms registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.10.1

# 拉取镜像 - 指定平台 - amd64
ctr image pull --platform amd64 registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.28.2
ctr image pull --platform amd64 registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.28.2
ctr image pull --platform amd64 registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.28.2
ctr image pull --platform amd64 registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.28.2
ctr image pull --platform amd64 registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9
ctr image pull --platform amd64 registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.9-0
ctr image pull --platform amd64 registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.10.1

# 修改镜像 - harbor 私有仓库
ctr image tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.28.2 r.oso.plus/k8s/kube-apiserver:v1.28.2
ctr image tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.28.2 r.oso.plus/k8s/kube-controller-manager:v1.28.2
ctr image tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.28.2 r.oso.plus/k8s/kube-scheduler:v1.28.2
ctr image tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.28.2 r.oso.plus/k8s/kube-proxy:v1.28.2
ctr image tag registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9 r.oso.plus/k8s/pause:3.9
ctr image tag registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.9-0 r.oso.plus/k8s/etcd:3.5.9-0
ctr image tag registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.10.1 r.oso.plus/k8s/coredns/coredns:v1.10.1

# 推送镜像 - harbor 私有仓库
# 如果需要推送到私仓 拉取镜像时 带上 --all-platforms 或 --platform amd64 参数
# 否则可能会报错 ctr: content digest sha256:7c8b276747b861f0b3189fd04539ad81251a6e93c302769ae92652ca3195f81c: not found
ctr image push --user yfc:i4Seeyon -k r.oso.plus/k8s/kube-apiserver:v1.28.2
ctr image push --user yfc:i4Seeyon -k r.oso.plus/k8s/kube-controller-manager:v1.28.2
ctr image push --user yfc:i4Seeyon -k r.oso.plus/k8s/kube-scheduler:v1.28.2
ctr image push --user yfc:i4Seeyon -k r.oso.plus/k8s/kube-proxy:v1.28.2
ctr image push --user yfc:i4Seeyon -k r.oso.plus/k8s/pause:3.9
ctr image push --user yfc:i4Seeyon -k r.oso.plus/k8s/etcd:3.5.9-0
ctr image push --user yfc:i4Seeyon -k r.oso.plus/k8s/coredns/coredns:v1.10.1

# 删除镜像
ctr image rm registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.28.2
ctr image rm registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.28.2
ctr image rm registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.28.2
ctr image rm registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.28.2
ctr image rm registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9
ctr image rm registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.9-0
ctr image rm registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.10.1

ctr image rm registry.k8s.io/kube-apiserver:v1.28.2
ctr image rm registry.k8s.io/kube-controller-manager:v1.28.2
ctr image rm registry.k8s.io/kube-scheduler:v1.28.2
ctr image rm registry.k8s.io/kube-proxy:v1.28.2
ctr image rm registry.k8s.io/pause:3.9
ctr image rm registry.k8s.io/etcd:3.5.9-0
ctr image rm registry.k8s.io/coredns/coredns:v1.10.1

ctr image rm sha256:55f13c92defb1eb854040a76e366da866bdcb1cc31fd97b2cde94433c8bf3f57
ctr image rm sha256:73deb9a3f702532592a4167455f8bf2e5f5d900bcc959ba2fd2d35c321de1af9
ctr image rm sha256:7a5d9d67a13f6ae031989bc2969ec55b06437725f397e6eb75b1dccac465a7b8
ctr image rm sha256:c120fed2beb84b861c2382ce81ab046c0ae612e91264ef7c9e61df5900fa0bb0
ctr image rm sha256:cdcab12b2dd16cce4efc5dd43c082469364f19ad978e922d110b74a42eff7cce
ctr image rm sha256:e6f1816883972d4be47bd48879a08919b96afcd344132622e4d444987919323c
ctr image rm sha256:ead0a4a53df89fd173874b46093b6e62d8c72967bbf606d672c9e8c9b601a4fc

ctr i rm r.oso.plus/k8s/coredns/coredns:v1.10.1
ctr i rm r.oso.plus/k8s/etcd:3.5.9-0
ctr i rm r.oso.plus/k8s/kube-apiserver:v1.28.2
ctr i rm r.oso.plus/k8s/kube-controller-manager:v1.28.2
ctr i rm r.oso.plus/k8s/kube-proxy:v1.28.2
ctr i rm r.oso.plus/k8s/kube-scheduler:v1.28.2
ctr i rm r.oso.plus/k8s/pause:3.9

# 获取 Harbor 中镜像信息

# 命令行获取Harbor镜像清单

适用于可以接触 harbor 及 minio 等存储服务器的场景。

# 根据配置文件查找数据存储目录
grep data_volume /apps/harbor/harbor.yml
# data_volume: /data/harbor

# 进入到Harbor的数据目录下
cd /data/harbor/registry

# 如果使用非本地的对象存储则需要到对象存储所在服务器下去操作
# cd /data/minio/disk1/harbor

# 搜索并创建一个镜像列表文件
find docker/ -type d -name "current" | sed 's|docker/registry/v2/repositories/||g;s|/_manifests/tags/|:|g;s|/current||g' | sort > images.list

# Shell脚本获取Harbor镜像列表

适用于不能接触 harbor 及 minio 等存储服务器的场景。

大致思路:通过调用 harbor api,先获取所有项目列表,再循环获取每个项目下的镜像名称,再循环获取某个镜像的所有tag版本,最后以指定格式输出到镜像清单文件中。

# 查看API版本

由于 harbor 的 API 有 v1 和 v2 两个版本,每个版本的 API 调用方法都不一样。所以,根据 harbor 的 api 版本号编写不同的 shell 脚本。

# 可将 r.oso.plus 替换为实际的 IP地址或域名加端口
curl https://r.oso.plus/api/version -k

# Harbor API v1 的 shell 脚本实现

cat get-harbor-image-list-v1.sh
#!/usr/bin/env bash
#
# 镜像清单文件
saveFile="harbor-image-$(date '+%Y-%m-%d').list"
# harbor 连接地址
addr="https://r.oso.plus"
# harbor 连接凭证,包含用户名和密码,因为是获取全部的镜像,只有 admin 用户才有该权限
credentials="admin:i4Seeyon"

# 获取 harbor 中有哪些项目
projectList=$(curl -u "${credentials}" -X GET "${addr}/api/projects" -H "Content-Type: application/json" | grep name | awk '/"name": /' | awk -F '"' '{print $4}')

for project in ${projectList}; do
    # 循环获取每个项目下所有的镜像
    imageNames=$(curl -u "${credentials}" -X GET "${addr}/api/search?q=${project}" -H "Content-Type: application/json" | grep "repository_name" | awk -F "\"" '{print $4}')
    for image in ${imageNames}; do
        # 循环获取每个镜像所有的标签(版本)
        imageTags=$(curl -u "${credentials}" -X GET "${addr}/api/repositories/${image}/tags" -H "Content-Type: application/json" | awk '/"name": /' | awk -F '"' '{print $4}')
        for tag in ${imageTags}; do
            # 将获取到的镜像完整路径存档到镜像清单文件
            echo "${addr}/${image}:${tag}" | grep -v Base | grep -v image | grep -v CentOS >> "${saveFile}"
        done
    done
done
# 执行脚本
bash get-harbor-image-list-v1.sh

# Harbor API v2 的 shell 脚本实现

cat get-harbor-image-list-v2.sh
#!/usr/bin/env bash
#
# harbor 访问地址
addr="https://r.oso.plus"
# 登录凭证
credentials=admin:i4Seeyon
# 镜像清单文件
saveFile="harbor-image-$(date '+%Y-%m-%d').list"
# 镜像tar包存放路径
# tarFileDir=/backup/harbor

set -x
# 获取Harbor中所有的项目(projects)
projectList=$(curl -u "${credentials}" -H "Content-Type: application/json" -X GET "${addr}/api/v2.0/projects" -k | python -m json.tool | grep name | awk '/"name": /' | awk -F '"' '{print $4}')

for project in ${projectList};do
   # 循环获取项目下所有的镜像
    imageNames=$(curl -u "${credentials}" -H "Content-Type: application/json" -X GET "${addr}/api/v2.0/projects/${project}/repositories" -k | python -m json.tool | grep name | awk '/"name": /' | awk -F '"' '{print $4}')
    for image in ${imageNames};do
        # 循环获取镜像的版本(tag)
        imageTags=$(curl -u "${credentials}" -H "Content-Type: application/json" -X GET "${addr}/v2/${image}/tags/list" -k | awk -F '"'  '{print $8,$10,$12}')
        for tag in ${imageTags};do
            # 格式化输出镜像信息
            echo "${addr}/${image}:${tag}" >> "${saveFile}"
        done
    done
done
# 执行脚本
bash get-harbor-image-list-v2.sh

# 将Harbor中的镜像制作成tar包

前面通过shell脚本获取harbor中的镜像列表,并以Harbor地址:服务端口/项目名称/镜像名称:tag的格式输出到文件中。

接下来可根据该文件批量pull镜像到本地,然后制作成tar包。

# 脚本待调整
cat image-pull.sh
#!/usr/bin/env bash
#
# 使用docker从镜像文件中下载镜像-将下载的镜像进行打包保存-删除下载到本地的镜像-将封装好的镜像包移动到备份目录
Image_tags=$(uniq $Images_File)
for image_tag in $Image_tags;do
    image_Name=$(echo $image_tag | awk -F/ '{print $3}' |  awk -F: '{print $1}')
    image_Lable=$(echo $image_tag | awk -F/ '{print $3}' |  awk -F: '{print $2}')
    docker pull $image_tag
    docker save $image_tag  -o $image_Name-$image_Lable.tar
    docker rmi  $image_tag
    mv $image_Name-$image_Lable.tar  $Tar_File
done

chmod +x image-pull.sh

# 集群部署

# 方案简述

本环境的集群部署,指主从复制+负载均衡的高可用部署方式,严格来说,不算是通常所说的集群模式。

一般的高可用部署架构有很多种,可根据自己的实际业务场景选择或组合。亦可参照官方文档,基于 k8s 集群进行高可用部署。

有些方案,是通过把 redis、postgresql、存储等等剥离出来分别独立组建集群的方案。此方案带来极大的实施、运维成本,并且实际意义不大。不建议采用。

另外,一般情况下,单机部署即可保证性能和日常应用,如非必要,可不用做集群部署。

# 主备模式

部署简单,主节点宕机后手动切换到备节点,同一时间只有一个节点提供服务。一个节点的资源未能充分利用。

processon

# 双主复制

部署简单,两个节点同时提供服务,前端有负载均衡调度。两个节点的资源被充分利用。

processon

# 一主多从

此方案有很多种组合,仅提供其中一种架构做参考。对于私有化部署来说,意义不大,否则就直接使用云服务提供的了。

processon

# 实施部署

harbor 对外提供提供服务时,客户端直连 harbor 服务或通过代理转发服务。

# keepalived 和 nginx/haproxy

在 harbor 双向复制架构中,使用 keepalived 提供一个 VIP 进行切换和使用 nginx 或 haproxy 进行代理各有优缺点。

使用 keepalived 提供 VIP 进行切换的优点:

  1. 实现简单:keepalived是轻量级软件,易于安装和配置。

  2. 负载均衡:keepalived支持基于轮询、随机等算法的负载均衡。

  3. 故障切换:当主服务器出现故障时,VIP会自动切换到备用服务器上。

使用 nginx 或 haproxy 进行代理的优点:

  1. 功能强大:nginx 和 haproxy 都是高性能的代理软件,具有丰富的功能和特性。

  2. 负载均衡:nginx 和 haproxy 都支持基于轮询、权重等算法的负载均衡。

  3. 会话保持:nginx 和 haproxy 都可以实现会话保持,确保同一用户的请求被转发到同一台服务器。

  4. 健康检查:nginx 和 haproxy 都具有健康检查功能,可以检测后端服务器的状态,避免将请求转发给故障的服务器。

总之,如果对性能要求不高且实现简单,可以选择使用 keepalived 提供 VIP 进行切换。如果对性能要求较高,需要更多的功能和特性,可以选择使用 nginx 或 haproxy 进行代理。

鉴于测试目的,以及实际生产环境中的情况,本环境采用双主复制高可用方案。

此方案比较适合各种私有化部署的场景需求。

关于前端负载均衡的部署可参考相关文档进行部署,这里不再赘述。

processon{width="5.763888888888889in" height="5.0462959317585305in"}

# 常用的命令

# 使用脚本在harbor中创建项目

vim create_project_harbor.sh
#!/usr/bin/env bash

url="https://r.oso.plus"
user="admin"
passwd="i4Seeyon"
harbor_projects=(
    library
    kubesphereio
    kubesphere
    argoproj
    calico
    coredns
    openebs
    csiplugin
    minio
    mirrorgooglecontainers
    osixia
    prom
    thanosio
    jimmidyson
    grafana
    elastic
    istio
    jaegertracing
    jenkins
    weaveworks
    openpitrix
    joosthofman
    nginxdemos
    fluent
    kubeedge
    openpolicyagent
)
for project in "${harbor_projects[@]}"; do
    echo "creating $project"
    curl -u "${user}:${passwd}" -X POST -H "Content-Type: application/json" "${url}/api/v2.0/projects" -d "{ \"project_name\": \"${project}\", \"public\": true}" -k #curl命令末尾加上 -k
done
bash offline-installation-tool.sh -s -l images-list.txt -d ./kubesphere-images
bash offline-installation-tool.sh -l images-list.txt -d ./kubesphere-images -r r.oso.plus

# 填坑

# harbor服务正常,本机测试正常,局域网其他地址不能访问。

curl -kL -u admin:i4Seeyon -X GET "http://192.168.100.199/api/v2.0/users/current"
curl -kL -u admin:i4Seeyon -X GET "https://192.168.100.199/api/v2.0/users/current"

情况发生在openEuler 24.03 LTS 版本中。

原因:ipv4流量转发未开启

[root@dev scripts]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

解决:开启转发并重启服务器

sed -i 's|net.ipv4.ip_forward = 0|net.ipv4.ip_forward = 1|' /etc/sysctl.conf
sysctl -p
sysctl net.ipv4.ip_forward
# reboot

排查过程:

万能的重启

调整docker-compose.yaml

重装harbor

调整nginx端口映射

关闭防火墙

关闭selinux

调整端口到高于1024的8080和8443

检查路由

检查harbor容器日志

# docker logs -f harbor-<name>
docker compose -f /apps/harbor/docker-compose.yaml ps -a
docker compose -f /apps/harbor/docker-compose.yaml down
docker compose -f /apps/harbor/docker-compose.yaml up -d

检查路由和网络桥接等情况

yum install -y bridge-utils
brctl show
ip route
lsof -i :8080; lsof -i :8443
iptables -L

部署命令历史

docker compose -f /apps/harbor/docker-compose.yaml ps -a
docker logs -f harbor-portal
iptables -L
docker info
brctl show
yum install -y bridge-utils
brctl show
systemctl stop harbor
brctl show
docker compose -f /apps/harbor/docker-compose.yaml down
brctl show
docker compose -f /apps/harbor/docker-compose.yaml up -d
brctl show
lsof -i:1080
lsof -i:8080
lsof -i :8080
lsof -i :8443
lsof -i :22
firewall-cmd --list-ports
systemctl restart firewalld
firewall-cmd --list-ports
systemctl status firewalld
firewall-cmd --list-ports
systemctl stop firewalld
firewall-cmd --list-ports
sysctl net.ipv4.ip_forward
vim /etc/sysctl.conf
sysctl -p
systemctl restart network
systemctl restart networking
systemctl restart 
vim /etc/sysctl.conf
sysctl -p
docker compose -f /apps/harbor/docker-compose.yaml ps -a
docker compose -f /apps/harbor/docker-compose.yaml down
docker compose -f /apps/harbor/docker-compose.yaml up -d
vim /apps/harbor/docker-compose.yaml 
docker network ls
docker compose -f /apps/harbor/docker-compose.yaml up -d
brctl 
brctl show
docker compose -f /apps/harbor/docker-compose.yaml down
brctl show
compose -f /apps/harbor/docker-compose.yaml up -d
docker compose -f /apps/harbor/docker-compose.yaml ps -a
docker compose -f /apps/harbor/docker-compose.yaml up -d
brctl show
iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X && iptables -t mangle -F && iptables -t mangle -X && iptables -P INPUT ACCEPT && iptables -P FORWARD ACCEPT && iptables -P OUTPUT ACCEPT
brctl show
docker compose -f /apps/harbor/docker-compose.yaml up -d
docker compose -f /apps/harbor/docker-compose.yaml down
brctl show
reboot
cd "/seeyon/scripts"
mkdir -p /seeyon/scripts && cd /seeyon/scripts
pwd
ls
curl -vkL https://192.168.100.199
vim /apps/harbor/docker-compose.yaml 
vim /apps/harbor/config/proxy/nginx.conf 
ls /apps/harbor/certs/
systemctl stop firewalld
vim /apps/harbor/docker-compose.yaml 
docker images -a
docker compose -f /apps/harbor/docker-compose.yaml ps -a
docker compose -f /apps/harbor/docker-compose.yaml ps
docker compose -f /apps/harbor/docker-compose.yaml ps -a
docker logs -f harbor-nginx
docker logs -f harbor-db
docker logs -f harbor-core
date
docker logs -f harbor-db
docker compose -f /apps/harbor/docker-compose.yaml ps -a
docker logs -f harbor-registry
systemctl stop firewalld
ip a
hostname -I
vim /apps/harbor/docker-compose.yaml 
systemctl status harbor
docker compose -f /apps/harbor/docker-compose.yaml ps -a
docker logs -f harbor-registry
docker compose -f /apps/harbor/docker-compose.yaml ps -a
docker logs -f harbor-core
docker compose -f /apps/harbor/docker-compose.yaml ps -a
docker logs -f harbor-registryctl
docker compose -f /apps/harbor/docker-compose.yaml ps -a
mkdir -p /seeyon/scripts && cd /seeyon/scripts
docker logs -f harbor-registryctl
docker compose -f /apps/harbor/docker-compose.yaml ps -a
brctl show
docker compose -f /apps/harbor/docker-compose.yaml down
brctl show
docker compose -f /apps/harbor/docker-compose.yaml up -d
brctl show
vim /etc/selinux/config 
ss -tlnpu
vim /apps/harbor/docker-compose.yaml 
vim /apps/harbor/config/proxy/nginx.conf 
docker compose -f /apps/harbor/docker-compose.yaml down
docker compose -f /apps/harbor/docker-compose.yaml up -d

# 运行 Harbor 时用到的镜像

在运行 Harbor 时,并不是所有镜像都会同时被使用。Harbor 是一个由多个组件组成的分布式系统,每个组件负责不同的功能。根据 Harbor 的架构和部署模式,以下是一些常见的组件及其对应的镜像:

镜像名称 是否必用 说明
goharbor/harbor-core 必用 核心服务
goharbor/harbor-portal 必用 用户界面
goharbor/harbor-jobservice 必用 后台任务服务
goharbor/registry-photon 必用 镜像存储和分发
goharbor/harbor-registryctl 必用 Registry 管理
goharbor/harbor-db 必用 PostgreSQL 数据库
goharbor/harbor-log 必用 日志服务
goharbor/redis-photon 必用 缓存和会话存储
goharbor/nginx-photon 必用 反向代理
goharbor/trivy-adapter-photon 可选 漏洞扫描(需启用)
goharbor/harbor-exporter 可选 监控指标导出(需启用)
goharbor/prepare 不用 仅用于安装或升级

# 实际运行时用到的镜像

在默认情况下,运行 Harbor 时会用到以下 9 个镜像:

goharbor/harbor-core
goharbor/harbor-portal
goharbor/harbor-jobservice
goharbor/registry-photon
goharbor/harbor-registryctl
goharbor/harbor-db
goharbor/harbor-log
goharbor/redis-photon
goharbor/nginx-photon

如果启用了漏洞扫描功能,还会用到:

goharbor/trivy-adapter-photon

如果启用了监控功能,还会用到:

goharbor/harbor-exporter

# 启用exporter

从 2.3.0 版本开始支持启用 harbor-exporter 来暴露一些数据指标给 prometheus 收集。

参考链接:https://goharbor.io/docs/2.12.0/install-config/configure-yml-file/

在 harbor.yml 文件中配置。

# 取消以下 4 行注释 且把 enabled: false 调整为 enabled: true
metric:
  enabled: true
  port: 9090
  path: /metrics

# 执行安装或更新
./install.sh --with-trivy

# 测试
curl http://192.168.100.193:9090/metrics

# 安全相关

# 手动扫描镜像

法一:进入项目页签,选择对应镜像进行扫描。

法二:在审查服务里设置扫描器进行扫描。

# 自动扫描镜像

harbor 2.11.0 开始支持自动生成和手动生成软件物料清单(SBOM)

官方资料:https://goharbor.io/docs/edge/administration/sbom-integration/

# 在推送镜像时将自动生成和扫描

1740340677873.png

1740340696254.png

对于使用Docker-compose部署的Harbor,可编辑harbor.yml文件,添加或修改以下配置项:

core:
  quota_update_provider: redis这些设置有助于优化资源消耗,包括数据库连接、CPU和内存使用率等,从而确保在高并发情况下也能平稳运行。
编撰人:yangfc