如何在Ubuntu16.04上使用Rancher部署Node.js和MongoDB应用程序
介绍
Rancher 是一个开源、自托管、完整的平台,可以在生产环境中运行和轻松管理容器。 作为 Docker 镜像本身,Rancher 服务器可以在任何可以使用 Docker 的 Linux 主机上运行。
使 Rancher 成为有吸引力的解决方案的一些关键特性是:
- 跨主机网络:添加到 Rancher 的所有服务器都是链接的,允许容器之间的安全通信。
- 负载平衡:包括负载平衡服务,用于在容器之间甚至跨多个云之间分配工作负载。
- 服务发现:Rancher 包含一个内部 DNS 系统,可以通过名称识别容器和服务,以便在网络上的其他服务中使用它们。
- 基础设施管理:使用 Rancher,您可以添加、监控和管理来自任何云提供商的计算资源。
- 编排引擎:Rancher是唯一支持Cattle、Swarm、Kubernetes、Mesos等最流行的容器编排框架的容器管理平台。 因此,如果您的基础架构已经使用其中一个框架,那么您将能够轻松使用 Rancher。
- 开源:Rancher 是免费、开放、透明的。 您可以绝对控制您的基础架构。
在本指南中,您将构建一个 Rancher 集群来部署负载均衡的 Node.js 应用程序,并支持使用 MongoDB 进行数据存储。
在本教程结束时,您将拥有一个简单的 Node.js 应用程序的四个负载平衡实例和一个 MongoDB 服务器,该服务器具有一个用于持久存储的单独数据容器。
先决条件
- 一个安装了 Rancher 的 1GB Ubuntu 16.04 Droplet。 我们将使用 Rancher 创建六个额外的 Droplet,每个具有 1GB 的 RAM。 按照 如何在 Ubuntu 16.04 上使用 Rancher 和 Docker 机器管理您的多节点部署的步骤 1 和 2 来使用 Rancher 设置您的初始 Droplet。
- 一个使用 MongoDB 进行数据存储的 Node.js 应用程序。 本教程提供了一个使用 Hapi.js 库的简单示例,您可以在尚未准备好自己的应用程序时使用。 您可以在 这个 Github 存储库 中找到这个示例应用程序,我们将在步骤 1 中详细探索它。
- Git 安装在您的本地计算机上,因此您可以克隆示例应用程序。 如果您需要满足此先决条件,请遵循 官方 Git 安装文档 。
- Docker 安装在您的本地计算机上,因此您可以构建我们将部署的应用程序映像。 您可以关注官方文档。
- Docker Hub 上的一个帐户,它是 Docker 镜像的免费和公共注册表。 这是我们将托管应用程序代码的地方,因此我们可以使用 Rancher 将其部署到多个主机。 您将需要您的 Docker Hub 用户名来完成本教程中的步骤。
- 具有读取和写入访问权限的 DigitalOcean 访问令牌,您可以通过访问应用程序和 API 页。 复制此令牌,因为您需要在 Rancher 中输入它以创建其他主机。
您还应该具备 Docker 概念 的基本知识,例如容器、图像和 Dockerfile。 有关使用 Docker 的更多信息,请参阅 如何在 Ubuntu 16.04 上安装和使用 Docker。
第 1 步——探索 Node.js 应用程序
在本教程中,我们将使用一个基于 Hapi.js 框架的简单 Node.js 应用程序,它接收消息、记录它并列出之前提交的所有消息。 让我们探索应用程序如何工作以及它如何接收配置值,以便我们可以在创建映像时使用 Docker 设置这些值。
您将在本地开发机器上准备应用程序和 Docker 映像,而不是在服务器上。
使用以下命令将示例应用程序克隆到本地计算机:
git clone https://github.com/do-community/hapi-example
然后导航到项目目录:
cd hapi-example
让我们看一下应用程序的主文件,server.js
。 它包含 MongoDB 连接和初始化服务器的代码。 在本地文本编辑器中打开此文件,您将看到以下内容:
服务器.js
const Hapi = require('hapi'); const mongojs = require('mongojs'); // Loads environment variables // Used only in development require('dotenv').config({silent: true}); const server = new Hapi.Server(); server.connection({ port: process.env.PORT || 3000 }); // Connect with the database server.app.db = mongojs(process.env.MONGO_HOST + '/api'); // Add the routes server.register(require('./routes'), (err) => { if (err) { console.error('Failed to load plugin:', err); } // Start the server server.start((err) => { if (err) { throw err; } console.log('Server running at:', server.info.uri); }); });
在本教程中,路由的代码被封装为 Hapi.js 插件以节省篇幅,但如果您好奇,可以查看文件 routes.js
。
server.js
文件的关键部分如下:
服务器.js
require('dotenv').config({silent: true});
这使用 dotenv
Node.js 包从 .env
文件加载我们的环境变量。 如果您对它的工作原理感兴趣,可以查看其 Github 存储库 中的 dotenv
包的文档。 我们将变量保存在这个文件中只是为了开发过程; 这比在终端中手动编写变量更容易。 在生产环境中,我们将通过 Rancher 从 Docker 获取变量。
接下来,我们使用名为 PORT
的环境变量设置服务器的端口,并使用 3000
的回退值,以防变量未定义:
服务器.js
server.connection({ port: process.env.PORT || 3000 });
端口 3000
是 Node.js 应用程序的常用约定。 如有必要,可以更改此值; 唯一的要求是它应该 高于 1023 并低于 65535。
最后,在加载路由并启动服务器之前,我们使用名为 MONGO_HOST
的环境变量连接到 MongoDB 服务器:
服务器.js
server.app.db = mongojs(process.env.MONGO_HOST + '/api');
一旦我们创建了 MongoDB 容器,这个环境值将通过 Rancher 使用 MongoDB 服务器的主机名来定义。 值 api
是我们要连接的数据库的名称,如果它不存在会自动设置。
现在您已经了解了应用程序正在寻找什么以及我们如何配置其端口和数据库连接的背景知识,让我们将 Docker 带入图片中。
第 2 步 — 构建 Docker 映像
Rancher 使用 Docker 镜像将应用程序部署到服务器,所以让我们为我们的应用程序创建一个 Docker 镜像。 为了为我们的应用程序构建 Docker 镜像,我们需要一个名为 Dockerfile
的文件,其中包含 Docker 在构建镜像时将遵循的一系列步骤。 此文件已包含在您克隆的应用程序存储库中。 让我们看看它的内容以了解它是如何工作的。 在文本编辑器中打开它,您将看到以下代码:
Dockerfile
FROM node:6 MAINTAINER James Kolce <contact@jameskolce.com> RUN mkdir -p /usr/api COPY . /usr/api WORKDIR /usr/api RUN npm install --production ENV PORT 3000 EXPOSE $PORT CMD ["npm", "start"]
让我们详细看看每个步骤。 首先,我们看到这一行:
Dockerfile
FROM node:6
这一行声明我们的镜像构建在来自 Docker Hub 的 官方 Node.js 镜像 之上,我们选择 Node.js 版本 6,因为我们的应用程序使用了一些 ES6 特性,仅在该版本中可用版本或更高版本。 推荐的做法是选择特定版本,而不是在生产中仅使用 latest,这样您就可以避免任何可能破坏您的应用程序的更改。
在那一行之后,我们设置了我们的工作目录:
Dockerfile
RUN mkdir -p /usr/api COPY . /usr/api WORKDIR /usr/api
首先,我们运行 mkdir
命令来创建一个名为 /usr/api
的新目录,这是我们的应用程序所在的位置。 -p
标志意味着 mkdir
将根据需要创建中间目录。 然后我们将图像的内容复制到该目录。 然后我们将这个新目录设置为我们的工作目录,以便后续命令将从该目录运行。
下一行运行 npm
命令并为我们的应用程序安装生产依赖项。
Dockerfile
RUN npm install --production
接下来,我们看到这两行:
Dockerfile
ENV PORT 3000 EXPOSE $PORT
第一行定义了一个名为 PORT
的环境变量,我们的应用程序将使用它作为其监听端口。 万一这个变量没有定义,我们将 3000
设置为默认值。 然后我们公开该端口,以便我们可以从容器外部访问它。 使用环境变量可以更轻松地更改它,而无需重写我们的应用程序代码。 请记住,我们的应用程序旨在使用这些环境变量。
Dockerfile 中的最后一步通过发出 npm start
命令运行我们的 Node.js 服务器:
Dockerfile
CMD ["npm", "start"]
要从此文件创建我们应用程序的 Docker 映像,请确保您位于终端的 hapi-example
文件夹中并执行以下命令:
docker build -t your_dockerhub_username/hapi-example .
此命令使用我们的 Dockerfile
创建 Docker 映像。 注意命令末尾的点。 这指定了当前文件夹中的 Dockerfile
的路径。 -t
标志为我们的图像设置标签,我们将使用 your_dockerhub_username/hapi-example
作为标签,这是我们应用于图像的标签,因此我们可以使用它从图片。 我们使用 Docker Hub 用户名作为前缀,因为我们准备在测试后发布此映像,并且 Docker 映像的本地标记必须与 Docker Hub 上的存储库名称匹配。
注意:如果您在运行此命令时收到消息Cannot connect to the Docker daemon. Is the docker daemon running on this host?
,请确保Docker应用程序正在运行并且Docker已启动。 然后再次运行该命令。
现在让我们测试我们刚刚构建的图像,这样我们就可以确定一切都按预期工作。 正如您之前看到的,我们的应用程序依赖于 MongoDB,因此让我们创建一个 MongoDB 容器,我们的应用程序可以使用它来存储其数据。 运行以下命令,基于官方 MongoDB Docker 镜像创建并启动一个 MongoDB 容器:
docker run --name testdb -p 27017:27017 -d mongo:3
我们使用 --name
选项为容器分配一个临时名称; 当我们完成测试应用程序时,我们将使用该名称来停止服务器。 我们还将主机端口 27017
绑定到容器公开的端口,以便我们可以使用本地 Web 浏览器测试 MongoDB 是否正在运行。 最后,我们指定要使用的图像。 最好使用与开发应用程序相同的 MongoDB 版本,以确保一切按预期工作,因此在这种情况下,我们指定版本 3
。
执行该命令后,在浏览器中访问 http://localhost:27017
,您将看到消息:It looks like you are trying to access MongoDB over HTTP on the native driver port
,表示 MongoDB 正在运行。
现在运行您的应用程序容器并通过运行以下命令将其链接到 MongoDB 容器:
docker run --name hapi-app -p 3000:3000 -d -e MONGO_HOST=testdb --link testdb your_dockerhub_username/hapi-example
这个命令和我们用来启动 MongoDB 容器的命令类似,但是这次我们使用我们的应用程序镜像 (your_dockerhub_username/hapi-example
) 并将我们主机的端口 3000
映射到容器暴露的端口. 这与我们在创建 Dockerfile
时使用的端口相同。 此外,我们添加了一个名为 MONGO_HOST
的环境变量,它指定了我们的 MongoDB 容器的名称,我们的应用程序将使用该名称连接到数据库服务器。 --link testdb
参数允许我们使用数据库容器的名称作为应用程序容器内的主机。
运行命令后,通过在浏览器中访问 http://localhost:3000
来测试应用程序。 它应该显示一个空白页面,没有任何错误。
注意:当您访问 http://localhost:3000
时,您可能还会在 Firefox 上看到一个空数组 ([ ]
) 或 JSON 视图。 这些结果中的任何一个都可以。
现在我们已经证明 Docker 镜像在本地工作,让我们停止并删除我们的本地容器。 请记住,删除容器与删除映像不同。 我们的镜像将保持原样,以便我们稍后重新创建容器,或者将镜像推送到 Rancher,这是我们清理本地环境后要做的事情。
首先,使用您之前定义的名称停止数据库容器:
docker stop testdb
现在容器已停止,您可以将其从您的机器中删除,因为您不再需要它:
docker rm testdb
对您的应用程序容器重复相同的步骤。 首先停止它,然后将其删除。
docker stop hapi-app && docker rm hapi-app
现在让我们发布工作镜像,以便我们可以在 Rancher 中使用它。
第 3 步 — 将图像上传到 Docker Hub
要使用 Rancher 部署容器,我们需要访问 Docker 注册表,我们可以在其中创建一个存储库来存储我们的 Docker 映像。 我们将使用 Docker Hub,它是 Docker 的官方注册表。 Docker Hub 可免费用于公共存储库。
使用您的用户名和密码登录 Docker Hub。 登录后,单击屏幕右侧的 Create Repository 按钮。 填写以下字段:
- Name (required):你的仓库的名字,在这个例子中是
hapi-example
。 - Description:一个简短的描述,以便将来快速识别您的图像。
- 完整描述:您可以在此处添加降价文档作为图像的参考,但由于我们的应用程序非常简单,您可以将此字段留空。
- 可见性:您可以将图像设置为只有您可以访问的私有图像,或者每个人都可以使用您的图像的公共图像。 对于本教程,创建一个公共存储库。
注意:如果将存储库设置为私有,则必须在 Rancher UI 的 Infrastructure -> Registries 页面中添加 Docker Hub 凭据。
填写完所有必填字段后,单击创建按钮。 该过程完成后,您将被重定向到新的存储库站点。
为了上传您的 Docker 镜像,您必须通过 docker
命令登录到 Docker Hub。 回到您的终端,执行以下命令:
docker login
系统将提示您输入用户名和密码。 与大多数 CLI 工具一样,您在键入密码时不会看到密码。
登录后,使用以下命令将图像上传到 Docker Hub,该命令会将所有文件上传到存储库:
docker push your_dockerhub_username/hapi-example
推送图像可能需要几分钟时间,具体取决于您当地的互联网连接。 成功发布镜像后,我们可以使用 Rancher 设置主机。
第 4 步 — 在 Rancher 中创建和标记主机服务器
让我们使用 Rancher 创建部署服务所需的所有主机。 我们需要两个用于 Node.js 应用程序,一个用于 MongoDB 服务器,一个用于负载均衡器。 我们将使用 DigitalOcean 的 API 在 Rancher UI 中完成所有这些工作。
通过访问 http://your_rancher_ip_address
在浏览器中访问 Rancher 界面,然后按照以下步骤创建我们需要的四个主机:
- 转到 Infrastructure > Hosts,然后单击页面顶部的 Add Host 按钮。
- 选择 DigitalOcean 作为主机提供者。
- 将您生成的 DigitalOcean Application Token 粘贴到 Access Token 字段中,然后单击 Next: Configure Droplet。
- 指定名称。 输入
host
,会自动生成从host1
到host4
的名称。 - 将 Quantity 滑块移动到 4 主机。
- 对于 Image,使用默认值 Ubuntu 16.04.1 x64。
- 对于 Size,使用默认值 1gb RAM, 30gb Disk, 1 vCPU。
- 您可以将 SSH 用户 字段保留为 root。
- 单击 Create 按钮并等待几分钟,服务器已创建并添加到 Rancher。
一旦 Rancher 完成创建所有主机,我们将为每个主机添加一个标签以对其类型进行分类,以便我们可以组织我们将放置每个组件的位置。 标记主机还可以让我们根据服务器类型扩展我们的服务器。 例如,如果我们的应用程序需求过多,我们可以增加该类型服务器的数量,Rancher 会自动为我们部署合适的 Docker 容器。 我们要创建的标签是:loadbalancer
、application
和 database
。
让我们创建第一个标签,loadbalancer
。
- 转到 Infrastructure > Hosts 并选择第一个主机
host1
。 - 单击选项按钮(页面顶部带有三个垂直点的图标)并选择编辑选项。
- 点击+添加标签按钮,在Key输入单词
type
,然后在Value输入loadbalancer
] 输入。 - 单击保存按钮。
注意:您的主机可能显示为host1.localdomain
; 这是正常行为,可能会根据您的环境而改变。
接下来,标记应用程序主机。 对接下来的两个主机重复前面的过程,但这次在 Value 输入中使用 application
。
对于最后一个主机,再次重复该过程,但在 Value 输入中使用 database
。
现在所有四个主机都应该有标签,所以让我们设置服务。 我们将从数据库开始。
第 5 步 — 部署 MongoDB 服务器
我们将使用 Docker Hub 上的官方 MongoDB Docker 镜像来部署我们的数据库服务器。 MongoDB 容器还将有一个 sidekick 容器来存储我们所有的数据。 两个容器都将部署在标记为 database
的主机上。
为此,请在 Rancher 用户界面中执行以下步骤:
- 选择Stacks菜单,选择User选项,然后点击Define a Service按钮。
- 在 Add Service 页面中,确保 Scale 滑块设置为 Run 1 container。
- 对于服务的名称,使用
MongoDB
。 - 对于图像,输入
mongo:3
。 - 单击顶部的 Add Sidekick Container 按钮。
- 将此新容器命名为
Data
。 该容器将用作存储 MongoDB 数据的卷。 - 由于我们将只将此容器用于数据,因此请使用
busybox
图像。 - 在下面的 Command 选项卡中,将 Autorestart 选项切换为 Never (Start Once),因为我们将使用此容器仅用于存储。
- 切换到 Volumes 选项卡并通过单击 Add Volume 按钮添加新卷。 在出现的文本字段中输入
/data/db
。 这是 MongoDB 存储数据的默认路径。 - 切换到Scheduling选项卡,点击Add Schedule Rule按钮,输入如下参数:
The Host must have a host label of type = database
。 使用下拉菜单帮助您创建此规则。 - 单击 MongoDB 服务选项卡,然后向下滚动到 Command 选项卡,并确保 Autorestart 选项设置为 Always。
- 切换到 Volumes 选项卡并在 Volumes From 选项中选择 Data。
- 切换到 Scheduling 选项卡并使用以下参数添加新的调度规则:
The Host must have a host label of type = database
- 最后,点击底部的 Create 按钮,等待几分钟服务激活。
现在让我们配置应用程序服务。
第 6 步 — 部署 Node.js 应用程序
我们将使用类似的方法来部署我们之前准备的 Node.js 应用程序。 我们存储在 Docker Hub 上的镜像将部署在标有 application
的主机上,并将链接到 MongoDB 服务以存储和访问数据。 因此,请在 Rancher 用户界面中执行以下步骤:
- 选择Stacks菜单,选择User选项,然后点击Default堆栈中的Add Service按钮。
- 在 Scale 部分中,选择选项 始终在每个主机上运行此容器的一个实例 。
- 我们将为此服务使用的名称是
NodeJS
。 - 对于图像,我们将使用我们部署到 Docker Hub 的图像。 输入
your_dockerhub_username/hapi-example
。 - 点击Service Links按钮,选择Destination Service并选择MongoDB。 然后选择As name,输入
db
,这样我们的NodeJS
服务就可以使用这个名字访问MongoDB服务了。 - 在页面底部的Command选项卡中,点击Add Environment Variable按钮,添加一个名为
MONGO_HOST
的变量,其值为db
,它映射到我们在上一步中使用的目标服务名称。 请记住,我们的应用程序依赖此环境变量来定位数据库服务器。 - 切换到 Scheduling 选项卡,单击 Add Scheduling Rule 按钮并使用下拉菜单构建一个显示为
The Host must have a host label of type = application
的规则。 - 最后点击【X16X】创建【X26X】,等待Rancher设置服务。
很快,您会看到新的 NodeJS
服务启动了两个容器。 选择 Infrastructure 菜单,单击 Hosts,您会看到标有 application
的两个主机现在都在运行这个新服务。 由于不止一个,让我们设置一个负载平衡器,以便我们可以有效地使用这两个主机。
第 7 步 — 部署负载均衡器
我们的负载均衡器将链接到我们的 NodeJS
服务,以平衡应用程序主机上所有容器之间的工作负载。
- 要创建负载均衡器,请选择 Stacks 菜单并选择 User 选项。 这次单击 Add Service 按钮旁边的箭头,然后从下拉列表中选择 Add Load Balancer。
- 对于 名称 ,输入
LoadBalancer
。 - 在 Port Rules 部分,将 Request Host Port(第一个 Port 字段)设置为
80
,并将 Target Port(第二个一)到3000
这是我们的NodeJS
容器暴露的端口。 - 在 Target Service 选项中选择 NodeJS,这是我们最近创建的服务。
- 在页面底部的 Scheduling 选项卡中,单击 Add Scheduling Rule 按钮并创建一个显示为
The Host must have a host label of type = loadbalancer
的规则。 - 最后,点击Create,等待 Rancher 激活服务。
每次创建服务时,我们都会使用创建的标签来确定服务的部署方式。 这使得将来管理其他主机变得容易。 现在让我们确保一切正常。
第 8 步 — 测试应用程序
为了测试我们的应用程序,我们需要获取负载均衡器主机的地址。 选择 LoadBalancer 服务,您将在 Ports 选项卡中看到 IP 地址。
要测试我们的应用程序是否正常工作,请在终端中执行以下命令:
curl your_load_balancer_ip
此命令向服务器发送 GET 请求。 您将看到一个包含空数组 ([]
) 的响应,因为我们的数据库是空的。
执行以下命令将消息添加到数据库并确保应用程序可以保存数据:
curl -i -X POST -H "Content-Type:application/json" your_load_balancer_ip -d '{"message":"This is a test"}'
此命令使用 JSON 对象向服务器发送 POST 请求,该对象包含 message
键,值为 This is a test。 发送请求后,您应该会收到作为响应发送的相同消息,以及来自 MongoDB 的 _id
。 这意味着与 MongoDB 服务器的连接正在工作并且应用程序保存了您的数据。
此应用程序只接受 message
键,任何其他名称都将被丢弃。
现在,要仔细检查应用程序是否正常运行,请再次执行第一个命令,您应该会收到您在上一步中添加的消息。
curl your_load_balancer_ip
输出将如下所示:
HTTP/1.1 200 OK content-type: application/json; charset=utf-8 cache-control: no-cache content-length: 61 Date: Wed, 05 Jan 2017 20:07:02 GMT Connection: keep-alive {"message":"This is a test","_id":"e64d85579aee7d1000b075a2"}
Warning:此示例应用程序不安全; 任何知道地址和 API 的人都可以向系统添加消息。 完成本教程后,您可能希望通过删除 Rancher 中的服务来禁用此应用程序。
至此,您现在已经配置了两个应用程序服务器、一个数据库和一个负载均衡器,并且可以使用了。 让我们看看如何扩展我们的服务以处理更多流量。
第 9 步 — 扩展 Node.js 服务器
当您的应用程序开始收到大量需求并且您的服务器无法处理负载时,您可以增加 Node.js 服务器的数量,并且负载将在应用程序主机的容器之间自动分配。 请按照以下步骤扩展您的应用程序:
- 转到 Infrastructure > Hosts 页面,然后单击 Add Host 按钮。
- 在同名字段中添加您的 DigitalOcean Access Token。
- 使用
host5
作为第一个新主机的名称,因为我们创建的最后一个主机是host4
。 因为我们要创建两个新主机,Rancher 会自动将下一个主机命名为host6
。 - 选择您想要的数量; 在这种情况下,我们将添加 2 更多主机。
- 对于 Image,使用默认值 Ubuntu 16.04.1 x64。
- 对于 Size,使用默认值 1gb RAM, 30gb Disk, 1 vCPU。
- 点击添加标签按钮,在Key输入中输入
type
,然后在Value输入中输入application
。 - 单击 Create 按钮并等待新主机被激活并添加到 Rancher。
新主机上线后,由于被标记为应用程序主机,NodeJS
应用程序的新实例将自动配置和部署,负载均衡器将工作负载分布在四个容器的四个主机之间。
结论
在本教程中,您学习了如何准备、部署和扩展功能性 Node.js 应用程序,并支持使用 MongoDB 进行数据存储。 如您所见,使用 Rancher 及其 GUI,该过程非常直观,并且可以轻松扩展完整的应用程序。 并且由于 Rancher 的调度功能,当您的应用程序大获成功时,您将能够轻松处理负载。