Docker 启蒙教程(2)

这部分提到了数据持久化、网络访问容器、Dockerfile 和 Docker Compose。

第六章 数据持久化

在前面的教程里,你学会了操作容器。然而,容器销毁后,数据也将消失。

为了保存容器运行时产生的数据,我们需要使用数据卷(Volume)。

创建数据卷

可以使用以下命令创建一个名为 MyVolume 的数据卷

1
docker volume create MyVolume

image-20240211215721478

查看数据卷信息

如果想要查看 MyVolume 数据卷的信息,则使用

1
docker volume inspect MyVolume

image-20240211215808696

docker volume inspect 是一个有用的命令,可以帮助我们迅速找到数据存储的路径。我们创建的 MyVolume 数据卷在主机上的实际存储路径是 /var/lib/docker/volumes/MyVolume/_data,到这里就可以找到你的数据了。

列出数据卷

然而,要查看数据卷信息,首先要知道数据卷的名字。It is easy for you. 你只需要使用 docker volume ls 命令就可以列出所有数据卷,让它们乖乖立正站好。

1
docker volume ls

image-20240211220017261

数据卷的特点

数据卷是特殊的。即使删除了依赖他的容器,数据卷也会保留在你的主机上;数据卷的数据是实时更新的;数据卷可以在容器之间重用,往返自然,可以让这个容器用,也可以让那个容器用。

数据卷具体使用

创建完数据卷,我们就需要在创建容器时使用 --mount 参数把它挂载到容器上。

下面的命令创建了名为 MyContainer 的容器,并挂载 MyVolume 数据卷到容器内部的 /home/yourdata 目录。该目录里的文件都会保存到这个数据卷里。

1
docker run -d --name MyContainer --mount source=MyVolume,target=/home/yourdata mysql:latest

第七章 网络访问容器

指定端口映射

容器内部与主机的端口是隔离的。我们可以通过 -p 参数进行端口映射。

下面的命令将容器内部的 3306 端口映射到主机的 12345 端口。

1
docker run -p 12345:3306 mysql:latest

冒号左边是主机的端口,冒号右边是容器内部端口。

除此之外,还有两种映射格式,在这里就不多讲了。

拿不定主意?

如果你拿不定主意,也可以使用 -P 参数,让 Docker 随机映射一个端口到内部容器开放的网络端口。

1
docker run -P -d mysql:latest

查看端口映射配置

你有可能会忘记你设置的端口映射,不过不用怕,你可以使用 docker port 命令查看端口映射的配置。

下面的命令查询 MyContainer 容器的 3306 端口映射情况。3306 是容器内部的端口。

1
docker port MyContainer 3306

第八章 Dockerfile

Docker 通过读取 Dockerfile 中的指令来自动构建镜像,Dockerfile 是一个文本文件。Docker 会按照顺序执行此文件里的命令来构建镜像。

质疑 Dockerfile

如果你曾经浅浅了解过 Docker,你可能会在某个文件夹里看到 Dockerfile 文件。它没有任何后缀名,使用文本编辑器打开发现里面的指令 短小、全大写,有汇编内味了,对于一个习惯了 Windows 系统的人来说很难受。

人的天性就是这样,不喜欢未知的东西。

理解 Dockerfile

部分内容摘自阿里云开发者社区。

要理解 Dockerfile,我们首先要了解之前提过的「镜像分层」的概念。

Docker 的镜像是分层的,每执行一条 Dockerfile 指令,就会创建一个新的层。这些层是堆叠在一起的,每一层都是与前一层的变化的增量,堆叠到最后变成了你需要的镜像。

不分层就会导致拉取的镜像体积很大,让我们举个例子。

在不分层的情况下:假如我们拉取 6.2 版本的 Redis,文件大小是 100 MB ,下次要下载 6.3 版本,就要再次下载 100 MB 的文件,尽管两个版本之间只有几行配置文件的差异。这样做效率非常低,如果能只下载有差异的部分就好了。

这个痛点是镜像分层要解决的问题。如果版本 2 基于版本 1,那么拉取版本 2 只需要一份和版本 1 有差异的增量数据。是不是感觉在哪里见过?没错!你的手机系统更新的时候也是这样做的。

这样做的好处是:

  • 拉取更快:分层了,只需要拉取本地不存在的层
  • 存储更少:相同的层仅需存储一份
  • 运行时存储更少:容器运行时可以共享相同的层

让我们看一段具体的例子:

1
2
3
4
FROM ubuntu:22.04
COPY . /app
RUN make /app
CMD python /app/app.py

就像上面我们讲的一样,在这个例子中,每执行一条命令,就会创建一个新的层。

  • FROMubuntu:22.04 镜像中创建一个层
  • COPY 从 Docker 客户端当前目录添加文件
  • RUN 使用 make 命令构建你的应用
  • CMD 制定要在容器内运行的命令

当你运行镜像,生成容器时,你将在底层之上添加一个新的可写层,也称为「容器层」。对正在运行的容器做的所有更改,例如写入新文件、修改现有文件和删除文件,都会写入容器层。

当容器被销毁时,可写层也将随之销毁。如果你做了数据持久化,可写层的修改将被实时同步到数据卷中。

编写 Dockerfile

Dockerfile 有许多指令,作为启蒙教程,我们在这里仅介绍四个常用的命令,FROMCOPYRUNCMD

FROM

FROM 指令初始化一个新的构建阶段,并为后续执行的命令设置基础镜像。前面我们讲过,在 Dockerfile 中,每执行一条命令,就会创建一个新的层。使用此命令意味着你创建了镜像中的第一个层,后续指令均以这层为基础。

这个指令有三种格式。

1
FROM [--platform=<platform>] <image> [AS <name>]
1
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
1
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

FROM 可以在单个 Dockerfile 内出现多次,以创建多个镜像或使用一个构建阶段作为另一构建阶段的依赖项。

image 填镜像名,去掉尖括号;tag 是标签,用来指定版本。用 [] 括起来的均为可选项。

作为一个新手,其他参数大概率用不到,这里就不讲了。

1
FROM ubuntu:latest

通常情况下,我们只需要用到这个例子这样的指令。

COPY

1
COPY <src> <dest>

COPY 指令从主机的 <src> 处复制新文件或目录,并把他们粘贴到容器内的 <dest> 路径。

1
COPY hom* /mydir/

这个命令展示了将主机当前目录所有以 hom 开头的文件粘贴到容器内 /mydir 目录中。

RUN

RUN 指令将执行你指定的命令,并在上一层的基础上创建新层,创建的新层将在 Dockerfile 的下一步中使用。一般用来安装软件包。

1
2
RUN apt-get update
RUN apt-get install -y curl

直接在 RUN 后写命令即可。

CMD

CMD 指令设置容器新建时要在容器内执行的命令。和 RUN 指令的用法类似。

如果你需要再容器新建时默认执行一条命令,那就可以使用 CMD 语句。但如果在新建时指定了其他命令,该语句就会被省略。

在 Dockerfile 中,只能有一条 CMD 指令。如果有多个 CMD 指令,则只有最后一个生效。

Dockerfile 的介绍到此为止,后面也许会放一些例子,让大家更直观的感受。

第九章 Docker Compose

Docker Compose 类似于一个配置文件,我们称呼它为「模板文件」。你在里面写好了对新容器的要求,就可以执行一个非常简洁的指令启动,不必使用需要指定许多参数的 docker run 命令。

写好配置文件以后,只需要在配置文件目录执行下面的命令即可。

1
docker-compose up

是不是非常简洁?让我们现在就学习如何使用吧。

安装

如果你使用的是本教程的安装指令,那么 Docker Compose 已经安装到了你的主机。跳过本小节即可。

1
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

亲自写一个模板文件吧

模板文件是 YAML 格式。先看一下官方给出的样例。

1
2
3
4
5
6
7
8
9
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
redis:
image: redis

webredis 都是容器的名字,就相当于在用 docker run 命令时添加的 --name 参数,为容器指定名字。

你可能会疑惑:「为什么有多个容器名?是不是可以启动多个容器?」没错!Docker Compose 生来就是高山,用来批量启动容器。既然能同时启动多个容器,那就可以只启动单个容器。不管你通过它启动多少容器,都要比使用 docker run 方便得多。

我们首先看 web 下的内容:

  • build 指定的是 Dockerfile 的路径,用来构建容器,这里有一个 . 是相对路径,表示的就是当前目录。
  • ports 相当于 -p 参数,用来配置端口映射。冒号左侧是主机端口,右侧是容器内部的端口。
  • volumes 是使用数据卷存储数据。冒号左侧是主机的路径,冒号右侧是主机内部路径,这里的效果是将容器内部 /code 路径下的文件存到主机的当前目录。

再看 redis 下的内容:

  • image 指定容器使用的镜像,这里不需要 build,只有没有指定镜像的时候才需要 build

内容就是这些。此时你可以把文件命名为 compose.yaml

Docker Compose,启动!

compose.yaml 同级目录下,输入 docker-compose up 就可以启动了。你使用这条简单的命令分别启动了依赖同目录 Dockerfile 构建的镜像、容器内部 5000 端口映射到主机 5000 端口、在主机同目录存储容器内部 /code 路径内的数据、名为 web 的容器,依赖 redis 镜像、名为 redis 的容器。

如果想让它运行在后台,则使用 -d 参数,以守护态运行容器。

1
docker-compose up -d
作者

晓夜

发布于

2024-02-19

更新于

2024-02-19

许可协议