构建Go项目Docker镜像报goproxy.cn i/o timeout错误
问题现象
VPN环境下执行make debian构建Docker镜像时持续报错:
go: github.com/PuerkitoBio/goquery@v1.8.0: Get "https://goproxy.cn/github.com/%21puerkito%21bio/goquery/@v/v1.8.0.mod": dial tcp: lookup goproxy.cn: i/o timeout
已在Dockerfile中配置Go代理相关环境变量,问题仍未解决。
相关配置如下:
Dockerfile
ARG BASE_IMAGE_ARCH="amd64" FROM ${BASE_IMAGE_ARCH}/golang:buster AS build # blocking github workaround ENV GO111MODULE=on ENV GOPROXY=https://goproxy.cn,direct ENV GOSUMDB="sum.golang.google.cn" ENV DEBIAN_FRONTEND noninteractive RUN apt-get update -q && \ apt-get install -y -qq build-essential devscripts dh-make dh-systemd && \ mkdir -p /build/debian && \ mkdir /pkg/ ADD . /src CMD ["/src/packaging/debian/build.sh"]
Makefile
APP := miniflux DOCKER_IMAGE := miniflux/miniflux VERSION := $(shell git describe --tags --abbrev=0) COMMIT := $(shell git rev-parse --short HEAD) BUILD_DATE := `date +%FT%T%z` LD_FLAGS := "-s -w -X 'miniflux.app/version.Version=$(VERSION)' -X 'miniflux.app/version.Commit=$(COMMIT)' -X 'miniflux.app/version.BuildDate=$(BUILD_DATE)'" PKG_LIST := $(shell go list ./... | grep -v /vendor/) DB_URL := postgres://postgres:postgres@localhost/miniflux_test?sslmode=disable DEB_IMG_ARCH := amd64 export PGPASSWORD := postgres .PHONY: \ miniflux \ linux-amd64 \ linux-arm64 \ linux-armv7 \ linux-armv6 \ linux-armv5 \ linux-x86 \ darwin-amd64 \ darwin-arm64 \ freebsd-amd64 \ freebsd-x86 \ openbsd-amd64 \ openbsd-x86 \ netbsd-x86 \ netbsd-amd64 \ windows-amd64 \ windows-x86 \ build \ run \ clean \ test \ lint \ integration-test \ clean-integration-test \ docker-image \ docker-image-distroless \ docker-images \ rpm \ debian \ debian-packages miniflux: @ go build -buildmode=pie -ldflags=$(LD_FLAGS) -o $(APP) main.go linux-amd64: @ GOOS=linux GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-amd64 main.go linux-arm64: @ GOOS=linux GOARCH=arm64 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-arm64 main.go linux-armv7: @ GOOS=linux GOARCH=arm GOARM=7 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-armv7 main.go linux-armv6: @ GOOS=linux GOARCH=arm GOARM=6 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-armv6 main.go linux-armv5: @ GOOS=linux GOARCH=arm GOARM=5 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-armv5 main.go darwin-amd64: @ GOOS=darwin GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-darwin-amd64 main.go darwin-arm64: @ GOOS=darwin GOARCH=arm64 go build -ldflags=$(LD_FLAGS) -o $(APP)-darwin-arm64 main.go freebsd-amd64: @ GOOS=freebsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-freebsd-amd64 main.go openbsd-amd64: @ GOOS=openbsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-openbsd-amd64 main.go windows-amd64: @ GOOS=windows GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-windows-amd64 main.go build: linux-amd64 linux-arm64 linux-armv7 linux-armv6 linux-armv5 darwin-amd64 darwin-arm64 freebsd-amd64 openbsd-amd64 windows-amd64 # NOTE: unsupported targets netbsd-amd64: @ GOOS=netbsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-netbsd-amd64 main.go linux-x86: @ GOOS=linux GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-x86 main.go freebsd-x86: @ GOOS=freebsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-freebsd-x86 main.go netbsd-x86: @ GOOS=netbsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-netbsd-x86 main.go openbsd-x86: @ GOOS=openbsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-freebsd-x86 main.go windows-x86: @ GOOS=windows GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-windows-x86 main.go run: @ LOG_DATE_TIME=1 go run main.go -debug clean: @ rm -f $(APP)-* $(APP) $(APP)*.rpm $(APP)*.deb test: go test -cover -race -count=1 ./... lint: golint -set_exit_status ${PKG_LIST} integration-test: psql -U postgres -c 'drop database if exists miniflux_test;' psql -U postgres -c 'create database miniflux_test;' go build -o miniflux-test main.go DATABASE_URL=$(DB_URL) \ ADMIN_USERNAME=admin \ ADMIN_PASSWORD=test123 \ CREATE_ADMIN=1 \ RUN_MIGRATIONS=1 \ DEBUG=1 \ ./miniflux-test >/tmp/miniflux.log 2>&1 & echo "$$!" > "/tmp/miniflux.pid" while ! nc -z localhost 8080; do sleep 1; done go test -v -tags=integration -count=1 miniflux.app/tests clean-integration-test: @ kill -9 `cat /tmp/miniflux.pid` @ rm -f /tmp/miniflux.pid /tmp/miniflux.log @ rm miniflux-test @ psql -U postgres -c 'drop database if exists miniflux_test;' docker-image: docker build -t $(DOCKER_IMAGE):$(VERSION) -f packaging/docker/alpine/Dockerfile . docker-image-distroless: docker build -t $(DOCKER_IMAGE):$(VERSION) -f packaging/docker/distroless/Dockerfile . docker-images: docker buildx build \ --platform linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 \ --file packaging/docker/alpine/Dockerfile \ --tag $(DOCKER_IMAGE):$(VERSION) \ --push . debian: @ docker build \ --build-arg BASE_IMAGE_ARCH=$(DEB_IMG_ARCH) \ -t $(DEB_IMG_ARCH)/miniflux-deb-builder \ -f packaging/debian/Dockerfile \ --network=host \ . @ docker run --rm \ -v ${PWD}:/pkg $(DEB_IMG_ARCH)/miniflux-deb-builder
根因分析
- 当前Dockerfile的所有RUN步骤仅完成系统依赖安装,Go依赖下载、编译动作是在容器启动执行
/src/packaging/debian/build.sh时才触发。虽然Dockerfile中配置的Go环境变量会生效,但docker run启动的容器默认不会继承宿主机VPN的代理规则、DNS配置,直接导致goproxy.cn域名解析失败。 - 即使build阶段加了
--network=host参数,在macOS/Windows桌面版Docker环境下,host网络模式也不会完全复用宿主机VPN的路由和DNS配置;Linux环境下如果VPN做了全局流量劫持,同样会出现容器内流量不通的问题。 - 现有配置没有把宿主机的代理环境变量透传到容器内部,容器所有出网流量不会走VPN通道。
修复步骤
按优先级依次尝试:
- 透传代理配置到构建、运行全流程
修改Makefile的debian目标,把宿主机代理变量透传到build和run流程,同时给run命令加上host网络模式:
debian: @ docker build \ --build-arg BASE_IMAGE_ARCH=$(DEB_IMG_ARCH) \ --build-arg HTTP_PROXY=$(HTTP_PROXY) \ --build-arg HTTPS_PROXY=$(HTTPS_PROXY) \ --build-arg NO_PROXY=$(NO_PROXY) \ -t $(DEB_IMG_ARCH)/miniflux-deb-builder \ -f packaging/debian/Dockerfile \ --network=host \ . @ docker run --rm \ --network=host \ -e HTTP_PROXY=$(HTTP_PROXY) \ -e HTTPS_PROXY=$(HTTPS_PROXY) \ -e NO_PROXY=$(NO_PROXY) \ -v ${PWD}:/pkg $(DEB_IMG_ARCH)/miniflux-deb-builder
对应修改Dockerfile开头,声明代理构建参数,让apt安装阶段也能走代理:
ARG BASE_IMAGE_ARCH="amd64" ARG HTTP_PROXY ARG HTTPS_PROXY ARG NO_PROXY FROM ${BASE_IMAGE_ARCH}/golang:buster AS build # blocking github workaround ENV GO111MODULE=on ENV GOPROXY=https://goproxy.cn,direct ENV GOSUMDB="sum.golang.google.cn" ENV HTTP_PROXY=${HTTP_PROXY} ENV HTTPS_PROXY=${HTTPS_PROXY} ENV NO_PROXY=${NO_PROXY} # 后续原有内容保持不变
执行构建前先在终端执行echo $HTTP_PROXY确认代理变量存在,再运行make debian。
- 显式指定容器DNS
如果透传代理后仍报域名解析错误,在docker run参数中加入公共DNS配置,绕过VPN下发的不可用DNS:
--dns=223.5.5.5 --dns=114.114.114.114
- 更换Go代理源
如果当前VPN路由规则屏蔽国内站点,直接把Dockerfile里的GOPROXY改为VPN可正常访问的源:
ENV GOPROXY=https://proxy.golang.org,direct
对应把GOSUMDB改为官方地址即可。
- 复用本地依赖缓存
如果网络始终不稳定,先在宿主机(已正常连通可拉取Go依赖的环境)执行go mod download把所有依赖下载到本地,再在docker run参数中加入挂载规则,把本地Go模块缓存挂到容器内,完全跳过容器内的依赖下载步骤:
-v $(go env GOMODCACHE):/go/pkg/mod
内容的提问来源于stack exchange,提问作者Jia
相关产品推荐
相关产品推荐

