本文介紹了搭建docker內(nèi)網(wǎng)私服的方法,分享給大家。具體如下:
主要思路:
1. Docker Registry 說明
關(guān)于如何創(chuàng)建和使用本地倉庫,其實已經(jīng)有很多文章介紹了。因為docker技術(shù)正處于發(fā)展和完善階段,所以有些文章要么內(nèi)容已經(jīng)過時,要么給出了錯誤的配置,導(dǎo)致無法正常創(chuàng)建倉庫。本文記錄的是個人完整的搭建過程,docker version
為1.1.2。
官方提供了Docker Hub網(wǎng)站來作為一個公開的集中倉庫。然而,本地訪問Docker Hub速度往往很慢,并且很多時候我們需要一個本地的私有倉庫只供網(wǎng)內(nèi)使用。
Docker倉庫實際上提供兩方面的功能,一個是鏡像管理,一個是認(rèn)證。前者主要由docker-registry項目來實現(xiàn),通過http服務(wù)來上傳下載;后者可以通過docker-index(閉源)項目或者利用現(xiàn)成認(rèn)證方案(如nginx)實現(xiàn)http請求管理。
docker-registry既然也是軟件應(yīng)用,自然最簡單的方法就是使用官方提供的已經(jīng)部署好的鏡像registry。官方文檔中也給出了建議,直接運行sudo docker run -p 5000:5000 registry
命令。這樣確實能啟動一個registry服務(wù)器,但是所有上傳的鏡像其實都是由docker容器管理,放在了/var/lib/docker/….某個目錄下。而且一旦刪除容器,鏡像也會被刪除。因此,我們需要想辦法告訴docker容器鏡像應(yīng)該存放在哪里。registry鏡像中啟動后鏡像默認(rèn)位置是/tmp/registry
,因此直接映射這個位置即可,比如到本機(jī)的/opt/data/registry目錄下。
2. 在CentOS上搭建docker私服
2.1 安裝docker-registry
方法有多種,直接運行下面的命令:
復(fù)制代碼 代碼如下:
# docker run -d -e SETTINGS_FLAVOR=dev -e STORAGE_PATH=/tmp/registry -v /opt/data/registry:/tmp/registry -p 5000:5000 registry
如果本地沒有拉取過docker-registry,則首次運行會pull registry,運行時會映射路徑和端口,以后就可以從/opt/data/registry
下找到私有倉庫都存在哪些鏡像,通過主機(jī)的哪個端口可以訪問。
你也可以把項目 https://github.com/docker/docker-registry.git 克隆到本地,然后使用Dockerfile來build鏡像:
# git clone https://github.com/docker/docker-registry.git
# cd docker-registry && mkdir -p /opt/data/registry
# docker build -t "local-sean" .
build完成后,就可以運行這個docker-registry
我們先配置自己的config.yml文件,第一種方法是直接在run的時候指定變量
# cp config/config_sample.yml /opt/data/registry/config.yml
# vi /opt/data/registry/config.yml
##這里可以設(shè)置本地存儲SETTINGS_FLAVOR=dev,local STORAGE_PATH:/tmp/registry等待
# docker run -d -v /opt/data/registry:/tmp/registry -p 5000:5000 -e DOCKER_REGISTRY_CONFIG=/tmp/registry/config.yml registry
或
docker run -d -e SETTINGS_FLAVOR=dev -e STORAGE_PATH=/tmp/registry -v /db/docker-images:/tmp/registry -p 5000:5000 registry
2.2 客戶端使用
要從私服上獲取鏡像或向私服提交鏡像,現(xiàn)在變得非常簡單,只需要在倉庫前面加上私服的地址和端口,形如172.29.88.222:5000/centos6
。注意,這里可以選擇不使用IP,而是用hostname,如registry.domain.com:5000,但不能僅用不帶.的主機(jī)名registry,docker會認(rèn)為registry是用戶名,建議使用帶域名的hostname加port來表示。
于是在另外一臺要使用docker的主機(jī)上就可以通過這臺私服拉取和推送鏡像了:
從私服上搜索存在哪些可用鏡像
復(fù)制代碼 代碼如下:
# curl -X GET http://sean.domain.com:5000/v1/search
{"num_results": 2, "query": "", "results": [{"description": "", "name": "library/centos6"}, {"description": "", "name": "library/nginx"}]}
按條件搜索nginx
# curl -X GET http://sean.domain.com:5000/v1/search?q=centos6
拉取image到本地
docker pull library/centos6
## 本地對份鏡像啟動起來,形成container
## 給container去另外一個名字
# docker tag 68edf809afe7 registry.domain.com:5000/centos6-test
## 最后將新的docker images推送到私服上
docker push registry.domain.com:5000/centos6-test
第一次push到私服上時會提示用戶名、密碼和郵箱,創(chuàng)建即可。也可以在docker私服端加入認(rèn)證機(jī)制。
3. 加入nginx認(rèn)證
(請在實際操作以前,先閱讀完本節(jié),再確定是否在前端加入nginx)
3.1 安裝及配置nginx
從上面的過程可以看到,除非防火墻限制,否則任何主機(jī)可以創(chuàng)建賬號并想私服推送鏡像,更安全的做法是在外層加入登錄認(rèn)證機(jī)制。
最好安裝1.4.x版本,不然下面的有些配置可能會不兼容
創(chuàng)建兩個登錄用戶
# htpasswd -c /etc/nginx/docker-registry.htpasswd sean
New password:
Re-type new password:
Adding password for user sean
# htpasswd /etc/nginx/docker-registry.htpasswd itsection
為了讓nginx使用這個密碼文件,并且轉(zhuǎn)發(fā)8080端口的請求到Docker Registry,新增nginx配置文件
vi /etc/nginx/sites-enabled/docker-registry:
# For versions of Nginx > 1.3.9 that include chunked transfer encoding support
# Replace with appropriate values where necessary
upstream docker-registry {
server localhost:5000;
}
server {
listen 8080;
server_name sean.domain.com; -- your registry server_name
# ssl on;
# ssl_certificate /etc/ssl/certs/docker-registry;
# ssl_certificate_key /etc/ssl/private/docker-registry;
proxy_set_header Host $http_host; # required for Docker client sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client IP
client_max_body_size 0; # disable any limits to avoid HTTP 413 for large image uploads
# required to avoid HTTP 411: see Issue #1486 (https://github.com/dotcloud/docker/issues/1486)
chunked_transfer_encoding on;
location / {
# let Nginx know about our auth file
auth_basic "Restricted";
auth_basic_user_file docker-registry.htpasswd;
proxy_pass http://docker-registry;
}
location /_ping {
auth_basic off;
proxy_pass http://docker-registry;
}
location /v1/_ping {
auth_basic off;
proxy_pass http://docker-registry;
}
}
讓nginx來使用這個virtual-host
# ln -s /etc/nginx/sites-enabled/docker-registry /etc/nginx/conf.d/docker-registry.conf
重啟nginx來激活虛擬主機(jī)的配置
3.2 加入認(rèn)證后使用docker-registry
此時主機(jī)的5000端口應(yīng)該通過防火墻禁止訪問(或者在docker run端口映射時只監(jiān)聽回環(huán)接口的IP -p 127.0.0.1:5000:5000
)。
# curl localhost:5000
"docker-registry server (dev) (v0.8.1)"
如果直接訪問訪問將得到未授權(quán)的信息:
# curl localhost:8080
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.4.7</center>
</body>
</html>
帶用戶認(rèn)證的docker-registry:
# curl http://sean:sean@sean.domain.com:8080/v1/search
{"num_results": 2, "query": "", "results": [{"description": "", "name": "library/centos6"}, {"description": "", "name": "library/nginx"}]}
# docker login registry.domain.com:8080
Username: sean
Password:
Email: zhouxiao@domain.com
Login Succeeded
# docker pull registry.domain.com:8080/library/centos6
不出意外的話,上面的docker pull會失?。?/p>
# docker pull registry.domain.com:8080/library/centos6
Pulling repository registry.domain.com:8080/library/centos6
2014/11/11 21:00:25 Could not reach any registry endpoint
# docker push registry.domain.com:8080/ubuntu:sean
The push refers to a repository [registry.domain.com:8080/ubuntu] (len: 1)
Sending image list
Pushing repository registry.domain.com:8080/ubuntu (1 tags)
2014/11/12 08:11:32 HTTP code 401, Docker will not send auth headers over HTTP.
nginx日志
2014/11/12 07:03:49 [error] 14898#0: *193 no user/password was provided for basic
authenticatGET /v1/repositories/library/centos6/tags HTTP/1.1", host: "registry.domain.com:8080"
本文后的第1篇參考文檔沒有出現(xiàn)這個問題,但評論中有提及。
有人說是backend storage
的問題,這里是本地存儲鏡像,不應(yīng)該。經(jīng)過查閱大量資料,并反復(fù)操作驗證,是docker-registry版本的問題。從v0.10.0
開始,docker login
雖然Succeeded,但pull或push的時候,~/.dockercfg下的用戶登錄信息將不允許通過HTTP明文傳輸。(如果你愿意可以查看v0.10.0
的源碼 registry.go
,在分支v0.9.1及以前是沒有HTTP code 401, Docker will not send auth headers over HTTP
的)
目前的辦法三個:
- 撤退,這就是為什么先說明在操作前線查看到這的原因了
- 換成
v0.9.1
及以下版本?,F(xiàn)在都v1.3.1
了,我猜你不會這么做
- 修改源碼session.go,去掉相應(yīng)的判斷行,然后git下來重新安裝。我猜你更不會這么做
- 安裝SSL證書,使用HTTPS傳輸。這是明智的選擇,新版本docker也推薦我們這么做,往下看。
3.3 為nginx安裝ssl證書
首先打開nginx配置文件中ssl的三行注釋
# vi /etc/nginx/conf.d/docker-registry.conf
...
server {
listen 8000;
server_name registry.domain.com;
ssl on;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
...
保存之后,nginx會分別從/etc/nginx/ssl/nginx.crt和/etc/nginx/ssl/nginx.key
讀取ssl證書和私鑰。如果你自己愿意花錢買一個ssl證書,那就會變得非常簡單,把證書和私鑰拷貝成上面一樣即可。關(guān)于SSL以及簽署ssl證書,請參考其他文章。
這里我們自簽署一個ssl證書,把當(dāng)前系統(tǒng)作為(私有)證書頒發(fā)中心(CA)。
創(chuàng)建存放證書的目錄
# mkdir /etc/nginx/ssl
確認(rèn)CA的一些配置文件
# vi /etc/pki/tls/openssl.cnf
...
[ CA_default ]
dir = /etc/pki/CA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
#unique_subject = no # Set to 'no' to allow creation of
# several ctificates with same subject.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem # The private key
RANDFILE = $dir/private/.rand # private random number file
...
default_days = 3650 # how long to certify for
...
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = CN
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = GD
...[ req_distinguished_name ]部分主要是頒證時一些默認(rèn)的值,可以不動
(1) 生成根密鑰
# cd /etc/pki/CA/
# openssl genrsa -out private/cakey.pem 2048
為了安全起見,修改cakey.pem私鑰文件權(quán)限為600或400,也可以使用子shell生成( umask 077; openssl genrsa -out private/cakey.pem 2048
),下面不再重復(fù)。
(2) 生成根證書
# openssl req -new -x509 -key private/cakey.pem -out cacert.pem
會提示輸入一些內(nèi)容,因為是私有的,所以可以隨便輸入,最好記住能與后面保持一致。上面的自簽證書cacert.pem
應(yīng)該生成在/etc/pki/CA
下。
(3) 為我們的nginx web服務(wù)器生成ssl密鑰
# cd /etc/nginx/ssl
# openssl genrsa -out nginx.key 2048
我們的CA中心與要申請證書的服務(wù)器是同一個,否則應(yīng)該是在另一臺需要用到證書的服務(wù)器上生成。
(4) 為nginx生成證書簽署請求
# openssl req -new -key nginx.key -out nginx.csr
...
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:GD
Locality Name (eg, city) []:SZ
Organization Name (eg, company) [Internet Widgits Pty Ltd]:COMPANY
Organizational Unit Name (eg, section) []:IT_SECTION
Common Name (e.g. server FQDN or YOUR name) []:your.domain.com
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
...
同樣會提示輸入一些內(nèi)容,其它隨便,除了Commone Name
一定要是你要授予證書的服務(wù)器域名或主機(jī)名,challenge password不填。
(5) 私有CA根據(jù)請求來簽發(fā)證書
# openssl ca -in nginx.csr -out nginx.crt
上面簽發(fā)過程其實默認(rèn)使用了-cert cacert.pem -keyfile cakey.pem
,這兩個文件就是前兩步生成的位于/etc/pki/CA
下的根密鑰和根證書。
到此我們已經(jīng)擁有了建立ssl安全連接所需要的所有文件,并且服務(wù)器的crt和key都位于配置的目錄下,唯有根證書cacert.pem
位置不確定放在CentOS6下的哪個地方。
經(jīng)驗證以下幾個位置不行:(Adding trusted root certificates to the server)
/etc/pki/ca-trust/source/anchors、/etc/pki/ca-trust/source、/etc/pki/ca-trust/extracted、
/etc/pki/ca-trust/extracted/pem/、/etc/pki/tls/certs/cacert.crt
都會報錯:
# docker login https://registry.domain.com:8000
Username (sean): sean
2014/11/14 02:32:48 Error response from daemon: Invalid Registry endpoint: Get https://registry.domain.com:8000/v1/_ping: x509: certificate signed by unknown authority
# curl https://sean:sean@registry.domain.com:8000/
curl: (60) Peer certificate cannot be authenticated with known CA certificates
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
(6) 目前讓根證書起作用的只發(fā)現(xiàn)一個辦法:
# cp /etc/pki/tls/certs/ca-bundle.crt{,.bak} 備份以防出錯
# cat /etc/pki/CA/cacert.pem >> /etc/pki/tls/certs/ca-bundle.crt
# curl https://sean:sean@registry.domain.com:8000
"docker-registry server (dev) (v0.8.1)"
將cacert.pem
根證書追加到ca-bundle.crt
后一定要重啟docker后臺進(jìn)程才行。
如果docker login依然報錯certificate signed by unknown authority
,參考Running Docker with https,啟動docker后臺進(jìn)程時指定信任的CA根證書:
# docker -d --tlsverify --tlscacert /etc/pki/CA/cacert.pem
或者將cacert.pem拷貝到~/.docker/ca.pem
# mkdir ~/.docker && cp /etc/pki/CA/cacert.pem ~/.docker/ca.pem
# docker -d
最好重啟一下registry
# docker restart <registry_container_id>
上面用“如果”是因為一開始總提示certificate signed by unknown authority
,有人說將根證書放在/etc/docker/certs.d
下,還有人說啟動docker daemon收加入--insecure-registry
.. 但終究是因為版本差異不成功。但后來又奇跡般的不需要--tlscacert
就好了。
這個地方掙扎了很久,重點關(guān)注一下這個下面幾個issue:
- https://github.com/docker/docker-registry/issues/82
- https://github.com/docker/docker/pull/2687
- https://github.com/docker/docker/pull/2339
(7) 最終搞定:
# docker login https://registry.domain.com:8000
Username: sean
Password:
Email: zhouxiao@domain.com
Login Succeeded
# curl https://sean:sean@registry.domain.com:8000
"docker-registry server (dev) (v0.8.1)"
# docker push registry.domain.com:8000/centos6:test_priv
The push refers to a repository [registry.domain.com:8000/centos6] (len: 1)
Sending image list
Pushing repository registry.domain.com:8000/centos6 (1 tags)
511136ea3c5a: Image successfully pushed
5b12ef8fd570: Image successfully pushed
68edf809afe7: Image successfully pushed
40627956f44c: Image successfully pushed
Pushing tag for rev [40627956f44c] on {https://registry.domain.com:8000/v1/repositories/centos6/tags/test_priv}
但還有一個小問題沒解決,雖然已經(jīng)可以正常使用,但每次請求在nginx的error.log中還是會有[error] 8299#0: *27 no user/password was provided for basic authentication
,應(yīng)該是這個版本docker暫未解決的bug。
3.3 其它問題
(1) docker后臺進(jìn)程意外中斷后,重新docker start <container_id>
報錯
# docker start b36bd796bd3d
Error: Cannot start container b36bd796bd3d: Error getting container b36bd796bd3d463c4fedb70d98621e7318ec3d5cd14b2f60b1d182ad3cbcc652
from driver devicemapper: Error mounting '/dev/mapper/docker-253:0-787676-b36bd796bd3d463c4fedb70d98621e7318ec3d5cd14b2f60b1d182ad3cbcc652'
on '/var/lib/docker/devicemapper/mnt/b36bd796bd3d463c4fedb70d98621e7318ec3d5cd14b2f60b1d182ad3cbcc652': device or resource busy
2014/11/08 15:14:57 Error: failed to start one or more containers
經(jīng)分析產(chǎn)生這個問題的原因是做了一個操作:在docker后臺進(jìn)程啟動的終端,繼續(xù)回車后會臨時退出后臺進(jìn)程的日志輸出,我就在這個shell下使用yum安裝軟件包,但由于網(wǎng)絡(luò)原因yum卡住不動,于是我就另起了一個終端kill了這個yum進(jìn)程,不知為何會影響到表面已經(jīng)退出前臺輸出的docker。解決辦法是umount容器的掛載點:(見這里)
# umount /var/lib/docker/devicemapper/mnt/b36bd796bd3d463c4fedb70d98621e7318ec3d5cd14b2f60b1d182ad3cbcc652
# service docker start 正常
能想到的另外一個辦法是,啟動docker后臺進(jìn)程時,重定向輸出docker -d > /dev/null 2>&1(/var/log/docker已自動記錄了一份日志)。
(2) 配置完nginx的docker-registry.conf后啟動報錯
# service nginx start
[emerg] 14714#0: unknown directive "upstream" in /etc/nginx/conf.d/docker-registry.conf:4
原因是nginx版本太低,一些配置指令不兼容,使用yum install nginx默認(rèn)安裝了1.0.x,卸載重新下載nginx-1.4.7-1.el6.ngx.x86_64.rpm
安裝解決。
(3) 網(wǎng)絡(luò)設(shè)置代理問題
pull, push官網(wǎng)的鏡像時由于GFW的原因需要設(shè)置代理,但不是http_proxy而是HTTP_PROXY,對于docker來說同時設(shè)置這兩個值就會出問題,有時出于安裝軟件包的需要設(shè)置http_proxy,就會導(dǎo)致沖突。在docker-registry中如果忘記了當(dāng)前哪一個在起作用,找遍所有問題都發(fā)現(xiàn)不了原因,而docker返回給我們的錯誤也難以判斷。切記~
TO-DO
如何刪除docker-registry的里的鏡像
4. 參考
部署自己的私有 Docker Registry [英文]
Official docker-registry README
How To Set Up a Private Docker Registry on Ubuntu 14.04
The Docker Hub and the Registry spec
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。