Docker scratch 默认包含什么?

huangapple go评论75阅读模式
英文:

What Docker scratch contains by default?

问题

有一个选项可以使用FROM scratch,对我来说,这似乎是构建Go容器的一种非常吸引人的方式。

我的问题是,它本身还有什么可以运行二进制文件的功能,我需要添加什么才能可靠地运行Go二进制文件?编译后的Go二进制文件似乎至少可以在我的笔记本上运行。

我的目标是将镜像大小最小化,既出于安全考虑,也出于基础设施管理的原因。在最理想的情况下,我的容器在构建阶段之外将无法执行二进制文件或shell命令。

英文:

There is an option to use FROM scratch for me it looks like a really attractive way of building my Go containers.

My question is what does it still have natively to run binaries do I need to add anything in order to reliably run Go binaries? Compiled Go binary seems to run it at least on my laptop.

My goal is to keep image size to a minimum both for security and infra management reasons. In an optimal situation, my container would not be able to execute binaries or shell commands outside of build phase.

答案1

得分: 13

scratch镜像中不包含任何内容,没有文件。但实际上,这对你有利。事实证明,使用CGO_ENABLED=0构建的Go二进制文件除了它们使用的内容之外,不需要任何其他东西。有几个要注意的事项:

  • 使用CGO_ENABLED=0时,你不能使用任何C代码。实际上并不太难。
  • 使用CGO_ENABLED=0时,你的应用程序将不使用系统的DNS解析器。我认为默认情况下它也不会使用系统的DNS解析器,因为它是阻塞的,而Go的本地DNS解析器是非阻塞的。
  • 你的应用程序可能依赖一些不存在的东西:
    • 需要进行HTTPS调用的应用程序(例如,向其他服务(如Amazon S3或Stripe API)发出的调用)需要ca-certs以确认HTTPS证书的真实性。这也需要随时间进行更新。这对于提供HTTPS内容是不需要的。
    • 需要时区意识的应用程序需要时区信息文件。

FROM scratch的一个不错的替代方案是FROM alpine,它将包含一个基本的Alpine镜像 - 这是非常小的(我相信只有5 MiB),包含与Go兼容的musl libc,允许你链接到C库并且可以在不设置CGO_ENABLED=0的情况下进行编译。你还可以利用alpine定期更新的特点,使用其tzinfo和ca-certs。

(值得注意的是,Docker层的开销在一定程度上被Docker的去重功能摊销了,尽管当然这会因基础镜像的更新频率而被抵消。不过,这有助于推广使用非常小的Alpine镜像的想法。)

你现在可能不需要tzinfo或ca-certs,但最好安全起见;你可能会无意中添加一个依赖项,而没有意识到它会破坏你的构建。因此,我建议使用alpine作为你的基础镜像。alpine:latest应该没问题。

额外提示:如果你想在Docker中获得可重复构建的优势,同时又要保持较小的镜像大小,你可以使用Docker 17.06+中提供的新的多阶段构建功能。

它的工作方式如下:

FROM golang:alpine
ADD . /go/src/github.com/some/gorepo  # 如果没有使用vendor,可能需要一些go getting
RUN go build -o /app github.com/some/gorepo

FROM scratch  # 或者使用alpine
COPY --from=0 /app /app
ENTRYPOINT ["/app"]

(如果我有任何错误,请原谅,我是凭记忆打字的。)

请注意,当使用FROM scratch时,必须使用ENTRYPOINT的exec形式,因为shell形式不起作用(它依赖于Docker镜像具有/bin/sh,而scratch镜像没有)。这在Alpine中可以正常工作。

英文:

The scratch image contains nothing. No files. But actually, that can work to your advantage. It turns out, Go binaries built with CGO_ENABLED=0 require absolutely nothing, other than what they use. There are a couple things to keep in mind:

  • With CGO_ENABLED=0, you can't use any C code. Actually not too hard.
  • With CGO_ENABLED=0, your app will not use the system DNS resolver. I don't think it does by default anyways because it's blocking and Go's native DNS resolver is non-blocking.
  • Your app may depend on some things that are not present:
    • Apps that make HTTPS calls (as in, to other services, i.e. Amazon S3, or the Stripe API) will need ca-certs in order to confirm HTTPS certificate authenticity. This also has to be updated over time. This is not needed for serving HTTPS content.
    • Apps that need timezone awareness will need the timezone info files.

A nice alternative to FROM scratch is FROM alpine, which will include a base Alpine image - which is very tiny (5 MiB I believe) and includes musl libc, which is compatible with Go and will allow you to link to C libraries as well as compile without setting CGO_ENABLED=0. You can also leverage the fact that alpine is regularly updated, using its tzinfo and ca-certs.

(It's worth noting that the overhead of Docker layers is amortized a bit because of Docker's deduplication, though of course that is negated by how often your base image is updated. Still, it helps sell the idea of using the quite small Alpine image.)

You may not need tzinfo or ca-certs now, but it's better to be safe than sorry; you can accidentally add a dependency without realizing it breaks your build. So I recommend using alpine as your base. alpine:latest should be fine.

Bonus: If you want the advantages of reproducible builds inside Docker, but with small image sizes, you can use the new Docker multi-stage builds available in Docker 17.06+.

It works a bit like this:

FROM golang:alpine
ADD . /go/src/github.com/some/gorepo  # may need some go getting if you don't vendor
RUN go build -o /app github.com/some/gorepo

FROM scratch  # or alpine
COPY --from=0 /app /app
ENTRYPOINT ["/app"]

(I apologize if I've made any mistakes, I'm typing that from memory.)

Note that when using FROM scratch you must use the exec form of ENTRYPOINT, because the shell form won't work (it depends on the Docker image having /bin/sh, which it won't.) This will work fine in Alpine.

huangapple
  • 本文由 发表于 2017年8月17日 04:50:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/45722785.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定