如何在DC操作系统上构建高度可扩展的物联网平台


我最近一直在为DC操作系统开发新的Percona-Server-MongoDB服务,作为这项工作的一部分,我决定尝试构建一个使用Percona-Server-MongoDB作为持久存储后端的物联网架构。MongoDB是物联网架构中持久存储的流行选择,原因有很多,包括高可伸缩性、对复杂查询的支持,以及它不需要严格模式的事实,因此您可以在本机JSON中将文档推送到它,每个字段有不同的类型。如果你想运行这个博客中使用的代码,你可以在GitHub

让我们先看一下我们要部署的平台的整体架构:

在顶部,我们有许多使用MQTT协议的数据生成器设备。MQTT是一个为传感器设计的标准化协议,基于发布/订阅模型。它最初是由我的前IBM英国同事安迪·斯坦福-克拉克在IBM开发的,旨在运行在处理开销非常低的设备上。因为我们的演示环境中没有真正的设备,所以我使用了Eclipse Pahopython库来模拟具有单个传感器的设备,以可配置的采样速率给出随机输出。这可能是一个工业传感器,例如给出温度输出。

MQTT生产者需要连接到一个代理来发布他们的数据,在我们的例子中,我们将使用开源的Eclipse Mosquitto经纪人。

为了将我们的采集层代理拉进我们的MongoDB聚合层,我们需要某种网关,它将订阅我们代理上的相关主题,获取新消息,重新格式化它们,然后将它们写入后端的MongoDB副本集中。我再次将它实现为一个基于Python的服务,使用了paho-mqtt和pymongo图书馆。

让我们更详细地看看这些元素,从仿真设备开始。这方面的代码非常简单:

#!/usr/bin/env python
"""
MQTT generator
"""

import random
import time
import uuid
import json
from argparse import ArgumentParser
import paho.mqtt.client as mqtt
parser = ArgumentParser()
parser.add_argument("-b", "--broker", dest="broker_address",
required=True, help="MQTT broker address")
parser.add_argument("-p", "--port", dest="broker_port", default=1883, help="MQTT broker port")
parser.add_argument("-r", "--rate", dest="sample_rate", default=5, help="Sample rate")
parser.add_argument("-q", "--qos", dest="qos", default=0, help="MQTT QOS")
args = parser.parse_args()

uuid = str(uuid.uuid4())
topic = "device/%s" % uuid
mqttc = mqtt.Client(uuid, False)
mqttc.connect(args.broker_address, args.broker_port)
while True:
rand = random.randint(20,30)
msg = {
'uuid': uuid,
'value': rand
}
mqttc.publish(topic, payload=json.dumps(msg), qos=args.qos)
time.sleep(float(args.sample_rate))
mqttc.loop_forever()


正如我们所见,我们的设备需要一些不同的参数。首先,我们需要查看要连接的MQTT代理的地址和端口、我们想要生成值的速率以及当前未使用的QOS设置。QOS(服务质量)是MQTT标准的一部分,您可以在Mosquitto docs

在启动时,设备生成一个唯一的uuid,连接到一个主题为device/$uuid的MQTT代理,然后在每个采样周期它生成一个范围内的随机数,然后发布一个包含它的UUID和值的小JSON有效负载。

为了在DC/操作系统上运行,我们可以使用pyinstaller来捆绑我们的依赖关系,正如我在上一篇博客文章中所描述的那样Python microservices,但在这种情况下,我们将构建一个Docker映像并将其发布到存储库中。我不打算介绍在您的环境中安装Docker互联网上有很多很好的操作方法可以帮助解决这个问题,所以我们假设你已经在本地有了一个工作环境。

首先,我们想要生成一个符合我们应用需求的文本文件。我在一个 virtualenv,从pip安装我的依赖项,因此我可以通过运行:

$ pip freeze > requirements.txt
$ cat requirements.txt
paho-mqtt==1.3.1


我们的下一步是创建一个Dockerfile,我们将使用它来构建一个Docker映像。这在我们的案例中非常简单:

$ cat Dockerfile
FROM python:2

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY device.py .

CMD [ "/bin/bash" ]


在我们的例子中,我们将使用标准的Python 2 Docker映像作为基础,我们将在我们的requirements.txt中复制,使用pip安装我们的依赖项,然后在中复制我们的实际设备代码。因为我们将使用通用容器运行时来运行它,所以我们实际上不需要在这里定义CMD,但是出于测试的目的,我已经将它设置为运行一个外壳。

现在我们有了我们的码头工人档案,我们可以继续建立我们的码头工人形象:

$ docker build -t device .
Sending build context to Docker daemon 12.78MB
Step 1/6 : FROM python:2
2: Pulling from library/python
0bd44ff9c2cf: Pull complete
047670ddbd2a: Pull complete
ea7d5dc89438: Pull complete
ae7ad5906a75: Pull complete
0f2ddfdfc7d1: Pull complete
85124268af27: Pull complete
1be236abd831: Pull complete
fe14cb9cb76d: Pull complete
cb05686b397d: Pull complete
Digest: sha256:c45600ff303d92e999ec8bd036678676e32232054bc930398da092f876c5e356
Status: Downloaded newer image for python:2
---> 0fcc7acd124b
Step 2/6 : WORKDIR /usr/src/app
Removing intermediate container ea5359354513
---> a382209b69ea
Step 3/6 : COPY requirements.txt ./
---> b994369a0a58
Step 4/6 : RUN pip install --no-cache-dir -r requirements.txt
---> Running in 1e60a96f7e7a
Collecting paho-mqtt==1.3.1 (from -r requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/2a/5f/cf14b8f9f8ed1891cda893a2a7d1d6fa23de2a9fb4832f05cef02b79d01f/paho-mqtt-1.3.1.tar.gz (80kB)
Installing collected packages: paho-mqtt
Running setup.py install for paho-mqtt: started
Running setup.py install for paho-mqtt: finished with status 'done'
Successfully installed paho-mqtt-1.3.1
Removing intermediate container 1e60a96f7e7a
---> 3340f783442b
Step 5/6 : COPY device.py .
---> 72a88b68e43c
Step 6/6 : CMD [ "/bin/bash" ]
---> Running in a128ffb330fc
Removing intermediate container a128ffb330fc
---> dad1849c3966
Successfully built dad1849c3966
Successfully tagged device:latest
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
device latest dad1849c3966 About an hour ago 903MB
python 2 0fcc7acd124b 9 days ago 902MB


现在我们在本地有了我们的Docker映像,我们想发布它,以便以后可以在DC/操作系统中使用Marathon部署它。首先,我们需要确保我们登录了Dockerhub:

$ docker login --username=mattjarvis
Password:
Login Succeeded


现在,我们需要标记我们的本地图像:

$ docker tag dad1849c3966 mattjarvis/device:latest


一旦图像被标记,我们就可以推送至我们的存储库:

$ docker push mattjarvis/device
The push refers to repository [docker.io/mattjarvis/device]
d52256b6a396: Pushed
6b19db956ca6: Pushed
cd0c68b16296: Pushed
812812e9c2f5: Pushed
05331f1f8e6f: Layer already exists
d8077e47eb94: Layer already exists
5c0800b60a4e: Layer already exists
ebc569cb707f: Layer already exists
9df2ff4714f2: Layer already exists
c30dae2762bd: Layer already exists
43701cc70351: Layer already exists
e14378b596fb: Layer already exists
a2e66f6c6f5f: Layer already exists
latest: digest: sha256:8a1407f64dd0eff63484f8560b605021fa952af00552fec6c8efb913d5bba076 size: 3053


在我们开始实际部署之前,让我们看看我们的第二段代码,它是我们的MQTT代理和我们的MongoDB集群之间的连接器。同样,这是用Python写的:

$ cat mongogw.py
#!/usr/bin/env python
"""
MQTT to MongoDB Gateway
"""

import json
from argparse import ArgumentParser
import paho.mqtt.client as mqtt
import pymongo
import datetime
import os
parser = ArgumentParser()
parser.add_argument("-b", "--broker", dest="broker_address",
required=True, help="MQTT broker address")
parser.add_argument("-p", "--port", dest="broker_port", default=1883, help="MQTT broker port")
parser.add_argument("-m", "--mongouri", dest="mongo_uri", required=True, help="MongoDB URI")
parser.add_argument("-u", "--mongouser", dest="mongo_user", required=True, help="MongoDB user")
parser.add_argument("-w", "--mongopwd", dest="mongo_password", required=True, help="MongoDB password")
args = parser.parse_args()
def on_message(client, userdata, message):
json_data = json.loads(message.payload)
post_data = {
'date': datetime.datetime.utcnow(),
'deviceUID': json_data['uuid'],
'value': json_data['value'],
'gatewayID': os.environ['MESOS_TASK_ID']
}
result = devices.insert_one(post_data)
# MongoDB connection
mongo_client = pymongo.MongoClient(args.mongo_uri,
username=args.mongo_user,
password=args.mongo_password,
authSource='mongogw',
authMechanism='SCRAM-SHA-1')
db = mongo_client.mongogw
devices = db.devices
# MQTT connection
mqttc = mqtt.Client("mongogw", False)
mqttc.on_message=on_message
mqttc.connect(args.broker_address, args.broker_port)
mqttc.subscribe("device/#", qos=0)
mqttc.loop_forever()


在这种情况下,我们连接到MQTT代理和MongoDB,然后我们订阅所有以device/然后在每次读取消息时使用回调函数来重新格式化消息,并在将其写入MongoDB之前添加一些我们自己的附加元数据。我们在这个阶段添加了时间戳,这反映了在现实世界的例子中,我们的传感器设备可能没有足够的处理能力来提供时钟功能的可能性。我们还添加了网关进程的Mesos任务标识,这将允许我们在扩大采集层时跟踪消息通过的网关。

我们将以与设备示例完全相同的方式构建它,使用Dockerfile并将Docker映像推送到存储库。该文件的摘要与设备非常相似:

$ cat Dockerfile
FROM python:2

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install –no-cache-dir -r requirements.txt
COPY mongogw.py .

CMD [ “/bin/bash” ]


现在我们有了自己的定制代码,让我们开始部署吧。我们首先要部署的是我们的MongoDB副本集。我写了一篇关于Percona-Server-MongoDB服务的更长的文章,在文章中您可以找到关于该服务的高级功能集的更多信息,但是为了演示的目的,我们将只使用默认值,这将为我们提供一个三节点副本集。首先,我们需要创建一个设置默认用户、密码和密钥的配置文件。应该是这样的:

$ cat demo.json
{
"mongodb-credentials": {
"backupUser": "backup",
"backupPassword": "backupuserpassword",
"userAdminUser": "useradmin",
"userAdminPassword": "useradminpassword",
"clusterAdminUser": "clusteradmin",
"clusterAdminPassword": "clusteradminpassword",
"clusterMonitorUser": "clustermonitor",
"clusterMonitorPassword": "monitoruserpassword",
"key": "8cNNTVP6GqEOKzhUVDVryxIt04K6kDbXygamH4upPGAO59gzXVQAgX9NwxwqDvpt 094zMkkRWDLzuCgbg3Aj8EFVEM0/W1Nz+XUSTHEn4HiNzCVG4TTHFP6P1PEPswG6 tQMP6bnRXL7uGWmdGhbAxOV/+p6AfNs67MTvfCeH0EaPCgPPXhJft9D0nZ0SPOm9 VvfxG3djnHClIlclkchoIwc1Kw21loyXwuOjX4RkywVDdmFXjKC+l9yxfyt/9Gyh YE0OlS7ozWLiH8zy0MyzBdK+rc0fsxb2/Kb/8/2diC3O3gdVxjneQxaf66+FHVNW mV9/IHDptBHosdWkv0GboW8ZnTXnk0lyY0Jw85JFuTeFBzqPlB37jR0NU/HFm5QT Ld62woaGIWCTuXGb81QHaglPZUBIhEq/b3tahJBmLc+LKd0FUShoupTtPc2FjxbH xD8dZ+L9Uv7NPtSe+o3sTD60Pnsw1wbOrNDrrC+wpwoMy2GbQjXk/d+SRK/CXfuk Z676GKQDivpinhdF58l4OEi+WEN633yuNtNAQDgz+aOVZKN4oLoyR22B1nrea1qW wzZjRw7kpVxcQKiyn+gDmAZZPbctiVqTNHPE5n9LrOcctuLZKpoQk97lvZTSCKfy d32mfx9szZZ/QCfF9Dt7+G5nJUAULigKnQYRi/i86ZTPHSzfun+ZIzYLCzJuZfyS 7E8DMsmv9wCPrPAF/8cOFMWW0o0Na7GZKCJ8U+AMm92R725h4g5ao6+kQPG7vOkY LR8MJzDOqcmAC0M9AwE5UXQl56V6qBNyREx/WGGYS1B5DOfZvVTJNDkoHVIL1upZ geSlACiXQ+M0Rkgo0h8BJUhGY9LTuc6S8qiMBEnhBClg4kA/u4FJ06nlmF3ZpIXT KsVSr9ee3mu0vSr6P52slvAAX+RL3y+JgSlz2kC8oVgCZZdKn7yq9e6yB3zHNMjX 8VIi/UgFmfqCiaAlUT0pt2ZzGuw1L9QUOuNAZfufSkK1ED4V"
}
}


用户密码必须至少包含10个字符,密钥长度必须至少为1024个字符。您可以在MacOS上生成适当的密钥,使用:

$ openssl rand -base64 756


现在我们有了选项json,我们可以用它来安装软件包:

$ dcos package install percona-server-mongodb --options=demo.json
By Deploying, you agree to the Terms and Conditions https://mesosphere.com/catalog-terms-conditions/#community-services
Default configuration requires 3 agent nodes each with: 1.0 CPU | 1024 MB MEM | 1 1000 MB Disk
Continue installing? [yes/no] yes
Installing Marathon app for package [percona-server-mongodb] version [0.4.0-3.6.6]
Installing CLI subcommand for package [percona-server-mongodb] version [0.4.0-3.6.6]
New command available: dcos percona-server-mongodb
The DC/OS Percona Server for MongoDB service is being installed.

Documentation: https://docs.mesosphere.com/service-docs/percona-server-mongodb/
Issues: https://jira.percona.com/secure/CreateIssue!default.jspa?pid=12402.


我们还想为将要使用的数据库配置一个用户。我们可以使用Percona-Server-MongoDB命令行界面扩展,从DC/操作系统命令行界面直接执行此操作。首先,我们需要创建一个JSON文件,定义我们想要创建的用户:

$ cat mongouser.json
{
"user": "mongogw",
"pwd": "123456",
"roles": [
{ "db": "mongogw", "role": "readWrite" }
]
}


现在,我们将它与数据库名称以及具有用户管理权限的管理员用户的凭据一起传递给命令行界面:

$ dcos percona-server-mongodb user add mongogw mongouser.json
{
"message": "Received cmd: start update-user with parameters: {MONGODB_CHANGE_USER_DB=mongogw, MONGODB_CHANGE_USER_DATA=eyJ1c2VycyI6W3sidXNlciI6Im1vbmdvZ3ciLCJwd2QiOiIxMjM0NTYiLCJyb2xlcyI6W3sicm9sZSI6InJlYWRXcml0ZSIsImRiIjoibW9uZ29ndyJ9XX1dfQ==}"
}


接下来,我们将部署我们的MQTT层。因为理论上我们可能有成千上万的设备,所以我们的采集层是可扩展的这一点很重要。我们将在MQTT端实现这一点,方法是使用DC/操作系统中的一个名为VIP的服务来对Mosquitto端点的多个实例进行负载平衡。虽然在现实世界中,面向互联网的例子,我们可能会使用马拉松-LB实例暴露在我们的集群之外。

虽然这将适用于面向设备的一侧,但是如果我们从网关层使用VIP,并且我们希望网关层也是可扩展的,那么我们可能会丢失数据,因为我们的网关可能正在从任何Mosquitto实例读取数据,并且MQTT层不是集群的。为了解决这个问题,我们将在中部署每个Mostitt实例及其自己的专用网关微服务,并让网关微服务通过本地主机连接连接到Mostitt,确保每个网关仅从单个Mostitt实例获取数据。因此,对于通过负载平衡器地址连接的设备,它可以连接到我们部署的任何Mosquitto实例,我们的存储层将获得数据。

下面是我们将用来部署吊舱的JSON:

{
"id": "/mqtt",
"containers": [
{
"name": "mosquitto",
"resources": {
"cpus": 0.1,
"mem": 64
},
"image": {
"id": "eclipse-mosquitto",
"kind": "DOCKER"
},
"endpoints": [
{
"name": "mqtt",
"containerPort": 1883,
"hostPort": 1883,
"protocol": [
"tcp"
],
"labels": {
"VIP_0": "/mqtt:1883"
}
}
]
},
{
"name": "mongogw",
"resources": {
"cpus": 0.1,
"mem": 64
},
"image": {
"id": "mattjarvis/mongogw",
"kind": "DOCKER"
},
"exec": {
"command": {
"shell": "./mongogw.py -b localhost -m mongo-rs-0-mongod.percona-server-mongodb.autoip.dcos.thisdcos.directory,mongo-rs-1-mongod.percona-server-mongodb.autoip.dcos.thisdcos.directory,mongo-rs-2-mongod.percona-server-mongodb.autoip.dcos.thisdcos.directory:27017 -u mongogw -n 123456"
}
}
}
],
"scaling": {
"instances": 1,
"kind": "fixed"
},
"networks": [
{
"name": "dcos",
"mode": "container"
}
],
"volumes": [],
"fetch": [],
"scheduling": {
"placement": {
"constraints": []
}
}
}


莫斯奇托提供了一个预先构建的码头工人图像,所以我们将把它用于我们的第一个集装箱。我们还需要为MQTT连接公开端口1883,所以我们将它映射到主机上的一个端口,并通过负载平衡VIP公开它。

第二个容器是我们的mongogw Python微服务,它将启动mongogw进程,通过本地主机连接到Mosquitto,并使用DC/操作系统为每个MongoDB实例分配的域名连接到MongoDB副本集。

为此,我们只需使用DC操作系统命令行界面将JSON传递给马拉松:

$ dcos marathon pod add mqttpod.json
Created deployment 19887892-f3e9-44b4-9dd3-22a5790196f3


现在我们的采集层已经启动并运行,我们可以启动我们的设备了。在这里,我们还需要JSON中的一些马拉松配置:

{
"id": "device",
"instances": 1,
"cpus": 0.1,
"mem": 16,
"cmd": "./device.py -b mqtt.marathon.l4lb.thisdcos.directory -r 2",
"container": {
"type": "MESOS",
"docker": {
"image": "mattjarvis/device",
"forcePullImage": true,
"privileged": false
}
},
"requirePorts": false
}


我们将使用通用容器运行时,提取相关的Docker图像,并运行带有一些参数的python脚本,将设备配置为连接到我们为Mosquitto pods分配的负载平衡VIP,并每隔2秒发布一次。正如我们所看到的,这方面的中央处理器和内存需求非常小,所以我们应该能够扩大规模并运行许多实例。

因此,让我们继续部署我们的第一台设备:

$ dcos marathon app add device.json
Created deployment 231be2c7-47c6-4f28-a7e0-40f4aae2f743


一旦我们的第一个设备启动并运行,我们就可以在MongoDB中进行检查,以确保我们的所有层都正常运行。首先,我们需要一个蒙古数据库副本集的任务标识:

$ dcos task
NAME HOST USER STATE ID MESOS ID REGION ZONE
admin-0-watchdog 10.0.2.229 root R admin-0-watchdog__a3ff9cc4-daeb-4f76-b730-aea8e2667417 3ba115f5-b4fe-43e9-a05a-0d9b0240fb51-S4 --- ---
device 10.0.3.192 root S device.769ef300-b75d-11e8-9d5d-fe0bc23c90b8 3ba115f5-b4fe-43e9-a05a-0d9b0240fb51-S3 --- ---
mongo-rs-0-mongod 10.0.0.44 root R mongo-rs-0-mongod__f0a27fca-138a-4f39-a0b2-4a1a0960c079 3ba115f5-b4fe-43e9-a05a-0d9b0240fb51-S6 --- ---
mongo-rs-1-mongod 10.0.3.152 root R mongo-rs-1-mongod__a039fb0f-6ca7-4706-974a-855542fa5e36 3ba115f5-b4fe-43e9-a05a-0d9b0240fb51-S2 --- ---
mongo-rs-2-mongod 10.0.0.26 root R mongo-rs-2-mongod__5c68c451-c11d-49bd-bf49-e99b8bcceb5c 3ba115f5-b4fe-43e9-a05a-0d9b0240fb51-S1 --- ---
mongogw 10.0.0.44 root R mqtt.instance-565e6b1f-b75d-11e8-9d5d-fe0bc23c90b8.mongogw 3ba115f5-b4fe-43e9-a05a-0d9b0240fb51-S6 --- ---
mosquitto 10.0.0.44 root R mqtt.instance-565e6b1f-b75d-11e8-9d5d-fe0bc23c90b8.mosquitto 3ba115f5-b4fe-43e9-a05a-0d9b0240fb51-S6 --- ---
percona-server-mongodb 10.0.0.26 root R percona-server-mongodb.cfbfcaae-b75b-11e8-9d5d-fe0bc23c90b8 3ba115f5-b4fe-43e9-a05a-0d9b0240fb51-S1 --- ---


一旦我们有了任务标识,我们就可以使用DC/操作系统命令行界面在该容器中获取一个外壳:

$ dcos task exec --tty --interactive mongo-rs-0-mongod__f0a27fca-138a-4f39-a0b2-4a1a0960c079 /bin/bash
root@ip-10-0-0-44:/mnt/mesos/sandbox#


然后,我们将使用蒙古外壳连接到蒙古数据库:

root@ip-10-0-0-44:/mnt/mesos/sandbox# mongo mongodb://mongogw:123456@mongo-rs-0-mongod.percona-server-mongodb.autoip.dcos.thisdcos.directory,mongo-rs-1-mongod.percona-server-mongodb.autoip.dcos.thisdcos.directory,mongo-rs-2-mongod.percona-server-mongodb.autoip.dcos.thisdcos.directory:27017/mongogw?replicaSet=rs


请注意,我们使用的是之前创建的用户,以及为每个蒙古数据库副本集自动分配的DC/操作系统域名系统条目。

现在,我们应该以一个MongoDB外壳提示结束,所以让我们切换到我们的数据库:

rs:PRIMARY> use mongogw;
switched to db mongogw


从这里,我们可以计算出我们的设备集合中现在有多少条目:

rs:PRIMARY> db.devices.count();
117


我们还可以查看插入的数据:

rs:PRIMARY> db.devices.findOne();
{
"_id" : ObjectId("5b9a6db71284f4000452fd31"),
"date" : ISODate("2018-09-13T14:01:27.529Z"),
"deviceUID" : "f5265ed9-a162-4c72-926d-f537b0ef356c",
"value" : 22,
"gatewayID" : "mqtt.instance-565e6b1f-b75d-11e8-9d5d-fe0bc23c90b8.mongogw"
}


从这个输出中,我们可以看到来自我们的网关进程的重新格式化的数据,它还添加了时间戳和DC/操作系统中网关进程的任务标识。

因此,我们现在可以看到为我们的单个设备采集的数据。让我们扩大设备的规模。在我们的device.json中,让我们将数字增加到3:

$ cat device.json
{
"id": "device",
"instances": 3,
"cpus": 0.1,
"mem": 16,
"cmd": "./device.py -b mqtt.marathon.l4lb.thisdcos.directory -r 2",
"container": {
"type": "MESOS",
"docker": {
"image": "mattjarvis/device",
"forcePullImage": true,
"privileged": false
}
},
"requirePorts": false
}


现在,我们将使用修改后的JSON来更新我们的应用程序:

$ dcos marathon app update device < device.json Created deployment 83c91f20-9944-4933-943b-90cee2711640


当我们查看DC操作系统用户界面时,我们可以看到我们现在有三个设备实例。我们现在还可以在蒙古数据库层接收的数据中看到不同的UUID:

rs:PRIMARY> db.devices.find().limit(5).sort({$natural:-1})
{ "_id" : ObjectId("5b9a6ef01284f4000452fdef"), "date" : ISODate("2018-09-13T14:06:40.698Z"), "deviceUID" : "919473a4-b332-4929-9b5e-c0a80f498222", "value" : 24, "gatewayID" : "mqtt.instance-565e6b1f-b75d-11e8-9d5d-fe0bc23c90b8.mongogw" }
{ "_id" : ObjectId("5b9a6ef01284f4000452fdee"), "date" : ISODate("2018-09-13T14:06:40.165Z"), "deviceUID" : "9474a1ee-c1c7-4f1d-a012-c6e4c883c7d3", "value" : 27, "gatewayID" : "mqtt.instance-565e6b1f-b75d-11e8-9d5d-fe0bc23c90b8.mongogw" }
{ "_id" : ObjectId("5b9a6eef1284f4000452fded"), "date" : ISODate("2018-09-13T14:06:39.882Z"), "deviceUID" : "f5265ed9-a162-4c72-926d-f537b0ef356c", "value" : 29, "gatewayID" : "mqtt.instance-565e6b1f-b75d-11e8-9d5d-fe0bc23c90b8.mongogw" }
{ "_id" : ObjectId("5b9a6eee1284f4000452fdec"), "date" : ISODate("2018-09-13T14:06:38.696Z"), "deviceUID" : "919473a4-b332-4929-9b5e-c0a80f498222", "value" : 25, "gatewayID" : "mqtt.instance-565e6b1f-b75d-11e8-9d5d-fe0bc23c90b8.mongogw" }
{ "_id" : ObjectId("5b9a6eee1284f4000452fdeb"), "date" : ISODate("2018-09-13T14:06:38.163Z"), "deviceUID" : "9474a1ee-c1c7-4f1d-a012-c6e4c883c7d3", "value" : 25, "gatewayID" : "mqtt.instance-565e6b1f-b75d-11e8-9d5d-fe0bc23c90b8.mongogw" }


正如我们所看到的,这些都使用同一个网关,因为我们只部署了一个。所以,让我们在那一层部署另一个吊舱。同样,我们修改JSON中的实例数量,然后更新pod配置:

$ dcos marathon pod update mqtt < mqttpod.json Created deployment 1fdc863b-9815-417e-87ac-858b56f8630f Since all of our devices are already attached to the first gateway through the load balanced VIP, let’s increase the device count to 5, and update as above. Once our new devices are up and running, because they are using the load balancer, we should see some of them connect to the new gateway. We can query MongoDB again and see we now have more than one gateway ID in our received data : rs:PRIMARY> db.devices.find().limit(5).sort({$natural:-1})
{ "_id" : ObjectId("5b9a6f981284f4000452fef9"), "date" : ISODate("2018-09-13T14:09:28.076Z"), "deviceUID" : "f5265ed9-a162-4c72-926d-f537b0ef356c", "value" : 26, "gatewayID" : "mqtt.instance-565e6b1f-b75d-11e8-9d5d-fe0bc23c90b8.mongogw" }
{ "_id" : ObjectId("5b9a6f971284f4000452fef8"), "date" : ISODate("2018-09-13T14:09:27.158Z"), "deviceUID" : "43e2785e-90b2-4cac-9e68-c3b72984f83c", "value" : 27, "gatewayID" : "mqtt.instance-565e6b1f-b75d-11e8-9d5d-fe0bc23c90b8.mongogw" }
{ "_id" : ObjectId("5b9a6f964931e30004900d25"), "date" : ISODate("2018-09-13T14:09:26.942Z"), "deviceUID" : "6b9d763b-699e-47eb-8541-704931dbb6e9", "value" : 26, "gatewayID" : "mqtt.instance-6f0de323-b75e-11e8-9d5d-fe0bc23c90b8.mongogw" }
{ "_id" : ObjectId("5b9a6f961284f4000452fef7"), "date" : ISODate("2018-09-13T14:09:26.882Z"), "deviceUID" : "919473a4-b332-4929-9b5e-c0a80f498222", "value" : 30, "gatewayID" : "mqtt.instance-565e6b1f-b75d-11e8-9d5d-fe0bc23c90b8.mongogw" }
{ "_id" : ObjectId("5b9a6f961284f4000452fef6"), "date" : ISODate("2018-09-13T14:09:26.363Z"), "deviceUID" : "9474a1ee-c1c7-4f1d-a012-c6e4c883c7d3", "value" : 26, "gatewayID" : "mqtt.instance-565e6b1f-b75d-11e8-9d5d-fe0bc23c90b8.mongogw" }


最后,我们还可以扩展MongoDB层,通过向副本集添加更多实例来水平扩展读取,或者通过改变实例的大小来垂直扩展读取。出于本演示的目的,让我们再向副本集中添加两个实例。我们可以在DC/操作系统用户界面中非常简单地做到这一点,方法是单击Percona-Server-MongoDB服务,然后单击编辑,并选择MongoDB选项卡:

将计数从3改为5。一旦我们更改了配置,单击查看并运行,然后运行服务,Percona-Server-MongoDB服务将向我们的副本集部署另外两个实例。

一旦部署完成,我们将在服务选项卡中看到另外两个mongod实例,Percona-Server-MongoDB服务本身没有中断。

因此,我们可以看到,在我们的物联网演示应用的每一层中,我们都有一个高度可扩展的体系结构,而DC操作系统使部署和管理变得极其容易。随着设备数量的增加,我们可以简单地在负载平衡器后面部署更多的Mostoitto/Gateway吊舱,如果我们需要在存储层增加容量或性能,我们可以使用Percona-Server-MongoDB服务非常轻松地扩展MongoDB层,以实现纵向或横向扩展。您可以在我们的中找到上面使用的所有代码,以及Dockerfiles和马拉松配置GitHub仓库。