这样做,能让我的Docker镜像更轻巧

Posted by Naah on Sunday, Jun 23,2019 14:12:25

1 打造最小docker基础镜像

1.1 使用alpine-glib作为基础镜像

Alpine 操作系统是一个面向安全的轻型 Linux 发行版。采用了 musl libc 和 busybox 以减小系统的体积和运行时资源消耗,但功能上比 busybox 又完善的多,因此得到开源社区越来越多的青睐,相比于其他 Docker 镜像,它的容量非常小,仅仅只有 5 MB 左右

但是由于Alpine是基于MUSL libc(mini libc)
而我们日常使用的很多环境都需要GUN Standard C library(glibc)

所以我们选择带有glibAlpine镜像:docker.io/jeanblanchard/alpine-glibc(12M)
docker.io/jeanblanchard/alpine-glibc

1.2 优化你的alpine-glibc

alpine-glibc虽然解决了Alpine没有glib的问题,但是还是有许多坑等着我们踩的!

如果你没看过这篇文章,你怕是要吃苦啦,要反反复复的打基础包,然后打语言包,然后打应用包..

在这里我要优化我们的alpine-glibc镜像,来解决一些后面可能会踩到的坑

  1. 编码问题(解决乱码问题)
  2. 时区问题(默认可是UTC+0)
  3. apk镜像源问题(安装加速)
  4. bash问题(默认只有sh,没有bash,有些软件是要用bash的,比如conda
FROM docker.io/jeanblanchard/alpine-glibc

MAINTAINER Naah

# 修改编码为utf-8
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8

# 增加镜像源
RUN echo "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.9/main/" > /etc/apk/repositories

# 修改时区为上海时区
RUN apk add tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo "Asia/Shanghai" > /etc/timezone \
    && apk del tzdata

# 安装bash
RUN apk update \
        && apk upgrade \
        && apk add --no-cache bash \
        && rm -rf /var/cache/apk/*

2 优化你的Dockerfile

要想优化我们的Dockerfile,首先我们要知道Docker的镜像原理是什么样的!

Unable to find image 'jeanblanchard/alpine-glibc:latest' locally
latest: Pulling from jeanblanchard/alpine-glibc
921b31ab772b: Pull complete
a8498d4f0312: Pull complete
Digest: sha256:d42d763ab3db4eb2164b0beccab0860bcb4cd087fe7c3b0ed5eca464e3efbf67
Status: Downloaded newer image for jeanblanchard/alpine-glibc:latest

通过上面的docker pull日志,我们发现有很多次的Pull complete,这是为啥呢?

2.1 docker 镜像基础

docker 镜像就是一个多层存储的文件,docker使用sha256来确定是否需要下载

分层存储会带来两个优点:

2.1.1 分层机制

1.分层镜像容易扩展

下面我们就举个栗子! 比如我们可以基于一个alpine-glibc镜像去构建我们的jre镜像python镜像php镜像
这样我们只需要在alpine-glibc镜像的基础上面加jre环境就可以完成我们的jre镜像了,这也符合我们的复用实现

2.分层镜像的存储节省磁盘空间

假设我们不使用alpine-glibc,使用centos7镜像(100+M)

不使用分层机制:

我们基于centos7镜像构造jre镜像python镜像php镜像
当我们下载这些镜像的时候,每一个都要全部拉取,每一次都100+M或更多
那么我们要通过网络下载至少:(100+M)* 3 + jre层 + python层 + php层

使用分层机制:

如果我们使用分层机制,那么我们只需要拉取centos7层jre层python层等等, 当我们下周这些镜像的时候,每次只需要下载jre层python层php层
那么我们要通过网络下载仅需要:(100+M)* 1 + jre层 + python层 + php层

特别在生产中,我们会通过监控手段来对服务进行自动弹性伸缩,那么就需要在节点下载镜像,使用分层机制,这样,在紧急大流量场景下,我们就可以快速拉取服务镜像,进行快速扩容

2.1.2 怎么分层

通过上面的阅读,我们知道docker使用了分层机制来构建镜像,那么docker是如何分层的呢?

这就涉及到我们的Dockerfile了!

Dockerfile中,我们每一条命令都会被docker构建一层!

2.2 优化Dockerfile

2.2.1 减少Dockerfile的命令数

  1. 通过合并命令来减少层数(&&)
  2. 需要删除的文件在同一层内进行删除(如果在下层删除是没有用的!)

比如下方的这个Dockerfile,可以说是漏洞百出,

  1. 11行的rm是没有效果,不会减少反而会增加docker镜像的大小
  2. 命令太多,很多命令是可以进行合并的

优化前:269M

#registry.plt.babytree-inc.com/base/python:3.6.8
FROM registry.plt.babytree-inc.com/base/alpine-glib-shanghaizone:1.0

MAINTAINER Naah
ADD conda.tar.gz /opt

ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
ENV PATH /opt/conda/bin:$PATH

RUN ln -s /opt/conda/etc/profile.d/conda.sh /etc/profile.d/conda.sh
RUN conda clean -p -t -y -all
RUN rm -rf /opt/conda/pkgs

RUN conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
RUN conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
RUN conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
RUN conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
RUN conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/
RUN conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/menpo/
RUN conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
RUN conda config --set show_channel_urls yes

WORKDIR ~
RUN rm -rf ~/.pip/pip.conf
RUN mkdir ~/.pip && echo "[global] " >> ~/.pip/pip.conf
RUN echo "trusted-host=mirrors.aliyun.com " >> ~/.pip/pip.conf
RUN echo "index-url=https://mirrors.aliyun.com/pypi/simple/ " >> ~/.pip/pip.conf
RUN cat ~/.pip/pip.conf

CMD [ "/bin/sh" ]

优化后:172M

#registry.plt.babytree-inc.com/base/python:3.6.8
FROM registry.plt.babytree-inc.com/base/alpine-glib-shanghaizone:1.0

MAINTAINER Naah
ADD conda.tar.gz /opt

ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
ENV PATH /opt/conda/bin:$PATH


RUN ln -s /opt/conda/etc/profile.d/conda.sh /etc/profile.d/conda.sh \
    && rm -rf ~/.pip && mkdir ~/.pip && echo "[global] " >> ~/.pip/pip.conf \
    && echo "trusted-host=mirrors.aliyun.com " >> ~/.pip/pip.conf \
    && echo "index-url=https://mirrors.aliyun.com/pypi/simple/ " >> ~/.pip/pip.conf \
    && cat ~/.pip/pip.conf \
    && echo "channels:" > ~/.condarc \
    && echo "  - defaults" > ~/.condarc \
    && echo "show_channel_urls: true" > ~/.condarc \
    && echo "default_channels:" > ~/.condarc \
    && echo "  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main" > ~/.condarc \
    && echo "  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free" > ~/.condarc \
    && echo "  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r" > ~/.condarc \
    && echo "custom_channels:" > ~/.condarc \
    && echo "  conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud" > ~/.condarc \
    && echo "  msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud" > ~/.condarc \
    && echo "  bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud" > ~/.condarc \
    && echo "  menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud" > ~/.condarc \
    && echo "  pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud" > ~/.condarc \
    && echo "  simpleitk: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud" > ~/.condarc \
    && conda config --set show_channel_urls yes \
    && conda clean -p -t -y -all \
    && rm -rf /opt/conda/pkgs \
    && rm -rf ~/.cache/pip
    
CMD [ "/bin/sh" ]

在这里也教大家一个命令,可以查看镜像的分层信息,让我们清楚的知道哪层需要优化!

 docker history 镜像名