为了解决以上问题,Docker 官方提供了一个叫做 registry 的镜像用于搭建本地私有仓库使用。在内部网络搭建的 Docker 私有仓库可以使内网人员下载、上传都非常快速,不受外网带宽等因素的影响,同时不在内网的人员也无法下载我们的镜像,并且私有仓库也支持配置仓库认证功能 。
使用 registry 镜像创建私有仓库容器
安装Docker后,可以通过官方提供的registry镜像来简单搭建一套本地私有仓库环境:
[root@docker ~]# docker run -d --name myregistry -p 5000:5000 -v /mnt/my_registry:/var/lib/registry registry
ad2a3a85f7f4fa1ad059250f566e2e51dbb4d837a7c533f36b5eb60dd2cfe34f
# 参数:
-d 后台运行
--name 为创建的容器命名
-p 端口映射,该服务运行默认使用5000端口通信
-v 数据卷挂载,默认情况下,仓库会创建在容器的 /var/lib/registry 目录下。可以通过 -v 参数来将镜像文件存放在本地的指定路径。
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad2a3a85f7f4 registry "/entrypoint.sh /etc…" 24 seconds ago Up 22 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp myregistry
打开浏览器输入:
http://192.168.126.20:5000/v2/_catalog
显示 {“repositories”:[]} 表示私有仓库搭建成功并且内容为空。
在任意安装Docker服务并可以连接该安装私有仓库主机的主机上(包括安装该私有仓库主机本机)执行以下步骤便可以推送镜像到该私有仓库
修改配置
修改 daemon.json 文件
[root@docker ~]# vim /etc/docker/daemon.json
{
"registry-mirrors": [
"https://e9hk8fzj.mirror.aliyuncs.com",
"https://docker.mirrors.ustc.edu.cn"
],
"insecure-registries": ["192.168.126.20:5000"] # 此行为添加的内容,为运行仓库容器的主机IP,端口与使用registry镜像运行容器时指定的宿主机端口一致
}
重新加载配置信息及重启 Docker 服务
[root@docker ~]# systemctl daemon-reload && systemctl restart docker
镜像打标签
必须先给需要推送上传的镜像设置标签
用法:
docker tag SOURCE_IMAGE[:TAG] IP:PORT/TARGET_IMAGE[:TAG]
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest 1fd8e1b0bb7e 2 months ago 26.2MB
busybox 1.31.1 1c35c4412082 13 months ago 1.22MB
[root@docker ~]# docker tag busybox:1.31.1 192.168.126.20:5000/busybox_v1:1.31.1
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest 1fd8e1b0bb7e 2 months ago 26.2MB
192.168.126.20:5000/busybox_v1 1.31.1 1c35c4412082 13 months ago 1.22MB
busybox 1.31.1 1c35c4412082 13 months ago 1.22MB
将镜像推送至私有仓库
用法:
docker push IP:PORT/TARGET_IMAGE[:TAG]
[root@docker ~]# docker push 192.168.126.20:5000/busybox_v1:1.31.1
The push refers to repository [192.168.126.20:5000/busybox_v1]
1be74353c3d0: Pushed
1.31.1: digest: sha256:fd4a8673d0344c3a7f427fe4440d4b8dfd4fa59cfabbd9098f9eb0cb4ba905d0 size: 527
用法:
# 查询镜像
curl -XGET http://IP:PORT/v2/_catalog
# 查询镜像tag(版本)
curl -XGET http://IP:PORT/v2/IMAGE_NAME/tags/list
# 获取镜像digest_hash
curl http://IP:PORT/v2/IMAGE_NAME/manifests/TAG_NAME --header "Accept: application/vnd.docker.distribution.manifest.v2+json"
在搭建仓库的主机上查看
# 查看数据卷
[root@docker ~]# ls /mnt/my_registry/
docker
[root@docker ~]# ls /mnt/my_registry/docker/registry/v2/repositories/busybox_v1/
_layers _manifests _uploads
# 容器内查看
[root@docker ~]# docker exec -it myregistry /bin/sh
/ # ls /var/lib/registry/docker/registry/v2/repositories/
busybox_v1
[root@docker ~]# curl -XGET http://192.168.126.20:5000/v2/_catalog
{"repositories":["busybox_v1"]}
[root@docker ~]# curl -XGET http://192.168.126.20:5000/v2/busybox_v1/tags/list
{"name":"busybox_v1","tags":["1.31.1"]}
# 或在浏览器访问 http://192.168.126.20:5000/v2/_catalog
或在上传镜像的客户端查看
[root@k8s-master ~]# curl -XGET http://192.168.126.20:5000/v2/_catalog
{"repositories":["busybox_v1"]}
[root@k8s-master ~]# curl -XGET http://192.168.126.20:5000/v2/busybox_v1/tags/list
{"name":"busybox_v1","tags":["1.31.1"]}
# 或在浏览器访问 http://192.168.126.20:5000/v2/_catalog
[root@k8s-master ~]# curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -I -X HEAD http://192.168.126.20:5000/v2/hello_test/manifests/v1
[root@k8s-master ~]# curl -v -X DELETE http://192.168.126.20:5000/v2/hello_test/manifests/sha256:1b26826f602946860c279fce658f31050cff2c596583af237d971f4629b57792
起初,安装私有仓库的时候,并没有思考到删除镜像的问题,当想要删除上传的镜像时,镜像删不掉,比较麻烦,就得重新运行私有仓库容器
Docker仓库在2.1版本中支持了删除镜像的API,但这个删除操作只会删除镜像元数据,不会删除层数据。在2.4版本中对这一问题进行了解决,增加了一个垃圾回收命令,删除未被引用的层数据
首先,registry 需开启删除
因为缺省Docker private registry不允许删除镜像,如果遇到“405 Unsupported” 错误,需要在运行registry容器时修改配置文件中的相关参数
# 开启删除(添加 delete: enabled: true)
[root@docker ~]# docker exec -it myregistry sh -c "sed -i '/storage:/a\ delete:' /etc/docker/registry/config.yml"
[root@docker ~]# docker exec -it myregistry sh -c "sed -i '/delete:/a\ enabled: true' /etc/docker/registry/config.yml"
# 查看
[root@docker ~]# docker exec -it myregistry cat /etc/docker/registry/config.yml
version: 0.1
log:
fields:
service: registry
storage:
delete: # 此行为添加的
enabled: true # 此行为添加的
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
重启私有仓库容器
[root@docker ~]# docker restart myregistry
myregistry
查看仓库中的镜像
[root@docker ~]# curl -XGET http://192.168.126.20:5000/v2/_catalog
{"repositories":["busybox_v1","hello_test"]}
# 查看版本
[root@docker ~]# curl -XGET http://192.168.126.20:5000/v2/hello_test/tags/list
{"name":"hello_test","tags":["v1"]}
[root@docker ~]# curl -XGET http://192.168.126.20:5000/v2/busybox_v1/tags/list
{"name":"busybox_v1","tags":["1.31.1"]}
获取镜像 digest_hash
[root@docker ~]# curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -I -X HEAD http://192.168.126.20:5000/v2/hello_test/manifests/v1
HTTP/1.1 200 OK
Content-Length: 525
Content-Type: application/vnd.docker.distribution.manifest.v2+json
Docker-Content-Digest: sha256:1b26826f602946860c279fce658f31050cff2c596583af237d971f4629b57792
Docker-Distribution-Api-Version: registry/2.0
Etag: "sha256:1b26826f602946860c279fce658f31050cff2c596583af237d971f4629b57792"
X-Content-Type-Options: nosniff
Date: Mon, 28 Jun 2021 08:19:30 GMT
[root@docker ~]# curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -I -X HEAD http://192.168.126.20:5000/v2/busybox_v1/manifests/1.31.1
HTTP/1.1 200 OK
Content-Length: 527
Content-Type: application/vnd.docker.distribution.manifest.v2+json
Docker-Content-Digest: sha256:fd4a8673d0344c3a7f427fe4440d4b8dfd4fa59cfabbd9098f9eb0cb4ba905d0
Docker-Distribution-Api-Version: registry/2.0
Etag: "sha256:fd4a8673d0344c3a7f427fe4440d4b8dfd4fa59cfabbd9098f9eb0cb4ba905d0"
X-Content-Type-Options: nosniff
Date: Mon, 28 Jun 2021 08:24:17 GMT
删除镜像的API
用法:
DELETE /v2/<name>/manifests/<reference>
# 用法:curl -v -X DELETE http://IP:PORT/v2/IMAGE_NAME/manifests/REFERENCE
# name:镜像名称
# reference: 镜像对应sha256值
删除镜像
[root@docker ~]# curl -v -X DELETE http://192.168.126.20:5000/v2/hello_test/manifests/sha256:1b26826f602946860c279fce658f31050cff2c596583af237d971f4629b57792
* About to connect() to 192.168.126.20 port 5000 (#0)
* Trying 192.168.126.20...
* Connected to 192.168.126.20 (192.168.126.20) port 5000 (#0)
> DELETE /v2/hello_test/manifests/sha256:1b26826f602946860c279fce658f31050cff2c596583af237d971f4629b57792 HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.126.20:5000
> Accept: */*
>
< HTTP/1.1 202 Accepted
< Docker-Distribution-Api-Version: registry/2.0
< X-Content-Type-Options: nosniff
< Date: Mon, 28 Jun 2021 08:29:58 GMT
< Content-Length: 0
<
* Connection #0 to host 192.168.126.20 left intact
这里的删除镜像只是删除了一些元数据,需要执行垃圾回收才能真正地从硬盘上删除镜像数据
执行垃圾回收:
registry garbage-collect /etc/docker/registry/config.yml
垃圾回收
用法:
registry garbage-collect /etc/docker/registry/config.yml
进入私有仓库容器进行垃圾回收
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad2a3a85f7f4 registry "/entrypoint.sh /etc…" 2 hours ago Up 7 minutes 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp myregistry
[root@docker ~]# docker exec -it myregistry /bin/sh
/ # registry garbage-collect /etc/docker/registry/config.yml
busybox_v1
busybox_v1: marking manifest sha256:fd4a8673d0344c3a7f427fe4440d4b8dfd4fa59cfabbd9098f9eb0cb4ba905d0
busybox_v1: marking blob sha256:1c35c441208254cb7c3844ba95a96485388cef9ccc0646d562c7fc026e04c807
busybox_v1: marking blob sha256:76df9210b28cbd4bc127844914d0a23937ed213048dc6289b2a2d4f7d675c75e
hello_test
3 blobs marked, 3 blobs and 0 manifests eligible for deletion
blob eligible for deletion: sha256:1b26826f602946860c279fce658f31050cff2c596583af237d971f4629b57792
INFO[0000] Deleting blob: /docker/registry/v2/blobs/sha256/1b/1b26826f602946860c279fce658f31050cff2c596583af237d971f4629b57792 go.version=go1.11.2 instance.id=3e01bf0b-8ca7-4b3f-9114-9290839edb2d service=registry
blob eligible for deletion: sha256:b8dfde127a2919ff59ad3fd4a0776de178a555a76fff77a506e128aea3ed41e3
INFO[0000] Deleting blob: /docker/registry/v2/blobs/sha256/b8/b8dfde127a2919ff59ad3fd4a0776de178a555a76fff77a506e128aea3ed41e3 go.version=go1.11.2 instance.id=3e01bf0b-8ca7-4b3f-9114-9290839edb2d service=registry
blob eligible for deletion: sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726
INFO[0000] Deleting blob: /docker/registry/v2/blobs/sha256/d1/d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726 go.version=go1.11.2 instance.id=3e01bf0b-8ca7-4b3f-9114-9290839edb2d service=registry
成功删除。
要确保私有仓库的安全性,还需要一个安全认证证书,防止发生意想不到的事情。所以需要在搭建私有仓库的 Docker 主机上先生成自签名证书。
创建证书存储目录
[root@docker ~]# mkdir -p /usr/local/registry/certs
生成自签名证书
通过 openssl 先生成自签名证书,运行命令以后需要填写一些证书信息,里面最关键的部分是:Common Name (eg, your name or your server’s hostname) []:192.168.126.20,这里填写的是私有仓库的地址
[root@docker ~]# openssl req -newkey rsa:2048 -nodes -sha256 -keyout /usr/local/registry/certs/domain.key -x509 -days 365 -out /usr/local/registry/certs/domain.crt
Generating a 2048 bit RSA private key
...............+++
...................................+++
writing new private key to '/usr/local/registry/certs/domain.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:cn
State or Province Name (full name) []:sx
Locality Name (eg, city) [Default City]:sl
Organization Name (eg, company) [Default Company Ltd]:test
Organizational Unit Name (eg, section) []:it
Common Name (eg, your name or your server's hostname) []:192.168.126.20 # 这里填写的是私有仓库的地址
Email Address []:[email protected]
openssl req:创建证书签名请求等功能;
-newkey:创建 CSR 证书签名文件和 RSA 私钥文件;
rsa:2048:指定创建的 RSA 私钥长度为 2048;
-nodes:对私钥不进行加密;
-sha256:使用 SHA256 算法;
-keyout:创建的私钥文件名称及位置;
-x509:自签发证书格式;
-days:证书有效期;
-out:指定 CSR 输出文件名称及位置;
生成鉴权密码文件
# 创建存储鉴权密码文件目录
[root@docker ~]# mkdir -p /usr/local/registry/auth
# 如果没有 htpasswd 功能需要安装 httpd
[root@docker ~]# yum install -y httpd
# 创建用户和密码
[root@docker ~]# htpasswd -Bbn zhangsan 123456 > /usr/local/registry/auth/htpasswd
# htpasswd 是 apache http 的基本认证文件,使用 htpasswd 命令可以生成用户及密码文件。用户名zhangsan,密码123456
创建私有仓库容器
[root@docker ~]# docker run -di --name myregistry -p 5000:5000 \
-v /mydata/docker_registry:/var/lib/registry \
-v /usr/local/registry/certs:/certs \
-v /usr/local/registry/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry
d7d612a0c914f0bcf68ac0a9447bdc79ab43e404c2584a48dc1f0ccb19ed1fdc
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d7d612a0c914 registry "/entrypoint.sh /etc…" 14 seconds ago Up 12 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp myregistry
修改Docker客户端的daemon.json文件
[root@docker-client ~]# vim /etc/docker/daemon.json
{
"registry-mirrors": [
"https://e9hk8fzj.mirror.aliyuncs.com",
"https://docker.mirrors.ustc.edu.cn"
],
"insecure-registries": ["192.168.126.20:5000"] # 此行为添加的内容,为运行仓库容器的主机IP,端口与使用registry镜像运行容器时指定的宿主机端口一致
}
在Docker客户端给本地镜像打标签
[root@docker-client ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos 7 8652b9f0cb4c 7 months ago 204MB
[root@docker-client ~]# docker tag centos:7 192.168.126.20:5000/centos_test:v1
[root@k8s-docker-client ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.126.20:5000/centos_test v1 8652b9f0cb4c 7 months ago 204MB
centos 7 8652b9f0cb4c 7 months ago 204MB 7 8652b9f0cb4c 7 months ago 204MB
推送镜像至私有仓库失败
[root@k8s-docker-client ~]# docker push 192.168.126.20:5000/centos_test:v1
The push refers to repository [192.168.126.20:5000/centos_test]
174f56854903: Preparing
no basic auth credentials # 因为没有进行登录认证。
如果直接 push 镜像肯定会失败,并且出现 no basic auth credentials 的错误,这是因为没有进行登录认证。
登录账号
通过 docker login 命令输入账号密码登录私有仓库
[root@k8s-docker-client ~]# docker login 192.168.126.20:5000 -u zhangsan -p 123456
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
推送镜像至私有仓库成功
再次 push 镜像,发现已经可以推送成功了。
[root@k8s-master ~]# docker push 192.168.126.20:5000/centos_test:v1
The push refers to repository [192.168.126.20:5000/centos_test]
174f56854903: Pushed
v1: digest: sha256:e4ca2ed0202e76be184e75fb26d14bf974193579039d5573fb2348664deef76e size: 529
查看
私有镜像仓库的搭建还可以通过 Harbor 实现,Harbor 是由 VMware 公司开源的企业级的 Docker Registry 管理项目,它包括权限管理(RBAC)、LDAP、日志审核、管理界面、自我注册、镜像复制和中文支持等功能。