好问题。
简短回答:
由于存储比处理能力便宜,因此“实时”构建图像可能很复杂、耗时,而且可能无法预测。
例如,在你的 Kubernetes 集群上,你只想拉取你知道它有效的图像的“缓存”层,然后你只需运行它......在几秒钟内而不是编译二进制文件和下载东西(正如你在你的 Dockerfile)。
关于构建图像:
您不必在本地构建这些图像,您可以使用 CI/CD 运行器并从将代码推送到 git 存储库时运行的管道中运行 docker build 和 docker push。
而且,如果图像太大,你应该研究通过使用multi-stage building、使用更轻/最小的基础图像、使用更少的层来减小其大小的方法(例如多个RUN apt install可以分组为一个apt install命令列出多个包),并使用 .dockerignore 不将不必要的文件发送到您的图像。最后阅读更多关于 caching in docker builds 的信息,因为它可能会减少您在进行更改时可能推送的层的大小。
长答案:
将 Dockerfile 视为源代码,将 Image 视为最终的二进制文件。我知道这是一个经典的例子。
但是只要考虑一下每次要使用它时构建/编译二进制文件需要多长时间(通过运行它,或者将它作为库导入到不同的软件中)。然后考虑下载该软件的依赖项或每次运行它们时在不同机器上编译它们的不确定性。
你可以拿 Node.js 的 Dockerfile 为例:
https://github.com/nodejs/docker-node/blob/main/16/alpine3.16/Dockerfile
这是基于 Alpine 的:https://github.com/alpinelinux/docker-alpine
您不希望您的应用程序在实际启动您的应用程序之前在运行时执行这些文件(及其脚本)中指定的所有操作,因为它可能不可预测、耗时并且比应有的更复杂(例如,您从集群到互联网的出口流量需要防火墙例外,以下载一些您不知道它们是否可用的依赖项)。
相反,您可以根据您测试并构建要运行的代码的基础映像来发布映像。该图像将被构建并发送到注册表,然后 k8s 将把它作为一个黑盒子运行,这可能是可预测和确定的。
然后关于你每次推送巨大的 docker 图像是多么烦人的观点:
您可以通过遵循一些最佳实践并精心设计 Dockerfile 来减小该大小,例如:
- 减少层数,例如,只要有可能就向命令传递多个参数,而不是多次重新运行它们。
- 使用多阶段构建,因此您只会推送最终图像,而不是编译和配置应用程序所需构建的阶段。
- 避免将数据注入图像,您可以稍后在运行时将其传递给容器。
- 对图层进行排序,这样您在进行更改时就不必重新构建未触及的图层。
- 不要包含不必要的文件,使用
.dockerignore。
最后但并非最不重要:
你不必从你的机器上推送图像,你可以使用 CI/CD 运行器(例如build-push Github action),或者你可以使用你的云提供商的“云构建”产品(比如Cloud Build for GCP和AWS CodeBuild )