Docker之- 使用Docker 镜像和仓库

使用Docker 镜像和仓库

上一篇文章中,我们学习了包括 docker run 在内的许多对容器进行操作的基本指令,那么在本节中,我们主要探讨 Docker 镜像的一些概念,比如什么是镜像,如何对镜像进行管理,如何修改镜像,如何创建、存储、共享自己创建的镜像等,那么就开始我们的学习

什么是 Docker 镜像

Docker 镜像是由文件系统叠加而成,最底端是一个引导文件系统,也就是bootfs,这很像典型的 Linux/Unix 的引导文件系统。Docker 用户永远不会和引导文件系统有什么交互。实际上,一个容器启动后,它就会被移入内容,而引导文件系统则会被卸载,从而留出更多的空间。(感觉有点像古代的餐馆招待?负责引导顾客进入餐馆,自己的工作就算是完成了)

传统的Linux 引导过程中,root文件系统最先以只读的方式加载,当引导结束后,会切换为读写模式。但是在Docker 中,root文件系统永远只是只读状态,并且使用联合加载的技术一次同时加载多个文件系统。联合加载会将各层系统文件叠加在一起,最终的文件系统包含底层的文件和目录。

联合加载:联合加载指的是一次同时加载多个文件系统,但是外面看起来只有一个文件系统。

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

用一幅图来表示一下:

1515111-20190817223405592-1291430381.png

列出 Docker 镜像

我们先从如何列出系统中的 Docker 镜像来开始,可以使用 docker images 命令来实现,如下

1515111-20190817223412440-1075951254.png

可以看到,我们已经获取了一个镜像列表。那么,这些镜像是从哪来的呢?我们执行 docker run 命令时,同时进行了镜像下载

镜像从仓库下载下来。镜像保存在仓库中,而仓库存在于 Registry 中。默认的 Registry 是由 Docker 公司运行的公共 Registry 服务,即 Docker Hub。需要进行ID的注册

1515111-20190817223431985-298011311.png

Docker Registry 的代码是开源的,你也可以拥有自己的Registry。

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

仓库可以包含很多镜像,你可以使用docker pull来拉取仓库中的镜像,如下
1515111-20190817223448532-1817120037.png

Git 拉取代码的指令是 git pull ,这样就很相似了。

再来使用 docker images 看一下现在有哪些镜像

1515111-20190817223458377-1403495712.png

因为我的 Docker Hub 仓库中只有一个 ubuntu 的镜像,所以图中标红的这个镜像是我们刚从 Docker Hub 上下载下来的。

tag 标签

为了区分同一个仓库中的不同镜像,Docker 为我们提供了 tag 这个标签,每个镜像在列出来的时候都带有一个标签,如12.10、 12.04等,这种标签机制使得一个仓库中允许存储多个镜像。

我们可以在仓库后面加一个冒号:标签名 的方式来指定该仓库中的某一个镜像,例如 docker run -t -i --name new_container ubuntu:12.04 /bin/bash

1515111-20190817223512870-1481163797.png

Docker 会自动帮我们切换到 Ubuntu 的环境下,当然,这种方式创建了一个交互式任务。

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

Docker Hub

Docker Hub 有两种仓库,一种是用户仓库,一种是顶层仓库。用户仓库是由开发人员自己创建的,顶层仓库是由Docker Hub 内部人员管理。

用户仓库的命名由两部分构成,如 cxuan/ubuntu

  • 用户名 例如 : cxuan
  • 仓库名 例如 : ubuntu

相对的,顶层仓库的命名就比较严谨,如 ubuntu 仓库。顶层仓库由 Docker 公司和选定的优质基础镜像厂商来管理,用户可以基于这些镜像构建自己的镜像。

用户镜像都是由爱好者社区自己提供的,没有经过 Docker 公司的认证,所以需要自己承担相应的风险。

拉取镜像

还记得docker run 的启动过程吗?再来一下这张图回顾一下

1515111-20190817223524377-1410938821.png

其实也可以通过 docker pull 命令先预先拉取镜像到本地,使用 docker pull 命令可以节省从一个新镜像启动一个容器所需要的时间。下面就来领取一下fedora基础镜像( fedora 是 Fedora 优质厂商提供的基础镜像 )

1515111-20190817223532074-2046793086.png

可以使用 docker images 查看新镜像是否拉取到本地,不过我们这次只希望看到 fedora 的镜像,那么你可以使用这个命令: docker images fedora

1515111-20190817223540667-2051737556.png

可以看到我们已经把 fedora 镜像拉取到了本地

查找镜像

我们可以通过 docker search 命令来查找所有 Docker Hub 上公共可用的镜像,如下

1515111-20190817223548669-942943150.png

下面还有很多镜像,我们主要看一下每条镜像都返回了哪些内容

  • 仓库名称
  • 镜像描述
  • 用户评价 --- 反应一个镜像的受欢迎程度
  • 是否官方 --- 是否由Docker 公司及其指定厂商开发的镜像
  • 是否自动构建 --- 表示这个镜像是由 Docker Hub 自动构建的

从上面查询的结果中选择一个镜像进行拉取,docker pull jamtur01/puppetmaster这条命令将会下载 jamtur01/puppetmaster镜像到本地。

接下来就可以使用这个镜像来构建一个容器,下面就用 docker run 命令构建一个容器。

1515111-20190817223559410-1998589230.png
...

查看版本号

1515111-20190817223609539-1664953257.png

构建镜像

上面我们看到如何拉取并且构建好带有定制内容的 Docker 镜像,那么我们如何修改自己的镜像,并且管理和更新这些镜像呢?

  • 使用 docker commit 命令
  • 使用 docker build 命令和 Dockerfile 文件

现在我们不推荐使用 docker commit 命令,相反应该使用更灵活更强大的 Dockerfile 来构建镜像。不过,为了对 Docker 又一个更深的了解,我们还是会先介绍一下 docker commit 构建镜像。之后,我们重点介绍Docker 所推荐的构建方法:编写 Dockerfile 之后使用 docker build 命令。

创建Docker Hub 账号

构建镜像中很重要的一环就是如何共享和发布镜像。可以将镜像推送到 Docker Hub中或者自己的私有 Registry 中。为了完成这项工作,需要在 Docker Hub上创建一个账号

如果你还没有Docker 通行证,在 hub.docker.com 注册一个,记下你的用户名,登录本地计算机上的Docker公共注册表。使用docker login,输入用户名和密码进行登录

1515111-20190817223622488-792708725.png

你的个人信息会保存在 $HOME/.dockercfg 文件中

使用 Docker 的commit 命令创建镜像

创建 Docker镜像的第一种方式是使用 docker commit 命令。可以将此想象为我们是在版本控制系统里面提交变更,毕竟这和 git commit 命令真是太像了。

我们先从创建一个容器开始,这个容器基于我们前面见过的 ubuntu 镜像。如下

1515111-20190817223633507-124356800.png

接下来,我们在 ubuntu 中安装 apache 服务器,使用apt-get -yqq updateapt-get -y install apache2 命令。

1515111-20190817223643652-1621943623.png

我们启动了一个容器,并安装了 Apache 服务器,我们会将这个服务器作为 Web 服务器运行,所以我们想把它当前状态保存起来。这样下次启动就不用重新安装了。为了完成这项工作,需要先使用 exit 从 ubuntu 中退出,之后再运行 docker commit 命令。如下

1515111-20190817223652479-771395374.png

我们看到,在上图所示的 docker commit 命令中,指定了要提交修改过的容器ID(可以通过 docker ps -l -q 命令得到刚刚创建的容器 ID),以及一个镜像仓库和镜像名,这里是 jamtur01/puppetmaster

可以使用 docker images jamtur01/puppetmaster 命令查看刚刚创建的镜像。

1515111-20190817223702106-596341186.png

可以在提交时指定更多的数据,就和 git 的命令是一样的,使用 docker commit -m命令

1515111-20190817223713564-1193942424.png

这条命令中我们使用 -m(message) 指定提交信息,同时指定了 --authro 选项,列出镜像作者信息。接着列出了想要提交的容器ID, 最后指定了 jamtur01/puppetmaster ,并为其打上了 webserver 的tag 标签。

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

1515111-20190817223722288-1826114730.png

使用 Dockerfile 构建镜像

我们并不推荐使用 docker commit 方法来构建镜像。相反,我们推荐使用 Dockerfiledocker build的命令来构建镜像。Dockerfile 使用基于 DSL 语法的指令来构建一个 Docker 镜像,之后使用 docker build 命令基于 Dockerfile 中的指令构建一个新的镜像。

我们的第一个 Dockerfile

下面我们创建一个目录并初始化 Dockerfile,我们创建一个包含简单web服务器的Docker镜像

1515111-20190817223731195-1171251446.png

如上图所示,我们在 /usr/local/docker 目录下创建了一个 static_web 的目录,再创建了一个 Dockerfile 文件。那么这个 static_web 目录就是我们的构建环境。Docker 称此环境为上下文(context)或者 构建上下文(build context)Docker 会在构建镜像时将构建上下文和该上下文中的文件和目录上传到 Docker 守护进程。这样 Docker 守护进程就可以直接访问你镜像中的 代码、文件和数据。

下面是一个通过 Dockerfile 来构建 web 服务器的 Docker 镜像

# Version: 0.0.1
FROM ubuntu:14.04
MAINTAINER James Turnbull "james@example.com"
RUN apt-get update
RUN apt-get install -y nginx
RUN echo 'Hi, I am in your container' \
        >/usr/share/nginx/html/index.html
EXPOSE 80

该 Dockerfile 由一系列指令和参数组成。每条指令,如FROM,都必须为大写字母,而且后面要跟随一个参数: FROM ubuntu:14.04。Dockerfile 中的指令会按照顺序由上向下执行,所以编写 Dockerfile 时,请注意它的顺序。

如果不能使用 :wq 来进行保存的话,请首先使用 sudo su切换到管理员模式,然后就可以保存啦。

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

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

从上面可以看出,如果你的Dockerfile 由于某些原因(例如指令失败了)没有正常结束,那么你将得到了一个可以使用的镜像。这对调试很有帮助: 可以基于镜像运行一个具备交互功能的容器,使用最后创建的镜像对你最后失败的指令做出调试

Dockerfile 也支持中文注释,以 # 开头的行都会被认为是注释。Dockerfile 中的第一行就是注释的例子

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

  • 在上面的示例中,我们使用了 ubuntu:14.04 作为新镜像基础镜像。基于这个 Dockerfile 构建的新镜像以 Ubuntu 14.04 操作系统为基础。再运行一个容器时,必须要指明基于哪个基础镜像进行构建。

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

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

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

    如下 : RUN["apt-get", "install", "-y", "nginx"]

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

  • 接着设置了 EXPOSE 命令,这条执行告诉 Docker 容器内的应用程序将会使用容器的指定接口。这并不意味着可以自动访问任意容器运行中的服务端口,可以指定多个 EXPOSE 指令向外公开多个端口。

基于 Dockerfile 构建新镜像

执行 docker build 命令时,Dockerfile 中的所有指令都会被执行并且提交,并且在命令成功结束后返回一个新镜像,下面就来看看如何构建一个新镜像。

1515111-20190817223746302-1422727618.png

一定不要忘记最后面有个空格 和. ,也可以在构建镜像的过程中为镜像设置一个标签: 使用方法为“镜像名 : 标签”,如下所示

指令失败时呢?

之前大致介绍了一下指令失败时的执行过程,下面来看一个例子: 假设我们在上面的 Dockerfile 中把 nginx 拼成了 ngnx ,再来构建一遍

1515111-20190817223755181-2045054937.png

我们可以看到,程序出错了,这个时候我们希望去调试一下这次失败。我们使用 docker run 命令来基于这次构建到目前为止已经成功的最后一步创建一个容器,这里它的ID 是 dee85a65a396,我们可以使用如下命令

docker run -t -i dee85a65a396 /bin/bash,来恢复到出错之前的镜像,然后重新运行出错的指令apt-get install -y ngnx ,可以看到哪里出错了

1515111-20190817223803292-1783935603.png

但是感觉这个步骤是多余了一些,如果 Dockerfile 中出现了错误,那么 Docker 就会给你提示,用不着重新运行命令来找出问题原因。

Dockerfile 和构建缓存

由于每一步的结果都会作为下一步的基础镜像,所以Docker 构建镜像的过程非常聪明,它会将之前的镜像层作为缓存。

正如上面 Dockerfile 来举例,比如,在我们调试过程中,不需要在第一步和第三步之间做任何修改,因此 Docker 会将之前构建时创建的镜像当作缓存并作为新的开始点。再次构建时,Docker 会直接从第四步开始。当之前的构建步骤没有变化时,这会节省大量的时间。如果第一步到第三步之间有什么变化,则回到第一步开始。

然而,有的时候不希望有缓存的功能,这个时候你需要使用 apt-get update,那么 Docker 将不会刷新 APT 包的缓存,要想略过缓存,可以使用 docker build 的 --no-cache 标志。

基于构建缓存的 Dockerfile 模版

构建缓存的一个好处就是,我们可以实现简单的 Dockerfile 模版,一般会在 Dockerfile 文件顶部使用相同的指令集模版,比如对 ubuntu ,使用下面的模版

FROM ubuntu:14.04
MAINTAINER James Turnbull "james@example.com"
ENV REFRESHED_AT 2019-08-15
RUN apt-get -qq update

我们来分析一下这个新的 Dockerfile :

  • 首先,通过 FROM 指令为新镜像设置了一个基础镜像 ubuntu:14.04。
  • 接着,使用 MAINTAINER 指令添加了自己的详细信息
  • 然后,通过 ENV 指令设置了一个名为 REFRESHED_AT 的环境变量,用来表示最后一次的更新时间
  • 最后,使用 RUN 指令运行 apt-get -qq update 命令,该指令会刷新 APT 包的缓存,用来确保每个安装的软件包都在最新版本。

查看新镜像

现在来看一下新构建的镜像,使用 docker images 命令来完成

1515111-20190817223813665-1652970172.png

如果想要了解镜像是如何构建出来的,可以使用 docker history 命令,如下

1515111-20190817223821321-456096829.png

从结果可以看出镜像构建的每一层都是哪些指令构成的

从新镜像启动容器

我们可以基于新构建的镜像启动新容器,来检查我们的构建工作是否正常

1515111-20190817223830322-786384087.png

在这里,我们使用 docker run 命令,启动一个 static_web 的容器, -d表示的是以分离(detached) 的方式在后台运行。这种方式适合 nginx守护进程 这种需要长时间运行的进程。我们也指定了需要在 容器中运行的命令: nginx -g "daemon off;",将以前台方式运行 nginx 作为我们的服务器。

我们这里也使用了一个新的 -p 标志,用来控制 Docker 再运行时应该给外部开放哪些端口

  • Docker 可以在宿主机上随机选择 49153 --- 65535 之间的一个比较大的端口映射到 80 端口上
  • 可以在 Docker 宿主机指定一个具体的端口来映射到 80 端口上

使用 docker ps查看一下端口分配情况

1515111-20190817223839517-221149885.png

Docker 把 32769 端口映射到了 80 端口上

也可以通过 docker port查看端口的映射情况

1515111-20190817223851845-942943546.png

Dockerfile 指令

Dockerfile 指令比较多,这里我们会对 Dockerfile 单独列一个章节进行说明

将镜像推送至 Docker Hub

镜像构建完毕之后,我们也可以将它上传到 Docker Hub 上面去,这样其他人就能使用这个镜像了。

Docker Hub 的私有仓库是需要收费的

我们可以使用 docker push 命令将镜像推送至 Docker Hub。命令如下

1515111-20190817223859693-275706165.png

为什么推送不上去?

网上搜索了一下,大概是镜像标签的问题,重新为镜像设置一个标签

1515111-20190817223907037-1910110231.png

然后把这个标签推送上去,相当于就是把镜像推送上去

1515111-20190817223914680-500704109.png

我们可以在 Docker Hub 上看到我们推送的镜像了

1515111-20190817223921137-1877459867.png

删除镜像

如果不再需要一个镜像了,也可以将它删除,使用 docker rmi命令来删除一个镜像

1515111-20190817223929973-1614002437.png

该操作只能删除本地镜像,如果你已经推送至 Docker Hub 上,那么你还需要在 Docker Hub 上将其删除

登录 Docker Hub ,直接点下面的链接删除

1515111-20190817223937915-2134186987.png

docker rmi 删除多个容器的方式直接在后面枚举容器即可,中间用空格隔开

总结

本篇文章主要讲述了 Docker 中的镜像和仓库的一些概念和基本用法,那么你是否能回顾起来下面这些内容呢?

  • 什么是镜像
  • 如何列出Docker中的镜像,tag标签是干什么用的
  • 如何拉取远程仓库中的镜像
  • 如何查找镜像
  • 对于镜像构建,你能想到哪些内容
  • 如何推送镜像至 Docker Hub
  • 如何删除镜像

1515111-20190817224024111-1728326994.png

转载于:https://www.cnblogs.com/cxuanBlog/p/11370739.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值