Kubernetes要从容器化开始,而容器又须要从Dockerfile开始,本文将介绍如何写出一个优雅的Dockerfile文件。python
文章主要内容包括:linux
感谢公司提供大量机器资源及时间让咱们能够实践,感谢在此专题上不断实践的部分项目及人员的支持。git
咱们都知道容器就是一个标准的软件单元,它有如下特色:github
目前市面上的主流容器引擎有Docker、Rocket/rkt、OpenVZ/Odin等等,而独霸一方的容器引擎就是使用最多的Docker容器引擎。golang
Docker容器是与系统其余部分隔离开的一系列进程,运行这些进程所需的全部文件都由另外一个镜像提供,从开发到测试再到生产的整个过程当中,Linux 容器都具备可移植性和一致性。相对于依赖重复传统测试环境的开发渠道,容器的运行速度要快得多,而且支持在多种主流云平台(PaaS)和本地系统上部署。Docker容器很好地解决了“开发环境能正常跑,一上线就各类崩”的尴尬。docker
Docker容器的特色:ubuntu
Dockerfile是用来描述文件的构成的文本文档,其中包含了用户能够在使用行调用以组合Image的全部命令,用户还可使用Docker build实现连续执行多个命令指今行的自动构建。服务器
经过编写Dockerfile生磁镜像,能够为开发、测试团队提供基本一致的环境,从而提高开发、测试团队的效率,不用再为环境不统一而发愁,同时运维也能更加方便地管理咱们的镜像。运维
Dockerfile的语法很是简单,经常使用的只有11个:测试
编写优雅的Dockerfile主要须要注意如下几点:
只要记住以上三点就能写出不错的Dockerfile。
为了方便你们了解,咱们用两个Dockerfile实例进行简单的对比:
FROM ubuntu:16.04 RUN apt-get update RUN apt-get install -y apt-utils libjpeg-dev \ python-pip RUN pip install --upgrade pip RUN easy_install -U setuptools RUN apt-get clean
FROM ubuntu:16.04 RUN apt-get update && apt-get install -y apt-utils \ libjpeg-dev python-pip \ && pip install --upgrade pip \ && easy_install -U setuptools \ && apt-get clean
咱们看第一个Dockerfile,乍一看条理清晰,结构合理,彷佛还不错。再看第二个Dockerfile,紧凑,不易阅读,为何要这么写?
从下表能够看出两个Dockerfile所编译出来的镜像大小:
$ docker images | grep ubuntu REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu 16.04 9361ce633ff1 1 days ago 422MB ubuntu 16.04-1 3f5b979df1a9 1 days ago 412MB
呃…. 好像并无特别的效果,但若Dockerfile很是长的话能够考虑减小层次,由于Dockerfile最高只能有127层。
Docker在升级到Docker 17.05以后就能支持多阶构建了,为了使镜像更加小巧,咱们采用多阶构建的方式来打包镜像。在多阶构建出现以前咱们一般使用一个Dockerfile或多个Dockerfile来构建镜像。
在多阶构建出来以前使用单个文件进行构建,单文件就是将全部的构建过程(包括项目的依赖、编译、测试、打包过程)所有包含在一个Dockerfile中之下:
FROM golang:1.11.4-alpine3.8 AS build-env ENV GO111MODULE=off ENV GO15VENDOREXPERIMENT=1 ENV BUILDPATH=github.com/lattecake/hello RUN mkdir -p /go/src/${BUILDPATH} COPY ./ /go/src/${BUILDPATH} RUN cd /go/src/${BUILDPATH} && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go install –v CMD [/go/bin/hello]
这种的作法会带来一些问题:
以Golang为例,它运行时不依赖任何环境,只须要有一个编译环境,那这个编译环境在实际运行时是没有任务做用的,编译完成后,那些源码和编译器已经没有任务用处了也就不必留在镜像里。
上表能够看到,单文件构建最终占用了312MB的空间。
在多阶构建出来以前有没有好的解决方案呢?有,好比采用多文件构建或在构建服务器上安装编译器,不过在构建服务器上安装编译器这种方法咱们就不推荐了,由于在构建服务器上安装编译器会致使构建服务器变得很是臃肿,须要适配各个语言多个版本、依赖,容易出错,维护成本高。因此咱们只介绍多文件构建的方式。
多文件构建,其实就是使用多个Dockerfile,而后经过脚本将它们进行组合。假设有三个文件分别是:Dockerfile.run、Dockerfile.build、build.sh。
Dockerfile.build
FROM golang:1.11.4-alpine3.8 AS build-env ENV GO111MODULE=off ENV GO15VENDOREXPERIMENT=1 ENV BUILDPATH=github.com/lattecake/hello RUN mkdir -p /go/src/${BUILDPATH} COPY ./ /go/src/${BUILDPATH} RUN cd /go/src/${BUILDPATH} && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go install –v
Dockerfile.run
FROM alpine:latest RUN apk –no-cache add ca-certificates WORKDIR /root ADD hello . CMD ["./hello"]
Build.sh
#!/bin/sh docker build -t –rm hello:build . -f Dockerfile.build docker create –name extract hello:build docker cp extract:/go/bin/hello ./hello docker rm -f extract docker build –no-cache -t –rm hello:run . -f Dockerfile.run rm -rf ./hello
执行build.sh完成项目的构建。
从上表能够看到,多文件构建大大减少了镜像的占用空间,但它有三个文件须要管理,维护成本也更高一些。
最后咱们来看看万众期待的多阶构建。
完成多阶段构建咱们只须要在Dockerfile中屡次使用FORM声明,每次FROM指令可使用不一样的基础镜像,而且每次FROM指令都会开始新的构建,咱们能够选择将一个阶段的构建结果复制到另外一个阶段,在最终的镜像中只会留下最后一次构建的结果,这样就能够很容易地解决前面提到的问题,而且只须要编写一个Dockerfile文件。这里值得注意的是:须要确保Docker的版本在17.05及以上。下面咱们来讲说具体操做。
在Dockerfile里可使用as来为某一阶段取一个别名”build-env”:
FROM golang:1.11.2-alpine3.8 AS build-env
而后从上一阶段的镜像中复制文件,也能够复制任意镜像中的文件:
COPY –from=build-env /go/bin/hello /usr/bin/hello
看一个简单的例子:
FROM golang:1.11.4-alpine3.8 AS build-env ENV GO111MODULE=off ENV GO15VENDOREXPERIMENT=1 ENV GITPATH=github.com/lattecake/hello RUN mkdir -p /go/src/${GITPATH} COPY ./ /go/src/${GITPATH} RUN cd /go/src/${GITPATH} && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go install -v FROM alpine:latest ENV apk –no-cache add ca-certificates COPY --from=build-env /go/bin/hello /root/hello WORKDIR /root CMD ["/root/hello"]
执行docker build -t –rm hello3 .后再执行docker images ,而后咱们来看镜像的大小:
多阶构建给咱们带来不少便利,最大的优点是在保证运行镜像足够小的状况下还减轻了Dockerfile的维护负担,所以咱们极力推荐使用多阶构建来将你的代码打包成Docker 镜像。
做者:王聪
内容来源:宜信技术学院