当下,使用容器进行应用开发和部署已经是非常普遍的了。
在容器带来许多价值的同时,也带来了一些挑战,比如:
- 更大的镜像意味着增加构建时间,同时也增加了开销
- 镜像包含越多的库会增加漏洞扫描工具需要扫描的范围
等等。
这个问题最常见的解决方案是 - 使用更小的 Linux 发行版作为基础镜像!
在开发人员中,使用像 Alpine 这样的轻量级发行版是一种非常常见的技术,可以避免使容器镜像过于庞大。但其底层库总是存在开放漏洞的风险。
谷歌通过引入 Distroless 镜像解决了这个问题。
Distroless 镜像只包含你的应用程序及其运行时依赖项。它们不包含包管理器、shell 或你希望在标准 Linux 发行版中找到的任何其他程序。
在阅读 Gaurav Agarwal 的关于 使用 Distroless 让你的容器更加安全 的故事后,我决定对它进行尝试和获得第一手的经验。
在本文中,我将分别使用 Distroless 和 Alpine 镜像创建一个 Java 应用程序容器,在它们上进行漏洞扫描,并比较构建时间、镜像大小等内容。
那就让我们开始吧。
使用 Distroless 镜像
我为这个实验创建了一个简单的 Hello World 的 Spring Boot 应用程序。
首先,我使用 Distroless 镜像创建了一个 Dockerfile,如下图所示:
FROM maven:3.5-jdk-8 AS build
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean package
FROM gcr.io/distroless/java:8
ARG DEPENDENCY=/usr/src/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","DemoApplication.Application"]
构建完成,如下图所示:
然后我用 Trivy 扫描了一下镜像,结果如下图所示:
使用 Alpine 镜像
接下来,我使用 Alpine 镜像重复了这个实验。这个实验的 Dockerfile 如下所示:
FROM maven:3.5-jdk-8 AS build
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean package
FROM openjdk:8-jre-alpine
ARG DEPENDENCY=/usr/src/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","DemoApplication.Application"]
构建成功完成:
然后我对镜像进行了 Trivy 扫描,结果如下图:
直接比较
基于这个实验,这是在我的环境中观察到的:
- 镜像大小 - 使用 Alpine 作为基础镜像构建出的镜像大小是 93.5 MB,而 disroless 镜像是 139 MB。所以 Alpine 镜像比 disroless 镜像更加轻量。
- 漏洞数量 - Alpine 镜像共有216个漏洞(未知:0,轻微:106,中等:79,严重:27,危急:4),反观 distroless 镜像共有 50 个漏洞(未知:0,轻微:30,中等:6,严重:9,危急:5)
总结
最后,如果你更关心安全性,那么肯定建议使用 Distroless 镜像。如果你担心镜像大小,那么 Alpine 可能是一个更好的选择。
实验代码
你可以在这里找到这个实验的完整代码:deshpandetanmay/distroless。