Nexus3.x配置Docker镜像仓库及仓库代理

使用 Nexus3 配置全功能的私有Docker 镜像仓库及Docker 镜像代理仓库。

Nexus3.x配置Docker镜像仓库及仓库代理

update @2018/07/12

背景

我们一直使用 harbor 作为docker的镜像仓库,但Harbor只能作为私有仓库,当需要从Docker HubGoogle Cloud Containers 上拉镜像时,我们只能自己手动pull,重新打tag,再push到harbor上。

如果需要拉取多个镜像,这样相当麻烦,尤其是我们使用Kubespray来部署Kubernetes集群,仅仅准备镜像就需要花费很多时间。

我们希望有一个Docker仓库,能同时托管私有镜像,还能代理访问公共的镜像仓库。

正好我们在使用Nexus作为Maven的仓库,同时nexus3提供了Docker, yum, apt, npm, ruby gems, pypi 等诸多类型的仓库功能。

经过技术调研,Nexus3完全可以达到我们的预期。

Nexus3 提供了的3种类型的Docker仓库,前两者都可以创建多个仓库,最后一个则可以将他们全部聚合到一个URL来访问。

  • docker (hosted): 自托管
  • docker (proxy): 代理
  • docker (group): 聚合

配置Docker仓库

运行一个nexus3服务器

最新稳定版下载地址,选择 3.x 的版本:

https://github.com/sonatype/nexus-public/releases

version=3.15.2-01
curl -SLO https://sonatype-download.global.ssl.fastly.net/repository/repositoryManager/3/nexus-${version}-unix.tar.gz
tar -C /data/nexus3 -xf nexus-${version}-unix.tar.gz
ln -sf /data/nexus3/nexus-{version} /data/nexus3/nexus
export RUN_AS_USER=root
/data/nexus3/nexus/bin/nexus start

参考下面的文档,为nexus添加系统服务:

https://help.sonatype.com/repomanager3/installation/run-as-a-service#RunasaService-systemd

vim /etc/systemd/system/nexus.service

[Unit]
Description=Nexus Repository Manager OSS 3
After=network.target

[Service]
Type=forking
ExecStart=/data/nexus3/nexus/bin/nexus start
ExecStop=/data/nexus3/nexus/bin/nexus stop
ExecReload=/data/nexus3/nexus/bin/nexus restart
User=nexus
Restart=on-abort
LimitNOFILE=65536

[Install]
Alias=nexus
WantedBy=multi-user.target

上面的服务指定了一个用户nexus为nexus3的运行用户,因此我们需要创建一个用户,并添加nexus3的目录所有权给他:

useradd nexus
chmod -R nexus. /data/nexus3/

然后启动服务并添加开机启动:


systemctl enable nexus
systemctl restart nexus

Nexus的默认端口是8081, 用户名/密码:admin/admin123

登录nexus3,点击设置齿轮,并定位到左侧的Repository:

image

创建一个hosted类型的docker仓库

Hosted类型仓库用作我们的私有仓库,我们可以push自己的镜像到这个仓库,替代harbor的功能。

  • 创建专用的blob

    为了仓库数据的独立性和安全性,我们可以给每一个repository创建一个独立的Blob块存储。

    点击 Repository下面的 Blob Stores - Create blob store:

    • Name:填写一个易于辨认的名字
    • Path:会自动生成并补全。默认在Nexus安装目录下面的sonatype-work/nexus3/blobs/下,也可以修改到其它目录或磁盘,甚至可以是NFS或者cephfs的目录。

    image

  • 创建hosted类型的私有仓库

    点击 Repository下面的 Repositories - Create repository - docker(hosted) :

    • Name: docker-private

    • Online: 勾选。这个开关可以设置这个Docker repo是在线还是离线。

    • Repository Connectors

      下面包含HTTPHTTPS两种类型的port。

      有什么用呢?说明讲得很清楚:

      连接器允许docker客户端直接连接到docker仓库,并实现一些请求操作,如docker pull, docker push, API查询等。对于私有仓库来说,这个连接器是必需的,只有通过这个端口才能push镜像到仓库内。后面我们会用group类型的docker仓库,它只有只读(即pull)功能。

      因此,这里我们勾选并填写一个端口,如8090,Nexus会用java启动一个连接器进程监听在这个端口上。

      然后在daemon.json中设置
      "insecure-registries": [172.xx.xxx.xxx:8090"], 重启docker后,通过如下命令访问:

      docker push 172.xx.xxx.xxx:8090/centos:7.5.1804

      Force basic authentication

      不勾选。这样就允许匿名执行docker pull了。当然,push之前还是要进行docker login操作的。

    • Docker Registry API Support

      Docker registry默认使用的是API v2, 但是为了兼容性,我们可以勾选启用API v1

    • Storage

      Blob store,我们下拉选择前面创建好的专用blob:blob-docker-private

    • Hosted

      开发环境,我们运行重复发布,因此Delpoyment policy 我们选择Allow redeploy

    • 截图参考
      image

创建一个proxy类型的docker仓库

proxy类型仓库,可以帮助我们访问不能直接到达的网络,如另一个私有仓库,或者国外的公共仓库,如Google cloud registry。

对于代理Docker hub, 官方有简要的文档可以参考,就不再细写,请参考Proxy Repository for Docker

下面以创建一个Google cloud registry的代理为例,简要写一下如何创建proxy类型的docker仓库。

  • 创建一个专用的blob

    image

  • 创建一个proxy类型的仓库

  • Name: proxy-google-containers

  • Repository Connectors: 不设置。

  • Proxy

    • Remote Storage: Google cloud registry的地址:https://gcr.io

      配置docker hub的proxy时,这里填写: https://registry-1.docker.io

    • Docker Index: Use proxy registry(specified above)

      配置docker hub的Docker Index时,点选Use Docker Hub或者填写:https://index.docker.io/

  • Storage: blob-google-containers

  • 截图参考

    image

创建一个group类型的docker仓库

group类型的docker仓库,是一个聚合类型的仓库。它可以将前面我们创建的3个仓库聚合成一个URL对外提供服务,可以屏蔽后端的差异性,实现类似透明代理的功能。

注意:
Group仓库时只读的,也就是说我们只能做docker pull 操作,无法docker push,详见文档说明:

https://help.sonatype.com/repomanager3/private-registry-for-docker/repository-groups-for-docker
image

Group类型创建过程类似于上面的proxy类型。

  • 名字比较简单:-docker-registry

  • 启用了一个监听在8082端口的http连接器;

  • 启用了匿名pull
    这一步要求在左侧的 Securety -- Realms 中启用Docker Bearer Token Realm 插件

  • 启用 Docker Registry V1 API

  • Storage:选择专用的blob存储blob-docker

  • group : 将左边可选的4个仓库,添加到右边的members下;

  • 截图参考

    image


初次使用该仓库

配置代理服务器

根据我们上面配置,我们还无法直接使用仓库,我们还得配置一个代理服务器。

Setting - System - HTTP 下,设置一个代理服务器。

在使用中发现,仓库页面的timeout太多,总是进入auto block状态,我们在查看文档后,增大了Connection/Socket timeoutConnection/Socket retry attempts的值,参考
Search
A proxy repository is not working, status says "Remote Automatically Blocked and Unavailable"

下面是配置截图:

image

添加insecure-registry设置

完成了上述配置,我们本以为可以从这个仓库pull镜像了,但是由于我们使用的是http协议,Docker客户端认为它是不安全的,默认不支持这样的连接,直接就报错了:

image

怎么办呢?

我们需要修改本机docker的配置,让它能够从我们指定的非安全仓库地址拉取镜像。

Docker不同版本设置上有差异,参考如下:

  • docker version 1.13 以前

    vim /etc/systemd/system/docker.service.d/docker-options.conf

    [Service]
    Environment="DOCKER_OPTS=--insecure-registry=172.xx.xxx.xx:8082 --insecure-registry=10.233.0.0/18 --graph=/var/lib/docker  --log-opt max-size=50m --log-opt max-file=5 \
    --iptables=false"
    
  • Docker version 1.13 及其后

    vim /etc/docker/daemon.json

    {
    "bip": "10.0.0.1/24",
    "ipv6": false,
    "insecure-registries": [
      "172.xx.xxx.xx:8082"]
    }
    

然后重启docker服务:

systemctl daemon-reload; systemctl restart docker

这样,我们就可以从这个私有镜像仓库拉取镜像了。

使用仓库

根据以上配置,这个仓库就有一个可以使用的URL了,可以使用下面的命令直接pull上面push的镜像:

docker pull 172.xx.xxx.xxx:8082/centos:7.5.1804

以上行为会优先从本地仓库(docker-private)查找,没有再从代理仓库(docker-hub 和 google-containers)查找。
我们再来pull一个 google-containers的镜像看看:

docker pull 172.xx.xxx.xxx:8082/google-containers/kubernetes-dashboard-amd64:v1.8.3

image

成功了。

同理,我们可以拉取docker hub上的镜像,只要把url中的域名,替换成我们自己的域名就可以了。

最后我们看下镜像仓库的存储结构:

image

可以看到,Docker Hub的官方仓库镜像,被放到了libray 目录下,GCR的则新建了一个google-containers的目 录,而其他的用户的镜像,则会被放在用户名同名的目录下,目录名与远程目录一致。


配置反向代理

显然, 上上述方法并不友好。尤其是当我们已有的Kubernetes集群需要修改Docker配置时,我们不可能去重启docker服务。

第二个办法就是给这个仓库加上ssl证书,使之使用https协议来工作。

这里我们需要用到nginx来配置反向代理。

安装nginx

在centos7上,可以直接安装nginx v1.12.2,这个版本已经有tcp的代理功能了,不过我们此处不会用到。

yum -y install nginx
systemctl enable nginx
systemctl start nginx 

配置ssl证书

测试过程中,我们临时使用了个人注册的域名,并在Let's Encrypt申请了免费通配符证书。

由于docker不信任Let's Encrypt这个新秀CA,我们在配置nginx的ssl_certificate时,不能直接使用cert.pem证书,而要使用fullchain.pem证书。

yum安装的nginx,默认配置文件路径为:/etc/nginx/conf.d/

新建一个配置文件:vim /etc/nginx/conf.d/nexus_443_docker.conf

server {
    listen 443;
    ssl on;
    charset utf-8;
    client_max_body_size 2048M;
    server_name hub.some-awsome-site.cn;
    ssl_certificate      /etc/ssl/nexus/fullchain1.pem;
    ssl_certificate_key  /etc/ssl/nexus/privkey1.pem;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;

    access_log /var/log/nginx/nexus_ssl.access.log main;
    error_log /var/log/nginx/nexus_ssl.error.log warn;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
        proxy_connect_timeout    60;
        proxy_read_timeout       60;
        proxy_send_timeout       60;
        proxy_pass http://127.0.0.1:8082;
    }
} #End server

然后重启nginx服务:

nginx -t
nginx -s reload

现在,我们就可以直接使用域名来pull镜像了:

docker pull hub.some-awsome-site.cn/google-containers/kubernetes-dashboard-amd64:v1.8.3

值得一提的是,我们可以使用这个仓库从Docker Hub和 Google Cloud Registry下载镜像,如果我们把这个仓库地址设置成 docker的镜像仓库("registry-mirrors"),我们在执行docker pull时完全不需要指定我们自己的域名!

Docker客户端配置

鉴于我们已经有了group类型的代理仓库,我们可以直接通过该仓库从各大主流的docker镜像服务器上下载镜像了,并且我们配置了ssl证书,以前需要配置的insecure-registry配置也不用了。

撸起袖子直接用:

docker pull hub.some-awsome-site.cn/ubuntu:18.04

当然,如果你想再懒一点,连域名都不想要--也是可以的!

vim /etc/docker/daemon.json , 修改或添加如下配置:

{
  "registry-mirrors": ["https://hub.some-awsome-site.cn"]
}

然后你就可以这样用了:

docker pull ubuntu:18.04

image

如何Docker push

前面我们说了,这个group类型的仓库它是只读的,我们只能通过它来下载,不能提交自己的镜像,有解决办法么?

有!

我们创建的私有仓库启用了一个链接器,并监听到了8090端口,我们可以用nginx再配置一个8443的ssl端口,来代理这个私有仓库。

在nginx配置目录下新增一个配置文件:
vim /etc/nginx/conf.d/nexus_8090_docker-push.conf

server {
    listen 8443;
    ssl on;
    charset utf-8;
    client_max_body_size 2048M;
    server_name hub.some-awsome-site.cn;
    ssl_certificate      /etc/ssl/nexus/fullchain1.pem;
    ssl_certificate_key  /etc/ssl/nexus/privkey1.pem;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;

    access_log /var/log/nginx/nexus_8443.access.log main;
    error_log /var/log/nginx/nexus_8443.error.log warn;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
        proxy_connect_timeout    60;
        proxy_read_timeout       60;
        proxy_send_timeout       60;
        proxy_pass http://127.0.0.1:8090;
    }
} #End server

配置与上面的443端口配置无异,唯一差别是listenproxy_pass的端口不一样。

然后重启nginx服务:

nginx -t
nginx -s reload

然后们就可以先docker login,再直接使用域名来push镜像了:

docker login hub.some-awsome-site.cn:8443 -u admin -p admin123
docker tag ubuntu:18.04 hub.some-awsome-site.cn:8443/corp/org/ubuntu:18.04
docker push hub.some-awsome-site.cn:8443/corp/org/ubuntu:18.04

这里我们使用的默认账户admin,后面会针对各个项目提供专用的nexus账户,用来push docker镜像。


有什么好处

我们大费周章的配置这个docker仓库,有什么好处?

  • 私有仓库

    提供公司内部公共的docker私有镜像托管服务。

  • 加速镜像下载

    下载过的镜像都会被缓存在私服上,其它人员会直接从内部拉取,大大提高下载速度。

  • 无需单独设置fq
    不用再想方设法从google去下载镜像;

  • 节约出口带宽
    下载过的镜像都会被缓存在私服上,其它人员会直接从内部拉取,大大节约出口带宽。

  • 节约磁盘空间
    相同的镜像不在需要被重复存储在不同的服务器上,节约大量的磁盘资源。

  • 镜像聚合功能
    可以直接从该镜像服务器下载Google Cloud、Docker Hub等仓库的镜像,无需更改镜像域名。

  • 开箱即用
    只需要添加"registry-mirrors": ["https://hub.some-awsome-site.cn"]一条配置,即可开始使用。