自动代理软件仓库镜像源,容器免替换镜像源
我一直有个痛点,在中国大陆环境启动 debian / ubuntu / alpine 容器拉取 apt / apk 包信息的时候,总是因为网络缓慢要等好久。
正常就会去 Ubuntu 软件仓库镜像使用帮助 - MirrorZ Help 改包管理器的镜像源,之前也调研过方便的手段 使用 mirror 协议为 Debian 选择 APT 镜像站,也有 RubyMetric/chsrc: chsrc 全平台通用换源工具与框架. Change Source everywhere for every software 这种工具可以一键换源。
能不能自动地拦截发往包管理平台的流量,跳转到镜像站呢。
今日调研结论是,可以,但只能 HTTP。
先在路由器的 DNS 把流量劫持到本地 Nginx 服务器,对 HTTP 流量 return 302,对 HTTPS 流量就只能原样转发到上游了。
实现也不难,主要是要避免 DNS 死循环。
我最终选择在主路由的 [MerlinClash](简介 | 魔法师云撸猫) 上完成这一套,毕竟大部分流量都经过它,而且主 DNS 也是它。
用 Docker 部署主要的服务
这次还学到 可以用模板文件来让 nginx 容器支持 stream block,只要 templates 目录有个带
stream {}的块
services:
alpine-redirect:
image: nginx:alpine
container_name: alpine-redirect
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./templates:/etc/nginx/templates一个是 80 端口的重定向
server {
listen 80;
server_name dl-cdn.alpinelinux.org;
location / {
return 302 http://mirrors.cernet.edu.cn$request_uri;
}
}另一个是 443 端口的流式转发。
这里给 proxy_pass 设置变量的特殊技巧,用来避免 nginx 启动时就记住了错误的域名解析结果,强制每次解析。
server {
listen 443;
resolver 1.1.1.1 ipv6=off;
# Using a variable in proxy_pass forces re-resolution of the DNS names
# https://serverfault.com/a/593003/48752
set $backend dl-cdn.alpinelinux.org:443;
proxy_pass $backend;
}还没完,在 MerlinClash 上还有设置。重点是两处
- hosts 用来把域名解析指向本地服务器,也可以用
/etc/hosts来实现。但在路由器上要持久化/etc/hosts的变更得写脚本,干脆让 clash DNS 来解析 - 如果启用了域名嗅探 sniffer 功能,一定要把相关域名加到忽略列表中,否则解析会死循环
hosts:
dl-cdn.alpinelinux.org: 192.168.2.62
sniffer:
...
skip-domain:
- 'dl-cdn.alpinelinux.org'然后就能验证效果了。容器如果默认就是 HTTP 源就不再需要修改软件仓库源了
$ dig dl-cdn.alpinelinux.org
NAME TYPE CLASS TTL ADDRESS NAMESERVER TIME TAKEN
dl-cdn.alpinelinux.org. A IN 9s 192.168.2.99 192.168.9.1:53 2ms
dl-cdn.alpinelinux.org. AAAA IN 9s ::ffff:192.168.2.99 192.168.9.1:53 2ms
$ curl -I http://dl-cdn.alpinelinux.org/alpine/v3.22/main/x86_64/APKINDEX.tar.gz
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.29.4
Date: Wed, 10 Dec 2025 14:56:12 GMT
Content-Type: text/html
Content-Length: 145
Connection: keep-alive
Location: http://mirrors.cernet.edu.cn/alpine/v3.22/main/x86_64/APKINDEX.tar.gz
$ curl -I https://dl-cdn.alpinelinux.org/alpine/v3.22/main/x86_64/APKINDEX.tar.gz
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 500103
Content-Security-Policy: script-src 'self'
Content-Type: application/octet-stream
Etag: "69391eaf-7a187"
Last-Modified: Wed, 10 Dec 2025 07:18:07 GMT
Referrer-Policy: origin-when-cross-origin
Server: nginx/1.27.5
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Via: 1.1 varnish, 1.1 varnish
Accept-Ranges: bytes
Date: Wed, 10 Dec 2025 14:56:07 GMT
Age: 12
X-Served-By: cache-ams21061-AMS, cache-sin-wsss1830023-SIN
X-Cache: HIT, HIT
X-Cache-Hits: 117, 2
X-Timer: S1765378567.125863,VS0,VE0
Vary: OriginNginx Docker 容器日志输出到 stdout
我说怎么 nginx.conf 配置文件里明明是 access_log /var/log/nginx/access.log,可日志内容还是输出到容器终端上了。原来是 /var/log/nginx/access.log -> /dev/stdout 文件有链接。
- docker - Nginx logs are not shown - Stack Overflow
- logging - Make a Docker application write to stdout - Server Fault
- docker-nginx/mainline/debian/Dockerfile at df5ce7be2ddbb1d0938a1b5dd89b1da22b3f048a · nginx/docker-nginx
# forward request and error logs to docker log collector
ln -sf /dev/stdout /var/log/nginx/access.log
ln -sf /dev/stderr /var/log/nginx/error.log