跳到主要内容

优化容器镜像

CloudBase 云托管支持托管任意镜像,但我们建议您尽可能地优化您的镜像,以获得更快的启动速度、更快的构建速度、更小的容器体积、更优的服务性能。

以下是一些推荐做法:

选择更小的基础镜像

同样一种语言的运行时容器,由于选择的基础容器不同,体积也会有很大的差异,对于大多数业务,我们推荐您使用更小的基础镜像

以 Node.js 为例,官方镜像仓库 提供了以下不同体积的基础镜像:

REPOSITORY      TAG              IMAGE ID         CREATED          SIZE
node 15 ca36fba5ad66 2 days ago 941MB
node 15-slim 922b09b21278 2 days ago 165MB
node 15-buster 36754275e286 2 days ago 910MB
node 15-buster-slim eda2c7e487ff 2 days ago 179MB
node 15-alpine 1e8b781248bb 2 days ago 115MB

可以注意到 15-slim15-buster-slim15-alpine 的体积明显较小,这是由于它们使用了经过裁剪的底层操作系统镜像。使用这些裁剪、优化后的镜像通常不会影响您服务的运行。

减少镜像层数

Dockerfile 中的每一行指令,都会生成一个层,镜像的层数会直接影响镜像的体积大小。

我们推荐您将多个指令合并串联,以减少层的数量。例如,您可以将以下的指令合并为同一个:

RUN cd my-app
RUN make
RUN make install
RUN rm -rf /tmp

合并后:

RUN cd my-app && \
make && \
make install && \
rm -rf /tmp

充分利用层的缓存

Docker 会对镜像的每一层单独进行缓存,如果层的内容没有变化,那么会直接使用之前的缓存,以提高构建、上传镜像的速度。为了能尽量复用这一机制,我们推荐您将镜像变动不大的层独立出来

例如,您的应用可能存在诸多依赖,通常来说,这些依赖在开发过程中变动较小,所以依赖的拷贝最好独立成为一层指令:

COPY ./deps deps
RUN make && make install

以上的做法将 COPY 语句独立出来,每次构建、推送镜像时,只要依赖的文件内容没有变化,那么都可以复用之前的缓存,以提高构建、推送速度。

清理不必要的文件

您可以使用 .dockerignore 在容器构建时忽略文件,以减少非必要文件的导入,同时也可以提高安全性,避免将一些本地敏感文件打包到镜像中。

多阶段构建

以 Java 服务为例,实际运行的过程中只需要最后编译生成的 jar 文件即可,而构建期使用的依赖、扩展包以及源代码文件都是不必要的。

此时我们可以使用多阶段构建的方式,分离构建环境和运行环境,从而精简运行时容器的体积,例如以下的 Dockerfile:

# 使用官方 maven/Java 8 镜像作为构建环境
# https://hub.docker.com/_/maven
FROM maven:3.6-jdk-11 as builder

# 将代码复制到容器内
WORKDIR /app
COPY pom.xml .
COPY src ./src

# 构建项目
RUN mvn package -DskipTests

# 使用 AdoptOpenJDK 作为基础镜像
# https://hub.docker.com/r/adoptopenjdk/openjdk8
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM adoptopenjdk/openjdk11:alpine-slim

# 将 jar 放入容器内
COPY --from=builder /app/target/helloworld-*.jar /helloworld.jar

# 启动服务
CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/helloworld.jar"]