导读

Kubernetes要从容器化开始,而容器又需要从Dockerfile开始,本文将介绍如何写出一个优雅的Dockerfile文件。

文章主要内容包括:

  • Docker容器

  • Dockerfile

  • 使用多阶构建

感谢公司提供大量机器资源及时间让我们可以实践,感谢在此专题上不断实践的部分项目及人员的支持。

一、Docker容器

1.1 容器的特点

我们都知道容器就是一个标准的软件单元,它有以下特点:

  • 随处运行:容器可以将代码与配置文件和相关依赖库进行打包,从而确保在任何环境下的运行都是一致的。

  • 高资源利用率:容器提供进程级的隔离,因此可以更加精细地设置CPU和内存的使用率,进而更好地利用服务器的计算资源。

  • 快速扩展:每个容器都可作为单独的进程予以运行,并且可以共享底层操作系统的系统资源,这样一来可以加快容器的启动和停止效率。

1.2 Docker容器

目前市面上的主流容器引擎有Docker、Rocket/rkt、OpenVZ/Odin等等,而独霸一方的容器引擎就是使用最多的Docker容器引擎。

Docker容器是与系统其他部分隔离开的一系列进程,运行这些进程所需的所有文件都由另一个镜像提供,从开发到测试再到生产的整个过程中,Linux 容器都具有可移植性和一致性。相对于依赖重复传统测试环境的开发渠道,容器的运行速度要快得多,并且支持在多种主流云平台(PaaS)和本地系统上部署。Docker容器很好地解决了“开发环境能正常跑,一上线就各种崩”的尴尬。

Docker容器的特点:

  • 轻量:容器是进程级的资源隔离,而虚拟机是操作系统级的资源隔离,所以Docker容器相对于虚拟机来说可以节省更多的资源开销,因为Docker容器不再需要GuestOS这一层操作系统了。

  • 快速:容器的启动和创建无需启动GuestOS,可以实现秒级甚至毫秒级的启动。

  • 可移植性:Docker容器技术是将应用及所依赖的库和运行时的环境技术改造包成容器镜像,可以在不同的平台运行。

  • 自动化:容器生态中的容器编排工作(如:Kubernetes)可帮助我们实现容器的自动化管理。

二、Dockerfile

Dockerfile是用来描述文件的构成的文本文档,其中包含了用户可以在使用行调用以组合Image的所有命令,用户还可以使用Docker build实现连续执行多个命令指今行的自动构建。

通过编写Dockerfile生磁镜像,可以为开发、测试团队提供基本一致的环境,从而提升开发、测试团队的效率,不用再为环境不统一而发愁,同时运维也能更加方便地管理我们的镜像。

Dockerfile的语法非常简单,常用的只有11个:

2.1 编写优雅地Dockerfile

编写优雅的Dockerfile主要需要注意以下几点:

  • Dockerfile文件不宜过长,层级越多最终制作出来的镜像也就越大。

  • 构建出来的镜像不要包含不需要的内容,如日志、安装临时文件等。

  • 尽量使用运行时的基础镜像,不需要将构建时的过程也放到运行时的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的好处是:当正在执行的过程某一层出错,对其进行修正后再次Build,前面已经执行完成的层不会再次执行。这样能大大减少下次Build的时间,而它的问题就是会因层级变多了而使镜像占用的空间也变大。

  • 第二个Dockerfile把所有的组件全部在一层解决,这样做能一定程度上减少镜像的占用空间,但在制作基础镜像的时候若其中某个组编译出错,修正后再次Build就相当于重头再来了,前面编译好的组件在一个层里,得全部都重新编译一遍,比较消耗时间。

从下表可以看出两个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来构建镜像。

3.1单文件构建

在多阶构建出来之前使用单个文件进行构建,单文件就是将所有的构建过程(包括项目的依赖、编译、测试、打包过程)全部包含在一个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 –vCMD [/go/bin/hello]

这种的做法会带来一些问题:

  • Dockerfile文件会特别长,当需要的东西越来越多的时候可维护性指数级将会下降;

  • 镜像层次过多,镜像的体积会逐步增大,部署也会变得越来越慢;

  • 代码存在泄漏风险。

以Golang为例,它运行时不依赖任何环境,只需要有一个编译环境,那这个编译环境在实际运行时是没有任务作用的,编译完成后,那些源码和编译器已经没有任务用处了也就没必要留在镜像里。

上表可以看到,单文件构建最终占用了312MB的空间。

3.2 多文件构建

在多阶构建出来之前有没有好的解决方案呢?有,比如采用多文件构建或在构建服务器上安装编译器,不过在构建服务器上安装编译器这种方法我们就不推荐了,因为在构建服务器上安装编译器会导致构建服务器变得非常臃肿,需要适配各个语言多个版本、依赖,容易出错,维护成本高。所以我们只介绍多文件构建的方式。

多文件构建,其实就是使用多个Dockerfile,然后通过脚本将它们进行组合。假设有三个文件分别是:Dockerfile.run、Dockerfile.build、build.sh。

  • Dockerfile.run就是运行时程序所必须需要的一些组件的Dockerfile,它包含了最精简的库;

  • Dockerfile.build只是用来构建,构建完就没用了;

  • build.sh的功能就是将Dockerfile.run和Dockerfile.build进行组成,把Dockerfile.build构建好的东西拿出来,然后再执行Dockerfile.run,算是一个调度的角色。

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完成项目的构建。

从上表可以看到,多文件构建大大减小了镜像的占用空间,但它有三个文件需要管理,维护成本也更高一些。

3.3 多阶构建

最后我们来看看万众期待的多阶构建。

完成多阶段构建我们只需要在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-envENV 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 -vFROM 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 镜像。

作者:王聪

内容来源:宜信技术学院

转载于:https://blog.51cto.com/14159827/2386102

程序员笔记|如何编写优雅的Dockerfile相关推荐

  1. GitHub 热点速览 Vol.24:程序员自我增值,优雅赚零花钱

    作者:HelloGitHub-小鱼干 摘要:升职加薪,出任 CTO,迎娶白富美/高帅富,走向人生巅峰是很多人的梦想.在本期的热点速览中你将了解自由作者 Easy 如何优雅赚取零花钱的方法,以及定投改变 ...

  2. 免费又好用的程序员笔记应用Boostnote for mac

    此文为原文转载 Boostnote笔记可以快速清晰的棒程序员创建一个组织良好的文章,并且轻松地记录开发知识的备忘录,会议笔记等,是专业为程序员设计的笔记本开源应用,另外,它识别超过100种语法高亮使其 ...

  3. 提高python 程序运行速度_3个Python函数帮程序员们避免编写循环,提高运行速度...

    图源:wired 诞生于1991年的Python,这几年突然火了.简历上有了Python,就业竞争力瞬间提升,甚至一些小学教材上都出现了Python内容.这种语言的热度超过了以往任何时候. 作为21世 ...

  4. java 代码重用需要注意的事项_程序员笔记|编写高性能的Java代码需要注意的4个问题...

    一.并发 无法创建新的本机线程...... 问题1:Java的中创建一个线程消耗多少内存? 每个线程有独自的栈内存,共享堆内存 问题2:一台机器可以创建多少线程? CPU,内存,操作系统,JVM,应用 ...

  5. 程序员笔记(知识)管理的一点经验

    记笔记这件事,也许在很多人看来,再普通.简单不过了--从小老师就教育我们要这么做.不同的人有不同的方式,我们最终的目的,还是希望不要停留在只是记录这一层面上,而是将它们转变为我们的知识.作为一个程序员 ...

  6. 写笔记插件_Java程序员笔记(知识)管理的一点经验

    记笔记这件事,也许在很多人看来,再普通.简单不过了--从小老师就教育我们要这么做.不同的人有不同的方式,我们最终的目的,还是希望不要停留在只是记录这一层面上,而是将它们转变为我们的知识.作为一个程序员 ...

  7. SVN黑马程序员笔记

    一.SVN概述 • SVN全称SubVersion • SVN是近年来崛起的版本管理工具,是CVS的接班人.目前,绝大多数软件公司都使用SVN作为代码版本管理软件. 特点:操作简单,入门容易 支持跨平 ...

  8. Python 黑马程序员 笔记

    目录 计算机组成 什么是计算机? 计算机是由什么组成的? 1. 硬件系统: 2. 软件系统: 3. 计算机是如何处理程序的? 4. 编程语言是什么? 5. 什么是Python? # 计算机组成原理和P ...

  9. 如何提升程序员的代码编写能力

    啊呀妈呀,那个熟悉的阿朱又回归了,呕心之作,跪了. 一.先列三个常见的开发场景: 1.拿到一个模块详细设计文档,大部分程序员的通常做法就是开始搭建界面代码,然后从第一个按钮点击事件或页面Load事件开 ...

最新文章

  1. HSF服务的开发与使用
  2. Linux安装jdk学习
  3. access开发精要(12)-文本 和 备注 数据类型格式(1)
  4. javascript / node.js / npm install 时 --save 和 --save-dev 的区别
  5. hdu 1535 spfa
  6. linux重启mysql一直_LINUX重启MYSQL的命令
  7. 为什么会出现“无法连接服务器-与网络有关或与实例有关的错误”?
  8. Linux kmalloc/kfree 源码解读
  9. 刚办的电信卡显示无服务器,刚刚买的电信卡怎么不能用说什么只限紧急呼叫
  10. golang 数组随机排序
  11. winpe下安装linux工具箱,(U盘中安装WinPE、Ubuntu、BT3、CDLinux系统和DOS工具箱等工具的方法.doc...
  12. 三菱fx2n64mr说明书_三菱PLC模块FX3U-64MR/DS使用手册
  13. ROS1/2 机器人编程实践汇总 kinetic/melodic/noetic foxy/galactic/humble
  14. 机器人编程趣味实践20-版本课程(教学)
  15. 树袋熊无线网络打印服务器,树袋熊
  16. Vue3使用组件库的tab切换echarts图表,图表出现宽度压缩变窄的问题
  17. AD17开发流程和经验技巧
  18. SqlServer 按日、周、月、季、年统计SQL语句
  19. 潍坊学院计算机考研,潍坊学院考研经验
  20. I51开发板----STC15F2K60S2教程

热门文章

  1. (区间dp 或 记忆化搜素 )Brackets -- POJ -- 2955
  2. sqlserver导入excel的电话号码(身份证)变为科学计数解决方式
  3. javascript 利用 - 枚举思想 - 添加地名的一个小例子
  4. android String.format
  5. TCP编程函数和步骤
  6. C#的6种常用集合类大比拼【月儿原创】
  7. GCC生成的汇编代码
  8. ASP.NET2.0 GridView小技巧汇粹
  9. 深度学习开源库tiny-dnn的使用(MNIST)
  10. OpenCV代码提取:resize函数的实现