Docker系列(四):私有仓库(一)

简介

有些私有镜像不方便传输到公共镜像库中,或者内网环境,可以搭建一个私有Registry作为镜像仓库。

Docker Registry

Registry是用于存储和交付Docker镜像的系统。 和本地镜像管理中存储的镜像一样,可以使用不同的标签版本。

1. 使用docker-distribution启动Registry

可以直接安装docker组件distribution来启动Registry。

  • 安装

对于CentOS系统,直接使用yum安装即可:

$ yum install docker-distribution
  • 启动

通过systemctl启动Registry服务:

$ systemctl start docker-distribution
  • 配置说明

安装好后的Registry的配置文件可以正常启动应用,修改/etc/docker-distrbution/registry/config.yml文件来修改默认配置:

version: 0.1
log:
  fields:
    service: registry
storage:
    cache:
        layerinfo: inmemory
    filesystem:
        rootdirectory: /var/lib/registry  #镜像文件存储的目录
http:
    addr: :5000 #对外服务的端口

后面会继续说明一些用到的配置。

2. 使用已有的镜像启动Registry

官方文档都是通过下载已经配置好的Registry的镜像,通过容器启动此镜像,来提供Registry的服务。

  • 下载官方镜像

直接拉取官方镜像:

$ docker pull registry:2
2: Pulling from library/registry
49388a8c9c86: Pull complete 
e4d43608dd22: Pull complete 
3a41740f900c: Pull complete 
e16ef4b76684: Pull complete 
65f212f7c778: Pull complete 
Digest: sha256:d837de65fd9bdb81d74055f1dc9cc9154ad5d8d5328f42f57f273000c402c76d
Status: Downloaded newer image for registry:2
  • 通过镜像启动Registry服务

通过官方镜像启动容器:

$ docker run -d -p 5000:5000 --name registry registry:2
4199973ca1c7511fd608da534570375023fa69715948b13f4d9b3a3c277d3536

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
4199973ca1c7        registry:2          "/entrypoint.sh /e..."   5 seconds ago       Up 4 seconds        0.0.0.0:5000->5000/tcp   registry

其实启动容器时如果没有对应的镜像,会自动下载:

$ docker run -d -p 5000:5000 --name registry registry:2
Unable to find image 'registry:2' locally #提示在本地镜像库中没有找到镜像
2: Pulling from library/registry          #开始从公共镜像库下载镜像
49388a8c9c86: Pull complete 
e4d43608dd22: Pull complete 
3a41740f900c: Pull complete 
e16ef4b76684: Pull complete 
65f212f7c778: Pull complete 
Digest: sha256:d837de65fd9bdb81d74055f1dc9cc9154ad5d8d5328f42f57f273000c402c76d
Status: Downloaded newer image for registry:2  #镜像下载完成
6dabd62f77e8e2079bed029599aea96b65a551bcdb517a4adb24630828dea761 #启动容器

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
registry            2                   a07e3f32a779        5 days ago          33.3MB

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
6dabd62f77e8        registry:2          "/entrypoint.sh /e..."   5 minutes ago       Up 5 minutes        0.0.0.0:5000->5000/tcp   registry

使用容器启动Registry时注意将存储镜像的目录挂载到本地硬盘,如果存储镜像在容器中,容器销毁时,所有镜像也一起销毁:

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

3. 访问无验证的Registry

默认的docker client访问无认证的Registry可能会出现错误:

$ docker push 10.0.2.15:5000/registry:2
The push refers to a repository [10.0.2.15:5000/registry]
Get https://10.0.2.15:5000/v2/: http: server gave HTTP response to HTTPS client

docker client客户端默认需要访问https的地址,需要增加配置,重启docker:

$ echo "{\"insecure-registries\":[\"10.0.2.15:5000\"]}" >> /etc/docker/daemon.json
$ systemctl restart docker

重启后重新推送镜像:

$ docker push 10.0.2.15:5000/registry:2
The push refers to a repository [10.0.2.15:5000/registry]
3c133a51bc00: Pushed 
a2717186d7dd: Pushed 
656c7684d0bd: Pushed 
7683d4fcdf4e: Pushed 
ef763da74d91: Pushed 
2: digest: sha256:435db1be85c6c10b2f506516aa14d8c485c1f1bd5f4a941a637808b085f294b6 size: 1364

如果是通过二进制程序启动的docker,直接在启动时增加参数即可:

$ dockerd --insecure-registry 10.0.2.15:5000 & #如已启动docker,请先停止已启动的进程

4. 查看私有库中的镜像

Registry没有图形界面,只能通过命令访问接口:

$ curl http://10.0.2.15/v2/_catalog
{"repositories":["registry"]}

5. 其它

以上2种配置启动Registry的方法,对比来说第一种方法(直接安装docker-distribution包)更为方便简洁,后期也较好维护(官网只有第二种方法,根据情况选择适合自己的方法来启动Registry)。

安全的Registry

Docker官方是推荐采用带有认证的Registry,传输采用tls。

1. 制作自签署证书

制作证书前需要在openssl.cnf配置中加入证书绑定的域名(或主机名或IP)。否则推送镜像时会差生以下错误:

$ docker push 10.0.2.15:5000/registry:2
The push refers to a repository [10.0.2.15:5000/registry]
Get https://10.0.2.15:5000/v2/: x509: cannot validate certificate for 10.0.2.15 because it doesn't contain any IP SANs

修改配置文件,加入IP:

$ cp /etc/pki/tls/openssl.cnf /tmp/
$ vim /tmp/openssl.cnf
......
[v3_ca]
subjectAltName = IP: 10.0.2.15

修改后保存退出即可。继续使用openssl来制作自签署的证书,注意需要指定刚才修改的openssl.cnf配置文件:

$ mkdir /etc/docker-distribution/registry/certs
$ cd /etc/docker-distribution/registry/certs
$ openssl req -newkey rsa:2048 -nodes -sha256 -keyout domain.key -x509 -days 365 -out domain.crt -config /tmp/openssl.cnf
Generating a 2048 bit RSA private key
.............+++
...............................+++
writing new private key to 'domain.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:BJ
Locality Name (eg, city) [Default City]:BJ
Organization Name (eg, company) [Default Company Ltd]:ZL
Organizational Unit Name (eg, section) []:ZL
Common Name (eg, your name or your server's hostname) []:registry
Email Address []:

$ ls
domain.crt  domain.key

2. 修改配置文件

修改Registry的配置文件,添加tls证书:

$ vim /etc/docker-distribution/registry/config.yml
version: 0.1
log:
  fields:
    service: registry
storage:
    cache:
        layerinfo: inmemory
    filesystem:
        rootdirectory: /var/lib/registry
http:
    addr: :5000
    #增加以下tls的配置
    tls:
        certificate: /etc/docker-distribution/registry/certs/domain.crt
        key: /etc/docker-distribution/registry/certs/domain.key

重启Registry服务:

$ systemctl restart docker-distribution

3. 客户端证书配置

Registry服务配置好后,需要将证书配置到客户端,否则会产生以下错误:

$ docker push 10.0.2.15:5000/registry:2
The push refers to a repository [10.0.2.15:5000/registry]
Get https://10.0.2.15:5000/v2/: x509: cannot validate certificate for 10.0.2.15 because it doesn't contain any IP SANs

将证书配置到客户端,并重启客户端:

$ mkdir -p /etc/docker/certs.d/10.0.2.15:5000
# 注意,本文档测试时客户端与私有库是同一台服务器,以下这个证书应该是拷贝到真实的客户端服务器中。
$ cp /etc/docker-distribution/registry/certs/domain.crt /etc/docker/certs.d/10.0.2.15\:5000/ca.crt
$ systemctl restart docker

测试是否正常访问私有镜像库:

$ docker push 10.0.2.15:5000/registry:2
The push refers to a repository [10.0.2.15:5000/registry]
3c133a51bc00: Pushed 
a2717186d7dd: Pushed 
656c7684d0bd: Pushed 
7683d4fcdf4e: Pushed 
ef763da74d91: Pushed 
2: digest: sha256:435db1be85c6c10b2f506516aa14d8c485c1f1bd5f4a941a637808b085f294b6 size: 1364

Registry鉴权管理

Registry提供了基础的鉴权方式,可以像公有镜像库一样先登录,再提交镜像。

1. 生成鉴权密码文件

在Registry服务器上,增加一个admin用户,密码为admin123:

$ mkdir /etc/docker-distribution/registry/auth
$ docker run --entrypoint htpasswd registry:2 -Bbn admin admin123 > /etc/docker-distribution/registry/auth/htpasswd

2. 修改配置并重启服务

增加对应配置:

$ vim /etc/docker-distribution/registry/config.yml
version: 0.1
log:
  fields:
    service: registry
storage:
    cache:
        layerinfo: inmemory
    filesystem:
        rootdirectory: /var/lib/registry
http:
    addr: :5000
    tls:
        certificate: /etc/docker-distribution/registry/certs/domain.crt
        key: /etc/docker-distribution/registry/certs/domain.key
#增加以下配置:    
auth:
    htpasswd:
        realm: basic-realm
        path: /etc/docker-distribution/registry/auth/htpasswd

重启服务:

$ systemctl restart docker-distribution

测试推送镜像:

$ docker push 10.0.2.15:5000/registry:2                                                                                 
The push refers to a repository [10.0.2.15:5000/registry]
3c133a51bc00: Preparing 
a2717186d7dd: Preparing 
656c7684d0bd: Preparing 
7683d4fcdf4e: Preparing 
ef763da74d91: Preparing 
no basic auth credentials

推送失败,尝试登录后再次推送:

$ docker login 10.0.2.15:5000
Username: admin
Password: 
Login Succeeded

$ docker push 10.0.2.15:5000/registry:2
The push refers to a repository [10.0.2.15:5000/registry]
3c133a51bc00: Layer already exists 
a2717186d7dd: Layer already exists 
656c7684d0bd: Layer already exists 
7683d4fcdf4e: Layer already exists 
ef763da74d91: Layer already exists 
2: digest: sha256:435db1be85c6c10b2f506516aa14d8c485c1f1bd5f4a941a637808b085f294b6 size: 1364

成功登录后,推送镜像成功(因之前已推送过此镜像,所以提示镜像已存在)。

使用容器启动的Registry的安全和鉴权配置

使用容器启动的Registry可以通过多种方法进行配置:

  • 使用Dockerfile将修改好的配置文件、证书及密码文件打入启动镜像。

如Dockerfile加入下列几行后重新制作镜像:

COPY config.yml /etc/docker/registry/config.yml
COPY domain.crt /etc/docker/registry/domain.crt
COPY domain.key /etc/docker/registry/domain.key
COPY htpasswd /etc/docker/registry/htpasswd
  • 将修改好的配置文件、证书及密码文件的目录挂载到镜像对应目录。

启动镜像时将配置目录重新挂载:

$ docker run -d -p 5000:5000 --name registry \
  -v /mnt/registry:/var/lib/registry \
  -v /config:/etc/docker/registry \
  registry:2
  • 启动容器时使用特定环境变量指定修改。

启动容器是通过环境变量也可以修改Registry的配置:

$ 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`/data:/var/lib/registry \
-v `pwd`/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry:2