乐趣区

Dockerfile实践小提示

https://mengz.me/posts/docker…

在进行利用容器化的实际中,咱们能够应用多种形式来创立容器镜像,而应用 Dockerfile 是咱们最罕用的形式。
而且在实现 CI/CD Pipeline 的过程中,应用 Dockerfile 来构建利用容器也是必须的。

本文不具体介绍 Dockerfile 的指令和写法,仅仅是在实践中积攒的一些写好一个 Dockerfile 的小提示,体现在一下几个方面:

  • 缩小构建工夫
  • 减小镜像大小
  • 镜像可维护性
  • 反复构建一致性
  • 安全性

减小构建工夫

首先来看看上面这个 Dockerfile

FROM ubuntu:18.04
COPY . /app
RUN apt-get update
RUN apt-get -y install ssh vim openjdk-8-jdk
CMD [“java”,”-jar”,”/app/target/app.jar”]

要减小构建的工夫,那咱们能够例如 Docker 构建的缓存个性,尽量保留不常常扭转的层,而在 Dockerfile 的指令中,COPYRUN 都会产生新的层,而且缓存的无效是与命令的程序有关系的。
在下面的 Dockerfile 中,COPY . /appRUN apt-get ... 之前,而 COPY 是常常扭转的局部,所以每次构建都会到导致 RUN apt-get ... 缓存生效。

Tip-1 : 正当利用缓存,而执行命令的程序是会影响缓存的可用性的。

要减小构建工夫,另一方面是应该仅仅 COPY 须要的货色,对于下面这个 Dockerfile 的目标,应该仅仅须要 COPY Java 利用的 jar 文件。

Tip-2 : 构建过程中仅仅 COPY 须要的货色。

下面的 Dockerfile 对 apt-get 命令别离应用了两个 RUN 指令,会生成两个不同的层。

Tip-3 : 尽量合并最终镜像的层数。

还有对于这个示例,咱们最终是想要一个 JRE 环境来运行 Java 利用,因而能够抉择一个 jre 的镜像来作为根底镜像,这样不必花工夫再去装置 jdk。

Tip-4 : 抉择适合的根底镜像

这样咱们能够把 Dockerfile 写成:

FROM ubuntu:18.04
RUN apt-get update \
    && apt-get y install ssh vim openjdk-8-jdk
COPY target/app.jar /app
CMD [“java”,”-jar”,”/app/app.jar”]

减小镜像大小

进一步,咱们如何尽量减小最终利用镜像的大小,来减速咱们的 CI 构建,以及减小镜像在网络上传输的效率。
在上例中,ssh, vim应该都是不必要的软件包,它们会咱用镜像的空间。

Tip-5 : 移除不必要的软件包装置(包含一些 debug 工具)。

其次,相似 apt-get 之类的零碎包管理工具会产生缓存数据,咱们也应该革除。

Tip-6 : 在应用零碎包管理工具装置软件包后清理缓存数据。

另外咱们应该应用 Docker 提供的多阶段构建个性来减小最终的镜像大小,咱们在前面介绍。

咱们进一步改良 Dockerfile:

FROM ubuntu:18.04
RUN apt-get update \
  && apt-get y install –no-install-recommends openjdk-8-jdk \
  && rm -rf /var/lib/apt/lists/*
COPY target/app.jar /app
CMD [“java”,”-jar”,”/app/app.jar”]

镜像的可维护性

咱们看看下面的 Dockerfile,应用了一个 ubuntu 的镜像来装置 jdk 包,而在装置 jdk 包的不同工夫点,可能会导致不同的版本,这样就导致了镜像的不易保护。

Tip-7 : 尽可能应用利用(语言)运行时的官网根底镜像,并指定 Tag 版本

一般来说,官网会保护一些变种镜像来提供多样性,例如基于 alpine 的,还有 -slim 精简版本的,其次对于像 Java 利用,最终咱们须要的应该只是 JRE,因而应该抉择 jre 的镜像,这样既保证了可维护性,同时也能够减小镜像的大小。

FROM openjdk:8-jre-alpine
COPY target/app.jar /app
CMD [“java”,”-jar”,”/app/app.jar”]

反复构建一致性

咱们应该保障咱们的镜像构建在任何时候,以及任何构建服务器上是统一的,然而咱们看下面的 Dockerfile,是将 jar 文件 COPY 到容器中,然而这个 jar 文件是在什么环境构建的呢?

Tip-8 : 在统一的环境中从源代码构建

同样,Docker 的多阶段构建提供了最好的解决方案,将源码编译构建放到构建阶段,将最终生成的软件包 COPY 放到运行是阶段。

Tip-9 : 应用多阶段构建

FROM maven:3.6-jdk-11 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn -e -B dependency:resolve
COPY src ./src
RUN mvn -e -B package

FROM openjdk:11-jre-slim
COPY –from=builder /app/target/app.jar /
CMD [“java”,”-jar”,”/app.jar”]

例如下面的 Dockerfile,咱们应用了 maven 的镜像来构建代码,应用 openjdk:jre 的镜像来运行。

安全性

最初咱们来看看安全性,如何使咱们的利用容器更加平安。首先,容器里蕴含的软件包越少,那可能的破绽就会越少,所以这也是 Tip-5 所强调的。

Tip-10 : 应用非 root 用户运行容器利用过程

其次,咱们应该应用非 root 用户来运行咱们的利用,默认状况下容器都是应用 root 用户来执行,咱们能够应用以下两种办法来应用非 root 用户来运行。

  1. 应用 USER 指令,记得在应用 USER 指令前创立相应的用户
  2. CMD 或者 ENTRYPOINT 中应用 su-exec , gosu 等工具来启动利用

我举荐应用第二种办法,因为第一种形式,在启动容器后进入容器会默认应用非 root 用户,这样不便于装置某些调试工具来执行调试(当然也能够通过配置 sudo)。
而第二种形式须要装置 su-exec 等工具,我倡议本人基于官网的根底镜像保护一些本人的运行时根底镜像,这样防止在每次构建利用镜像的时候都进行一次装置。

FROM gradle:6.4-jdk11 as builder
WORKDIR /code
COPY . .
RUN gradle assemble

FROM mengzyou/openjdk:11-jre-alpine
ENV APP_HOME="/opt/app" \
APP_USER="appuser" \
JAR_OPTS="--spring.profiles.active=prod"
RUN addgroup ${APP_USER} && \
  adduser -D -h ${APP_HOME} -S -G ${APP_USER} ${APP_USER}
COPY --from=builder --chown=${APP_USER}:${APP_USER} /code/build/libs/*.jar ${APP_HOME}/app.jar
EXPOSE 8080/tcp
WORKDIR ${APP_HOME}
CMD su-exec app java ${JAVA_OPTS} -jar ${APP_HOME}/app.jar ${JAR_OPTS}
# CMD [“su-exec”,”appuser”,”sh -c”,”java -jar /opt/app/app.jar"]

在来一个 golang 的示例

FROM golang:1.14-alpine AS builder
RUN apk add --no-cache git && \
    mkdir -p $GOPATH/src/app \
WORKDIR $GOPATH/src/app
COPY . $GOPATH/src/app
RUN go mod tidy \
    && go build -o /go/bin/app

FROM mengzyou/alpine:3.12
ENV APP_HOME=/opt/app \
    APP_USER=appuser
RUN addgroup ${APP_USER} && \
  adduser -D -h ${APP_HOME} -S -G ${APP_USER} ${APP_USER}
COPY --from=builder --chown=${APP_USER}:${APP_USER} /go/bin/app ${APP_HOME}/
EXPOSE 8080/tcp
WORKDIR ${APP_HOME}
CMD su-exec ${APP_USER} ${APP_HOME}/app

总结

这里仅仅是在 Dockerfile 实际中的一些提醒,要写好 Dockerfile,还有很多方面须要留神的中央,可参考 Docker 官网的 Best practices for writing Dockerfiles。

退出移动版