如何将docker容器打包成镜像?

本文最后更新于:2020年9月16日 上午

摘要

本文记录了一种不使用Dockerfile,将docker容器打包镜像的方法,番外篇讲解了给使用PPA安装了python的镜像安装第三方包的方法。

开头先交代背景

写这篇文章的原因,是我从事蜜罐制作有很长的时间了,制作蜜罐时,需要将做好的蜜罐程序打包成docker镜像交付。通常,打包docker镜像的过程就是编写一个Dockerfile,之后通过docker build命令就可以打包一个镜像了。

PS: Dockerfile的编写,可以理解为编写一堆命令,这堆命令做的事,就是基于一个基础镜像(这个镜像通常是选择官方的镜像),跑一个临时的容器,然后将代码添加到临时容器中并安装运行代码所需要的环境,最后将这个临时的容器打包成新的镜像,删除这个临时的镜像。

这种方式就容易出现一个问题,docker官方的ubuntu:16.04镜像并没有带python3.6的环境,这使得我每次更新镜像时,都需要在Dockerfile中写相关的命令,安装一遍python3.6环境。但是docker官方的ubuntu:16.04镜像源在国外,由于网络的原因,国内访问经常超时,我们肯定第一时间想到更换源,但是官方的镜像极度精简,替换源会出现一系列问题,至少折腾了半天我是不想折腾了。所以为了不出现因为网络的原因,一个Dockerfile跑10分钟,结果超时了,又需要重新跑一次的蛋疼局面,我总结了可以直接将一个安装好环境的容器打包成镜像的方法

那么以向官方的ubuntu镜像中安装python3.6为例,使用Dockerfile打包镜像和将容器打包成镜像的区别如何?

  • 通过Dockerfile
    • 基于官方ubuntu镜像,将安装python3.6的命令写入Dockerfile,任何一步超时了就从头再跑一遍。
  • 将容器打包成镜像
    • 运行一个容器
    • 在容器中安装python3.6,哪一步超时了,重复哪一步的过程即可。
    • 打包成镜像

无论使用哪种方式,我都强烈建议阅读文章的您,维护一个属于自己,具备自己程序所需环境的基础镜像,这样每次提交代码时,编写Dockerfile只需基于基础镜像,拷贝相关程序即可,非常节约时间。

打包镜像的全过程(所有命令一行一行复制运行)

本文以安装python3.6为例,讲述将容器打包成镜像的过程。在安装python3.6有两种思路:

  • 源码编译安装的方式
  • 通过PPA(Personal Package Archive)的apt工具包安装

我们打包、制作基础镜像的原则,是要保证镜像体积尽可能少,因为docker是分层的结构,基础镜像大了,即使Dockerfile中删除了一些包,最终镜像的体积也不会减小。在我实际测试的过程中,编译安装python3.6打包后的镜像体积会比通过PPA的apt工具包安装大三分之一左右,有可能是编译所需的环境没有卸载干净,但是也不重要了。

镜像制作过程

有条件的,请全程使用代理或者使用国外的VPSdocker官方ubuntu:16.04镜像经过专门的精简,其源国内访问有问题,替换源会导致一系列问题

  1. 宿主机上运行一个容器

    1
    docker run -it ubuntu:16.04
  1. 容器内更新源

    1
    apt-get update
  2. 容器内安装PPA

    1
    apt-get install software-properties-common
  3. 容器内安装python3.6并验证安装结果:

    1
    2
    3
    4
    add-apt-repository ppa:jonathonf/python-3.6
    apt-get update
    apt-get install python3.6
    python3.6 -V
  4. 容器内卸载PPA,缩小镜像体积:

    1
    2
    3
    4
    apt-get remove --purge software-properties-common
    apt-get autoremove
    apt-get autoclean
    dpkg -l |grep ^rc|awk '{print $2}' |xargs dpkg -P
  5. 退出容器,在容器内输入命令

    1
    exit
  6. 宿主机上提交镜像至本地仓库

    1
    sudo docker commit -m="ubuntu:16.04 with python3.6 env" 131123f077e9 myubuntu:test

    这里需要注意,131123f077e9 为刚才退出容器的ID,可以通过docker ps -a查看。

  7. 宿主机上查看提交到本地仓库的镜像

    1
    docker images |grep myubuntu

番外篇:给使用PPA安装python的镜像安装第三方库

由于通过PPA(Personal Package Archive)的apt工具包安装python环境的缘故,导致上述基础镜像并没有安装pip工具,这也是一个大坑,所以安装第三方库时,有两种思路。

  • 通过pip安装
    • 操作方便,但镜像体积略大
  • 通过setuptools安装
    • 安装时依赖问题麻烦,能缩小10 MB左右的镜像体积

所以,希望方便还是希望缩小10 MB左右的镜像体积,可以自行考虑。

通过pip安装

这种思路,是在宿主机上下载好pip的安装脚本拷贝进容器,安装好pip工具后,借助pip安装第三方库。

  1. 宿主机上,启动一个容器:

    1
    docker run --name silly_burnell -d myubuntu:test
  2. 宿主机上,下载安装脚本:

    1
    curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
  3. 宿主机上,拷贝安装脚本到容器(silly_burnell是容器名):

    1
    docker cp get-pip.py silly_burnell:/tmp
  4. 宿主机上输入命令,进入容器:

    1
    2
    docker start silly_burnell
    docker attach silly_burnell
  5. 容器内,安装pip

    1
    2
    3
    cd /tmp
    python3.6 get-pip.py
    rm get-pip.py
  6. 容器内,安装第三方库(以requests为例)

    1
    pip3.6 install requests
  7. 在容器内输入命令,退出容器

    1
    exit
  8. 宿主机上,提交镜像:

    1
    2
    docker commit -m "ubuntu:16.04 with requests env" silly_burnell myubuntu:installed_requests
    docker rm silly_burnell

通过setuptools安装

通常,我们找到的第三方库,在github上都有源码, 其支持通过setuptools来安装第三方库,这种方法的好处在于,可以减少镜像的体积(实测缩小10 Mb左右吧),毕竟不需要安装pip了,但缺点是,有的库有依赖,一个一个找依赖的库也确实挺麻烦的。

  1. 宿主机上,启动一个容器:

    1
    docker run --name silly_burnell -d myubuntu:test
  2. 宿主机上,下载setuptools及第三方库的源码:

    1
    2
    3
    4
    git clone https://github.com/pypa/setuptools.git
    git clone https://github.com/certifi/python-certifi.git
    git clone git://github.com/kennethreitz/requests.git

  3. 宿主机上,拷贝源码到容器(silly_burnell是容器名):

    1
    2
    3
    4
    docker cp setuptools silly_burnell:/tmp
    docker cp python-certifi silly_burnell:/tmp
    docker cp requests silly_burnell:/tmp

  4. 宿主机上,输入命令进入容器:

    1
    2
    3
    docker start silly_burnell
    docker attach silly_burnell

  5. 容器内,安装setuptool和第三方库:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    cd /tmp/setuptools
    python3.6 bootstrap.py
    python3.6 setup.py build
    python3.6 setup.py install
    cd /tmp/certifi
    python3.6 setup.py build
    python3.6 setup.py install
    cd /tmp/requests
    python3.6 setup.py build
    python3.6 setup.py install
    rm /tmp/* -rf
  6. 在容器内,输入命令,退出容器

    1
    exit
  7. 宿主机上,退出容器,提交镜像:

    1
    2
    docker commit "ubuntu:16.04 with requests env" silly_burnell myubuntu:installed_requests
    docker rm silly_burnell

尾巴

docker官方的ubuntu镜像真的精简到有些病态的状态了,除了能跑起来,常用的curlnc等等命令什么都没有,这种情况下,直接编写Dockerfile绝对是踩坑不断的。所以,自己跑一个容器,把环境安装好,重新打包成一个镜像备用,不失为一种良好的习惯。