docker 学习笔记二

docker 学习笔记二

Scroll Down

什么是 docker 镜像?

Docker 镜像是由文件系统叠加而成。最底端是一个引导文件系统,即 bootfs,这很像典型的 Linux/Unix 的引导文件系统。Docker 用户几乎永远不会和引导文件系统有什么交互。实际上,当一个容器启动后,它将会被移到内存中,而引导文件系统则会被卸载(unmount),以留出更多的内存供 initrd 磁盘镜像使用。

实际上,Docker 镜像的第二层是 root 文件系统 rootfs,它位于引导文件系统之上。rootfs 可以是一种或多种操作系统(如 Debian 或者 Ubuntu 文件系统)

在传统的 Linux 引导过程中,root 文件系统会最先以只读的方式加载,当引导结束并完成了完整性检查之后,它才会被切换为读写模式。但是在 Docker 里,root 文件系统永远只能是只读状态,并且 Docker 利用联合加载 [1](union mount)技术又会在 root 文件系统层上加载更多的只读文件系统。联合加载指的是一次同时加载多个文件系统,但是在外面看起来只能看到一个文件系统。联合加载会将各层文件系统叠加到一起,这样最终的文件系统会包含所有底层的文件和目录。

  Docker 将这样的文件系统称为镜像。一个镜像可以放到另一个镜像的顶部。位于下面的镜像称为父镜像(parent image),可以依次类推,直到镜像栈的最底部,最底部的镜像称为基础镜像(base image)。最后,当从一个镜像启动容器时,Docker 会在该镜像的最顶层加载一个读写文件系统。我们想在 Docker 中运行的程序就是在这个读写层中执行的。

当 Docker 第一次启动一个容器时,初始的读写层是空的。当文件系统发生变化时,这些变化都会应用到这一层上。比如,如果想修改一个文件,这个文件首先会从该读写层下面的只读层复制到该读写层。该文件的只读版本依然存在,但是已经被读写层中的该文件副本所隐藏。

         通常这种机制被称为写时复制(copy on write),这也是使 Docker 如此强大的技术之一。每个只读镜像层都是只读的,并且以后永远不会变化。当创建一个新容器时,Docker 会构建出一个镜像栈,并在栈的最顶端添加一个读写层。这个读写层再加上其下面的镜像层以及一些配置数据,就构成了一个容器。在上一章我们已经知道,容器是可以修改的,它们都有自己的状态,并且是可以启动和停止的。容器的这种特点加上镜像分层框架(image-layering framework),使我们可以快速构建镜像并运行包含我们自己的应用程序和服务的容器

列出镜像

$docker images

镜像从仓库下载下来。镜像保存在仓库中,而仓库存在于 Registry 中。默认的 Registry 是由 Docker 公司运营的公共 Registry 服务,即 Docker Hub

在 Docker Hub(或者用户自己运营的 Registry)中,镜像是保存在仓库中的。可以将镜像仓库想象为类似 Git 仓库的东西。它包括镜像、层以及关于镜像的元数据(metadata)

拉取的镜像并不是一个完整的操作系统,只是一个裁剪版,只包含最低限度的支持系统运行的组件。

$ sudo docker run -t -i --name new_container ubuntu:12.04 /bin/bash 

这个例子会从镜像 ubuntu:12.04 启动一个容器,而这个镜像的操作系统则是 Ubuntu 12.04

在构建容器时指定仓库的标签也是一个很好的习惯。这样便可以准确地指定容器来源于哪里

Docker Hub 中有两种类型的仓库:用户仓库(user repository)和顶层仓库(top-level repository)。用户仓库的镜像都是由 Docker 用户创建的,而顶层仓库则是由 Docker 内部的人来管理的。

  用户仓库的命名由用户名和仓库名两部分组成,如 myhub/zjib

    用户名:myhub

仓库名:zjib

顶层仓库只包含仓库名部分,如 ubuntu 仓库。顶层仓库由 Docker 公司和由选定的能提供优质基础镜像的厂商(如 Fedora 团队提供了 fedora 镜像)管理,用户可以基于这些基础镜像构建自己的镜像。同时顶层仓库也代表了各厂商和 Docker 公司的一种承诺,即顶层仓库中的镜像是架构良好、安全且最新的

拉取镜像

用 docker run 命令从镜像启动一个容器时,如果该镜像不在本地,Docker 会先从 Docker Hub 下载该镜像。如果没有指定具体的镜像标签,那么 Docker 会自动下载 latest 标签的镜像

其实也可以通过 docker pull 命令先发制人地将该镜像拉取到本地。使用 docker pull 命令可以节省从一个新镜像启动一个容器所需的时间

$docker pull ……

查找镜像

通过 docker search 命令来查找所有 Docker Hub 上公共的可用镜像

这条命令会完成镜像查找工作,并返回如下信息:

    仓库名;

    镜像描述;

    用户评价(Stars)—反应出一个镜像的受欢迎程度;

    是否官方(Official)—由上游开发者管理的镜像(如 fedora 镜像由 Fedora 团队管理);

    自动构建(Automated)—表示这个镜像是由 Docker Hub 的自动构建(Automated Build)流程创建的。

构建镜像

前面我们已经看到了如何拉取已经构建好的带有定制内容的 Docker 镜像,那么我们如何修改自己的镜像,并且更新和管理这些镜像呢?构建 Docker 镜像有以下两种方法。

    使用 docker commit 命令。

    使用 docker build 命令和 Dockerfile 文件

首次学习的时候那本书上说并不推荐使用 docker commit 命令,而应该使用更灵活、更强大的 Dockerfile 来构建 Docker 镜像

创建 Docker Hub 账号

官网注册,在 linux 上使用

$docker login        进行登录

Docker **commit**命令创建镜像

创建 Docker 镜像的第一种方法是使用 docker commit 命令。可以将此想象为我们是在往版本控制系统里提交变更。我们先创建一个容器,并在容器里做出修改,就像修改代码一样,最后再将修改提交为一个新镜像

$docker commit

我们启动了一个容器,并在里面安装了 nginx。我们会将这个容器作为一个 Web 服务器来运行,所以我们想把它的当前状态保存下来。这样就不必每次都创建一个新容器并再次在里面安装 nginx 了。为了完成此项工作,需要先使用 exit 命令从容器里退出,之后再运行 docker commit 命令

在这条命令里,我们指定了更多的信息选项。首先 - m 选项用来指定新创建的镜像的提交信息。同时还指定了 --a 选项,用来列出该镜像的作者信息。接着指定了想要提交的容器的 ID。最后的 myhub/nginx 指定了镜像的用户名和仓库名,: 并为该镜像增加了一个 webserver 标签

可以用 docker inspect 命令来查看新创建的镜像的详细信息

如果想从刚创建的新镜像运行一个容器,可以使用 docker run 命令

⑳ 删除 image

 docker images

 容器在运行中的话是没办法删除镜像的,所以删除镜像之前要先让容器停止运行

docker stop

docker  rmi  id/name   删除镜像

docker  rm  id/name  删除容器

2、用 Dockerfile 构建镜像

并不推荐使用 docker commit 的方法来构建镜像。相反,推荐使用被称为 Dockerfile 的定义文件和 docker build 命令来构建镜像。Dockerfile 使用基本的基于 DSL(Domain Specific Language))语法的指令来构建一个 Docker 镜像,我们推荐使用 Dockerfile 方法来代替 docker commit,因为通过前者来构建镜像更具备可重复性、透明性以及幂等性

wo 创建了一个名为 static_web 的目录用来保存 Dockerfile,这个目录就是我们的构建环境(build environment),Docker 则称此环境为上下文(context)或者构建上下文(build context)。Docker 会在构建镜像时将构建上下文和该上下文中的文件和目录上传到 Docker 守护进程。这样 Docker 守护进程就能直接访问用户想在镜像中存储的任何代码、文件或者其他数据

该 Dockerfile 由一系列指令和参数组成。每条指令,如 FROM,都必须为大写字母,且后面要跟随一个参数:FROM centos。Dockerfile 中的指令会按顺序从上到下执行,所以应该根据需要合理安排指令的顺序。

  每条指令都会创建一个新的镜像层并对镜像进行提交。Docker 大体上按照如下流程执行 Dockerfile 中的指令

  •    Docker 从基础镜像运行一个容器。
  • 执行一条指令,对容器做出修改。
  • 执行类似docker commit的操作,提交一个新的镜像层。
  • Docker 再基于刚提交的镜像运行一个新容器。
  • 执行Dockerfile中的下一条指令,直到所有指令都执行完毕。

每个 Dockerfile 的第一条指令必须是 FROM。FROM 指令指定一个已经存在的镜像,后续指令都将基于该镜像进行,这个镜像被称为基础镜像(base iamge)

在前面的 Dockerfile 示例里,我们指定了 centos 作为新镜像的基础镜像。基于这个 Dockerfile 构建的新镜像将以 centos 操作系统为基础。在运行一个容器时,必须要指明是基于哪个基础镜像在进行构建。

  接着指定了 MAINTAINER 指令,这条指令会告诉 Docker 该镜像的作者是谁,以及作者的电子邮件地址。这有助于标识镜像的所有者和联系方式

  RUN 指令会在当前镜像中运行指定的命令。在这个例子里,我们通过 RUN 指令更新了已经安装的 APT 仓库,安装了 nginx 包,之后创建了 / usr/share/nginx/html/index.html 文件,该文件有一些简单的示例文本。像前面说的那样,每条 RUN 指令都会创建一个新的镜像层,如果该指令执行成功,就会将此镜像层提交,之后继续执行 Dockerfile 中的下一条指令

默认情况下,RUN 指令会在 shell 里使用命令包装器 / bin/sh -c 来执行。如果是在一个不支持 shell 的平台上运行或者不希望在 shell 中运行(比如避免 shell 字符串篡改),也可以使用 exec 格式的 RUN 指令

在这种方式中,我们使用一个数组来指定要运行的命令和传递给该命令的每个参数

EXPOSE 指令,这条指令告诉 Docker 该容器内的应用程序将会使用容器的指定端口。这并不意味着可以自动访问任意容器运行中服务的端口(这里是 80)。出于安全的原因,Docker 并不会自动打开该端口,而是需要用户在使用 docker run 运行容器时来指定需要打开哪些端口。一会儿我们将会看到如何从这一镜像创建一个新容器