介绍
Docker 是部署服务器的绝佳工具。 Docker 甚至有一个名为 Docker Hub 的公共注册表来存储 Docker 映像。 虽然 Docker 允许您免费将 Docker 创建上传到他们的 Docker Hub,但您上传的任何内容也是公开的。 这可能不是您项目的最佳选择。
本指南将向您展示如何设置和保护您自己的私有 Docker 注册表。 在本教程结束时,您将能够将自定义 Docker 镜像推送到您的私有注册表并从不同的主机安全地拉取镜像。
本教程不涉及容器化您自己的应用程序,而只涉及如何创建可以存储部署的注册表。 如果您想了解如何开始使用 Docker 本身(而不是注册表),您可能需要阅读 如何安装和使用 Docker:入门 教程。
本教程已经在运行 Ubuntu 14.04 的注册表服务器和注册表客户端上进行了测试,但它可能适用于其他基于 Debian 的发行版。 它还涵盖了 Docker Registry 2.0 版。
码头工人概念
如果您之前没有使用过 Docker,那么有必要花一点时间了解一下 Docker 的一些关键概念。 如果您已经在使用 Docker 并且只想知道如何开始运行自己的注册表,那么请跳到下一部分。
要重新了解如何使用 Docker,请查看优秀的 Docker 备忘单。
Docker 的核心是一种将应用程序和运行它所需的依赖项与操作系统本身分离的方法。 为了使这成为可能,Docker 使用 containers 和 images。 Docker 映像基本上是文件系统的模板。 当你运行一个 Docker 镜像时,这个文件系统的一个实例被激活并在你的系统上运行在一个 Docker 容器中。 默认情况下,这个容器不能触及原始镜像本身或运行 Docker 的主机的文件系统。 这是一个独立的环境。
您在容器中所做的任何更改都会保留在该容器本身中,并且不会影响原始图像。 如果您决定要保留这些更改,则可以将容器“提交”到 Docker 映像(通过 docker commit
命令)。 这意味着您可以生成以旧容器内容开头的新容器,而不会影响原始容器(或映像)。 如果您熟悉 git
,那么工作流程应该看起来非常相似:您可以从任何容器创建新的分支(Docker 术语中的图像)。 运行图像有点像 git checkout
。
继续类比,运行私有 Docker 注册表就像为 Docker 映像运行私有 Git 存储库。
先决条件
要完成本教程,您将需要以下内容:
- 2 Ubuntu 14.04 Droplets:一个用于私有 Docker 注册表,一个用于 Docker 客户端
- 对每个 Droplet 具有 sudo 权限的非 root 用户(Initial Server Setup with Ubuntu 14.04 解释了如何设置。)
- 使用 How To Install and Use Docker Compose on Ubuntu 14.04 中的说明安装 Docker 和 Docker Compose
- 解析为私有 Docker 注册表的 Droplet 的域名
第 1 步 — 安装包以增加安全性
要为 Docker Registry 设置安全性,最好使用 Docker Compose。 这样我们就可以轻松地在一个容器中运行 Docker Registry,并让 Nginx 在另一个容器中处理安全和与外界的通信。 您应该已经从先决条件部分安装了它。
由于我们将使用 Nginx 来处理我们的安全性,我们还需要一个地方来存储我们想要访问我们的注册表的用户名和密码组合列表。 我们将安装 apache2-utils
包,其中包含 htpasswd
实用程序,可以轻松生成 Nginx 可以理解的密码哈希:
sudo apt-get -y install apache2-utils
第 2 步 — 安装和配置 Docker 注册表
Docker 命令行工具非常适合启动和管理一个或两个 Docker 容器,但是在 Docker 容器中运行的大多数应用程序并不是孤立存在的。 要完全部署大多数应用程序,您需要一些并行运行的组件。 例如,大多数 Web 应用程序由提供应用程序代码的 Web 服务器、解释性脚本语言(如 PHP 或 Ruby(使用 Rails))和数据库服务器(如 MySQL)组成。
Docker Compose 允许您为每个容器的配置编写一个 .yml
配置文件,以及有关容器如何相互通信的信息。 然后使用 docker-compose
命令行工具向构成应用程序的所有组件发出命令。
由于 Docker 注册表本身是一个包含多个组件的应用程序,因此我们将使用 Docker Compose 来管理我们的配置。
要启动基本注册表,唯一需要的配置是定义注册表将存储其数据的位置。 让我们设置一个基本的 Docker Compose YAML 文件来创建一个基本的注册表实例。
首先创建一个文件夹,用于存放本教程的文件以及我们需要的一些子文件夹:
mkdir ~/docker-registry && cd $_ mkdir data
使用您喜欢的文本编辑器,创建一个 docker-compose.yml
文件:
nano docker-compose.yml
将以下内容添加到文件中:
码头工人-compose.yml
registry: image: registry:2 ports: - 127.0.0.1:5000:5000 environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./data:/data
有趣的是在最后。 environment
部分在 Docker 注册表容器中使用路径 /data
设置环境变量。 Docker 注册表应用程序知道在启动时检查此环境变量,并因此开始将其数据保存到 /data
文件夹。
只有在这种情况下,volumes: - ./data:/data
位告诉 Docker,该容器中的 /data
目录实际上应该映射到我们主机上的 /data
。 所以最终结果是 Docker 注册表的数据都存储在我们本地机器上的 ~/docker-registry/data
中。
让我们继续启动它以确保一切正常:
cd ~/docker-registry docker-compose up
您会看到一堆下载栏在屏幕上移动(这是 Docker 从 Docker 自己的 Docker Registry 下载实际的 Docker Registry 映像)。 如果在一两分钟内一切顺利,您应该会看到如下所示的输出(版本可能会有所不同):
Output of docker-compose upregistry_1 | time="2015-10-18T23:45:58Z" level=warning msg="No HTTP secret provided - generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable." instance.id=44c828de-c27a-401e-bb2e-38b17e6a4b7b version=v2.1.1 registry_1 | time="2015-10-18T23:45:58Z" level=info msg="redis not configured" instance.id=44c828de-c27a-401e-bb2e-38b17e6a4b7b version=v2.1.1 registry_1 | time="2015-10-18T23:45:58Z" level=info msg="using inmemory blob descriptor cache" instance.id=44c828de-c27a-401e-bb2e-38b17e6a4b7b version=v2.1.1 registry_1 | time="2015-10-18T23:45:58Z" level=info msg="listening on [::]:5000" instance.id=44c828de-c27a-401e-bb2e-38b17e6a4b7b version=v2.1.1 registry_1 | time="2015-10-18T23:45:58Z" level=info msg="Starting upload purge in 1m0s" instance.id=44c828de-c27a-401e-bb2e-38b17e6a4b7b version=v2.1.1
不用担心 No HTTP secret provided
消息。 这是正常的。
伟大的! 此时,您已经启动并运行了完整的 Docker 注册表并在端口 5000 上侦听(这是由 docker-compose.yml
文件中的 ports:
位设置的)。 此时注册表还没有那么有用——除非您手动启动注册表,否则它不会启动。 此外,Docker 注册表没有任何内置的身份验证机制,因此它是不安全的,并且现在完全对公众开放。
默认情况下,Docker Compose 将永远等待您的输入,因此请继续点击 CTRL-C
以关闭您的 Docker 注册表容器。
第 3 步 — 设置 Nginx 容器
让我们着手解决这些安全问题。 第一步是在另一个 Docker 容器中设置 Nginx 的副本,并将其链接到我们的 Docker 注册表容器。
让我们首先创建一个目录来存储我们的 Nginx 配置:
mkdir ~/docker-registry/nginx
现在,重新打开 ~/docker-registry
目录中的 docker-compose.yml
文件:
nano docker-compose.yml
将以下内容粘贴到文件顶部:
码头工人-compose.yml
nginx: image: "nginx:1.9" ports: - 5043:443 links: - registry:registry volumes: - ./nginx/:/etc/nginx/conf.d:ro
这将基于官方 Nginx 镜像创建一个新的 Docker 容器。 这里有趣的是 links
部分。 它会自动建立一个从一个 Docker 容器到另一个容器的“链接”。 当 Nginx 容器启动时,无论 registry
容器最终拥有的实际 IP 地址是什么,它都可以访问主机名为 registry
的 registry
容器。 (在幕后 Docker 实际上是在 nginx
容器中的 /etc/hosts
文件中插入一个条目,告诉它 registry
容器的 IP)。
volumes:
部分类似于我们为 registry
容器所做的。 在这种情况下,它为我们提供了一种将用于 Nginx 的配置文件存储在主机上而不是在 Docker 容器中的方法。 末尾的 :ro
只是告诉 Docker Nginx 容器应该只有对主机文件系统的只读访问权限。
您的完整 docker-compose.yml
文件现在应该如下所示:
码头工人-compose.yml
nginx: image: "nginx:1.9" ports: - 5043:443 links: - registry:registry volumes: - ./nginx/:/etc/nginx/conf.d registry: image: registry:2 ports: - 127.0.0.1:5000:5000 environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./data:/data
运行 docker-compose up
现在将同时启动两个容器:一个用于 Docker 注册表,一个用于 Nginx。
我们需要先配置 Nginx,然后才能正常工作,所以让我们创建一个新的 Nginx 配置文件。
创建一个 registry.conf
文件:
nano ~/docker-registry/nginx/registry.conf
将以下内容复制到文件中:
~/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; } }
保存并退出文件。
现在您可以使用一个命令安装 Nginx 并启动两个 Docker 容器:
docker-compose up
Nginx 在启动时不会打印任何输出,但如果一切顺利,您现在正在运行一个 Nginx 副本,该副本设置为代理到您的 registry
容器。 为了测试它,让我们使用 curl
直接向我们的 Docker 注册表发出 HTTP 请求,然后向我们的 Nginx 端口发出另一个请求。 如果一切设置正确,则两种情况下的输出都是相同的(在撰写本文时,Docker 返回一个空的 json 对象“{}
”),因为 Nginx 会将请求代理到 Docker 注册表。
首先,直接向 Docker 注册表发出 HTTP 请求:
curl http://localhost:5000/v2/
在撰写本文时,Docker 返回一个空的 json 对象,因此您应该看到:
Output{}
现在向 Nginx 端口发送一个 HTTP 请求:
curl http://localhost:5043/v2/
您应该看到相同的输出:
Output{}
如果一切正常,您将在 docker-compose
终端中看到一些输出,如下所示:
Output of docker-compose 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" "-"
如果你看到带有 registry_
前缀的行(_
后面的数字在你的机器上可能不同),那么一切都很好,Nginx 已经成功地将我们的 HTTP 请求代理到 Docker 注册表。
继续在 docker-compose
终端中再次点击 CTRL-C
以关闭 Docker 容器。
第 4 步 — 设置身份验证
现在 Nginx 正在正确地代理请求,让我们使用 HTTP 身份验证对其进行设置,以便我们可以控制谁可以访问我们的 Docker 注册表。 为此,我们将通过我们之前安装的 htpasswd
实用程序创建一个 Apache 格式的身份验证文件(Nginx 也可以读取它)并向其中添加用户。
如下创建第一个用户,将 USERNAME 替换为您要使用的用户名:
cd ~/docker-registry/nginx htpasswd -c registry.password USERNAME
出现提示时为此用户创建一个新密码。
如果以后要添加更多用户,只需重新运行上面的命令,不带 -c
选项(c
用于创建):
htpasswd registry.password USERNAME
此时,我们有一个 registry.password
文件,其中包含我们的用户设置和一个可用的 Docker 注册表。 如果您想查看您的用户(如果您想撤销访问权限则删除用户),您可以随时查看该文件。
接下来,我们需要告诉 Nginx 使用该身份验证文件。
在您喜欢的文本编辑器中打开 ~/docker-registry/nginx/registry.conf
:
nano ~/docker-registry/nginx/registry.conf
滚动到文件的中间,您会看到一些如下所示的行:
~/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;
通过删除行首的 # 字符,取消注释以 auth_basic
开头的两行以及以 add_header
开头的行。 然后它应该看起来像这样:
~/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;
我们现在告诉 Nginx 为代理到 Docker 注册表的所有请求启用 HTTP 基本身份验证,并告诉它使用我们刚刚创建的密码文件。
让我们恢复我们的容器以查看身份验证是否有效:
cd ~/docker-registry docker-compose up
重复之前的 curl 测试:
curl http://localhost:5043/v2/
您应该收到一条抱怨未经授权的消息:
Output of curl<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
请求中:
curl http://USERNAME:PASSWORD@localhost:5043/v2/
您应该得到与之前相同的输出 - 空 json 对象 {}
。 您还应该在 docker-compose
终端中看到相同的 registry_
输出。
继续使用 docker-compose
终端中的 CTRL-C
来关闭 Docker 容器。
第 5 步 — 设置 SSL
此时,我们已经在 Nginx 后面启动并运行了注册表,并且 HTTP 基本身份验证正常工作。 但是,由于连接未加密,因此设置仍然不是很安全。 您可能已经注意到我们之前创建的 Nginx 配置文件中被注释掉的 SSL 行。
让我们启用它们。 首先,打开 Nginx 配置文件进行编辑:
nano ~/docker-registry/nginx/registry.conf
使用箭头键四处移动并查找这些行:
~/docker-registry/nginx/registry.conf
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;
通过删除它们前面的 #
字符来取消对 SSL 注释下方的行的注释。 如果您为服务器设置了域名,请在使用时将 server_name
的值更改为您的域名。 完成后,文件顶部应如下所示:
~/docker-registry/nginx/registry.conf
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;
保存文件。 Nginx 现在配置为使用 SSL,并将分别在 /etc/nginx/conf.d/domain.crt
和 /etc/nginx/conf.d/domain.key
中查找 SSL 证书和密钥文件。 由于我们之前在 docker-compose.yml
文件中设置的映射,Nginx 容器中的 /etc/nginx/conf.d/
路径对应于我们主机上的文件夹 ~/docker-registry/nginx/
,所以我们将把我们的证书文件在那里。
如果您已经设置了 SSL 证书或打算购买,则只需将证书和密钥文件复制到 registry.conf
中列出的路径(ssl_certificate
和 [X179X ])。
您还可以获得免费签名的SSL证书。
否则,我们将不得不使用自签名 SSL 证书。
签署您自己的证书
由于 Docker 目前不允许您使用自签名 SSL 证书,这比平时要复杂一些——我们还必须设置我们的系统来充当我们自己的证书签名机构。
首先,让我们切换到我们的 ~/docker-registry/nginx
文件夹并准备创建证书:
cd ~/docker-registry/nginx
生成一个新的根密钥:
openssl genrsa -out devdockerCA.key 2048
生成根证书(在提示处输入您想要的任何内容):
openssl req -x509 -new -nodes -key devdockerCA.key -days 10000 -out devdockerCA.crt
然后为您的服务器生成一个密钥(这是我们的 Nginx 配置中 ssl_certificate_key
引用的文件):
openssl genrsa -out domain.key 2048
现在我们必须提出一个证书签名请求。
键入此命令后,OpenSSL 将提示您回答几个问题。 前几个你想写什么,但是当 OpenSSL 提示你输入 “通用名”时,请确保输入你的服务器的域或 IP 。
openssl req -new -key domain.key -out dev-docker-registry.com.csr
例如,如果您的 Docker 注册表将在域 www.ilovedocker.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.ilovedocker.com Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
不要输入挑战密码。
接下来,我们需要对证书请求进行签名:
openssl x509 -req -in dev-docker-registry.com.csr -CA devdockerCA.crt -CAkey devdockerCA.key -CAcreateserial -out domain.crt -days 10000
由于我们刚刚生成的证书没有经过任何已知的证书颁发机构(例如 VeriSign)的验证,我们需要告诉任何将要使用这个 Docker 注册表的客户端这是一个合法的证书。 让我们在主机本地执行此操作,以便我们可以从 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
警告: 您必须对连接到此 Docker 注册表的每台机器重复此步骤! Step 9 — Accessing Your Docker Registry from a Client 中列出了如何为 Ubuntu 14.04 客户端执行此操作的说明。
第 6 步 — 测试 SSL
通过现在熟悉的 docker-compose up
启动我们的 Docker 容器:
cd ~/docker-registry docker-compose up
从另一个终端执行另一个 curl
测试(仅这次使用 https)以验证我们的 SSL 设置是否正常工作。 请记住,要使 SSL 正常工作,您必须使用之前在创建 SSL 证书时在 Common Name
字段中键入的相同域名。
curl https://USERNAME:PASSWORD@[YOUR-DOMAIN]:5043/v2/
注意:如果你使用的是自签名证书,你会从curl
看到如下错误:
curl: (60) SSL certificate problem: self signed certificate
使用 -k
选项告诉 curl
not 与对等方进行验证:
curl -k https://USERNAME:PASSWORD@[YOUR-DOMAIN]:5043/v2/
例如,如果您设置的用户和密码是 sammy
和 test
,并且您的 SSL 证书是用于 www.example.com
,那么您将键入以下内容:
curl https://sammy:test@www.example.com:5043/v2/
如果一切顺利,curl
将打印一个空的 json 对象 {}
,而您的 docker-compose
终端将打印通常的 registry_
输出。
如果不是,请重新检查 SSL 步骤和您的 Nginx 配置文件以确保一切正确。
在这一点上,我们有一个功能性的 Docker 注册表 2.0 并在 Nginx 服务器后面运行,该服务器通过 SSL 提供身份验证和加密。 如果您的防火墙配置为允许从外部访问端口 5043
,那么您应该能够从任何机器 docker login https://<YOURDOMAIN>
登录到此 Docker 注册表并输入您在前面的部分。
第 7 步 — 将 SSL 端口设置为 443
在我们完成之前还需要执行几个步骤:更改端口以使用标准 SSL 端口 443
(可选)并设置 docker-compose
以在启动时启动这组容器。
让我们首先将我们的 dockerized Nginx 容器设置为侦听端口 443(SSL 的标准端口),而不是我们目前使用的非标准端口 5043。 不过,低于 1024 的端口是 Linux 上的“特权”端口,这意味着我们将不得不以 root 身份运行我们的 docker-compose
容器。
首先在文本编辑器中打开 docker-compose.yml
:
nano ~/docker-registry/docker-compose.yml
在 Nginx 部分下,您将看到 ports:
部分,将 - 5043:443
行(这将我们主机上的端口 5043 映射到 Nginx 容器内的端口 443)更改为 - 443:443
这样我们的 Nginx 容器的 443 端口就会映射到我们主机的 443 端口。 完成后,您的 docker-compose.yml
应如下所示:
~/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: - 127.0.0.1:5000:5000 environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./data:/data
如果 docker-compose
会话仍在运行,请通过 CTRL-C 终止会话,然后在端口 443 上重新启动它:
sudo docker-compose up
注意: 只有root用户才能监听1024以下的端口。 请注意,这次您需要将 sudo
与 docker-compose
命令一起使用,以便 Nginx 可以在默认的 SSL 端口 443 上运行。
您应该会看到 docker-compose
正常启动。
让我们使用我们的域名尝试另一个 curl
测试,只是这次我们不会在 URL 中指定 :5043
:
curl https://<YOURUSERNAME>:<YOURPASSWORD>@YOUR-DOMAIN/v2/
如果一切顺利,您应该会在 docker-compose
终端中看到通常的 registry_
输出。 您可能还想尝试从另一台机器上运行相同的 curl
命令,以确保您的端口 443 暴露给外界。
继续使用 docker-compose
终端中的 CTRL-C
关闭 Docker 容器,然后再进行下一步。
第 8 步 - 启动 Docker Registry 作为服务
如果一切正常,让我们继续创建一个 Upstart 脚本,以便我们的 Docker 注册表在系统启动时启动。
首先让我们删除所有现有容器,将我们的 Docker 注册表移动到系统范围的位置并将其权限更改为 root:
cd ~/docker-registry docker-compose rm # this removes the existing containers sudo mv ~/docker-registry /docker-registry sudo chown -R root: /docker-registry
然后使用您最喜欢的文本编辑器创建一个 Upstart 脚本:
sudo nano /etc/init/docker-registry.conf
添加以下内容以创建 Upstart 脚本(让 Upstart 正确监控 Docker 容器有点棘手,如果您想了解有关此 Upstart 脚本在做什么的更多信息,请查看 this blog post):
/etc/init/docker-registry.conf
description "Docker Registry" start on runlevel [2345] stop on runlevel [016] respawn respawn limit 10 5 chdir /docker-registry exec /usr/local/bin/docker-compose up
有关 Upstart 脚本的更多信息,请阅读本教程。
让我们通过运行来测试我们的新 Upstart 脚本:
sudo service docker-registry start
您应该看到如下内容:
docker-registry start/running, process 25303
您可以通过执行以下命令来验证服务器是否正在运行:
docker ps
输出应类似于以下内容(注意名称均以 dockerregistry_
开头
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d4b6fef0b4d1 nginx:1.9 "nginx -g 'daemon of 2 minutes ago Up 2 minutes 80/tcp, 0.0.0.0:443->443/tcp dockerregistry_nginx_1 77668352bd39 registry:2 "registry cmd/regist 2 minutes ago Up 2 minutes 127.0.0.1:5000->5000/tcp dockerregistry_registry_1
Upstart 会将 docker-compose
命令的输出记录到 /var/log/upstart/docker-registry.log
。 对于我们的最终测试,让我们用 tail
来“实时观察”日志文件(sudo
是必要的,因为 upstart 日志是以 root 用户身份写入的):
sudo tail -f /var/log/upstart/docker-registry.log
您应该看到通常的 registry_
输出。 从另一个终端或机器继续运行我们现在熟悉的 curl
测试:
curl https://<YOUR_USERNAME>:<YOURPASSWORD>@[YOUR-DOMAIN]/v2/
如果一切正常,curl 将在终端打印 {}
,您应该会看到通常的情况:
registry_1 | time="2015-08-12T08:01:12.241887501Z" level=debug msg="authorizing request" environment=development http.request.host=docker.meatflavoredbeer.com http.request.id=e8d69e16-9448-4c48-afd8-57b1f1302742 http.request.method=GET http.request.remoteaddr=106.1.247.4 http.request.uri="/v2/" http.request.useragent="curl/7.37.1" instance.id=14d4727b-fda1-463f-8d0e-181f4c70cb17 service=registry version=v2.0.1 registry_1 | time="2015-08-12T08:01:12.242206499Z" level=info msg="response completed" environment=development http.request.host=docker.meatflavoredbeer.com http.request.id=e8d69e16-9448-4c48-afd8-57b1f1302742 http.request.method=GET http.request.remoteaddr=106.1.247.4 http.request.uri="/v2/" http.request.useragent="curl/7.37.1" http.response.contenttype="application/json; charset=utf-8" http.response.duration=3.359883ms http.response.status=200 http.response.written=2 instance.id=14d4727b-fda1-463f-8d0e-181f4c70cb17 service=registry version=v2.0.1 registry_1 | 172.17.0.4 - - [12/Aug/2015:08:01:12 +0000] "GET /v2/ HTTP/1.0" 200 2 "" "curl/7.37.1" nginx_1 | 106.1.247.4 - nik [12/Aug/2015:08:01:12 +0000] "GET /v2/ HTTP/1.1" 200 2 "-" "curl/7.37.1" "-"
第 9 步 — 从客户端计算机访问您的 Docker 注册表
要从另一台机器访问您的 Docker 注册表,首先将您之前创建的 SSL 证书添加到新的客户端机器。 您想要的文件位于 ~/docker-registry/nginx/devdockerCA.crt
。
您可以直接将其复制到新机器或使用以下说明复制并粘贴它:
在注册服务器上,查看证书:
sudo cat /docker-registry/nginx/devdockerCA.crt
您将获得如下所示的输出:
Output of sudo cat /docker-registry/nginx/devdockerCA.crt-----BEGIN CERTIFICATE----- MIIDXTCCAkWgAwIBAgIJANiXy7fHSPrmMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQwHhcNMTQwOTIxMDYwODE2WhcNNDIwMjA2MDYwODE2WjBF MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEAuK4kNFaY3k/0RdKRK1XLj9+IrpR7WW5lrNaFB0OIiItHV9FjyuSWK2mj ObR1IWJNrVSqWvfZ/CLGay6Lp9DJvBbpT68dhuS5xbVw3bs3ghB24TntDYhHMAc8 GWor/ZQTzjccHUd1SJxt5mGXalNHUharkLd8mv4fAb7Mh/7AFP32W4X+scPE2bVH OJ1qH8ACo7pSVl1Ohcri6sMp01GoELyykpXu5azhuCnfXLRyuOvQb7llV5WyKhq+ SjcE3c2C+hCCC5g6IzRcMEg336Ktn5su+kK6c0hoD0PR/W0PtwgH4XlNdpVFqMST vthEG+Hv6xVGGH+nTszN7F9ugVMxewIDAQABo1AwTjAdBgNVHQ4EFgQULek+WVyK dJk3JIHoI4iVi0FPtdwwHwYDVR0jBBgwFoAULek+WVyKdJk3JIHoI4iVi0FPtdww DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAkignESZcgr4dBmVZqDwh YsrKeWSkj+5p9eW5hCHJ5Eg2X8oGTgItuLaLfyFWPS3MYWWMzggxgKMOQM+9o3+k oH5sUmraNzI3TmAtkqd/8isXzBUV661BbSV0obAgF/ul5v3Tl5uBbCXObC+NUikM O0C3fDmmeK799AM/hP5CTDehNaFXABGoVRMSlGYe8hZqap/Jm6AaKThV4g6n4F7M u5wYtI9YDMsxeVW6OP9ZfvpGZW/n/88MSFjMlBjFfFsorfRd6P5WADhdfA6CBECG LP83r7/MhqO06EOpsv4n2CJ3yoyqIr1L1+6C7Erl2em/jfOb/24y63dj/ATytt2H 6g== -----END CERTIFICATE-----
将该输出复制到剪贴板,然后连接到您的客户端计算机。
在 客户端机器 上,创建证书目录:
sudo mkdir /usr/local/share/ca-certificates/docker-dev-cert
打开证书文件进行编辑:
sudo nano /usr/local/share/ca-certificates/docker-dev-cert/devdockerCA.crt
粘贴证书内容。
通过查看文件来验证文件是否正确保存到客户端计算机:
cat /usr/local/share/ca-certificates/docker-dev-cert/devdockerCA.crt
如果一切正常,您将看到之前的相同文本:
Output of cat /usr/local/share/ca-certificates/docker-dev-cert/devdockerCA.crt-----BEGIN CERTIFICATE----- MIIDXTCCAkWgAwIBAgIJANiXy7fHSPrmMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV ... ... LP83r7/MhqO06EOpsv4n2CJ3yoyqIr1L1+6C7Erl2em/jfOb/24y63dj/ATytt2H 6g== -----END CERTIFICATE-----
现在更新证书:
sudo update-ca-certificates
您应该得到如下所示的输出(注意 1 added
):
Output of sudo update-ca-certificatesUpdating certificates in /etc/ssl/certs... 1 added, 0 removed; done. Running hooks in /etc/ca-certificates/update.d....done.
如果您尚未在客户端上安装 Docker,请立即安装(请参阅先决条件部分)。
重新启动 Docker 以确保它重新加载系统的 CA 证书。
sudo service docker restart
您现在应该能够从客户端机器登录到您的 Docker 注册表:
docker login https://YOUR-DOMAIN
请注意,您使用的是 https://。 输入您之前设置的用户名和密码(如果出现提示,请输入您想要的电子邮件)。
Output of docker loginUsername: USERNAME Password: PASSWORD Email: Account created. Please see the documentation of the registry http://localhost:5000/v1/ for instructions how to activate it.
您应该看到以下消息:
Output of docker loginLogin Succeeded
此时,您的 Docker 注册表已启动并正在运行! 让我们制作一个测试图像以推送到注册表。
第 10 步 — 发布到您的私有 Docker 注册表
您现在已准备好将映像发布到您的私有 Docker 注册表,但首先我们必须创建一个映像。 我们将基于来自 Docker Hub 的 ubuntu
映像创建一个简单的映像。
从您的 客户端计算机 创建一个小的空图像以推送到我们的新注册表。
docker run -t -i ubuntu /bin/bash
下载完成后,您将进入 Docker 提示符。 让我们通过创建一个名为 SUCCESS
的文件来快速更改文件系统:
touch /SUCCESS
退出 Docker 容器:
exit
提交更改:
docker commit $(docker ps -lq) test-image
此命令基于已运行的映像以及您所做的任何更改创建一个名为 test-image
的新映像。 在我们的例子中,添加的 /SUCCESS
文件包含在新图像中。
这个镜像现在只存在于本地,所以让我们将它推送到我们创建的新注册表中。
在上一步中,您登录了您的私有 Docker 注册表。 如果你还没有登录,让我们再次登录(注意你要使用https://):
docker login https://YOUR-DOMAIN
输入您之前设置的用户名和密码:
Username: USERNAME Password: PASSWORD Email: Account created. Please see the documentation of the registry http://localhost:5000/v1/ for instructions how to activate it.
Docker 有一个不寻常的机制来指定要推送到哪个注册表。 您必须使用私有注册表的位置标记图像才能推送到它。 让我们将图像标记到我们的私有注册表中:
docker tag test-image [YOUR-DOMAIN]/test-image
请注意,您首先使用图像的本地名称,然后是要添加到其中的标签。 标签确实 not 使用 https://
,只是域、端口和图像名称。
现在我们可以将该图像推送到我们的注册表。 这次我们只使用标签名称:
docker push [YOUR-DOMAIN]/test-image
这需要一点时间才能上传到注册表服务器。 您应该会看到以类似以下内容结尾的输出:
Output of docker pushlatest: digest: sha256:5ea1cfb425544011a3198757f9c6b283fa209a928caabe56063f85f3402363b4 size: 8008
第 11 步 — 从 Docker 注册表中提取
为了确保一切正常,让我们回到我们的原始服务器(您安装 Docker 注册表的地方)并拉取我们刚刚从客户端推送的映像。 您也可以从第三台服务器对此进行测试。
如果您的测试拉取服务器上未安装 Docker,请返回并按照步骤 6 中的安装说明(如果是第三台服务器,则为 SSL 说明)。
使用您之前设置的用户名和密码登录。
docker login https://[YOUR-DOMAIN]
现在拉图像。 您只需要“标签”镜像名称,其中包括域名、端口和镜像名称(但不包括 https://
):
docker pull [YOUR-DOMAIN]/test-image
Docker 将进行一些下载并将您返回到提示符。 如果你在新机器上运行镜像,你会看到我们之前创建的 SUCCESS 文件在那里:
docker run -t -i [YOUR-DOMAIN]/test-image /bin/bash
列出 bash shell 中的文件:
ls
您应该会看到我们之前为此图像创建的 SUCCESS
文件:
SUCCESS bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
结论
恭喜! 您刚刚使用自己的私有 Docker 注册表来推送和拉取您的第一个 Docker 容器!
快乐 Dockering!