[distroless-1.jpeg] 使用 Distroless 镜像来保护 Kubernetes 上的容器。 容器改变了我们看待技术基础设施的方式。这是我们运行应用程序方式的一次巨大飞跃。 Distroless 基础镜像 谷歌为大多数流行的编程语言和平台提供了 Distroless 的基础镜像。 以下基础镜像是正式发布的版本: gcr.io/distroless/static-debian10 gcr.io/distroless/base-debian10 gcr.io/distroless/java-debian10 gcr.io/distroless/cc-debian10 gcr.io/distroless/nodejs-debian10 下面的基础镜像仍在实验阶段,不推荐用于生产环境: gcr.io/distroless /python2.7-debian10 gcr.io/distroless/python3-debian10 gcr.io/distroless/java/jetty-debian10 gcr.io/distroless
pull gcr.io/distroless/base-debian9 docker pull gcr.io/distroless/cc docker pull gcr.io/distroless/cc-debian10 /distroless/dotnet docker pull gcr.io/distroless/dotnet-debian10 docker pull gcr.io/distroless/dotnet-debian9 docker pull gcr.io/distroless/java docker pull gcr.io/distroless/java-debian10 docker pull gcr.io/distroless /distroless/nodejs-debian9 docker pull gcr.io/distroless/python2.7 docker pull gcr.io/distroless/python2.7 /distroless/python3-debian9 docker pull gcr.io/distroless/static docker pull gcr.io/distroless/static-debian10
pull gcr.io/distroless/base-debian9 docker pull gcr.io/distroless/cc docker pull gcr.io/distroless/cc-debian10 /distroless/dotnet docker pull gcr.io/distroless/dotnet-debian10 docker pull gcr.io/distroless/dotnet-debian9 docker pull gcr.io/distroless/java docker pull gcr.io/distroless/java-debian10 docker pull gcr.io/distroless /distroless/nodejs-debian9 docker pull gcr.io/distroless/python2.7 docker pull gcr.io/distroless/python2.7 /distroless/python3-debian9 docker pull gcr.io/distroless/static docker pull gcr.io/distroless/static-debian10
所以他们提供了自己的方法来解决这个问题,即 distroless 镜像。 与典型的Linux 基础镜像(绑定了很多软件)不同,在 distroless 上对你的应用进行 docker化,最终的镜像只包含应用及其运行时的依赖项,大多数 Linux 发行版中包含的标准软件,如包管理器 当然也有另外一个好处,那就是和上面的 distroless 相比,Alpine 是成熟的 Linux 发行版,提供基本的 shell 访问,使得调试 Docker 容器应用更为方便。 Guix 区分了包的运行时依赖与构建依赖,所以 Guix 构建的 Docker 镜像将只包含明确指定的程序,加上他们的运行时依赖,就像 distroless 的方法一样。 但和 distroless 不同的时候,distroless 需要你自己去查程序的运行时依赖关系(当然也要写 Dockerfile),而 Guix 只需要运行一条命令即可:$ guix pack -f
但它依然不是最轻量级的,今天要说的是 Google 的 distroless 基础镜像,distroless 镜像 gcr.io/distroless/static-debian11 只有 2 MB,是 什么情况应该使用 distroless? 常规的容器部署的可以进行命令执行: 而 distroless 部署的就没有: 原因是 distroless 没有 ls 命令: 也就是说应始终避免使用那些帮助黑客收集更多信息或执行权限提升的工具。 因此,如果注重推拉取镜像的速度和容器的安全性,或者用于生产环境,可以用 distroless。 什么情况应该不使用 distroless? distroless。
使用 distroless 作为基础镜像 虽然通过多阶段构建能减小最终生成的镜像的大小,但 459MB 的体积仍相对过大。 Google 开源的项目 distroless 正是为了解决基础镜像体积过大这一问题。 目前,distroless 为依赖 java、python、nodejs、dotnet 等环境的应用提供了基础镜像。 使用该 dockerfile 构建出的镜像体积为 99.2MB,比基于 distroless 的还要小。 distroless vs alpine 既然 distroless 和 alpine 都能提供非常小的基础镜像,那么在生产环境中到底应该选择哪一种呢?
distroless镜像并不是什么新东西,但由于某些原因,我觉得它们并没有得到应有的采纳。 ◆ 什么是distroless图像? 我不得不说这不是什么新东西,我是说真的。 它已经存在很多年了,你可以在以下内容中查看 GoogleContainerTools/distroless. "distroless无发行版 "只包含你的应用程序和它的运行时依赖。 ◆ 不应该使用distroless的场景 说什么时候使用distroless很容易,但什么时候不应该使用它们? 储存库 GoogleContainerTools/distroless有一个关于如何为Golang工具制作无发行版distroless镜像的例子。 另外,将alpine和 scratch创造出令人惊奇的distroless。 问题是,为你的应用程序建立distroless仍然是相当手动的,并不像你希望的那样有趣。
在生产中通常会从Scratch空镜像或distroless开始。 distroless镜像仅包含应用程序及其运行时依赖项。 Distroless 镜像非常小。最小的 distroless 图像gcr.io/distroless/static大约为 650 kB。 gcr.io/distroless/base-debian10只包含一组基本的包,如包括只需要的库,如glibc、libssl和openssl 当然对于像 Go 这样不需要libc 的静态编译应用程序我们就可以替换为如下基镜像 关于distroless基镜像的更多信息可以参考https://github.com/GoogleContainerTools/distroless 3.及时更新镜像 使用经常更新的基础镜像,在需要时重构你的镜像
/gradlew build # 使用 distroless 镜像作为运行时 FROM gcr.io/distroless/java17-debian11 WORKDIR /app COPY --from 采用 distroless 镜像增强安全性(无 shell 和多余工具)。 通过环境变量配置 JVM 参数,便于动态调整。 构建时使用缓存加速(COPY 命令分离依赖和代码)。 通用运维最佳实践 镜像大小优化 使用多阶段构建 选择适当的基础镜像(如 Alpine、distroless) 清理不必要的缓存和临时文件 安全性增强 使用非 root 用户运行容器 定期更新基础镜像
所幸的是,谷歌为我们提供了 distroless(https://github.com/GoogleCloudPlatform/distroless)。 以下是 distroless 存储库的描述: “distroless”镜像只包含应用程序及其运行时依赖项,不包含程序包管理器、shell 以及在标准 Linux 发行版中可以找到的任何其他程序。 $ docker images | grep node-distroless node-distroless 7b4db3b7f1e5 76.7MB 只有 76.7MB! 但在使用 distroless 时有一些事项需要注意。 你应该使用 Alpine、distroless 还是原始镜像? 如果你是在生产环境中运行容器,并且更关心安全性,那么可能 distroless 镜像更合适。
所幸的是,谷歌为我们提供了 distroless。 以下是 distroless 存储库的描述: “distroless”镜像只包含应用程序及其运行时依赖项,不包含程序包管理器、shell 以及在标准 Linux 发行版中可以找到的任何其他程序。 $ docker images | grep node-distroless node-distroless 7b4db3b7f1e5 76.7MB 只有 76.7MB! 但在使用 distroless 时有一些事项需要注意。 你应该使用 Alpine、distroless 还是原始镜像? 如果你是在生产环境中运行容器,并且更关心安全性,那么可能 distroless 镜像更合适。
用distroless去除容器中所有不必要的东西 这个镜像包含了Node.js以及yarn、npm、bash和其他的二进制文件。 所幸的是,谷歌为我们提供了distroless。 以下是distroless存储库的描述: ? 这正是你所需要的! 你可以对Dockerfile进行调整,以利用新的基础镜像,如下所示: ? 但在使用distroless时有一些事项需要注意。 当容器在运行时,如果你想要检查它,可以使用以下命令attach到正在运行的容器上: ? 你应该使用Alpine、distroless还是原始镜像? 如果你是在生产环境中运行容器,并且更关心安全性,那么可能distroless镜像更合适。 例如,如果攻击者能够利用运行在distroless上的应用程序的漏洞,他们将无法在容器中使用shell,因为那里根本就没有shell! ?
对于这个任务,我个人最喜欢的是来自 Distroless 项目的 Python 镜像。可是,Distroless 是什么呢? 这就是 Distroless 的用途——它让每个人都可以FROM scratch。 好了,现在让我们具体描述一下 Distroless 是什么。 Line - Switch to Distroless image FROM gcr.io/distroless/python3-debian10 AS runner # ... 比大小更重要的是安全性,从这个意义上说,Distroless 肯定更有优势,因为 Alpine(一个很好的替代选项)有很多额外的包,增加了攻击面。关于 Distroless,最后值得一提的是镜像调试。 考虑到 Distroless 不包含任何 shell(甚至不包含sh),当你需要调试和查找时,就变得非常棘手。为此,所有 Distroless 镜像都有调试版本。
由于Distroless是原始操作系统的精简版本,不包含额外的程序。容器里并没有Shell!如果黑客入侵了我们的应用程序并获取了容器的访问权限,他也无法造成太大的损害。 /RUN npm installFROM gcr.io/distroless/nodejsCOPY --from=build /app /EXPOSE 3000CMD ["index.js"]Distroless vs Alpine如果是在生产环境中运行,并且注重安全性, Distroless 镜像可能会更合适。 举个例子,如果黑客在运行于Distroless的应用中发现了一个漏洞,他也无法在容器中创建Shell,因为根本就没有。如果更在意要是大小,则可以换成Alpine基础镜像。这两个都很小,代价是兼容性。
main.go RUN upx manager proxy backup-agent restore-agent # # IMAGE TARGETS # ------------- FROM gcr.io/distroless USER nonroot:nonroot ENTRYPOINT ["/manager"] FROM gcr.io/distroless/static:nonroot as proxy WORKDIR USER nonroot:nonroot ENTRYPOINT ["/proxy"] FROM gcr.io/distroless/static:nonroot as backup-agent WORKDIR USER nonroot:nonroot ENTRYPOINT ["/backup-agent"] FROM gcr.io/distroless/static as restore-agent WORKDIR
这种情况下,大家可能会尝试使用 kubectl exec,但有时候这样也还不行,因为 Distroless 等容器甚至不允许通过 SSH 进入 shell。 为了解决这个问题,我们通过以下方式使用 kubectl debug: ~ $ kubectl run distroless-python --image=martinheinz/distroless-python kubectl debug -it distroless-python --image=praqma/network-multitool --target=distroless-python -- sh 这种情况下,我们可以像这样使用进程共享: ~ $ kubectl run distroless-python --image=martinheinz/distroless-python --restart =Never ~ $ kubectl debug -it distroless-python --image=busybox --share-processes --copy-to=distroless-python-debug
CREATED SIZE hello 0.1.0 ca1a9e1e5948 About a minute ago 628MB 更可以通过 distroless IMAGE ID CREATED SIZE hello 0.1.0 332ce3b4f717 30 seconds ago 8.38MB distroless : # Dockerfile.distroless ARG BASE_IMAGE=rust:1.52.1-slim-buster FROM $BASE_IMAGE as planner WORKDIR app/target target COPY --from=cacher $CARGO_HOME $CARGO_HOME RUN cargo build --release FROM gcr.io/distroless 否则,选择 distroless 。 https://azzamsa.com/n/rust-docker/ 什么时候不使用 Rust?
master/bazel-sample/docker WORKSPACE文件内容: 加载rules_go 加载rules_docker 加载gazelle 准备基础镜像alpine_linux_amd64和distroless_linux_amd64 "index.docker.io", repository = "library/alpine", tag = "3.15",)container_pull( name = "distroless_linux_amd64 ", registry = "gcr.io", repository = "distroless/base", tag = "latest",) BUILD文件: ⚡ root public"],)container_image( name = "image",# base = "@alpine_linux_amd64//image", base = "@distroless_linux_amd64
因为之前没有关注过 distroless/base-debian10 这个镜像,而这个镜像又是 mpi-operator 的基础镜像,特意搜了一下,发现这个项目,GoogleContainerTools /distroless,看一眼 README 就理解了。
七、使用 Distroless 让正式环境更加安全 尽管上面我们已经使用 Alpine 让 Docker Image 变得这么小,但在文章的最后我想再提供读者另一个选择, 那就是「Distroless」 node AS builder WORKDIR /usr/src/app COPY package.json index.js ./ RUN npm install --production # 改成用 Distroless FROM gcr.io/distroless/nodejs WORKDIR /usr/src/app COPY --from=builder /usr/src/app .