Docker 鏡像加速教程

原文連接:fuckcloudnative.io/posts/docke…node

在使用 Docker 和 Kubernetes 時,咱們常常須要訪問 gcr.ioquay.io 鏡像倉庫,因爲衆所周知的緣由,這些鏡像倉庫在中國都沒法訪問,惟一能訪問的是 Docker Hub,但速度也是奇慢無比。gcr.azk8s.cngcr.io 鏡像倉庫的代理站點,原來能夠經過 gcr.azk8s.cn 訪問 gcr.io 倉庫裏的鏡像,可是目前 *.azk8s.cn 已經僅限於 Azure 中國的 IP 使用,再也不對外提供服務了。國內其餘的鏡像加速方案大多都是採用定時同步的方式來緩存,這種方法是有必定延遲的,不能保證及時更新,ustc 和七牛雲等鏡像加速器我都試過了,很是不靠譜,不少鏡像都沒有。nginx

爲了可以順利訪問 gcr.io 等鏡像倉庫,咱們須要在牆外本身搭建一個相似於 gcr.azk8s.cn 的鏡像倉庫代理站點。利用 Docker 的開源項目 registry 就能夠實現這個需求,registry 不只能夠做爲本地私有鏡像倉庫,還能夠做爲上游鏡像倉庫的緩存,也就是 pull through cachegit

先來感覺下速度:github

1. 前提條件


  • 一臺可以施展魔法的服務器(你懂得,能夠直接訪問 gcr.io)
  • 一個域名和域名相關的 SSL 證書(docker pull 鏡像時須要驗證域名證書),通常用 Let's Encrypt 就夠了。

2. 核心思路


registry 能夠經過設置參數 remoteurl 將其做爲遠端倉庫的緩存倉庫,這樣當你經過這個私有倉庫的地址拉取鏡像時,regiistry 會先將鏡像緩存到本地存儲,而後再提供給拉取的客戶端(有可能這兩個步驟是同時的,我也不太清楚)。咱們能夠先部署一個私有 registry,而後將 remoteurl 設爲須要加速的鏡像倉庫地址,基本上就能夠了。web

3. 定製 registry

爲了可以支持緩存 docker.iogcr.iok8s.gcr.ioquay.ioghcr.io 等常見的公共鏡像倉庫,咱們須要對 registry 的配置文件進行定製,Dockerfile 以下:docker

FROM registry:2.6
LABEL maintainer="registry-proxy Docker Maintainers https://fuckcloudnative.io"
ENV PROXY_REMOTE_URL="" \
    DELETE_ENABLED=""
COPY entrypoint.sh /entrypoint.sh
複製代碼

其中 entrypoint.sh 用來將環境變量傳入配置文件:json

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

#!/bin/sh

set -e

CONFIG_YML=/etc/docker/registry/config.yml

if [ -n "$PROXY_REMOTE_URL" -a `grep -c "$PROXY_REMOTE_URL" $CONFIG_YML` -eq 0 ]; then
    echo "proxy:" >> $CONFIG_YML
    echo " remoteurl: $PROXY_REMOTE_URL" >> $CONFIG_YML
    echo " username: $PROXY_USERNAME" >> $CONFIG_YML
    echo " password: $PROXY_PASSWORD" >> $CONFIG_YML
    echo "------ Enabled proxy to remote: $PROXY_REMOTE_URL ------"
elif [ $DELETE_ENABLED = true -a `grep -c "delete:" $CONFIG_YML` -eq 0 ]; then
    sed -i '/rootdirectory/a\ delete:' $CONFIG_YML
    sed -i '/delete/a\ enabled: true' $CONFIG_YML
    echo "------ Enabled local storage delete -----"
fi

sed -i "/headers/a\ Access-Control-Allow-Origin: ['*']" $CONFIG_YML
sed -i "/headers/a\ Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']" $CONFIG_YML
sed -i "/headers/a\ Access-Control-Expose-Headers: ['Docker-Content-Digest']" $CONFIG_YML

case "$1" in
    *.yaml|*.yml) set -- registry serve "$@" ;;
    serve|garbage-collect|help|-*) set -- registry "$@" ;;
esac

exec "$@"
複製代碼

{{< /expand >}}api

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" >}}

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dockerhub
  labels:
    app: dockerhub
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dockerhub
  template:
    metadata:
      labels:
        app: dockerhub
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - dockerhub
              topologyKey: kubernetes.io/hostname
            weight: 1
      dnsPolicy: None
      dnsConfig:
        nameservers:
          - 8.8.8.8
          - 8.8.4.4
      containers:
      - name: dockerhub
        image: yangchuansheng/registry-proxy:latest
        env:
        - name: PROXY_REMOTE_URL
          value: https://registry-1.docker.io
        - name: PROXY_USERNAME
          value: yangchuansheng
        - name: PROXY_PASSWORD
          value: ********
        ports:
        - containerPort: 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: dockerhub
  labels:
    app: dockerhub
spec:
  selector:
    app: dockerhub
  ports:
    - protocol: TCP
      name: http
      port: 5000
      targetPort: 5000
複製代碼

{{< /expand >}}

使用資源清單建立對應的服務:

🐳  → 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" >}}

apiVersion: apps/v1
kind: Deployment
metadata:
  name: envoy
  namespace: kube-system
  labels:
    app: envoy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: envoy
  strategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: envoy
    spec:
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet
      containers:
      - name: envoy
        image: envoyproxy/envoy:v1.17-latest
        imagePullPolicy: IfNotPresent
        command:
        - envoy
        - /etc/envoy/envoy.yaml
        ports:
        - containerPort: 443
          name: https
        - containerPort: 80
          name: http
        - containerPort: 15001
          name: http-metrics
        volumeMounts:
        - mountPath: /etc/localtime
          name: localtime
        - mountPath: /etc/envoy
          name: envoy
        - mountPath: /root/.acme.sh/fuckcloudnative.io
          name: ssl
      volumes:
      - name: localtime
        hostPath:
          path: /etc/localtime
      - name: ssl
        hostPath:
          path: /root/.acme.sh/fuckcloudnative.io
      - name: envoy
        hostPath:
          path: /etc/envoy
複製代碼

{{< /expand >}}

使用資源清單建立對應的服務:

🐳  → kubectl apply -f envoy.yaml
複製代碼

這裏選擇使用 hostPath 將 envoy 的配置掛載到容器中,而後經過文件來動態更新配置。來看下 Envoy 的配置,先進入 /etc/envoy 目錄。

bootstrap 配置:

{{< expand "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
複製代碼

{{< /expand >}}

LDS 的配置:

{{< expand "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/fuckcloudnative.io/fullchain.cer"
            private_key:
              filename: "/root/.acme.sh/fuckcloudnative.io/fuckcloudnative.io.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: docker
            domains:
            - docker.fuckcloudnative.io
            routes:
            - match:
                prefix: "/"
              route:
                cluster: dockerhub
                timeout: 600s
        http_filters:
        - name: envoy.filters.http.router
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router 
複製代碼

{{< /expand >}}

CDS 的配置:

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

version_info: "0"
resources:
- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
  name: dockerhub
  connect_timeout: 15s
  type: strict_dns
  dns_lookup_family: V4_ONLY
  lb_policy: ROUND_ROBIN
  load_assignment:
    cluster_name: dockerhub
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: dockerhub.default
              port_value: 5000
複製代碼

{{< /expand >}}

這裏的 address 使用的是 Kubernetes 集羣內部域名,其餘部署方式請本身斟酌。

配置好了 Envoy 以後,就能夠經過代理服務器拉取 docker.io 的鏡像了。

7. 驗證加速效果

如今你就能夠經過代理服務器來拉取公共鏡像了。好比你想拉取 nginx:alpine 鏡像,可使用下面的命令:

🐳  → docker pull docker.fuckcloudnative.io/library/nginx:alpine

alpine: Pulling from library/nginx
801bfaa63ef2: Pull complete
b1242e25d284: Pull complete
7453d3e6b909: Pull complete
07ce7418c4f8: Pull complete
e295e0624aa3: Pull complete
Digest: sha256:c2ce58e024275728b00a554ac25628af25c54782865b3487b11c21cafb7fabda
Status: Downloaded newer image for docker.fuckcloudnative.io/library/nginx:alpine
docker.fuckcloudnative.io/library/nginx:alpine
複製代碼

8. 緩存全部鏡像倉庫

前面的示例只是緩存了 docker.io,若是要緩存全部的公共鏡像倉庫,能夠參考 4-6 節的內容。以 k8s.gcr.io 爲例,先準備一個資源清單:

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

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gcr-k8s
  labels:
    app: gcr-k8s
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gcr-k8s
  template:
    metadata:
      labels:
        app: gcr-k8s
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - gcr-k8s
              topologyKey: kubernetes.io/hostname
            weight: 1
      dnsPolicy: None
      dnsConfig:
        nameservers:
          - 8.8.8.8
          - 8.8.4.4
      containers:
      - name: gcr-k8s
        image: yangchuansheng/registry-proxy:latest
        env:
        - name: PROXY_REMOTE_URL
          value: https://k8s.gcr.io
        ports:
        - containerPort: 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-k8s
  labels:
    app: gcr-k8s
spec:
  selector:
    app: gcr-k8s
  ports:
    - protocol: TCP
      name: http
      port: 5000
      targetPort: 5000
複製代碼

{{< /expand >}}

將其部署到 Kubernetes 集羣中:

🐳  → kubectl apply -f gcr-k8s.yaml
複製代碼

lds.yaml 中添加相關配置:

virtual_hosts:
          - name: docker
            ...
            ...
          - name: k8s
            domains:
            - k8s.fuckcloudnative.io
            routes:
            - match:
                prefix: "/"
              route:
                cluster: gcr-k8s
                timeout: 600s
複製代碼

cds.yaml 中添加相關配置:

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

其餘鏡像倉庫可照搬上述步驟,如下是我本身跑的全部緩存服務容器:

🐳  → kubectl get pod -o wide

gcr-8647ffb586-67c6g                     1/1     Running   0          21h     10.42.1.52    blog-k3s02
ghcr-7765f6788b-hxxvc                    1/1     Running   0          21h     10.42.1.55    blog-k3s01
dockerhub-94bbb7497-x4zwg                1/1     Running   0          21h     10.42.1.54    blog-k3s02
gcr-k8s-644db84879-7xssb                 1/1     Running   0          21h     10.42.1.53    blog-k3s01
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,添加下面的內容:

{
    "registry-mirrors": [
        "https://docker.fuckcloudnative.io"
    ]
}
複製代碼

而後重啓 Docker 服務,就能夠直接拉取 docker.io 的鏡像了,不須要顯示指定代理服務器的地址,Docker 服務自己會自動經過代理服務器去拉取鏡像。好比:

🐳 → docker pull nginx:alpine
🐳 → docker pull docker.io/library/nginx:alpine
複製代碼

Containerd

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

[plugins."io.containerd.grpc.v1.cri".registry]
      [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
          endpoint = ["https://docker.fuckcloudnative.io"]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
          endpoint = ["https://k8s.fuckcloudnative.io"]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"]
          endpoint = ["https://gcr.fuckcloudnative.io"]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."ghcr.io"]
          endpoint = ["https://ghcr.fuckcloudnative.io"]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"]
          endpoint = ["https://quay.fuckcloudnative.io"]
複製代碼

重啓 Containerd 服務後,就能夠直接拉取全部鏡像了,不須要修改任何前綴,Containerd 會根據配置自動選擇相應的代理 URL 拉取鏡像。

Podman

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

unqualified-search-registries = ['docker.io', 'k8s.gcr.io', 'gcr.io', 'ghcr.io', 'quay.io']

[[registry]]
prefix = "docker.io"
insecure = true
location = "registry-1.docker.io"

[[registry.mirror]]
location = "docker.fuckcloudnative.io"

[[registry]]
prefix = "k8s.gcr.io"
insecure = true
location = "k8s.gcr.io"

[[registry.mirror]]
location = "k8s.fuckcloudnative.io"

[[registry]]
prefix = "gcr.io"
insecure = true
location = "gcr.io"

[[registry.mirror]]
location = "gcr.fuckcloudnative.io"

[[registry]]
prefix = "ghcr.io"
insecure = true
location = "ghcr.io"

[[registry.mirror]]
location = "ghcr.fuckcloudnative.io"

[[registry]]
prefix = "quay.io"
insecure = true
location = "quay.io"

[[registry.mirror]]
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" >}}

apiVersion: apps/v1
kind: Deployment
metadata:
  name: reg-local
  labels:
    app: reg-local
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reg-local
  template:
    metadata:
      labels:
        app: reg-local
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - reg-local
              topologyKey: kubernetes.io/hostname
            weight: 1
      containers:
      - name: reg-local
        image: yangchuansheng/registry-proxy:latest
        env:
        - name: DELETE_ENABLED
          value: "true"
        ports:
        - containerPort: 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: reg-local
  labels:
    app: reg-local
spec:
  selector:
    app: reg-local
  ports:
    - protocol: TCP
      name: http
      port: 5000
      targetPort: 5000
複製代碼

{{< /expand >}}

將其部署到 Kubernetes 集羣中:

🐳  → kubectl apply -f reg-local.yaml
複製代碼

再準備一個 Docker Registry UI 的資源清單:

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

apiVersion: apps/v1
kind: Deployment
metadata:
  name: registry-ui
  labels:
    app: registry-ui
spec:
  replicas: 1
  selector:
    matchLabels:
      app: registry-ui
  template:
    metadata:
      labels:
        app: registry-ui
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - registry-ui
              topologyKey: kubernetes.io/hostname
            weight: 1
      tolerations:
      - key: node-role.kubernetes.io/ingress
        operator: Exists
        effect: NoSchedule
      containers:
      - name: registry-ui
        image: joxit/docker-registry-ui:static
        env:
        - name: REGISTRY_TITLE
          value: My Private Docker Registry
        - name: REGISTRY_URL
          value: "http://reg-local:5000"
        - name: DELETE_IMAGES
          value: "true"
        ports:
        - containerPort: 80
          protocol: TCP
        volumeMounts:
        - mountPath: /etc/localtime
          name: localtime
      volumes:
      - name: localtime
        hostPath:
          path: /etc/localtime
---
apiVersion: v1
kind: Service
metadata:
  name: registry-ui
  labels:
    app: registry-ui
spec:
  selector:
    app: registry-ui
  ports:
    - protocol: TCP
      name: http
      port: 80
      targetPort: 80
複製代碼

{{< /expand >}}

將其部署到 Kubernetes 集羣中:

🐳  → kubectl apply -f registry-ui.yaml
複製代碼

這樣就能夠經過 Dashboard 來清理鏡像釋放空間了。

或者直接簡單粗暴,定時刪除整個存儲目錄的內容。例如,執行命令 crontab -e,添加以下內容:

* * */2 * * /usr/bin/rm -rf /var/lib/registry/* &>/dev/null
複製代碼

表示每過兩天清理一次 /var/lib/registry/ 目錄。

11. 防白嫖認證

最後還有一個問題,我把緩存服務的域名所有公開了,若是你們都來白嫖,個人雲主機確定承受不住。爲了防止白嫖,我得給 registry-proxy 加個認證,最簡單的方法就是使用 basic auth,用 htpasswd 來存儲密碼。

  1. 爲用戶 admin 建立一個密碼文件,密碼爲 admin

    🐳 → docker run \
      --entrypoint htpasswd \
      registry:2.6 -Bbn admin admin > htpasswd
    複製代碼
  2. 建立 Secret:

    🐳 → kubectl create secret generic registry-auth --from-file=htpasswd
    複製代碼
  3. 修改資源清單的配置,以 docker.io 爲例:

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

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: dockerhub
      labels:
        app: dockerhub
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: dockerhub
      template:
        metadata:
          labels:
            app: dockerhub
        spec:
          affinity:
            podAntiAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - podAffinityTerm:
                  labelSelector:
                    matchExpressions:
                    - key: app
                      operator: In
                      values:
                      - dockerhub
                  topologyKey: kubernetes.io/hostname
                weight: 1
          dnsPolicy: None
          dnsConfig:
            nameservers:
              - 8.8.8.8
              - 8.8.4.4
          containers:
          - name: dockerhub
            image: yangchuansheng/registry-proxy:latest
            env:
            - name: PROXY_REMOTE_URL
              value: https://registry-1.docker.io
            - name: PROXY_USERNAME
              value: yangchuansheng
            - name: PROXY_PASSWORD
              value: ********
    +       - name: REGISTRY_AUTH_HTPASSWD_REALM
    +         value: Registry Realm
    +       - name: REGISTRY_AUTH_HTPASSWD_PATH
    +         value: /auth/htpasswd 
            ports:
            - containerPort: 5000
              protocol: TCP
            volumeMounts:
            - mountPath: /etc/localtime
              name: localtime
            - mountPath: /var/lib/registry
              name: registry
    +       - mountPath: /auth
    +         name: auth
          volumes:
          - name: localtime
            hostPath:
              path: /etc/localtime
          - name: registry
            hostPath:
              path: /var/lib/registry
    +     - name: auth
    +       secret:
    +         secretName: registry-auth
    複製代碼

    {{< /expand >}}

    apply 使其生效:

    🐳 → kubectl apply -f dockerhub.yaml
    複製代碼
  4. 嘗試拉取鏡像:

    🐳 → docker pull docker.fuckcloudnative.io/library/nginx:latest
    
    Error response from daemon: Get https://docker.fuckcloudnative.io/v2/library/nginx/manifests/latest: no basic auth credentials
    複製代碼
  5. 登陸鏡像倉庫:

    🐳 → docker login docker.fuckcloudnative.io
    Username: admin
    Password:
    WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
    Configure a credential helper to remove this warning. See
    https://docs.docker.com/engine/reference/commandline/login/#credentials-store
    
    Login Succeeded
    複製代碼

    如今就能夠正常拉取鏡像了。

若是你想更細粒度地控制權限,可使用 Token 的方式來進行認證,具體能夠參考 docker_auth 這個項目。

12. 費用評估

好了,如今咱們來評估一下這一切的費用。首先你得有一個會魔法的服務器,國內的確定不用考慮了,必須選擇國外的,並且到國內的速度還過得去的,最低最低不會低於 30 人民幣/月 吧。除此以外,你還得擁有一個我的域名,這個價格很差說,總而言之,加起來確定不會低於 30 吧,多數人確定是下不去這個手的。不要緊,我有一個更便宜的方案,我已經部署好了一切,你能夠直接用個人服務,固然我也是本身買的服務器,每月也是要花錢的,若是你真的想用,只須要每個月支付 3 元,以此來保障我每月的服務器費用。固然確定不止你一我的,目前大概有十幾個用戶,後面若是人數特別多,再考慮加服務器。這個須要你本身考慮清楚,有意者掃描下方的二維碼向我諮詢:

相關文章
相關標籤/搜索