# 查看本地已下载的镜像 $ docker images # 下载镜像 $ docker pull hello-world # 查看镜像信息 $ docker images hello-world
当我们下载镜像并执行一些命令的时候,docker实际上是记录下来的。
所有的命令都被转化成了bash命令存储在文件里。 这也就解释了为什么hello-world文件才1.5k
如何构建镜像 我们可以从简单的分析:hello-world 每个镜像的创建都需要有一个Dockerfile,从docker hub下载的image可以在这里搜索dockerfile,包括后面我们自己构建也是一样。
现在分析一下hello-world的[Dockerfile]
FROM scratch # 从0开始 COPY hello / # 将镜像里的hello可执行文件拷贝到容器根目录 CMD ["/hello"] # 当容器启动后执行hello文件内容
而hello的内容,也很简单就是打印这样一段内容
1. 不依赖其他镜像,从 scratch 构建。 2. 其他镜像可以之为基础进行扩展。
这个怎么理解呢?很重要。 比如说,我们下载的CentOS镜像
你一定很纳闷。怎么才200M不到?我们平时下载的都几个G的。
这里就来解答一下 Linux 操作系统由内核空间和用户空间组成。
内核空间是 kernel,Linux 刚启动时会加载 bootfs 文件系统,之后 bootfs 会被卸载掉。 用户空间的文件系统是 rootfs,包含我们熟悉的 /dev, /proc, /bin 等目录。 对于 base 镜像来说,【底层直接用 Host 的 kernel】,自己只需要提供 rootfs 就行了。 而对于一个精简的 OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了。相比其他 Linux 发行版,CentOS 的 rootfs 已经算臃肿的了,alpine 还不到 10MB。 我们平时安装的 CentOS 除了 rootfs 还会选装很多软件、服务、图形桌面等,需要好几个 GB 就不足为奇了。
照样来分析一下,Dockerfile:地址
FROM scratch MAINTAINER The CentOS Project <cloud-ops@centos.org> ADD centos-7.2.1511-docker.tar.xz / CMD ["/bin/bash"]
对于容器来说,他底层使用的Host的kernel,所以假如我们的容器是CentOS的版本(内核是3.x),当我们把容器放到Ubuntu(内核是4.x)上使用是,他实际上使用的是Ubuntu的内核。
容器只能使用 Host 的 kernel,并且不能修改。 所有容器都共用 host 的 kernel,在容器中没办法对 kernel 升级。如果容器对 kernel 版本有要求(比如应用只能在某个 kernel 版本下运行),则不建议用容器,这种场景虚拟机可能更合适。
从上面我们知道,每个软件都信赖存活于一个base镜像,而每个base镜像都200M,是不是意味着我们每次都要重复下载base镜像呢?
肯定不是,这里就要说到镜像的分层结构,它使得多个容器,可以共用一个base镜像。 那么就产生一个问题,不同容器要是修改同一文件呢?该怎么办?会不会互相影响?
答案是不会。
因为,镜像层的文件都是只读的,如果容器层需要对文件进行操作,都要将文件进行拷贝,然后编辑,而容器会对这份文件进行管理,而这份文件只存在于当前容器层。不会对镜像层的文件产生影响,如果有新的容器以这个容器层做为基础镜像,那么这个文件将会被容器层所检索到。
添加文件 在容器中创建文件时,新文件被添加到容器层中。 读取文件 在容器中读取某个文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,打开并读入内存。 修改文件 在容器中修改已存在的文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改之。 删除文件 在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。
为了理解这个我们来构建个自己的容器。
第一步,先下载base镜像:centos
$ docker pull centos
第二步,编辑Dockerfile
FROM centos RUN yum install -y tree
第三步,创建容器,并命名为centos-tree
$ docker build -t centos-tree .
FROM centos RUN yum install -y tree COPY testfile /
第五步,再创建一个容器
docker build -t centos-tree-testfile .
注意点,镜像的缓存要求构建的命令顺序要严格一致,只要有一行命令不同,缓存就会失效,即使最后的容器内容完全一致,也无法使用缓存。 比如,我们把第二次的Dockerfile改成如下,也是需要重新构建
FROM centos COPY testfile / RUN yum install -y tree
当我们build一个新的镜像,也许会有很多的很复杂的步骤,如果一次性build很有可能遇到各种意外情况,导致build失败,这时候,我们就需要进行调试,找出失败原因,修改Dockerfile,最终成功。
那么如何调试?在我们build镜像的时候,每一步都会有一个镜像id,通过这个id我们就可以进入该层镜像。

/目录下没有testfile文件
$ docker run -it e316b390cf2a
友情提示
进入容器后,如何退出 1. exit # 退出并关闭容器 2. ctl+d # 退出并关闭容器 3. ctl+p+q # 退出不关闭容器 退出后,可以使用 docker kill <id> 手动关闭
FROM
指定 base 镜像。
MAINTAINER
设置镜像的作者,可以是任意字符串。
COPY
将文件从 build context 复制到镜像。
COPY 支持两种形式:
COPY src dest
COPY ["src", "dest"]
注意:src 只能指定 build context 中的文件或目录。
ADD
与 COPY 类似,从 build context 复制文件到镜像。不同的是,如果 src 是归档文件(tar, zip, tgz, xz 等),文件会被自动解压到 dest。
ENV
设置环境变量,环境变量可被后面的指令使用。例如:
ENV MY_VERSION 1.3
RUN apt-get install -y mypackage=$MY_VERSION
EXPOSE
指定容器中的进程会监听某个端口,Docker 可以将该端口暴露出来。我们会在容器网络部分详细讨论。
VOLUME
将文件或目录声明为 volume。我们会在容器存储部分详细讨论。
WORKDIR
为后面的 RUN, CMD, ENTRYPOINT, ADD 或 COPY 指令设置镜像中的当前工作目录,当我们进入容器时,即为工作目录。
RUN
在容器中运行指定的命令。
CMD
容器启动时运行指定的命令。
Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效。CMD 可以被 docker run 之后的参数替换。
ENTRYPOINT
设置容器启动时运行的命令。
Dockerfile 中可以有多个 ENTRYPOINT 指令,但只有最后一个生效。CMD 或 docker run 之后的参数会被当做参数传递给 ENTRYPOINT。
这两个很容易混淆,但是又很重要。所以单独拿出来讲。
相同的点:都是容器启动后执行。相当于开机自启 不同的点:一个会被覆盖,一个不会被覆盖
CMD:两个功能
1. 执行一些命令
- 若run时,没有指定其他命令,则会在启动容器的时候默认执行
- 若run时,指定了其他命令,则该命令会被覆盖,不被执行
- exec和bash格式都可以使用
exec:CMD ["/bin/echo", "hello world"]
bash:CMD echo "hello world"
2. 给ENTRYPOINT传递参数 - 若run时,指定了参数,CMD传递的参数同样被覆盖,而ENTRYPOINT永远不会被覆盖。 - 注意:如果要传递参数,必须使用exec格式,诸如CMD [""]
推荐用法
1. CMD:若想做开机启动命令,两种格式都可以;若想传递参数,则必须使用exec格式 2. ENTRYPOINT:同样也接受两种格式,但是请使用exec格式,即方便传递参数,实现定制,而且不容易出错。
为了方便多台Host,使用同一Image,有如下三种方法。
1. 在多台Host上,使用同一Dockerfile创建镜像; 2. 上传至Docker Hub,在需要的Host上下载 3. 搭建本地私有仓库。
使用Docker Hub
1. 需要联网,而且速度不快 2. 任何人都可以访问,不安全,可能不适合企业
使用方法
1. 先到 Docker Hub 上注册一个账号 2. 在 Docker Host 上登录 $ docker login -u username 输入密码,登陆。 3. 修改镜像名字和tag:格式:[username]/name:tag $ docker tag hello wangbm/hello:v0.1 4. 上传 $ docker push wangbm/hello:v0.1 5. 下载,在其他Host $ docker pull wangbm/hello:v0.1 6. 若要删除,只能在网上删除,https://hub.docker.com
搭建本地仓库步骤
1. 下载并启动Registry容器 $ docker -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:2 参数说明 -d 后台运行 -p 将本机5000端口和容器的5000端口绑定 -v 将容器内的/var/lib/registry 和本机的 /myregistry 路径进行映射。 下载的是registry:2 版本 2. 重命令镜像 $ docker tag hello [host-ip]:5000/[username]/name:tag $ docker tag hello 192.168.2.55:5000/wangbm/hello:v0.1 3. 上传 $ docker push 192.168.2.55:5000/wangbm/hello:v0.1 4. 下载 $ docker pull 192.168.2.55:5000/wangbm/hello:v0.1
# 删除镜像必须先停止并删除容器 docker ps -a # 找到对应id docker stop <container_id> docker rm <container_id> docker rmi <image_id> # 在容器内创建新镜像 docker commit # 给镜像打tag docker tag old_tag new_tag # 上传/下载镜像 docker push image docker pull image # 搜索Docker Hub中的镜像 docker search image # 从 Dockerfile 构建镜像 docker build -t image_tag . # 显示镜像构建历史 docker history image








