如何在Ubuntu上搭建私有Docker仓库
本章的内容已经过时!!
Docker作为在服务器上,非常不错的一个部署部署工具。那大家应该使用过Docker hub,一个存储镜像的公有的Docker仓库。但是,当你创建了一个镜像上传到Docker hub上之后,必须公开你所创建的镜像——这可能不是你所想要的。因为,我们的很多项目,可能需要私有化。
这是一篇如何搭建Docker私有仓库,并对仓库进行加密的教程。在教程的最后,你将学会如何建立私有镜像上传到自己的Docker仓库,并且以一种加密的方式,把自己的镜像从私有仓库拉取回来。
这篇教程没有把容器相关的知识全部涵盖到,只是把创建私有Docker放在第一介绍的位置。如果说,你想去学习Docker(而非Docker仓库本身),你可以在这个地方学习:
这篇教程在Ubuntu14.04环境上架设,也可以在基于Debian的发行版上架设。同样,本教程涵盖了Docker 2.0版本。
Docker相关概念
如果说之前你没有使用过Docker,那可能需要先来学习几个Docker相关的概念。如果说,你已经学习并使用过Docker,可以跳过这个章节,进入下一步。
为小白准备了一个Docker备忘录。
Docker的核心是分离应用和运行于该操作系统上的应用依赖。为了达到这个目的,Docker使用了容器与镜像。Docker镜像是一个基础的模块文件系统。当创建了Docker镜像之后,一个实例的文件系统就会创建,并在宿主机的Docker容器中运行起来。但是,宿主机的文件系统与容器的文件系统默认是隔离的,容器是一个隔离的运行环境。
无论在容器内部作了任何的改变,都不会影响到Docker的原始镜像,只会影响到正在运行的镜像本身。如果说,需要把改变保留下来,使用commit
命令把该镜像容器生成一个镜像(docker commit
命令)。这意味着,你可以根据旧的容器来产生新的容器,并且不影响原先的容器的文件系统(或者说镜像)。如果说你之前学习过git
,那么Docker与Git有很相近的工作流:Docker里面的新容器或者说新镜像,就像是在Git中新建
了一个分支一样,运行珍上新的镜像,就像是git中的切换分支git checkout
。
形象一点说,存放Docker镜像的Docker仓库中的就像Git仓库一样。
先决条件
继续这个教程,可能需要以下环境:
- 有管理员账户的2个Ubuntu14.04环境: 一个Docker仓库,一个Docker客户机。
- 安装Docker和Docker Compose。
- 一个可以解析到Ubuntu机器的域名。
安装Apache2扩展包
为了让Docker仓库更加安全,最好使用Docker Compose。 通过Docker Compose可以轻松在Docker容器中构建Docker仓库,并且在另一个Docker镜像中构建Nginx容器,来负责Docker仓库认证环境。
所以,我们需要一个文件来存放访问我们私有仓库的用户名与密码。需要安装apached2-utils
,它包含了htpasswd
工具包,可以产生Nginx可以理解的hashes值,来保证Docker仓库的安全:
sudo apt-get -y install apache2-utils
安装与配置Docker仓库
Docker原生的命令行工具可以很好的管理与运行Docker容器,但是大多数容器并不是独立运行的容器。为了可以部署大部分应用,可能需要并行运行多个组件。例如:大多数应用需要的Web服务,可能需要像解析语言PHP、或者Ruby(with rails),数据库服务MySQL。
使用Docker Compose可以在.yml
的配置文件中,配置所有的Docker容器,以及容器之间的通信。使用docker-compose
命令行工具,可以轻松的管理docker容器,docker-compose
就像是一个复合管理命令。
Docker仓库需要多个组件来运行,使用docker-compose
来进行管理,唯一需要定义的是仓库存放数据的位置。开始吧,在宿主机上,建立一个放置YAML配置文件和数据的位置:
mkdir ~/docker-registry && cd $_
mkdir data
使用nano
或者vim
来编辑docker-compose.yml
文件:
nano docker-compose.yml
添加如下内容:
registry:
container_name: "docker-registry"
image: registry:2
ports:
- 5000:5000
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
- /root/docker-registry/data:/data
通过设置docker仓库的环境变量REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
来指定仓库容器建立之后,数据在容器中的存放位置。当容器启动之后,会到该变量的地方去建立数据。而volumes
配置,设置了与宿主机的数据映射关系:在容器中的/data
目录与宿主机的~/docker-registry/data
建立了宿主关系。启动容器:
cd ~/docker-registry
docker-compose up -d
可以通过docker logs docker-registry
查看docker-registry的运行日志信息。
上面的配置文件很好理解,有一个叫docker-registry
的docker镜像registry:2
,运行在5000端口,映射到宿主机的5000端口。
可以通过docker-compose stop docker-registry
和docker=compose rm docker-registry
来停止与删除容器。
运行Nginx容器
下面来着手解决容器的安全问题。第一步,需要另外新建一个Nginx容器,并且与我们的Dokcer仓库链接起来。新建一个Ngnix的配置文件:
mkdir ~/docker-registry/nginx
nano ~/docker-registry/nginx/registry.conf
加入以下内容:
upstream docker-registry {
server registry:5000;
}
server {
listen 443;
server_name myregistrydomain.com;
# SSL
# ssl on;
# ssl_certificate /etc/nginx/conf.d/domain.crt;
# ssl_certificate_key /etc/nginx/conf.d/domain.key;
# 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/docker/docker/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 plus add_header
# auth_basic "registry.localhost";
# auth_basic_user_file /etc/nginx/conf.d/registry.password;
# add_header 'Docker-Distribution-Api-Version' 'registry/2.0' 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;
}
}
并且再次编辑目录~/docker-registry
下的docker-compose.yml
文件:
nano docker-compose.yml
加入以下内容:
registry:
container_name: "docker-registry"
image: registry:2
ports:
- 5000:5000
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
- ./data:/data
nginx:
container_name: "docker-nignx"
image: "nginx:1.9"
ports:
- 5043:443
links:
- registry:registry
volumes:
- ./nginx/:/etc/nginx/conf.d:ro
volumes
可以使用绝对位置,也可以使用相对位置,相对该配置文件的位置。
使用docker-compose up -d
命令,可以在同一时间一次运行两个容器:docker仓库和Nginx。同样,使用log
命令,查看docker与nginx的运行情况。
测试HTTP请求:
curl http://localhost:5000/v2/
如果看到:
{}
或者:
curl http://localhost:5043/v2/
看到:
{}
说明,已经成功构建了Docker仓库,并且Nginx运行良好。如果,没有curl命令,可以通过apt-get install curl
来安装curl
。
使用docker-compose
中的日志命令,在docker-compose.yml
配置文件下使用如下命令:
docker-compose logs
可以看到如下信息:
registry_1 | time="2015-08-11T10:24:53.746529894Z" level=debug msg="authorizing request" environment=development http.request.host="localhost:5043" http.request.id=55c3e2a6-4f34-4b0b-bc57-11c814b4f4d3 http.request.method=GET http.request.remoteaddr=172.17.42.1 http.request.uri="/v2/" http.request.useragent="curl/7.35.0" instance.id=55634dfc-c9e0-4ec9-9872-6f4930c17759 service=registry version=v2.0.1
registry_1 | time="2015-08-11T10:24:53.747650205Z" level=info msg="response completed" environment=development http.request.host="localhost:5043" http.request.id=55c3e2a6-4f34-4b0b-bc57-11c814b4f4d3 http.request.method=GET http.request.remoteaddr=172.17.42.1 http.request.uri="/v2/" http.request.useragent="curl/7.35.0" http.response.contenttype="application/json; charset=utf-8" http.response.duration=8.143193ms http.response.status=200 http.response.written=2 instance.id=55634dfc-c9e0-4ec9-9872-6f4930c17759 service=registry version=v2.0.1
registry_1 | 172.17.0.21 - - [11/Aug/2015:10:24:53 +0000] "GET /v2/ HTTP/1.0" 200 2 "" "curl/7.35.0"
nginx_1 | 172.17.42.1 - - [11/Aug/2015:10:24:53 +0000] "GET /v2/ HTTP/1.1" 200 2 "-" "curl/7.35.0" "-"
设置认证服务
现在,Nginx作为代理控制着Docker仓库的HTTP请求,所以,可以通过Nginx来控制用户访问权限。使用之前安装的apache的htpasswd
组件,创建第一个用户:(下面的USERNAME
可以替换成其他的用户)
cd ~/docker-registry/nginx
htpasswd -c registry.password USERNAME
创建用户密码:
htpasswd registry.password USERNAME
使用-c
可以重新创建配置文件,会覆盖之前的用户配置信息。
查看之前创建的Nginx的配置文件:
nano ~/docker-registry/nginx/registry.conf
把如下注释给取消:
# To add basic authentication to v2 use auth_basic setting plus add_header
auth_basic "registry.localhost";
auth_basic_user_file /etc/nginx/conf.d/registry.password;
add_header 'Docker-Distribution-Api-Version' 'registry/2.0' always;
使用docker-compose up --force-recreate -d
重新创建容器,使上述的配置生效。再次使用curl
命令:
curl http://localhost:5043/v2/
会看到如下内容:
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.9.7</center>
</body>
</html>
使用如下命令:
curl http://USERNAME:PASSWORD@localhost:5043/v2/
如果看到:
{}
说明,设置认证服务已经生效。
设置SSL
首先,设置nginx配置文件,取消如下注释内容:
server {
listen 443;
server_name myregistrydomain.com;
# SSL
ssl on;
ssl_certificate /etc/nginx/conf.d/domain.crt;
ssl_certificate_key /etc/nginx/conf.d/domain.key;
这个地方要注意一下,之前在docker-compose.yml
配置文件中,宿主机的~/docker-registry/nginx/
与Nginx容器的/etc/nginx/conf.d/
映射关系,把相应的认证密钥与证书放置在宿主机的~/docker-registry/nginx/
下,即Nginx容器可以访问了。
两种获取SSL证书的方式:自签或者申请。
申请有免费的DV证书,也有前面博客介绍的使用Let’s Encrypt全站启用HTTPS协议的方法申请Let’s Encrypt的免费证书。
下面介绍一下自签证书的方法:
cd ~/docker-registry/nginx
使用openssl
来自签证书:
openssl genrsa -out devdockerCA.key 2048
openssl req -x509 -new -nodes -key devdockerCA.key -days 10000 -out devdockerCA.crt
openssl genrsa -out domain.key 2048
重要:在
Common Name
处输入docker服务器的域名或者IP。
openssl req -new -key domain.key -out dev-docker-registry.com.csr
如果域名www.toimc.com
,那么可以按照如下设置:
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:www.toimc.com
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
不需要设置challenge password。
openssl x509 -req -in dev-docker-registry.com.csr -CA devdockerCA.crt -CAkey devdockerCA.key -CAcreateserial -out domain.crt -days 10000
因为证书没有任何认证机构进行认证,所以需要给所有的客户机这是一个合法的证书。首先,在宿主机上设置,这样,可以在Docker容器中使用Docker仓库中的证书。
sudo mkdir /usr/local/share/ca-certificates/docker-dev-cert
sudo cp devdockerCA.crt /usr/local/share/ca-certificates/docker-dev-cert
sudo update-ca-certificates
重启Docker服务:
sudo service docker restart
测试SSL证书
cd ~/docker-registry
docker-compose up --force-recreate -d
使用curl
来测试HTTPS连接:
curl https://USERNAME:PASSWORD@[YOUR-DOMAIN]:5043/v2/
如果使用是自签的证书,可能看到如下错误:
curl: (60) SSL certificate problem: self signed certificate
使用
-k
选项,不认证peer:curl -k https://USERNAME:PASSWORD@[YOUR-DOMAIN]:5043/v2/
如果域名是:www.toimc.com
curl https://liwei:test@www.toimc.com:5043/v2/
收到{}
,说明HTTPS工作正常。
设置SSL为443端口
打开docker-compose.yml
的配置:
nano ~/docker-registry/docker-compose.yml
修改端口的映射关系:
nginx:
image: "nginx:1.9"
ports:
- 443:443
links:
- registry:registry
volumes:
- ./nginx/:/etc/nginx/conf.d:ro
registry:
image: registry:2
ports:
- 5000:5000
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
- ./data:/data
使用--force-recreate
来重新生成容器。
现在可以使用:
docker login https://<YOURDOMAIN>
或者:
curl https://<YOURUSERNAME>:<YOURPASSWORD>@YOUR-DOMAIN/v2/
在客户机上进行测试。
如何使用
与docker.io的使用是一样的,只不过在commit镜像的时候,需要加上仓库地址,如:
docker login https://<YOURDOMAIN>
输入密码,然后:
docker commit <容器ID> <YOURDOMAIN>/<容器名>
docker push <YOURDOMAIN>/<容器名>
使用镜像:
docker pull <YOURDOMAIN>/<容器名>