DockerOps:Docker 入门






4.23/5 (4投票s)
本文将为理解 Docker 引擎奠定概念基础。
- 下载 nodejs-dockerized-master - 235.3 KB
- GitHub:https://github.com/afzaal-ahmad-zeeshan/nodejs-dockerized
- Docker 镜像:https://hub.docker.com/r/afzaalahmadzeeshan/express-nodejs/
引言
如今,几乎每个人至少都听说过 Docker 和容器化这个词。无论他们是构建面向容器服务的专家,还是了解容器如何工作,这都不重要。但他们可能在文档、电子书、杂志、文章或视频中看到过关于容器化应用程序、编排工具或 Docker、Kubernetes 等技术的介绍。关键在于,人们对这项技术有所了解,并且认识到它与虚拟机的演进相似。而这一切现在已经有点过时了。我希望在这门 Docker 101 课程中,能够建立 Docker 设置的核心基础,讲解它的工作原理,如何快速获取 Docker,以及如何进行尝试。
本文将作为系列文章的一部分,我将尽我所能,通过本教程系列的这些文章,涵盖 Docker 和应用程序容器化的各个方面。除了关于 Docker 本身的一些主题,我们还将探讨以下话题:
- 容器中的数据持久化
- Docker 中的服务
- 容器编排
- Kubernetes 和 Docker
这些是我可以讨论的关于 Docker 的一些话题,既不会让初学者感到迷失,也不会让专家觉得我夸夸其谈。
什么是 Docker?
对于那些仍然不知道 Docker 是什么以及为什么应该关心它的人,这里有一个简短的定义。当人们谈论 Docker 时,他们会想到一个容器化工具,有些人甚至认为它是一个进程编排器。这些说法都是正确的。Docker 是,也可以被看作是:
- 包管理器
- 编排器
- 负载均衡器
我甚至还没有提到互联网上那些听起来很炫的说法,比如 Docker 是同名公司开发的产品,或者 Docker 是一个开源的容器化管理工具,或者像 Docker 是通往云的入口。从各自的角度看,这些说法都是正确的,但它们仍然不能涵盖 Docker 所能做的一切。这就是我写这篇文章的原因,旨在展示 Docker 的功能和特性,而不是仅仅谈论它。让我们从 Docker 的要求和先决条件开始。
平台和要求
Docker 本身可以安装在 Linux、Windows 或 macOS 上,但我将在这里使用 Linux 平台。命令和 Docker 的行为在 Windows 和 macOS 上将是相似的。但我向您保证,如果您使用 Linux 发行版,特别是 Ubuntu 16.04 或更新版本进行设置,那将是一次精彩的旅程。我在此设置中使用了 Ubuntu 18.04。
第二点需要注意的是,我正在使用 Snapcraft 的 Snap。之所以选择这样做,是因为 Snap 的安装、升级和管理非常容易。它们预装了服务、程序和实用工具,可以帮助您轻松管理该实用程序。不用担心,在安装和设置部分,我将根据需要解释并提供其他安装和要求的指导,但主要方法是这个。
安装和配置
如前所述,我们将安装 Docker 的 Snap,对于那些想学习编排的高级主题的人,还会安装 Kubernetes。Snap 在 Linux 发行版上可用,即 2018 年或 2017 年发布的较新版本。Docker 网站上清楚地说明了安装过程,但无论如何,我仍将尝试在几个平台上解释该方法。
Windows
在 Windows 上,在执行 Docker 安装程序之前,您需要确保以下几点:
- 硬件虚拟化
- 已安装并启用了 Hyper-V
这也意味着您无法在 Windows 家庭版上尝试这些功能。您需要拥有 Windows 专业版或更高版本。但这不会阻止您在虚拟化环境中尝试 Docker 安装,在那里您可以轻松设置 Linux 发行版并开始下一部分。
但是,如果您已经拥有 Windows 专业版,只需前往 Docker 下载并安装适用于您机器的 Docker。
Linux (Ubuntu 18.04)
虽然任何支持 Snap 的 Linux 发行版都可以,但我将在本文以及后续文章中使用 Ubuntu 发行版。为此,只需确保您的系统中有 Snap。要确保您已安装 Snap 系统,请确保您运行的是 Ubuntu 16.04 或更新版本,这就是为什么我**建议**使用**Ubuntu 18.04**。Snap 守护程序默认安装在这些操作系统上,如果以下命令没有响应,
$ snap version
则表示您的操作系统没有安装 Snap 系统。要安装一个,您可以运行以下命令:
$ sudo apt-get install snapd
这将安装 snappy 环境的守护程序,之后您可以验证 snappy 是否可用。您也可以遵循 Snap 网站提供的文档,对于 Ubuntu,或选择您选择的操作系统和发行版。
完成之后,直接从 Snap 商店安装 Docker:
$ sudo snap install docker
这需要一些时间,并将为您安装和设置 Docker 引擎。现在您可以继续安装和设置一些其他东西,例如用于试用的 hello world Docker 镜像。但我更愿意在这里提示一些关于 Docker 的内容,然后我们再进入这个 Docker 系列的第二集。现在,要继续进行,前提是您已下载并安装了 Docker 引擎,请继续到下一步,开始探索 Docker。
非 Snap 方法
Docker 也为 Linux 环境发布了相关文件,您可以下载并安装这些 tarball,或使用 apt、yum 等仓库管理器。
以下是一些您可能想要关注的重要链接:
由于 Docker 是开源的,您可以随时自行编译源代码。Packt 的这个 cookbook 片段可以告诉您需要执行哪些步骤来构建 Docker 引擎的源代码。
探索 Docker
在我结束本章之前,让我们从存储、网络和进程信息的角度快速了解一下 Docker 引擎。这将为我们后面将要学习的概念奠定基础。因此,首先,我们将使用我自己的 Node.js 应用程序,原因如下:
- 这是我自己的开发项目,我可以轻松地解释我做了什么,以及在哪里做的。
- 它是一个开源项目,需要在 GitHub 上获得社区的贡献和协作。
- 它轻量级,并展示了一些可以在开发 Dockerized 包时,或者应该 employed 的最佳实践。
- Docker 包仅仅是 4 行代码,底层项目是一个用 Node.js 编写的完整的 Web 应用程序项目,它暴露了多种设计模式和架构设计,特别是面向 Serverless 的方法。
- 该项目还附带其他可配置的、可立即执行的脚本,例如 Docker Compose 文件。
您可以在 GitHub 上探索该项目(链接在上面的下载部分提供),亲自查看其结构。但现在,废话不多说,让我们创建我们的第一个 Docker 容器,并可视化这个过程。在我们应用程序中,我们通过以下代码暴露 Web 应用:
// Inside the serverconfig.js
let port = process.env.PORT || process.env.PORT_AZURE || process.env.PORT_AWS || 5000;
// Inside the app.js
app.listen(serverConfigurations.serverPort, () => {
let serverStatus = `Server listening on localhost:${serverConfigurations.serverPort}.`;
logger.logEvent("server start", serverStatus);
console.log(serverStatus);
});
从 Docker 的角度来看,这段代码并不重要,但我想让您看到的是,这将执行并提供一个在容器中运行的 Web 服务器。在一个非常正常的环境中,这将只是执行函数并启动 Node.js 应用程序的事件循环,这意味着您的进程将启动,它将监听该主机名以及我们分配的端口的网络流量。在这种情况下,您的命令输出
$ npm start
将如下所示:
> express-nodejs@1.3.0 start /home/afzaal/Projects/Git/Nodejs-Dockerized/nodejs-dockerized
> node ./src/app.js
Cannot start Application Insights; either pass the value to this app,
or use the App Insights default environment variable.
[Event] server start: Server listening on localhost:5000..
Server listening on localhost:5000.
从一个天真的角度来看,这一切看起来都很好,但现在我们遇到了一个**问题**。如果您仔细查看(忽略 Application Insights 行),您会发现它写着:“**Server listening on localhost:5000.**”,这让我们清楚地意识到**端口 5000 已经绑定**到该进程。这将导致我们的应用程序的**可伸缩性出现问题**,而该应用程序有权成为一项服务,进而要求我们
- 要么在不同且可配置的端口上运行应用程序
- 在我写的应用程序中,这完全可行,您可以传递应用程序要监听的端口,但*这不是推荐的方法*。因为它还需要您管理来自该机器外部的流量转发,这就引出了第二点。
- 要么在负载均衡器后面运行应用程序,并让负载均衡器将流量转发到每个进程的 5000 端口,您为该服务启动的每个进程。
这种方法的另一个主要,也是最重要的一个问题是,如果我们的进程失败或终止,我们的环境将无法恢复该进程。我们可以通过编写自己的管理程序或利用另一个 Web 服务器程序来克服这个问题,但即使在那里,我们也需要管理和维护如何指定我们的应用程序正在运行。
这些就是 Docker 大显身手的地方,不仅是 Docker,还有其他编排器和进程管理器,如 Kubernetes、DC/OS Marathon 等。但由于我们正在讨论 Docker 的探索,我们将在后面的章节中深入探讨这些功能。在本章中,我们想探索如何在机器上部署应用程序的这种行为是如何实现的。为了理解我们是如何做到的,我们将执行我们之前作为问题提出的两个操作,并将所有这一切都交给 Docker。我们如何做到这一点呢?我们创建一个新的 Docker 镜像,并将我们的应用程序打包在其中。在 Docker 中,打包是通过 Dockerfile 完成的,该文件包含启动**进程**所需的命令序列。Dockerfile 需要以下信息才能启动您的进程:
- 依赖项
- 源文件
- 入口点或命令
遵循这个模式,我们知道我们的应用程序依赖于 Node.js 运行时,并且文件在同一个文件夹中,我们可以使用 `npm start` 命令启动项目。我们的 Dockerfile 将是:
FROM node:10-alpine
COPY . .
RUN [ "npm", "update" ]
CMD [ "npm", "start" ]
就这些!
我们不需要再写一个字。我已经在我的另一篇文章这里详细讨论了这些命令。而且我觉得没有必要再多说。让我们构建镜像并运行它,以验证一切是如何为我们处理的。
# docker build -t codeproject/dockerops:gettingstarted .
此命令将构建我们的第一个容器镜像,并提供具有该名称的容器镜像。
Sending build context to Docker daemon 1.527MB
Step 1/4 : FROM node:10-alpine
---> 7ca2f9cb5536
Step 2/4 : COPY . .
---> 9ac07aa147b7
Step 3/4 : RUN [ "npm", "update" ]
---> Running in ba65eff2e6d9
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN express-nodejs@1.3.1 No repository field.
+ uuid@3.3.2
+ body-parser@1.18.3
+ pug@2.0.3
+ express@4.16.4
+ applicationinsights@1.0.6
added 120 packages from 179 contributors and audited 261 packages in 30.756s
found 0 vulnerabilities
Removing intermediate container ba65eff2e6d9
---> 558e5026cb19
Step 4/4 : CMD [ "npm", "start" ]
---> Running in 807b13ed3a29
Removing intermediate container 807b13ed3a29
---> 46dc26469806
Successfully built 46dc26469806
Successfully tagged codeproject/dockerops:gettingstarted
如您所见,在此过程中,Docker 会注入镜像内部所需的一切。所有必需的组件都会被下载、修补、任何可执行文件都会被运行,最后构建一个镜像。构建过程与其他任何构建过程非常相似,可以将其与 .NET Core 构建过程进行比较,
$ dotnet restore
# Assuming the .NET Core project is in the same directory, we execute
$ dotnet build
# If everything goes fine, we do
$ dotnet run
同样,Docker 上面所做的只是构建了镜像。现在我们的项目已准备好运行。我们将镜像命名为 `codeproject/dockerops:gettingstarted`。这更容易记住,并且我们将在创建容器的过程中使用镜像的标签。这样,我们就可以轻松地管理其他操作,例如检查和删除系统中的容器。让我们继续创建容器,并对其进行检查。
# docker run -d --name gettingstarted codeproject/dockerops:gettingstarted
这只需要几秒钟就能为您创建容器!请注意 `-d` 标志,它将使我们的终端保持空闲,并在**分离模式**下运行容器。我们可以查看日志以了解进程内部发生了什么,这些日志将在文章的后面部分显示,请继续阅读……在上一篇文章中,我们讨论了如何选择不暴露端口,在本篇文章中,我们将看到容器如何始终监听 Docker 引擎映射给它的 IP 地址的网络流量。还记得我们将 5000 指定为进程端口吗?让我们检查容器并找出它的 IP 地址。
# Notice how gettingstarted helps us in passing the container reference.
# docker inspect gettingstarted
这会输出大量信息,而我们只关心(目前仅)容器信息,例如状态、网络以及主机名或 IP 地址信息。如下所示:
[
{
"Id": "c3f799ed9a74f1cb23c1046bceeee9144aad1122f70f4a39860722b625c5ef5b",
"Created": "2018-10-17T00:44:09.340443736Z",
"Path": "npm",
"Args": [
"start"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 16146,
"ExitCode": 0,
"Error": "",
"StartedAt": "2018-10-17T00:44:10.767427825Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:46dc2646980611d74bd95d24fbee78f6b09a56d090660adf0bd0bc452228f82d",
...
"Name": "/gettingstarted",
"RestartCount": 0,
"Driver": "aufs",
"Platform": "linux",
...
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
...
"Config": {
"Hostname": "c3f799ed9a74",
"Domainname": "",
...
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "2b6ace2bb890dcd59370798285d3c917f40e3f23330298f8a11dff1fbe27b627",
"EndpointID": "12c5e6c571af574e88c796e9804bd571523fc046aaaaa2d39a842580b391e745",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
...
所有标有“...”的行都用于覆盖和截断当前阶段的额外信息。从上面的文本中,您可以提取一些关于容器的信息,而一些信息则留给 Docker 服务的不同部分。请记住,Docker 引擎既可用于独立容器模式,也可用于 Swarm 模式。此值中的大多数设置都是为 Swarm 模式提供的,在该模式下,Docker 将管理机器、服务或堆栈。但是,我们感兴趣的是提供 `IPAddress` 值的区域。这就是托管 Node.js 应用程序的容器的 IP 地址。回想一下我们的端口是 5000,组合并访问 URL,我们得到:
这就是我们如何从 Docker 访问服务。这和您可能已经看腻了的页面是相同的,但是的,这是从 Docker 引擎暴露的服务。如果我们查看容器的日志,我们会得到以下信息:
# docker logs gettingstarted
> express-nodejs@1.3.1 start /
> node ./src/app.js
Cannot start Application Insights; either pass the value to this app,
or use the App Insights default environment variable.
[Event] server start: Server listening on localhost:5000..
Server listening on localhost:5000.
[Request] GET: /.
[Request] GET: /about.
[Request] GET: /.
[Request] GET: /contact.
[Request] GET: /.
[Request] GET: /.
我只是在容器中浏览了一会儿网站,如上所示,这是容器的日志。应用程序的执行方式没有任何区别。同样,运行时,或者进程使用的 CPU、RAM 或网络资源的使用方式也没有区别。区别在于当我们从不同的角度看待进程时——不是作为一个单独的玩家,而是作为集群中的一个工人。
有什么不同?您说。
回想一下我们之前提到的问题,1. 容错性,2. 可伸缩性。这两个问题都得到了解决。Docker 在其中发挥着至关重要的作用,它既是服务的健康管理器,也是一个良好且生产级别的负载均衡器。这让我们有机会将 Docker 不仅仅用作应用程序的包管理器,还可以用作应用程序的良好负载均衡器。虽然我必须承认,像 Kubernetes 这样的其他负载均衡器和编排器在处理可伸缩性方面要好得多,但 Docker 对于应用程序和服务的快速部署仍然是一个不错的选择。
**剧透警告**,虽然这是下一集的主要讨论话题,但 Docker Compose 概念在处理容器的可伸缩性和容错性方面非常有用。在同一个GitHub 仓库中可以找到一个非常简单、最小的 Docker Compose 文件示例。
version: '3'
services:
nodejsapp:
build: .
image: afzaalahmadzeeshan/express-nodejs:latest
deploy:
replicas: 3
resources:
limits:
cpus: ".1"
memory: 100M
restart_policy:
condition: on-failure
ports:
- "12345:80"
networks:
- appnetwork
networks:
appnetwork:
从这个部署脚本的设置可以看出,我们有以下配置:
restart_policy:
condition: on-failure
其次是这个:
replicas: 3
这些配置告诉 Docker 引擎(创建并;非按需)将服务扩展到 3 个实例,并确保在容器出现问题时引擎会重新创建容器。我不会在本文中深入探讨这个脚本,而是在下一篇文章中,讨论服务,我们将探索使用 Docker Compose 而不是普通 Docker CLI 的好处,然后我们还将关注 Docker Stack 和 Docker Services。Docker 在可伸缩性方面存在一个很大的问题,但是……下一篇文章将讨论这个问题。:-)
现在,我们将把未来的几集专门用于 Docker 开发和管理的特定领域。
- DockerOps:服务及其可伸缩性
- DockerOps:存储和网络
- DockerOps:编排和可扩展性
- DockerOps:容器化的最佳实践
我希望您能加入我的旅程,我将探索 Docker 引擎,并展示一些在开发面向 Docker 引擎进行容器化的应用程序时应采用的最佳实践。
继续前进...
在本文中,我们仅仅探索了 Docker 允许我们做什么。我们甚至还没有触及 Docker 及其强大功能和特性的表面。我们将在下一篇文章开始深入挖掘。使用 Docker 的主要好处是我们可以拥有自己独立的应用程序和进程运行环境,并且我们能够控制它们如何增长以及如何进行规模扩展。
现在,您可以删除已创建的资源,以释放机器上的一些硬件资源供进一步使用。或者您可以将它们保留原样,但我们仍将在后续文章中遵循重新创建过程。要删除资源,请运行:
# docker stop gettingstarted && docker rm gettingstarted
这将停止容器然后将其删除,以便您可以再次创建它,如果您希望继续阅读本文。现在,在我们继续下一集之前,这里是您在继续前进之前需要了解的内容:
- Docker 镜像 afzaalahmadzeeshan/express-nodejs (在几篇文章中用于快速部署)
- Docker inspect 参考 (每篇文章都用于提取信息)
- Docker volumes 用于存储 (用于存储章节)
- Docker compose 参考 (在未来的文章中将大量使用)