完全解决 gcr、quay、DockerHub 镜像下载难题

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

为了可以顺利访问 gcr.io 等镜像仓库,咱们须要在墙外本身搭建一个相似于 gcr.azk8s.cn 的镜像仓库代理站点。直接反代能够保证获取到的镜像是最新最全的,比缓存靠谱多了。nginx

1. 前提条件

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

2. 代理方案

quay.ioDocker Hub 很好代理,能够直接使用 Envoyhost_rewrite_literal 参数(这是新版本的参数,若是你使用旧版本的 Envoy,参数应该是 host_rewrite),当 Envoy 将请求转发给上游集群时,会直接将头文件中的 host 改成指定的值。好比,若是上游集群是 quay.io,就将头文件改成 quay.io。我以前写过的 使用 Envoy 反向代理谷歌搜索 用的就是此方案。什么?你是 Envoy 小白?莫慌,我已经为大家准备了一本 Envoy 从入门到放弃 的电子书,快快点击下方的连接学习去吧(记得给我一个 star 哦)~~git

gcr.io 稍微有点难办,由于它在链接的时候须要二次认证,即便你经过反代服务器 pull 镜像,它仍是会再次访问 gcr.io 进行验证,而后才能够经过反代服务器 pull 镜像。这就有点尴尬了,我特么要是能访问 gcr.io,还要什么反代啊。。。话说 Docker 官方不是有一个 registry 镜像吗,能够经过设置参数 remoteurl 将其做为远端仓库的缓存仓库,这样当你经过这个私有仓库的地址拉取镜像时,regiistry 会先将镜像缓存到本地存储,而后再提供给拉取的客户端(有可能这两个步骤是同时的,我也不太清楚)。咱们能够先部署一个私有 registry,而后将 remoteurl 设为 https://gcr.io,最后再经过 Envoy 反代,基本上就能够了。github

3. 代理配置

方案肯定了以后,就能够动手配置了。仍是使用我以前反复提到的方法:经过文件动态更新配置。若是你不是很清楚我在说什么,请参考 Envoy 基础教程:基于文件系统动态更新配置。这里我就直接贴配置了。docker

bootstrap 配置:json

# envoy.yaml
node:
  id: node0
  cluster: cluster0
dynamic_resources:
  lds_config:
    path: /etc/envoy/lds.yaml
  cds_config:
    path: /etc/envoy/cds.yaml
admin:
  access_log_path: "/dev/stdout"
  address:
    socket_address:
      address: "0.0.0.0"
      port_value: 15001复制代码

LDS 的配置:bootstrap

# lds.yaml
version_info: "0"
resources:
- "@type": type.googleapis.com/envoy.config.listener.v3.Listener
  name: listener_http
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 80
  filter_chains:
  - filters:
    - name: envoy.filters.network.http_connection_manager
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
        stat_prefix: ingress_http
        codec_type: AUTO
        access_log:
          name: envoy.access_loggers.file
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
            path: /dev/stdout
        route_config:
          name: http_route
          virtual_hosts:
          - name: default
            domains:
            - "*"
            routes:
            - match:
                prefix: "/"
              redirect:
                https_redirect: true
                port_redirect: 443
                response_code: "FOUND"
        http_filters:
        - name: envoy.filters.http.router
- "@type": type.googleapis.com/envoy.config.listener.v3.Listener
  name: listener_https
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 443
  listener_filters:
  - name: "envoy.filters.listener.tls_inspector"
    typed_config: {}
  filter_chains:
  - transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
        common_tls_context:
          alpn_protocols: h2,http/1.1
          tls_certificates:
          - certificate_chain:
              filename: "/root/.acme.sh/xxx.com/fullchain.cer"
            private_key:
              filename: "/root/.acme.sh/xxx.com/xxx.com.key"
    filters:
    - name: envoy.filters.network.http_connection_manager
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
        stat_prefix: ingress_https
        codec_type: AUTO
        use_remote_address: true
        access_log:
          name: envoy.access_loggers.file
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
            path: /dev/stdout
        route_config:
          name: https_route
          response_headers_to_add:
          - header:
              key: Strict-Transport-Security
              value: "max-age=15552000; includeSubdomains; preload"
          virtual_hosts:
          - name: gcr
            domains:
            - gcr.xxx.com
            routes:
            - match:
                prefix: "/"
              route:
                cluster: gcr
                timeout: 600s
          - name: quay
            domains:
            - quay.xxx.com
            routes:
            - match:
                prefix: "/"
              route:
                cluster: quay
                host_rewrite_literal: quay.io
          - name: docker
            domains:
            - docker.xxx.com
            routes:
            - match:
                prefix: "/"
              route:
                cluster: dockerhub
                host_rewrite_literal: registry-1.docker.io
        http_filters:
        - name: envoy.filters.http.router
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router复制代码

友情提醒:我这里使用的是 v3 版本的 API,v2 版本的 API 即将被废弃,请奔走相告。api

CDS 的配置:缓存

# cds.yaml
version_info: "0"
resources:
- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
  name: gcr
  connect_timeout: 1s
  type: strict_dns
  dns_lookup_family: V4_ONLY
  lb_policy: ROUND_ROBIN
  load_assignment:
    cluster_name: gcr
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: gcr.default
              port_value: 5000
- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
  name: dockerhub
  connect_timeout: 15s
  type: logical_dns
  dns_lookup_family: V4_ONLY
  dns_resolvers:
    socket_address:
      address: 8.8.8.8
      port_value: 53
  lb_policy: ROUND_ROBIN
  load_assignment:
    cluster_name: dockerhub
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: registry-1.docker.io
              port_value: 443
  transport_socket:
    name: envoy.transport_sockets.tls
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
      sni: registry-1.docker.io
- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
  name: quay
  connect_timeout: 15s
  type: logical_dns
  dns_lookup_family: V4_ONLY
  dns_resolvers:
    socket_address:
      address: 8.8.8.8
      port_value: 53
  lb_policy: ROUND_ROBIN
  load_assignment:
    cluster_name: quay
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: quay.io
              port_value: 443
  transport_socket:
    name: envoy.transport_sockets.tls
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
      sni: quay.io复制代码

各个字段的含义我实在是懒得解释,能够直接去看上面提到的电子书。bash

配置好了 Envoy 以后,就能够经过代理服务器拉取 Docker Hubquay.io 的镜像了。最后来解决 gcr.io 镜像的难题。

4. registry 配置

首先须要部署一个私有的 registry,若是你只有一台服务器(我想大多数人应该只会买一台吧),可使用 docker-compose,我这里是使用 Kubernetes 部署的,首先须要准备一个部署清单:

# registry-proxy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gcr
  labels:
    app: gcr
spec:
  replicas: 1 
  selector:
    matchLabels:
      app: gcr
  template:
    metadata:
      labels:
        app: gcr
    spec:
      nodeSelector:
        kubernetes.io/hostname: blog-k3s03
      tolerations:
      - key: node-role.kubernetes.io/ingress
        operator: Exists
        effect: NoSchedule
      hostNetwork: false 
      containers:
      - name: gcr
        image: findsec/registry-proxy:latest 
        env:
        - name: PROXY_REMOTE_URL
          value: https://gcr.io
        ports:
        - containerPort: 5000
          hostPort: 5000
          protocol: TCP
        volumeMounts:
        - mountPath: /etc/localtime
          name: localtime
        - mountPath: /var/lib/registry
          name: registry
      volumes:
      - name: localtime
        hostPath:
          path: /etc/localtime
      - name: registry
        hostPath:
          path: /var/lib/registry
---
apiVersion: v1
kind: Service
metadata:
  name: gcr
  labels:
    app: gcr
spec:
  sessionAffinity: ClientIP
  selector:
    app: gcr
  ports:
    - protocol: TCP
      name: http
      port: 5000 
      targetPort: 5000 复制代码

而后将其部署到 Kubernetes 集群中:

$ kubectl apply -f registry-proxy.yaml复制代码

如今再回过头来看看 Envoy 的配置中关于 gcr 的部分,先来看看 LDS

virtual_hosts:
- name: gcr
  domains:
  - gcr.xxx.com
  routes:
  - match:
      prefix: "/"
    route:
      cluster: gcr
      timeout: 600s复制代码

很简单,不须要解释,再来看看 CDS:

- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
  name: gcr
  connect_timeout: 1s
  type: strict_dns
  dns_lookup_family: V4_ONLY
  lb_policy: ROUND_ROBIN
  load_assignment:
    cluster_name: gcr
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: gcr.default
              port_value: 5000复制代码

这里的 address 使用的是 Kubernetes 集群内部域名,其余部署方式请本身斟酌。

最后,给服务器换个新内核,开启 BBR 加速不过度吧?否则你的镜像拉取仍然是龟速。

5. Docker 配置

如今你就能够经过代理服务器来拉取公共镜像了。

对于 Docker Hub 来讲,只须要将 docker.io 换成 docker.xxx.com 就好了,好比你想拉取 nginx:alpine 镜像,可使用下面的命令:

🐳 → docker pull docker.xxx.com/library/nginx:alpine复制代码

对于 quay.io 来讲,只须要将 quay.io 换成 quay.xxx.com 就好了,好比你想拉取 quay.io/coreos/kube-state-metrics:v1.5.0 镜像,可使用下面的命令:

🐳 → docker pull quay.xxx.com/coreos/kube-state-metrics:v1.5.0复制代码

对于 gcr.io 来讲,只须要将 gcr.io 换成 gcr.xxx.com 就好了,好比你想拉取 gcr.io/google-containers/etcd:3.2.24 镜像,可使用下面的命令:

🐳 → docker pull gcr.xxx.com/google-containers/etcd:3.2.24复制代码

固然,Docker 是能够设置 registry mirror 的,但只支持 Docker Hub。能够修改配置文件 /etc/docker/daemon.json,添加下面的内容:

{
    "registry-mirrors": [
        "https://docker.xxx.com"
    ]
}复制代码

而后重启 Docker 服务,就能够直接拉取 Docker Hub 的镜像了,不须要显示指定代理服务器的地址,Docker 服务自己会自动经过代理服务器去拉取镜像。好比:

🐳 → docker pull nginx:alpine
🐳 → docker pull docker.io/library/nginx:alpine复制代码

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

[plugins.cri.registry.mirrors]
  [plugins.cri.registry.mirrors."docker.io"]
    endpoint = ["https://docker.xxx.com"]
  [plugins.cri.registry.mirrors."quay.io"]
    endpoint = ["https://quay.xxx.com"]
  [plugins.cri.registry.mirrors."gcr.io"]
    endpoint = ["http://gcr.xxx.com"]复制代码

重启 Containerd 服务后,就能够直接拉取 Docker Hubgcr.ioquay.io 的镜像了,不须要修改任何前缀,Containerd 会根据配置自动选择相应的代理 URL 拉取镜像。

6. 费用评估

好了,如今咱们来评估一下这一切的费用。首先你得有一个会魔法的服务器,国内的确定不用考虑了,必须选择国外的,并且到国内的速度还过得去的,最低最低不会低于 30 人民币/月 吧。除此以外,你还得拥有一个我的域名,这个价格很差说,总而言之,加起来确定不会低于 30 吧,多数人确定是下不去这个手的。不要紧,我有一个更便宜的方案,我已经部署好了一切,你能够直接用个人服务,固然我也是本身买的服务器,每月也是要花钱的,若是你真的想用,只须要每个月支付 3 元,以此来保障我每月的服务器费用。固然确定不止你一我的,我可能还会赚点钱,但最多不容许超过 40 人,若是人数特别多,我可能会考虑加服务器。这个须要你本身考虑清楚,有意者扫描下方的二维码加入交流群:

若是二维码已过时,能够加我微信拉你进群。

相关文章
相关标签/搜索