D

Docker 初学者指南

2025-06-07

Docker 初学者指南

对于任何刚接触云原生的人来说,理解 Docker 镜像和容器的概念都至关重要。无论您从事开发、DevOps 还是项目管理(或任何其他技术岗位 :)。一旦您掌握了 Docker 的基础知识,您将更容易理解 Kubernetes、服务网格以及几乎所有其他云原生工具的工作原理。您可以将本指南视为云原生的第一本实用指南。

什么是 Docker?

Docker 容器是由 Docker 公司推广的。容器的概念并不新鲜。容器在 Linux 中已经存在了十多年。正是 Docker 让它们变得更加流行。

容器背后的理念是将操作系统进行划分,以便安全地运行多个应用程序。Linux 的命名空间cgroups 功能使这成为可能。简而言之,使用命名空间,您可以对操作系统的不同组件进行切片,并创建一个独立的工作空间。cgroups 允许对资源进行细粒度的控制。例如,这可以防止单个容器耗尽所有资源。

虚拟机 (VM) 和容器之间有什么区别?

您可以使用在物理计算机上运行的软件(虚拟机管理程序)来模拟特定的硬件系统。虚拟机管理程序用于创建和运行虚拟机。它位于计算机硬件和虚拟机之间。每个虚拟机都运行其客户操作系统,并拥有其二进制文件、应用程序等。虚拟映像通常体积巨大,占用大量内存。

虚拟机和容器

另一方面,容器直接在主机操作系统上运行。它们共享操作系统和内核,并且硬件未被虚拟化。

与虚拟机相比,容器更轻量级。它们不需要虚拟机管理程序,因此启动速度更快。容器启动时间通常以秒或更短为单位,而虚拟机启动时间则以分钟为单位。

容器、图像以及如何创建它们?

在“运行”或“创建”容器之前,您需要先创建它。就像虚拟机一样,要运行虚拟机,您必须创建虚拟机的 ISO 映像。

Docker 镜像一个只读模板,其中包含有关如何创建或运行容器的说明。Docker 镜像由如下所示的Dockerfile创建:

FROM ubuntu:18.04
WORKDIR /app
COPY hello.sh /app
RUN chmod +x hello.sh
RUN apt-get update
RUN apt-get install curl -y
CMD ["./hello.sh"]
Enter fullscreen mode Exit fullscreen mode

大多数情况下,你的镜像将基于现有的 Docker 镜像。在上面的 Dockerfile 中,我们以名为 ubuntu:18.04 的现有镜像为基础,该镜像代表 Ubuntu 18.04。

Dockerfile 中的其余指令执行以下操作:

  • 设置工作目录(请注意,这是图像内的工作目录,而不是主机上的目录)
  • hello.sh将文件从主机复制到/app图像内的文件夹
  • 运行几个命令
  • 使用 CMD 设置容器的默认命令 - 即这是我们在运行容器时想要执行的命令

使用类似上述的 Dockerfile,我们可以创建一个 Docker 镜像。Docker 镜像是多个层的集合(Dockerfile 中的一个命令等于镜像中的一个层)。这些层层堆叠,除了最顶层的可写层外,其余都是只读的。

Docker 镜像层

您可以将 Docker 镜像视为模板,将容器视为这些模板的实例。运行容器时,您正在创建一个具有可写层的 Docker 镜像实例。对正在运行的容器所做的任何更改都会在可写层上进行。例如,如果您在容器内运行一个写入文件的应用程序,则该文件存储在可写层上。这里需要记住的一点是,您在容器运行时对可写层所做的任何修改都会在容器停止后丢失。

等功能可用于将数据存储在正在运行的容器之外。运行容器时,您可以指定并挂载这些卷,以便在容器内部使用。即使容器停止,数据仍会保留,因为它不属于容器的一部分。

图像命名

所有 Docker 镜像都通过其名称引用。镜像名称由三部分组成:仓库名称、镜像名称和镜像标签。镜像标签(或版本)可以省略,但是,任何没有标签的镜像都会获得名为 的默认标签。始终使用镜像标签,并且切勿依赖或使用标签latest始终是一个好习惯latest

Docker 镜像命名结构

您可以将 Docker 镜像存储在计算机上。如果您想共享这些镜像并供其他人使用,则必须将镜像上传(或推送)到Docker 镜像仓库。

在本文的后面,我们将使用一个名为 的镜像alpine:3.10.3。请注意,该镜像名称仅由两部分组成。这是因为它是 Docker Hub 上的官方镜像。Docker Hub 上有很多官方镜像都以这个名称命名,但不包含仓库。您可以在此处阅读更多关于官方镜像的信息

Docker 注册表

Docker 镜像仓库是您可以上传和存储 Docker 镜像的地方。每个云提供商也提供 Docker 或镜像仓库作为托管服务。您也可以运行和托管自己的镜像仓库,或者使用Docker Hub等免费镜像仓库。镜像仓库可以是公共的,也可以是私有的。公共镜像仓库允许任何人拉取或下载镜像,而私有镜像仓库则需要身份验证。

常见场景

了解了基本概念之后,让我们来看看使用 Docker 时会遇到的几个常见场景。

构建并推动

Docker 构建是指获取Dockerfile、构建上下文(包含代码或可能包含在镜像中的文件的文件夹)和镜像名称,并使用 Docker CLI构建镜像。此操作的结果是一个 Docker 镜像。

Docker 构建和 Docker 推送

构建好镜像(或现有镜像)后,即可其推送到 Docker 镜像仓库。推送其实就是上传镜像。

使用 Docker CLI,构建镜像的命令如下:

docker build -t myrepository/imagename:0.1.0 .
Enter fullscreen mode Exit fullscreen mode

标志-t用于提供镜像名称,.末尾的 表示如何提供构建上下文。点号表示我们希望当前文件夹作为构建上下文。但是,您的构建上下文可以是其他文件夹。

构建上下文的意义是什么?

我们从上面的 Dockerfile 中取出以下指令:

COPY hello.sh /app
Enter fullscreen mode Exit fullscreen mode

这条指令告诉 Docker CLI 将hello.sh文件从主机复制到镜像内的文件夹。但是 Docker CLI 如何知道该文件在哪里呢?这时,构建上下文就派上用场了,它告诉 Docker在构建上下文中/app查找该文件。hello.sh

要将图像推送到注册表,您需要确保首先登录到注册表,然后才能运行docker push myrepository/imagename:0.1.0

拉动并运行

当镜像位于镜像仓库中时,您可以使用pull命令从镜像仓库下载镜像。您提供要拉取的镜像的名称,Docker CLI 会访问镜像仓库并将镜像下载到您的本地计算机。

使用 Docker CLI 拉取镜像的命令与 push 命令类似:

docker pull myrepository/imagename:0.1.0
Enter fullscreen mode Exit fullscreen mode

最后,您可以运行 Docker 镜像或从中创建容器。请注意,您无需构建镜像、推送镜像、然后拉取镜像即可运行。如果您不想将镜像推送到镜像仓库,也可以不执行此操作。您仍然可以使用 run 命令来运行镜像。

第二点:如果你想运行镜像,你也不需要先拉取它。如果你尝试运行计算机上不存在的镜像(即你还没有拉取它),Docker CLI 会很智能地先拉取镜像,然后再运行它。

Docker pull 和 Docker run

Docker run 命令有很多选项可以传入,以控制容器的执行方式,如果它向主机公开任何端口,挂载任何卷,您还可以传入环境变量等等。

Docker 运行命令示例可能如下所示:

docker run --name mycontainername -p 5000:8080 myrepository/imagename:0.1.0
Enter fullscreen mode Exit fullscreen mode

请注意,在上面的命令中,我们为容器命名 (mycontainername),但这不是必需的。如果您不提供名称,Docker 会为您随机取一个巧妙的名称:interesting_tharpstrange_hypatiapractical_morse等等。Docker 使用一系列科学家和著名黑客来为容器命名。您可以在此处查看他们使用的列表。请注意,您的容器永远不会被命名boring_wozniak为 Steve Wozniak,这并不无聊 :)

Docker 实践

了解了基本术语和概念后,让我们开始动手尝试 Docker 吧!在本节中,你将安装 Docker CLI,创建Docker Hub帐户(如果你还没有),并尝试不同的 Docker CLI 命令。

安装 Docker Desktop

适用于 Mac 和 Windows 的 Docker Desktop 是一套简化 Docker 使用的工具。它包含 Docker 引擎、CLI 客户端以及一系列其他工具。

创建 Docker Hub 帐户

您需要一个 Docker Hub 帐户才能将镜像推送到公共 Docker 注册表。您可以在 https://hub.docker.com 上注册并创建您的 Docker Hub 帐户

登录后,您可以创建不同的仓库- 仓库名称是 Docker 镜像名称前面的值。例如,我的一个仓库名为learncloudnative。名为hello-web且标签为 0.1.0的镜像的完整名称learncloudnative/hello-web:0.1.0。

完成 Docker 安装后,打开终端并运行以下docker version命令:

$ docker version
Client: Docker Engine - Community
 Version: 19.03.8
 API version: 1.40
 Go version: go1.12.17
 Git commit: afacb8b
 Built: Wed Mar 11 01:21:11 2020
 OS/Arch: darwin/amd64
 Experimental: true

Server: Docker Engine - Community
 Engine:
  Version: 19.03.8
  API version: 1.40 (minimum version 1.12)
  Go version: go1.12.17
  Git commit: afacb8b
  Built: Wed Mar 11 01:29:16 2020
  OS/Arch: linux/amd64
  Experimental: true
 containerd:
  Version: v1.2.13
  GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version: 1.0.0-rc10
  GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version: 0.18.0
  GitCommit: fec3683
Enter fullscreen mode Exit fullscreen mode

该命令的输出应该与上面的输出类似。

Dockerfile 和其他文件

在您的计算机上的某个位置创建一个空文件夹,并创建一个名为的文件,Dockerfile其内容如下:

FROM alpine:3.10.3
WORKDIR /app
COPY hello.sh /app
RUN chmod +x hello.sh
RUN apk update
RUN apk add curl
CMD ["./hello.sh"]
Enter fullscreen mode Exit fullscreen mode

这个 Dockerfile 将一个名为的文件复制hello.sh到镜像中,使其可执行,然后运行 ​​apk update 和 apk add curl,最后将 hello.sh 设置为容器运行时执行的命令。

什么是apk?apk是Alpine上用来管理软件的工具。

hello.sh在与 Dockerfile 相同的文件夹中创建该文件:

#!/bin/sh
echo "Hello Docker!"
Enter fullscreen mode Exit fullscreen mode

通过此设置,我们可以开始研究 Docker 层。

Docker 层

在本节中,您将构建您的第一个 Docker 镜像,并使用 Docker CLI 检查各个层。

我们将从构建名为 的 Docker 镜像开始docker-layers。您可以从 Dockerfile (和 hello.sh 文件) 所在的同一文件夹运行以下命令:

docker build -t docker-layers:0.1.0 .
Enter fullscreen mode Exit fullscreen mode

在命令输出中,您将看到构建上下文如何发送到 Docker 守护进程,镜像如何被拉取,然后 Dockerfile 中的每条指令如何被执行。输出的最后一行应该显示您的镜像 (docker-layers:0.1.0) 已构建并已标记。

我之前没提过镜像标记——你可以把它理解为重命名镜像,或者创建一个引用旧镜像的新镜像。例如,你可以运行命令将镜像myimagename:0.1.0标记为somethingelse:2.0.0docker tag。该命令会创建另一个引用原镜像的新镜像。需要注意的是,Docker 不会创建该镜像的另一个副本。

您可以运行docker images来获取机器上的图像列表。

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker-layers 0.1.0 6a5b8912d27f 5 minutes ago 8.31MB
alpine 3.10.3 965ea09ff2eb 6 months ago 5.55MB
Enter fullscreen mode Exit fullscreen mode

如果你之前没有安装或使用过 Docker,你的机器上会有两个镜像:你刚刚构建的docker-layers镜像和Dockerfile 中用作基础镜像的alpine镜像。

我们还尝试运行容器以获取“Hello Docker!”输出:

$ docker run docker-layers:0.1.0
Hello Docker!
Enter fullscreen mode Exit fullscreen mode

Docker 在创建镜像时会利用层以及层之间的差异。这可以加快后续构建速度。如果您再次构建镜像,您会注意到命令完成所需的时间比第一次要少得多。(是的,除去第一次尝试下载镜像的时间,后续构建速度仍然更快。)

在我的计算机上,第二次 Docker 构建花费了不到一秒钟:

$ time docker build -t docker-layers:0.1.0 .
real 0m0.501s
user 0m0.088s
sys 0m0.061s
Enter fullscreen mode Exit fullscreen mode

让我们看看为图像创建的图层:

$ docker history docker-layers:0.1.0
IMAGE CREATED CREATED BY SIZE COMMENT
6a5b8912d27f 4 minutes ago /bin/sh -c #(nop) CMD ["./hello.sh"] 0B
3030475d0a23 4 minutes ago /bin/sh -c apk add curl 1.34MB
81cd9a8738f0 4 minutes ago /bin/sh -c apk update 1.42MB
e68bc418551f 4 minutes ago /bin/sh -c chmod +x hello.sh 30B
14d207dc283c 4 minutes ago /bin/sh -c #(nop) COPY file:c2c91b54b63f7c0e… 30B
4628741a4e97 4 minutes ago /bin/sh -c #(nop) WORKDIR /app 0B
965ea09ff2eb 6 months ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 6 months ago /bin/sh -c #(nop) ADD file:fe1f09249227e2da2… 5.55MB
Enter fullscreen mode Exit fullscreen mode

请注意, apk add curlapk update命令分别创建了两个独立的层。我们可以将这两个命令合并到 Dockerfile 中的一个命令中,以提高效率。

打开 Dockerfile 并将两行合并如下:

FROM alpine:3.10.3
WORKDIR /app
COPY hello.sh /app
RUN chmod +x hello.sh
RUN apk update && apk add curl
CMD ["./hello.sh"]
Enter fullscreen mode Exit fullscreen mode

让我们再次重建图像并检查图层:

$ docker build -t docker-layers:0.1.0 .
...

$ docker history docker-layers:0.1.0
IMAGE CREATED CREATED BY SIZE COMMENT
8470cb0b2e17 19 seconds ago /bin/sh -c #(nop) CMD ["./hello.sh"] 0B
ed2a17906a01 19 seconds ago /bin/sh -c apk update && apk add curl 2.76MB
...
Enter fullscreen mode Exit fullscreen mode

这次我们只用了一个 apk 命令的图层。接下来,我们将向镜像中添加另一个文件,看看它如何影响图层。

创建一个名为 hello.txt 的文件,其中包含一条简单消息:

echo "Hello!" >> hello.txt
Enter fullscreen mode Exit fullscreen mode

接下来,让我们更新 Dockerfile 以将此文件包含在镜像中:

FROM alpine:3.10.3
WORKDIR /app
COPY hello.sh /app
RUN chmod +x hello.sh
COPY hello.txt /app
RUN apk update && apk add curl
CMD ["./hello.sh"]
Enter fullscreen mode Exit fullscreen mode

我在运行apk命令之前复制了新文件。如果你再次重建镜像 ( ),你会注意到apk命令被重复执行了,并没有被重复使用。这是因为层的堆叠方式。新的层与之前的层有所不同,而且由于我们在运行apk之前添加了 COPY 命令,之前的缓存也就失效了。docker build -t docker-layers

好消息是,我们可以改进这一点。如果我们将apk命令移动到包含 FROM 指令的行之后,镜像中的第二层就会是apk层。让我们在实践中看看。

将命令移动RUN apk update && apk add curl到 Dockerfile 中第一行的正下方:

FROM alpine:3.10.3
RUN apk update && apk add curl
WORKDIR /app
COPY hello.sh /app
RUN chmod +x hello.sh
COPY hello.txt /app
CMD ["./hello.sh"]
Enter fullscreen mode Exit fullscreen mode

我们需要再次重建镜像来创建层。如果你检查层,你会注意到apk命令现在更接近堆栈底部了。让我们来证明一下,如果我们添加另一个文件并重建镜像,该命令将不会再次执行。

创建bye.txt文件:

echo "Bye!" >> bye.txt
Enter fullscreen mode Exit fullscreen mode

在Dockerfile的底部添加COPY命令:

FROM alpine:3.10.3
RUN apk update && apk add curl
WORKDIR /app
COPY hello.sh /app
RUN chmod +x hello.sh
COPY hello.txt /app
COPY bye.txt /app
CMD ["./hello.sh"]
Enter fullscreen mode Exit fullscreen mode

如果你这次重新构建镜像,你会发现速度明显加快了。这是因为 Docker 重新使用了镜像层,而不再重新运行 apk 命令。

了解层的工作原理非常重要,因为您可以显著提高 Docker 构建的速度。

推送和标记 Docker 镜像

您需要登录到镜像仓库才能推送 Docker 镜像,否则推送命令将失败。您可以通过 Docker Desktop 登录镜像仓库,也可以在终端中使用docker login命令。

登录后,您几乎可以开始推送镜像了。在上一节中,我们将镜像命名为docker-layers:0.1.0 -未使用仓库名称。要推送到镜像仓库,需要仓库名称。仓库名称是您注册 Docker Hub 时使用的名称。

我们可以再次重建镜像,并为镜像仓库提供一个全名,但是使用tag命令并为镜像提供一个新名称会更快。将[your-repository]​​替换为您的仓库名称:

 docker tag docker-layers:0.1.0 [your-repository]/docker-layers:0.1.0
Enter fullscreen mode Exit fullscreen mode

如果命令执行成功,您将不会看到任何输出。请注意,我们也可以使用 tag 命令来更新镜像名称的其他部分。

现在您可以继续通过运行以下命令将图像推送到注册表:

docker push [your-repository]/docker-layers:0.1.0
Enter fullscreen mode Exit fullscreen mode

Docker 将镜像推送到注册表,任何人都可以拉取或下载它!

拉取 Docker 镜像并运行容器

可以使用pull命令从 registry 拉取或下载镜像。我们以alpine镜像为例:

$ docker pull alpine
Using default tag: latest
latest: Pulling from library/alpine
cbdbe7a5bc2a: Pull complete
Digest: sha256:9a839e63dad54c3a6d1834e29692c8492d93f90c59c978c1ed79109ea4fb9a54
Status: Downloaded newer image for alpine:latest
docker.io/library/alpine:latest
Enter fullscreen mode Exit fullscreen mode

由于我们没有为图像提供特定版本,因此 Docker 使用:latest标签拉取图像。

您也可以直接使用运行命令,Docker 将为我们拉取镜像:

docker run alpine
Enter fullscreen mode Exit fullscreen mode

使用上述命令运行镜像将启动容器,但它会立即退出。我们可以做的是在容器内部获取一个 shell 提示符,以保持容器处于活动状态。我们需要提供-i-t标志以使容器具有交互性,并分配一个伪终端 (TTY)。最后,我们还需要提供一个在容器启动时运行的命令。由于我们需要一个 shell,因此可以运行 shell - /bin/sh。让我们尝试在 Alpine 容器内部运行该命令以获取 shell:

docker run -it alpine /bin/sh
Enter fullscreen mode Exit fullscreen mode

您会注意到提示已经改变,如果您运行 env 命令,您将看到环境变量来自容器,而不是您的主机。

$ docker run -it alpine /bin/sh
/ # env
HOSTNAME=32a19aa0c35f
SHLVL=1
HOME=/root
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
/ #
Enter fullscreen mode Exit fullscreen mode

要退出容器,您可以输入exit。每个正在运行的容器都有一个 ID - 您可以打开单独的终端窗口并运行docker ps命令来检查该 ID:

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
32a19aa0c35f alpine "/bin/sh" 2 minutes ago Up 2 minutes sharp_sanderson
Enter fullscreen mode Exit fullscreen mode

接下来,你可以通过这个ID杀死容器:docker kill 32a19aa0c35f。随着容器的死亡,你会看到另一个终端窗口中的提示消失。

映射端口

另一个相当常见的任务是在本地运行容器,并将容器端口映射到本地机器的端口。例如,你可以在容器内运行一个服务,但为了访问该服务,你需要将容器端口映射到主机端口,以便访问它。

让我们使用图像中提供的简单 Node.js 应用程序learncloudnative/helloworld:0.1.0

如果您只是运行映像,则将无法访问其中运行的应用程序,因为应用程序在容器内监听的端口未映射到您的机器。

要将容器端口映射到主机端口,可以-p在运行容器时使用标志,如下所示:

docker run -p 8080:3000 learncloudnative/helloworld:0.1.0
Enter fullscreen mode Exit fullscreen mode

镜像下载完成后,您将看到“正在监听 3000 端口”的消息。这是容器内正在监听 3000 端口的应用程序发出的消息。我们使用 -p 参数将容器的 3000 端口映射到主机的 8080 端口。

打开浏览器并访问http://localhost:8080,您将看到“Hello World!”网页。您还会注意到容器在终端输出中写入的任何日志。要停止运行容器,可以按 CTRL+C。有时,端口映射也被称为“暴露”端口或“发布”端口。Dockerfile 中可以包含一个名为 的指令EXPOSE。“暴露端口”和“EXPOSE”指令的组合可能会造成混淆。该EXPOSE指令的目的是记录容器内运行的应用程序正在监听哪个端口。例如,镜像的 Dockerfilehelloworld应该包含一条EXPOSE 3000指令,因为这是应用程序监听的端口。

因此,即使EXPOSEDockerfile 中包含该指令,您仍然需要使用该-p标志。您可以使用其他标志(-P),在这种情况下,Docker 会将指令中的端口映射EXPOSE到主机上的随机端口。

在这种情况下,运行命令如下所示:

docker run -P learncloudnative/helloworld:0.1.0
Enter fullscreen mode Exit fullscreen mode

请注意,这里没有实际的端口号。容器的启动方式与之前类似,但现在如果列出所有正在运行的容器,您会注意到主机上的一个随机端口 ( 32768) 被映射到了容器端口 ( 3000):

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
94cfd042df18 learncloudnative/helloworld:0.1.0 "npm start" 9 seconds ago Up 8 seconds 0.0.0.0:32768->3000/tcp hardcore_northcutt
Enter fullscreen mode Exit fullscreen mode

结论

在本文中,我尝试解释 Docker 是什么、它与虚拟机有何不同,以及在工作或阅读 Docker 相关内容时会遇到的一些常见概念和术语。希望最后的教程能够帮助您在实践中理解这些概念。

Docker 还有许多其他功能,本文只是略微介绍,但足以帮助您入门。在接下来的文章中,我将解释如何使用 Docker Volumes 和 Docker Compose。

如果你有兴趣深入了解并查看更多实际示例,可以查看我撰写的网关指南。该指南讲解了你需要了解的有关网关和代理的基础知识,并在实践部分展示了如何使用 Docker Compose 运行代理。

你喜欢这本指南吗?我很乐意听到你的反馈——请在下方留言,或者通过 Twitter电子邮件联系我们

文章来源:https://dev.to/peterj/beginners-guide-to-docker-4pkk
PREV
Docker React 在 Docker 容器中运行 React 应用程序 RUN yarn run build
NEXT
如何使用 CSS 创建 BBC 新闻风格的连接项目符号列表 1 ......... 2.........