原文链接:https://fuckcloudnative.io/posts/docker-registry-proxy/

在使用 Docker 和 Kubernetes 时,我们经常需要访问 gcr.ioquay.io 镜像仓库,由于众所周知的原因,这些镜像仓库在中国都无法访问,唯一能访问的是 Docker Hub,但速度也是奇慢无比。gcr.azk8s.cngcr.io 镜像仓库的代理站点,原来可以通过 gcr.azk8s.cn 访问 gcr.io 仓库里的镜像,但是目前 *.azk8s.cn 已经仅限于 Azure 中国的 IP 使用,不再对外提供服务了。国内其他的镜像加速方案大多都是采用定时同步的方式来缓存,这种方法是有一定延迟的,不能保证及时更新,ustc 和七牛云等镜像加速器我都试过了,非常不靠谱,很多镜像都没有。

为了能够顺利访问 gcr.io 等镜像仓库,我们需要在墙外自己搭建一个类似于 gcr.azk8s.cn 的镜像仓库代理站点。利用 Docker 的开源项目 registry 就可以实现这个需求,registry 不仅可以作为本地私有镜像仓库,还可以作为上游镜像仓库的缓存,也就是 pull through cache

先来感受下速度:

1. 前提条件


  • 一台能够施展魔法的服务器(你懂得,可以直接访问 gcr.io)
  • 一个域名和域名相关的 SSL 证书(docker pull 镜像时需要验证域名证书),一般用 Let's Encrypt 就够了。

2. 核心思路


registry 可以通过设置参数 remoteurl 将其作为远端仓库的缓存仓库,这样当你通过这个私有仓库的地址拉取镜像时,regiistry 会先将镜像缓存到本地存储,然后再提供给拉取的客户端(有可能这两个步骤是同时的,我也不太清楚)。我们可以先部署一个私有 registry,然后将 remoteurl 设为需要加速的镜像仓库地址,基本上就可以了。

3. 定制 registry

为了能够支持缓存 docker.iogcr.iok8s.gcr.ioquay.ioghcr.io 等常见的公共镜像仓库,我们需要对 registry 的配置文件进行定制,Dockerfile 如下:

  1. FROM registry:2.6
  2. LABEL maintainer="registry-proxy Docker Maintainers https://fuckcloudnative.io"
  3. ENV PROXY_REMOTE_URL="" \
  4. DELETE_ENABLED=""
  5. COPY entrypoint.sh /entrypoint.sh

其中 entrypoint.sh 用来将环境变量传入配置文件:

{{< expand "entrypoint.sh" >}}

  1. #!/bin/sh
  2. set -e
  3. CONFIG_YML=/etc/docker/registry/config.yml
  4. if [ -n "$PROXY_REMOTE_URL" -a `grep -c "$PROXY_REMOTE_URL" $CONFIG_YML` -eq 0 ]; then
  5. echo "proxy:" >> $CONFIG_YML
  6. echo " remoteurl: $PROXY_REMOTE_URL" >> $CONFIG_YML
  7. echo " username: $PROXY_USERNAME" >> $CONFIG_YML
  8. echo " password: $PROXY_PASSWORD" >> $CONFIG_YML
  9. echo "------ Enabled proxy to remote: $PROXY_REMOTE_URL ------"
  10. elif [ $DELETE_ENABLED = true -a `grep -c "delete:" $CONFIG_YML` -eq 0 ]; then
  11. sed -i '/rootdirectory/a\ delete:' $CONFIG_YML
  12. sed -i '/delete/a\ enabled: true' $CONFIG_YML
  13. echo "------ Enabled local storage delete -----"
  14. fi
  15. sed -i "/headers/a\ Access-Control-Allow-Origin: ['*']" $CONFIG_YML
  16. sed -i "/headers/a\ Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']" $CONFIG_YML
  17. sed -i "/headers/a\ Access-Control-Expose-Headers: ['Docker-Content-Digest']" $CONFIG_YML
  18. case "$1" in
  19. *.yaml|*.yml) set -- registry serve "$@" ;;
  20. serve|garbage-collect|help|-*) set -- registry "$@" ;;
  21. esac
  22. exec "$@"

{{< /expand >}}

4. 启动缓存服务

构建好 Docker 镜像之后,就可以启动服务了。如果你不想自己构建,可以直接用我的镜像:yangchuansheng/registry-proxy

一般来说,即使你要同时缓存 docker.iogcr.iok8s.gcr.ioquay.ioghcr.io,一台 1C 2G 的云主机也足够了(前提是你不在上面跑其他的服务)。我的博客、评论服务和其他一堆乱七八糟的服务都要跑在云主机上,所以一台是不满足我的需求的,我直接买了两台腾讯云香港轻量级服务器。

既然买了两台,肯定得组个 k3s 集群啦,看主机名就知道我是用来干啥的。其中 2C 4G 作为 master 节点,1C 2G 作为 node 节点。

docker.io 为例,创建资源清单:

{{< expand "dockerhub.yaml" >}}

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: dockerhub
  5. labels:
  6. app: dockerhub
  7. spec:
  8. replicas: 1
  9. selector:
  10. matchLabels:
  11. app: dockerhub
  12. template:
  13. metadata:
  14. labels:
  15. app: dockerhub
  16. spec:
  17. affinity:
  18. podAntiAffinity:
  19. preferredDuringSchedulingIgnoredDuringExecution:
  20. - podAffinityTerm:
  21. labelSelector:
  22. matchExpressions:
  23. - key: app
  24. operator: In
  25. values:
  26. - dockerhub
  27. topologyKey: kubernetes.io/hostname
  28. weight: 1
  29. dnsPolicy: None
  30. dnsConfig:
  31. nameservers:
  32. - 8.8.8.8
  33. - 8.8.4.4
  34. containers:
  35. - name: dockerhub
  36. image: yangchuansheng/registry-proxy:latest
  37. env:
  38. - name: PROXY_REMOTE_URL
  39. value: https://registry-1.docker.io
  40. - name: PROXY_USERNAME
  41. value: yangchuansheng
  42. - name: PROXY_PASSWORD
  43. value: ********
  44. ports:
  45. - containerPort: 5000
  46. protocol: TCP
  47. volumeMounts:
  48. - mountPath: /etc/localtime
  49. name: localtime
  50. - mountPath: /var/lib/registry
  51. name: registry
  52. volumes:
  53. - name: localtime
  54. hostPath:
  55. path: /etc/localtime
  56. - name: registry
  57. hostPath:
  58. path: /var/lib/registry
  59. ---
  60. apiVersion: v1
  61. kind: Service
  62. metadata:
  63. name: dockerhub
  64. labels:
  65. app: dockerhub
  66. spec:
  67. selector:
  68. app: dockerhub
  69. ports:
  70. - protocol: TCP
  71. name: http
  72. port: 5000
  73. targetPort: 5000

{{< /expand >}}

使用资源清单创建对应的服务:

  1. kubectl apply -f dockerhub.yaml

如果你只有一台主机,可以使用 docker-compose 来编排容器,配置文件可以自己参考 k8s 的配置修改,本文就不赘述了。

5. 代理选择

如果只缓存 docker.io,可以直接将 registry-proxy 的端口改成 443,并添加 SSL 证书配置。如果要缓存多个公共镜像仓库,就不太推荐这么做了,因为 443 端口只有一个,多个 registry-proxy 服务不能共用一个端口,合理的做法是使用边缘代理服务根据域名来转发请求到不同的 registry-proxy 服务。

对于 Kubernetes 集群来说,Ingress Controller 即边缘代理,常见的 Ingress Controller 基本上都是由 Nginx 或者 Envoy 来实现。Envoy 虽为代理界新秀,但生而逢时,它的很多特性都是原生为云准备的,是真正意义上的 Cloud Native L7 代理和通信总线。比如它的服务发现和动态配置功能,与 Nginx 等代理的热加载不同,Envoy 可以通过 API 来实现其控制平面,控制平面可以集中服务发现,并通过 API 接口动态更新数据平面的配置,不需要重启数据平面的代理。不仅如此,控制平面还可以通过 API 将配置进行分层,然后逐层更新。

目前使用 Envoy 实现的 Ingress Controller 有 ContourAmbassadorGloo 等,如果你对 Envoy 比较感兴趣,并且想使用 Ingress Controller 作为边缘代理,可以试试 Contour。Ingress Controller 对底层做了抽象,屏蔽了很多细节,无法顾及到所有细节的配置,必然不会支持底层代理所有的配置项,所以我选择使用原生的 Envoy 来作为边缘代理。如果你是单机跑的 registry-proxy 服务,也可以试试 Envoy

6. 代理配置

首先创建 Envoy 的资源清单:

{{< expand "envoy.yaml" >}}

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: envoy
  5. namespace: kube-system
  6. labels:
  7. app: envoy
  8. spec:
  9. replicas: 2
  10. selector:
  11. matchLabels:
  12. app: envoy
  13. strategy:
  14. rollingUpdate:
  15. maxSurge: 0
  16. maxUnavailable: 1
  17. type: RollingUpdate
  18. template:
  19. metadata:
  20. labels:
  21. app: envoy
  22. spec:
  23. hostNetwork: true
  24. dnsPolicy: ClusterFirstWithHostNet
  25. containers:
  26. - name: envoy
  27. image: envoyproxy/envoy:v1.17-latest
  28. imagePullPolicy: IfNotPresent
  29. command:
  30. - envoy
  31. - /etc/envoy/envoy.yaml
  32. ports:
  33. - containerPort: 443
  34. name: https
  35. - containerPort: 80
  36. name: http
  37. - containerPort: 15001
  38. name: http-metrics
  39. volumeMounts:
  40. - mountPath: /etc/localtime
  41. name: localtime
  42. - mountPath: /etc/envoy
  43. name: envoy
  44. - mountPath: /root/.acme.sh/fuckcloudnative.io
  45. name: ssl
  46. volumes:
  47. - name: localtime
  48. hostPath:
  49. path: /etc/localtime
  50. - name: ssl
  51. hostPath:
  52. path: /root/.acme.sh/fuckcloudnative.io
  53. - name: envoy
  54. hostPath:
  55. path: /etc/envoy

{{< /expand >}}

使用资源清单创建对应的服务:

  1. kubectl apply -f envoy.yaml

这里选择使用 hostPath 将 envoy 的配置挂载到容器中,然后通过文件来动态更新配置。来看下 Envoy 的配置,先进入 /etc/envoy 目录。

bootstrap 配置:

{{< expand "envoy.yaml" >}}

  1. node:
  2. id: node0
  3. cluster: cluster0
  4. dynamic_resources:
  5. lds_config:
  6. path: /etc/envoy/lds.yaml
  7. cds_config:
  8. path: /etc/envoy/cds.yaml
  9. admin:
  10. access_log_path: "/dev/stdout"
  11. address:
  12. socket_address:
  13. address: "0.0.0.0"
  14. port_value: 15001

{{< /expand >}}

LDS 的配置:

{{< expand "lds.yaml" >}}

  1. version_info: "0"
  2. resources:
  3. - "@type": type.googleapis.com/envoy.config.listener.v3.Listener
  4. name: listener_http
  5. address:
  6. socket_address:
  7. address: 0.0.0.0
  8. port_value: 80
  9. filter_chains:
  10. - filters:
  11. - name: envoy.filters.network.http_connection_manager
  12. typed_config:
  13. "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
  14. stat_prefix: ingress_http
  15. codec_type: AUTO
  16. access_log:
  17. name: envoy.access_loggers.file
  18. typed_config:
  19. "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
  20. path: /dev/stdout
  21. route_config:
  22. name: http_route
  23. virtual_hosts:
  24. - name: default
  25. domains:
  26. - "*"
  27. routes:
  28. - match:
  29. prefix: "/"
  30. redirect:
  31. https_redirect: true
  32. port_redirect: 443
  33. response_code: "FOUND"
  34. http_filters:
  35. - name: envoy.filters.http.router
  36. - "@type": type.googleapis.com/envoy.config.listener.v3.Listener
  37. name: listener_https
  38. address:
  39. socket_address:
  40. address: 0.0.0.0
  41. port_value: 443
  42. listener_filters:
  43. - name: "envoy.filters.listener.tls_inspector"
  44. typed_config: {}
  45. filter_chains:
  46. - transport_socket:
  47. name: envoy.transport_sockets.tls
  48. typed_config:
  49. "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
  50. common_tls_context:
  51. alpn_protocols: h2,http/1.1
  52. tls_certificates:
  53. - certificate_chain:
  54. filename: "/root/.acme.sh/fuckcloudnative.io/fullchain.cer"
  55. private_key:
  56. filename: "/root/.acme.sh/fuckcloudnative.io/fuckcloudnative.io.key"
  57. filters:
  58. - name: envoy.filters.network.http_connection_manager
  59. typed_config:
  60. "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
  61. stat_prefix: ingress_https
  62. codec_type: AUTO
  63. use_remote_address: true
  64. access_log:
  65. name: envoy.access_loggers.file
  66. typed_config:
  67. "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
  68. path: /dev/stdout
  69. route_config:
  70. name: https_route
  71. response_headers_to_add:
  72. - header:
  73. key: Strict-Transport-Security
  74. value: "max-age=15552000; includeSubdomains; preload"
  75. virtual_hosts:
  76. - name: docker
  77. domains:
  78. - docker.fuckcloudnative.io
  79. routes:
  80. - match:
  81. prefix: "/"
  82. route:
  83. cluster: dockerhub
  84. timeout: 600s
  85. http_filters:
  86. - name: envoy.filters.http.router
  87. typed_config:
  88. "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

{{< /expand >}}

CDS 的配置:

{{< expand "cds.yaml" >}}

  1. version_info: "0"
  2. resources:
  3. - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
  4. name: dockerhub
  5. connect_timeout: 15s
  6. type: strict_dns
  7. dns_lookup_family: V4_ONLY
  8. lb_policy: ROUND_ROBIN
  9. load_assignment:
  10. cluster_name: dockerhub
  11. endpoints:
  12. - lb_endpoints:
  13. - endpoint:
  14. address:
  15. socket_address:
  16. address: dockerhub.default
  17. port_value: 5000

{{< /expand >}}

这里的 address 使用的是 Kubernetes 集群内部域名,其他部署方式请自己斟酌。

配置好了 Envoy 之后,就可以通过代理服务器拉取 docker.io 的镜像了。

7. 验证加速效果

现在你就可以通过代理服务器来拉取公共镜像了。比如你想拉取 nginx:alpine 镜像,可以使用下面的命令:

  1. docker pull docker.fuckcloudnative.io/library/nginx:alpine
  2. alpine: Pulling from library/nginx
  3. 801bfaa63ef2: Pull complete
  4. b1242e25d284: Pull complete
  5. 7453d3e6b909: Pull complete
  6. 07ce7418c4f8: Pull complete
  7. e295e0624aa3: Pull complete
  8. Digest: sha256:c2ce58e024275728b00a554ac25628af25c54782865b3487b11c21cafb7fabda
  9. Status: Downloaded newer image for docker.fuckcloudnative.io/library/nginx:alpine
  10. docker.fuckcloudnative.io/library/nginx:alpine

8. 缓存所有镜像仓库

前面的示例只是缓存了 docker.io,如果要缓存所有的公共镜像仓库,可以参考 4-6 节的内容。以 k8s.gcr.io 为例,先准备一个资源清单:

{{< expand "gcr-k8s.yaml" >}}

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: gcr-k8s
  5. labels:
  6. app: gcr-k8s
  7. spec:
  8. replicas: 1
  9. selector:
  10. matchLabels:
  11. app: gcr-k8s
  12. template:
  13. metadata:
  14. labels:
  15. app: gcr-k8s
  16. spec:
  17. affinity:
  18. podAntiAffinity:
  19. preferredDuringSchedulingIgnoredDuringExecution:
  20. - podAffinityTerm:
  21. labelSelector:
  22. matchExpressions:
  23. - key: app
  24. operator: In
  25. values:
  26. - gcr-k8s
  27. topologyKey: kubernetes.io/hostname
  28. weight: 1
  29. dnsPolicy: None
  30. dnsConfig:
  31. nameservers:
  32. - 8.8.8.8
  33. - 8.8.4.4
  34. containers:
  35. - name: gcr-k8s
  36. image: yangchuansheng/registry-proxy:latest
  37. env:
  38. - name: PROXY_REMOTE_URL
  39. value: https://k8s.gcr.io
  40. ports:
  41. - containerPort: 5000
  42. protocol: TCP
  43. volumeMounts:
  44. - mountPath: /etc/localtime
  45. name: localtime
  46. - mountPath: /var/lib/registry
  47. name: registry
  48. volumes:
  49. - name: localtime
  50. hostPath:
  51. path: /etc/localtime
  52. - name: registry
  53. hostPath:
  54. path: /var/lib/registry
  55. ---
  56. apiVersion: v1
  57. kind: Service
  58. metadata:
  59. name: gcr-k8s
  60. labels:
  61. app: gcr-k8s
  62. spec:
  63. selector:
  64. app: gcr-k8s
  65. ports:
  66. - protocol: TCP
  67. name: http
  68. port: 5000
  69. targetPort: 5000

{{< /expand >}}

将其部署到 Kubernetes 集群中:

  1. kubectl apply -f gcr-k8s.yaml

lds.yaml 中添加相关配置:

  1. virtual_hosts:
  2. - name: docker
  3. ...
  4. ...
  5. - name: k8s
  6. domains:
  7. - k8s.fuckcloudnative.io
  8. routes:
  9. - match:
  10. prefix: "/"
  11. route:
  12. cluster: gcr-k8s
  13. timeout: 600s

cds.yaml 中添加相关配置:

  1. - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
  2. name: gcr-k8s
  3. connect_timeout: 1s
  4. type: strict_dns
  5. dns_lookup_family: V4_ONLY
  6. lb_policy: ROUND_ROBIN
  7. load_assignment:
  8. cluster_name: gcr-k8s
  9. endpoints:
  10. - lb_endpoints:
  11. - endpoint:
  12. address:
  13. socket_address:
  14. address: gcr-k8s.default
  15. port_value: 5000

其他镜像仓库可照搬上述步骤,以下是我自己跑的所有缓存服务容器:

  1. kubectl get pod -o wide
  2. gcr-8647ffb586-67c6g 1/1 Running 0 21h 10.42.1.52 blog-k3s02
  3. ghcr-7765f6788b-hxxvc 1/1 Running 0 21h 10.42.1.55 blog-k3s01
  4. dockerhub-94bbb7497-x4zwg 1/1 Running 0 21h 10.42.1.54 blog-k3s02
  5. gcr-k8s-644db84879-7xssb 1/1 Running 0 21h 10.42.1.53 blog-k3s01
  6. quay-559b65848b-ljclb 1/1 Running 0 21h 10.42.0.154 blog-k3s01

9. 容器运行时配置

配置好所有的缓存服务后,就可以通过代理来拉取公共镜像了,只需按照下面的列表替换镜像地址中的字段就行了:

原 URL 替换后的 URL
docker.io/xxx/xxx 或 xxx/xxx docker.fuckcloudnative.io/xxx/xxx
docker.io/library/xxx 或 xxx docker.fuckcloudnative.io/library/xxx
gcr.io/xxx/xxx gcr.fuckcloudnative.io/xxx/xxx
k8s.gcr.io/xxx/xxx k8s.fuckcloudnative.io/xxx/xxx
quay.io/xxx/xxx quay.fuckcloudnative.io/xxx/xxx
ghcr.io/xxx/xxx ghcr.fuckcloudnative.io/xxx/xxx

当然,最好的方式还是直接配置 registry mirror,Docker 只支持配置 docker.io 的 registry mirror,ContainerdPodman 支持配置所有镜像仓库的 registry mirror。

Docker

Docker 可以修改配置文件 /etc/docker/daemon.json,添加下面的内容:

  1. {
  2. "registry-mirrors": [
  3. "https://docker.fuckcloudnative.io"
  4. ]
  5. }

然后重启 Docker 服务,就可以直接拉取 docker.io 的镜像了,不需要显示指定代理服务器的地址,Docker 服务本身会自动通过代理服务器去拉取镜像。比如:

  1. docker pull nginx:alpine
  2. docker pull docker.io/library/nginx:alpine

Containerd

Containerd 就比较简单了,它支持任意 registry 的 mirror,只需要修改配置文件 /etc/containerd/config.toml,添加如下的配置:

  1. [plugins."io.containerd.grpc.v1.cri".registry]
  2. [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
  3. [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
  4. endpoint = ["https://docker.fuckcloudnative.io"]
  5. [plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
  6. endpoint = ["https://k8s.fuckcloudnative.io"]
  7. [plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"]
  8. endpoint = ["https://gcr.fuckcloudnative.io"]
  9. [plugins."io.containerd.grpc.v1.cri".registry.mirrors."ghcr.io"]
  10. endpoint = ["https://ghcr.fuckcloudnative.io"]
  11. [plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"]
  12. endpoint = ["https://quay.fuckcloudnative.io"]

重启 Containerd 服务后,就可以直接拉取所有镜像了,不需要修改任何前缀,Containerd 会根据配置自动选择相应的代理 URL 拉取镜像。

Podman

Podman 也支持任意 registry 的 mirror,只需要修改配置文件 /etc/containers/registries.conf,添加如下的配置:

  1. unqualified-search-registries = ['docker.io', 'k8s.gcr.io', 'gcr.io', 'ghcr.io', 'quay.io']
  2. [[registry]]
  3. prefix = "docker.io"
  4. insecure = true
  5. location = "registry-1.docker.io"
  6. [[registry.mirror]]
  7. location = "docker.fuckcloudnative.io"
  8. [[registry]]
  9. prefix = "k8s.gcr.io"
  10. insecure = true
  11. location = "k8s.gcr.io"
  12. [[registry.mirror]]
  13. location = "k8s.fuckcloudnative.io"
  14. [[registry]]
  15. prefix = "gcr.io"
  16. insecure = true
  17. location = "gcr.io"
  18. [[registry.mirror]]
  19. location = "gcr.fuckcloudnative.io"
  20. [[registry]]
  21. prefix = "ghcr.io"
  22. insecure = true
  23. location = "ghcr.io"
  24. [[registry.mirror]]
  25. location = "ghcr.fuckcloudnative.io"
  26. [[registry]]
  27. prefix = "quay.io"
  28. insecure = true
  29. location = "quay.io"
  30. [[registry.mirror]]
  31. location = "quay.fuckcloudnative.io"

然后就可以直接拉取所有镜像了,不需要修改任何前缀,Podman 会根据配置自动选择相应的代理 URL 拉取镜像。而且 Podman 还有 fallback 机制,上面的配置表示先尝试通过 registry.mirrorlocation 字段的 URL 来拉取镜像,如果失败就会尝试通过 registry 中 location 字段的 URL 来拉取。

10. 清理缓存

缓存服务会将拉取的镜像缓存到本地,所以需要消耗磁盘容量。一般云主机的磁盘容量都不是很大,OSS 和 s3 存储都比较贵,不太划算。

为了解决这个问题,我推荐定期删除缓存到本地磁盘的部分镜像,或者删除所有镜像。方法也比较简单,单独再部署一个 registry,共用其他 registry 的存储,并启用 delete 功能,然后再通过 API 或者 Dashboard 进行删除。

先准备一个资源清单:

{{< expand "reg-local.yaml" >}}

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: reg-local
  5. labels:
  6. app: reg-local
  7. spec:
  8. replicas: 1
  9. selector:
  10. matchLabels:
  11. app: reg-local
  12. template:
  13. metadata:
  14. labels:
  15. app: reg-local
  16. spec:
  17. affinity:
  18. podAntiAffinity:
  19. preferredDuringSchedulingIgnoredDuringExecution:
  20. - podAffinityTerm:
  21. labelSelector:
  22. matchExpressions:
  23. - key: app
  24. operator: In
  25. values:
  26. - reg-local
  27. topologyKey: kubernetes.io/hostname
  28. weight: 1
  29. containers:
  30. - name: reg-local
  31. image: yangchuansheng/registry-proxy:latest
  32. env:
  33. - name: DELETE_ENABLED
  34. value: "true"
  35. ports:
  36. - containerPort: 5000
  37. protocol: TCP
  38. volumeMounts:
  39. - mountPath: /etc/localtime
  40. name: localtime
  41. - mountPath: /var/lib/registry
  42. name: registry
  43. volumes:
  44. - name: localtime
  45. hostPath:
  46. path: /etc/localtime
  47. - name: registry
  48. hostPath:
  49. path: /var/lib/registry
  50. ---
  51. apiVersion: v1
  52. kind: Service
  53. metadata:
  54. name: reg-local
  55. labels:
  56. app: reg-local
  57. spec:
  58. selector:
  59. app: reg-local
  60. ports:
  61. - protocol: TCP
  62. name: http
  63. port: 5000
  64. targetPort: 5000

{{< /expand >}}

将其部署到 Kubernetes 集群中:

  1. kubectl apply -f reg-local.yaml

再准备一个 Docker Registry UI 的资源清单:

{{< expand "registry-ui.yaml" >}}

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: registry-ui
  5. labels:
  6. app: registry-ui
  7. spec:
  8. replicas: 1
  9. selector:
  10. matchLabels:
  11. app: registry-ui
  12. template:
  13. metadata:
  14. labels:
  15. app: registry-ui
  16. spec:
  17. affinity:
  18. podAntiAffinity:
  19. preferredDuringSchedulingIgnoredDuringExecution:
  20. - podAffinityTerm:
  21. labelSelector:
  22. matchExpressions:
  23. - key: app
  24. operator: In
  25. values:
  26. - registry-ui
  27. topologyKey: kubernetes.io/hostname
  28. weight: 1
  29. tolerations:
  30. - key: node-role.kubernetes.io/ingress
  31. operator: Exists
  32. effect: NoSchedule
  33. containers:
  34. - name: registry-ui
  35. image: joxit/docker-registry-ui:static
  36. env:
  37. - name: REGISTRY_TITLE
  38. value: My Private Docker Registry
  39. - name: REGISTRY_URL
  40. value: "http://reg-local:5000"
  41. - name: DELETE_IMAGES
  42. value: "true"
  43. ports:
  44. - containerPort: 80
  45. protocol: TCP
  46. volumeMounts:
  47. - mountPath: /etc/localtime
  48. name: localtime
  49. volumes:
  50. - name: localtime
  51. hostPath:
  52. path: /etc/localtime
  53. ---
  54. apiVersion: v1
  55. kind: Service
  56. metadata:
  57. name: registry-ui
  58. labels:
  59. app: registry-ui
  60. spec:
  61. selector:
  62. app: registry-ui
  63. ports:
  64. - protocol: TCP
  65. name: http
  66. port: 80
  67. targetPort: 80

{{< /expand >}}

将其部署到 Kubernetes 集群中:

  1. kubectl apply -f registry-ui.yaml

这样就可以通过 Dashboard 来清理镜像释放空间了。

或者直接简单粗暴,定时删除整个存储目录的内容。例如,执行命令 crontab -e,添加如下内容:

  1. * * */2 * * /usr/bin/rm -rf /var/lib/registry/* &>/dev/null

表示每过两天清理一次 /var/lib/registry/ 目录。

11. 防白嫖认证

最后还有一个问题,我把缓存服务的域名全部公开了,如果大家都来白嫖,我的云主机肯定承受不住。为了防止白嫖,我得给 registry-proxy 加个认证,最简单的方法就是使用 basic auth,用 htpasswd 来存储密码。

  1. 为用户 admin 创建一个密码文件,密码为 admin

    1. docker run \
    2. --entrypoint htpasswd \
    3. registry:2.6 -Bbn admin admin > htpasswd
  2. 创建 Secret:

    1. kubectl create secret generic registry-auth --from-file=htpasswd
  3. 修改资源清单的配置,以 docker.io 为例:

    {{< expand "dockerhub.yaml" >}}

    1. apiVersion: apps/v1
    2. kind: Deployment
    3. metadata:
    4. name: dockerhub
    5. labels:
    6. app: dockerhub
    7. spec:
    8. replicas: 1
    9. selector:
    10. matchLabels:
    11. app: dockerhub
    12. template:
    13. metadata:
    14. labels:
    15. app: dockerhub
    16. spec:
    17. affinity:
    18. podAntiAffinity:
    19. preferredDuringSchedulingIgnoredDuringExecution:
    20. - podAffinityTerm:
    21. labelSelector:
    22. matchExpressions:
    23. - key: app
    24. operator: In
    25. values:
    26. - dockerhub
    27. topologyKey: kubernetes.io/hostname
    28. weight: 1
    29. dnsPolicy: None
    30. dnsConfig:
    31. nameservers:
    32. - 8.8.8.8
    33. - 8.8.4.4
    34. containers:
    35. - name: dockerhub
    36. image: yangchuansheng/registry-proxy:latest
    37. env:
    38. - name: PROXY_REMOTE_URL
    39. value: https://registry-1.docker.io
    40. - name: PROXY_USERNAME
    41. value: yangchuansheng
    42. - name: PROXY_PASSWORD
    43. value: ********
    44. + - name: REGISTRY_AUTH_HTPASSWD_REALM
    45. + value: Registry Realm
    46. + - name: REGISTRY_AUTH_HTPASSWD_PATH
    47. + value: /auth/htpasswd
    48. ports:
    49. - containerPort: 5000
    50. protocol: TCP
    51. volumeMounts:
    52. - mountPath: /etc/localtime
    53. name: localtime
    54. - mountPath: /var/lib/registry
    55. name: registry
    56. + - mountPath: /auth
    57. + name: auth
    58. volumes:
    59. - name: localtime
    60. hostPath:
    61. path: /etc/localtime
    62. - name: registry
    63. hostPath:
    64. path: /var/lib/registry
    65. + - name: auth
    66. + secret:
    67. + secretName: registry-auth

    {{< /expand >}}

    apply 使其生效:

    1. kubectl apply -f dockerhub.yaml
  4. 尝试拉取镜像:

    1. docker pull docker.fuckcloudnative.io/library/nginx:latest
    2. Error response from daemon: Get https://docker.fuckcloudnative.io/v2/library/nginx/manifests/latest: no basic auth credentials
  5. 登录镜像仓库:

    1. docker login docker.fuckcloudnative.io
    2. Username: admin
    3. Password:
    4. WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
    5. Configure a credential helper to remove this warning. See
    6. https://docs.docker.com/engine/reference/commandline/login/#credentials-store
    7. Login Succeeded

    现在就可以正常拉取镜像了。

如果你想更细粒度地控制权限,可以使用 Token 的方式来进行认证,具体可以参考 docker_auth 这个项目。

12. 费用评估

好了,现在我们来评估一下这一切的费用。首先你得有一个会魔法的服务器,国内的肯定不用考虑了,必须选择国外的,而且到国内的速度还过得去的,最低最低不会低于 30 人民币/月 吧。除此之外,你还得拥有一个个人域名,这个价格不好说,总而言之,加起来肯定不会低于 30 吧,多数人肯定是下不去这个手的。没关系,我有一个更便宜的方案,我已经部署好了一切,你可以直接用我的服务,当然我也是自己买的服务器,每个月也是要花钱的,如果你真的想用,只需要每月支付 3 元,以此来保障我每个月的服务器费用。当然肯定不止你一个人,目前大概有十几个用户,后面如果人数特别多,再考虑加服务器。这个需要你自己考虑清楚,有意者扫描下方的二维码向我咨询:


Kubernetes 1.18.2 1.17.5 1.16.9 1.15.12离线安装包发布地址http://store.lameleg.com ,欢迎体验。 使用了最新的sealos v3.3.6版本。 作了主机名解析配置优化,lvscare 挂载/lib/module解决开机启动ipvs加载问题, 修复lvscare社区netlink与3.10内核不兼容问题,sealos生成百年证书等特性。更多特性 https://github.com/fanux/sealos 。欢迎扫描下方的二维码加入钉钉群 ,钉钉群已经集成sealos的机器人实时可以看到sealos的动态。

Docker 镜像加速教程的更多相关文章

  1. Centos7.0 配置docker 镜像加速

    在Docker Hub官网上注册帐号,即可下载使用仓库里的全部的docker镜像.而因为网络原因,国内的开发者没办法流畅的下载镜像,经常会出现下载中断的错误.解决方法就是使用国内的容器Hub加速服务, ...

  2. Docker 镜像制作教程:针对不同语言的精简策略

    本系列文章将分为三个部分: 第一部分着重介绍多阶段构建(multi-stage builds),因为这是镜像精简之路至关重要的一环.在这部分内容中,我会解释静态链接和动态链接的区别,它们对镜像带来的影 ...

  3. docker镜像加速,docker更换为国内镜像

    docker镜像加速,docker更换为国内镜像 一.使用官方镜像 二.Docker守护进程配置加速器 相关博文原文地址: CSDN:让我思考一下 :docker更换为国内镜像 一.使用官方镜像 Do ...

  4. Centos7服务器安装Docker及Docker镜像加速,Docker删除

    Centos7服务器安装Docker及Docker镜像加速,Docker删除 1.Centos7服务器安装Docker 1.1 root账户登录,查看内核版本如下 1.1.1 卸载服务器旧版本Dock ...

  5. Centos7安装Docker&镜像加速

    目录 Docker Docker安装 方式一 方式二 docker 镜像加速 Docker Docker安装 Docker安装 方式一 step1: 删除老版本(Uninstall old versi ...

  6. [第五篇]——Docker 镜像加速之Spring Cloud直播商城 b2b2c电子商务技术总结

    Docker 镜像加速 国内从 DockerHub 拉取镜像有时会遇到困难,此时可以配置镜像加速器.Docker 官方和国内很多云服务商都提供了国内加速器服务,例如: 科大镜像: 网易: 阿里云: 你 ...

  7. learning docker steps(6) ----- docker 镜像加速

    http://www.docker-cn.com/registry-mirror 通过 Docker 官方镜像加速,中国区用户能够快速访问最流行的 Docker 镜像.该镜像托管于中国大陆,本地用户现 ...

  8. 使用阿里云Docker镜像加速

    使用docker官方的docker hub速度太慢,正好看到国内阿里云也做了docker镜像,于是想试试看阿里云的docker源.先附上 阿里云docker hub地址 .新用户需要注册成为开发者.打 ...

  9. Docker 镜像加速

    通过 Docker 官方镜像加速,中国区用户能够快速访问最流行的 Docker 镜像.该镜像托管于中国大陆,本地用户现在将会享受到更快的下载速度和更强的稳定性,从而能够更敏捷地开发和交付 Docker ...

随机推荐

  1. C语言之数据在内存中的存储

    C语言之数据在内存中的存储 在我们学习此之前,我们先来回忆一下C语言中都有哪些数据类型呢? 首先我们来看看C语言中的基本的内置类型: char //字符数据类型 short //短整型 int //整 ...

  2. Neeto-Vue:我为了记笔记,手写了一个为知笔记客户端

    构建自托管的笔记系统 这两年各种笔记系统快速井喷,好像谁都能来掺一脚,app store随便搜索一个关键字就会有一大堆的结果,从老牌的印象笔记,Bear,MWeb,有道云笔记再到新星专注笔记之类,从买 ...

  3. Eclipse无法打开提示could not open jvm.cfg错误

    先言:我的问题只是出在之前我的java_jdk原来安装在F:\Temp这里,但是我之后在整理文件的时候把这个文件夹改成了F:\java_jdk.所以导致了错误的出现 解决方法1: 因为我的只是目录名称 ...

  4. zoj3623 Battle Ships

    Battle Ships is a new game which is similar to Star Craft. In this game, the enemy builds a defense ...

  5. Codeforces 11D A Simple Task 统计简单无向图中环的个数(非原创)

    太难了,学不会.看了两天都会背了,但是感觉题目稍微变下就不会了.dp还是摸不到路子. 附ac代码: 1 #include<iostream> 2 #include<cstdio> ...

  6. mybaits(七)spring整合mybaits

    与 Spring 整合分析 http://www.mybatis.org/spring/zh/index.html 这里我们以传统的 Spring 为例,因为配置更直观,在 Spring 中使用配置类 ...

  7. JVM终结篇

    1.1 重新认知JVM 之前我们画过一张图,是从Class文件到类装载器,再到运行时数据区的过程.现在咱们把这张图不妨丰富完善一下,展示了JVM的大体物理结构图. 1.2 GC优化 内存被使用了之后, ...

  8. HDU 3920 Clear All of Them I(状压DP)题解

    题意:2n个点,一个起点,开n枪,每枪必须打两个点,花费为起点到其中一点距离加上两点距离.问打完2n个点的最小花费. 思路:很显然应该dp状态,然后枚举i j两个空位置去填,那么复杂度$O(20 * ...

  9. LOJ6285 数列分块入门9(分块 区间众数)题解

    题意:给出区间内的最小众数 思路:分块,离散化每个数,开vector记录每个数p出现的位置,这样就能二分出L,R以内p的个数了.众数有一个性质,用mode(a)表示集合a的众数,那么mode(a∪b) ...

  10. IE & 自定义滚动条 & scroll

    IE & 自定义滚动条 & scroll 请问这种在 IE 下的自定义滚动条,是如何实现的? https://fairyever.gitee.io/d2-admin-preview/# ...