我的第一本Docker书Docker镜像和仓库

58次阅读

共计 5638 个字符,预计需要花费 15 分钟才能阅读完成。

Docker 镜像和仓库

什么是 Docker 镜像

docker 镜像是由文件系统系统叠加而成, 最底层是一个引导文件系统,即 bootfs 和 linux 引导文件系统很像,用户是不会和引导文件系统有什么交互,当一个容器启动后,它将会被移到内存中,docker 看起来很像一个典型的 linux 虚拟化栈,

docker 镜像第二层是 root 文件系统 rootfs, 它位于引导文件系统之上,rootfs 可以是一种或多张操作系统,root 文件系统永远是能是只读状态,并且 docker 利用 联合加载技术 又会在 root 文件系统层上加载更多的只读文件系统, 联合加载 是指一次同时加载多个文件系统,但是在外层看起来只能看到一个文件系统,联合文件系统会将各层文件系统叠加到一起,这样最终的文件系统会包含所有底层的文件和目录。

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

当 docker 第一次启动一个容器时 ,初始读写层是空的,当 文件系统发生变化时,这些变化都会应用到这一层上 ,当修改一个文件时,这个文件首先会从 该读写层下面的只读层复制到该读写层 只读版本依然存在 ,但是已经 被读写层中的该文件副本所隐藏 , 这种机制称为 写时复制, 每个只读镜像层都是只读的,并且以后永远不会变化,创建新容器时,docker 会构建初一个镜像栈,并在栈的最顶端添加一个读写层,这个读写层加上其下面的镜像层以及一些配置数据,就构成一个容器。

列出镜像

想要知道本机中有哪些镜像,可以使用 docker images 命令查看,本地镜像都保存在 Docker 宿主机的 /var/lib/docker 目录下。

$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu latest c4ff7513909d 6 days ago 225.4 MB

docker 从仓库中下载下来,镜像保存在仓库中,而仓库位于 Registry 中,默认的 Registry 是由Docker 公司运营的公共 Registry 服务,Docker 提供一种标签功能 (tag) 来区别镜像,这样可以使得同一个仓库中可以存储多个镜像, 同一个镜像可以有多个标签,在构建镜像的时候,建议指定标签,这样更容易来区分镜像

拉取镜像

启动容器时,如果镜像不在本地,Docker 会从 Docker hub 拉去镜像,没有指定镜像标签,会自动下载 latest 标签的镜像。也可以使用 docker pull 拉取镜像到本地,然后再启动容器,拉取指定标签的镜像为docker pull 镜像名: 标签名

查找镜像

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

$ sudo docker search puppet
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
wfarr/puppet-module...
jamtur01/puppetmaster
. . .

查找镜像会返回该镜像的详细信息,仓库名 , 镜像描述 , 用户评价 (stars), 是否官方 (official)- 由上游开发这管理的镜像, 自动构建(Automated)- 表示镜像是由 Docker Hub 自动构建

构建镜像

在使用过程中我们需要修改,更新和管理这些镜像,可以自动构建镜像,构建镜像的方法有两种,docker commit 命令和 Dockerfile 文文件构建,在实际开发中,不推荐使用 docker commit 方式构建,应该使用 更灵活 更强大,的Dockerfile 来构建镜像

docker commit

docker commit 方式构建镜像,需要我们先创建一个容器,并在容器內做出修改,最后将修改提交为一个新的镜像

  • 创建容器
$ sudo docker run -i -t ubuntu /bin/bash
root@4aab3ce3cb76:/#
  • 安装软件
root@4aab3ce3cb76:/# apt-get -yqq update
. . .
root@4aab3ce3cb76:/# apt-get -y install nginx
. . .
  • 构建镜像
$ sudo docker commit 4aab3ce3cb76 jamtur01/apache2
8ce0ea7a1528

docker commit 指定要提交的修改过的容器 ID, 以及一个目标镜像仓库,和镜像名,docker commit 提交的两个镜像的差异部分,使得构建镜像非常的轻量化

$ sudo docker commit -m"A new custom image" -a"James Turnbull" \
4aab3ce3cb76 jamtur01/apache2:webserver
f99ebb6fed1f559258840505a0f5d5b6173177623946815366f3e3acff01adef

还可以指定 -m 来指定新创建的镜像的提交信息,-a 来指定该镜像的作者信息

Dockerfile

并不推荐 docker commit 来构建镜像,推荐使用 Dockerfile 定义文件,和 docker build 命令来构建镜像,使用 Dockerfile 的好处是构建镜像更具可重复性,透明性等好处。

Dockerfile 初体验

编写 Dockerfile 文件
  • 创建一个目录,并初始化 Dockerfile 文件
$ mkdir static_web
$ cd static_web
$ touch Dockerfile

创建的目录 就是我们的 构建环境 ,Docker 称此环境为 上下文 ,或者 构建上下文 ,Docker 会在构建镜像时, 将构建上下文和该上下文中的文件和目录上传到 Docker 守护进程 ,这样 Docker 守护进程就能 直接访问用户想要在镜像中存储的任何代码,文件,或者其他数据

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

Dockerfile 由 一系列指令和参数组成 ,指令都是 大写字母 , 指令后面跟随一个参数,Dockerfile 会按照顺序,从上到下执行,在书写 Dockerfile 时,需要合理安排指令的顺序。
每条指令都会创建一个新的镜像层并对镜像进行提交, 大致执行流程如下

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

如果用户的 Dockerfile 由于某些原因(指令失败) 没有正常结束,那么用户将得到一个可以使用的镜像,方便用户调试,用户可以基于该镜像运行一个具备交互功能的容器,进行 失败指令调试。

Dockerfile 也支持注视,已 # 开始。

每个 Dockerfile第一条指令必须是 FROM, FROM 指定一个与存在的镜像,后续指令都将基于该镜像进行,这个镜像被称 基础镜像

MAINTAINER 指令,指定该镜像的作者是谁,以及作者的电子邮件嘻,联系方式
RUN 指令,会在当前镜像中运行指定的命令,每条 RUN 指令都会创建一个新的镜像层,如果指令成功,就会将此镜像层提交,之后继续执行 Dockerfile 中的吓一条指令, 默认情况下,RUN 指令会在 shell 中使用命令包装器,/bin/sh -c 来执行,如果在一个不支持 shell 的平台上运行,或者不希望在 shell 中运行,可以使用 exec 格式的 run 命令

  • exec 格式的 RUN 指令
RUN ["apt-get", "install", "-y", "nginx"]

EXPOSE 指令,告诉 Docker 该容器內额应用程序将会使用容器的指定端口,并不意味这可以自动访问任意容器运行中服务的端口,出于安全原因,Docker 并不会自动打开该端口,而是需要用户在使用 docker run 运行容器是来指定需要打开那些端口,可以指定多个 EXPOST 指令向外部公开多个端口

Dockerfile 构建新镜像

Dockerfile 编写完毕,需要使用 docker build 来构建镜像,执行docker build 命令时,Dockefile 中所有的指令都会被执行,并且提交,并且在该命令成功结束后返回一个新镜像

$ cd static_web
$ sudo docker build -t="jamtur01/static_web" .
Sending build context to Docker daemon 2.56 kB
Sending build context to Docker daemon
Step 0 : FROM ubuntu:14.04
---> ba5877dc9bec
Step 1 : MAINTAINER James Turnbull "james@example.com"
---> Running in b8ffa06f9274
---> 4c66c9dcee35
Removing intermediate container b8ffa06f9274
Step 2 : RUN apt-get update
---> Running in f331636c84f7
---> 9d938b9e0090
Removing intermediate container f331636c84f7
Step 3 : RUN apt-get install -y nginx
---> Running in 4b989d4730dd
---> 93fb180f3bc9
Removing intermediate container 4b989d4730dd
Step 4 : RUN echo 'Hi, I am in your container' >/usr/share/
nginx/html/index.html
---> Running in b51bacc46eb9
---> b584f4ac1def
Removing intermediate container b51bacc46eb9
Step 5 : EXPOSE 80
---> Running in 7ff423bd1f4d
---> 22d47c8cb6e5
Successfully built 22d47c8cb6e5

-t 参数,为新镜像设置了仓库和名称,也可以为过程中镜像设置一个标签,镜像名: 标签

$ sudo docker build -t="jamtur01/static_web:v1" .

Docker 1.5.0 开始,-f 标志指定一个区别与标准Dockerfile 的构建源的位置,这个文件可以不必命名为Dockerfile 但是必须要位于构建上下文中

docker build –t="jamtur01/static_web" -f path/to/file
构建失败

在构建过程中,指令失败,也会也会得到一个镜像,我们可以使用 docker run命令来基于这个构建到目前为止已经成功的最后一步创建一个容器,方便调试指令

Dockerfile 和构建缓存

每一步构建过程都会将结果提交为镜像,Docker 会讲之前的镜像层看作缓存,构建起来额速度非常块,也可以在确保构建过程中不实用缓存, 可以使用 --no-cache标志

$ sudo docker build --no-cache -t="jamtur01/static_web" .
查找新镜像

可以使用 docker images 可以查看到刚刚构建出来的镜像,也可以使用 docker history 命令来查看镜像是如何构建出来的

$ sudo docker history 22d47c8cb6e5
IMAGE CREATED CREATED BY SIZE
22d47c8cb6e5 6 minutes ago /bin/sh -c #(nop) EXPOSE map[80/tcp:{}] 0 B
b584f4ac1def 6 minutes ago /bin/sh -c echo 'Hi, I am in your container' 27 B
93fb180f3bc9 6 minutes ago /bin/sh -c apt-get install -y nginx 18.46 MB
9d938b9e0090 6 minutes ago /bin/sh -c apt-get update 20.02 MB
4c66c9dcee35 6 minutes ago /bin/sh -c #(nop) MAINTAINER James Turnbull " 0 B
. . .
新镜像启动容器

我们可以使用新镜像,检查构建镜像是否一切正常

$ sudo docker run -d -p 80 --name static_web jamtur01/static_web \
nginx -g "daemon off;"
6751b94bb5c001a650c918e9a7f9683985c3eb2b026c2f1776e61190669494a8

-p 该标志用来控制Docker 在运行是应该公开那些网络端口给外部(宿主机),Docker 可以在宿主机随机选择一个位于 32768-61000 的一个较大的端口来映射到容器的 80 端口上,可以在宿主机指定一个具体的端口来映射到容器的 80 端口上
Docker 提供了一个更简单的方式,即 -P 参数,该参数可以用来对外公开在Dockerfile 中通过 EXPOSE 指令公开的所有端口

$ sudo docker run -d -P --name static_web jamtur01/static_web \
nginx -g "daemon off;"

该命令会将容器內的 80 端口对本地宿主机公开 并且绑定到宿主机的一个随机端口上 ,该命令会将用来构建该镜像的Dockerfile 文件中的 EXPOST 指令 指定的其他端口也一并公开

正文完
 0