搭建Docker私有仓库

摘要

这篇文章内容包括搭建docker私有仓库的一些配置项和遇到的问题及解决方案。html

1.配置项
1.1. 数据持久化
1.2. TLS 支持
1.3. 登陆受权验证
1.4. docker compose
2. 测试
3. NGINX作代理
3.1. 个人方式和遇到的问题
3.2. NGINX 做为一个容器
4. 其它方案
5. 相关连接

Docker官方提供了 registry镜像, 能够方便的搭建私有仓库,详细文档参考这里linux

配置项

数据持久化

能够经过采用数据卷挂载或者直接挂载宿主机目录的方式来进行。挂载到容器内默认位置: /var/lib/registry
好比能够像以下方式启动, 这里将容器数据存储在了 /mnt/registry.nginx

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

固然,镜像还提供了其它支持的存储方式,好比OSS等。git

官方文档参见这里github

TLS 支持

为了使得私有仓库安全地对外开放,须要配置 TLS 支持。web

测试的时候,若是不配置的话TLS,能够在docker客户端中的 "insecure registry" 里添加私有仓库地址,否则默认的都以安全的tsl方式来访问私有仓库,具体更改方式能够参考这里docker

个人CA证书是从阿里云获取的(由于域名是在上面注册的,能够提供免费的证书,虽然若是作得很隐蔽)。apache

registry镜像能够经过 REGISTRY_HTTP_TLS_CERTIFICATEREGISTRY_HTTP_TLS_KEY 环境参数配置TLS支持。
例以下面这样, domain.crtdomain.key 是得到的证书,另外配置容器监听ssl默认的 443 端口。ubuntu

$ 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

官方文档参见这里api

登陆受权验证

能够经过 htpasswd 来配置简单的authentication (注意:验证须要 TLS 支持)。

首先在 auth 目录下经过reistry里的 htpasswd 工具建立 验证文件 auth/htpasswd

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

启动的时候经过 REGISTRY_AUTH, REGISTRY_AUTH_HTPASSWD_REALM, REGISTRY_AUTH_HTPASSWD_PATH 来配置:

$ 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

这样就启动了一个监听5000端口的、支持TLS和简单登陆验证的docker 私有仓库。

官方文档参见这里

docker compose

"docker compose" 是一个方便定义和运行多个容器的工具, 安装参见这里, 或者经过pip安装: pip install docker-compose

以上配置项经过 docker compose 的方式组织起来以下:

文件命名成 docker-compose.yaml

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

docker-compose.yaml 所在目录运行:

docker-compose up

测试

私有仓库搭建好了如何测试?

# 先拉取官方镜像
$ docker pull ubuntu:16.04

# 打上标签
$ docker tag ubuntu:16.04 myregistrydomain.com/my-ubuntu

# 推到私有仓库
$ docker push myregistrydomain.com/my-ubuntu

# 从私有仓库获取
$ docker pull myregistrydomain.com/my-ubuntu

Nginx作代理

个人方式和遇到的问题

实际配置中,我采用了 nginx 做为代理,来访问 registry服务。我将TLS支持和登陆验证都加到了nginx一层。

nginx 配置文件:

upstream docker-registry {
  server localhost:5000;                          # !转发到registry 监听的5000 端口!
}

## Set a variable to help us decide if we need to add the
## 'Docker-Distribution-Api-Version' header.
## The registry always sets this header.
## In the case of nginx performing auth, the header is unset
## since nginx is auth-ing before proxying.
map $upstream_http_docker_distribution_api_version $docker_distribution_api_version {
  '' 'registry/2.0';
}

server {
  listen 443 ssl;
  server_name domain.com;  # !这里配置域名!

  # SSL
  ssl_certificate /path/to/domain.pem;               # !这里配置CA 证书信息!
  ssl_certificate_key /path/to/domain.key;           # !这里配置CA 证书信息!

  # Recommendations from https://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 413 for large image uploads
  client_max_body_size 0;

  # required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
  chunked_transfer_encoding on;

  location /v2/ {
    # Do not allow connections from docker 1.5 and earlier
    # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
    if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
      return 404;
    }

    # To add basic authentication to v2 use auth_basic setting.
    auth_basic "Registry realm";
    auth_basic_user_file /path/to/auth/htpasswd;          # !这里配置auth文件位置!

    ## If $docker_distribution_api_version is empty, the header is not added.
    ## See the map directive above where this variable is defined.
    add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always;

    proxy_pass                          http://docker-registry;
    proxy_set_header  Host              $http_host;   # required for docker client's sake
    proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
    proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header  X-Forwarded-Proto $scheme;
    proxy_read_timeout                  900;
  }
}

其中 /path/to/auth/htpasswd 文件是经过 registry 中的或者本地的 htpasswd 工具生成的

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

registry的 docker-compose 文件:

version: '2'
services:
  my_registry:
    restart: always
    image: registry:2
    ports:
      - 127.0.0.1:5000:5000
    volumes:
      - ./data:/var/lib/registry

启动后,当我从本地视图login到私有仓库时,发生错误:

➜  ~ docker login domain.com
Username: testuser
Password:
Error response from daemon: login attempt to https://hub.docker.equiz.cn/v2/ failed with status: 500 Internal Server Error

查看日志发现nginx 错误日志里有个编码相关的错误:

# nginx error log

*4 crypt_r() failed (22: Invalid argument)

通过一番研究,发现以前加密时,是采用 Bcrypt 加密方式,看下 htpasswd 的使用说明:

root@data1:~# htpasswd
Usage:
        htpasswd [-cimBdpsDv] [-C cost] passwordfile username
        htpasswd -b[cmBdpsDv] [-C cost] passwordfile username password

        htpasswd -n[imBdps] [-C cost] username
        htpasswd -nb[mBdps] [-C cost] username password
 -c  Create a new file.
 -n  Don't update file; display results on stdout.
 -b  Use the password from the command line rather than prompting for it.
 -i  Read password from stdin without verification (for script usage).
 -m  Force MD5 encryption of the password (default).
 -B  Force bcrypt encryption of the password (very secure).
 -C  Set the computing time used for the bcrypt algorithm
     (higher is more secure but slower, default: 5, valid: 4 to 31).
 -d  Force CRYPT encryption of the password (8 chars max, insecure).
 -s  Force SHA encryption of the password (insecure).
 -p  Do not encrypt the password (plaintext, insecure).
 -D  Delete the specified user.
 -v  Verify password for the specified user.
On other systems than Windows and NetWare the '-p' flag will probably not work.
The SHA algorithm does not use a salt and is less secure than the MD5 algorithm.

能够看到 -B 会使用 bcrypt 的方式来加密,nginx默认不支持。至于如何让nginx支持bcrypt我暂时还未找到方案,留待之后研究了(TODO)

简单的解决方式是换成默认的MD5加密(由于安全等级问题又不推荐不用bcrypt方式的),

docker run --rm --entrypoint htpasswd registry:2 -bn testuser testpassword > auth/htpasswd   # 这里少了 -B 选项

关于 bcrypt 加密方式,这里 有一篇不错的文章介绍。不过好像对于这个加密方式,网上有一些争论,我就不详究了。

不依赖 "apche tools" 的 nginx 加密方式参考 这里, 好比MD5加密:

printf "testuser:$(openssl passwd -1 testpassword)\n" >> .htpasswd # this example uses MD5 encryption

Nginx 做为一个容器

docker 文档也有如何采用nginx容器和registry配合使用的说明,参考这里

"docker-compose.yaml" 以下:

nginx:
  # Note : Only nginx:alpine supports bcrypt.
  # If you don't need to use bcrypt, you can use a different tag.
  # Ref. https://github.com/nginxinc/docker-nginx/issues/29
  image: "nginx:alpine"     # !这里必定要采用alpine镜像,由于它里的nginx支持 bcrypt 加密!
  ports:
    - 5043:443
  links:
    - registry:registry
  volumes:
    - ./auth:/etc/nginx/conf.d
    - ./auth/nginx.conf:/etc/nginx/nginx.conf:ro

registry:
  image: registry:2
  ports:
    - 127.0.0.1:5000:5000
  volumes:
    - ./data:/var/lib/registry

这里nginx容器监听的是5043, 因此使用的私有仓库的地址变成了 registrydomain.com:5043,
我不喜欢后面加端口,但本机又存在其余须要nginx监听的443端口的服务(不一样doamin下),因此不能让nginx容器直接监听443端口,故没有采用这种方式。

另外在寻找解决方案的时候发现一个镜像 nginx-proxy, 能够方便地监听新添加的容器,留待之后探索。

其它方案

或许你想用letsencrypts免费证书,不妨看看这个工具:acme.sh

或许你不知足的简易方案,你可能还须要web界面来方便查看和管理你的镜像仓库,那么你能够查看下企业级的容器仓库方案: vmware/harbor

相关连接