容器化的过程当中老是免不了要构建镜像,一个体积更小的镜像除了可以节省机器的磁盘空间以外,还可以提高传输效率。这篇文章主要是想讲述一下本身在优化镜像体积时所采起的措施,固然并非全部方案都对减小镜像体积有明显效果,具体项目还要具体分析。这篇文章我以Rails项目的镜像构建做为例子。node
在优化镜像大小以前首先要知道为什么咱们所构建的镜像会这么大?下面是我项目中用于构建镜像的Dockerfile
文件mysql
FROM ruby:2.5.3
RUN apt-get update -y && apt-get install -y \
build-essential \
imagemagick \
default-libmysqlclient-dev
RUN apt-get install -y \
nodejs \
yarn
RUN rm -rf /var/lib/apt/lists/*
WORKDIR /beansmile-web
COPY . /beansmile-web
RUN bundle install
复制代码
镜像文件我定义得比较随意,它所构建出的镜像信息以下git
web1 latest 1a8a32d5253a 9 hours ago 1.26GB
复制代码
构建的镜像的过程跟日常基于一个操做系统打造供项目运行基础环境的过程差很少。只是平常的操做系统一般都不仅一个项目在运行,所以系统里所包含的东西是比较全面的。而镜像只指望提供给特定的项目使用,所以所依赖的东西比较有针对性,没必要要的东西尽可能不要加进去。github
针对上面的Dockerfile文件我以为有如下几个优化方向web
下面一条条来分析。sql
前面的例子最终构建出来的镜像体积十分庞大,主要归咎于相关的基础镜像自己就很大。docker
REPOSITORY TAG IMAGE ID CREATED SIZE
ruby 2.5.3 60c3a1518797 3 weeks ago 871MB
web1 latest 1a8a32d5253a 9 hours ago 1.26GB
复制代码
可见咱们的基础Ruby镜像自己就800多M了,构建镜像的过程还须要安装依赖,致使了最终的web
镜像体积会达到1.26G。这个体积可不利于网络传输,官方所提供的Ruby基础镜像有许多个版本,除了Ruby自己的版本不一样以外,还有许多基于不一样操做系统所构建的基础镜像能够选择,而这些不一样的操做系统所构建出来的Ruby基础镜像的体积相差甚大ubuntu
REPOSITORY TAG IMAGE ID CREATED SIZE
ruby 2.5.3-slim-stretch 20132a4ab93d 2 weeks ago 129MB
ruby 2.5.3 60c3a1518797 3 weeks ago 871MB
ruby 2.5.3-alpine b3361f13ff1f 3 weeks ago 43.6MB
复制代码
基于alpine
操做系统的Ruby镜像是最迷你的,只有43.6MB。slim-stretch
也是个不错的选择。或许采用更轻量级的镜像将会是一个优化的契机。缓存
经验小贴士: 从我本身的构建经验来看,采用slim-stretch
或许会是更加亲民的选择,它是Debian系,包管理器跟ubuntu
是同样的都是用apt-get
,用惯ubuntu
的人确定会以为比较亲切。alpine
所用的包管理器是apk
(是否是想到安卓的安装包?),一些经常使用包的命名有点不太同样须要本身慢慢去解决。*ruby
不过不管用哪一种方案都避免不了时间的投入,网上也没那么多现成的解决方案,迷你镜像的话你不得不本身安装一些构建过程当中所依赖的软件。
Docker官网对镜像的说法是,它是由一层层的只读层组成的,层次越少镜像的性能表现越出众。这也是官方建议咱们采用特定基础镜像去构建本身的项目镜像,而不是基于一个赤裸裸的操做系统镜像(如Ubuntu镜像)的缘由。
上述的例子中咱们用了三个RUN
命令,这会无心中多构建了两个层,其实咱们能够把它合并成一条RUN
命令
RUN apt-get update -y && apt-get install -y \
build-essential \
imagemagick \
default-libmysqlclient-dev \
nodejs \
yarn \
&& rm -rf /var/lib/apt/lists/*
复制代码
基于这个改动从新建立一个镜像web2
REPOSITORY TAG IMAGE ID CREATED SIZE
web2 latest 221a316a6903 14 minutes ago 1.25GB
web1 latest 1a8a32d5253a 9 hours ago 1.26GB
复制代码
可见这种改动对于缩减镜像体积效果并不明显
官方的说法是这样的
In older versions of Docker, it was important that you minimized the number of layers in your images to ensure they were performant.
咱们能够得出结论,或许缩减层数主要是为了让镜像操做起来更高效吧,减小层数这个优化方向对于缩减镜像体积并无多大的帮助,不过咱们这样作仍是有好处的。
从上面的配置能够看出,为了方便镜像的构建我直接把整个项目都移动到镜像中去(COPY
命令)。然而对于构建的镜像而言,并非全部的文件咱们都应该关心,最为值得关心的应该只有源码部分。因此我预想着在构建的镜像中能够把如下的目录剔除掉
PS: 固然每一个人对实际项目的考量会有所不一样,这几个目录只是根据我我的的项目状况所作的决定,并不具备通用性。
要忽略这些文件,咱们采用一个名为.dockerignore
的文件,把它放在当前的目录下便可,它的写法跟.gitignore
文件很类似,内容大概以下
/public/**
/tmp/**
/log/**
复制代码
而后从新构建镜像
web3 latest fb13cc1301b2 About a minute ago 1.2GB
web2 latest 221a316a6903 23 hours ago 1.25GB
web1 latest 1a8a32d5253a 33 hours ago 1.26GB
复制代码
这种方式的影响也不怎么大,这是由于目前我本地这些目录下所包含的“垃圾”资源所占的比重较小。
这个是官方推荐的方案,在Docker17.05以后能够使用
In Docker 17.05 and higher, you can do multi-stage builds and only copy the artifacts you need into the final image. This allows you to include tools and debug information in your intermediate build stages without increasing the size of the final image.
好像看起来有点复杂,不过它的原理大概就是先使用一个体积较大,依赖较为齐全的镜像来构建所须要的资源,而后把这些资源复制到一个轻量的基础镜像中,并继续咱们的镜像构建工做,这样就能够把原先庞大的基础镜像给抛弃了。这种作法能避免咱们最终的镜像中包含了一堆无用的依赖,在某种程度上可以减小最终镜像的体积。
这看起来是个很不错的策略,我也在项目中进行了尝试。咱们决定把bundle依赖包的安装以及,静态文件的编译都放到一个功能完备的基础镜像中去完成,而后把所须要的资源拷贝到一个轻量级的基础镜像中(相似alpine这种轻量级系统的相关镜像)再继续完成构建步骤。
不过我构建过程当中遇到以下问题
bin/rails c
须要依赖JS运行时,这不管对于开发仍是生产都是一个比较重要的操做,所以在最终镜像中舍弃JS运行时并非个好主意。前面提到了4个优化的方向,但彷佛最终只有
对最终的镜像体积影响较大。考虑到multi-stage
的解决方案所带来的好处可能还不如麻烦来得多,所以最终仍是舍弃了这个方案,与其这样绕来绕去还不如直接采用最精简的ruby:2.5.3-alpine
做为基础镜像来打造本身的项目镜像。选择一个精简的操做系统最大的问题就是在构建项目镜像过程当中的全部基础依赖都得本身一个个去解决,要投入很多的时间和精力,如下是我通过反复测试所获得的Dockerfile文件(仅供参考,毕竟你的项目所依赖的东西可能有所不一样)
FROM ruby:2.5.3-alpine
RUN apk --update --upgrade add \
# bundle 安装相关的依赖
git \
curl \
# mysql2 依赖
mysql-dev \
# 基础设施,好比gcc相关的东西
build-base \
# nokogiri 相关依赖
libxslt-dev \
libxml2-dev \
# 图片处理相关依赖
imagemagick \
# tz相关,若是没有bundle的时候会报错
tzdata \
nodejs \
yarn \
&& rm -rf /var/cache/apk/*
WORKDIR /beansmile-web
COPY . /beansmile-web/
RUN bundle install
复制代码
构建出来的镜像以下
web4 latest 71b75128d0d9 14 hours ago 586MB
复制代码
与以前的镜像相比体积大幅度减小了。这是一个咱们能够接受的大小了,考虑到时间成本就不进一步压缩了。
这篇文章主要简单总结了我的在缩减Rails项目镜像方面的探究。为了缩减镜像体积提出了4个主要的优化方向,用迷你的操做系统构建镜像的方式来减小镜像的体积的方式十分有效。不过不一样类型,基于不一样语言的项目可能会有不一样的侧重点,不能一律而论,可能有的项目中multi-stage
会帮你省下更多的时间。