llz's zone

云原生制品注册表Harbor

Dec 06, 2024

什么是Harbor

官方定义为:“A complete OCI compliant cloud-native artifact registry”,“artifact”意为“制品”,容器镜像就是云原生开发的制品,Harbor则是完全符合OCI标准的容器镜像仓库。

PS:Helm Chart也是云原生开发的制品,因此Harbor仓库不只是可以存放容器镜像。

OCI镜像标准

其详细规范可以参考OCI image spec,其目的是为了让其他人按照这个标准创建镜像的构建和分发工具。

什么是镜像

镜像的本质,其实就是包含可执行代码的文件。镜像仓库则是提供镜像上传和下载的能力的服务器。

通过docker save命令,即可将镜像保存为文件:

$ docker pull busybox
$ docker save -o busybox.tar busybox

继续将镜像解包,看看它里面有什么:

$ mkdir busybox
$ tar -xvf busybox.tar -C busybox
$ tree busybox
busybox
├── blobs
│   └── sha256
│       ├── 3391f54c206456f4e74241be27addc5c28110e00bc5f514b5b6961917dbab998
│       ├── 770ccedf8091c5679d06a7f9d04d790c19503b76bd7ad7ad7dd055e1d95e6a91
│       ├── e49f83aa12d30427e1d2bcded8a283231d2e7e2ab408e1b0fb5ab881773c2355
│       └── f21ad18174949794e810922c8ada6ff8416aabab8ef3fd3bd144e47058359f52
├── index.json
├── manifest.json
├── oci-layout
└── repositories

镜像索引

先来看看index.json文件写了什么:

$ jq . index.json
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:e49f83aa12d30427e1d2bcded8a283231d2e7e2ab408e1b0fb5ab881773c2355",
      "size": 400,
      "annotations": {
        "io.containerd.image.name": "docker.io/library/busybox:latest",
        "org.opencontainers.image.ref.name": "latest"
      }
    }
  ]
}

这个文件的索引能力,主要来自digest(摘要)字段:

$ jq '.manifests[0].digest' index.json
"sha256:e49f83aa12d30427e1d2bcded8a283231d2e7e2ab408e1b0fb5ab881773c2355"

通过摘要就能找到对应的文件,这种索引方式,称为“内容寻址”。摘要是文件内容的SHA256哈希值,使用同一个摘要总是能找到同样的文件内容,体现了文件内容的不可变性。

$ jq . 'blobs/'$(jq -r '.manifests[0].digest' index.json | tr : /)
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:3391f54c206456f4e74241be27addc5c28110e00bc5f514b5b6961917dbab998",
    "size": 390
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar",
      "digest": "sha256:f21ad18174949794e810922c8ada6ff8416aabab8ef3fd3bd144e47058359f52",
      "size": 4280320
    }
  ]
}

与内容寻址相对的是“符号寻址”,文件内容可能发生变化,例如通过index.json这个文件名能找到对应的文件,这一过程就是符号寻址。

镜像清单

再来看看manifest.json文件写了什么:

$ jq . manifest.json
[
  {
    "Config": "blobs/sha256/3391f54c206456f4e74241be27addc5c28110e00bc5f514b5b6961917dbab998",
    "RepoTags": [
      "busybox:latest"
    ],
    "Layers": [
      "blobs/sha256/f21ad18174949794e810922c8ada6ff8416aabab8ef3fd3bd144e47058359f52"
    ],
    "LayerSources": {
      "sha256:f21ad18174949794e810922c8ada6ff8416aabab8ef3fd3bd144e47058359f52": {
        "mediaType": "application/vnd.oci.image.layer.v1.tar",
        "size": 4280320,
        "digest": "sha256:f21ad18174949794e810922c8ada6ff8416aabab8ef3fd3bd144e47058359f52"
      }
    }
  }
]

这个manifests.json并不是OCI image spec中规范的,而是docker将所有的清单文件内容汇总到了一起形成的文件。

镜像清单中记录了configurationlayers的摘要,可以索引到镜像配置和镜像层。

镜像配置

从镜像清单中我门找到了配置的摘要,这个配置摘要和镜像标签是一对一的,例如这里latest就是对应3391f54c...,通过配置摘要索引到镜像配置:

$ jq . blobs/sha256/3391f54c206456f4e74241be27addc5c28110e00bc5f514b5b6961917dbab998
{
  "config": {
    "Cmd": [
      "sh"
    ]
  },
  "created": "2024-09-26T21:31:42Z",
  "history": [
    {
      "created": "2024-09-26T21:31:42Z",
      "created_by": "BusyBox 1.37.0 (glibc), Debian 12"
    }
  ],
  "rootfs": {
    "type": "layers",
    "diff_ids": [
      "sha256:f21ad18174949794e810922c8ada6ff8416aabab8ef3fd3bd144e47058359f52"
    ]
  },
  "architecture": "arm64",
  "os": "linux",
  "variant": "v8"
}

可以看到配置文件最后表示了镜像的平台是linux/arm64,一般的一个镜像清单对应一个平台。

镜像层

从镜像清单中我门找到了层文件的摘要,通过层文件摘要索引到镜像层,但如果去查看层文件会发现是一堆乱码,从清单中我们可以看到这个层文件其实是tar格式的文件:

$ jq '.layers[0].mediaType' 'blobs/'$(jq -r '.manifests[0].digest' index.json | tr : /)
"application/vnd.oci.image.layer.v1.tar"

因此可以使用tar命令解包:

$ mkdir layer
$ tar -xvf f21ad181... -C layer
$ tree -L1 layer
layer
├── bin
├── dev
├── etc
├── home
├── lib
├── lib64 -> lib
├── root
├── tmp
├── usr
└── var

原来一个层文件,就是一个系统的根目录。但是busybox这个镜像只有一个层,或许一个层文件并不是一个完整的文件系统呢?我们在busybox的基础上做一些修改:

FROM busybox

RUN echo 'test layer' > /test

接着构建镜像,并在此解包,查询清单文件,可以看到有两个层:

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:34c9b411c55616d73aaa3cb104807d7dfcd0ac1d829a0fb32b50ab8d8ceba960",
    "size": 629
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar",
      "digest": "sha256:f21ad18174949794e810922c8ada6ff8416aabab8ef3fd3bd144e47058359f52",
      "size": 4280320
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar",
      "digest": "sha256:28e61e7b8c2aa5790126c7d0ee6f97c97dc20025c5797d139e7c2d16815344c7",
      "size": 3584
    }
  ]
}

其中f21ad1817...busybox:latest的层一样,而28e61e7b...则是新加的层文件,我们将它解包看看:

$ tree -L1 layer
layer
├── etc
├── proc
├── sys
└── test

原来不是每个层都是完整的文件系统,只有所有的层叠加在一起才是完整的文件系统,我们可以将镜像跑起来确认一下:

$ docker run busybox ls /
bin
dev
etc
home
lib
lib64
proc
root
sys
tmp
usr
var
$ docker run busybox:test ls /
bin
dev
etc
home
lib
lib64
proc
root
sys
test
tmp
usr
var

可以看到确实是在busybox:latest的基础上创建了test文件,镜像多个一个层,记录了这个增量的修改。

PS:Dockerfile中,RUN、ADD、COPY命令会创建新的层,其余命令会修改镜像配置。

DockerArchive转换为OCI格式

我们最开始得到的是docker的镜像格式:

$ tree busybox
busybox
├── blobs
│   └── sha256
│       ├── 3391f54c... # 配置
│       ├── 770ccedf...
│       ├── e49f83aa... # 清单
│       └── f21ad181... # 层
├── index.json # 索引
├── manifest.json # 清单汇总
├── oci-layout # 格式版本
└── repositories # 镜像名称和摘要

oci-layout文件记录了镜像格式对应的版本。

$ cat oci-layout | jq
{
  "imageLayoutVersion": "1.0.0"
}

repositories文件记录了镜像的对应的摘要,这个文件也不是OCI image spec要求的。

$ cat repositories | jq
{
  "busybox": {
    "latest": "f21ad18174949794e810922c8ada6ff8416aabab8ef3fd3bd144e47058359f52"
  }

还剩一个没看的770ccedf...文件,这也是一个配置文件,是docker构建镜像过程中的中间产物,最终的镜像配置以实际索引到的为准。

使用skopeo可以将docker archive转换为OCI镜像格式:

$ skopeo copy docker-archive:busybox.tar oci:busybox_oci
$ tree busybox_oci
busybox_oci
├── blobs
│   └── sha256
│       ├── 0f1a7ba9... # 配置
│       ├── 1933e30a... # 层
│       └── aa5d684e... # 清单
├── index.json # 索引
└── oci-layout # 格式版本

Harbor的架构

Harbor本身是一套微服务,其中核心的仓库功能,使用的是Docker开源的方案。在此基础上,又有其他的服务,提供如多租户、漏洞扫描等企业级功能,因此各大公有云厂商的镜像服务都基于Harbor实现。

整体架构图如下:

Harbor架构

可以看到,Harbor的接入层由nginx实现,也就是说Harbor从外部看,就是一个http服务。

图中的Harbor组件有这些:

图中的Harbor的依赖组件有这些:

还有一些以插件形式存在的组件。最核心需要掌握的有两个:Harbor-Core和Distribution,也是使用Harbor最常用到的。

源码编译 & 构建镜像 & 本地部署

这里以harbor 2.7.4为例进行编译,这是最后一个支持HelmChart的habor版本,本地是Apple M2的芯片。虽然官方提供了编译文档,但是没有很好的适配arm64架构,所以在Apple芯片上编译会有一些问题。

本小节所有命令,在harbor源码根目录下执行。

下载harbor源码

git clone https://github.com/goharbor/harbor.git
git checkout v2.7.4
git switch -c v2.7.4 # 基于2.7.4版本创建一个新的分支

编写配置文件

配置文件说明参考本文附录。

cp make/harbor.yml.tmpl make/harbor.yml

生成api代码

这个命令就是使用go-swagger从/api/v2.0/swagger.yaml生成go代码:

make gen_apis

这里由于本地是arm64会报错,需要做如下修改后再执行:

# 1. Makefile文件,GOBUILDIMAGE=goharbor/golang:$(GOVERSION),替换为GOBUILDIMAGE=golang:$(GOVERSION)
#    原因是goharbor/golang镜像中没有arm64的版本
# 2. Makefile文件,SPECTRAL_VERSION=v6.1.0,替换为SPECTRAL_VERSION=v6.14.2
#    原因是6.1.0没有arm64的版本
# 3. tools/spectral/Dockerfile文件,下载地址spectral-linux替换为spectral-linux-arm64
# 4. tools/swagger/Dockerfile文件,下载地址swagger_linux_amd64替换为swagger-linux-arm64

生成的代码在src/server/v2.0目录下。

检查编译环境

这个命令会去检查本地的golang、docker、docker-compose版本:

make check_environment

设置依赖组件版本

包括distribution、trivy、trivy-adapter、chartmuseum这些依赖组件。

make versions_prepare

正常这个命令不会有输出,可以编辑Makefile文件,把versions_prepare修改为:

versions_prepare:
  @echo $$VERSIONS_FOR_PREPARE
  echo "$$VERSIONS_FOR_PREPARE" > $(MAKE_PREPARE_PATH)/$(PREPARE_VERSION_NAME)

如此make versions_prepare输出:

VERSION_TAG: dev REGISTRY_VERSION: v2.8.0-patch-redis NOTARY_VERSION: v0.6.1 TRIVY_VERSION: v0.46.1 TRIVY_ADAPTER_VERSION: v0.30.18 CHARTMUSEUM_VERSION: v0.14.0-redis
echo "$VERSIONS_FOR_PREPARE" > /Users/lixiang97/go/src/github.com/goharbor/harbor/make/photon/prepare/versions

这个命令的作用就是,把依赖组件的版本写入到make/photon/prepare/versions文件中。

编译core

make compile_core

这个命令先执行make gen_apis,由于我们之前已经执行过了,可以从makefile里去掉:

# compile_core: gen_apis
compile_core:
	@echo "compiling binary for core (golang image)..."
	@echo $(GOBUILDPATHINCONTAINER)
  # 去掉@,显示执行过程
  # @$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATHINCONTAINER) -w $(GOBUILDPATH_CORE) $(GOBUILDIMAGE) $(GOIMAGEBUILD_CORE) -o $(GOBUILDPATHINCONTAINER)/$(GOBUILDMAKEPATH_CORE)/$(CORE_BINARYNAME)
	$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATHINCONTAINER) -w $(GOBUILDPATH_CORE) $(GOBUILDIMAGE) $(GOIMAGEBUILD_CORE) -o $(GOBUILDPATHINCONTAINER)/$(GOBUILDMAKEPATH_CORE)/$(CORE_BINARYNAME)
	@echo "Done."

命令输出为:

compiling binary for core (golang image)...
/harbor
/usr/local/bin/docker run --rm -v /Users/lixiang97/go/src/github.com/goharbor/harbor:/harbor -w /harbor/src/core golang:1.21.4 /usr/local/go/bin/go build -mod vendor -buildvcs=false  --ldflags "-w -s -X github.com/goharbor/harbor/src/pkg/version.GitCommit=f4e08e2a -X github.com/goharbor/harbor/src/pkg/version.ReleaseVersion=v2.7.4" -o /harbor/make/photon/core/harbor_core
Done.

这个命令会把编译的harbor_core二进制文件放在make/photon/core目录下。

编译jobservice

make compile_jobservice

这个命令会把编译的harbor_jobservice二进制文件放在make/photon/jobservice目录下。

编译registryctl

make compile_registryctl

这个命令会把编译的harbor_registryctl二进制文件放在make/photon/registryctl目录下。

编译migrate_patch

make compile_notary_migrate_patch

这个命令会把编译的migrate-patch二进制文件放在make/photon/notary目录下。

构建prepare镜像

make -f make/photon/Makefile _build_prepare \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev

注意这里PULL_BASE_FROM_DOCKERHUB不能是true,否则会去dockerhub拉取镜像,那里只有amd64架构的。

这个命令会按如下两个命令,构建出两个镜像:

# BUILD_BASE=true时会构建该镜像
# harbor-prepare-base镜像tag为BASEIMAGETAG
/usr/local/bin/docker build --no-cache -f make/photon/prepare/Dockerfile.base -t goharbor/harbor-prepare-base:dev --label base-build-date=20250107 .

# PULL_BASE_FROM_DOCKERHUB=false使用本地刚构建的harbor-prepare-base镜像
# prepare镜像的tag为VERSIONTAG
/usr/local/bin/docker build --no-cache --pull=false --build-arg harbor_base_image_version=dev --build-arg harbor_base_namespace=goharbor -f make/photon/prepare/Dockerfile -t goharbor/prepare:dev .

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED          SIZE
# prepare基于harbor-prepare-base,将make/photon/prepare目录下的python项目,copy到了镜像中
goharbor/prepare                              dev       6b2ef89246ec   38 seconds ago   208MB
# harbor-prepare-base基于photon,安装了一些必备工具
goharbor/harbor-prepare-base                  dev       3f1f1a9843f4   45 seconds ago   162MB

构建pgsql镜像

make -f make/photon/Makefile _build_db \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev

这个命令会按如下两个命令,构建出两个镜像:

/usr/local/bin/docker build --no-cache -f make/photon/db/Dockerfile.base -t goharbor/harbor-db-base:dev --label base-build-date=20250107 .
/usr/local/bin/docker build --no-cache --pull=false --build-arg harbor_base_image_version=dev --build-arg harbor_base_namespace=goharbor -f make/photon/db/Dockerfile -t goharbor/harbor-db:dev .

这里构建harbor-db-base的时候会报错,编辑make/photon/db/Dockerfile.base修改为:

RUN tdnf install -y shadow gzip postgresql13 findutils bc >> /dev/null \
    # 这里group已存在
    # && groupadd -r postgres --gid=999 \
    && groupadd -r postgres --gid=999 || true \
    # 这里user已存在
    # && useradd -m -r -g postgres --uid=999 postgres \
    && useradd -m -r -g postgres --uid=999 postgres || true \

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED              SIZE
# harbor-db初始化表结构等
goharbor/harbor-db                            dev       48f3fbd04894   About a minute ago   169MB
# 基于photon构建pgsql13镜像
goharbor/harbor-db-base                       dev       3df28325014c   About a minute ago   169MB

构建portal镜像

make -f make/photon/Makefile _build_portal \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev

这个命令会按如下两个命令,构建出两个镜像:

/usr/local/bin/docker build --no-cache -f make/photon/portal/Dockerfile.base -t goharbor/harbor-portal-base:dev --label base-build-date=20250107 .
/usr/local/bin/docker build --no-cache --pull=false --build-arg harbor_base_image_version=dev --build-arg harbor_base_namespace=goharbor --build-arg npm_registry= -f make/photon/portal/Dockerfile -t goharbor/harbor-portal:dev .

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED          SIZE
# 基于node和harbor-portal-base构建portal镜像
goharbor/harbor-portal                        dev       4d8d07c13c9f   35 seconds ago   125MB
# 基于photon构建nginx镜像
goharbor/harbor-portal-base                   dev       569cd73f95e4   2 minutes ago    116MB

构建core镜像

make -f make/photon/Makefile _build_core \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev

这个命令会按如下两个命令,构建出两个镜像:

/usr/local/bin/docker build --no-cache -f make/photon/core/Dockerfile.base -t goharbor/harbor-core-base:dev --label base-build-date=20250107 .
/usr/local/bin/docker build --no-cache --pull=false --build-arg harbor_base_image_version=dev --build-arg harbor_base_namespace=goharbor -f make/photon/core/Dockerfile -t goharbor/harbor-core:dev .

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED          SIZE
goharbor/harbor-core                          dev       34deb90a3320   45 seconds ago   220MB
goharbor/harbor-core-base                     dev       e699127e38d8   48 seconds ago   46.4MB

构建jobservice镜像

make -f make/photon/Makefile _build_jobservice \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev

这个命令会按如下两个命令,构建出两个镜像:

/usr/local/bin/docker build --no-cache -f make/photon/jobservice/Dockerfile.base -t goharbor/harbor-jobservice-base:dev --label base-build-date=20250107 .
/usr/local/bin/docker build --no-cache --pull=false --build-arg harbor_base_image_version=dev --build-arg harbor_base_namespace=goharbor -f make/photon/jobservice/Dockerfile -t goharbor/harbor-jobservice:dev .

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED          SIZE
goharbor/harbor-jobservice                    dev       1c90cae3892e   22 seconds ago   274MB
goharbor/harbor-jobservice-base               dev       b573d990857d   24 seconds ago   46.4MB

构建log镜像

make -f make/photon/Makefile _build_log \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev

这个命令会按如下两个命令,构建出两个镜像:

/usr/local/bin/docker build --no-cache -f make/photon/log/Dockerfile.base -t goharbor/harbor-log-base:dev --label base-build-date=20250107 .
/usr/local/bin/docker build --no-cache --pull=false --build-arg harbor_base_image_version=dev --build-arg harbor_base_namespace=goharbor -f make/photon/log/Dockerfile -t goharbor/harbor-log:dev .

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED          SIZE
goharbor/harbor-log                           dev       ff4d9e48c5fa   31 seconds ago   127MB
goharbor/harbor-log-base                      dev       81f2a5a06b6a   33 seconds ago   127MB

构建nginx镜像

make -f make/photon/Makefile _build_nginx \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev

这个命令会按如下两个命令,构建出两个镜像:

/usr/local/bin/docker build --no-cache -f make/photon/nginx/Dockerfile.base -t goharbor/harbor-nginx-base:dev --label base-build-date=20250107 .
/usr/local/bin/docker build --no-cache --pull=false --build-arg harbor_base_image_version=dev --build-arg harbor_base_namespace=goharbor -f make/photon/nginx/Dockerfile -t goharbor/nginx-photon:dev .

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED          SIZE
goharbor/harbor-nginx-base                    dev       9777a81bf63a   49 seconds ago   116MB
goharbor/nginx-photon                         dev       41fb831783b8   49 seconds ago   116MB

构建registry镜像

make -f make/photon/Makefile _build_registry \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev \
  -e BUILDBIN=true \
  -e REGISTRY_SRC_TAG=v2.8.0

其中BUILDBIN=true表示本地拉取distribution源码,基于REGISTRY_SRC_TAG构建。具体过程在make/photon/registry/builder脚本中。这个脚本通过git apply添加一些代码到distribution源码中。

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED              SIZE
goharbor/registry-photon                      dev       66e96a2cbca5   13 seconds ago       80.1MB
goharbor/harbor-registry-base                 dev       ec78ec83b388   About a minute ago   44.6MB

构建registryctl镜像

make -f make/photon/Makefile _build_registryctl \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED             SIZE
goharbor/harbor-registryctl                   dev       ac4ae05f0e41   2 minutes ago       138MB
goharbor/harbor-registryctl-base              dev       192e77d51d12   2 minutes ago       44.6MB

构建trivy-adapter镜像

make -f make/photon/Makefile _build_trivy_adapter \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev \
  -e TRIVYFLAG=true \
  -e TRIVYVERSION=v0.46.1 \
  -e BUILDBIN=true \
  -e TRIVYADAPTERVERSION=v0.30.18 \
  -e TRIVY_DOWNLOAD_URL=https://github.com/aquasecurity/trivy/releases/download/v0.46.1/trivy_0.46.1_Linux-64bit.tar.gz

BUILDBIN=true表示本地clone构建trivy-adapter二进制文件,trivy还是需要直接下载。本地clone构建trivy-adapter的逻辑在make/photon/trivy-adapter/builder.sh中。

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED              SIZE
goharbor/trivy-adapter-photon                 dev       dcb8a48d4d16   19 seconds ago       467MB
goharbor/harbor-trivy-adapter-base            dev       2ff7e3732d32   About a minute ago   45MB

构建redis镜像

make -f make/photon/Makefile _build_redis \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED             SIZE
goharbor/redis-photon                         dev       43fd89318b4d   4 minutes ago       119MB
goharbor/harbor-redis-base                    dev       8c129a3a51bc   4 minutes ago       119MB

构建chart-server镜像

make -f make/photon/Makefile _build_chart_server \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev \
  -e CHARTFLAG=true \
  -e BUILDBIN=true \
  -e GOBUILDIMAGE=golang:1.21.4 \
  -e CHARTMUSEUM_SRC_TAG=v0.14.0 \
  -e DOCKERIMAGENAME_CHART_SERVER=goharbor/chartmuseum-photon

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED             SIZE
goharbor/chartmuseum-photon                   dev       5f9ad5a5832c   6 seconds ago       238MB
goharbor/harbor-chartserver-base              dev       54070b204d0b   4 minutes ago       44.6MB

构建exporter镜像

make -f make/photon/Makefile _compile_and_build_exporter \
  -e BUILD_BASE=true \
  -e BASEIMAGETAG=dev \
  -e PULL_BASE_FROM_DOCKERHUB=false \
  -e VERSIONTAG=dev \
  -e BASEIMAGETAG=dev \
  -e GOBUILDIMAGE=golang:1.21.4

查看本地镜像列表:

% docker images
REPOSITORY                                    TAG       IMAGE ID       CREATED             SIZE
goharbor/harbor-exporter                      dev       631fe20b4497   6 seconds ago       99MB
goharbor/harbor-exporter-base                 dev       d0f7bc604dc2   11 seconds ago      46.4MB

部署前准备

make prepare -e TRIVYFLAG=true -e CHARTFLAG=true

这个命令首先会执行:

/usr/bin/sed -i '' -e 's/goharbor\/prepare:.*[[:space:]]\+/goharbor\/prepare:dev prepare /' make/prepare

作用是把make/prepare脚本中的prepare镜像替换为之前构建的镜像。

接着执行:

make/prepare --conf /compose_location/harbor.yml --with-trivy --with-chartmuseum

这个脚本会把make/harbor.yml拷贝出来,找到data_volume,接着使用prepare镜像在data_volume里创建如下内容:

% tree
.
├── ca_download
├── chart_storage
├── database
├── job_logs
├── redis
├── registry
├── secret
│   ├── core
│   │   └── private_key.pem
│   ├── keys
│   │   └── secretkey
│   └── registry
│       └── root.crt
└── trivy-adapter
    ├── reports
    └── trivy

14 directories, 3 files

以及在make/common/config目录下创建如下内容,包含各个组件的配置,有的是文件形式,有的是环境变量形式。

tree make/common/config
make/common/config
├── chartserver
│   └── env
├── core
│   ├── app.conf
│   ├── certificates
│   └── env
├── db
│   └── env
├── jobservice
│   ├── config.yml
│   └── env
├── log
│   ├── logrotate.conf
│   └── rsyslog_docker.conf
├── nginx
│   ├── conf.d
│   └── nginx.conf
├── portal
│   └── nginx.conf
├── registry
│   ├── config.yml
│   └── passwd
├── registryctl
│   ├── config.yml
│   └── env
├── shared
│   └── trust-certificates
└── trivy-adapter
    └── env

15 directories, 15 files

以及在make目录下生成docker-compose.yml文件,用于docker-compose部署。

本地部署

make start

这个命令就是使用上一步创建的docker-compose.yml文件来部署。看到如下输出,即为部署成功:

Start complete. You can visit harbor now.

如果出问题了,可以make restart重新部署。

浏览器访问localhost即可进入Harbor控制台。

附录

Harbor配置文件说明

2.7配置

必选参数

配置 参数 描述
hostname   Harbor服务域名。
http port HTTP的端口号。默认80。
https port HTTPS的端口号。默认443。
  certificate SSL证书的路径。
  private_key SSL密钥的路径。
internal_tls enable 启用内部组件HTTPS通信。
  dir 包含内部证书和密钥的目录路径。
harbor_admin_password   系统管理员初始密码。
database   本地数据库配置。选择配置外部数据库时禁用此选项。
  password root密码。
  max_idle_conns 空闲连接池中的最大连接数。
  max_open_conns 数据库的最大打开连接数。
data_volume   指定用于存储Harbor数据的目标主机位置。
trivy   配置Trivy扫描器。
  ignore_unfixed 设置为true以仅显示已修复的漏洞。默认false。
  skip_update 是否从github更新漏洞库。默认false。
  insecure 设置为true以跳过验证注册表证书。默认false。
  github_token 指定gitHub访问令牌提高访问速率。
jobservice max_job_workers worker最大数量。默认10。
notification webhook_job_max_retry webhook最大重试次数。默认10。
chart absolute_url 设置为enabled使Chart使用绝对URL。
log   Harbor使用rsyslog收集每个容器的日志。
  level debug、info、warning、error或fatal。默认info。
  local 设置日志保留参数。
  external_endpoint 启用此选项以将日志转发到syslog服务器。
proxy   配置trivy-adapter、复制作业服务和Harbor使用的代理。如果不需要代理留空。
  http_proxy 配置HTTP代理。
  https_proxy 配置HTTPS代理。
  no_proxy 配置哪些情况下不使用代理。

可选参数

配置 参数 描述
external_url   外部代理地址。启用后hostname不再生效。
storage_service   使用外部存储而不是主机数据卷。
  ca_bundle CA根证书位置。
  filesystem 这里默认是文件系统,也可以是s3等。
  redirect disable/enable,是否禁用注册表重定向。
external_database   外部pgSQL数据库。
  harbor harbor使用的库。
  notary_signer notary signer使用的库。
  notary_server notary server使用的库。
external_redis   外部redis数据库。
  host redisHost:redisPort,哨兵模式多个地址使用逗号分隔。
  sentinel_master_set 哨兵模式时设置。
  password redis密码。
  registry_db_index 服务隔离,registry用的索引号。
  jobservice_db_index 服务隔离,jobservice用的索引号。
  chartmuseum_db_index 服务隔离,chartmuseum用的索引号。
  trivy_db_index 服务隔离,trivy用的索引号。
metric   指标配置。
  enabled 是否暴露指标,默认false。
  port 端口。默认9090。
  path 路径。默认/metrics。

配置文件参考

hostname: harbor.su.com
http:
  port: 80
harbor_admin_password: Harbor12345
database:
  password: root123
  max_idle_conns: 100
  max_open_conns: 900
  conn_max_lifetime: 5m
  conn_max_idle_time: 0
data_volume: /usr/local/harbor-data
trivy:
  ignore_unfixed: false
  skip_update: false
  offline_scan: false
  security_check: vuln
  insecure: false
jobservice:
  max_job_workers: 10
notification:
  webhook_job_max_retry: 10
chart:
  absolute_url: disabled
log:
  level: info
  local:
    rotate_count: 50
    rotate_size: 200M
    location: /var/log/harbor
_version: 2.7.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