base 镜像有两层含义:
    不依赖其他镜像,从FROM构建
    其他镜像可以之为基础进行扩展

hello-world 是Docker官方提供的一个镜像,通常用来验证Docker是否安装成功,不到2KB
在docker中centos镜像不到200MB,而官网镜像好几个G,因为
Linux 操作系统由内核空间和用户空间组成如下图

rootfs
内核空间是kernel,Linux刚启动时会加载bootfs文件系统,之后bootfs会被卸载掉;用户空间的文件系统是rootfs,包含我们熟悉的/dev, /proc, /bin等目录,对于base镜像来说,底层直接用Host的kernel,自己只需要提供rootfs就行了;而对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了;相比其他 Linux 发行版,CentOS的rootfs已经算臃肿的了,alpine还不到10MB;平时安装的CentOS除了rootfs还会选装很多软件、服务、图形桌面等,需要好几个GB就不足为奇了

base镜像提供的是最小安装的Linux发行版(内容如下)
FROM cbb
ADD centos7-docker.tar.bz2 /
CMD ["/bin/bash"]
第二行ADD指令添加到镜像的tar包就是CentOS 7的rootfs;在制作镜像时,这个tar包会自动解压到/目录下,生成/dev、/proc、/bin等目录

    不同Linux发行版的区别主要就是rootfs;比如Ubuntu 14.04使用upstart管理服务,apt管理软件包;而CentOS 7使用systemd和yum;这些都是用户空间上的区别,Linux kernel差别不大,所以Docker可以同时支持多种Linux镜像,模拟出多种操作系统环境
Debian和BusyBox(一种嵌入式 Linux)上层提供各自的rootfs,底层共用Docker Host的kernel

base镜像只是在用户空间与发行版一致,kernel版本与发行版是不同的
所有容器都共用host的kernel,在容器中没办法对kernel升级、修改;如果容器对kernel版本有要求(比如应用只能在某个kernel版本下运行)则不建议用容器,这种场景虚拟机可能更合适


某镜像构建过程


    新镜像是从base镜像一层一层叠加生成的;每安装一个软件,就在现有镜像的基础上增加一层,最大的一个好处就是共享资源;有多个镜像都从相同的base镜像构建而来,那么DockerHost只需在磁盘上保存一份base镜像;同时内存中也只需加载一份base镜像,就可以为所有容器服务了;而且镜像的每一层都可以被共享,当容器启动时,一个新的可写层被加载到镜像的顶部;这一层通常被称作"容器层","容器层"之下的都叫"镜像层";所有对容器的改动,无论添加、删除、还是修改文件都只会发生在容器层中;只有容器层是可写的,容器层下面的所有镜像层都是只读的;镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统;如果不同层中有一个相同路径的文件,在容器层中,用户看到的就是一个叠加之后的文件系统

    1 添加文件
    2 在容器中创建文件时,新文件被添加到容器层中
    3 读取文件在容器中读取某个文件时,Docker会从上往下依次在各镜像层中查找此文件;一旦找到,立即将其复制到容器层,然后打开并读入内存
    4 修改文件在容器中修改已存在的文件时,Docker会从上往下依次在各镜像层中查找此文件;一旦找到,立即将其复制到容器层,然后修改
    5 删除文件在容器中删除文件时,Docker也是从上往下依次在镜像层中查找此文件;找到后,会在容器层中记录下此删除操作

    只有当需要修改时才复制一份数据,这种特性被称作Copy-on-Write;可见容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改;容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享


Docker 提供了两种构建镜像的方法
    docker commit 命令
    Dockerfile 构建文件


docker commit命令是创建新镜像最直观的方法,其过程包含三个步骤:
    1 运行容器
    2 修改容器
    3 将容器保存为新的镜像
然而Docker并不建议用户通过这种方式构建镜像,因为:
    这是一种手工创建镜像的方式,容易出错,效率低且可重复性弱;比如要在debian base镜像中也加入vi,还得重复前面的所有步骤
    使用者并不知道镜像是如何创建出来的,里面是否有恶意程序;也就是说无法对镜像进行审计,存在安全隐患


Dockerfile创建镜像
docker层不要超过127层,超过报错,层越多查询越慢
第一行必须指定基础镜像(如果在同一个Dockerfile中创建多个镜像,可以指定多个FROM)
FROM 镜像
维护者信息
MAINTAINER 信息
镜像的操作指令(可以为多条,但尽量用&&来执行多条命令)
RUN 命令
启动容器时执行的命令(每个dockerfile只能有一条CMD命令,如果指定多条,只执行最后一条,如果启动容器时用户指定了运行命令,则覆盖CMD指定的命令)
CMD 指令
指定容器暴露的端口(可以指定多个以空格隔开)
EXPOSE 端口
指定一个环境变量,会被后续RUN指令使用
ENV 环境变量
复制本地指定的文件或目录到容器中,tar文件自动解压
ADD 源 目标
复制本地指定文件到容器中,与ADD功能一样,但不会自动解压文件
COPY 源 目标
容器启动后执行的命令(每个dockerfile中只能有一个,指定多个买最后一个生效)
ENTRYPOINT 命令
创建一个从本地或其他容器的挂载点(用来存放数据)
VOLUME 挂载点
指定运行容器的用户名或UID(后续的RUN也会使用指定的用户)
USER 用户
为后续的RUN、CMD、ENTRYPOINT指令配置工作目录(可以多个)
WORKDIR 路径
配置当所创建的镜像为其他新镜像的基础镜像是所执行的操作命令
ONBUILD ADD\RUN\……
Dockerfile编写完成后可以通过docker bulid命令来创建镜像

例如
FROM centos
MAINTAINER CBB
RUN yum -y install httpd
ADD start.sh /usr/local/bin/start.sh
ADD index.html /var/www/html/index.html
echo "/usr/sbin/httpd -DFOREGROUND" > start.sh    (/usr/sbin/httpd -DFOREGROUND 相当于执行了 systemctl start httpd)
chmod a+x start.sh
echo "docker image build test" > index.html
docker build -t 镜像名:TAG .