這篇文章內容包括搭建docker私有倉庫的一些配置項和遇到的問題及解決方案。html
1.配置項 1.1. 數據持久化 1.2. TLS 支持 1.3. 登陸受權驗證 1.4. docker compose 2. 測試 3. NGINX作代理 3.1. 個人方式和遇到的問題 3.2. NGINX 做爲一個容器 4. 其它方案 5. 相關連接
Docker官方提供了 registry鏡像, 能夠方便的搭建私有倉庫,詳細文檔參考這裏。linux
能夠經過採用數據卷掛載或者直接掛載宿主機目錄的方式來進行。掛載到容器內默認位置: /var/lib/registry
。
好比能夠像以下方式啓動, 這裏將容器數據存儲在了 /mnt/registry
.nginx
$ docker run -d \ -p 5000:5000 \ --restart=always \ --name registry \ -v /mnt/registry:/var/lib/registry \ registry:2
固然,鏡像還提供了其它支持的存儲方式,好比OSS等。git
官方文檔參見這裏。github
爲了使得私有倉庫安全地對外開放,須要配置 TLS 支持。web
測試的時候,若是不配置的話TLS,能夠在docker客戶端中的 "insecure registry" 裏添加私有倉庫地址,否則默認的都以安全的tsl方式來訪問私有倉庫,具體更改方式能夠參考這裏。docker
個人CA證書是從阿里雲獲取的(由於域名是在上面註冊的,能夠提供免費的證書,雖然若是作得很隱蔽)。apache
registry鏡像能夠經過 REGISTRY_HTTP_TLS_CERTIFICATE
和 REGISTRY_HTTP_TLS_KEY
環境參數配置TLS支持。
例以下面這樣, domain.crt
和 domain.key
是得到的證書,另外配置容器監聽ssl默認的 443
端口。ubuntu
$ docker run -d \ --restart=always \ --name registry \ -v `pwd`/certs:/certs \ -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \ -p 443:443 \ registry:2
官方文檔參見這裏。api
能夠經過 htpasswd
來配置簡單的authentication (注意:驗證須要 TLS 支持)。
首先在 auth
目錄下經過reistry裏的 htpasswd
工具建立 驗證文件 auth/htpasswd
。
$ mkdir auth $ docker run \ --entrypoint htpasswd \ registry:2 -Bbn testuser testpassword > auth/htpasswd
啓動的時候經過 REGISTRY_AUTH
, REGISTRY_AUTH_HTPASSWD_REALM
, REGISTRY_AUTH_HTPASSWD_PATH
來配置:
$ docker run -d \ -p 5000:5000 \ --restart=always \ --name registry \ -v `pwd`/auth:/auth \ -e "REGISTRY_AUTH=htpasswd" \ -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \ -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ -v `pwd`/certs:/certs \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \ registry:2
這樣就啓動了一個監聽5000端口的、支持TLS和簡單登陸驗證的docker 私有倉庫。
官方文檔參見這裏。
"docker compose" 是一個方便定義和運行多個容器的工具, 安裝參見這裏, 或者經過pip安裝: pip install docker-compose
以上配置項經過 docker compose
的方式組織起來以下:
文件命名成 docker-compose.yaml
registry: restart: always image: registry:2 ports: - 5000:5000 environment: REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt REGISTRY_HTTP_TLS_KEY: /certs/domain.key REGISTRY_AUTH: htpasswd REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm volumes: - /path/data:/var/lib/registry - /path/certs:/certs - /path/auth:/auth
在 docker-compose.yaml
所在目錄運行:
docker-compose up
私有倉庫搭建好了如何測試?
# 先拉取官方鏡像 $ docker pull ubuntu:16.04 # 打上標籤 $ docker tag ubuntu:16.04 myregistrydomain.com/my-ubuntu # 推到私有倉庫 $ docker push myregistrydomain.com/my-ubuntu # 從私有倉庫獲取 $ docker pull myregistrydomain.com/my-ubuntu
實際配置中,我採用了 nginx
做爲代理,來訪問 registry服務。我將TLS支持和登陸驗證都加到了nginx一層。
nginx 配置文件:
upstream docker-registry { server localhost:5000; # !轉發到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 is 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 domain.com; # !這裏配置域名! # SSL ssl_certificate /path/to/domain.pem; # !這裏配置CA 證書信息! ssl_certificate_key /path/to/domain.key; # !這裏配置CA 證書信息! # 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 /path/to/auth/htpasswd; # !這裏配置auth文件位置! ## If $docker_distribution_api_version is empty, the header is not 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; } }
其中 /path/to/auth/htpasswd
文件是經過 registry 中的或者本地的 htpasswd
工具生成的
$ docker run \ --entrypoint htpasswd \ registry:2 -Bbn testuser testpassword > auth/htpasswd
registry的 docker-compose
文件:
version: '2' services: my_registry: restart: always image: registry:2 ports: - 127.0.0.1:5000:5000 volumes: - ./data:/var/lib/registry
啓動後,當我從本地視圖login到私有倉庫時,發生錯誤:
➜ ~ docker login domain.com Username: testuser Password: Error response from daemon: login attempt to https://hub.docker.equiz.cn/v2/ failed with status: 500 Internal Server Error
查看日誌發現nginx 錯誤日誌裏有個編碼相關的錯誤:
# nginx error log *4 crypt_r() failed (22: Invalid argument)
通過一番研究,發現以前加密時,是採用 Bcrypt
加密方式,看下 htpasswd
的使用說明:
root@data1:~# htpasswd Usage: htpasswd [-cimBdpsDv] [-C cost] passwordfile username htpasswd -b[cmBdpsDv] [-C cost] passwordfile username password htpasswd -n[imBdps] [-C cost] username htpasswd -nb[mBdps] [-C cost] username password -c Create a new file. -n Don't update file; display results on stdout. -b Use the password from the command line rather than prompting for it. -i Read password from stdin without verification (for script usage). -m Force MD5 encryption of the password (default). -B Force bcrypt encryption of the password (very secure). -C Set the computing time used for the bcrypt algorithm (higher is more secure but slower, default: 5, valid: 4 to 31). -d Force CRYPT encryption of the password (8 chars max, insecure). -s Force SHA encryption of the password (insecure). -p Do not encrypt the password (plaintext, insecure). -D Delete the specified user. -v Verify password for the specified user. On other systems than Windows and NetWare the '-p' flag will probably not work. The SHA algorithm does not use a salt and is less secure than the MD5 algorithm.
能夠看到 -B
會使用 bcrypt
的方式來加密,nginx默認不支持。至於如何讓nginx支持bcrypt我暫時還未找到方案,留待之後研究了(TODO)
簡單的解決方式是換成默認的MD5加密(由於安全等級問題又不推薦不用bcrypt方式的),
docker run --rm --entrypoint htpasswd registry:2 -bn testuser testpassword > auth/htpasswd # 這裏少了 -B 選項
關於 bcrypt
加密方式,這裏 有一篇不錯的文章介紹。不過好像對於這個加密方式,網上有一些爭論,我就不詳究了。
不依賴 "apche tools" 的 nginx 加密方式參考 這裏, 好比MD5加密:
printf "testuser:$(openssl passwd -1 testpassword)\n" >> .htpasswd # this example uses MD5 encryption
docker 文檔也有如何採用nginx容器和registry配合使用的說明,參考這裏。
"docker-compose.yaml" 以下:
nginx: # Note : Only nginx:alpine supports bcrypt. # If you don't need to use bcrypt, you can use a different tag. # Ref. https://github.com/nginxinc/docker-nginx/issues/29 image: "nginx:alpine" # !這裏必定要採用alpine鏡像,由於它裏的nginx支持 bcrypt 加密! 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
這裏nginx容器監聽的是5043, 因此使用的私有倉庫的地址變成了 registrydomain.com:5043
,
我不喜歡後面加端口,但本機又存在其餘須要nginx監聽的443端口的服務(不一樣doamin下),因此不能讓nginx容器直接監聽443端口,故沒有采用這種方式。
另外在尋找解決方案的時候發現一個鏡像 nginx-proxy
, 能夠方便地監聽新添加的容器,留待之後探索。
或許你想用letsencrypts免費證書,不妨看看這個工具:acme.sh
或許你不知足的簡易方案,你可能還須要web界面來方便查看和管理你的鏡像倉庫,那麼你能夠查看下企業級的容器倉庫方案: vmware/harbor