使用Kubernetes模拟数以百计的物联网设备


【编者的话】本文主要介绍了如何使用Kubernetes模拟数以百计的物联网设备的详细过程,主要涉及的Kubernetes的特性有Statefulsets、Secrets、ConfigMap等,最后介绍了如何使用.Net Core读取设备数据以及调试过程。
01.png

02.png

为了测试我们的物联网系统,我们经常需要使用模拟设备(比如普通的传感器或者车载传感器等),以了解他们是如何管理负载,以及如何处理故障的。

每个模拟设备都被认为是一个独立的实体,该实体具有自己的一整套装置,比如电池电量、上传频率、服务中断、永久存储器等。

我之前在Azure中使用Webjobs完成了这一模拟过程,如果你只需要模拟一两个设备,它是能够完成我们的模拟工作的,但是一旦你超过了两个设备,Webjobs就搞不定了。

后来,我们发现利用Kubernetes的StatefulSets特性,这个过程会变得非常容易。有了这一特性,我们可以管理数百个模拟设备的凭证,并根据需要进行弹性伸缩。我们接下来要做的事情将是对Webjobs的一个沉重的打击。

部署过程:

从Git仓库地址下载IotHubCredentials.json文件,并执行以下kubectl命令:
PS C:\Users\vladi\Downloads> kubectl create secret generic sim-sensor-credentials --from-file=IoTHubCredentials.json
secret "sim-sensor-credentials" created
PS C:\Users\vladi\Downloads> kubectl create -f https://raw.githubusercontent.com/VladimirAkopyan/IoTSimulator/master/Kubernetes.yaml
configmap "sim-sensor-settings" created
service "simulated-sensors-service" created
statefulset "simulated-sensors" created

StatefulSets是什么?

在一个普通的ReplicaSet中,pod是临时的,未持久化数据的,并且具有随机的名称。如果它死掉了,另一个具有不同名称的不同的pod会取代它。

StatefulSet旨在管理将数据保存在磁盘上的有状态的应用程序。在StatefulSet中,pods被编号为0、1、2、3……,如果一个pod死掉了,将重新创建一个相同名称的pod。它们也可以与持久卷搭配,将数据保存到磁盘,但在本例中我们实际上并不需要使用磁盘。

详细步骤

每个应用程序容器模拟一个设备,每个pod都有编号,我们用编号来确定哪个pod模拟哪个设备。同时,我们为所有的pods提供所有凭证的清单——比如说其中的一百张清单。

pod会在清单列表中找到合适的凭证,使用凭证连接到物联网系统,并完成模拟工作。

下面是StatefulSet的一个yaml声明,将应用程序的名称作为环境变量提供给应用程序。如果你想要模拟更多的pods,只需要改变yaml文件中的副本数量。

只要证书列表足够大,并且群集具有足够的容量,随时都可以通过修改为一个更大的副本数量来模拟更多的设备,也就是说,模拟多少个完全独立的设备是没有限制的。
apiVersion: v1
kind: Service
metadata:
name: simulated-sensors-service
labels:
app: simulated-sensors-service
spec:
clusterIP: None
selector:
app: simulated-sensors-service
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: simulated-sensors
spec: 
replicas: 5
serviceName: "simulated-sensors-service"
selector:
  matchLabels:
  app: sim-sensor
template:
metadata:
  labels:
    app: sim-sensor
spec:
  containers:
    - name: test-container
      image: clumsypilot/iotsimulator
      env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name

03.png

实际上,我们使用podNames作为IoT集线器的设备ID——并且是单独设置

管理设置和凭证

有时候我们想要改变设置,比如通过提高每个设备的数据产出速率,来模拟高负载的情况。同时,我们希望能够更改设置而不触及信号源的变化。

当然了,我们也不希望在源代码中存储auth凭证,因此,接下来让我来解释一下如何在Kubernetes中正确执行这一操作。

我们正在使用.Net Core应用程序来模拟设备,但我尽可能地保持整个过程与具体的编程语言无关。

ConfgMap中的设置

应用程序将从环境变量中读取设置,但管理它们的最佳方式是通过ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: sim-sensor-settings
namespace: default
data:
APPINSIGHTS_INSTRUMENTATIONKEY: ####
ReadingsDelay: "30"
ImageDelay: "3600"

在data部分中,可以添加你希望管理的任何设置信息。为集群创建这个ConfigMap
kubectl create -f ConfigMap.yaml

StatefulSet的声明进行了如下修改:
apiVersion: v1
kind: Service
metadata:
name: simulated-sensors-service
labels:
app: simulated-sensors-service
spec:
clusterIP: None
selector:
app: simulated-sensors-service
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: simulated-sensors
spec: 
replicas: 5
serviceName: "simulated-sensors-service"
selector:
matchLabels:
  app: sim-sensor
template:
metadata:
  labels:
    app: sim-sensor
spec:
  containers:
    - name: test-container
      image: clumsypilot/iotsimulator
      envFrom:
        - configMapRef:
            name: sim-sensor-settings
      env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name

确认已正确地将设置信息附加到pod上:
04.png

通过Secrets管理凭证

Secrets擅长的是存储敏感数据,而且由于我们有大量的敏感数据,所以最好将其作为文件(IoTHubCredentials.json)提供。
{
"Credentials": {
    "simulated-sensors-0": "************",
    "simulated-sensors-1": "************",
    "simulated-sensors-2": "************",
    "simulated-sensors-3": "************",
    "simulated-sensors-4": "************"
}


如同CongfigMaps一样,Secrets也是键—值对的形式存在。在CongfigMaps中,我们分别指定每个键值对,然而在Secrets这种情况下,文件名是键,其内容是“值”。在一个secret中可以有多个文件。
kubectl create secret generic sim-sensor-credentials --from-file=./IoTHubCredentials.json

在本例中,实际上我们希望以文件的形式提供这些信息,因此我们将通过以卷的形式挂载secrets来实现这一功能。当这样做时,每个存储在secret中的key都表示一个文件。


路径:Linux系统中以/为起点,比如/secret/file.json,这样的路径是绝对路径,而secret/file.json是相对于当前应用程序目录而言的相对路径。
apiVersion: v1
kind: Service
metadata:
name: simulated-sensors-service
labels:
app: simulated-sensors-service
spec:
clusterIP: None
selector:
app: simulated-sensors-service
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: simulated-sensors
spec: 
replicas: 5
serviceName: "simulated-sensors-service"
selector:
matchLabels:
  app: sim-sensor
template:
metadata:
  labels:
    app: sim-sensor
spec:
    volumes:
    - name: secrets
      secret:
        secretName: sim-sensor-credentials      
    containers:
    - name: test-container
      image: clumsypilot/iotsimulator
      envFrom:
        - configMapRef:
            name: sim-sensor-settings
      volumeMounts:
      - name: secrets
        mountPath: /secrets
        readOnly: true          
      env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name

好了,现在可以在/secrets/IoTHubCredentials.json中读取文件内容了。

使用.Net core读取此数据

.net中读取这些信息,最简单的方法是使用nuget包:
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Abstractions
Microsoft.Extensions.Configuration.EnvironmentVariables
Microsoft.Extensions.Configuration.Json

ConfigutationBuilder使用配置文件内容以及环境变量中的数据创建字典——以帮助管理应用程序的配置。随后的每个.Add都会将数据添加到字典中,覆盖以前文件中的内容。我们正在提供一个文件appsettings.json来为应用程序提供一些默认值,在开发人员的计算机上构建应用程序并启用测试,从而不用考虑环境变量。
IConfiguration config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) /
.AddJsonFile("/secrets/IoTHubCredentials.json", optional: true) 
.AddEnvironmentVariables()
.Build();

只有在Kubernetes集群中运行应用时,secrets和环境变量才会生效。
Console.WriteLine($"Pod Name is: {config["POD_NAME"]}");
Console.WriteLine($"Application Insights Key: {config["APPINSIGHTS_INSTRUMENTATIONKEY"]}");
Console.WriteLine($"Image delay is: {config["ImageDelay"]}");
Console.WriteLine($"Readings delay is: {config["ReadingsDelay"]}");
string devicekeyPath = $"IoTHubCreds:{config["POD_NAME"]}";
Console.WriteLine($"IoT Credentials are: {config[devicekeyPath]}");

调试

我使用alpine容器进行构建,其体积小巧但不包含普通bash。可以使用以下命令执行:
kubectl exec -it simulated-sensors-4 /bin/sh

ConfigMaps使用kubectl edit configmaps/sim-sensor-setting非常容易对设置的信息进行更新,但是secrets要稍微困难一些——因为我们通常从文件中创建它。
kubectl create secret generic sim-sensor-credentials --from-file=./IoTHubCredentials.json --dry-run -o yaml | kubectl apply -f -


注意,更新SecretsConfigMaps将更新驻留在容器中的应用程序可见的值,但不会重新启动容器。

一个典型的应用程序会在启动时读取这些值,然后从不检查这些值是否已经改变。

一般来说,一种好的技术就是将这些信息视为不可变的对象。创建一个ConfigMap v1.1,并将deploymentstatefullset更新为新值。在这种情况下,正常的kubectl安全机制会支持你的这一操作。

原文链接:Simulating hundreds of IoT devices with Kubernetes (翻译:刘志超)

0 个评论

要回复文章请先登录注册