Docker 操作指南之构建镜像篇
构建自己的 image
构建自己的镜像,有两种方法:
- 通过 docker commit
- 通过 Dockerfile
通过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)
将现有的容器固化成镜像。
实例
下面我们创建一个可以 ssh 的镜像:
- 启动一个容器:
docker run -i -t ubuntu:16.04 /bin/bash
- 启动成功后,登录容器,执行所需要的操作:
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 #退出容器
- 保存镜像
$ docker commit 4aa51652291a ubuntusshd
5f5a2a905d89fa2daf3b9537fa0cf5e0c83ad0e95d6078420d2a582080120418
- 以后台方式运行容器
docker run -d -p 50022:22 ubuntusshd /usr/sbin/sshd –D
ubuntu容器内运行着的 SSH Server
占用 22 端口,对外为 50022 端口。
- 查看容器是否运行
$ 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
- 客户端连接
ssh root@127.0.0.1 -p 50022
用 Dockerfile 文件构建映像
使用 docker commit
来扩展一个 image 比较简单,但它不容易在一个团队中分享它。我们使用 docker build
来创建一个新的image。为此,我们需要创建一个 dockerfile,包含一些如何创建我们的 image 的指令。
创建一个 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
确认安装该软件,不会在安装过程中提示确认。
创建 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
查看是否存在该 images
# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
user1/web v2 3e333b27becb 3 hours ago 190 MB
Dockerfile 指令总结
FROM
格式为 FROM <image> 或 FROM <image>:<tag>
第一条指令必须为 FROM 指令。并且,如果在同一个 Dockerfile 中创建多个镜像时,可以使用多个 FROM 指令(每个镜像一次)。
MAINTAINER
格式为 MAINTAINER <name> ,指定维护者信息
RUN
格式为 RUN <command> 或 RUN ["executable", "param1", "param2"]
前者将在 shell 终端中运行命令,即 /bin/sh -c
;后者则使用 exec 执行。指定使用其它终端可以通过第二种方式实现,例如 RUN ["/bin/bash", "-c", "echo hello"]
。
每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \ 来换行。
CMD
支持三种格式:
- CMD [“executable”,“param1”,“param2”] 使用 exec 执行,推荐方式;
- CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;
- CMD [“param1”,“param2”] 提供给 ENTRYPOINT 的默认参数;
指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。
如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。
EXPOSE
格式为 EXPOSE <port> [<port>...]
告诉 Docker 服务端容器暴露的端口号,供互联系统使用。
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
ADD
格式为 ADD <src> <dest>
该命令将复制指定的到容器中的。 其中可以是 Dockerfile 所在目录的一个相对路径;也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录)。
如果源文件是个目录,则将该目录下的所有文件复制到,但不包括该目录。
COPY
格式为 COPY <src> <dest>
复制本地主机的(为 Dockerfile 所在目录的相对路径)到容器中的。
如果源文件是个目录,则将该目录下的所有文件复制到,但不包括该目录。
当使用本地目录为源目录时,推荐使用 COPY 。
ENTRYPOINT
两种格式:
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2 (shell中执行)
配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。
每个 Dockerfile 中只能有一个 ENTRYPOINT ,当指定多个时,只有最后一个起效。
VOLUME
格式为 VOLUME ["/data"]
创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。
USER
格式为 USER daemon
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。
当服务不需要管理员权限时,可以通过该命令指定运行用户,并且可以在之前创建所需要的用户。
例如:
RUN groupadd -r postgres && useradd -r -g postgres postgres
要临时获取管理员权限可以使用 gosu ,而不推荐 sudo 。
WORKDIR
格式为 WORKDIR /path/to/workdir
为后续的 RUN 、 CMD 、 ENTRYPOINT 指令配置工作目录。
可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。
例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c
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
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"]
如何减小 Dockerfile 镜像大小
为了减少镜像大小,Dockerfile 需要遵循如下原则:
-
将命令合并一起,尽量减小命令的数量。使用 && 进行链式指令,即将多条指令使用 && 链接起来,使得一条构建语句中包含多个执行指令,这样的话能够有效减少元数据总和。
-
apt 安装后,添加如下命令清除缓存:
RUN apt-get autoremove && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
- make 编译安装的方式,make install 后添加 make clean命 令,清除编译过程中的中间文件。
构建 tomcat 镜像
准备所需软件
所需软件:
- apache-tomcat-7.0.59
- jdk1.7.0_75
$ mkdir -p tomcat7/soft
将以上两个软件上传到 soft 目录,需要解压开的软件
准备环境变量和 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
准备 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"]
准备 build
首次 build 会需要一些时间,要下载基础镜像 ubuntu:16.04
$ cd tomcat7
$ docker build -t="tomcat7:v1.0" .
构建完成后 400 M左右
启动容器
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
创建容器脚本(可重复使用)
本脚本于用于一键创建容器,更改相关变量后可多次使用.
#!/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
相关系列:
- 点赞
- 收藏
- 关注作者
评论(0)