原文鏈接:https://fuckcloudnative.io/posts/docker-registry-proxy/
在使用 Docker 和 Kubernetes 時,我們經常需要訪問 gcr.io
和 quay.io
鏡像倉庫,由於眾所周知的原因,這些鏡像倉庫在中國都無法訪問,唯一能訪問的是 Docker Hub,但速度也是奇慢無比。gcr.azk8s.cn
是 gcr.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.io
、gcr.io
、k8s.gcr.io
、quay.io
和 ghcr.io
等常見的公共鏡像倉庫,我們需要對 registry 的配置文件進行定制,Dockerfile 如下:
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
用來將環境變量傳入配置文件:
{{< expand "entrypoint.sh" >}}
#!/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 >}}
4. 啟動緩存服務
構建好 Docker 鏡像之后,就可以啟動服務了。如果你不想自己構建,可以直接用我的鏡像:yangchuansheng/registry-proxy
。
一般來說,即使你要同時緩存 docker.io
、gcr.io
、k8s.gcr.io
、quay.io
和 ghcr.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 有 Contour、Ambassador 和 Gloo 等,如果你對 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,Containerd
和 Podman
支持配置所有鏡像倉庫的 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.mirror
中 location
字段的 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
來存儲密碼。
-
為用戶
admin
創建一個密碼文件,密碼為admin
:🐳 → docker run \ --entrypoint htpasswd \ registry:2.6 -Bbn admin admin > htpasswd
-
創建 Secret:
🐳 → kubectl create secret generic registry-auth --from-file=htpasswd
-
修改資源清單的配置,以 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
-
嘗試拉取鏡像:
🐳 → 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
-
登錄鏡像倉庫:
🐳 → 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 元,以此來保障我每個月的服務器費用。當然肯定不止你一個人,目前大概有十幾個用戶,后面如果人數特別多,再考慮加服務器。這個需要你自己考慮清楚,有意者掃描下方的二維碼向我咨詢:
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的動態。