docker之仓库管理

1     简介

docker 仓库,即所谓registry,实现了镜像的管理、分发,同时还包括用户的认证。dockerregistry仓库是一个无状态的、高可靠的服务器应用程序,用来存储docker镜像。docker.io为docker官方的仓库,默认所有的pull均是从官方仓库拉取镜像。

公司内部平台应用如果使用docker镜像,则必须搭建私有仓库,使用docker的官方镜像Registry的最新版本2(目前是2.5),可以很方便的搭建私有仓库。

2     初识仓库

2.1    下载registry

命令如下:

docker  pull registry:2 

这条命令会从docker的官方仓库中下载registry镜像,版本号是2

 

下载完成后可以使用docker images查看。

2.2    镜像命名规则

myregistrydomain:port/foo/bar:version

以上是一个完整的镜像名称,myregistrydomain为镜像仓库所在的域名地址,port为镜像仓库开发的端口。foo为用户名,可以省略。bar为镜像名称,必填项。version为版本号,可以省略,默认为latest。

 

2.3    运行registry

命令如下:

docker  run --rm  -p 5000:5000  registry:2

 

运行成功后可以看到本机已经在5000端口监听了,但此时还是无法使用pull和push。

3     证书配置

3.1    服务器配置

由于目前没有公有机构颁发的ssl证书,而且使用明文http有不安全性,故计划使用自签名的ssl证书。命令如下:

mkdir-p certs && openssl req \

   -newkey rsa:4096 -nodes -sha256 -keyoutcerts/domain.key \

   -x509 -days 365 -out certs/domain.crt

注意必须填写CN字段为你的域名地址,目前使用yourdomain.com这个域名作为dockerregistry的域名。

3.2    客户端配置

所有docker客户端也需要作相应配置,使用证书文件之一domain.crt,复制到以下目录:

/etc/docker/certs.d/ yourdomain.com:5000/ca.crt

即需要将文件名从domain.crt修改为ca.crt,然后客户端的docker引擎也需要重启,如此可以直接使用docker pull  yourdomain.com:5000/image-name来下载镜像了。

如果需要push镜像,则需要首先使用docker  login,命令如下:

docker login  yourdomain.com:5000 -u=user-p=pass

然后再使用docker  push上传自己的镜像文件。

 

4     失败尝试

4.1    目标

搭建一个docker私有仓库,存储所有“测试、生产”的平台应用。

使用ssl安全连接,ssl证书为自签名证书,使用者只有获取此证书才能使用。

所有有证书的人员,可以直接从仓库中下载镜像。

只有经过用户名、密码认证后的人员,才能往仓库中上传镜像。

 

本次尝试使用registry/nginx两个容器实现,但后续发现无法实现原有目标。由于v2版本的registry在登录、push、pull均使用GET /v2/这个相同的url,而且首次连接时一定使用,用来确认registry是否需要认证,即如果返回为401则表示registry需要认证,否则表示不认证。由于有这一层限制导致对GET /v2/这个url无法做处理,如果让其不认证,则客户端就会认为不需要认证,会以不认证的方式继续后续操作,比如push,如此会在push时无法成功;如果让其认证,则会导致匿名用户无法pull,因为匿名用户pull时第一个url也是GET /v2/,由于需要认证且又是匿名,会导致无法pull。

虽然整个章节都是失败的尝试,但也记录下来,一方面加深了对nginx作http代理的了解,另一方面对于registry的1.0版本,使用此方法是有效的。

 

4.2    架构

使用registry:2和nginx两个镜像完成私有仓库的搭建,架构如下:

当用户使用pull下载镜像时,不过“认证”模块,故不需要用户登录;如果需要push上传镜像,则必须经过“认证”模块,故push镜像必须标准用户名、密码。详细研究registry后发现暂时找不到如何配置,故转而使用nginx配置规则实现此目标。

4.3    服务器配置

使用docker-compose统一配置,如下详细描述。

4.3.1 nginx compose配置

  nginx:

    restart: always

    image: yourdomain.com:5000/nginx

    ports:

      - "5000:5000"

    links:

      - registry

 

    volumes:

      - ./auth:/etc/nginx/conf.d/

      -./auth/nginx.conf:/etc/nginx/nginx.conf:ro

      - ./log:/var/log/nginx

nginx容器会将数据转发到registry容器,故使用links指令。

对外开放5000端口。

有三个数据卷:

auth,其中存储ssl证书和push需要的用户名密码文件;在上一章节生成的证书也放置到此目录下,供nginx使用。

nginx.conf,nginx真正的配置文件

log,将nginx的日志输出到宿主机,方便查看日志。

4.3.2 nginx配置

用户名密码文件

使用htpasswd命令生成用户名密码文件,供nginx认证使用。

在镜像registry:2中包含了htpasswd程序,使用如下命令:

docker run --rm  --entrypoint htpasswd  registry:2 -bn user pass >auth/nginx.htpasswd

注意以上的user pass,正是在push镜像时需要认证的用户名、密码。

nginx.conf

events {

    worker_connections  1024;

}

 

http {

    upstream docker-registry {

        server registry:5000;

    }

 

    ## Set a variable to help us decide if weneed to add the

    ## 'Docker-Distribution-Api-Version'header.

    ## The registry always sets this header.

    ## In the case of nginx performing auth,the header will be unset

    ## since nginx is auth-ing before proxying.

    map$upstream_http_docker_distribution_api_version $docker_distribution_api_version{

        '' 'registry/2.0';

    }

   

    server {

        listen        5000;

        server_name        yourdomain.com;

 

        ssl            on;

        ssl_certificate        /etc/nginx/conf.d/domain.crt;

        ssl_certificate_key    /etc/nginx/conf.d/domain.key;

 

        # Recommendations fromhttps://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html

        ssl_protocols TLSv1.1 TLSv1.2;

        ssl_ciphers'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';

        ssl_prefer_server_ciphers on;

        ssl_session_cache shared:SSL:10m;

 

        # disable any limits to avoid HTTP 413for 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;

 

        ## If $docker_distribution_api_versionis empty, the header will not be added.

        ## See the map directive above wherethis variable is defined.

        add_header 'Docker-Distribution-Api-Version'$docker_distribution_api_version always;

       

        proxy_set_header  Host              $http_host;

        proxy_set_header  X-Real-IP         $remote_addr;

        proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;

       proxy_set_header  X-Forwarded-Proto $scheme;

        proxy_read_timeout                  900;

 

        location ~ ^/v2/$ {

            auth_basic                      "Restricted";

            auth_basic_user_file            /etc/nginx/conf.d/nginx.htpasswd;

            proxy_pass                      https://docker-registry;

        }

        location / {

            limit_except GET HEAD OPTIONS {

                auth_basic                  "Restricted";

                auth_basic_user_file        /etc/nginx/conf.d/nginx.htpasswd;

            }

            proxy_pass                      https://docker-registry;

        }

    }

}

如下配置为nginx.conf的内容,其中大部分内容都取自于docker文档。

原理

(1)   docker交互API

首先要详细分析docker客户端与v2版本的registry交互的流程,交互过程为HTTPS,可以通过nginx的access.log日志来分析。

a)     查询api

对应的URL是“GET /v2/_catalog HTTP/1.0”

b)    pull镜像

对应的URL是"GET/v2/hello-world/manifests/latest HTTP/1.0"

c)     push镜像

对应的URL是"POST/v2/hello-world/blobs/uploads/ HTTP/1.0"

d)    dockerlogin

对应的URL是"GET /v2/  HTTP/1.0"

 

根据分析以上数据交互过程,确保pull时无需认证,push时必须认证,则需要对所有的非GET请求进行认证,同时对docker login也需要认证。

(2)   location配置

使用两个location规则实现“不认证pull、认证才能push”的目的,location ~ ^/v2/$,此规则表示URL仅是’/v2/’,不包含后续的内容,恰好满足docker login的要求,即确保docker login时进入认证流程;location /此规则表示其他所有的URL都使用此配置,因为nginx中的location是最长匹配原则。其中limit_except GET HEADOPTIONS表示除了这三种HTTP方法:GET/HEAD/OPTIOINS,其他的方法都需要认证,因此对于push镜像就必须经过认证。认证所需要的文件就是htpasswd生成的用户名、密码文件。

4.4    客户端查询

只有push权限的客户端才有查询能力,即确保使用者的安全性,只告知可以下载哪些镜像,使用第三方的python脚本完成这个功能,文件名为registry.py,命令如下。

registry.py -r https://yourdomain.com:5000

如下显示内容是一个样例:

Image: django

  tag: latest

Image: mysql

  tag: latest

Image: nginx

  tag: latest

Image: redis

  tag: latest

4.5   镜像删除

镜像删除工作比较复杂,目前docker没有提供方便的接口完成registry中镜像的删除工作,不过可以使用上一章节中的registry.py脚本配合完成,步骤如下:

(1)  删除tags

registry.py -r  https://yourdomain.com:5000  -d -i imagename   -n numberoftags

       imagename,要清理的镜像名称

       numberoftags,保留版本的个数,默认是10个。

(2)  停止仓库registry

使用docker-compose down停止服务

(3)  垃圾回收

使用如下命令:

docker-compose  run  registry  bin/registry  garbage-collect  /etc/docker/registry/config.yml

(4)  启动仓库registry

docker-compose  up

 

5     用户认证

引入docker_auth这个新的认证镜像,其中包含了一个静态认证的服务器,同时此认证服务器还有多种功能,支持google、github、ldap、mongo等多种动态认证方式,在搭建私有仓库时极其有用,比如要控制不同镜像的不同人员访问权限,php人员只能下载php镜像,java人员只可以下载、上传java相关镜像,有非常良好的用户认证机制。但由于此镜像隐藏得非常深,找了很久才发现,而且中文世界中对其描述很少,以下所有配置均可参见网站:

https://github.com/cesanta/docker_auth

也可以下载对应的github源码深度学习,其中有详细的配置,还有认证服务器的go语言源代码,非常有价值。

5.1   认证原理

docker认证完全遵循oauth2.0标准,标准流程如下:

1.      docker client 尝试到registry中进行push/pull操作;

2.      registry会返回401未认证信息给client(未认证的前提下),同时返回的信息中还包含了到哪里去认证的信息;

3.      client发送认证请求到认证服务器(authorization service);

4.      认证服务器(authorization service)返回token;

5.      client携带这附有token的请求,尝试请求registry;

6.      registry接受了认证的token并且使得client继续操作;

5.2   实现

使用两个官方镜像袜,registry:2镜像实现仓库功能,cesanta/docker_auth实现认证功能。docker_auth镜像使用go语言实现认证功能,包括静态用户、google、github、mongo、外部认证等方式,总共代码行数才2600行。

5.2.1 registry配置

覆盖默认的config.yml文件,如下:

version: 0.1

log:

  fields:

   service: registry

   

storage:

 delete:

   enabled: true

  cache:

   blobdescriptor: inmemory

 filesystem:

   rootdirectory: '/var/lib/registry'

 

auth:

  token:

   realm: 'https://dockerreg.maxnetsys.com:5001/auth'

   service: 'Docker registry'

   issuer: 'maxnet auth server'

   rootcertbundle: '/certs/domain.crt'

   

http:

  addr::5000

  tls:

   certificate: '/certs/domain.crt'

    key:'/certs/domain.key'

 headers:

   X-Content-Type-Options: [nosniff]

   

health:

 storagedriver:

   enabled: true

   interval: 10s

   threshold: 3

在部分配置是默认配置,关注auth块,其中使用了token认证,填充四个参数,realm表示为docker_auth镜像启动时的路径,docker_auth监听5001端口;issuer值必须与docker_auth中的配置完全一致;rootcertbundle就是公钥;service随便填。

5.2.2 docker_auth配置

覆盖默认配置。

server:

  addr:"0.0.0.0:5001"

 

 certificate: "/ssl/domain.crt"

  key:"/ssl/domain.key"

 

token:

 issuer: "maxnet auth server"

 expiration: 900

 

users:

 "admin":

   password:"$2y$05$Hr7we48EODPWnzRRmBJEfucuBc.RM9FJZHFe6aZk3gPKU67U3.9P2"

 

 "": {}

 

acl:

  -match: {account: ""}

    actions:["pull"]

   comment: "Anonymous users can pull everything."

  -match: {account: "admin"}

   actions: ["*"]

   comment: "Allow everything for dockerreg users."

server块中指定监听端口,还需要写明ssl的公钥和私钥;token中的issuer与registry保持一致;users块为用户配置,其中有两个用户,一个是admin,另一个是匿名用户;acl配置用户的权限,匿名用户只能pull,admin用户有所有权限。

用户的密码请使用htpasswd生成,命令格式如下:

htpasswd –nb -B  username  password

注意忽略输出的内容中的用户名和冒号,即只复制冒号后面的内容到password。

docker_auth的配置详细可以参考github中的reference.yml,其中有所有项的详细说明。在实际生产环境中如果期望更细粒度的配置权限,可以扩展users和acl,非常方便。

5.2.3 compose内容

使用docker-compose部署。

version: "2"

services:

 registry:

   restart: always

   image: registry:2

   ports:

      -"5000:5000"

   volumes:

      -./certs:/certs:ro

      -./registry_config.yml:/etc/docker/registry/config.yml

      -./data:/var/lib/registry

 dockerauth:

   restart: always

   image: cesanta/docker_auth

   ports:

      -"5001:5001"

   command: --v=2 --alsologtostderr /config/auth_config.yml

   volumes:

      -./auth_config.yml:/config/auth_config.yml:ro

      -./certs:/ssl:ro

      - ./log:/logs

registry和docker_auth分别监听5000、5001端口,均使用https。registry需要将镜像存储的volumes,docker_auth启动命令将日志输出到终端,方便运维人员查看日志。

 

6     web管理

web化的仓库管理客户端,目前个人尚未找到,如果能找到将极大的降低运维人员的使用复杂度。尝试了hyper/docker-registry-web这个镜像,发现其对oauth2.0的认证方式不支持,故放弃。

你可能感兴趣的:(linux,相关,虚拟化)