唠唠闲话

在现代软件开发和运维中,容器技术已经成为构建、部署和管理应用程序的标准工具。然而,在实际操作中,我们常常需要面对一些常见的挑战,如容器访问外部资源的代理配置内网环境下的镜像共享以及企业级镜像管理

本教程将详细介绍三大关键内容,以便更高效地使用容器技术:

  1. 容器代理:在很多网络环境中,直接访问外部资源可能受到限制。我们将介绍如何进行配置,确保顺利访问互联网和外部镜像仓库。

  2. 默认注册表:为了在内网环境中共享镜像,我们可以搭建一个无需登录验证的简单注册表。通过这种方式,可以在团队内部方便地推送和拉取镜像,提高开发效率。

  3. Harbor 注册表:对于需要更高级别管理和安全功能的企业,Harbor 是一个理想的选择。我们将详细介绍如何安装和配置 Harbor,利用其丰富的功能,提升容器镜像管理的安全性和效率。

废话不多说,下边开始吧!

容器代理

在使用 Docker 时,有时需要配置代理以便访问互联网或镜像仓库。以下是配置 Docker 代理的方法:

Linux

  1. 创建或编辑 /etc/systemd/system/docker.service.d/http-proxy.conf 文件:

    1
    2
    sudo mkdir -p /etc/systemd/system/docker.service.d
    sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf
  2. 在文件中添加以下内容,替换 http://proxy.example.com:8080http://proxy.example.com:8080 为代理地址:

    1
    2
    3
    4
    [Service]
    Environment="HTTP_PROXY=http://proxy.example.com:8080"
    Environment="HTTPS_PROXY=http://proxy.example.com:8080"
    Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.somecorporation.com"
  3. 重新加载系统守护进程并重启 Docker:

    1
    2
    sudo systemctl daemon-reload
    sudo systemctl restart docker

Windows

  1. 在 PowerShell 中创建或编辑 C:\ProgramData\Docker\config\daemon.json 文件:

    1
    notepad C:\ProgramData\Docker\config\daemon.json
  2. 在文件中添加代理配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "proxies": {
    "default": {
    "httpProxy": "http://proxy.example.com:8080",
    "httpsProxy": "http://proxy.example.com:8080",
    "noProxy": "localhost,127.0.0.1,docker-registry.somecorporation.com"
    }
    }
    }
  3. 重启 Docker 服务:

    1
    Restart-Service docker

Dockerfile

如果是在构建阶段需要,可以直接在 Dockerfile 中添加环境变量,并在结尾 unset

1
2
3
4
5
6
ENV http_proxy http://proxy.example.com:8080
ENV https_proxy http://proxy.example.com:8080

...

RUN unset http_proxy https_proxy

自建镜像站

如果有国外的 vps,可以直接通过 Nginx 反向代理配置镜像站。

示例配置如下,其中 docker.example.com 替换为实际域名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
server {
listen 443 ssl;
server_name docker.example.com;

ssl_certificate /etc/letsencrypt/live/docker.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/docker.example.com/privkey.pem;

proxy_ssl_server_name on; # 启用SNI

ssl_session_timeout 24h;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;

location / {
proxy_pass https://registry-1.docker.io;
proxy_set_header Host registry-1.docker.io;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;
proxy_intercept_errors on;
recursive_error_pages on;
error_page 301 302 307 = @handle_redirect;
}
location @handle_redirect {
resolver 1.1.1.1;
set $saved_redirect_location '$upstream_http_location';
proxy_pass $saved_redirect_location;
}
}

然后修改 /etc/docker/daemon.json 文件,添加镜像站地址:

1
2
3
{
"registry-mirrors": ["https://docker.example.com"]
}

重启 Docker 服务:

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

当然,如果不方便重启 docker 可以直接添加前缀,比如拉取 hello-world 镜像:

1
docker pull docker.example.com/library/hello-world

默认注册表

在内网环境中共享镜像,可以搭建一个无需登录验证的简单注册表,并使用 HTTP 协议进行通信。

搭建方法

创建一个 docker-compose.yml 文件,并定义以下内容:

1
2
3
4
5
6
7
8
9
10
version: '3'

services:
registry:
image: registry:2
ports:
- "5000:5000"
volumes:
- ./data:/var/lib/registry
restart: always

在这个配置中,我们定义了一个名为 registry 的服务,使用 Docker 官方提供的 registry:2 镜像。配置将本机的 5000 端口映射到容器的 5000 端口,通过数据卷 ./data 将注册表的数据保存到本地。restart: always 确保在容器意外停止后自动重启。

docker-compose.yml 文件所在的目录下,运行以下命令来启动服务:

1
docker-compose up -d

配置 Nginx

添加 Nginx 配置文件,以使用 https 访问注册表。参考配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
listen 443 ssl;
server_name registry.example.com; # 使用您的实际域名

ssl_certificate /etc/letsencrypt/live/registry.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/registry.example.com/privkey.pem;

# 配置安全的 SSL 协议
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers on;

# 添加其他任意需要的 SSL 配置

location / {
proxy_pass http://localhost:5000; # 将请求转发给本地的注册表服务
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

常用命令

启动服务后,可以通过以下命令管理和使用注册表。

  1. 给本地镜像添加标签,使其指向我们的注册表:

    1
    docker tag alpine registry.example.com/alpine
  2. 推送镜像,将标记的镜像推送到注册表:

    1
    docker push registry.example.com/alpine
  3. 拉取镜像,从注册表中拉取:

    1
    docker pull registry.example.com/alpine
  4. 查看注册表的镜像,使用 curl 命令查看注册表中已存储的镜像列表:

    1
    curl http://registry.example.com/v2/_catalog

镜像导入导出

除了通过注册表共享容器镜像,还可以选择直接传输镜像文件,这在某些情况下可能更为便捷。

导出镜像

使用 docker save 命令将镜像导出为一个 tar 文件:

1
docker save -o <filename>.tar <image>

例如,将 alpine 镜像导出到 alpine.tar 文件:

1
docker save -o alpine.tar alpine

传输文件

使用 SCP、FTP 或其他网络文件传输方法将导出的 tar 文件从一台服务器传输到另一台服务器。例如,使用 SCP 命令:

1
scp alpine.tar user@destination_host:/path/to/destination

导入镜像

在目标服务器上使用 docker load 命令从 tar 文件中导入镜像:

1
docker load -i <filename>.tar

例如,从 alpine.tar 文件导入镜像:

1
docker load -i alpine.tar

这种方法可以有效地在不同服务器之间传输和共享 Docker 镜像,尤其适用于没有设置注册表或者网络条件不支持直接推送和拉取镜像的场景。

Harbor 注册表

方法概述

在选择容器注册表时,有多种选项可供选择,从简单到复杂的三种方案包括:

  1. Portus:一个轻量级的 Docker 镜像仓库管理工具,适合小型团队和简单的需求。它提供了基本的镜像管理功能和用户权限控制。

  2. Harbor:由 VMware 开发的开源企业级容器镜像仓库,提供了高级功能,如镜像复制、漏洞扫描、身份认证和角色管理,非常适合大中型企业的需求。

  3. Nexus:一个功能强大的仓库管理工具,支持多种格式的包管理(如 Maven、NPM 等),包括 Docker 镜像。它提供了丰富的企业级功能,但配置较为复杂。

教程选择 Harbor,其有以下优点:

  • 功能丰富:Harbor 提供了企业级所需的各类功能,包括镜像复制、漏洞扫描、内容签名和策略管理等。
  • 易于使用:相较于 Nexus,Harbor 的配置和管理更为简便,且有详细的文档支持。
  • 开源社区支持:Harbor 拥有活跃的开源社区和持续的更新维护,确保了其稳定性和安全性。

更多关于 Harbor 的信息和文档请参考 Harbor 官方文档

下载安装包

Harbor 发行页 下载安装包,可以选择在线版或离线版。这里以 offline-installer 为例:

1
2
wget -c https://github.com/goharbor/harbor/releases/download/v2.11.0/harbor-offline-installer-v2.11.0.tgz
tar xvf harbor-offline-installer-v2.11.0.tgz

仓库还有一个 *.asc 签名文件,用于验证下载文件的完整性。

解压后的目录结构如下:

1
2
3
4
5
6
7
harbor
├── common.sh
├── harbor.v2.11.0.tar.gz
├── harbor.yml.tmpl
├── install.sh
├── LICENSE
└── prepare

在开始安装前,我们需要编辑 harbor.yml 文件以配置必要的参数。

编辑配置文件

Harbor 提供了模板文件 harbor.yml.tmpl。复制并修改该文件:

1
cp harbor.yml.tmpl harbor.yml

根据需要修改 hostnamehttp 端口。如果要启用 HTTPS,可以用 certbot 生成域名证书(参考另一篇博客 Let's Encrypt 域名证书增强网站安全),也可以选择注释掉 https 字段,在外部通过 Nginx 配置 SSL。

如果希望用 harbor 复制镜像,比如来自 docker hub 或 Github,可设置 proxy 字段:

1
2
3
4
5
6
7
8
proxy:
http_proxy:
https_proxy:
no_proxy:
components:
- core
- jobservice
- trivy

更详细的配置说明请参考 Harbor 官方文档

安装

Harbor 默认安装选项不包括 Trivy,这是由 Aqua Security 开发的开源漏洞扫描器。可通过以下命令安装 Harbor 并包含 Trivy:

1
sudo ./install.sh --with-trivy

安装完成后,会提示:

1
✔ ----Harbor has been installed and started successfully.----

此时目录下会生成一个 docker-compose.yml 文件,可以通过 docker-compose down/up -d 来停止或启动服务。

安装完成后,可以通过配置文件中指定的端口访问 Harbor。首次登录页面如下,默认用户名为 admin,密码参考 harbor.yml 配置。

20240608072647

更新配置

如果要修改 harbor.yml 对 Harbor 进行重新配置,先使用 docker-compose 停止服务,修改好文件后,运行 prepare 脚本:

1
2
3
4
# cd harbor 安装目录
docker-compose down
# vim harbor.yml
sudo ./prepare

Nginx 反向代理

为了更好地管理和访问 Harbor,可以使用 Nginx 作为反向代理。以下是一个参考配置,基于 Github Issue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
server{
listen 80;
server_name your.domain.com;
rewrite ^.*$ https://$host$request_uri?;
}

upstream harbor {
server localhost:8041;
}

server {
listen 443 ssl;
server_name your.domain.com;
ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem;

location / {
proxy_pass http://harbor/;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

proxy_buffering off;
proxy_request_buffering off;
}
}

基本使用

登录 Harbor 后,你可以创建自己的账号以便日常使用。

使用方式与 DockerHub 类似:

1
2
3
4
5
6
7
# 登录账号
docker login your.domain.com
# 推送镜像
docker tag myapp:latest your.domain.com/yourproject/myapp:latest
docker push your.domain.com/yourproject/myapp:latest
# 拉取镜像
docker pull your.domain.com/yourproject/myapp:latest

上传后在项目中查看镜像列表,如果希望免登录拉取镜像,可设置为公开。

Harbor 管理

Harbor 管理员页面提供了更多功能,包括用户管理,项目管理,镜像复制,漏洞扫描,垃圾回收,审计日志等。

20240608074101

镜像复制

在 Harbor 和非 Harbor 注册表之间复制资源。我们主要演示如何从 Docker Hub 复制镜像到 Harbor。

创建复制端点

使用拥有 Harbor 系统管理员权限的账号登录到 Harbor 界面。

在仓库管理中,点击新建目标:

20240609151831

选择镜像源

20240609151856

如果需要登录鉴权,可以填写 Access IDAccess Secret 字段。如果目标是 http 资源,可以把 Verify Remote Cert 勾选去掉。

新建复制规则

点击复制管理,新建规则:
20240609162758

以 OneAPI 为例,如下进行配置:

20240609163216

参数说明:

  • 名称/name:规则名称,必填,小写数字下划线组成
  • 描述/description:规则描述
  • 复制模式/replication mode:拉取或推送
  • 源资源过滤器/source resource filter:可以使用通配符匹配需要拉取的资源
  • 源仓库/source registry:上一步设置的端点,比如 dockerhub
  • 目标命名空间/source resource filter:目标命名空间,默认放置在与源注册表中相同的命名空间中。
  • 触发模式/trigger mode:手动或定时或事件驱动,这里选手动
  • 带宽/bandwidth:复制带宽限制,默认无限制
  • 覆盖/override:如果目标已有镜像,是否覆盖

如果选择了手动触发,添加规则后,在复制管理中点击复制按钮,进行复制操作,下方会显示任务及进展。

20240609163124

此外,名称和标签过滤器支持以下模式:

  • *:匹配任何非分隔符字符序列 /
  • **:匹配任何字符序列,包括路径分隔符 /。注意双星号必须作为独立路径组件出现,如 /path*/**
  • ?:匹配任何单个非分隔符字符 /
  • {alt1,…}:匹配逗号分隔的任何一个替代项。

举个例子:

模式 匹配 不匹配
* library/hello-world library/my/hello-world
** library/hello-world, library/my/hello-world
{library,goharbor}/** library/hello-world, goharbor/harbor-core google/hello-world
1.? 1.0 1.01

此外,触发模式支持三种类型,可根据需求选择配置:

  • 手动:在需要时手动复制资源
  • 计划:通过定义 cron 作业定期复制资源
  • 基于事件:当新资源推送到项目或工件被重新标记时,它会立即复制到远程注册表。如果您选择“在本地删除时删除远程资源”,则当删除工件时,它会自动从复制目标中删除。

API 访问

Harbor 提供了 RESTful API,可以通过 API 访问 Harbor 的各种功能。例如,可以使用 API 创建项目、添加用户、推送和拉取镜像等。

部署后,在 https://yourdomain.com/devcenter-api-2.0 查看 API 文档,或者直接查看 swagger.yaml 文件。

1
curl -u admin:yourpassword -X GET "https://yourdomain.com/api/v2.0/registries" | jq

下边演示几个例子:

  1. 查看已添加注册表
  2. 添加注册表
  3. 查看复制规则
  4. 创建复制规则
  5. 删除复制规则
  6. 查看复制任务
  7. 启动复制任务

查看仓库注册表

命令如下:

1
curl -u $HARBOR_USER:$HARBOR_PASSWORD -X GET "$HARBOR_HOST/api/v2.0/registries" | jq

返回结果:

1
2
3
4
5
6
7
8
9
10
11
12
[
{
"creation_time": "2024-06-09T08:13:07.429Z",
"credential": {},
"id": 1,
"name": "dockerhub",
"status": "healthy",
"type": "docker-hub",
"update_time": "2024-06-09T08:13:07.429Z",
"url": "https://hub.docker.com"
}
]

添加注册表

以 Github 为例,命令如下:

1
2
3
4
5
6
7
curl -u $HARBOR_USER:$HARBOR_PASSWORD -X POST "$HARBOR_HOST/api/v2.0/registries" -H "Content-Type: application/json" -d '{
"name": "github",
"url": "https://ghcr.io",
"type": "github-ghcr",
"description": "GitHub Container Registry",
"insecure": true
}'

如果需要验证,可以加上 credential 字段:

1
2
3
4
5
"credential": {
"type": "basic",
"access_key": "youraccesskey",
"access_secret": "yoursecret"
}

一般地,可以设置类型为 docker-registry 和自定义 URL。

查看复制规则

命令如下:

1
2
curl -u $HARBOR_USER:$HARBOR_PASSWORD -X GET \
"$HARBOR_HOST/api/v2.0/replication/policies" -H 'accept: application/json' | jq

返回结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[
{
"copy_by_chunk": false,
"creation_time": "2024-06-09T08:29:00.841Z",
"description": "OneAPI 镜像更新。",
"dest_namespace_replace_count": 1,
"dest_registry": {
"creation_time": "0001-01-01T00:00:00.000Z",
"credential": {
"access_secret": "*****",
"type": "secret"
},
"id": 0,
"insecure": true,
"name": "Local",
"status": "healthy",
"type": "harbor",
"update_time": "0001-01-01T00:00:00.000Z",
"url": "http://core:8080"
},
"enabled": true,
"filters": [
{
"type": "name",
"value": "justsong/one-api"
},
{
"decoration": "matches",
"type": "tag",
"value": "latest"
}
],
}
]

创建复制规则

命令如下,其中 dest_registry 根据前边返回的结果填写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
curl -u $HARBOR_USER:$HARBOR_PASSWORD -X POST -H 'Content-Type: application/json'\
$HARBOR_HOST/api/v2.0/replication/policies -d '{
"name": "one-api",
"replication_mode": "pull",
"dest_registry": {
"id": 1,
"name": "dockerhub"
},
"enabled": true,
"trigger": {
"type": "manual"
},
"filters": [
{
"type": "name",
"value": "justsong/one-api"
},
{
"decoration": "matches",
"type": "tag",
"value": "latest"
}
]
}'

除了 manual 触发模式,还有 scheduledevent_based 两种模式,比如:

1
2
3
4
5
6
7
8
{
"trigger": {
"trigger_settings": {
"cron": "0 1 * * 1 *"
},
"type": "scheduled"
}
}

删除复制规则

命令如下,其中 /1 为前边返回的 ID:

1
2
curl -u $HARBOR_USER:$HARBOR_PASSWORD -X DELETE \
"$HARBOR_HOST/api/v2.0/replication/policies/1" -H 'accept: application/json'

查看复制任务

命令如下:

1
2
curl -u $HARBOR_USER:$HARBOR_PASSWORD -X GET \
"$HARBOR_HOST/api/v2.0/replication/executions" -H 'accept: application/json' | jq

返回结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[
{
"end_time": "2024-06-09T12:13:00.000Z",
"failed": 0,
"id": 12,
"in_progress": 0,
"policy_id": 8,
"start_time": "2024-06-09T12:12:51.245Z",
"status": "Succeed",
"status_text": "",
"stopped": 0,
"succeed": 1,
"total": 1,
"trigger": "manual"
}
]

启动任务

命令如下,其中 policy_id 为前边返回的结果:

1
2
3
4
5
curl -u $HARBOR_USER:$HARBOR_PASSWORD -X POST \
"$HARBOR_HOST/api/v2.0/replication/executions"\
-H 'accept: application/json'\
-H 'Content-Type: application/json'\
-d '{"policy_id": 8}'

升级和迁移

配置邮箱