Docker仓库

2022/03/05 Docker

Docker仓库

仓库(Repository)用来集中存储Docker镜像,支持镜像分发和更新。

什么是仓库

仓库(Repository)是集中存放镜像的地方,又分公共仓库和私有仓库。仓库用来集中存储Docker镜像,支持镜像分发和更新。目前世界上最大最知名的公共仓库是Docker官方发布的Docker Hub,上面有超过15000个镜像。国内比较知名的Docker仓库社区有Docker Pool、阿里云等。

为满足容灾需求,Docker仓库后端通常采用分布式存储。比较常用的分布式存储平台包括亚马逊S3、微软Azure和华为UDS等。

有时候容易把仓库与注册服务器(Registry)混淆。实际上注册服务器是存放仓库的具体服务器,一个注册服务器上可以有多个仓库,而每个仓库下面可以有多个镜像。从这方面来说,仓库可以被认为是一个具体的项目或目录。例如对于仓库地址private-docker.com/ubuntu来说,private-docker.com是注册服务器地址,ubuntu是仓库名。

仓库的组成

人们习惯称呼一个镜像集群社区(Hub)为仓库,如上面提到的Docker Hub、Docker Pool等,事实上在这类社区里面,又包含着很多具有特定功能的仓库(repository),这些仓库由一个或多个名称相同而版本不同的镜像组成。如Docker Hub上的Ubuntu、Redis都是具有具体功能的仓库,而且它们包含着不同的镜像版本。

仓库的名字通常由两部分组成,中间以斜线分开。斜线之前是用户名,斜线之后是镜像名。如tom/ubuntu表示属于用户tom的Ubuntu镜像。这也是社区上区分公有仓库和私有仓库的方法。

简单来说,仓库包括镜像存储系统和账户管理系统。前者通过Docker仓库注册服务(registry)实现;而一个有实用价值的仓库(如Docker Hub)都会有完善的账户管理系统。

使用者注册成为用户之后,可以通过login命令登录到仓库服务,登录方法如下:

$ docker login -u <user name> -p <password> -e <email> <registry domain>

为安全起见,对于以上命令行输入,请在login命令行回车后输入用户名、密码和邮箱。回车之后,注册的用户将会得到成功登录的提示:

$ Login Succeeded

通过命令行执行docker login命令来输入用户名、密码和邮箱来完成注册和登录。注册成功后,本地用户目录下会自动创建.docker/config.json文件,保存用户的认证信息。登录成功的用户可以上传个人制作的镜像到Docker Hub。

仓库镜像

仓库下面包含着一组镜像,镜像之间用标签(tag)区分。一个完整的镜像路径通常由服务器地址、仓库名称和标签组成,如:

registry.hub.docker.com/official/ubuntu:14.04

它代表Docker Hub上的Ubuntu官方镜像,发行版本14.04。通过Docker命令,可以从本地对仓库里的镜像进行上传、下载和查询等操作。

根据是否为官方提供,可将这些镜像资源分为两类:

  • 一种是类似于centos这样的基础镜像,也称为根镜像。这些镜像是由Docker公司创建、验证、支持、提供,这样的镜像往往使用单个单词作为名字;
  • 另一种类型的镜像,比如ansible/centos7-ansible镜像,是由Docker用户ansible创建并维护的,带有用户名称为前缀,表明是某用户下的某仓库。可以通过用户名称前缀“user_name/镜像名”来指定使用某个用户提供的镜像。

上传镜像

可通过push命令上传镜像,如下。

$ docker push localhost:5000/official/ubuntu:14.04

上面的示例中是向本地私有仓库上传镜像。如果不写服务器地址,则默认上传到官方Docker Hub(https://hub.docker.com)。需要指出的是,对于有账户管理系统的Docker服务器如Docker Hub,上传镜像之前需要先登录。而一般用户上传镜像的目标是自己的仓库。

下载镜像

通过pull命令可下载镜像,如下。

$ docker pull ubuntu:14.04

上面的示例同样会在省略地址的情况下,从Docker Hub上下载镜像。这里Ubuntu前面没有带用户名,表示下载的是Docker官方镜像。如果下载对象不带标签,则会把Ubuntu仓库下的官方(默认tag)镜像全部下载。

从上传/下载的过程信息中可以看到,一个镜像在操作过程中会被逐层上传/下载。不同镜像层的上传和下载操作能并发进行。因此,后一个下载动作不需要等到上一个动作的完成。

查询镜像

下面通过search命令实现查询操作(仓库开发者须实现查询功能)。

$ docker search localhost:5000/ubuntu

输入查询名称后,返回的结果包含了仓库中Ubuntu公有镜像。下载和查询不需要登录远程服务器。

再看Docker Hub

Docker Hub是目前世界上最大最知名的Docker镜像仓库,由Docker公司官方发布。Docker Hub的优点总结如下:

  • 为开发者提供了海量Docker镜像,供免费下载学习和使用;
  • 拥有完善的账户管理系统,为用户提供付费扩容;
  • 服务器采用分布式部署,支持负载均衡;
  • 支持镜像上传、下载、查询、删除及属性设置等多种操作;
  • 支持在线编译镜像;
  • 后端采用分布式存储,可容灾备份;
  • 其核心是Docker distribution,在开源社区上设计维护,会不断更新和完善;
  • 提供企业版Docker Hub,为企业级用户提供一站式解决方案。 事实上Docker Hub属开源社区性质,为Docker开发者服务,在发布形式和服务功能上很大程度模仿了Github。Github托管代码,而Docker Hub托管镜像。这就使有过开源社区经验的开发者觉得Docker Hub熟悉且方便。

网页分布

Docker Hub主页上展示了最活跃的官方仓库,如Ubuntu、Redis、MySQL等。 开发者通常以这些仓库镜像作为基础镜像开发自己的镜像。如Ubuntu提供了操作系统发行版,Redis提供常用的缓存功能,MySQL是流行的数据库,等等。

开发者可以在Docker Hub上搜索指定仓库。进入仓库页面即可了解到仓库的具体信息。在图4-3所示的Ubuntu仓库里,我们可以看到显示信息包括:

  • 镜像标签列表
  • 仓库说明文档
  • 仓库活跃度
  • 所属组织
  • 仓库下载帮助

开发者也可以搜索某个仓库组织下的所有仓库列表。如Docker官方发布维护的library,包含了大部分Docker官方镜像。 点击链接:https://registry.hub.docker.com/repos/library/,进入即可看到仓库列表。

搭建本地私有仓库

使用registry镜像创建私有仓库

安装Docker后,可以通过官方提供的registry镜像来简单搭建一套本地私有仓库环境:

$ docker run -d -p 5000:5000 registry:2

这将自动下载并启动一个registry容器,创建本地的私有仓库服务。

默认情况下,仓库会被创建在容器的/var/lib/registry目录下。可以通过-v参数来将镜像文件存放在本地的指定路径。 例如下面的例子将上传的镜像放到/opt/data/registry目录:

$ docker run -d -p 5000:5000 -v /opt/data/registry:/var/lib/registry registry:2

此时,在本地将启动一个私有仓库服务,监听端口为5000。

比较新的Docker版本对安全性要求较高,会要求仓库支持SSL/TLS证书。对于内部使用的私有仓库,可以自行配置证书或关闭对仓库的安全性检查。

首先,修改Docker daemon的启动参数,添加如下参数,表示信任这个私有仓库,不进行安全证书检查:

DOCKER_OPTS="--insecure-registry 10.0.2.2:5000"

账户管理系统

开发者可以免费在Docker Hub上注册,或者用Github的账户直接登录Docker Hub。

账户系统提供的功能类似Github。大致包括:

  • 用户可以编辑自己的注册信息,如密码、邮箱等;
  • 创建和删除用户下的镜像;
  • 收费用户可以创建和设置私有镜像;
  • 创建和维护组织,添加组员;
  • 用户之间可以相互关注。

另外,Docker Hub为用户提供了在线编译镜像的功能,这为本地没有安装Docker daemon环境的开发者提供了方便。在线编译使得用户端主机可以不具备Docker Engine运行环境。用户只需提供可以编译的程序源码即可,Docker Hub会负责完成编译。

仓库服务

Docker仓库和仓库注册服务经常被混为一谈。事实上服务是用来搭建仓库的,通常运行在容器里面。

Docker Registry是构建仓库的核心,用于实现开源Docker镜像的分发。 Docker Registry源码发布在:https://github.com/docker/distribution。Docker先后设计并发布了两个Registry开源项目,这里只介绍2.0版本Registry。

Registry功能和架构

从使用来讲,Registry旨在实现镜像的创建、存储、分发和更新等功能。

  • 镜像存储:镜像数据存储在Registry后端,与本地镜像存储方式类似,它也分隔成了多个镜像层,放置在各自的目录中,保存成tar包格式。除了镜像数据,Registry还保存了清单文件(manifest)和镜像签名文件(signature)等。
  • 镜像创建、分发和更新:本地用户和Registry之间通过Registry API传输镜像。Registry API即一系列HTTP/HTTPS请求,用来发送用户请求到Registry,并接收Registry的响应。在请求和响应中则包含了镜像数据交互。

从设计角度看,Registry有如下特点:

  • 快速上传和下载镜像;
  • 设计方案新颖且高性能;
  • 部署方便;
  • 有详细完整的Registry API(V2)说明文档;
  • 后端支持多种分布式云存储方案(如s3、azure)和本地文件系统等,接口以插件方式存在,易于配置;
  • 清单文件(Manifest)作为元数据完整地记录镜像信息;
  • 以Webhook方式实现的通知系统;
  • 实现了本地TLS,支持HTTPS安全访问;
  • 有可配置的认证模块;
  • 有健康检查模块;
  • 正在努力让用于管理镜像的清单和摘要文件格式更加清晰,以及更清楚地为镜像打标签;
  • 拥有完善镜像缓存机制,镜像下载更加快捷。

Registry API

API描述

Registry API遵循REST设计标准,用于Registry和Docker Engine之间的通信,实现Registry镜像分发,是Docker Registry的重要组成部分。

Registry API语句由方法(Method)、路径(Path)和实体(Entity)组成。它负责接收engine的访问请求,实现对Registry后端实体的操作,写入或获取数据及状态。

API传输的对象主要包括镜像layer的块数据(blob)和表单(Manifest)。

表单(Manifest)

Manifest是JSON格式的文件,记录镜像的元数据信息,并兼容V1版本镜像信息。Manifest的内容形式如下:

{
    "name": <name>,
    "tag": <tag>,
    "fsLayers": [
        {
            "blobSum": <tarsum>
        },
        ...
    ],
    "history": [...],
    "signatures": [...]
}

可以看到,Manifest里面包含了一段签名信息,用关键字signatures表示,所指的是一个签名镜像的表单;用户凭签名信息校验Manifest数据。给Manifest签名有两种方式:一是用Docker官方提供的libtrust函数库生成私钥,二是用工具链生成X509证书。签名信息的内容形式如下:

"signatures": [
    {
        "header": {
            "jwk": {
                "crv": "P-256",
                "kid": "OD6I:6DRK:JXEJ:KBM4:255X:NSAA:MUSF:E4VM:ZI6W:CUN2:L4Z6:LSF4",
                "kty": "EC",
                "x": "3gAwX48IQ5oaYQAYSxor6rYYc_6yjuLCjtQ9LUakg4A",
                "y": "t72ge6kIA1XOjqjVoEOiPPAURltJFBMGDSQvEGVB010"
            },
            "alg": "ES256"
        },
        "signature": "XREm0L8WNn27Ga_iE_vRnTxVMhhYY0Zst_FfkKopg6gWSoTOZTuW4rK0fg_IqnKkEKlbD83tD46LKEGi5aIVFg",
        "protected": "eyJmb3JtYXRMZW5ndGgiOjY2MjgsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAxNS0wNC0wOFQxODo1Mjo1OVoifQ"
    }
]

内容摘要(Content Digest)

Registry API采用内容寻址存储(CAS)方法,针对固定内容存储。数据单元的唯一ID与元数据一起构成所访问数据的实际有效地址,从而确保内容的可靠性。

Content Digest用于内容寻址,是依据哈希算法生成的一串校验码,是数据内容的唯一标识。为了通信的安全,digest具有不透明性。digest在Registry API的作用过程如下:

  • 用户通过Registry API请求获取数据;
  • Registry返回数据的同时,在响应头信息里面返回digest;
  • 用户收到digest,然后用它校验获取数据的完整性和一致性。 一条digest由算法名称和一段十六进制数据(把哈希校验码通过十六进制进行编码)组成,其语法表示如下:
    digest:= algorithm ":" hex algorithm:= /[A-Fa-f0-9_+.-]+/ hex:= /[A-Fa-f0-9]+/
    

    生成的digest内容形式如下:

    sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b
    

Registry API的使用

在使用Registry时,首先要检查Registry工作是否正常,命令如下:

$ curl -X GET http://localhost:5000/v2/
{}

上述命令可能返回如下状态:

  • 200 OK:Registry工作正常,并且用户可以访问操作。
  • 401 Unauthorized:Registry工作正常,但是用户没有访问权限,要想访问需要先做授权认证。
  • 404 Not Found:Registry并不存在。 然后,通过如下命令查找镜像:
    $ curl -X GET http://localhost:5000/v2/foo/bar/tags/list
    {"name":"foo/bar","tags":["2.0","1.0"]}
    

    在上面的命令中输入查询路径,将会返回JSON格式的结果,列出了仓库里面包含的所有镜像标签。 最后,通过如下命令获取Manifest。

    $ curl -X GET http://localhost:5000/v2/foo/bar/manifests/1.0
    {
      "name": "foo/bar",
      "tag": "1.0",
      "architecture": "amd64",
      "fsLayers": []
          <清单内容(...)>
    }
    

    在请求中输入指定仓库和标签,即可返回整个清单内容。

Registry API传输过程分析

用户对Registry的访问,包括镜像上传、下载、查询和删除等操作,都是通过向Registry发送一系列API请求来实现的。下面重点介绍Registry API在镜像上传和下载过程中的应用。

Search

    微信好友

    博士的沙漏

    Table of Contents