在开发docker app的时候,为了共享这个镜像,导出再分发给团队成员是比较麻烦的做法。Docker 提供了一个 Registry 给我们上传开发好的镜像(类似 maven 的私有仓库),这样需要使用的团队成员去 registry 上下载即可。
下面介绍下如何使用
1. 下载官方镜像 docker pull registry
[root@localhost ~]# docker pull registry
Using default tag: latest
latest: Pulling from library/registry
ab7e51e37a18: Pull complete
c8ad8919ce25: Pull complete
5808405bc62f: Pull complete
f6000d7b276c: Pull complete
f792fdcd8ff6: Pull complete
Digest: sha256:9d295999d330eba2552f9c78c9f59828af5c9a9c15a3fbd1351df03eaad04c6a
Status: Downloaded newer image for registry:latest
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest 177391bcf802 6 days ago 33.3MB
[root@localhost ~]#
2. 按照官方说明(传送门)启动
[root@localhost ~]# docker run -d -p 5000:5000 --name registry registry
acff7e5e012f996a7a03c7b7bf26773755cfa908cde3ae1dbdcd8ec970e2dff1
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
acff7e5e012f registry "/entrypoint.sh /e..." 6 seconds ago Up 6 seconds 0.0.0.0:5000->5000/tcp registry
[root@localhost ~]#
这样私有的 docker registry 就建好了。
接下来作为 demo 下载一个简单的 hello-world
[root@localhost ~]# docker pull hello-world
Using default tag: latest
latest: Pulling from library/hello-world
ca4f61b1923c: Pull complete
Digest: sha256:be0cd392e45be79ffeffa6b05338b98ebb16c87b255f48e297ec7f98e123905c
Status: Downloaded newer image for hello-world:latest
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest 177391bcf802 6 days ago 33.3MB
hello-world latest f2a91732366c 2 weeks ago 1.85kB
[root@localhost ~]#
重命名镜像名称 docker tag <镜像名> <仓库地址:端口>/新的镜像名
[root@localhost ~]# docker tag hello-world localhost:5000/my-hello-world
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest 177391bcf802 6 days ago 33.3MB
hello-world latest f2a91732366c 2 weeks ago 1.85kB
localhost:5000/my-hello-world latest f2a91732366c 2 weeks ago 1.85kB
[root@localhost ~]#
上传镜像 docker push <仓库地址:端口>/新的镜像名
[root@localhost ~]# docker push localhost:5000/my-hello-world
The push refers to a repository [localhost:5000/my-hello-world]
f999ae22f308: Pushed
latest: digest: sha256:8072a54ebb3bc136150e2f2860f00a7bf45f13eeb917cca2430fcd0054c8e51b size: 524
[root@localhost ~]#
删除本地镜像
[root@localhost ~]# docker rmi localhost:5000/my-hello-world
Untagged: localhost:5000/my-hello-world:latest
[root@localhost ~]# docker rmi hello-world
Untagged: hello-world:latest
Untagged: hello-world@sha256:be0cd392e45be79ffeffa6b05338b98ebb16c87b255f48e297ec7f98e123905c
Deleted: sha256:f2a91732366c0332ccd7afd2a5c4ff2b9af81f549370f7a19acd460f87686bc7
Deleted: sha256:f999ae22f308fea973e5a25b57699b5daf6b0f1150ac2a5c2ea9d7fecee50fdf
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest 177391bcf802 6 days ago 33.3MB
[root@localhost ~]#
从仓库下载镜像 docker pull <仓库地址:端口>/镜像名
[root@localhost ~]# docker pull localhost:5000/my-hello-world
Using default tag: latest
latest: Pulling from my-hello-world
ca4f61b1923c: Pull complete
Digest: sha256:8072a54ebb3bc136150e2f2860f00a7bf45f13eeb917cca2430fcd0054c8e51b
Status: Downloaded newer image for localhost:5000/my-hello-world:latest
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest 177391bcf802 6 days ago 33.3MB
localhost:5000/my-hello-world latest f2a91732366c 2 weeks ago 1.85kB
[root@localhost ~]#
重命名一下就可以使用了
[root@localhost ~]# docker tag localhost:5000/my-hello-world my-hello-world
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest 177391bcf802 6 days ago 33.3MB
my-hello-world latest f2a91732366c 2 weeks ago 1.85kB
localhost:5000/my-hello-world latest f2a91732366c 2 weeks ago 1.85kB
[root@localhost ~]#
基本概念就是这样,但是还有一些问题。
1. 这样的启动方式,上传的数据都是作为一个 docker volume 存储在宿主机上面。如果这个 registry 容器被删除,那么容器内的数据也会丢失,即我们上传的镜像。
所以我们需要将 registry 的镜像地址映射到宿主机上面,registry 在容器内部的存储地址是 /var/lib/registry/, 通过添加 -v 映射到宿主机。
[root@localhost ~]# docker rm -f registry
registry
[root@localhost ~]# docker run --name registry -d -p 5000:5000 -v /home/saul/registry:/var/lib/registry registry
a107eda18508d791085ebcec5cbb9378d496239fba0f7347e998eeda5966dc18
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a107eda18508 registry "/entrypoint.sh /e..." 4 seconds ago Up 3 seconds 0.0.0.0:5000->5000/tcp registry
[root@localhost ~]#
将原来的容器删除,重新建一个。这里我映射到了 /home/john/registry 这个目录下。
再次上传镜像
[root@localhost registry]# docker push localhost:5000/my-hello-world
The push refers to a repository [localhost:5000/my-hello-world]
f999ae22f308: Pushed
latest: digest: sha256:8072a54ebb3bc136150e2f2860f00a7bf45f13eeb917cca2430fcd0054c8e51b size: 524
2. 如何查看镜像
官网提供了一套操作 registry 的 api (传送门)
查看仓库 GET /v2/_catalog
[root@localhost registry]# curl http://127.0.0.1:5000/v2/_catalog
{"repositories":["my-hello-world"]}
查看镜像标签 GET /v2/<name>/tags/list
[root@localhost registry]# curl http://127.0.0.1:5000/v2/my-hello-world/tags/list
{"name":"my-hello-world","tags":["latest"]}
现在只有一个 latest 标签,这里 run 下 hello-world,然后提交一个新的版本1.0
[root@localhost registry]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest 177391bcf802 6 days ago 33.3MB
localhost:5000/my-hello-world latest f2a91732366c 2 weeks ago 1.85kB
my-hello-world latest f2a91732366c 2 weeks ago 1.85kB
[root@localhost registry]# docker run -it my-hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://cloud.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/engine/userguide/
[root@localhost registry]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e023cad89c0d my-hello-world "/hello" 6 seconds ago Exited (0) 5 seconds ago determined_keller
a107eda18508 registry "/entrypoint.sh /e..." 8 minutes ago Up 8 minutes 0.0.0.0:5000->5000/tcp registry
[root@localhost registry]# docker commit -m "1.0" e023cad89c0d my-hello-world:1.0
sha256:d9f41037af5b7648d4a1bfb0a95b903f16ebc52a23a0883896ae71d7831ee97c
[root@localhost registry]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-hello-world 1.0 d9f41037af5b 4 seconds ago 1.85kB
registry latest 177391bcf802 6 days ago 33.3MB
localhost:5000/my-hello-world latest f2a91732366c 2 weeks ago 1.85kB
my-hello-world latest f2a91732366c 2 weeks ago 1.85kB
[root@localhost registry]#
上传镜像
[root@localhost registry]# docker tag my-hello-world:1.0 localhost:5000/my-hello-world:1.0
[root@localhost registry]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-hello-world 1.0 d9f41037af5b About a minute ago 1.85kB
localhost:5000/my-hello-world 1.0 d9f41037af5b About a minute ago 1.85kB
registry latest 177391bcf802 6 days ago 33.3MB
my-hello-world latest f2a91732366c 2 weeks ago 1.85kB
localhost:5000/my-hello-world latest f2a91732366c 2 weeks ago 1.85kB
[root@localhost registry]# docker push localhost:5000/my-hello-world:1.0
The push refers to a repository [localhost:5000/my-hello-world]
f999ae22f308: Layer already exists
1.0: digest: sha256:69b12f1aeee0355bcf803b5159f96f69738f6b002ea3b6861b802aff337d26cf size: 524
[root@localhost registry]#
再次查看仓库的标签
[root@localhost registry]# curl http://127.0.0.1:5000/v2/my-hello-world/tags/list
{"name":"my-hello-world","tags":["latest","1.0"]}
可以看到有 latest, 1.0 2个标签
3. 那么怎么删除仓库里的镜像
DELETE /v2/<name>/manifests/<reference>
注:registry 2.3 版本以上 需要在头部添加
Accept: application/vnd.docker.distribution.manifest.v2+json
[root@localhost registry]# curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE http://127.0.0.1:5000/v2/my-hello-world/manifests/latest
{"errors":[{"code":"UNSUPPORTED","message":"The operation is unsupported."}]}
[root@localhost registry]#
提示 UNSUPPORTED! 为什么?
查阅资料得知 registry 有个配置文件 config.yml 内容如下
version: 0.1
log:
accesslog:
disabled: true
level: debug
formatter: text
fields:
service: registry
environment: staging
hooks:
- type: mail
disabled: true
levels:
- panic
options:
smtp:
addr: mail.example.com:25
username: mailuser
password: password
insecure: true
from: sender@example.com
to:
- errors@example.com
loglevel: debug # deprecated: use "log"
storage:
filesystem:
rootdirectory: /var/lib/registry
maxthreads: 100
azure:
accountname: accountname
accountkey: base64encodedaccountkey
container: containername
gcs:
bucket: bucketname
keyfile: /path/to/keyfile
rootdirectory: /gcs/object/name/prefix
chunksize: 5242880
s3:
accesskey: awsaccesskey
secretkey: awssecretkey
region: us-west-1
regionendpoint: http://myobjects.local
bucket: bucketname
encrypt: true
keyid: mykeyid
secure: true
v4auth: true
chunksize: 5242880
multipartcopychunksize: 33554432
multipartcopymaxconcurrency: 100
multipartcopythresholdsize: 33554432
rootdirectory: /s3/object/name/prefix
swift:
username: username
password: password
authurl: https://storage.myprovider.com/auth/v1.0 or https://storage.myprovider.com/v2.0 or https://storage.myprovider.com/v3/auth
tenant: tenantname
tenantid: tenantid
domain: domain name for Openstack Identity v3 API
domainid: domain id for Openstack Identity v3 API
insecureskipverify: true
region: fr
container: containername
rootdirectory: /swift/object/name/prefix
oss:
accesskeyid: accesskeyid
accesskeysecret: accesskeysecret
region: OSS region name
endpoint: optional endpoints
internal: optional internal endpoint
bucket: OSS bucket
encrypt: optional data encryption setting
secure: optional ssl setting
chunksize: optional size valye
rootdirectory: optional root directory
inmemory: # This driver takes no parameters
delete:
enabled: false
redirect:
disable: false
cache:
blobdescriptor: redis
maintenance:
uploadpurging:
enabled: true
age: 168h
interval: 24h
dryrun: false
readonly:
enabled: false
auth:
silly:
realm: silly-realm
service: silly-service
token:
realm: token-realm
service: token-service
issuer: registry-token-issuer
rootcertbundle: /root/certs/bundle
htpasswd:
realm: basic-realm
path: /path/to/htpasswd
middleware:
registry:
- name: ARegistryMiddleware
options:
foo: bar
repository:
- name: ARepositoryMiddleware
options:
foo: bar
storage:
- name: cloudfront
options:
baseurl: https://my.cloudfronted.domain.com/
privatekey: /path/to/pem
keypairid: cloudfrontkeypairid
duration: 3000s
storage:
- name: redirect
options:
baseurl: https://example.com/
reporting:
bugsnag:
apikey: bugsnagapikey
releasestage: bugsnagreleasestage
endpoint: bugsnagendpoint
newrelic:
licensekey: newreliclicensekey
name: newrelicname
verbose: true
http:
addr: localhost:5000
prefix: /my/nested/registry/
host: https://myregistryaddress.org:5000
secret: asecretforlocaldevelopment
relativeurls: false
tls:
certificate: /path/to/x509/public
key: /path/to/x509/private
clientcas:
- /path/to/ca.pem
- /path/to/another/ca.pem
letsencrypt:
cachefile: /path/to/cache-file
email: emailused@letsencrypt.com
debug:
addr: localhost:5001
headers:
X-Content-Type-Options: [nosniff]
http2:
disabled: false
notifications:
endpoints:
- name: alistener
disabled: false
url: https://my.listener.com/event
headers: <http.Header>
timeout: 500
threshold: 5
backoff: 1000
ignoredmediatypes:
- application/octet-stream
redis:
addr: localhost:6379
password: asecret
db: 0
dialtimeout: 10ms
readtimeout: 10ms
writetimeout: 10ms
pool:
maxidle: 16
maxactive: 64
idletimeout: 300s
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
file:
- file: /path/to/checked/file
interval: 10s
http:
- uri: http://server.to.check/must/return/200
headers:
Authorization: [Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==]
statuscode: 200
timeout: 3s
interval: 10s
threshold: 3
tcp:
- addr: redis-server.domain.com:6379
timeout: 3s
interval: 10s
threshold: 3
proxy:
remoteurl: https://registry-1.docker.io
username: [username]
password: [password]
compatibility:
schema1:
signingkeyfile: /etc/registry/key.json
validation:
enabled: true
manifests:
urls:
allow:
- ^https?://([^/]+\.)*example\.com/
deny:
- ^https?://www\.example\.com/
可以看到 storage 节点下 delete enabled 默认为 false
storage:
delete:
enabled: false
那么我们开启它,docker 允许通过 -e 传入环境变量(变量的格式为 REGISTRY_variable),我们传入一个 REGISTRY_STORAGE_DELETE_ENABLED=true 。官网说明(传送门)
那么删除原来的 registry 启动方式改为
[root@localhost registry]# docker rm -f registry
registry
[root@localhost registry]# docker run --name registry -d -p 5000:5000 -v /home/john/registry:/var/lib/registry -e REGISTRY_STORAGE_DELETE_ENABLED=true registry
8af585efa1914c2354def81eb0cbf3f30bf4fe0d5cab24ba7ce2491cfbc0678b
查看仓库
[root@localhost registry]# curl http://127.0.0.1:5000/v2/_catalog
{"repositories":["my-hello-world"]}
刚才上传的镜像还在,并没有随着容器删除而删除,证明我们刚才将镜像存在宿主机上的修改是有效的。
再次删除镜像
[root@localhost registry]# curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE http://127.0.0.1:5000/v2/my-hello-world/manifests/latest
{"errors":[{"code":"DIGEST_INVALID","message":"provided digest did not match uploaded content"}]}
提示 DISGEST_INVALID,删除api是 DELETE /v2/<name>/manifests/<reference>,查看官网 For deletes, reference must be a digest or the delete will fail. 一定要是一个 digest。
查阅资料 通过 curl -I 可以查得
[root@localhost registry]# curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -I -X GET http://localhost:5000/v2/my-hello-world/manifests/latest
HTTP/1.1 200 OK
Content-Length: 524
Content-Type: application/vnd.docker.distribution.manifest.v2+json
Docker-Content-Digest: sha256:8072a54ebb3bc136150e2f2860f00a7bf45f13eeb917cca2430fcd0054c8e51b
Docker-Distribution-Api-Version: registry/2.0
Etag: "sha256:8072a54ebb3bc136150e2f2860f00a7bf45f13eeb917cca2430fcd0054c8e51b"
X-Content-Type-Options: nosniff
Date: Fri, 08 Dec 2017 08:44:59 GMT
[root@localhost registry]#
Docker-Content-Digest 这个就是我们需要的 digest
再次调用api 删除
[root@localhost registry]# curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE http://127.0.0.1:5000/v2/my-hello-world/manifests/sha256:8072a54ebb3bc136150e2f2860f00a7bf45f13eeb917cca2430fcd0054c8e51b
[root@localhost registry]# curl http://127.0.0.1:5000/v2/my-hello-world/tags/list
{"name":"my-hello-world","tags":["1.0"]}
[root@localhost registry]#
可以看到 lastest 已经没有了,删除成功。
其实我们可以在文件系统看到tags的信息
[root@localhost tags]# pwd
/home/john/registry/docker/registry/v2/repositories/my-hello-world/_manifests/tags
[root@localhost tags]# ll
total 0
drwxr-xr-x. 4 root root 34 Dec 8 16:21 1.0
[root@localhost tags]#
我们可以浏览一下这些目录
[root@localhost v2]# pwd
/home/john/registry/docker/registry/v2
[root@localhost v2]# ll
total 0
drwxr-xr-x. 3 root root 20 Dec 8 11:28 blobs
drwxr-xr-x. 3 root root 28 Dec 8 16:13 repositories
[root@localhost v2]#
在这个目录下有个blobs目录
这个目录在删除完镜像也要清理一下,2.4以上registry才有次功能
docker exec -it <registry_container_id> bin/registry garbage-collect <path_to_registry_config>
[root@localhost v2]# docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml
my-hello-world
my-hello-world: marking manifest sha256:69b12f1aeee0355bcf803b5159f96f69738f6b002ea3b6861b802aff337d26cf
my-hello-world: marking blob sha256:d9f41037af5b7648d4a1bfb0a95b903f16ebc52a23a0883896ae71d7831ee97c
my-hello-world: marking blob sha256:ca4f61b1923c10e9eb81228bd46bee1dfba02b9c7dac1844527a734752688ede
3 blobs marked, 2 blobs eligible for deletion
blob eligible for deletion: sha256:8072a54ebb3bc136150e2f2860f00a7bf45f13eeb917cca2430fcd0054c8e51b
time="2017-12-08T08:56:15Z" level=info msg="Deleting blob: /docker/registry/v2/blobs/sha256/80/8072a54ebb3bc136150e2f2860f00a7bf45f13eeb917cca2430fcd0054c8e51b" go.version=go1.7.6 instance.id=fccb2a78-43b9-4542-9f9d-7bbf0ec0b044
blob eligible for deletion: sha256:f2a91732366c0332ccd7afd2a5c4ff2b9af81f549370f7a19acd460f87686bc7
time="2017-12-08T08:56:15Z" level=info msg="Deleting blob: /docker/registry/v2/blobs/sha256/f2/f2a91732366c0332ccd7afd2a5c4ff2b9af81f549370f7a19acd460f87686bc7" go.version=go1.7.6 instance.id=fccb2a78-43b9-4542-9f9d-7bbf0ec0b044
[root@localhost v2]#
这样删除就差不多了。
粗暴一点可以直接(不知道有没有副作用)
rm -rf /home/john/registry/docker/registry/v2/repositories/<镜像名>
4. 随服务启动
docker run 的时候添加 --restart=always 可以保证 docker 服务启动的时候,容器随服务一起启动。
docker run --name registry -d -p 5000:5000 --restart=always -v /home/john/registry:/var/lib/registry -e REGISTRY_STORAGE_DELETE_ENABLED=true registry
总结
启动
docker run --name registry -d -p 5000:5000 --restart=always -v /xxx/registry:/var/lib/registry -e REGISTRY_STORAGE_DELETE_ENABLED=true registry
上传
docker push <registry_ip:port>/<name>
下载
docker pull <registry_ip:port>/<name>
查看 docker registry
GET /v2/_catalog
查看镜像tags
GET /v2/<name>/tags/list
删除镜像
配置config.yml,获取镜像 digest
DELETE /v2/<name>/manifests/<reference>