参考文章:[Docker 技术入门与实战 第3板]
本章将具体讲解 Registry 的使用技巧,并通过案例来展示如何搭建一个功能完善的私有镜像仓库。在搭建完本地的私有仓库服务后,来剖析 Registry 的配置参数,最后会通过编写脚本来实现对镜像的快速批量化管理。
本地安装运行 Registry
有时候需要本地运行仓库服务,可以通过源码的方式进行安装。
首先安装 Golang 环境支持,以 CentOS 为例,可以执行如下命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# 下载 Go 安装包
wget https://golang.google.cn/dl/go1.15.6.linux-amd64.tar.gz
# 解压安装包
sudo tar -zxf go1.15.6.linux-amd64.tar.gz -C /usr/local/
# 添加环境变量
cat >/etc/profile.d/go.sh<<EOF
#!/bin/bash
GO_HOME=/usr/local/go
PATH=$PATH:$GO_HOME/bin
EOF
# 测试
source /etc/profile.d/go.sh
go version
go version go1.15.6 linux/amd64在 GO 家目录下,创建
src/github.com/docker/
目录,并拉取 registry 源码1
2
3
4
5
6
7# 创建目录
sudo mkdir -p /usr/local/go/src/github.com/docker
# 拉取代码
cd /usr/local/go/src/github.com/docker/
git clone https://github.com/docker/distribution.git
cd distribution将自带的模板复制到
/etc/docker/registry
目录下,创建存储目录/var/lib/registry
1
2
3sudo mkdir /etc/docker/registry
sudo cp cmd/registry/config-dev.yml /etc/docker/registry/config.yml
sudo mkdir /var/lib/registry执行安装操作
1
2
3
4
5
6cd /usr/local/go/src/github.com/docker/distribution
make PREFIX=/usr/local/go clean binaries
# 编译安装时可能会报 代理错误,此时需要设置 go 环境使用 GO111MODULE
go env -w GO111MODULE=on
# 使用阿里云代理
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct编译安装后,可执行的二进制文件在
distribution/bin
下面,添加环境变量1
2
3
4
5
6sudo vim /etc/profile.d/registry.sh
#!/bin/bash
REGISTRY_HOME=/usr/local/go/src/github.com/docker/distribution
PATH=$PATH:$REGISTRY_HOME/bin可以通过下面的命令来启动 registry
1
registry serve /etc/docker/registry/config.yml
此时使用
curl
访问本地的 5000 端口,看到返回信息为200 OK
,则说明运行成功1
2
3
4
5
6
7
8$ curl -i http://127.0.0.1:5000/v2/
HTTP/1.1 200 OK
Content-Length: 2
Content-Type: application/json
Docker-Distribution-Api-Version: registry/2.0
X-Content-Type-Options: nosniff
Date: Tue, 22 Dec 2020 02:00:27 GMT
{}
配置 TLS 证书
当本地主机运行 registry 服务后,所有能访问到该主机的 Docker Host 都可以把它作为私有仓库使用,只需要在镜像名称前面加上具体的服务器地址即可。
- 例如,将本地的 ubuntu:latest 镜像上传到私有仓库 registry.59izt.com:
1 | docker tag ubuntu:latest registry.59izt.com:5000/ubuntu:latest |
- 或者从私有仓库 registry.59izt.com 下载镜像到本地
1 | docker pull registry.59izt.com:5000/ubuntu |
私有仓库需要启用 TLS 认证,否则会报错。在前面我们介绍了通过添加 DOCKER_OPTS=”–insecure-registry reistry.59izt.com:5000” 来避免这个问题。在这里将介绍如何获取和生成 TLS 证书。
使用 OpenSSL 工具生成私人证书
1
2
3mkdir -p /data/docker/registry/certs
cd /data/docker/registry/
openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/registry.59izt.com.key -x509 -days 365 -out certs/registry.59izt.com.crt生成过程中会提示输入各种信息,注意 Common Name(CN)处一定要填入跟访问域名相同的域名,例如这里应该为 registry.59izt.com。其中证书需要发给用户,并且配置到用户 Docker Host 上,注意路径需要跟域名一致,如:
1
/etc/docker/certs.d/registry.59izt.com:5000/ca.crt
从代理商申请证书,如果 registry 服务需要对外公开,需要申请大家都认可的证书。知名的代理商包括 SSLs.com,GoDaddy.com,LetsEncrypt.org,GlobalSign.com 等。用户可以自行选择权威的证书提供商;
启用证书,当拥有秘钥文件和证书文件后,可以配置 registry 启用证书支持,主要通过使用 REGISTRY_HTTP_TLS_CERTIFICTE 和 REGISTRY_HTTP_TLS_KEY 参数:
1 | docker run -dit \ |
管理访问权限
通常在生产环境中,队友私有仓库还需要进行访问代理并提供认证和用户管理。
Docker Registry v2 的认证模式
Docker Registry v2 的认证模式和 v1 有了较大的变化,降低了系统的复杂度,减少了服务之间的交互次数,其基本工作模式如下所示:
具体交互过程包括如下步骤:
- Docker Daemon 或者其他客户端尝试访问 Registry 服务器,比如 pull,push 或者访问 manifiest 文件;
- 在 Registry 服务器开启了认证服务模式时,就会直接返回 401 Unauthorized 错误,并通知调用方如何获取授权;
- 调用方按照要求,想 Authorization Service 发送请求,并携带 Authorization Service 需要的信息,比如用户名,密码;
- 如果授权成功,则可以拿到合法的 Bearer Token,来标识该请求方可以获得的权限;
- 请求方将拿到的 Bearer Token 加到请求的 Authorization header 中,再次尝试步骤 1中的请求;
- Registry 服务通过验证 Bearer Token 以及 JWT 格式的授权数据,来决定用户是否有权限进行请求的操作。
当启用认证服务时,需要注意以下两个地方:
- 对于 Authorization Service,Docker 官方目前并没有放出对应的实现方案,需要自行实现对应的服务接口;
- Registry 服务和 Authorization 服务之间通过证书进行 Bearer Token 的生成和认证,所以要保证两个服务之间证书的匹配。
除了使用第三方实现的认证服务外(如 docker_auth,SUSE Portus 等),还可以通过 nginx 代理方式来配置基于用户名和密码的认证。
配置 Nginx 代理
使用 Nginx 来代理 registry 服务的原理十分简单,在上一节中,我们让 registry 服务监听在 127.0.0.1:5000,这意味着只允许本机才能通过 5000 端口访问到,其他主机是无法访问到的。为了让其他主机访问到,可以通过 Nginx 监听在对外地址的 15000 端口,当外部访问请求到达 15000 端口时,内部再将请求转发到本地的 5000 端口。具体操作如下:
首先,安装 Nginx
1
2
3yum install -y nginx
systemctl start nginx
systemctl enable nginx在 /etc/nginx/conf.d/ 目录下,创建新的站点配置文件 registry.59izt.com.conf,代理本地的 15000 端口转发到 5000 端口。配置文件内容如下:
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
35
36
37
38
39upstream docker-registry {
server localhost:5000;
}
server {
listen 443 ssl;
server_name registry.59izt.com;
add_header 'Docker-Distribution-Api-Version' 'registry/2.0' always;
# If youi have SSL certification files, then can enable this section.
ssl_certificate /etc/nginx/ssl/registry.59izt.com.crt;
ssl_certificate_key /etc/nginx/ssl/registry.59izt.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_timeout 10m;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_buffer_size 1400;
ssl_stapling_verify on;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Authorization "";
proxy_set_header Accept-Encoding "";
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 600;
client_max_body_size 0;
chunked_transfer_encoding on;
location /v2/ {
# if ( $http_user_agent ~ "$(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*\$" ) {
# return 404;
# }
proxy_pass http://docker-registry;
}
}创建 Nginx 证书存放目录以及拷贝证书
1
2mkdir /etc/nginx/ssl/
cp /data/docker/registry/certs/* /etc/nginx/ssl/测试 Nginx 配置文件,并重新加载配置文件
1
2nginx -t
nginx -s reload重新启动 docker 容器
1
2
3
4
5
6
7
8
9docker run -dit \
--restart=always \
--name registry \
-v /data/docker/registry/certs:/certs \
-v /data/docker/registry/data:/var/lib/registry \
-e REGISTRY_HTTP_SECRET=/certs/registry.59izt.com.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/registry.59izt.com.key \
-p 127.0.0.1:5000:5000 \
registry:2测试上传本地的 ubuntu:latest 镜像
1
2
3
4
5
6
7
8docker tag ubuntu:latest 127.0.0.1:443/ubuntu:latest
docker push 127.0.0.1:443/ubuntu:latest
The push refers to repository [127.0.0.1:443/ubuntu]
f6253634dc78: Pushed
9069f84dbbe9: Pushed
bacd3af13903: Pushed
latest: digest: sha256:4e4bc990609ed865e07afc8427c30ffdddca5153fd4e82c20d8f0783a291e241 size: 943
添加用户认证
公共仓库 Docker Hub 是通过注册索引(index) 服务来实现的。由于 index 服务并没有完善的开源实现,在这里介绍基于 Nginx 代理的用户访问管理方案。Nginx 支持基于用域名和密码的访问管理。
首先,在刚才的 Nginx 配置文件中添加以下内容两行:
1
2
3
4
5
6
7
8...
# 第一个 location 用于访问 docker-registry,需要进行认证
location / {
auth_basic "Please Input username/password";
auth_basic_user_file /data/docker/registey/auth/htpasswd;
proxy_pass http://docker-registry;
}
...代码解释:
- auth_basic,说明启用认证服务,不通过的请求将无法转发。
- auth_basic_user_file 指定了验证的用户名和密码存储文件;
/data/docker/registey/auth/htpasswd 文件中的用户名和密码格式为每一行放一个用户名,密码对。例如:
1
2user1:password1
user2:password2创建用户 wanwu,需要注意的是,密码字段存储的不是明文,而是使用 crypt 函数加密过的字符串。需要生成加密后的字符串,可以使用 htpasswd 工具,首先安装 apache2-utils(Ubuntu),httpd-tools(CentOS)
1
sudo htpasswd -c /data/docker/registry/auth/htpasswd wanwu
添加更多用户,可以重复上面的命令(密码文件存在后,不需要再使用 -c 选项来新建创建)。
或者使用 registry 容器创建,方法为:
1
docker run --entrypoint htpasswd registry:2.6.2 -Bbn wanwu test123 > /data/docker/registry/auth/htpasswd
最后重启 Nginx 服务
重启 registry 容器,命令如下:
1
2
3
4
5
6
7
8
9docker run -d --name registry \
-p 127.0.0.1:5000:5000 \
--restart=always \
-v /data/docker/registry/certs:/certs \
-v /data/docker/registry/data:/var/lib/registry \
-e REGISTRY_STORAGE_DELETE_ENABLED=true \
-e REGISTRY_HTTP_SECRET=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry:2此时,通过浏览器访问本地服务
https://registry.59izt.com/v2/
,会弹出对话框,提示需要输入用户名和密码。通过命令行访问,需要在地址前面带上用户名和密码才能正常返回:
通过命令行访问,需要在地址前面带上用户名和密码才能正常返回1
2
3
4
5
6
7
8
9
10
11
12curl -i -k --insecure -u wanwu:test123 https://registry.59izt.com/v2/_catalog
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Sat, 26 Dec 2020 13:02:24 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 49
Connection: keep-alive
Docker-Distribution-Api-Version: registry/2.0
X-Content-Type-Options: nosniff
Docker-Distribution-Api-Version: registry/2.0
{"repositories":["client-hello-world","ubuntu"]}除了使用 Nginx 作为反向代理外,Registry 自身也支持简单的基于用户名和密码的认证,以及基于 Token 的认证。可以通过如下环境变量来指定。
- REGISTRY_AUTH: htpasswd
- REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
- REGISTRY_AUTH_HTPASSWD_REALM: basic
用 Compose 启动 Registry
一般情况下,用户使用 Registry 需要的配置,包括存储路径,TLS证书和用户认证。这里提供一个基于 Docker Compose 的快速启动 Registry 的模板
1 | registry: |