Home

cuipf blog

26 Jun 2017

Docker介绍--镜像构建

[TOC]

构建Docker镜像

我们都知道如何获取构建好的带有制定内容的Docker镜像,如果对这些镜像做了修改,如何更新和管理这些镜像呢? 构建Docker镜像方式有一下两种:

  1. 使用docker commit命令;
  2. 使用docker build命令和Dockerfile文件;

docker commit构建镜像

当然现在已经不推荐使用该方式,这里只做简单的介绍;

注册Docker hub账号;

构建镜像很重的一部分就是如何共享和发布镜像,可以将镜像保存在Docker hub或者用户自己的私有Registry中,所以首先我们需要注册Docker Hub账号;然后使用docker login登陆; 登陆成功之后,个人认证信息会被保存在$HOME/.docker/config.json;

构建镜像

基于之前pull的镜像centos做简单的修改,比如安装gcc或者nginx等; 使用命令docker commit

//制定修改过容器的ID, dockerhubs用户名/目标镜像仓库
docker commit aa3316464263 cuipf/centos_gcc
//镜像推送到docker hub上
docker push cuipf/centos_gcc

当然,docker commit可以通过添加-m设置提交信息,--author设置作者;

使用Dockerfile构建镜像

从上面镜像的构建方式,我们发现构建镜像其实就是制定每一层所添加的配置,文件;我们可以把每一层的修改,安装,构建,操作的命令都写入一个脚本;使用这个脚本来构建镜像,这个脚本就是Dockerfile; Dockerfile是一个文本文件,由一条条命令组成,每一条命令构建一层,每条命令是描述该层如何构建。

Dockerfile

首先看一个简单的实例,这是以centos为基础镜像,搭建一个C++编译开发调试的环境;如下:

# 以centos:latest为基础镜像
FROM centos:latest
MAINTAINER cuipf
RUN yum install -y gcc
RUN yum install -y gdb
RUN echo "test Dockerfile" > /home/msg

实例非常的简单,简单说明几点:

  1. FROM指令用于指定基础镜像;也是Dockerfile的必备指令,且必须是第一条;官方提供了大量的镜像供你作为基础镜像;
     FROM scratch //scratch 特殊镜像,该镜像是虚拟概念,表示一个空白的镜像;
    
  2. MAINTAINER 可以用来指定镜像的作者和作者的邮箱;
  3. RUN指令在当前镜像中运行指定的命令;默认情况下,RUN指令会在shell中使用命令包装器/bin/sh -c来执行;如不希望在shell中运行,可以使用exec格式的RUN指令;
     RUN ["yum", "install", "-y", "protobuf"]
    
  4. Dockerfile中每一条RUN命令都会建立一层,可以看出上述简单几条命令就创建了三层,这显然是没有意义的;上述Dockerfile可以修改如下,层数就由三层减少为一层:
    # 以centos:latest为基础镜像
    FROM centos:latest
    MAINTAINER cuipf
    RUN yum install -y gcc \
    && yum install -y gdb \
    && echo "test Dockerfile" > /home/msg
    

Dockerfile指令

当然, Dockerfile能使用指令很多, 下面我们说一些经常会用到的指令:

COPY

COPY <原路径> ... <目标路径>

命令表示:从上下文目录中的<原路径>复制到新一层镜像的<目标路径>;

原路径可以是多个,可以使用通配符;目标路径可以是容器内部的绝对路径,也可以是相对工作目录的相对路径;

ADD

ADD的功能和COPY基本一样;ADD只是在原来基础增加了一些功能,比如:COPY的是一个压缩包,如果使用ADD命令就自动包含了解压过程(如果需要的话); 比较起来,COPY的语义更加的明确,所以官方推荐能使用COPY的地方尽量使用COPY;

CMD

CMD命令格式和RUN类似,有以下三种:

  • shell格式: CMD <命令>;
  • exec格式:CMD[‘可执行文件’,”参数1”,”参数2”…];
  • 参数列表格式: CMD [“参数1”, “参数2” …] CMD命令就是用于指定默认的容器主进程启动时的命令; 一般推荐使用exec格式, 这种格式在解析的时候会被解析为JSON, 所以后面必须使用双引号;

注意区分:

  • CMD命令是指定容器启动时候要运行的命令;而RUN则是指的在容器构建时候要运行的命令;
  • 如果同时指定CMD命令以及Docker run命令, Docker run命令可以覆盖CMD指令;

ENTRYPOINT

ENTRYPOINT与CMD一样是用来指定容器启动程序以及参数; 我们可以通过ENTRYPOINT来设置容器启动的命令, 然后配合Docker run传递参数;

//也可以ENTRYPOINT和CMD配合使用
ENTRYPOINT ["/usr/sbin/nginx"]
CMD["-h"]
//此时使用docker run启动容器, docker run后面任何参数都会传递给 /usr/sbin/nginx/, 如果设置会议/usr/sbin/nginx/ -h的形式运行;

WORKDIR

作用: WORKDIR指令用来从镜像中创建新的容器时候,在容器内部设置一个工作目录; 其他命令将在这个目录下运行;

当然, 我们也可以在使用docker run运行时候使用-w指定路径来覆盖该路径;

ENV

作用: 镜像构建过程中设置环境变量;

格式:

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>

通过EVN设置的环境变量, 可以在后续的任何RUN指令中利用; 我们也可以通过docker run命令行的-e标志来传递环境变量; 但这些变量仅仅运行时候有效;

USER

作用: 用来指定该镜像会以什么样的用户去运行; USER和WORKDIR有点像, 都是改变当前的环境状态并影响以后的层. 如:

USER nginx //基于该镜像启动的容器, 以nginx身份来运行;

其实, 我们可以指定用户的用户名, UID或者组, GID; 甚至是两者的组合; 如下:

USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group

也可以通过docker run的-u选项来覆盖该指令指定的值;

VOLUME

在了解该命令之前, 我们先来了解一下, 容器中卷的概念, 我们知道在容器的运行过程中, 我们要保证容器的存储层尽量不发生写操作; 对于需要保存动态数据的应用, 其数据文件就应该保存在卷中;

关于卷的使用与说明:

  • 卷可以存在一个或者多个容器特定目录, 这个目录可以绕过联合文件系统;
  • 卷可以在容器之间共享和重用;
  • 对卷的修改是立即生效的;
  • 对卷的修改不会对更新镜像产生影响;

回到VOLUME上;

作用: 用来向基于镜像创建的容器添加卷;

VOLUME /data

这里, /data目录就会在运行的时候自动挂载为匿名的卷; 任何向/data中写入的数据, 都不会被记录在容器的存储层;

当然运行时可以覆盖该挂载设置;

docker run -d -v mydata:/data ....

是用命名卷mydata挂载到/data来代替Dockerfile中的匿名卷;

ONBUILD

ONBUILD指令能为镜像添加触发器(trigger);ONBUILD是一个比较特殊的指令;它后面跟的其他指令(如 RUN, COPY),在当前的镜像构建时候并不会被执行; 只有当以当前镜像为基础镜像的时候, 去构建下一级镜像的时候才会被执行; Dockerfile中仅有此命令是为帮助别人定制自己而准备的;

ONBUILD ADD . /app/src
ONBUILD RUN cd /app/src && make

更多的Dockerfile命令可以参见官网

构建镜像

执行docker build命令时, Dockerfile中所有的指令都会被执行并且提交, 并且在命令成功执行后返回一个新的镜像; 我们在使用docker build构建的时候, 可以通过-t选项为新镜像设置了仓库和名称; 也可以为镜像设置标签;

//构建时候设置标签 path为Dockerfile路径也可以是github的连接
docker build -t="cuipf/proto_centos:v1" path
//构建镜像时,不使用缓存
docker build --no-cache -t="cuipf/proto_centos:v1" path

将镜像推向Docker hubs

镜像构建完毕, 我们把镜像推向Docker hubs, 这样其他人也可以使用我们构建的镜像; 具体的推送方式这里不再赘述, 参见上面使用docker commit构建镜像中推送镜像的方式;

cuipf

scribble