August 12, 2020

近期使用Docker打包镜像遇到的问题总结

总结最近使用docker遇到的问题和解决办法。

背景

github.com/yeqown/goreportcard 项目中我改造了 goreportcard。 后续为了方便部署,我准备将其打包成为docker镜像并上传到 DockerHub。期间遇到了下面的问题,并一一解决,这里做一个记录帮助以后遇到类似的问题可以快速解决。

初期的目标是:将goreportcardgolangci-lint编译好,尽可能较小镜像的体积。因此第一次尝试,我使用了分阶段编译,用golang:1.14.1编译,alpine来发布。

基本 Dockerfile 如下:

# building stage
FROM golang:1.14-alpine3.11 as build
WORKDIR /tmp/build

COPY . .
RUN export GOPROXY="https://goproxy.cn,direct" \
    && go mod download \
    && go build -o app ./cmd/goreportcard-cli/ \
    && go get github.com/golangci/golangci-lint && go install github.com/golangci/golangci-lint/cmd/golangci-lint

# release stage
FROM golang:1.14-alpine3.11 as release
WORKDIR /app/goreportcard

COPY --from=build /tmp/build/app .
COPY --from=build /tmp/build/tpl ./tpl
COPY --from=build /tmp/build/assets ./assets

# FIXED: 不能使用golangci-lint, `File not found` 错误
COPY --from=build /go/bin/golangci-lint /usr/local/bin

EXPOSE 8000

ENTRYPOINT ["./app", "start-web", "&"]

问题清单和解决方案

由于并不是所有的问题都和Docker有关,因此我会使用 [分类] 在标题上注明。

0. 如何调试Docker打包过程 [Docker]

在Docker打包过程中会有如下字样,其中---> 5d5cac8457ad 字样就是一个可以运行的镜像,因此我们可以尝试运行这个镜像来获取我们想要的信息, 而不用在 Dockerfile 中通过 echo 来Debug🐶。

docker build -t goreportcard:v1.0.0 .
Sending build context to Docker daemon  12.08MB
Step 1/13 : FROM golang:1.14-alpine3.11 as build
 ---> 5d5cac8457ad
Step 2/13 : WORKDIR /tmp/build
 ---> Using cache
 ---> c57b9255d5fb
Step 3/13 : COPY . .

运行打包过程中的镜像

docker run -it c57b9255d5fb

1 golangci-lint 编译好后复制到 alpine中无法运行?[Go]

遇到这个问题的时候,提示错误信息大致为File or Path not found,但同时 goreportcard 是可以运行,这两者是在相同地环境下编译出来的。 因此肯定是编译的时候存在差异,初步判断为CGO_ENABLED,GOOS,GOARCH三个变量,通过搜索和尝试最终统一增加这三个环境变量解决问题。

    && export CGO_ENABLED=0 \
    && export GOARCH=amd64 \
    && export GOOS=linux \

2. golangci-lint 需要Go可执行文件? [Go]

// TODO:

3. go get 需要 git / ssh 客户端? [Go]

// TODO:

4. 在 alpine 中如何安装预编译好的软件,如git? [Docker]

这个是在gitlinux安装帮助 里找到的😂,也不知道为啥,只能夸下git可真专业

apk add git

5. 在 alpine 中安装软件特别慢,如何解决? [Docker]

sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories

6. 镜像中 go get 怎么使用宿主的缓存? [Go+Docker]

把宿主机的 path/go/pkg 挂载到 /go/pkg,这里默认使用的是go的容器镜像。

7. could not import C (no metadata for C) [Go]

https://github.com/golangci/golangci-lint/issues/602