部署一个私有镜像库

本节我们介绍怎样部署一个公司内部使用的私有镜像库或者对用户使用的公有镜像库。例如,公司希望有一个私有镜像库支持CI,当发布版本或者测试时。当然,你们公司有很多服务或者产品在使用镜像,而且你希望你者所有的镜像只需要一个人维护。

Docker的公有镜像库维护了一个默认的registry镜像,以便你快速部署。这个registry镜像足够应对本地测试所需,但是不能满足线上生成。对于线上生成,你需要从docker/distribution里的代码构建和部署自定义的镜像。

说明:本示例只是在Ubuntu14.04上测试和使用,如果你再其它不同的操作系统上使用,则需要转换特定命令来满足你环境的需要。

使用官方镜像简单开始

在此,我们运行Docker官方镜像库中的镜像,你push镜像到镜像库或者从镜像库pull镜像。最容易理解的方式便是了解一个客户端十怎样和本地镜像库进行交互的。

  1. 安装Docker
  2. 从Docker官方镜像库中的镜像启动容器
    1. docker run hello-world

    run命令会自动从官方镜像库中pull hello-world镜像。

  3. 本地启动一个镜像库

    1. docker run -p 5000:5000 registry:2.0

    这将会在你的DOCKER_HOST的5000端口启动镜像库。

  4. 列举你的镜像

    1. docker images
    2. REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
    3. registry 2.0 bbf0b6ffe923 3 days ago 545.1 MB
    4. golang 1.4 121a93c90463 5 days ago 514.9 MB
    5. hello-world latest e45a5af57b00 3 months ago 910 B

    这将列举出包含hello-world镜像的列表。

  5. 重新为你的镜像库tag hello-world镜像

    1. docker tag hello-world:latest localhost:5000/hello-mine:latest

    hello-world:latest将使用新的命名方式[REGISTRYHOST/]NAME[:TAG],REGISTRYHOST在此处便是localhost,在Mac OS X则需使用$(boot2docker ip):5000替换localhost:5000。

  6. 重新列举你的镜像

    1. docker images
    2. REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
    3. registry 2.0 bbf0b6ffe923 3 days ago 545.1 MB
    4. golang 1.4 121a93c90463 5 days ago 514.9 MB
    5. hello-world latest e45a5af57b00 3 months ago 910 B
    6. localhost:5000/hello-mine latest ef5a5gf57b01 3 months ago 910 B

    这里会看到你新tag的镜像。

  7. push你的新镜像到你的私有镜像库

    1. docker push localhost:5000/hello-mine:latest
    2. The push refers to a repository [localhost:5000/hello-mine] (len: 1)
    3. e45a5af57b00: Image already exists
    4. 31cbccb51277: Image successfully pushed
    5. 511136ea3c5a: Image already exists
    6. Digest: sha256:a1b13bc01783882434593119198938b9b9ef2bd32a0a246f16ac99b01383ef7a
  8. 使用curl命令和Docker Registry API v2来列举你私有镜像库中的镜像

    1. curl -v -X GET http://localhost:5000/v2/hello-mine/tags/list
    2. * Hostname was NOT found in DNS cache
    3. * Trying 127.0.0.1...
    4. * Connected to localhost (127.0.0.1) port 5000 (#0)
    5. > GET /v2/hello-mine/tags/list HTTP/1.1
    6. > User-Agent: curl/7.35.0
    7. > Host: localhost:5000
    8. > Accept: */*
    9. >
    10. < HTTP/1.1 200 OK
    11. < Content-Type: application/json; charset=utf-8
    12. < Docker-Distribution-Api-Version: registry/2.0
    13. < Date: Sun, 12 Apr 2015 01:29:47 GMT
    14. < Content-Length: 40
    15. <
    16. {"name":"hello-mine","tags":["latest"]}
    17. * Connection #0 to host localhost left intact

    你也可以在浏览器中输入http://localhost:5000/v2/hello-mine/tags/list获得同样的信息。

  9. 移出你环境中未使用的镜像

    1. docker rmi -f $(docker images -q -a )

    这个命令会移除所有通过run获取的镜像,当你运行docker images,你不再看到hello-world和hello-mine镜像。

    1. docker images
    2. REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
    3. registry 2.0 bbf0b6ffe923 3 days ago 545.1 MB
    4. golang 1.4 121a93c90463 5 days ago 514.9 MB
  10. 尝试运行hello-mine

    1. docker run hello-mine
    2. Unable to find image 'hello-mine:latest' locally
    3. Pulling repository hello-mine
    4. FATA[0001] Error: image library/hello-mine:latest not found

    run命令运行失败,说明docker官方镜像库中不存在hello-mine。

  11. 现在尝试在镜像前加入镜像的私有镜像库地址

    1. docker run localhost:5000/hello-mine

    如果此时你运行docker images,你会惊奇发现hello-mine镜像。

预备将Docker官方的registry镜像应用到生成环境

Docker官方的镜像仅仅用来测试和调试的。它的配置不适用于生成环境。例如,任何有server host操作权限的人都可以pull或者push镜像。详细阅读本文接下来的内容,了解怎样将Docker官方的镜像应用到生成环境中。

了解生成部署

如果你要部署生成环境,需要了解如下因素:

  1. 后端存储:你的镜像存储在哪里?
  2. 权限和认证:是否全面开发权限,依赖于你是否部署在公有环境还是私有环境。
  3. 调试:当问题存在后,你需要去处理它。日志是能看到解决这些问题的趋势的。
  4. 缓存:快速检索是非常重要的,如果你依赖于镜像进行测试、构建或者其它自动化系统中。

你可以配置registry来调整这些因素。你可以通过指定命令行参数或者直接写入到配置文件中,其配置文件是YAML 格式的文件。

  1. version: 0.1
  2. log:
  3. level: debug
  4. fields:
  5. service: registry
  6. environment: development
  7. storage:
  8. cache:
  9. layerinfo: inmemory
  10. filesystem:
  11. rootdirectory: /tmp/registry-dev
  12. maintenance:
  13. uploadpurging:
  14. enabled: false
  15. http:
  16. addr: :5000
  17. secret: asecretforlocaldevelopment
  18. debug:
  19. addr: localhost:5001
  20. redis:
  21. addr: localhost:6379
  22. pool:
  23. maxidle: 16
  24. maxactive: 64
  25. idletimeout: 300s
  26. dialtimeout: 10ms
  27. readtimeout: 10ms
  28. writetimeout: 10ms
  29. notifications:
  30. endpoints:
  31. - name: local-8082
  32. url: http://localhost:5003/callback
  33. headers:
  34. Authorization: [Bearer <an example token>]
  35. timeout: 1s
  36. threshold: 10
  37. backoff: 1s
  38. disabled: true
  39. - name: local-8083
  40. url: http://localhost:8083/callback
  41. timeout: 1s
  42. threshold: 10
  43. backoff: 1s
  44. disabled: true

配置文件包含了基本的,在生成环境中能见到的。例如http详细配置了服务启动的端口。但是该服务还未使用TLS。接下来,让我们配置TLS。

在镜像库中配置TLS

本文中,我们将配置TLS,以便可以通过HTTPS通信。启动TLS,将让registry安全运行在公司的防火墙内。为了能办到这一点,我们需要自己构建镜像。

下载代码并生成证书

  1. 下载registry源码

    1. 当然,条件运行你可以使用git clone命令。
  2. 解压源码

    1. 解压过程会产生distribution文件夹。
  3. 进入工作路径

    1. cd distribution
  4. 创建certs子目录

    1. mkdir certs
  5. 使用SSL生成自签名证书

    1. openssl req \
    2. -newkey rsa:2048 -nodes -keyout certs/domain.key \
    3. -x509 -days 365 -out certs/domain.crt
    4. 该命令会提示你创建证书的一些基本信息。
  6. 列举certs目录下的文件

    1. ls certs
    2. domain.crt domain.key
    3. 当你构建容器时,certs会自动被复制进去。

添加TLS到配置文件

distribution包含了相同的配置文件在cmd目录下。在此,你需要编辑这些配置文件。

  1. 编辑./cmd/registry/config.yml文件

    1. gedit ./cmd/registry/config.yml
  2. 锁定http

    1. http:
    2. addr: :5000
    3. secret: asecretforlocaldevelopment
    4. debug:
    5. addr: localhost:5001
  3. 添加tls块

    1. http:
    2. addr: :5000
    3. secret: asecretforlocaldevelopment
    4. debug:
    5. addr: localhost:5001
    6. tls:
    7. certificate: /go/src/github.com/docker/distribution/certs/domain.crt
    8. key: /go/src/github.com/docker/distribution/certs/domain.key
    9. 当然,你需要提供certificates的路径。如果,你想有两种通信方式,你需要添加可选的`clientcas`
  4. 保存并关闭文件

构建并运行你自己的镜像库

  1. 构建镜像

    1. docker build -t secure_registry .
  2. 运行新的镜像

    1. docker run -p 5000:5000 secure_registry:latest
    2. time="2015-04-12T03:06:18.616502588Z" level=info msg="endpoint local-8082 disabled, skipping" environment=development instance.id=bf33c9dc-2564-406b-97c3-6ee69dc20ec6 service=registry
    3. time="2015-04-12T03:06:18.617012948Z" level=info msg="endpoint local-8083 disabled, skipping" environment=development instance.id=bf33c9dc-2564-406b-97c3-6ee69dc20ec6 service=registry
    4. time="2015-04-12T03:06:18.617190113Z" level=info msg="using inmemory layerinfo cache" environment=development instance.id=bf33c9dc-2564-406b-97c3-6ee69dc20ec6 service=registry
    5. time="2015-04-12T03:06:18.617349067Z" level=info msg="listening on :5000, tls" environment=development instance.id=bf33c9dc-2564-406b-97c3-6ee69dc20ec6 service=registry
    6. time="2015-04-12T03:06:18.628589577Z" level=info msg="debug server listening localhost:5001"
    7. 2015/04/12 03:06:28 http: TLS handshake error from 172.17.42.1:44261: remote error: unknown certificate authority
    8. Watch the messages at startup. You should see that `tls` is running.
  3. curl命令指定https链接

    1. $ curl -v https://localhost:5000
    2. * Rebuilt URL to: https://localhost:5000/
    3. * Hostname was NOT found in DNS cache
    4. * Trying 127.0.0.1...
    5. * Connected to localhost (127.0.0.1) port 5000 (#0)
    6. * successfully set certificate verify locations:
    7. * CAfile: none
    8. CApath: /etc/ssl/certs
    9. * SSLv3, TLS handshake, Client hello (1):
    10. * SSLv3, TLS handshake, Server hello (2):
    11. * SSLv3, TLS handshake, CERT (11):
    12. * SSLv3, TLS alert, Server hello (2):
    13. * SSL certificate problem: self signed certificate
    14. * Closing connection 0
    15. curl: (60) SSL certificate problem: self signed certificate
    16. More details here: http://curl.haxx.se/docs/sslcerts.html

为v1和v2版本registry配置Nginx

本节将介绍使用docker-compose结合v1和v2运行在Nginx代理后边。结合的registry都将可以在localhost:5000上访问。如果docker客户端版本低于docker1.6,Nginx将路由到v1,否则将会路由到v2。

这个过程我们继续使用distribution,路径下包含了一个compose示例。

安装Docker Compose

  1. 打开一个新终端

  2. 获取docker-compose二进制文件

    1. sudo wget https://github.com/docker/compose/releases/download/1.1.0/docker-compose-`uname -s`-`uname -m` -O /usr/local/bin/docker-compose
    2. 这个命令会将docker-compose安装到/usr/local/bin
  3. 然后为二进制文件添加可执行权限

    1. sudo chmod +x /usr/local/bin/docker-compose

做一些清理

  1. 移除先前的镜像

    1. docker rmi -f $(docker images -q -a )
  2. 编辑distribution/cmd/registry/config.yml然后移出tls块。

    1. 如果你运行了先前的实例,你会有一个tls块。
  3. 保存并关闭文件

配置SSL

  1. 进入distribution/contrib/compose/nginx目录

    1. 该路径下包含了俩版本的私有镜像库的Nginx的配置文件。
  2. 使用SSL生成自签名证书

    1. openssl req \
    2. -newkey rsa:2048 -nodes -keyout domain.key \
    3. -x509 -days 365 -out domain.crt
    4. 该命令会提示你创建证书的一些基本信息。
  3. 编辑Dockerfile并添加如下行

    1. COPY domain.crt /etc/nginx/domain.crt
    2. COPY domain.key /etc/nginx/domain.key
  4. 当你完成后,文件类似这样子

    1. FROM nginx:1.7
    2. COPY nginx.conf /etc/nginx/nginx.conf
    3. COPY registry.conf /etc/nginx/conf.d/registry.conf
    4. COPY docker-registry.conf /etc/nginx/docker-registry.conf
    5. COPY docker-registry-v2.conf /etc/nginx/docker-registry-v2.conf
    6. COPY domain.crt /etc/nginx/domain.crt
    7. COPY domain.key /etc/nginx/domain.key
  5. 保存并关闭Dockerfile

  6. 编辑registry.conf并写入如下行

    1. ssl on;
    2. ssl_certificate /etc/nginx/domain.crt;
    3. ssl_certificate_key /etc/nginx/domain.key;
    4. 这是一个nginx配置文件
  7. 保存并关闭registry.conf

构建并运行

  1. 进入distribution/contrib/compose

    1. 目录下包含docker-compose.yml
  1. nginx:
  2. build: "nginx"
  3. ports:
  4. - "5000:5000"
  5. links:
  6. - registryv1:registryv1
  7. - registryv2:registryv2
  8. registryv1:
  9. image: registry
  10. ports:
  11. - "5000"
  12. registryv2:
  13. build: "../../"
  14. ports:
  15. - "5000"
  16. 这个文件将指定nginx镜像通过nginx/Dockerfile文件构建,v1版本registry通过Docker官方镜像获取,v2版本registry则通过先前使用的distribution/Dockerfile文件构建。
  1. 获取registry v1

    1. docker pull registry:0.9.1
  2. 构建nginx和registry v2

    1. docker-compose build
    2. registryv1 uses an image, skipping
    3. Building registryv2...
    4. Step 0 : FROM golang:1.4
    5. ...
    6. Removing intermediate container 9f5f5068c3f3
    7. Step 4 : COPY docker-registry-v2.conf /etc/nginx/docker-registry-v2.conf
    8. ---> 74acc70fa106
    9. Removing intermediate container edb84c2b40cb
    10. Successfully built 74acc70fa106
  3. 启动服务

    1. docker-compose up
  4. 开启另外一个终端查看运行结果

    1. docker ps
    2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    3. a81ad2557702 compose_nginx:latest "nginx -g 'daemon of 8 minutes ago Up 8 minutes 80/tcp, 443/tcp, 0.0.0.0:5000->5000/tcp compose_nginx_1
    4. 0618437450dd compose_registryv2:latest "registry cmd/regist 8 minutes ago Up 8 minutes 0.0.0.0:32777->5000/tcp compose_registryv2_1
    5. aa82b1ed8e61 registry:latest "docker-registry" 8 minutes ago

验证

  1. 检查Nginx中的TLS

    1. curl -v https://localhost:5000
    2. * Rebuilt URL to: https://localhost:5000/
    3. * Hostname was NOT found in DNS cache
    4. * Trying 127.0.0.1...
    5. * Connected to localhost (127.0.0.1) port 5000 (#0)
    6. * successfully set certificate verify locations:
    7. * CAfile: none
    8. CApath: /etc/ssl/certs
    9. * SSLv3, TLS handshake, Client hello (1):
    10. * SSLv3, TLS handshake, Server hello (2):
    11. * SSLv3, TLS handshake, CERT (11):
    12. * SSLv3, TLS alert, Server hello (2):
    13. * SSL certificate problem: self signed certificate
    14. * Closing connection 0
    15. curl: (60) SSL certificate problem: self signed certificate
    16. More details here: http://curl.haxx.se/docs/sslcerts.html
  2. Tag registry v1

    1. docker tag registry:latest localhost:5000/registry_one:latest
  1. push registry v1 到私有镜像库

    1. docker push localhost:5000/registry_one:latest
    2. 如果你的docker客户端大于等于1.6,怎会pushregistry v2
  2. 使用curl列举镜像库中的镜像

    1. curl -v -X GET http://localhost:32777/v2/registry_one/tags/list
    2. * Hostname was NOT found in DNS cache
    3. * Trying 127.0.0.1...
    4. * Connected to localhost (127.0.0.1) port 32777 (#0)
    5. > GET /v2/registry_one/tags/list HTTP/1.1
    6. > User-Agent: curl/7.36.0
    7. > Host: localhost:32777
    8. > Accept: */*
    9. >
    10. < HTTP/1.1 200 OK
    11. < Content-Type: application/json; charset=utf-8
    12. < Docker-Distribution-Api-Version: registry/2.0
    13. < Date: Tue, 14 Apr 2015 22:34:13 GMT
    14. < Content-Length: 39
    15. <
    16. {"name":"registry1","tags":["latest"]}
    17. * Connection #0 to host localhost left intact