运行我们的第一个 Docker 镜像
由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!
介绍
近年来,各行各业的技术进步极大地提高了软件产品的需求和消费速度。敏捷开发和持续集成等趋势也促进了需求增长。因此,许多组织选择转向云基础设施。
云基础设施提供托管的虚拟化、网络和存储解决方案,并可按需付费。这些提供商允许任何组织(或个人)注册并访问基础设施,而这些基础设施如果要在本地或数据中心构建,则需要投入大量资金用于空间和设备。亚马逊云服务 (AWS) 和微软 Azure 等云提供商提供简单的 API,几乎可以立即开发和配置大规模虚拟机群。
将基础设施部署到云端,虽然为许多使用传统解决方案的组织解决了诸多问题,但也带来了新的挑战,例如大规模运行这些服务时的成本管理。事实上,这些云成本如此巨大,以至于催生了一门全新的学科。
虚拟机 (VM) 利用虚拟机管理程序技术在大型硬件之上创建小型服务器,彻底改变了基础设施采购方式。然而,虚拟化的缺点在于运行虚拟机需要消耗大量资源。虚拟机本身的外观、行为和体验与真正的裸机硬件无异,因为像 Zen、KVM 或 VMware 这样的虚拟机管理程序会分配资源来启动和管理整个操作系统镜像。分配给虚拟机的资源也使得它们体积庞大且难以管理。此外,在本地虚拟机管理程序和云端之间迁移虚拟机可能意味着每个虚拟机需要迁移数百 GB 的数据。
为了实现更高程度的自动化并优化云部署,企业纷纷转向容器化和微服务解决方案。容器在宿主操作系统内核的隔离区域内运行软件服务,这种技术被称为进程级隔离。这意味着,容器无需为每个进程运行完整的操作系统内核来实现隔离,而是可以共享宿主操作系统的内核来运行多个应用程序。这得益于Linux内核的两种特性:控制组(cgroups)和命名空间隔离。借助这些特性,用户可以在单个虚拟机上运行数百个容器,每个容器运行一个独立的应用程序实例。
这与传统的虚拟机架构截然不同。通常,部署虚拟机的目的是为了利用这台机器运行单个服务器或少量服务。这会导致 CPU 资源利用率低下,而这些资源本可以更好地分配给其他任务或处理更多请求。一种可能的解决方案是在单个虚拟机上安装多个服务。然而,这会导致在确定哪个机器运行哪个服务时出现严重的混乱。此外,它还将托管多个软件服务和后端依赖项的责任全部压在了单个操作系统上。
容器化微服务架构通过允许容器运行时在宿主机操作系统上调度和运行容器来解决这些问题。容器运行时并不关心容器内运行的是哪个应用程序,它只关心容器镜像是否存在,以及该镜像是否可以下载并在宿主机操作系统上执行。容器内运行的应用程序是简单的 Python 脚本、Kibana 安装还是传统的 Cobol 应用程序都无关紧要。只要容器镜像采用标准格式,容器运行时就会下载该镜像并运行其中的软件。
在本系列文章中,我们将探讨 Docker 容器运行时,并学习在本地和大规模环境中运行容器的基础知识。本文将讨论使用`docker run`命令运行容器的基础知识。
Docker 引擎
Docker Engine是提供对 Linux 内核进程隔离功能访问的接口。由于只有 Linux 公开了容器运行所需的功能,Windows 和 macOS 主机需要在后台运行 Linux 虚拟机才能实现容器执行。对于 Windows 和 macOS 用户,Docker 提供了Docker Desktop套件,用于在后台部署和运行此虚拟机。
Docker Engine 还提供内置功能,用于从称为Dockerfile 的源文件构建和测试容器镜像。构建完成后,容器镜像可以推送到容器镜像仓库。镜像仓库是一个容器镜像存储库,Docker 主机可以从中下载并执行容器镜像。
容器启动时,Docker 默认会下载容器镜像,将其存储在本地容器缓存中,最后执行容器的入口点指令。入口点指令是启动应用程序主进程的命令。当该进程停止时,容器也会停止运行。
根据容器内运行的应用程序,入口点指令可能是一个始终运行的长时间服务器守护进程,也可能是一个执行完毕后自然停止的短生命周期脚本。此外,许多容器会在启动主进程之前执行入口点脚本,完成一系列设置步骤。
运行 Docker 容器
容器的生命周期由容器本身的状态及其内部运行的进程决定。容器的运行状态取决于操作员、容器编排器或容器内运行的应用程序自身的状态。例如,您可以使用 ` docker start`或`docker stop`命令手动启动或停止容器。如果 Docker 检测到容器处于不健康状态,它也可能会自动重启或停止容器。此外,如果容器内运行的主应用程序发生故障或停止,容器也会停止运行。
在接下来的练习中,我们将看到如何使用docker run、docker ps和docker images命令来启动和查看简单容器的状态。
运行 Hello-World 容器
Docker 发布了一个体积极小、易于执行的Hello World容器镜像。该容器演示了容器运行单个进程且生命周期无限的特性。
在本练习中,我们将使用`docker run`命令启动hello-world容器,并在容器执行完毕后使用`docker ps`命令查看其状态。这将提供一个在本地运行容器的基本概览。
开始之前,如果您使用的是 Windows 或 macOS,请确保您的Docker Desktop实例正在运行。
在 Bash 终端或 PowerShell 窗口中输入docker run命令。这将指示 Docker 运行一个名为hello-world的容器:
docker run hello-world
shell 应该返回类似如下的输出:
Unable to find image 'hello-world: latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
Digest: sha256:
8e3114318a995a1ee497790535e7b88365222a21771ae7e53687ad76563e8e76
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working
correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64)
3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit: https://docs.docker.com/get-started/
让我们看看我们刚才做了什么。我们指示 Docker 运行容器hello-world。因此,Docker 首先会在其本地容器镜像缓存中查找同名容器。如果找不到(就像我们这里的情况一样),它会在互联网上的容器镜像仓库中查找相应的镜像。默认情况下,Docker 会查询 Docker Hub 上是否有已发布的同名容器镜像。
从日志中可以看到,它找到了名为library/hello-world的容器,并开始逐层拉取容器镜像。我们将在本系列的后续文章《Dockerfile 入门》中更深入地探讨容器镜像和层。
镜像下载完成后,Docker 会运行该镜像,并显示“ Hello from Docker”的输出。由于该镜像的主要功能仅仅是显示该输出,因此容器在显示完输出后便会停止运行。
输入docker ps命令查看系统上运行的容器:
docker ps
这将返回类似于以下内容的输出:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
docker ps命令的输出为空,因为它默认情况下只显示当前正在运行的容器。这与 Linux 的ps命令类似,后者也只显示正在运行的进程。
使用docker ps -a命令可以显示所有容器,包括已停止的容器:
docker ps -a
在返回的输出中,您应该看到hello-world实例:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0561352787ff hello-world "/hello" 4 minutes ago Exited (0) 4 minutes ago pedantic_mirzakhani
如您所见,Docker 为容器分配了一个唯一的 ID。它还显示了运行的镜像、该镜像中执行的命令、创建时间、运行该容器的进程状态以及一个易于理解的名称。这个容器大约在 4 分钟前创建,执行了程序`/hello`,并且运行成功。程序运行并执行成功,因为它返回了退出代码 (0)。
您还可以查询系统,查看 Docker 在本地缓存了哪些容器镜像。执行`docker images`命令即可查看本地缓存:
docker images
返回的输出应显示本地缓存的容器镜像:
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest d2c94e258dcb 13 months ago 13.3kB
目前缓存的唯一容器镜像为hello-world容器镜像。该镜像运行的是最新版本,创建于 13 个月前,大小为 13.3 KB。由此可见,该 Docker 镜像非常小巧,并且开发者在过去 13 个月中没有发布过任何代码更改。此输出对于在实际应用场景中排查软件版本差异非常有帮助。
由于我们只是简单地告诉 Docker 运行hello-world容器镜像而没有指定版本,Docker 默认会拉取最新版本。您可以通过在`docker run`命令中添加版本号来指定特定版本。例如,如果hello-world容器镜像的版本是2.1,您可以使用 ` docker run hello-world:2.1`命令来运行该版本。
如果再次执行相同的`docker run`命令,那么每次执行`docker run`命令都会创建一个新的容器实例。容器化的优势之一在于能够轻松运行软件应用程序的多个实例。为了了解 Docker 如何处理多个容器实例,我们将再次运行相同的`docker run`命令来创建另一个hello-world容器实例。
docker run hello-world
您应该看到以下输出:
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
请注意,这次 Docker 无需再次从 Docker Hub 下载容器镜像。这是因为我们已经将容器镜像缓存到本地。Docker 可以直接运行容器并将输出显示在屏幕上。
如果我们再次运行docker ps -a命令:
docker ps -a
在输出结果中,您应该看到此容器镜像的第二个实例已完成执行并进入停止状态,如输出结果的STATUS列中的Exit (0)所示:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2aa43b348c33 hello-world "/hello" 4 hours ago Exited (0) 4 hours ago elegant_cray
0561352787ff hello-world "/hello" 6 hours ago Exited (0) 6 hours ago pedantic_mirzakhani
现在输出中显示了该容器的第二个实例。每次执行`docker run`命令时,Docker 都会创建一个新的容器实例,并附带其属性和数据。您可以运行的容器实例数量取决于系统资源的处理能力。
再次执行docker images命令,检查基础镜像:
docker images
返回的输出将显示 Docker 基于创建两个运行实例的单个基础镜像:
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest d2c94e258dcb 13 months ago 13.3kB
概括
本文讨论了容器化的基础知识、在容器中运行应用程序的优势,以及用于管理容器实例的基本 Docker 生命周期命令。我们还讨论了容器镜像如何作为一种通用的软件部署包,真正实现一次构建,随处运行。
由于我们在本地运行 Docker,我们可以确信,在本地环境中运行的相同容器镜像可以部署到生产环境中并放心运行。
各位,抓紧了!这列货运列车即将出发,而且速度越来越快。为了确保内容公开透明,并注明出处(同时,也为大家提供一些精彩的探索方向),我在下面整理了一份参考资料列表:
- Docker 已启动并运行
- Docker 容器 - 使用 Kubernetes、Flannel、Cockpit 和 Atomic 进行构建和部署
- Docker Cookbook - 构建分布式应用程序的解决方案和示例
- 面向开发者的 Docker
- Docker 实战
- Docker 实践
- 专业版 Docker
把它当作你探索 Docker 世界的藏宝图吧!
文章来源:https://dev.to/kalkwst/running-our-first-docker-image-4gpm