本文讲述了如何通过 Docker 的多阶段构建功能来大幅度减小镜像大小,适用于需要在 Dockerfile 中构建程式(如 javac),且需要另外安装编译工具链的镜像。(如 Java)
先来学习单词(本文全部采用中文词汇,如需查询外文文档可对照该词汇表。理论上个人不赞成翻译术语):
- multi-stage 多阶段
- build 构建
- image 镜像
- stage 阶段
再来看一下效果: 原 110M+,现 92M。
对比一下 Dockerfile
优化前 Dockerfile:
FROM openjdk:8u171-jdk-alpine3.8 ADD . /app WORKDIR /app RUN apk add maven && mvn clean package && apk del maven && mv target/final.jar / && cd / && rm -rf /app && rm -rf /root/.m2 ENTRYPOINT java -jar /final.jar
优化后 Dockerfile:
FROM openjdk:8u171-jdk-alpine3.8 as builder ADD . /app WORKDIR /app RUN apk add maven && mvn clean package && apk del maven && mv target/final.jar / FROM openjdk:8u181-jre-alpine3.8 as environment WORKDIR / COPY --from=builder /final.jar . ENTRYPOINT java -jar /final.jar
很明显,优化后的 Dockerfile 新增了 FROM AS 这个命令,并出现了两个 FROM。这就是多阶段构建。
了解一下多阶段构建
多阶段构建是 Docker 17.05 的新增功能,它可以在一个 Dockerfile 中使用多个 FROM 语句,以创建多个 Stages(阶段)。每个阶段间独立(来源请求),可以通过 COPY --from 来获取其它阶段的文件。我们来打个比方,把最终镜像比作一盘菜(炒青椒)。把原料青椒炒完后上桌。
# 对比清单 镜像 -> 一盘菜 第一个阶段 -> 炒 第二个阶段 -> 上桌
两个阶段的目标是做好(生成)最终的菜(镜像)。我们要做的是将第一个阶段「炒」出来的食物进行「上桌」。我们的目标是 做出菜,且 菜盘子(盛菜和中间产物)最轻。
可视化流程如下:
# 做菜流程 ... 省略原料 原料 -> [第一个阶段——炒] # 此时盘子里有炒的工具、炒的结果和中间产物 # 这时候开启第二个阶段,只保留炒的结果,而不再需要其它。 -> 炒的结果 -> [开始上桌,只保留结果] # 把炒出来的青椒拿来(COPY --from),其它不要 -> 最终是一盘菜。
现在应该大致理解多阶段构建的流程了吧。我们把话筒交给 Java,看看在 Dockerfile 中使用编译工具构建一个 JAR,并只保留构建完的 JAR 和运行时交给 Image,其它则扔掉应该怎么做:
# 第一阶段——编译(炒) FROM openjdk:8u171-jdk-alpine3.8 as builder # 自带编译工具 ADD . /app WORKDIR /app RUN ... 省略编译和清理工作... # 现在,JAR 已经出炉。JDK 不再需要,所以不能留在镜像中。 # 所以我们开启第二阶段——运行(上桌),并扔掉第一阶段的所有文件(包括编译工具) FROM openjdk:8u181-jre-alpine3.8 as environment # 只带运行时 # 目前,编译工具等上一阶段的东西已经被我们抛下。目前的镜像中只有运行时,我们需要把上一阶段(炒)的结果拿来,其它不要。 COPY --from=0 /final.jar . # 好了,现在镜像只有必要的运行时和 JAR 了。 ENTRYPOINT java -jar /final.jar
如上就是多阶段构建的介绍。
使用多阶段构建
多阶段构建的核心命令是 FROM。FORM 对于身经百战的你来说已经不用多讲了。在多阶段构建中,每次 FROM 都会开启一个新的 Stage(阶段),可以看作一个新的 Image(不够准确、来源请求),与其它阶段隔离(甚至包括环境变量)。只有最后的 FROM 才会被纳入 Image 中。
我们来做一个最 simple 的多阶段构建例子:
# Stage 1 FROM alpine:3.8 WORKDIR /demo RUN echo "Hello, stage 1" > /demo/hi-1.txt # Stage 2 FROM alpine:3.8 WORKDIR /demo RUN echo "Hello, stage 2" > /demo/hi-2.txt
可以自己构建一下这个 Dockerfile,然后 docker save <tag> > docker.tar 看看其中的内容。不出意外应该只有 /demo/hi-2.txt 和 Alpine。
在这个 Dockerfile 中,我们创建了两个阶段。第一个阶段创建 hi-1.txt,第二个阶段创建 hi-2.txt,且第二个阶段会被加入最终 Image,其它不会。
复制文件——阶段间的桥梁
如果阶段间完全隔离,那么多阶段就没有意义——上一个阶段的结果会被完全抛弃,并进入全新的下一阶段。
我们可以通过 COPY 命令来获取其它阶段的文件。在多阶段中使用 COPY 和普通应用完全一致,仅需要添加 –form ` 即可。那么,我们修正上一个例子,使最终镜像包含两个阶段的产物:
# Stage 1 FROM alpine:3.8 WORKDIR /demo RUN echo "Hello, stage 1" > /demo/hi-1.txt # Stage 2 FROM alpine:3.8 WORKDIR /demo COPY --from=0 /demo/hi-1.txt /demo RUN echo "Hello, stage 2" > /demo/hi-2.txt
重新构建并保存(Save),你会发现多了一层 Layer,其中包含 hi-1.txt。
阶段命名——快速识别
对于只有七秒记忆的我们来说,每次使用 stage index 并不是一件很妙的事情。这时候,可以通过阶段命名的方式给它们赋予名字,以方便识别。
为阶段添加名字很简单,只需要在 FROM 后加上 as <name> 即可。
现在,我们更新 Dockerfile,给予阶段名称并使用名称来 COPY。
# Stage 1, it's name is "build1" FROM alpine:3.8 as build1 WORKDIR /demo RUN echo "Hello, stage 1" > /demo/hi-1.txt # Stage 2, it's name is "build2" FROM alpine:3.8 as build2 WORKDIR /demo # No longer use indexes COPY --from=build1 /demo/hi-1.txt /demo RUN echo "Hello, stage 2" > /demo/hi-2.txt
重新构建并保存,结果应该同上次相同。
仅构建部分阶段——轻松调试
Docker 还为我们提供了一个很方便的调试方式——仅构建部分阶段。它可以使构建停在某个阶段,并不构建后面的阶段。这可以方便我们调试;区分生产、开发和测试。
仍然沿用上次的 Dockerfile,但使用 --target <stage> 参数进行构建:
$ docker build --target build1 .
再次 Save,你会发现只有 build1 的内容。
总结
这就是多阶段构建的全部用法了。我们再回到开篇的两个 Dockerfile 对比,你能发现优化前的镜像胖在哪里了吗?
很显然,它包含了无用的 JDK,JDK 只在编译时起作用,编译完便无用了,只需要 JRE 即可。所以,利用多阶段构建可以隔离编译阶段和运行阶段,以达到镜像最优化。
参考文献
https://docs.docker.com/develop/develop-images/multistage-build/#name-your-build-stages
https://yeasy.gitbooks.io/docker_practice/image/multistage-builds.html
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]