Docker 操作指南之构建镜像篇

Posted zuozewei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Docker 操作指南之构建镜像篇相关的知识,希望对你有一定的参考价值。

一、构建自己的 image

构建自己的镜像,有两种方法:

  • 通过 docker commit
  • 通过 Dockerfile

1、通过docker commit(提交)命令

Usage:  docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
Create a new image from a container's changes
Options:
  -a, --author string    Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
  -c, --change list      Apply Dockerfile instruction to the created image (default [])
      --help             Print usage
  -m, --message string   Commit message
  -p, --pause            Pause container during commit (default true)

将现有的容器固化成镜像。

1.1、实例

下面我们创建一个可以 ssh 的镜像:

  1. 启动一个容器:
docker run -i -t ubuntu:16.04 /bin/bash    
  1. 启动成功后,登录容器,执行所需要的操作:
root@4aa51652291a:/# apt-get update     
root@4aa51652291a:/# apt-get install openssh-server     
# mkdir /var/run/sshd  #不创建该目录,ssh的时候会报错     
passwd #输入用户密码,我这里设置为123456,便于SSH客户端登陆使用     
# vi /etc/ssh/sshd_config     
更改为:PermitRootLogin yes   #允许客户端通过root用户ssh     
service ssh restart     
exit #退出容器     
  1. 保存镜像
$ docker commit 4aa51652291a ubuntusshd     
5f5a2a905d89fa2daf3b9537fa0cf5e0c83ad0e95d6078420d2a582080120418     
  1. 以后台方式运行容器
docker run -d -p 50022:22 ubuntusshd /usr/sbin/sshd –D     

ubuntu容器内运行着的 SSH Server 占用 22 端口,对外为 50022 端口。

  1. 查看容器是否运行
$ docker ps     
CONTAINER ID        IMAGE               COMMAND               CREATED             STATUS              PORTS                   NAMES     
61bb4b7d981f        ubuntusshd:v2       "/usr/sbin/sshd -D"   4 minutes ago       Up 4 minutes        0.0.0.0:50022->22/tcp   sad_pasteur     
  1. 客户端连接
ssh root@127.0.0.1 -p 50022

2、用 Dockerfile 文件构建映像

使用 docker commit 来扩展一个 image 比较简单,但它不容易在一个团队中分享它。我们使用 docker build 来创建一个新的image。为此,我们需要创建一个 dockerfile,包含一些如何创建我们的 image 的指令。

2.1、创建一个 Dockerfile

$ vi Dockerfile

加入如下内容:

FROM ubuntu:14.04     
MAINTAINER 7d  
RUN apt-get install haproxy     
RUN apt-get install –y openssh-server
  • 使用 # 来注释
  • ROM 指令告诉 docker 使用哪个 image 源,
  • 接着是维护者的信息
  • 最后,我们指定了 1 条 run 指令。每一条 run指 令在 image 执行一条命令,比如安装一个软件包,在这里我们使用 apt 来安装了一些软件。apt-get install –y xxxx 确认安装该软件,不会在安装过程中提示确认。

2.2、创建 image

docker build -t="user1/web:v2" .     
Sending build context to Docker daemon 2.048 kB     
Sending build context to Docker daemon     
Step 0 : FROM ubuntu:14.04     
 ---> 07f8e8c5e660     
Step 1 : MAINTAINER 7d     
 ---> Using cache     
 ---> 857c5cf770aa     
Step 2 : RUN apt-get install haproxy     
 ---> Using cache     
 ---> 3e333b27becb     
Successfully built 3e333b27becb

2.3、查看是否存在该 images

# docker images     
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE     
user1/web           v2                  3e333b27becb        3 hours ago         190 MB

3、Dockerfile 指令总结

3.1、FROM

格式为 FROM <image> 或 FROM <image>:<tag> 

第一条指令必须为 FROM 指令。并且,如果在同一个 Dockerfile 中创建多个镜像时,可以使用多个 FROM 指令(每个镜像一次)。

3.2、MAINTAINER

格式为 MAINTAINER <name> ,指定维护者信息

3.3、RUN

格式为 RUN <command> 或 RUN ["executable", "param1", "param2"]   

前者将在 shell 终端中运行命令,即 /bin/sh -c ;后者则使用 exec 执行。指定使用其它终端可以通过第二种方式实现,例如 RUN ["/bin/bash", "-c", "echo hello"]
每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \\ 来换行。

3.4、CMD

支持三种格式:

  • CMD [“executable”,“param1”,“param2”] 使用 exec 执行,推荐方式;
  • CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;
  • CMD [“param1”,“param2”] 提供给 ENTRYPOINT 的默认参数;

指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。
如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。

3.5、EXPOSE

 格式为 EXPOSE <port> [<port>...] 

告诉 Docker 服务端容器暴露的端口号,供互联系统使用。

3.6、ENV

格式为 ENV <key> <value> 

指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。

例如:

ENV PG_MAJOR 9.3     
ENV PG_VERSION 9.3.4     
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …     
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

3.7、ADD

格式为 ADD <src> <dest> 

该命令将复制指定的到容器中的。 其中可以是 Dockerfile 所在目录的一个相对路径;也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录)。
如果源文件是个目录,则将该目录下的所有文件复制到,但不包括该目录。

3.8、COPY

格式为 COPY <src> <dest>

复制本地主机的(为 Dockerfile 所在目录的相对路径)到容器中的。
如果源文件是个目录,则将该目录下的所有文件复制到,但不包括该目录。
当使用本地目录为源目录时,推荐使用 COPY 。

3.9、ENTRYPOINT

两种格式:

ENTRYPOINT ["executable", "param1", "param2"]     
ENTRYPOINT command param1 param2 (shell中执行)

配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。
每个 Dockerfile 中只能有一个 ENTRYPOINT ,当指定多个时,只有最后一个起效。

3.10、VOLUME

格式为 VOLUME ["/data"] 

创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。

3.11、USER

格式为 USER daemon  

指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。
当服务不需要管理员权限时,可以通过该命令指定运行用户,并且可以在之前创建所需要的用户。
例如:

  RUN groupadd -r postgres && useradd -r -g postgres postgres

要临时获取管理员权限可以使用 gosu ,而不推荐 sudo 。

3.12、WORKDIR

格式为 WORKDIR /path/to/workdir

为后续的 RUN 、 CMD 、 ENTRYPOINT 指令配置工作目录。
可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。
例如:

WORKDIR /a     
WORKDIR b     
WORKDIR c     
RUN pwd     

则最终路径为 /a/b/c

3.13、ONBUILD

格式为 ONBUILD [INSTRUCTION]

配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。

例如:
Dockerfile 使用如下的内容创建了镜像 image-A 。

[...]     
ONBUILD ADD . /app/src     
ONBUILD RUN /usr/local/bin/python-build --dir /app/src     
[...]

如果基于 A 创建新的镜像时,新的 Dockerfile中 使用 FROM image-A 指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。

FROM image-A     
#Automatically run the following     
ADD . /app/src     
RUN /usr/local/bin/python-build --dir /app/src

4、Dockerfile 示例

FROM ubuntu:16.04     
MAINTAINER 7d   
COPY sources.list /etc/apt/sources.list     
RUN rm -rf /bin/sh && ln -s /bin/bash /bin/sh && \\     
    apt-get update && \\     
    apt-get install build-essential python curl telnet -y && \\     
    mkdir -p /data/soft     
COPY heirloom-mailx_12.5.orig.tar.gz /data/soft     
COPY smtp /data/soft     
COPY mel3-1.0.2.tar.gz /data/soft     
COPY setuptools-0.6c11.tar.gz /data/soft     
COPY supervisor-3.3.1.tar.gz /data/soft     
RUN cd /data/soft && cat smtp >> /etc/nail.rc && \\     
    cd /data/soft && tar zxvf heirloom-mailx_12.5.orig.tar.gz && cd heirloom-mailx-12.5 && make && make install UCBINSTALL=/usr/bin/install && \\     
    cd /data/soft && tar zxf setuptools-0.6c11.tar.gz && cd setuptools-0.6c11 && python setup.py build && python setup.py  install && \\     
    cd /data/soft && tar zxvf meld3-1.0.2.tar.gz && cd meld3-1.0.2 && python setup.py install && \\     
    cd /data/soft && tar zxf supervisor-3.3.1.tar.gz && cd supervisor-3.3.1 && python setup.py install && \\     
    mkdir -p /etc/supervisor/conf.d     
COPY supervisord.conf /etc/supervisor/supervisord.conf     
COPY curl.conf /etc/supervisor/conf.d/curl.conf     
COPY detectport.conf /etc/supervisor/conf.d/detectport.conf     
RUN apt-get autoremove && \\     
    apt-get clean && \\     
    rm -rf /var/lib/apt/lists/*     
VOLUME ["/usr/local/share/detect"]     
CMD ["/usr/local/bin/supervisord","-c","/etc/supervisor/supervisord.conf"]

5、如何减小 Dockerfile 镜像大小

为了减少镜像大小,Dockerfile 需要遵循如下原则:

  • 将命令合并一起,尽量减小命令的数量。使用 && 进行链式指令,即将多条指令使用 && 链接起来,使得一条构建语句中包含多个执行指令,这样的话能够有效减少元数据总和。

  • apt 安装后,添加如下命令清除缓存:

RUN apt-get autoremove && \\     
apt-get clean && \\     
rm -rf /var/lib/apt/lists/*
  • make 编译安装的方式,make install 后添加 make clean命 令,清除编译过程中的中间文件。

6、构建 tomcat 镜像

6.1、准备所需软件

所需软件:

  • apache-tomcat-7.0.59
  • jdk1.7.0_75
$ mkdir -p tomcat7/soft

将以上两个软件上传到 soft 目录,需要解压开的软件

6.2、准备环境变量和 tomcat 启动脚本

编辑 script 文件,用于修改环境变量和 tomcat 启动脚本

$ vi soft/script
#!/bin/bash     
#准备环境变量     
echo -e 'JAVA_HOME=ROOT_DIR/JDK_DIR     
PATH=$PATH:$JAVA_HOME/bin     
CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib     
export JAVA_HOME PATHCLASSPATH' >> /etc/profile     

#准备tomcat启动脚本     
echo -e '#!/bin/bash     
source /etc/profile     
sh ROOT_DIR/TOMCAT_DIR/bin/catalina.sh run'  > ${ROOT_DIR}/run.sh     

#替换以上文件中为自定义的目录     
sed -i "s#ROOT_DIR#${ROOT_DIR}#g" /etc/profile     
sed -i "s#JDK_DIR#${JDK}#g" /etc/profile     
sed -i "s#ROOT_DIR#${ROOT_DIR}#g" ${ROOT_DIR}/run.sh     
sed -i "s#TOMCAT_DIR#${TOMCAT}#g" ${ROOT_DIR}/run.sh

6.3、准备 Dockerfile

$ vi Dockerfile
FROM ubuntu:16.04     
MAINTAINER 7d     
ENV ROOT_DIR=/app     
ENV TOMCAT=apache-tomcat-7.0.59     
ENV JDK=jdk1.7.0_75     
WORKDIR ${ROOT_DIR}     
RUN rm -rf /bin/sh && ln -s /bin/bash /bin/sh && \\     
    mkdir -p ${ROOT_DIR}     
COPY soft ${ROOT_DIR}     
RUN cd ${ROOT_DIR}/${TOMCAT}/bin && sed -i 's/\\"&\\"//g' catalina.sh && \\     
    cd ${ROOT_DIR} && sh script     
ENV LANG C.UTF-8     
EXPOSE 8080 22     
VOLUME ["${ROOT_DIR}/${TOMCAT}/webapps","${ROOT_DIR}/${TOMCAT}/logs"]     
CMD ["sh","/app/run.sh"]

6.4、准备 build

首次 build 会需要一些时间,要下载基础镜像 ubuntu:16.04

$ cd tomcat7     
$ docker build -t="tomcat7:v1.0" . 

构建完成后 400 M左右

6.5、启动容器

tomcat7 镜像构建完成后,可以启动个容器:

docker run -d -it \\     
    --name tomcat7 \\     
    --hostname tomcat7 \\     
    --restart=always \\     
    -p 8080:8080 \\     
    -v /etc/localtime:/etc/localtime:ro \\     
    -v /data/tomcat7/webapps:/app/apache-tomcat-7.0.59/webapps \\     
    -v /data/tomcat7/logs:/app/apache-tomcat-7.0.59/logs \\     
    --log-opt max-size=20m --log-opt max-file=5 \\     
    tomcat7:v1.0

6.6、创建容器脚本(可重复使用)

本脚本于用于一键创建容器,更改相关变量后可多次使用.

#!/bin/bash     
DOCKER=/usr/bin/docker     
CONTAINER_NAME=tomcat7     
CONTAINER_DIR=/data/containers     
IMAGE_NAME=tomcat7:v1.0     
OUT_PORT=18081     
COMMAND=/app/run.sh     

#获取容器、镜像     
GET_CO(){     
    CO_EXIST=$(${DOCKER} ps -a|awk '{print $NF}'|grep ${CONTAINER_NAME})     
}     

GET_IM(){     
    IM_EXIST=$(${DOCKER} images|awk '{print $1":"$2}'|grep -w ${IMAGE_NAME})     
}     

#创建容器     
CREATE_CONTAINER(){     
    docker run -d -it \\     
    --name ${CONTAINER_NAME} \\     
    --hostname ${CONTAINER_NAME} \\     
    --restart=always \\     
    -p ${OUT_PORT}:8080 \\     
    -v ${CONTAINER_DIR}/${CONTAINER_NAME}/webapps:/app/apache-tomcat-7.0.59/webapps \\     
    -v ${CONTAINER_DIR}/${CONTAINER_NAME}/logs:/app/apache-tomcat-7.0.59/logs \\     
    -v /etc/localtime:/etc/localtime:ro \\     
    --log-opt max-size=20m --log-opt max-file=5 \\     
    ${IMAGE_NAME} \\     
    ${COMMAND}     
    sleep 1     
    GET_CO     
    if [ -z ${CO_EXIST} ];then     
        echo "[-] ${CONTAINER_NAME} created failed."     
    else     
        echo "[+] ${CONTAINER_NAME} created Successful, publish port ${OUT_PORT}."     
    fi     
}     

#判断镜像是否存在     
GET_IM     
if [ -z ${IM_EXIST} ];then     
    echo "[-] ${IMAGE_NAME} is not exist."     
    exit 1     
else     
    echo "[+] Create a container from image ${IMAGE_NAME}."     
fi     
sleep 1     

#判断容器是否存在     
GET_CO     
if [ -z ${CO_EXIST} ];then     
    CREATE_CONTAINER     
else     
    echo "[-] ${CONTAINER_NAME} is exist."     
    exit 1     
fi

相关系列:

以上是关于Docker 操作指南之构建镜像篇的主要内容,如果未能解决你的问题,请参考以下文章

测试开发之系统篇-Docker 常用操作

测试开发之系统篇-Docker常用操作

Docker入坑指南之RUN

Docker 操作指南之使用示例篇

Docker 操作指南之常见使用篇

容器技术之Docker数据卷