runsisi's

technical notes

docker 镜像制作

2019-09-07 runsisi#docker

基于已有的容器制作镜像:

~$ docker commit 1a297846df5b runsisi:clove
sha256:2c7b5d9d87c755179a003a90aecbd94539151921efbf9af6846cc701677fee9f
~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
runsisi             clove               2c7b5d9d87c7        4 seconds ago       201 MB
ubuntu              14.04               f17b6a61de28        2 weeks ago         188 MB
ubuntu              trusty-20180420     8cef1fa16c77        7 months ago        223 MB

然后基于制作的新镜像创建容器:

~$ docker run -it runsisi:clove /bin/bash
root@35f682c836e9:/#

此外,基于 Dockerfile 制作容器镜像时一种更为常见的的方式,下面分别以制作 Ubuntu 14.04 和 CentOS 7 两个镜像为例进行说明。

Dockerfile 中的每一个 RUN 命令都会形成一层镜像,因此如果 RUN 命令执行的结果包含临时文件,最好在同一个 RUN 命令中进行清理,否则临时文件会极大的增加镜像的大小(这也是几乎所有的 Dockerfile 都会用 && 连接所有命令的原因)。

制作 Ubuntu 容器镜像

~$ ls
Dockerfile supervisord.conf

Dockerfile

FROM ubuntu:14.04.3
MAINTAINER runsisi <runsisi@hust.edu.cn>

# Note: The space before semicolon is important in Dockerfile, useless in normal shell script though
# for dpkg-divert, please refer to https://bugs.launchpad.net/ubuntu/+source/systemd/+bug/1325142
# for DEBIAN_FRONTEND, please refer to https://github.com/docker/docker/issues/4032
# for HOME, please refer to https://github.com/docker/docker/issues/2968

RUN dpkg-divert --local --add /etc/init.d/systemd-logind && ln -s /bin/true /etc/init.d/systemd-logind && \
    apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y openssh-server supervisor \
    language-pack-en language-pack-zh-hans \
    lsb-release dpkg-dev debhelper git vim-nox \
    sshpass tree lsof tcpdump \
    build-essential autoconf automake libtool pkg-config \
    python python-argparse python-pip python-virtualenv && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

RUN echo 'root:123456' | chpasswd && \
    useradd -m -s '/bin/bash' runsisi && \
    echo 'runsisi:123456' | chpasswd && \
    echo 'runsisi ALL=(ALL:ALL) NOPASSWD: ALL' > /etc/sudoers.d/runsisi && \
    mkdir -p /var/run/sshd /var/log/supervisor && \
    sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/g' /etc/ssh/sshd_config && \
    sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/g' /etc/ssh/sshd_config && \
    sed -i 's/#GSSAPIAuthentication no/GSSAPIAuthentication no/g' /etc/ssh/sshd_config && \
    sed -i '$aUseDNS no' /etc/ssh/sshd_config && \
    sed -i '$aalias sudo="sudo -E"' /etc/bash.bashrc && \
    sed -i '$aalias vi="vim"' /etc/bash.bashrc

COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

EXPOSE 22
ENV HOME /home/runsisi
ENV LC_ALL en_US.UTF-8
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

supervisord.conf

[supervisord]
nodaemon=true

[program:sshd]
command=/usr/sbin/sshd -D

然后在 Dockerfile 所在目录执行 docker build:

~$ docker build --build-arg http_proxy=http://10.120.155.xxx:12345 --build-arg https_proxy=http://10.120.155.xxx:12345 --tag runsisi:xxx .
Sending build context to Docker daemon 4.608 kB
Step 1/9 : FROM ubuntu:14.04.3
 ---> 3876b81b5a81
Step 2/9 : MAINTAINER runsisi <runsisi@hust.edu.cn>
 ---> Using cache
 ---> 15ce53fdd680
Step 3/9 : RUN dpkg-divert --local --add /etc/init.d/systemd-logind && ln -s /bin/true /etc/init.d/systemd-logind &&     apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y openssh-server supervisor     language-pack-en language-pack-zh-hans     lsb-release dpkg-dev debhelper git subversion vim-nox     sshpass tree lsof tcpdump     build-essential autoconf automake libtool pkg-config     python python-argparse python-pip python-virtualenv &&     apt-get clean &&     rm -rf /var/lib/apt/lists/*
 ---> Running in b45d4049fa4e
Adding 'local diversion of /etc/init.d/systemd-logind to /etc/init.d/systemd-logind.distrib'
Ign http://archive.ubuntu.com trusty InRelease
Get:1 http://archive.ubuntu.com trusty-updates InRelease [65.9 kB]
Get:2 http://archive.ubuntu.com trusty-security InRelease [65.9 kB]
Hit http://archive.ubuntu.com trusty Release.gpg
Get:3 http://archive.ubuntu.com trusty-updates/main Sources [526 kB]
Get:4 http://archive.ubuntu.com trusty-updates/restricted Sources [6449 B]
...
Step 8/9 : ENV LC_ALL en_US.UTF-8
 ---> Running in a62caa0125c6
 ---> 6ce6fce4d584
Removing intermediate container a62caa0125c6
Step 9/9 : CMD /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
 ---> Running in 42bd3293c888
 ---> ce9024ce6631
Removing intermediate container 42bd3293c888
Successfully built ce9024ce6631

注意其中 --build-arg 用来指定代理等环境变量,这里的代理和前面为 docker pull 或者 docker run 设置的代理并不一样,这里的代理是构建过程中容器进程使用的代理。

生成的容器镜像如下:

~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
runsisi             xxx                 ce9024ce6631        9 minutes ago       528 MB
...

制作 CentOS 容器镜像

~$ scp <CentOS 7.x host>:/etc/rc.d/init.d/functions .
~$ ls
Dockerfile  functions  supervisord.conf

Dockerfile

FROM centos:7
MAINTAINER runsisi <runsisi@hust.edu.cn>

RUN rm -f /etc/yum.repos.d/* && \
/bin/bash -c $'cat <<-EOF > /etc/yum.repos.d/hust.repo\n'\
$'[os]\n'\
$'name = os\n'\
$'gpgcheck = 0\n'\
$'baseurl = http://mirrors.hust.edu.cn/centos/7/os/x86_64\n'\
$'\n'\
$'[extras]\n'\
$'name = extras\n'\
$'gpgcheck = 0\n'\
$'baseurl = http://mirrors.hust.edu.cn/centos/7/extras/x86_64\n'\
$'\n'\
$'[updates]\n'\
$'name = updates\n'\
$'gpgcheck = 0\n'\
$'baseurl = http://mirrors.hust.edu.cn/centos/7/updates/x86_64\n'\
$'\n'\
$'[epel]\n'\
$'name = epel\n'\
$'gpgcheck = 0\n'\
$'baseurl = http://mirrors.hust.edu.cn/epel/7/x86_64\n'\
$'EOF' && \
    sed -i 's/enabled=1/enabled=0/g' /etc/yum/pluginconf.d/fastestmirror.conf && \
    yum install -y redhat-lsb-core sudo deltarpm openssh-server supervisor \
    sshpass which tree wget lsof vim-enhanced iproute tcpdump \
    git python-pip python-virtualenvwrapper \
    gcc-c++ && \
    yum clean all && \
    rm -f /root/anaconda-ks.cfg && \
    rm -f /anaconda-post.log && \
    rm -rf /tmp/* && \
    mv -f /etc/yum.repos.d/{hust.repo,hust.repo.bak}

RUN sed -i 's/Defaults\s\+requiretty/Defaults !requiretty/g' /etc/sudoers && \
    echo 'root:123456' | chpasswd && \
    useradd -m -s '/bin/bash' runsisi && \
    echo 'runsisi:123456' | chpasswd && \
    echo 'runsisi ALL=(ALL:ALL) NOPASSWD: ALL' > /etc/sudoers.d/runsisi && \
    sed -i 's/\[ "$PS1" = "\\\\s-\\\\v\\\\\\$ " \] \&\& PS1="\[\\u@\\h \\W\]\\\\$ "/\[ "$PS1" = "\\\\s-\\\\v\\\\\\$ " \] \&\& PS1="\[\\u@\\h \\w\]\\\\$ "/g' /etc/bashrc && \
    sed -i 's/#PermitRootLogin yes/PermitRootLogin yes/g' /etc/ssh/sshd_config && \
    sed -i 's/GSSAPIAuthentication yes/GSSAPIAuthentication no/g' /etc/ssh/sshd_config && \
    sed -i 's/#UseDNS yes/UseDNS no/g' /etc/ssh/sshd_config && \
    sed -i '$aalias sudo="sudo -E"' /etc/bashrc && \
    sed -i '$aalias vi="vim"' /etc/bashrc && \
    mkdir -p /var/run/sshd /var/log/supervisor

COPY supervisord.conf /etc/supervisord.d/supervisord.ini
COPY functions /etc/rc.d/init.d/functions
RUN sshd-keygen

EXPOSE 22
#USER runsisi
#WORKDIR /home/runsisi
ENV HOME /home/runsisi
ENV LC_ALL en_US.UTF-8
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

supervisord.conf

[supervisord]
nodaemon=true

[program:sshd]
command=/usr/sbin/sshd -D

然后在 Dockerfile 所在目录执行 docker build:

~$ docker build --build-arg http_proxy=http://10.120.155.xxx:12345 --build-arg https_proxy=10.120.155.xxx:12345 --tag runsisi:ccc .
Sending build context to Docker daemon 23.55 kB
Step 1/11 : FROM centos:7
 ---> 1e1148e4cc2c
Step 2/11 : MAINTAINER runsisi <runsisi@hust.edu.cn>
 ---> Running in 6058ef2f0ce3
 ---> 2507d30220fd
Removing intermediate container 6058ef2f0ce3
Step 3/11 : RUN rm -f /etc/yum.repos.d/* && /bin/bash -c $'cat <<-EOF > /etc/yum.repos.d/hust.repo\n'$'[os]\n'$'name = os\n'$'gpgcheck = 0\n'$'baseurl = http://mirrors.hust.edu.cn/centos/7/os/x86_64\n'$'\n'$'[extras]\n'$'name = extras\n'$'gpgcheck = 0\n'$'baseurl = http://mirrors.hust.edu.cn/centos/7/extras/x86_64\n'$'\n'$'[updates]\n'$'name = updates\n'$'gpgcheck = 0\n'$'baseurl = http://mirrors.hust.edu.cn/centos/7/updates/x86_64\n'$'\n'$'[epel]\n'$'name = epel\n'$'gpgcheck = 0\n'$'baseurl = http://mirrors.hust.edu.cn/epel/7/x86_64\n'$'EOF' &&     sed -i 's/enabled=1/enabled=0/g' /etc/yum/pluginconf.d/fastestmirror.conf &&     yum install -y redhat-lsb-core sudo deltarpm openssh-server supervisor     sshpass which tree wget lsof vim-enhanced iproute tcpdump     git python-pip python-virtualenvwrapper     gcc-c++ &&     yum clean all &&     rm -f /root/anaconda-ks.cfg &&     rm -f /anaconda-post.log &&     rm -rf /tmp/* &&     mv -f /etc/yum.repos.d/{hust.repo,hust.repo.bak}
 ---> Running in 6cbe4165b12c
Loaded plugins: ovl
Resolving Dependencies
--> Running transaction check
---> Package deltarpm.x86_64 0:3.6-3.el7 will be installed
---> Package gcc-c++.x86_64 0:4.8.5-36.el7 will be installed
--> Processing Dependency: libstdc++-devel = 4.8.5-36.el7 for package: gcc-c++-4.8.5-36.el7.x86_64
...
Step 10/11 : ENV LC_ALL en_US.UTF-8
 ---> Running in 495e253944f2
 ---> 866b1069aa97
Removing intermediate container 495e253944f2
Step 11/11 : CMD /usr/bin/supervisord -c /etc/supervisord.conf
 ---> Running in a7cc86e4e502
 ---> 1de1d0c5d923
Removing intermediate container a7cc86e4e502
Successfully built 1de1d0c5d923

生成的容器镜像如下:

~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
runsisi             ccc                 1de1d0c5d923        About a minute ago   400 MB
...

导出、导入容器镜像

当需要在机器之间以离线的方式传递容器镜像时,可以使用如下的方式。

首先在镜像所在的机器进行 docker save,将镜像保存到一个压缩文件:

~$ docker save ubuntu:14.04 | xz -0 > ubuntu14.04.tar.xz
~/docker/14.04.3$ ll -h ubuntu14.04.tar.xz
-rw-rw-r-- 1 runsisi runsisi 52M Dec 17 10:36 ubuntu14.04.tar.xz

然后在需要导入的机器进行 docker load,将镜像从压缩文件中进行加载:

~$ docker load -i ubuntu14.04.tar.xz
Loaded image: ubuntu:14.04

加载得到的镜像如下:

~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
...
ubuntu              14.04               f17b6a61de28        3 weeks ago         188 MB
...

如果导入所在的节点 1)已存在同名(即 name:tag 相同)的镜像,2)镜像的 id 不同,且 3)只有唯一的一个 tag 引用该镜像,则会将原有的镜像重命名为 :,如下所示:

~$ docker load -i ubuntu14.04.tar.xz
The image ubuntu:14.04 already exists, renaming the old one with ID sha256:1de1d0c5d923376fe90179c7db5d1b92a2245aab0e5b21972d9cf39c245135b3 to empty string
Loaded image: ubuntu:14.04
~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
<none>              <none>              1de1d0c5d923        30 minutes ago      400 MB
...

如果有多个 tag 引用该镜像,则只是简单的删除这个重名的 tag 而已。