动手实践学Docker(二)
本文最后更新于 602 天前,内容如有失效请评论区留言。

[toc]

实验3 Docker 镜像管理

实验介绍

第一节实验中我们已经接触了一些镜像的概念,简单的说镜像就是一个容器的只读模板,用来创建容器。当运行容器时需要指定镜像,如果本地没有该镜像,则会从 Docker Registry 下载。默认查找的是 Docker Hub。Docker 的镜像是增量的修改,每次创建新的镜像都会在老的镜像上面构建一个增量的层,使用到的技术是 Another Union File System(AUFS),感兴趣的同学可以学习文档 InfoQ: 剖析 Docker 文件系统:Aufs 与 Devicemapper

知识点

本节中,我们需要依次完成下面几项任务:

  • 查看镜像列表
  • 查看镜像详细信息
  • 搜索镜像
  • 拉取镜像
  • 构建镜像
  • 删除镜像

Docker 镜像

如果能够熟练的使用容器管理命令,那么这里关于镜像的部分操作,类比 Management Commands 的特性,可以很容易的学习相应的命令。

镜像存储中的核心概念仓库(Repository)是镜像存储的位置。Docker 注册服务器(Registry)是仓库存储的位置。每个仓库包含不同的镜像。

比如一个镜像名称 ubuntu:14.04,冒号前面的 ubuntu 是仓库名,后面的 14.04 是 TAG,不同的 TAG 可以对应相同的镜像,TAG 通常设置为镜像的版本号。在前面的实验中,我们的镜像都没有加 TAG,这时 Docker 会使用默认的 TAG:latest

Docker Hub 是 Docker 官方提供的公共仓库,提供大量的常用镜像,由于国内网络原因连接会比较慢。

并且 Docker 的镜像是分层存储,每一个镜像都是由很多层组成的。而一些镜像会共享一些相同的层。对于实验环境中的 Docker 来说(Server 版本是高于 17.05),其使用的存储驱动是 overlay2,如下图所示:

image

查看镜像列表

查看镜像可以使用如下命令:

# Management Commands
docker image ls

# 旧的命令格式如下:
docker https://lrl52.top/wp-content/uploads/2022/04

也可以查看指定仓库的镜像,例如。查看 ubuntu 仓库的镜像:

# 拉取 ubuntu 镜像
docker image pull ubuntu
# 查看镜像
docker image ls ubuntu

image

查看镜像的详细信息

查看镜像的详细信息使用如下命令:

# Management Commands
docker image inspect ubuntu

# 旧的命令格式如下:
docker inspect ubuntu

image

注意:docker inspect 命令不但可以用来查看镜像的信息,也可以用来查看容器的信息

搜索镜像

docker search ubuntu

这个命令会列出 Docker Hub 中所有包含 ubuntu 关键字的镜像,结果如下图所示:

image

拉取镜像

上面的内容中描述了仓库和注册表的内容,这里,我们学习从注册表中获得镜像或者仓库的命令,使用如下命令:

# Management Commands
docker image pull [OPTIONS] NAME[:TAG|@DIGEST]

# 旧的命令格式如下:
docker pull [OPTIONS] NAME[:TAG|@DIGEST]

比较常用的配置参数为 -a,代表下载仓库中的所有镜像,即下载整个存储库。

如下所示,我们下载 ubuntu:14.04 镜像,使用如下命令:

docker image pull ubuntu:14.04

此处输入图片的描述

除了通过标签来拉取具体的镜像, 我们还可以通过摘要来拉取不同标签的镜像。从刚刚下载的结果,我们有一行 Digest 信息,这就是摘要信息。

图片描述

接下来我们演示一下通过摘要拉取镜像:首先删除刚刚下载的镜像 docker image rm ubuntu:14.04。此处以获取到的 Digest 为例,具体 Digest 参数应参照你操作时的实际值。

图片描述

构建镜像

此时,对于我们 pull 的新镜像 ubuntu:14.04 来说,如果我们需要对其进行更新,可以创建一个容器,在容器中进行修改,然后将修改提交到一个新的镜像中。

提交修改使用如下命令:

# Management Commands
docker container commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

# 旧的命令格式如下:
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

该命令的解释为从一个容器的修改中创建一个新的镜像。例如,我们运行一个容器,然后在其中创建一个文件,最后使用 commit 命令:

# 使用 run 创建运行一个新命令
docker container run \
    --name shiyanlou001 \
    -it busybox /bin/sh

# 在运行的容器中创建两个文件,test1 和 test2
touch test1 test2

# 使用 ctrl + p  及  ctrl + q 键退出

# 使用提交命令,提交容器 shiyanlou001 的修改到镜像 busybox:test 中
docker container commit shiyanlou001 busybox:test

# 查看通过提交创建的镜像
docker image ls busybox

此处输入图片的描述

通过上述操作,我们创建了一个新的镜像 busybox:test。但是本方法不推荐用在生产系统中,因为未来会很难维护镜像。最好的创建镜像的方法是 Dockerfile,修改镜像的方法是修改 Dockerfile,然后重新从 Dockerfile 中构建新的镜像。

BUILD 镜像

Docker 可以从一个 Dockerfile 文件中自动读取指令构建一个新的镜像。 Dockerfile 是一个包含用户构建镜像命令的文本文件。在创建该文件后,我们可以使用如下命令构建镜像:

docker image build [OPTIONS] PATH | URL

构建镜像的第一件事是将 Dockerfile 文件所在目录下的所有内容递归的发送到守护进程。所以在大多数情况下,最好是创建一个新的目录,在其中保存 Dockerfile,并在其中添加构建 Dockerfile 所需的文件。

对于一个 Dockerfile 文件内容来说,基本语法格式如下所示:

# Comment
INSTRUCTION arguments

使用 # 号作为注释,指令(INSTRUCTION)不区分大小写,但是为了可读性,一般将其大写。而 Dockerfile 的指令一般包含下面几个部分:

  • 基础镜像:以哪个镜像为基础进行制作,使用 FROM 指令来指定基础镜像,一个 Dockerfile 必须以 FROM 指令启动。
  • 维护者信息:可以指定该 Dockerfile 编写人的姓名及邮箱,使用 MAINTAINER 指令。
  • 镜像操作命令:对基础镜像要进行的改造命令,比如安装新的软件,进行哪些特殊配置等,常见的是 RUN 命令。
  • 容器启动命令:基于该镜像的容器启动时需要执行哪些命令,常见的是 CMD 命令或 ENTRYPOINT

例如一个最基本的 Dockerfile:

# 指定基础镜像
FROM ubuntu:14.04

# 维护者信息
MAINTAINER shiyanlou/shiyanlou001@simplecloud.cn

# 镜像操作命令
RUN \
    apt-get -yqq update && \
    apt-get install -yqq apache2

# 容器启动命令
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

通过阅读上述内容中我们熟悉的一些 Linux 指令,可以很容易的得出该命令创建了一个 apache 的镜像。包含了最基本的四项信息。

其中 FROM 指定基础镜像。RUN 命令默认使用 /bin/sh,并使用 root 权限执行。CMD 命令也是默认在 /bin/sh 中执行,但是只能有一条 CMD 指令,如果有多条则只有最后一条会被执行。

下面我们创建一个空目录,并在其中编辑 Dockerfile 文件,并基于此构建一个新的镜像,使用如下操作:

# 首先创建目录并切换目录
mkdir /home/shiyanlou/test1 && cd /home/shiyanlou/test1

# 编辑 Dockerfile 文件,默认文件名为 Dockerfile,也可以使用其它值,使用其它值需要在构建时通过 `-f` 参数指定,这里我们使用默认值。并在其中添加上述示例的内容。
vim Dockerfile

# 使用 build 命令,`-t` 参数指定新的镜像,. 是基于该目录下的 Dockerfile 构建
docker image build -t shiyanlou:1.0 .

图片描述

在执行构建命令后,需要花费一些时间来完成构建。在运行结束后,最后查看新创建的镜像:

此处输入图片的描述

在构建完成后,我们可以使用该镜像启动一个容器来运行 apache 服务,运行如下命令:

# 使用 -p 参数将本机的 8000 端口映射到容器中的 80 端口上。
docker container run \
    -d -p 8000:80 \
    --restart=always \
    --name shiyanlou002 shiyanlou:1.0

其中 --restart=always 选项保证容器始终不关闭,在本实验中如果不加此参数无法运行时,可以加上这个参数保证容器一直在运行。

此处输入图片的描述

此时,容器启动成功后,并且配置了端口映射,我们就可以通过本机的 8000 端口访问容器 shiyanlou002 中的 apache 服务了。我们打开浏览器,输入 localhost:8000,显示结果如下图:

image

如果 localhost:8000 无法访问,那么可以输入 127.0.0.1:8000。更多有关于 Dockerfile 文件格式的信息可以参考官方文档 https://docs.docker.com/engine/reference/builder/。

删除镜像

我们删除 ubuntu:latest 镜像就可以使用如下命令:

# Management Commands
docker image rm ubuntu:latest

# 旧的命令格式如下:
docker rmi ubuntu:latest

image

需要注意的是,如果该镜像正在被一个容器所使用,需要将容器删除才能成功的删除镜像。

实验总结

本节实验中我们学习了以下内容:

  • 查看镜像列表
  • 查看镜像详细信息
  • 搜索镜像
  • 拉取镜像
  • 构建镜像
  • 删除镜像

请务必保证自己能够动手完成整个实验,只看文字很简单,真正操作的时候会遇到各种各样的问题,解决问题的过程才是收获的过程。

实验4 Docker 存储管理

实验介绍

在本节内容中,我们将讨论 Docker 中管理数据的几种方式,涉及将数据从 Docker 主机挂载到容器中的不同方法。

知识点

本节中,我们需要依次完成下面几项任务:

  • 使用 volumes
  • 使用 bind mounts
  • 使用 tmpfs
  • 数据卷容器
  • 数据卷的备份与恢复

存储

通过之前的学习,我们学习了有关于容器和镜像的一些知识。对于数据来说,我们可以将其保存在容器中,但是会存在一些缺点:

  • 当容器不再运行时,我们无法使用数据,并且容器被删除时,数据并不会被保存。
  • 数据保存在容器中的可写层中,我们无法轻松的将数据移动到其他地方。

针对上述的缺点而言,有些数据信息,例如我们的数据库文件,我们不应该将其保存在镜像或者容器的可写层中。Docker 提供三种不同的方式将数据从 Docker 主机挂载到容器中,分别为卷(volumes),绑定挂载(bind mounts),临时文件系统(tmpfs)。很多时候,volumes 总是正确的选择。

  • volumes 卷存储在 Docker 管理的主机文件系统的一部分中(/var/lib/docker/volumes/)中,完全由 Docker 管理。
  • bind mounts 绑定挂载,可以将主机上的文件或目录挂载到容器中。
  • tmpfs 仅存储在主机系统的内存中,而不会写入主机的文件系统。

无论使用上述的哪一种方式,数据在容器内看上去都是一样的。它被认为容器文件系统中的目录或单个文件。

卷列表

对于三种不同的存储数据的方式来说,卷是唯一完全由 Docker 管理的。它更容易备份或迁移,并且我们可以使用 Docker CLI 命令来管理卷。

列出本地可用的卷列表可以使用如下命令:

docker volume ls

image

运行之后,你可能会看到一些环境中预置镜像对应的卷,或者显示为空。

创建卷

创建卷我们可以直接使用如下命令:

docker volume create

上述命令会创建一个数据卷,并且会随机生成一个名称。创建之后我们可以查看卷列表:

docker volume ls

image

这种由系统随机生成名称的创建卷的方式被称为 匿名卷,直接使用该卷需要指定卷名,即自动生成的 ID,所以创建卷时一般手动指定其 name,例如我们创建一个名为 volume1 的卷。

docker volume create volume1

image

挂载卷

创建卷之后,我们可以用卷来启动一个容器,这里首先需要学习 docker container run 命令的两个参数:

  • -v
    

    --volume
    
    • 由三个由冒号(:)分隔的字段组成,[HOST-DIR:]CONTAINER-DIR[:OPTIONS]
    • HOST-DIR 代表主机上的目录或数据卷的名字。省略该部分时,会自动创建一个匿名卷。如果是指定主机上的目录,需要使用绝对路径。
    • CONTAINER-DIR 代表将要挂载到容器中的目录或文件,即表现为容器中的某个目录或文件
    • OPTIONS 代表配置,例如设置为只读权限(ro),此卷仅能被该容器使用(Z),或者可以被多个容器使用(z)。多个配置项由逗号分隔。

例如,我们使用 -v volume1:/volume1:ro,z。代表的是意思是将卷 volume1 挂载到容器中的 /volume1 目录。ro,z 代表该卷被设置为只读(ro),并且可以多个容器使用该卷(z)。

  • --mount
    
    • 由多个键值对组成,键值对之间由逗号分隔。例如:type=volume,source=volume1,destination=/volume1,ro=true
    • type,指定类型,可以指定为 bindvolumetmpfs
    • source,当类型为 volume 时,指定卷名称,匿名卷时省略该字段。当类型为 bind,指定路径。可以使用缩写 src
    • destination,挂载到容器中的路径。可以使用缩写 dsttarget
    • ro 为配置项,多个配置项直接由逗号分隔一般使用 truefalse

针对上述创建的卷 volume1,用其来运行一个容器就可以使用如下命令:

docker container run \
    -it \
    --name shiyanlou001 \
    -v volume1:/volume1 \
    --rm ubuntu /bin/bash

image

或者我们也可以使用 --mount,其语法格式如下:

docker container run \
    -it --name shiyanlou002 \
    --mount type=volume,src=volume1,target=/volume1 \
    --rm ubuntu /bin/bash

image

从命令中,可以很明显的得出,--mount 的可读性更好。所以,推荐大家使用 --mount

docker container run 中我们使用了参数 --rm,它的作用在容器退出时删除容器。这里我们创建的镜像只是希望它短期运行,其用户数据并无保留的必要,因而可以在容器启动时设置 --rm 选项,这样在容器退出时就能够自动清理容器内部的文件系统。

值得注意的是,后台运行的容器无法使用 -d--rm 选项。

上述操作,我们分别运行了两个容器,并分别挂载了一个卷,还可多次使用该参数挂载多个卷或目录。并且对于这两个容器来说,由于我们使用的是同一个卷,所以他们将共享该数据卷,但是对于多个容器共享数据卷时,需要注意并发性。大家可以分别连接到两个容器中,操作数据,验证其是同步的,这里就不再详细演示了。

绑定挂载

对于数据卷来说,其优点在于方便管理。而对于绑定挂载 bind-mounts 来说,通过将主机上的目录绑定到容器中,容器就可以操作和修改主机上该目录的内容。这既是其优点也是其缺点。

例如,我们将 /home/shiyanlou 目录挂载到容器中的 /home/shiyanlou 目录下,使用的命令如下:

docker container run \
    -it \
    -v /home/shiyanlou:/home/shiyanlou \
    --name shiyanlou003 \
    --rm ubuntu /bin/bash

image

而如果使用的是 --mount,相应的语句如下:

docker container run \
    -it \
    --mount type=bind,src=/home/shiyanlou,target=/home/shiyanlou \
    --name shiyanlou004 \
    --rm ubuntu /bin/bash

image

如果绑定挂载时指定的容器目录是非空的,则该目录中的内容将会被覆盖。并且如果主机上的目录不存在,会自动创建该目录。

上述两个操作针对的是目录,而对于挂载文件来说,可能会出现一些特殊情况,涉及到绑定挂载和使用卷的区别。下面我们重现这一操作:

  1. 首先在当前目录,即 /home/shiyanlou 目录下,创建一个 test.txt 文件。并向其中写入文本内容 “test1″:
    echo "test1" > test.txt
    
  2. 接着创建一个容器 shiyanlou005,将 test.txt 文件挂载到容器中的 /test.txt 文件,并查看容器中 /test.txt 文件的内容:
    docker container run \
       -it \
       -v /home/shiyanlou/test.txt:/test.txt \
       --name shiyanlou005 ubuntu /bin/bash
    

    image

  3. 这时新打开一个终端,通过 echo 命令向 /home/shiyanlou/test.txt 文件追加内容 “test2″,并在容器中查看 /test.txt 文件的内容:

    echo "test2" >> test.txt
    

    此处输入图片的描述

  4. 这时无论是在容器中还是主机上都能查看到该文件的内容。接下来在主机上查看 test.txtinode 号,并使用 VIM 编辑该文件,添加 “test3″,并查看该文件的内容:

    此处输入图片的描述

如上图所示,在主机上使用 VIM 编辑后,通过 VIM 做出的修改不能在容器中查看到。这是因为 VIM 编辑保存文件的时候,会将文件内容写入到一个新的文件中,保存好后,删除掉原来的文件,并将新文件重命名,从而完成保存的操作。但是我们标识文件是通过 inode,因此 Docker 绑定的主机文件,依旧是 VIM 编辑之前的 inode,即旧文件。所以容器中看到的,依然是旧的内容。

对于数据卷来说,由 Docker 完全管理,而绑定挂载,则需要我们自己去维护。我们需要自己手动去处理这些问题,这些问题并不仅仅是上面演示的内容,还可能有用户权限,SELINUX 等问题。

最后简单说一下临时文件系统 tmpfs。它只存储在主机的内存中。当容器停止时,相应的数据就会被移除。

docker run \
    -it \
    --mount type=tmpfs,target=/test \
    --name shiyanlou008 \
    --rm ubuntu bash

数据卷容器

如果容器之间需要共享一些持续更新的数据,最简单的方式就是使用用户数据卷容器。其他容器通过挂载这个容器实现数据共享,这个挂载数据卷的容器就叫做数据卷容器。数据卷容器就是一种普通容器,它专门提供数据卷供其它容器挂载使用。

首先,我们创建一个数据卷和数据卷容器,执行的命令如下:

# 创建一个名为 vdata 的数据卷
docker volume create vdata

# 创建一个挂载了 vdata 的容器,这个容器就是数据卷容器
docker container run \
    -it \
    -v vdata:/vdata \
    --name ShiyanlouVolume ubuntu /bin/bash

# 在 /vdata 目录下创建一个文本文件
echo "I am ShiyanlouVolume" > /vdata/f.txt

image

接下来我们分别打开新的终端输入以下命令,创建两个容器,在执行 docker container run 时,我们添加参数 --volumes-from 继承数据卷容器 ShiyanlouVolume 挂载的数据卷。进入容器后分别创建文件

# 创建容器 test1
docker container run \
    -it \
    --volumes-from ShiyanlouVolume \
    --name test1 ubuntu /bin/bash

# 查看 vdata 目录是否存在
ls -dl /vdata/

# 创建一个文件,并写入内容
echo "I am test1" > /vdata/test1.txt

图片描述

同样的,我们执行上述操作,创建容器 test2。

# 创建容器 test2
docker container run \
    -it \
    --volumes-from ShiyanlouVolume \
    --name test2 ubuntu /bin/bash

# 查看 vdata 目录是否存在
ls -dl /vdata/

# 创建一个文件,并写入内容
echo "I am test2" > /vdata/test2.txt

我们进入到 ShiyanlouVolume 容器所在的终端,在挂载的数据中查看文件的内容:

# 查看数据卷中的内容
ls -al /vdata/

图片描述

从上图的结果中我们可以看到,数据卷在三个容器之间是共享的。

数据备份

数据存在于数据卷中,如果我们想要备份它,可以采用创建备份容器的方式。

docker container run \
   --volumes-from ShiyanlouVolume \
   -v /home/shiyanlou/backup:/backup \
   ubuntu tar cvf /backup/backup.tar /vdata/
  • --volumes-from ShiyanlouVolume 使得备份容器继承容器 ShiyanlouVolume 的数据卷。
  • -v /home/shiyanlou/backup:/backup/home/shiyanlou/backup 目录采用绑定挂载的方式,挂载到容器的 /backup 目录上。
  • tar cvf /backup/backup.tar /vdata 容器中执行了这么一条压缩归档命令,将 /vdata 中的全部数据打包到了 /backup/backup.tar,而刚刚的数据绑定,使得整个压缩包存在于主机中,从而达到了数据备份的效果。

图片描述

如上图所示,数据卷中的所有数据都被打包到了 /home/shiyanlou/backup 目录中。

数据恢复

与数据备份相同的方式,我们可以使用如下命令创建恢复容器,来还原数据卷中的数据。

docker container run \
   --volumes-from ShiyanlouVolume \
   -v /home/shiyanlou/backup:/backup \
   ubuntu tar xvf /backup/backup.tar -C /

这里解压的路径为 //vdata 的上一级目录。由于与数据备份非常相似,这里不再给出结果分析。

实验总结

本节实验主要使用三种不同的方式将数据从 Docker 主机挂载到容器中,分别为卷(volumes),绑定挂载(bind mounts),临时文件系统(tmpfs)。还介绍了数据卷容器、数据卷的备份与恢复。

请务必保证自己能够动手完成整个实验,只看文字很简单,真正操作的时候会遇到各种各样的问题,解决问题的过程才是收获的过程。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇