本節內容:
- Registry相關概念
- Registry V1和V2
- 安裝Docker
- 搭建本地registry v2
- 搭建外部可訪問的Registry
- 添加認證
- 更高級的認證
- registry web ui
一、Registry相關概念
前面的文章講過Docker的組成部分,我們一般在使用Docker的過程中更為常用的是pull image、run image、build image和push image。主要是圍繞image展開的。
image和Registry的關系可以想象成自己機器上的源碼和遠端SVN或者Git服務的關系。Registry是一個幾種存放image並對外提供上傳下載以及一系列API的服務。可以很容易和本地源代碼以及遠端Git服務的關系相對應。
Docker hub是Docker公司提供的一些存儲鏡像的空間,這部分空間是有限的。我們一般會自主建設Docker私有倉庫Registry。
二、Registry V1和V2
Docker Registry 2.0版本在安全性和性能上做了諸多優化,並重新設計了鏡像的存儲的格式。Docker目前1.6之后支持V2。
三、安裝Docker
見前面發布的文章《CentOS安裝Docker CE》。
四、搭建本地registry v2
環境:172.16.7.151 CentOS 7.0 主機名:node1
[root@node1 ~]# docker run -d -p 5000:5000 --name wisedu_registry registry:2
本地push鏡像到registry倉庫:
[root@node1 ~]# docker pull ubuntu:16.04 [root@node1 ~]# docker tag ubuntu:16.04 localhost:5000/my-ubuntu [root@node1 ~]# docker push localhost:5000/my-ubuntu
刪除本地的ubuntu:16.04和localhost:5000/my-ubuntu鏡像:
[root@node1 ~]# docker image remove ubuntu:16.04 [root@node1 ~]# docker image remove localhost:5000/my-ubuntu
從本地registry中拉取 localhost:5000/my-ubuntu 鏡像:
[root@node1 ~]# docker pull localhost:5000/my-ubuntu
但是這種registry只是本地能使用,我們找另外一台主機172.16.7.152往該registry中push鏡像:
[root@node2 ~]# docker pull ubuntu:16.04 [root@node2 docker]# docker tag ubuntu:16.04 172.16.7.151:5000/ubuntu:v1 [root@node2 docker]# docker push 172.16.7.151:5000/ubuntu:v1 The push refers to a repository [172.16.7.151:5000/ubuntu] Get https://172.16.7.151:5000/v2/: http: server gave HTTP response to HTTPS client [root@node2 ~]# echo $? 1
這是因為從docker1.13.2版本開始,使用registry時,必須使用TLS保證其安全。
停止並刪除本地registry:
[root@node1 ~]# docker stop wisedu_registry wisedu_registry [root@node1 ~]# docker rm -v wisedu_registry wisedu_registry
五、搭建外部可訪問的Registry
官方文檔:https://docs.docker.com/registry/deploying/#run-an-externally-accessible-registry
Running a registry only accessible on localhost has limited usefulness. In order to make your registry accessible to external hosts, you must first secure it using TLS.
使用TLS認證registry容器時,必須有證書。一般情況下,是要去認證機構購買簽名證書。這里使用openssl生成自簽名的證書。
環境信息:172.16.206.32 CentOS 7.0 主機名:spark32
1.生成自簽名證書
[root@spark32 ~]# mkdir -p /opt/docker/registry/certs [root@spark32 ~]# openssl req -newkey rsa:4096 -nodes -sha256 -keyout /opt/docker/registry/certs/domain.key -x509 -days 365 -out /opt/docker/registry/certs/domain.crt Generating a 4096 bit RSA private key ... Country Name (2 letter code) [XX]:CN State or Province Name (full name) []:JiangSu Locality Name (eg, city) [Default City]:NanJing Organization Name (eg, company) [Default Company Ltd]:wisedu Organizational Unit Name (eg, section) []:edu Common Name (eg, your name or your server's hostname) []:registry.docker.com Email Address []:01115004@wisedu.com
2.創建帶有TLS認證的registry容器
[root@spark32 ~]# docker run -d --name registry2 -p 5000:5000 -v /opt/docker/registry/certs:/certs -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key registry:2
3.在每一個docker客戶端宿主機上配置/etc/hosts,以使客戶端宿主機可以解析域名”registry.docker.com”。並創建與這個registry服務器域名一致的目錄(因為我這里的域名是假的)
# vim /etc/hosts 172.16.206.32 registry.docker.com [root@node1 ~]# cd /etc/docker/certs.d/ [root@node1 certs.d]# mkdir registry.docker.com:5000
4.將證書 domain.crt 復制到每一個docker客戶端宿主機/etc/docker/certs.d/registry.docker.com:5000/ca.crt,不需要重啟docker
[root@spark32 ~]# scp -p /opt/docker/registry/certs/domain.crt root@172.16.7.151:/etc/docker/certs.d/registry.docker.com\:5000/ca.crt
5.push鏡像到registry
另找一個客戶機node1,push鏡像到registry。
[root@node1 certs.d]# docker tag ubuntu:16.04 registry.docker.com:5000/my-ubuntu:v1 [root@node1 certs.d]# docker push registry.docker.com:5000/my-ubuntu:v1 The push refers to a repository [registry.docker.com:5000/my-ubuntu] a09947e71dc0: Pushed 9c42c2077cde: Pushed 625c7a2a783b: Pushed 25e0901a71b8: Pushed 8aa4fcad5eeb: Pushed v1: digest: sha256:634a341aa83f32b48949ef428db8fefcd897dbacfdac26f044b60c14d1b5e972 size: 1357
6.列出私有倉庫中的所有鏡像
[root@node1 certs.d]# curl -X GET https://registry.docker.com:5000/v2/_catalog -k {"repositories":["my-ubuntu"]}
7.查看存儲在registry:2宿主機上的鏡像
在registry:2創建的私有倉庫中,上傳的鏡像保存在容器的/var/lib/registry目錄下。創建registry:2的容器時,會自動創建一個數據卷(Data Volumes),數據卷對應的宿主機下的目錄一般為:/var/lib/docker/volumes/XXX/_data。
[root@spark32 ~]# ls /var/lib/docker/volumes/91a0091963fa6d107dc988a60b61790bba843a115573e331db967921d5e83372/_data/docker/registry/v2/repositories/my-ubuntu/ _layers _manifests _uploads
可以在創建registry:2的容器時,通過-v參數,修改這種數據卷關系:
–v /opt/docker/registry/data:/var/lib/registry
除了可以將數據保存在當前主機的文件系統上,registry也支持其他基於雲的存儲系統,比如S3,Microsoft Azure, Ceph Rados, OpenStack Swift and Aliyun OSS等。可以在配置文件中進行配置:https://github.com/docker/distribution/blob/master/docs/configuration.md#storage
【補充】:
一般情況下,證書只支持域名訪問,要使其支持IP地址訪問,需要修改配置文件openssl.cnf。
在Redhat7系統中,文件所在位置是/etc/pki/tls/openssl.cnf。在其中的[ v3_ca]部分,添加subjectAltName選項:
[ v3_ca ] subjectAltName = IP:192.168.1.104
生成證書:
... Country Name (2 letter code) [XX]: State or Province Name (full name) []: Locality Name (eg, city) [Default City]: Organization Name (eg, company) [Default Company Ltd]: Organizational Unit Name (eg, section) []: Common Name (eg, your name or your server's hostname) []:172.16.206.32:5000 Email Address []:
六、添加認證:Native basic auth
The simplest way to achieve access restriction is through basic authentication (this is very similar to other web servers’ basic authentication mechanism). This example uses native basic authentication using htpasswd to store the secrets.
1.創建用戶密碼文件,testuser,testpassword
[root@spark32 ~]# mkdir /opt/docker/registry/auth [root@spark32 ~]# docker run --entrypoint htpasswd registry:2 -Bbn testuser testpassword > /opt/docker/registry/auth/htpasswd
2.運行registry容器
[root@spark32 ~]# docker run -d --name registry_native_auth -p 5000:5000 -v /opt/docker/registry/auth:/auth -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd -v /opt/docker/registry/certs:/certs -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key registry:2
3.現在嘗試拉取鏡像
[root@node1 ~]# docker pull registry.docker.com:5000/my-ubuntu:v1 Error response from daemon: Get https://registry.docker.com:5000/v2/my-ubuntu/manifests/v1: no basic auth credentials
4.登錄registry,push鏡像
[root@node1 ~]# docker login registry.docker.com:5000 Username: testuser Password: Login Succeeded [root@node1 ~]# docker tag ubuntu:16.04 registry.docker.com:5000/my-ubuntu:v1 [root@node1 ~]# docker push registry.docker.com:5000/my-ubuntu:v1
七、更高級的認證
更好的方式是在registry前使用代理,利用代理提供https的ssl的認證和basic authentication。https://docs.docker.com/registry/recipes/
1. 配置Nginx作為認證代理
https://docs.docker.com/registry/recipes/nginx/
(1)創建需要的目錄
[root@spark32 ~]# mkdir -p /opt/nginx_proxy_registry/{auth,data}
(2)創建Nginx主配置文件

events { worker_connections 1024; } http { upstream docker-registry { server registry:5000; } ## Set a variable to help us decide if we need to add the ## 'Docker-Distribution-Api-Version' header. ## The registry always sets this header. ## In the case of nginx performing auth, the header will be unset ## since nginx is auth-ing before proxying. map $upstream_http_docker_distribution_api_version $docker_distribution_api_version { '' 'registry/2.0'; } server { listen 443 ssl; server_name registry.docker.com; # SSL ssl_certificate /etc/nginx/conf.d/domain.crt; ssl_certificate_key /etc/nginx/conf.d/domain.key; # Recommendations from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html ssl_protocols TLSv1.1 TLSv1.2; ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; # disable any limits to avoid HTTP 413 for large image uploads client_max_body_size 0; # required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486) chunked_transfer_encoding on; location /v2/ { # Do not allow connections from docker 1.5 and earlier # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) { return 404; } # To add basic authentication to v2 use auth_basic setting. auth_basic "Registry realm"; auth_basic_user_file /etc/nginx/conf.d/nginx.htpasswd; ## If $docker_distribution_api_version is empty, the header will not be added. ## See the map directive above where this variable is defined. add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always; proxy_pass http://docker-registry; proxy_set_header Host $http_host; # required for docker client's sake proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 900; } } }
(3)創建密碼認證文件
[root@spark32 ~]# docker run --rm --entrypoint htpasswd registry:2 -bn testuser testpassword > /opt/nginx_proxy_registry/auth/nginx.htpasswd
(4)拷貝之前生成的證書和key到auth目錄下
[root@spark32 ~]# cp /opt/docker/registry/certs/domain.crt /opt/nginx_proxy_registry/auth/ [root@spark32 ~]# cp /opt/docker/registry/certs/domain.key /opt/nginx_proxy_registry/auth/
(5)創建compose文件

nginx: image: "nginx:1.9" ports: - 5043:443 links: - registry:registry volumes: - ./auth:/etc/nginx/conf.d - ./auth/nginx.conf:/etc/nginx/nginx.conf:ro registry: image: registry:2 ports: - 127.0.0.1:5000:5000 volumes: - ./data:/var/lib/registry
(6)啟動
[root@spark32 nginx_proxy_registry]# docker-compose up -d
(7)驗證啟動的服務
[root@spark32 nginx_proxy_registry]# docker-compose ps [root@spark32 ~]# docker ps
找一台docker客戶端機器登錄:
創建需要的目錄:
[root@node1 ~]# mkdir /etc/docker/certs.d/registry.docker.com:5043
把 domain.crt 傳到上一步生成的目錄里:
[root@spark32 ~]# scp -p /opt/nginx_proxy_registry/auth/domain.crt root@172.16.7.151:/etc/docker/certs.d/registry.docker.com:5043/ca.crt
登錄進行測試:
[root@node1 ~]# docker login -u=testuser -p=testpassword registry.docker.com:5043 Login Succeeded [root@node1 ~]# docker tag ubuntu:16.04 registry.docker.com:5043/ubuntu-test:v1 [root@node1 ~]# docker push registry.docker.com:5043/ubuntu-test:v1
(8)停止服務
[root@spark32 ~]# cd /opt/nginx_proxy_registry/
[root@spark32 nginx_proxy_registry]# docker-compose stop
(9)查看日志
[root@spark32 nginx_proxy_registry]# docker-compose logs
2. 補充Docker compose
(1)Docker compose是什么
Docker Compose是一個用來定義和運行復雜應用的Docker工具。使用Compose,你可以在一個文件中定義一個多容器應用,然后使用一條命令來啟動你的應用,完成一切准備工作。
一個使用Docker容器的應用,通常由多個容器組成。使用Docker Compose,不再需要使用shell腳本來啟動容器。
Docker Compose將所管理的容器分為三層,工程(project)、服務(service)以及容器(contaienr)。一個工程當中可包含多個服務,每個服務中定義了容器運行的鏡像,參數,依賴。一個服務當中可包括多個容器實例,Docker Compose並沒有解決負載均衡的問題,因此需要借助其他工具實現服務發現及負載均衡。
(2)安裝docker compose
將變量 $dockerComposeVersion 換成指定的版本
[root@spark32 ~]# curl -L https://github.com/docker/compose/releases/download/$dockerComposeVersion/docker-compose-`uname -m` -o /usr/local/bin/docker-compose
比如下載安裝1.15.0版本:
[root@spark32 ~]# curl -L https://github.com/docker/compose/releases/download/1.15.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
可能會下載失敗,多試幾次。實在不行就需要翻牆去下載。
賦予執行權限:
[root@spark32 ~]# chmod +x /usr/local/bin/docker-compose
查看docker compose版本:
[root@spark32 ~]# docker-compose --version docker-compose version 1.15.0, build e12f3b9
(3)卸載docker compose
如果docker compose是通過curl安裝的:
rm /usr/local/bin/docker-compose
如果docker compose是通過pip安裝的:
pip uninstall docker-compose
八、registry web ui
搭建完了docker registry,我們可以使用 docker 命令行工具對我們搭建的 registry 做各種操作了,如 push / pull。但是不夠方便,比如不能直觀的查看 registry 中的資源情況,如果有一個 ui 工具,能夠看到倉庫中有哪些鏡像、每個鏡像的版本是多少。
registry web ui主要有3個,一個是 docker-registry-frontend,一個是 hyper/docker-registry-web,還有一個是Portus。
關於registry ui的搭建會在后面的文章中介紹。