Dockerfile 标准

一、原则

精简,安全,够用

二、基础镜像选择

常用的 Linux 系统镜像一般有 Ubuntu、CentOs、Alpine,其中Alpine更推荐使用。大小对比如下:
> docker images
REPOSITORY TAG IMAGE ID SIZE
ubuntu latest 74f8760a2a8b 82.4MB
alpine latest 11cd0b38bc3c 4.41MB
centos 7 49f7960eb7e4 200MB
debian latest 3bbb526d2608 101MB

alpine是一个高度精简又包含了基本工具的轻量级Linux发行版,本身的Docker镜像只有4~5M大小。各开发语言和框架

都有基于alpine制作的基础镜像,在开发自己应用的镜像时,选择这些镜像作为基础镜像,可以大大减小镜像的体积。

语言
推荐镜像
额外说明
Java
openjdk:8-jdk-alpine,openjdk:8-jre-alpine
Tomcat
tomcat:8.5-alpine
Nodejs
node:9-alpine
Python
python:3-alpine
PHP
php:7-fpm-alpine,php:5-fpm-alpine
Go
可执行文件 - 直接基于alpine镜像,把编译后的可执行文件打入镜像。
重点编译参数:CGO_ENABLED=0
nginx
nginx:1-alpine

三、镜像通用设置

3.1 编码

RUN ENV LANG en_US.UTF-8

3.2 时区

RUN echo "Asia/Shanghai" > /etc/timezone

3.3 字体库

dejavu是很受欢迎的开源字体之一,没有商业版权限制。
RUN apk add --update ttf-dejavu fontconfig

3.4 用户权限

创建特定的用户组和用户给容器
RUN addgroup --system --gid 1000 appuser && adduser -S -s /bin/sh -G appuser -u 1000 appuser
USER appuser
WORKDIR /home/appuser

四、串联 Dockerfile 指令


太多的使用RUN指令,经常会导致镜像有特别多的层,镜像很臃肿,而且甚至会碰到超出最大层数(127层)限制的

问题,遵循 Dockerfile 最佳实践,我们应该把多个命令串联合并为一个 RUN(通过运算符&&和/ 来实现)

#FROM openjdk:8u191-alpine
FROM alpine
COPY java_pre_stop-1.0.jar /home/appuser/java_pre_stop-1.0.jar
RUN echo http://mirrors.aliyun.com/alpine/v3.13/main/ > /etc/apk/repositories && \
echo http://mirrors.aliyun.com/alpine/v3.13/community/ >> /etc/apk/repositories && \
addgroup --system --gid 1000 appuser && adduser -S -s /bin/sh -G appuser -u 1000 appuser && \
chown -R appuser:appuser /opt && \
chown -R appuser:appuser /usr && \
chown -R appuser:appuser /home/appuser && \
apk update && \
apk add openjdk8 && \
apk add --no-cache tini && \
echo "Asia/Shanghai" > /etc/timezone && \
apk add --update ttf-dejavu fontconfig && \
rm -rf /var/cache/apk/* && rm -rf /etc/apk/cache
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/bin
USER appuser
WORKDIR /home/appuser
ENV LANG en_US.UTF-8


五、多阶构建


程序构建和产物的执行最好不要放在同一个镜像里面,例如:springboot 的编译需要依赖 maven 环境,但是最
终 java 的执行并不需要 maven,为了让我们生成的镜像足够精简,这2部分要分开处理。目前我们构建是在 CI 的流程中额外拉取镜像来做的,如果非要放在同一个 dockerfile 里面,就得用以下模式(“建造者模式”)


FROM maven:3.6.3-jdk-11-slim@sha256:68ce1cd457891f48d1e137c7d6a4493f60843e84c9e2634e3df1d3d5b381d36c AS build
RUN mkdir /project
COPY . /project
WORKDIR /project
RUN mvn clean package -DskipTests
FROM adoptopenjdk/openjdk11:jre-11.0.9.1_1-alpine@sha256:b6ab039066382d39cfc843914ef1fc624aa60e2a16ede433509ccadd6d995b1f
RUN mkdir /app
COPY --from=build /project/target/java-application.jar /app/java-application.jar
WORKDIR /app
CMD "java" "-jar" "java-application.jar"

六、容器的首进程

  • 第一个应用程序将以进程ID为1(PID=1)运行
  • Linux内核会以特殊方式处理PID为1的进程,ps 无法查询到首进程的进程信息
  • 容器被杀死或者停止的时候会发送 `SIGTERM` 信号给首进程
不建议的做法
ENTRYPOINT java -jar /home/appuser/app.jar
建议做法
ENTRYPOINT /sbin/tini java -jar /home/appuser/app.jar
引入 tini 作为首进程,tini 会帮忙将 `SIGTERM` 信号发送给他的所有子进程,以防出现僵死进程。

七、.dockerignore

镜像 build 的时候,同目录的所有文件都会加载到容器的上下文引擎(不仅仅是 copy,add)的文件,所以有2个做法:
  1. 保持 dockerfile 目录的干净整洁
  2. 用.dockerignore 过滤掉不需要的文件。

八、瘦身工具

8.1 dive

可以方便查看镜像的目录结构,了解各个目录文件占用大小

9.2 Docker-squash

自动压缩

点赞(1) 打赏

Comment list 共有 1 条评论

zhourj 6 months ago 回复TA

Alpine DNS 解析有缺陷,慎用。

立即
投稿
发表
评论
返回
顶部