在使用 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
的鏡像倉庫代理站點。直接反代可以保證獲取到的鏡像是最新最全的,比緩存靠譜多了。
1. 前提條件
- 一台能夠施展魔法的服務器(你懂得,可以直接訪問 gcr.io)
- 一個域名和域名相關的 SSL 證書(docker pull 鏡像時需要驗證域名證書),一般用 Let's Encrypt 就夠了。
2. 代理方案
quay.io
和 Docker Hub
很好代理,可以直接使用 Envoy
的 host_rewrite_literal 參數(這是新版本的參數,如果你使用舊版本的 Envoy,參數應該是 host_rewrite),當 Envoy 將請求轉發給上游集群時,會直接將頭文件中的 host
改為指定的值。比如,如果上游集群是 quay.io,就將頭文件改為 quay.io
。我之前寫過的 使用 Envoy 反向代理谷歌搜索 用的就是此方案。什么?你是 Envoy 小白?莫慌,我已經為你們准備了一本 Envoy 從入門到放棄 的電子書,快快點擊下方的鏈接學習去吧(記得給我一個 star 哦)~~
gcr.io
稍微有點難辦,因為它在連接的時候需要二次認證,即使你通過反代服務器 pull 鏡像,它還是會再次訪問 gcr.io
進行驗證,然后才可以通過反代服務器 pull 鏡像。這就有點尷尬了,我特么要是能訪問 gcr.io,還要什么反代啊。。。話說 Docker 官方不是有一個 registry
鏡像嗎,可以通過設置參數 remoteurl
將其作為遠端倉庫的緩存倉庫,這樣當你通過這個私有倉庫的地址拉取鏡像時,regiistry 會先將鏡像緩存到本地存儲,然后再提供給拉取的客戶端(有可能這兩個步驟是同時的,我也不太清楚)。我們可以先部署一個私有 registry,然后將 remoteurl
設為 https://gcr.io
,最后再通過 Envoy 反代,基本上就可以了。
3. 代理配置
方案確定了之后,就可以動手配置了。還是使用我之前反復提到的方法:通過文件動態更新配置。如果你不是很清楚我在說什么,請參考 Envoy 基礎教程:基於文件系統動態更新配置。這里我就直接貼配置了。
bootstrap
配置:
# 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
的配置:
# 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 即將被廢棄,請奔走相告。
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
各個字段的含義我實在是懶得解釋,可以直接去看上面提到的電子書。
配置好了 Envoy 之后,就可以通過代理服務器拉取 Docker Hub
和 quay.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 Hub
、gcr.io
和 quay.io
的鏡像了,不需要修改任何前綴,Containerd 會根據配置自動選擇相應的代理 URL 拉取鏡像。
6. 費用評估
好了,現在我們來評估一下這一切的費用。首先你得有一個會魔法的服務器,國內的肯定不用考慮了,必須選擇國外的,而且到國內的速度還過得去的,最低最低不會低於 30 人民幣/月 吧。除此之外,你還得擁有一個個人域名,這個價格不好說,總而言之,加起來肯定不會低於 30 吧,多數人肯定是下不去這個手的。沒關系,我有一個更便宜的方案,我已經部署好了一切,你可以直接用我的服務,當然我也是自己買的服務器,每個月也是要花錢的,如果你真的想用,只需要每月支付 3 元,以此來保障我每個月的服務器費用。當然肯定不止你一個人,我可能還會賺點錢,但最多不允許超過 40 人,如果人數特別多,我可能會考慮加服務器。這個需要你自己考慮清楚,有意者掃描下方的二維碼加入交流群:
如果二維碼已過期,可以加我微信拉你進群。
微信公眾號
掃一掃下面的二維碼關注微信公眾號,在公眾號中回復◉加群◉即可加入我們的雲原生交流群,和孫宏亮、張館長、陽明等大佬一起探討雲原生技術