问题描述
我的目标是为我的 golang 应用程序比较两个 docker 解决方案:
- 使用 ubuntu 作为基础镜像
- 使用 golang:alpine 作为基础镜像
我的 Dockerfile 非常简单,类似于:
FROM ubuntu:20.04
# FROM golang:alpine for alpine based images
# myApp binary is pre-built before running docker build
copY bin/myApp app/myApp
copY myApp-config.json app/myApp-config.json
CMD MYAPP_CONfig=app/myApp-config.json ./app/myApp
对于基于 alpine 的图像,我点击了 same issue here,其中 /app/myApp 无法启动,因为我生成的图像中缺少 CGO 动态链接。我通过在 go build 期间禁用 CGO 来解决这个问题:
CGO_ENABLED=0
由于我对 docker 很陌生,所以我的问题是:
- 禁用 CGO 是否有任何风险?我的理解是go build会回退到CGO的原生go实现,但是不知道有没有什么隐藏的陷阱。我的应用确实严重依赖
net/http
,这似乎需要在运行时存在 CGO。 - 似乎 alpine 图像是用于 golang 的事实上的基础图像,处理这个 CGO 问题的标准方法是什么?
谢谢!
解决方法
如果您的应用基于 net/http
- 那么您可能需要担心的唯一考虑因素就是 DNS 解析。
TL;DR
您可能会看到的唯一问题是在具有主机名的 kubernetes 环境中
以 .local
使用 CGO_ENABLED=1
(go build 默认),它将使用本机操作系统的 DNS 解析器。
将 CGO_ENABLED=0
与 scratch
Docker 构建一起使用 - 然后使用 Go 的 DNS 解析器。
有什么区别?查看官方Go docs:
解析域名的方法,是否间接用 Dial 等函数或直接使用 LookupHost 和 LookupAddr,因操作系统而异。
在 Unix 系统上,解析器有两种解析名称的选项。它 可以使用纯 Go 解析器直接将 DNS 请求发送到 /etc/resolv.conf 中列出的服务器,或者它可以使用基于 cgo 的解析器 调用 C 库例程,例如 getaddrinfo 和 getnameinfo。
默认使用纯 Go 解析器,因为一个被阻止的 DNS 请求 只消耗一个 goroutine,而阻塞的 C 调用消耗一个 操作系统线程。当 cgo 可用时,基于 cgo 的解析器 在各种条件下使用:在不 让程序直接发出 DNS 请求 (OS X),当 LOCALDOMAIN 环境变量存在(即使为空),当 RES_OPTIONS 或 HOSTALIASES 环境变量非空,当 ASR_CONFIG 环境变量非空(仅限 OpenBSD),当 /etc/resolv.conf 或 /etc/nsswitch.conf 指定使用功能 Go 解析器未实现,以及当名称为 查找以 .local 结尾或 mDNS 名称。
就我个人而言,我已经在 net/http
Docker 容器中的 scratch
Docker 容器中构建了数十个基于 CGO_ENABLED=0
的微服务,没有任何问题。