如何通过自己build构建docker的私有仓库

90次阅读
没有评论

共计 12891 个字符,预计需要花费 33 分钟才能阅读完成。

这篇文章将为大家详细讲解有关如何通过自己 build 构建 docker 的私有仓库,丸趣 TV 小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

Docker Registry 说明

(注意,我是 ubuntu14.04 构建的,修改过 /etc/default/docker 文件,添加

 DOCKER_OPTS= $DOCKER_OPTS — insecure-registry=10.32.170.108:5000,然后 service restart docker, 可能可以解决 https 证书错误问题。)

关于如何创建和使用本地仓库,其实已经有很多文章介绍了。因为 docker 技术正处于发展和完善阶段,所以有些文章要么内容已经过时,要么给出了错误的配置,导致无法正常创建仓库。本文记录的是个人完整的搭建过程,docker version 为 1.1.2。

官方提供了 Docker Hub 网站来作为一个公开的集中仓库。然而,本地访问 Docker Hub 速度往往很慢,并且很多时候我们需要一个本地的私有仓库只供网内使用。

Docker 仓库实际上提供两方面的功能,一个是镜像管理,一个是认证。前者主要由 docker-registry 项目来实现,通过 http 服务来上传下载;后者可以通过 docker-index(闭源)项目或者利用现成认证方案(如 nginx)实现 http 请求管理。

docker-registry 既然也是软件应用,自然最简单的方法就是使用官方提供的已经部署好的镜像 registry。官方文档中也给出了建议,直接运行 sudo docker run -p 5000:5000 registry 命令。这样确实能启动一个 registry 服务器,但是所有上传的镜像其实都是由 docker 容器管理,放在了 /var/lib/docker/…. 某个目录下。而且一旦删除容器,镜像也会被删除。因此,我们需要想办法告诉 docker 容器镜像应该存放在哪里。registry 镜像中启动后镜像默认位置是 /tmp/registry,因此直接映射这个位置即可,比如到本机的 /opt/data/registry 目录下。

2. 在 CentOS 上搭建 docker 私服 2.1 安装 docker-registry

方法有多种,直接运行下面的命令:

# docker run -d -e SETTINGS_FLAVOR=dev -e STORAGE_PATH=/tmp/registry -v /opt/data/registry:/tmp/registry -p 5000:5000 registry(此为镜像名字,上面是 local-sean, 可修改其 tag 为 registry)

如果本地没有拉取过 docker-registry,则首次运行会 pull registry,运行时会映射路径和端口,以后就可以从 /opt/data/registry 下找到私有仓库都存在哪些镜像,通过主机的哪个端口可以访问。
你也可以把项目 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
docker run -d -e SETTINGS_FLAVOR=dev -e STORAGE_PATH=/tmp/registry -v /opt/data/registry:/tmp/registry -p 5000:5000 registry

2.2 客户端使用

要从私服上获取镜像或向私服提交镜像,现在变得非常简单,只需要在仓库前面加上私服的地址和端口,形如 172.29.88.222:5000/centos6。注意,这里可以选择不使用 IP,而是用 hostname,如 registry.domain.com:5000,但不能仅用不带. 的主机名 registry,docker 会认为 registry 是用户名,建议使用带域名的 hostname 加 port 来表示。

于是在另外一台要使用 docker 的主机上就可以通过这台私服拉取和推送镜像了:

 从私服上搜索存在哪些可用镜像 # 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 到私服上时会提示用户名、密码和邮箱,创建即可。也可以在 docker 私服端加入认证机制。

3. 加入 nginx 认证

(请在实际操作以前,先阅读完本节,再确定是否在前端加入 nginx)

3.1 安装及配置 nginx

从上面的过程可以看到,除非防火墙限制,否则任何主机可以创建账号并想私服推送镜像,更安全的做法是在外层加入登录认证机制。

 最好安装 1.4.x 版本,不然下面的有些配置可能会不兼容
# yum install nginx  创建两个登录用户  # htpasswd -c /etc/nginx/docker-registry.htpasswd sean New password: Re-type new password: Adding password for user sean # htpasswd -c /etc/nginx/docker-registry.htpasswd itsection

为了让 nginx 使用这个密码文件,并且转发 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 necessaryupstream 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 来激活虚拟主机的配置 # service nginx restart

3.2 加入认证后使用 docker-registry

此时主机的 5000 端口应该通过防火墙禁止访问(或者在 docker run 端口映射时只监听回环接口的 IP -p 127.0.0.1:5000:5000)。

# curl localhost:5000 docker-registry server (dev) (v0.8.1)

如果直接访问访问将得到未授权的信息:

# curl localhost:8080 html head title 401 Authorization Required /title /head body bgcolor= white center h2 401 Authorization Required /h2 /center hr center nginx/1.4.7 /center /body /html

带用户认证的 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:8080Username: seanPassword: Email: zhouxiao@domain.comLogin Succeeded# docker pull registry.domain.com:8080/library/centos6

不出意外的话,上面的 docker pull 会失败:

# docker pull registry.domain.com:8080/library/centos6Pulling repository registry.domain.com:8080/library/centos62014/11/11 21:00:25 Could not reach any registry endpoint# docker push registry.domain.com:8080/ubuntu:seanThe 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 篇参考文档没有出现这个问题,但评论中有提及。
有人说是 backend storage 的问题,这里是本地存储镜像,不应该。经过查阅大量资料,并反复操作验证,是 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 及以下版本。现在都 v1.3.1 了,我猜你不会这么做

修改源码 session.go,去掉相应的判断行,然后 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_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 证书,那就会变得非常简单,把证书和私钥拷贝成上面一样即可。关于 SSL 以及签署 ssl 证书,请参考其他文章。
这里我们自签署一个 ssl 证书,把当前系统作为(私有)证书颁发中心(CA)。

创建存放证书的目录

# mkdir /etc/nginx/ssl

确认 CA 的一些配置文件

# vi /etc/pki/tls/openssl.cnf...
[ CA_default ]
dir = /etc/pki/CA # Where everything is keptcerts = $dir/certs # Where the issued certs are keptcrl_dir = $dir/crl # Where the issued crl are keptdatabase = $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 certificateserial = $dir/serial # The current serial numbercrlnumber = $dir/crlnumber # the current crl number
 # must be commented out to leave a V1 CRLcrl = $dir/crl.pem # The current CRLprivate_key = $dir/private/cakey.pem # The private keyRANDFILE = $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 = 2countryName_max = 2stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = GD
...[ req_distinguished_name ] 部分主要是颁证时一些默认的值,可以不动 

(1) 生成根密钥

# cd /etc/pki/CA/# openssl genrsa -out private/cakey.pem 2048

为了安全起见,修改 cakey.pem 私钥文件权限为 600 或 400,也可以使用子 shell 生成 (umask 077; openssl genrsa -out private/cakey.pem 2048),下面不再重复。

(2) 生成根证书

# openssl req -new -x509 -key private/cakey.pem -out cacert.pem

会提示输入一些内容,因为是私有的,所以可以随便输入,最好记住能与后面保持一致。上面的自签证书 cacert.pem 应该生成在 /etc/pki/CA 下。

(3) 为我们的 nginx web 服务器生成 ssl 密钥

# cd /etc/nginx/ssl# openssl genrsa -out nginx.key 2048

我们的 CA 中心与要申请证书的服务器是同一个,否则应该是在另一台需要用到证书的服务器上生成。

(4) 为 nginx 生成证书签署请求

# openssl req -new -key nginx.key -out nginx.csr...Country Name (2 letter code) [AU]:CNState or Province Name (full name) [Some-State]:GDLocality Name (eg, city) []:SZOrganization Name (eg, company) [Internet Widgits Pty Ltd]:COMPANYOrganizational Unit Name (eg, section) []:IT_SECTIONCommon Name (e.g. server FQDN or YOUR name) []:your.domain.comEmail Address []:Please enter the following  extra  attributes
to be sent with your certificate requestA challenge password []:An optional company name []:...

同样会提示输入一些内容,其它随便,除了 Commone Name 一定要是你要授予证书的服务器域名或主机名,challenge password 不填。

(5) 私有 CA 根据请求来签发证书

# openssl ca -in nginx.csr -out nginx.crt

上面签发过程其实默认使用了 -cert cacert.pem -keyfile cakey.pem,这两个文件就是前两步生成的位于 /etc/pki/CA 下的根密钥和根证书。

到此我们已经拥有了建立 ssl 安全连接所需要的所有文件,并且服务器的 crt 和 key 都位于配置的目录下,唯有根证书 cacert.pem 位置不确定放在 CentOS6 下的哪个地方。
经验证以下几个位置不行:(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:8000Username (sean): sean2014/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.htmlcurl 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) 目前让根证书起作用的只发现一个办法:

# 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 后台进程才行。

如果 docker login 依然报错 certificate signed by unknown authority,参考 Running Docker with https,启动 docker 后台进程时指定信任的 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 就好了。
这个地方挣扎了很久,重点关注一下这个下面几个 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:8000Username: 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_privThe push refers to a repository [registry.domain.com:8000/centos6] (len: 1)
Sending image listPushing 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}

但还有一个小问题没解决,虽然已经可以正常使用,但每次请求在 nginx 的 error.log 中还是会有 [error] 8299#0: *27 no user/password was provided for basic authentication,应该是这个版本 docker 暂未解决的 bug。

3.3 其它问题

(1) docker 后台进程意外中断后,重新 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 busy2014/11/08 15:14:57 Error: failed to start one or more containers

经分析产生这个问题的原因是做了一个操作:在 docker 后台进程启动的终端,继续回车后会临时退出后台进程的日志输出,我就在这个 shell 下使用 yum 安装软件包,但由于网络原因 yum 卡住不动,于是我就另起了一个终端 kill 了这个 yum 进程,不知为何会影响到表面已经退出前台输出的 docker。解决办法是 umount 容器的挂载点:(见这里)

# umount /var/lib/docker/devicemapper/mnt/b36bd796bd3d463c4fedb70d98621e7318ec3d5cd14b2f60b1d182ad3cbcc652# service docker start  正常 

能想到的另外一个办法是,启动 docker 后台进程时,重定向输出 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 默认安装了 1.0.x,卸载重新下载 nginx-1.4.7-1.el6.ngx.x86_64.rpm 安装解决。

(3) 网络设置代理问题
pull, push 官网的镜像时由于 GFW 的原因需要设置代理,但不是 http_proxy 而是 HTTP_PROXY,对于 docker 来说同时设置这两个值就会出问题,有时出于安装软件包的需要设置 http_proxy,就会导致冲突。在 docker-registry 中如果忘记了当前哪一个在起作用,找遍所有问题都发现不了原因,而 docker 返回给我们的错误也难以判断。切记~

关于“如何通过自己 build 构建 docker 的私有仓库”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-16发表,共计12891字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)