部署注册表服务器

预计阅读时间:18分钟

该页面包含有关使用开源Docker Registry托管自己的注册表的信息。有关Docker Hub的信息,它提供了托管注册表以及其他功能,例如团队,组织,Web挂钩,自动构建等,请参阅Docker Hub

在部署注册表之前,需要在主机上安装Docker。注册表是registry映像的一个实例,在Docker中运行。

本主题提供有关部署和配置注册表的基本信息。有关配置选项的详尽列表,请参阅《 配置参考》

如果您有空白的数据中心,请参阅 空白注册表的注意事项

运行本地注册表

使用如下命令启动注册表容器:

$ docker run -d -p 5000:5000 --restart=always --name registry registry:2

注册表现在可以使用了。

警告:前几个示例显示了仅适用于测试的注册表配置。生产就绪型注册表必须受TLS保护,并且理想情况下应使用访问控制机制。继续阅读,然后继续阅读配置指南,以部署可用于生产环境的注册表。

将映像从Docker Hub复制到注册表

您可以从Docker Hub提取映像并将其推送到注册表。以下示例ubuntu:16.04从Docker Hub提取映像并将其重新标记为my-ubuntu,然后将其推送到本地注册表。最后, ubuntu:16.04my-ubuntu图像在本地删除,并且该 my-ubuntu图像从本地注册表中拉出。

  1. ubuntu:16.04离泊坞枢纽的形象。

    $ docker pull ubuntu:16.04
    
  2. 将图像标记为localhost:5000/my-ubuntu。这将为现有图像创建一个附加标签。当标签的第一部分是主机名和端口时,Docker在推送时会将其解释为注册表的位置。

    $ docker tag ubuntu:16.04 localhost:5000/my-ubuntu
    
  3. 将映像推送到在以下位置运行的本地注册表localhost:5000

    $ docker push localhost:5000/my-ubuntu
    
  4. 删除本地缓存ubuntu:16.04localhost:5000/my-ubuntu 图像,让您可以测试从注册表拉动图像。这不会localhost:5000/my-ubuntu从注册表中删除该图像。

    $ docker image remove ubuntu:16.04
    $ docker image remove localhost:5000/my-ubuntu
    
  5. localhost:5000/my-ubuntu从本地注册表中的形象。

    $ docker pull localhost:5000/my-ubuntu
    

停止本地注册表

要停止注册表,请使用docker container stop与其他任何容器相同的命令。

$ docker container stop registry

要卸下容器,请使用docker container rm

$ docker container stop registry && docker container rm -v registry

基本配置

要配置容器,可以将其他或修改的选项传递给 docker run命令。

以下各节提供了配置注册表的基本准则。有关更多详细信息,请参见注册表配置参考

自动启动注册表

如果要将注册表用作永久基础结构的一部分,则应将其设置为在Docker重新启动或退出时自动重新启动。本示例使用该--restart always标志设置注册表的重新启动策略。

$ docker run -d \
  -p 5000:5000 \
  --restart=always \
  --name registry \
  registry:2

自定义发布的端口

如果您已经在使用端口5000,或者要运行多个本地注册表来分离关注的区域,则可以自定义注册表的端口设置。本示例在端口5001上运行注册表,并对其进行命名 registry-test。请记住,该-p值的第一部分是主机端口,第二部分是容器内的端口。在容器内,注册表5000默认情况下在端口上侦听。

$ docker run -d \
  -p 5001:5000 \
  --name registry-test \
  registry:2

如果要更改注册表在容器内侦听的端口,则可以使用环境变量REGISTRY_HTTP_ADDR进行更改。此命令使注册表在容器内的端口5001上进行侦听:

$ docker run -d \
  -e REGISTRY_HTTP_ADDR=0.0.0.0:5001 \
  -p 5001:5001 \
  --name registry-test \
  registry:2

存储定制

自定义存储位置

默认情况下,您的注册表数据将作为docker卷保留 在主机文件系统上。如果要将注册表内容存储在主机文件系统上的特定位置,例如在特定目录中安装了SSD或SAN,则可以决定使用绑定安装。绑定挂载更依赖Docker主机的文件系统布局,但在许多情况下性能更高。以下示例将主机目录绑定安装 /mnt/registry到注册表容器中的/var/lib/registry/

$ docker run -d \
  -p 5000:5000 \
  --restart=always \
  --name registry \
  -v /mnt/registry:/var/lib/registry \
  registry:2

自定义存储后端

默认情况下,无论您使用绑定安装还是卷,注册表都将其数据存储在本地文件系统上。您可以使用存储驱动程序将注册表数据存储在Amazon S3存储桶,Google Cloud Platform或另一个存储后端中 。有关更多信息,请参阅 存储配置选项

运行外部可访问的注册表

运行仅可访问的注册表localhost具有有限的用途。为了使您的注册表可供外部主机访问,必须首先使用TLS保护注册表。

下面的“将注册表作为服务运行”中扩展了此示例。

取得证书

这些示例假定以下内容:

  • 您的注册表URL是https://myregistry.domain.com/
  • 您的DNS,路由和防火墙设置允许在端口443上访问注册表的主机。
  • 您已经从证书颁发机构(CA)获得证书。

如果您已获得了中间证书,请参阅 使用中间证书

  1. 创建一个certs目录。

    $ mkdir -p certs
    

    .crt.key文件从CA复制到certs目录中。以下步骤假定文件名为domain.crtdomain.key

  2. 如果注册表当前正在运行,请停止它。

    $ docker container stop registry
    
  3. 重新启动注册表,将其定向为使用TLS证书。此命令将certs/目录绑定安装到容器中的/certs/,并设置环境变量,该变量告诉容器在何处找到domain.crt anddomain.key文件。注册表在端口443(默认的HTTPS端口)上运行。

    $ 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
    
  4. Docker客户端现在可以使用其外部地址从注册表中拉入并推送到您的注册表。以下命令演示了这一点:

    $ docker pull ubuntu:16.04
    $ docker tag ubuntu:16.04 myregistry.domain.com/my-ubuntu
    $ docker push myregistry.domain.com/my-ubuntu
    $ docker pull myregistry.domain.com/my-ubuntu
    

使用中间证书

证书发行者可能会向您提供中间证书。在这种情况下,您必须将证书与中间证书连接起来以形成证书捆绑包。您可以使用以下cat命令执行此操作:

cat domain.crt intermediate-certificates.pem > certs/domain.crt

您可以像domain.crt在上一个示例中使用文件一样使用证书捆绑包。

支持“让我们加密”

注册表支持使用“让我们加密”来自动获取浏览器信任的证书。有关“让我们加密”的更多信息,请参见 https://letsencrypt.org/how-it-works/ 以及注册表配置的相关部分 。

使用不安全的注册表(仅测试)

可以使用自签名证书,也可以不安全地使用我们的注册表。除非您为自签名证书设置了验证,否则这仅用于测试。请参阅运行不安全的注册表

将注册表作为服务运行

与独立容器相比,群集服务具有多个优点。他们使用声明性模型,这意味着您定义了所需的状态,而Docker则将服务保持在该状态。服务提供了自动负载平衡扩展,并具有控制服务分配的能力以及其他优势。服务还允许您秘密存储敏感数据(例如TLS证书) 。

您使用的存储后端确定是使用完全扩展服务还是仅具有单个节点或节点约束的服务。

  • 如果您使用分布式存储驱动程序(例如Amazon S3),则可以使用完全复制的服务。每个工作程序都可以写入存储后端,而不会引起写入冲突。

  • 如果使用本地绑定安装或卷,则每个工作节点都将写入其自己的存储位置,这意味着每个注册表都包含一个不同的数据集。您可以通过使用单副本服务和节点约束来确保仅单个工作线程正在写入绑定装载来解决此问题。

以下示例将注册表作为单副本服务启动,可以在端口80上的任何群集节点上访问该注册表。它假定您使用的是与前面示例相同的TLS证书。

首先,将TLS证书和密钥另存为机密:

$ docker secret create domain.crt certs/domain.crt

$ docker secret create domain.key certs/domain.key

接下来,将标签添加到要运行注册表的节点。要获取节点的名称,请使用docker node ls。在node1下面替换您节点的名称 。

$ docker node update --label-add registry=true node1

接下来,创建服务,向其授予对两个秘密的访问权限,并将其约束为仅在带有标签的节点上运行registry=true。除了约束之外,您还指定一次只能运行一个副本。该示例将/mnt/registryswarm节点上的绑定安装到/var/lib/registry/ 容器内。绑定挂载依赖于预先存在的源目录,因此请确保/mnt/registry存在node1。您可能需要在运行以下docker service create命令之前创建它。

默认情况下,机密会在服务中挂载机密/run/secrets/<secret-name>

$ docker service create \
  --name registry \
  --secret domain.crt \
  --secret domain.key \
  --constraint 'node.labels.registry==true' \
  --mount type=bind,src=/mnt/registry,dst=/var/lib/registry \
  -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/run/secrets/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/run/secrets/domain.key \
  --publish published=443,target=443 \
  --replicas 1 \
  registry:2

您可以在任何群集节点的端口443上访问该服务。Docker将请求发送到运行该服务的节点。

负载平衡注意事项

可能需要使用负载平衡器来分配负载,终止TLS或提供高可用性。尽管完整的负载平衡设置不在本文档的讨论范围内,但是有一些注意事项可以使过程更平滑。

最重要的方面是注册表的负载平衡群集必须共享相同的资源。对于当前版本的注册表,这意味着以下内容必须相同:

  • 储存驱动程式
  • HTTP机密
  • Redis缓存(如果已配置)

以上任何方面的差异都会导致在处理请求时出现问题。例如,如果您使用文件系统驱动程序,则所有注册表实例必须有权访问同一台计算机上的同一文件系统根目录。对于其他驱动程序,例如S3或Azure,它们应该访问相同的资源并共享相同的配置。该HTTP秘密坐标上传,所以也必须是跨实例相同。配置不同的redis实例是可行的(在编写本文时),但如果实例不共享,则不是最佳选择,因为更多的请求将定向到后端。

重要/必需的HTTP标头

正确设置标题非常重要。对于“ / v2 /” URL空间下对任何请求的所有响应Docker-Distribution-API-Version ,即使对于4xx响应,标头也应设置为值“ registry / 2.0”。此标头允许docker引擎快速解析身份验证领域,并在必要时回退到版本1注册中心。确认此设置正确无误,可以帮助避免后备问题。

在思想的火车一样,你必须确保你正确发送 X-Forwarded-ProtoX-Forwarded-ForHost头到他们的“客户端”的价值观。否则,通常会使注册表问题重定向到内部主机名或从https降级为http。

当击中“ / v2 /”终结点而没有凭据时,正确保护的注册表应返回401。响应应包括一个WWW-Authenticate 挑战,提供有关如何进行身份验证的指导,例如使用基本身份验证或令牌服务。如果负载平衡器具有运行状况检查,建议对其进行配置,以将401响应视为正常,将其他响应视为不正常。通过确保身份验证的配置问题不会意外暴露不受保护的注册表,这可以保护您的注册表。如果您使用的是不太复杂的负载均衡器(例如Amazon的Elastic Load Balancer),该负载均衡器不允许更改正常的响应代码,则可以将运行状况检查指向“ /”,该代码始终返回200 OK响应。

限制访问

除了在安全本地网络上运行的注册表外,注册表应始终实施访问限制。

本机基本身份验证

实现访问限制的最简单方法是通过基本身份验证(这与其他Web服务器的基本身份验证机制非常相似)。本示例使用本机基本身份验证htpasswd来存储机密。

警告不能将身份验证与以纯文本形式发送凭据的身份验证方案一起使用。您必须先 配置TLS,身份验证才能正常工作。

  1. 创建一个密码文件,为用户提供一个条目testuser,密码为 testpassword

    $ mkdir auth
    $ docker run \
      --entrypoint htpasswd \
      httpd:2 -Bbn testuser testpassword > auth/htpasswd
    

    在Windows上,确保输出文件已正确编码:

    docker run --rm --entrypoint htpasswd httpd:2 -Bbn testuser testpassword | Set-Content -Encoding ASCII auth/htpasswd
    
  2. 停止注册表。

    $ docker container stop registry
    
  3. 使用基本身份验证启动注册表。

    $ 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
    
  4. 尝试从注册表中提取图像,或将图像推入注册表。这些命令失败。

  5. 登录到注册表。

    $ docker login myregistrydomain.com:5000
    

    从第一步提供用户名和密码。

    测试您现在可以从注册表中提取图像或将图像推送到注册表中。

X509错误:X509错误通常表明您在尝试尝试使用自签名证书而未正确配置Docker守护程序。请参阅运行不安全的注册表

更高级的身份验证

您可能想通过在注册表前面使用代理来利用更高级的基本身份验证实现。请参阅食谱列表

注册表还支持委托身份验证,该身份验证将用户重定向到特定的受信任令牌服务器。这种方法的设置更为复杂,只有在您需要完全配置ACL并需要更好地控制注册表到全局授权和身份验证系统中的集成时,这种方法才有意义。请在此处参考以下背景信息配置信息

这种方法要求您实施自己的身份验证系统或利用第三方实施。

使用Compose文件部署注册表

如果您的注册表调用是高级的,则使用Docker compose文件进行部署可能会更容易,而不是依赖于特定的docker run 调用。使用以下示例docker-compose.yml作为模板。

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

替换/path为包含certs/auth/ 目录的目录。

通过在包含docker-compose.yml文件的目录中发出以下命令来启动注册表:

$ docker-compose up -d

空缺注册表的注意事项

您可以在没有Internet连接的环境中运行注册表。但是,如果您依赖于任何非本地图像,则需要考虑以下因素:

  • 您可能需要在连接的主机上构建本地注册表的数据量,可以在该主机上运行docker pull以获取远程可用的任何映像,然后将注册表的数据量迁移到空缺的网络。

  • 某些映像(例如,正式的Microsoft Windows基本映像)不可分发。这意味着,当您将基于这些映像之一的映像推送到您的私有注册表中时,非可分发层不会 被推送,而是始终从其授权位置获取。这对于连接到互联网的主机来说很好,但是在没有空隙的设置中则不行。

    您可以配置Docker守护程序以允许将不可分发的层推送到私有注册表。 这仅在存在不可分发图像的气隙设置中或在带宽非常有限的情况下才有用。 您有责任确保您遵守不可分发层的使用条款。

    1. 编辑daemon.json文件,该文件位于/etc/docker/Linux主机和C:\ProgramData\docker\config\daemon.jsonWindows Server上。假设文件先前为空,请添加以下内容:

      {
        "allow-nondistributable-artifacts": ["myregistrydomain.com:5000"]
      }
      

      该值是注册表地址的数组,用逗号分隔。

      保存并退出文件。

    2. 重新启动Docker。

    3. 如果它不会自动启动,请重新启动注册表。

    4. 当您将映像推送到列表中的注册表时,它们的不可分发层也将推送到注册表中。

      警告:不可分配的工件通常在如何以及在何处可以分配和共享有限制。仅使用此功能将工件推送到私有注册表,并确保您遵守涵盖重新分发不可分发工件的所有条款。

下一步

以下各节提供了更具体和高级的信息:

注册表的预置型图像标签仓库配送部署