基于DC/OS打造高可扩展性的IoT物联网平台
在本篇文章中,我们将分享如何在DC/OS上使用
Percona-Server-MongoDB服务构建IoT架构
在为Mesosphere DC/OS平台集成Percona-Server-MongoDB服务时,笔者决定尝试以此服务作为持久化存储后端来构建一套IoT架构。
许多IoT架构都采用MongoDB作为持久化存储方案,MongoDB之所以那么流行有如下几点原因:
高可扩展性
支持复杂查询
不强制schema约束(即使某个字段包含不同类型的值,也可以按原生JSON格式直接导入MongoDB)
* 注:本篇文章的所有代码都可以在 https://github.com/dcos/demos 里找到。
首先,我们来看一下平台的整体架构:
上图顶端显示的是一组使用MQTT协议的设备,采集并生成数据。MQTT协议基于发布/订阅模式,是专为传感器设计的一种标准化协议。该协议是笔者在IBM时期一个英国同事Andy Stanford-Clark设计的,使用在处理能力很弱的低功耗设备上。由于我们的演示环境中并没有真实设备,笔者使用了Eclipse开源的Python库Paho (https://www.eclipse.org/paho/clients/python/docs/ )模拟了一个输出随机值的、采样率可配的单传感器设备。本例中假设它是一个工业用传感器,输出的值是温度。
使用MQTT协议的采集设备(生产者)需要连接到一个broker,来发布它们的数据。本次分享中,我们采用EclipseMosquitto( https://mosquitto.org/ )作为broker方案。
我们需要通过某种形式的网关,将感知层brokers提供的数据拉取到MongoDB的聚合层。通过向broker订阅相关主题(topic),持续获取消息报文,转换格式后写入后端MongoDB数据集。
我们利用paho-mqtt和the pymongo (https://api.mongodb.com/python/current/ )两个库,给出了Python版本的参考实现。
让我们从模拟设备开始,了解一下该方案的实施细节。代码非常简单:
#!/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 broker的地址和端口号;其次,希望模拟数据以什么频率生成(采样率);此外,还有个QoS设定,暂时没有用到。QoS (Quality of Service 服务质量)也是MQTT标准的一部分,可以参阅the Mosquitto的文档( https://mosquitto.org/man/mqtt-7.html )了解更多细节。
启动阶段,(IoT采集)设备会生成一个唯一的UUID编号,以device/$uuid格式的主题名连接到MQTT broker,接下来每个采样周期,设备发送一个很小的JSON载荷到broker,其中封装了UUID和模拟传感器生成的随机值。
为了在DC/OS上跑起服务,一种方案是使用pyinstaller打包所有依赖,可以参考笔者先前写的关于Python微服务的博客文章(https://mesosphere.com/blog/native-python-microservices-mesos/ )。这次我们可以试试用Docker,构建镜像发布到镜像仓库,最后部署到平台。在此不详述如何安装Docker环境,网上的参考有很多。我们假设您已经在本地建好了一个可运行的Docker环境。
首先要生成一个文本文件,描述我们的应用包含的所有需求。由于我在虚拟环境中(https://virtualenv.pypa.io/en/stable/ )已经通过pip安装了所有依赖,所以文本生成只需要一行命令:
$ pip freeze > requirements.txt
$ cat requirements.txt
paho-mqtt==1.3.1
接着,创建一个的Dockerfile用于构建镜像,这非常简单:
$ 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" ]
以Python2作为Docker基础镜像,将requirements.txt拷贝到工作目录下,使用pip按装依赖包,再加上运行设备使用的Python代码。由于我们最终将采用通用容器运行时(Universal Container Runtime)跑服务,其实并不需要定义CMD,不过为了测试,我们暂且把CMD设成shell。
有了Dockerfile,可以编译一个自定义镜像:
$ 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镜像,需要发布到DockerHub上,方便随后使用DC/OS上的Marathon来部署该镜像。
先确认一下是否已经登陆到DockerHub:
$ docker login --username=mattjarvis
Password:
Login Succeeded
确认登陆成功,给本地镜像打个标签(tag)推送到镜像库:
$ 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
在着手实施真正的部署之前,来看另一段Python代码mongogw.py,用于建立MQTT broker和MongoDB集群连接:
#!/usr/bin/env python
"""
MQTT to MongoDB Gateway
"""
import json
from argparse import ArgumentParser
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 = {
'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采集设备的broker和MongoDB,然后订阅所有以device/前缀的主题,使用回调函数读取消息报文,转换格式并附加自定义metadata后,写入MongoDB。这个步骤中,我们给写入后台的记录另加一个时间戳,这也是为了模拟真实环境中很可能存在的情况:传感器设备很可能没有足够的处理能力来实现时钟功能。同时,我们也添加了Mesos任务ID,以便在感知层(传感器阵列)扩充的时候,我们可以跟踪究竟是哪个网关实例处理了报文消息。
这一部分和在采集设备上的操作一样,定义Dockerfile构造镜像然后推送到镜像仓库。
连同Dockerfile定义也和应用到采集设备上的那套非常接近:
$ 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 replica集。我写了很长一篇关于Percona-Server-MongoDB服务的博文(发布后会附上链接),里面详述了如何利用该服务的很多高级特性。但是我们现在谈到的demo里,全部采用缺省设置就可以了,我们能轻松建起一个3副本的数据集。可以参考下述JSON配置默认用户名、密码、密钥:
$ 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个字符,生成的key不能少于1024个字符。在MacOS系统中可由下述命令生成key:
$ openssl rand -base64 756
我们用这个options.json配置来安装percona-server-mongod服务包:
$ 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 CLI扩展,我们还可以直接通过DC/OS的CLI直接给MongoDB数据库配一个用户。当然,这步操作需要事先在JSON文件里定义要创建的用户:
$ cat mongouser.json
{
"user": "mongogw",
"pwd": "123456",
"roles": [
{ "db": "mongogw", "role": "readWrite" }
]
}
JSON配置中需要附上具有管理员权限的登陆凭证,使用CLI指定要配置的数据库名称,操作如下:
$ dcos percona-server-mongodb user add mongogw mongouser.json
{
"message": "Received cmd: start update-user with parameters: "
}
下一步我们来部署MQTT层。理论上传感器设备可能多达成千上万,所以感知层必须做到可扩展。鉴于此,我们在DC/OS通过创建一个带别名的虚拟IP地址(VIP),实现多个Mosquitto端点的负载均衡接入。当然在真实的互联网环境下,我们通常采用集群外部暴露Marathon-LB实例的方式来实现。
上述方案做到了和设备的对接。然而,使用了网关层提供的虚拟IP地址接入,如果同时要求网关层支持可伸缩,会带来新的问题。
做采集的MQTT层并不是作为一个集群而是单独接入的,不方便统计管理发布数据实际的消费情况;同时接入后端MongoDB的网关可以从任意一个Mosquitto实例读取数据,
并不会感知到消息生产侧的情况,在网关层伸缩的时候很可能会丢失MQTT层生产的数据。
为解决上述问题,我们设计在POD里部署运行Mosquitto实例的同时,附带一个专属的网关微服务( https://docs.mesosphere.com/1.11/deploying-services/pods/ ),网关通过本地连接(localhost)访问Mosquitto,确保每个网关只从一个Mosquitto实例获取数据。
由此,确保通过负载均衡器IP接入的设备,确定能够访问到部署的某个Mosquitto实例,最终存储层都能获得传感器数据。
下面给出我们部署运行POD的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": {
}
}
}
],
"scaling": {
"instances": 1,
"kind": "fixed"
},
"networks": [
{
"name": "dcos",
"mode": "container"
}
],
"volumes": [],
"fetch": [],
"scheduling": {
"placement": {
"constraints": []
}
}
}
我们使用Mosquitto提供的预编译镜像建立我们的第一个容器,为MQTT连接开放1883端口。我们需要给它分配一个负载均衡器的虚拟IP地址,同时在宿主机上映射一个端口号。
第二个容器运行的是我们的mongogw网关Python微服务。mongogw进程启动后,通过本地连接(localhost)访问Mosquitto,通过DC/OS分配的DNS访问MongoDB实例。
把如下JSON通过DC/OS CLI传给Marathon,即可完成上述操作:
$ dcos marathon pod add mqttpod.json
Created deployment 19887892-f3e9-44b4-9dd3-22a5790196f3
我们的感知层已经启动了,可以打开采集设备了。我们还需要补充几个Marathon配置,JSON格式如下:
{
"id": "device",
"instances": 1,
"cpus": 0.1,
"mem": 16,
"container": {
"type": "MESOS",
"docker": {
"image": "mattjarvis/device",
"forcePullImage": true,
"privileged": false
}
},
"requirePorts": false
}
我们已经安装通用的容器运行时环境,下拉相关的Docker镜像。接下来需要配合参数运行Python脚本,配置设备间隔两秒连接一次分配了负载均衡VIP的Mosquitto POD发布数据。可以看到整个流程对CPU和内存的需求相当小,我们应该可以扩展出很多很多的实例。
开始部署我们的第一个(虚拟)采集设备:
$ dcos marathon app add device.json
Created deployment 231be2c7-47c6-4f28-a7e0-40f4aae2f743
一旦我们的设备启动运行了,可以通过检查MongoDB记录的方式,确认流程中每一层部署都正常运作。首先通过DC/OS命令行取得某个MongoDB副本的任务ID。有了任务ID,就能通过DC/OS命令行开个shell直接访问那个容器:
$ 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-
$ 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#
用mongo shell客户端访问MongoDB,使用先前创立的用户名,以及DC/OS自动分配给MongoDB的DNS地址别名:
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
现在我们应该能够看到MongoDB shell提示符了,随后切换到我们的数据库:
rs:PRIMARY> use mongogw;
switched to db mongogw
看看我们的设备集写入了几条记录:
117
当然也可以检视记录详情:
{
"_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/OS网关任务ID(标记是哪个网关实例处理了报文)
我们确认了数据可以从单一设备上采集到,我们修改devce.json,把设备数量扩充到3,更新一下应用:
$ cat device.json
{
"id": "device",
"instances": 3,
"cpus": 0.1,
"mem": 16,
"container": {
"type": "MESOS",
"docker": {
"image": "mattjarvis/device",
"forcePullImage": true,
"privileged": false
}
},
"requirePorts": false
}
$ dcos marathon app update device
我们可以从DC/OS UI里看到设备实例变成了3个,同时在MongoDB层也能看到出现了3个不同的设备UUID值:
{ "_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" }
显然,由于我们只部署了一个网关,所有设备都是通过同一个网关接入的。我们试试能不能在网关层扩充一个POD。我们先修改定义采集设备数量的JSON,然后更新POD的设置:
$ dcos marathon pod update mqtt
Created deployment 1fdc863b-9815-417e-87ac-858b56f8630f
当前所有设备都是通过负载均衡虚拟IP接入的,绑定到了第一个网关。如果把设备数量加到5,由于配置了负载均衡,启动运行新设备后,应该能观察到通过新网关接入的流量。可以通过查询MongoDB来验证,应该能够看到接收数据里出现了不止一个网关ID:
{ "_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层也可以任意扩展:通过往replica集里添加更多实例水平扩展;通过改变实例的规模实现垂直扩展。扩展通过DC/OS UI可以轻松实现。点选Percona-Server-MongoDB服务的”Edit“选项,切换到”MongoDB“选单,将计数由3改至5。修改完配置,点击“Review and Run”,Percona-Server-MongoDB服务会往副本集里另增两个实例。
(新增)部署完成后,我们还会在“服务”选项卡里看到另外两个mongod守候进程实例,修改MongoDB配置不会干扰到Percona-Server-MongoDB服务本身的运行。
综上所述,以上这个IoT应用的演示中,每一层都实现了高可扩展性,DC/OS极大地简化了部署和管理工作。随着传感器设备数量的增长,我们只需在负载均衡背后拉起更多的Mosquitto/网关POD实例即可。同时,借助Percona-Server-MongoDB服务,可以非常容易地实现MongoDB层的水平及垂直扩展。
上述例子中的所有代码、Dockerfile文件,以及Marathon配置,都可以在ttps://github.com/dcos/demos上找到。
原作者:Matt Jarvis,Mesosphere
译者:Isaac Lu, Mesosphere
领取专属 10元无门槛券
私享最新 技术干货