DockerCon2017已经结束了,从去年的版本到现在,Docker产生了很多的变化。Docker的开发者们一直强调他们希望Docker的体验越简单越好。观察下最近几个月Docker的新特性,你会发现所言非虚,DockerCon2017大会也向我们展示了这一点。下面介绍下Docker最近几个月发布的新特性
多阶段构建
构建一个镜像一般需要多个阶段。
- 编译你的应用
- 然后跑测试
- 当测试通过时,你将你的应用打包成可部署的软件包
- 最后你把软件包添加到镜像里面
你可以将这些步骤都放进一个Dockerfile中,但是这会导致镜像膨胀,加入了很多最终产品不需要的内容。例如编译和构建的框架,Docker镜像存储需要的空间也会变得很大。
一个解决方法是在Docker外面编译测试打包应用程序,或者使用多个Dockerfile。你可以用一个Dockerfile来编译测试打包你的应用,用另外一个Dockerfile来添加之前打好的软件包,并做最终的交付。
然而,整个构建的过程通过一个脚本捆绑在一起,而并不是以Docker的方式来执行构建。
Docker对于添加新的特性或者语法到Dockerfile中是谨慎的,当他们最终决定着手通过一个简单而优雅的方式来解决这个构建的问题。通过引入多阶段构建(multi-stage builds)
,使得通过使用多个FROM
指令来定义多个构建的阶段成为可能。示例如下
# First stage to build the application
FROM maven:3.5.0-jdk-8-alpine AS build-env # build-env构建阶段的开始
ADD ./pom.xml pom.xml
ADD ./src src/
RUN mvn clean package # build-env 构建阶段在此结束
# Final stage to define our minimal runtime
FROM FROM openjdk:8-jre # 新的构建阶段的开始
COPY --from=build-env target/app.jar app.jar
RUN java -jar app.jar
每一次使用FROM
指令时,相当于定义了一个新的构建阶段,一直到下一个FROM
指令之前或者到文件的结束为止,执行的指令均属于该构建阶段。通过AS
指令来给这一构建阶段命名,同时指定了该阶段使用的基础镜像。在接下来的构建阶段,可以使用COPY --from=<stage>
指令来拷贝之前构建阶段的软件制成品到当前构建阶段,继续进行构建,直到最后一个构建阶段生成的镜像,才是最终的交付镜像。通过对最终构建阶段的基础镜像选择,可以只让交付镜像包含最小的运行时和需要交付软件制成品,使得镜像变小,上传下载更加迅速。
该特性是Docker 17.05版本的一部分,敬请期待,待版本稳定后,容器服务也会支持该特性
在FROM
指令中使用参数指定镜像版本
在Dockerfile中使用参数,并不是一件新鲜的事物,我们已经可以使用ARG
指令来传递参数给构建过程,这些参数的值在Dockerfile中是可变的,经常被用来传递版本号,密码,例如SSH的密钥等。现在通过参数来指定基础镜像的版本也成为了可能,示例如下:
ARG GO_VERSION=1.8
FROM golang:${GO_VERSION}
ADD . /src
WORKDIR /src
RUN go build
CMD ["/bin/app"]
通过使用上面的Dockerfile,我们可以构建基于另外一个GO语言版本的镜像:
$ docker build --arg=GO_VERSION=1.7 .
清理Docker资源
使用Docker的开发者经常会抱怨Docker占用了太多的存储空间,如果不定期清理,这确实是个问题。Docker增加了docker system
子命令来检查磁盘的使用空间,同时清理无用的资源。
下面的命令列出了磁盘使用情况:
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 7 5 1.247GB 769MB (61%)
Containers 7 2 115.9MB 99.23MB (85%)
Local Volumes 1 1 85.59MB 0B (0%)
你可以使用prune
来清理不再需要的资源:
$ docker system prune
WARNING! This will remove:
- all stopped containers
- all volumes not used by at least one container
- all networks not used by at least one container
- all dangling images
Are you sure you want to continue? [y/N] y
只清理特定子系统的资源也是支持的:
$ docker image/container/volume/network prune
指定端口时增加可读性
由于指定端口的语法让人困惑,Docker的使用者经常在理解和定义一个容器发布的端口时有困难。当你在使用或者定义容器的端口时,可能的格式如下:
ports:
- 3000
- 3000-3005
- 49100:22
- 9090-9091:8080-8081
- 127.0.0.1:8001:8080-8081
- 6060:7060/udp
当使用客户端时,这些语法还比较容易理解,但是当你需要在一个compose模版中定义许多这样的端口时,可读性就会变得很差。为了解决这一问题,现在你可以使用一个更加详细的格式来定义端口:
ports:
- target: 6060 # 容器端口
published: 7060 # 映射到主机暴露的端口
protocol: udp # 使用的协议
指定数据卷时增加可读性
跟端口一样,数据卷(volume)
也有类似的语法:
volumes:
- /var/lib/mysql
- /opt/data:/var/lib/mysql
- ./cache:/tmp/cached
- datavolume:/var/lib/mysql
- ~/configs/etc/configs/:ro
也增加了一个更加详细的语法来声明和指定数据卷(volume)
:
volumes:
- type: bind
source: ~/configs
target: /etc/configs
read_only: true