IoT Edge Computing & Kiến Trúc Fog

Chào mừng bạn đến với Phase 3 của Lộ Trình IoT Patterns & Strategies! Trong Phase 1, chúng ta đã khám phá kiến trúc phân tầng IoT và các loại thiết bị. Trong Phase 2, chúng ta đã đào sâu vào các giao thức — MQTT, CoAP, AMQP, và WebSocket. Giờ câu hỏi là: xử lý diễn ra ở đâu?
Gửi mọi sensor reading từ 100.000 thiết bị thẳng lên cloud nghe có vẻ đơn giản — cho đến khi bạn thấy hóa đơn bandwidth, con số latency, và chuyện gì xảy ra khi internet sập. Edge computing đảo ngược mô hình — xử lý dữ liệu gần nơi nó được sinh ra, chỉ gửi lên cloud những gì cloud thực sự cần.
Đến cuối bài viết này, bạn sẽ hiểu khi nào dùng edge, fog, hay cloud processing, cách triển khai các edge patterns phổ biến, và cách giữ hệ thống chạy khi mất kết nối.
Bạn Sẽ Học Được Gì
✅ Hiểu tại sao edge computing quan trọng cho IoT (latency, bandwidth, resilience)
✅ So sánh kiến trúc edge vs fog vs cloud với trade-offs rõ ràng
✅ Triển khai data filtering, aggregation, và store-and-forward tại edge
✅ Đánh giá các edge runtimes: AWS Greengrass, Azure IoT Edge, K3s, Docker
✅ Thiết kế fog computing layers cho hệ thống IoT phân cấp
✅ Xây dựng ứng dụng IoT offline-first sync lại khi có kết nối
✅ Chạy ML inference tại edge cho quyết định real-time
Tại Sao Edge Computing Quan Trọng Cho IoT
Vấn Đề Của Mô Hình Cloud-Only
Hãy xem một nhà máy có 10.000 sensor, mỗi cái gửi readings mỗi giây:
10.000 sensors × 1 reading/giây × 200 bytes = 2 MB/giây = 172 GB/ngàyGiờ nhân với chi phí mạng, chi phí cloud compute, và 50-200ms latency round-trip lên cloud. Một cảm biến rung phát hiện hỏng thiết bị cần phản hồi trong dưới 10ms — chứ không phải 200ms sau khi đi vòng qua us-east-1.
Ba Lý Do Nên Xử Lý Tại Edge
1. Latency — Một số quyết định không thể chờ cloud.
| Tình Huống | Latency Yêu Cầu | Cloud Khả Thi? |
|---|---|---|
| Cảnh báo bảo trì dự đoán | < 100ms | Có thể |
| Tránh va chạm cánh tay robot | < 10ms | Không |
| Điều chỉnh đèn giao thông thông minh | < 50ms | Không |
| Báo cáo giám sát môi trường | < 5 phút | Có |
| Tổng hợp tiêu thụ năng lượng tháng | Hàng giờ | Có |
2. Bandwidth — Dữ liệu sensor thô tốn kém khi truyền.
3. Resilience — Internet sẽ sập. Nhà máy của bạn không nên dừng.
Edge processing đảm bảo các hoạt động quan trọng tiếp tục trong khi mất mạng. Quyết định ảnh hưởng đến an toàn, kiểm soát chất lượng, và điều chỉnh real-time xảy ra cục bộ — cloud nhận bản tóm tắt khi kết nối trở lại.
Edge vs Fog vs Cloud
Ba thuật ngữ này hay bị dùng lẫn lộn, nhưng chúng đại diện cho các tầng riêng biệt với trách nhiệm khác nhau:
Bảng So Sánh
| Khía Cạnh | Edge | Fog | Cloud |
|---|---|---|---|
| Vị trí | Trên hoặc cạnh thiết bị | Mạng cục bộ (gateway, on-prem server) | Trung tâm dữ liệu từ xa |
| Latency | < 10ms | 10-100ms | 50-500ms |
| Sức mạnh tính toán | Hạn chế (ARM, microcontroller) | Trung bình (x86 server, GPU) | Không giới hạn (auto-scale) |
| Lưu trữ | Phạm vi GB | Phạm vi TB | Phạm vi PB |
| Kết nối | Có thể hoạt động offline | Cần mạng cục bộ | Cần internet |
| Hardware ví dụ | Raspberry Pi, Jetson Nano, ESP32 | On-prem server, industrial PC | AWS, Azure, GCP |
| Loại xử lý | Lọc, ngưỡng, ML đơn giản | Tổng hợp, phân tích khu vực, model inference | Training, phân tích toàn cục, truy vấn lịch sử |
| Chi phí | Thấp mỗi thiết bị, scale tuyến tính | Trung bình (hạ tầng chia sẻ) | Trả theo dùng, có thể tăng đột biến |
Khi Nào Dùng Mỗi Tầng
Edge Computing Patterns
Pattern 1: Data Filtering
Edge pattern đơn giản nhất — bỏ data bạn không cần trước khi nó rời thiết bị.
// Edge data filter — chạy trên gateway device
interface SensorReading {
deviceId: string;
timestamp: number;
temperature: number;
humidity: number;
vibration: number;
}
class EdgeDataFilter {
private lastSent: Map<string, SensorReading> = new Map();
// Chỉ forward readings thay đổi đáng kể
shouldForward(reading: SensorReading): boolean {
const last = this.lastSent.get(reading.deviceId);
if (!last) {
this.lastSent.set(reading.deviceId, reading);
return true; // Luôn forward reading đầu tiên
}
const tempDelta = Math.abs(reading.temperature - last.temperature);
const humidDelta = Math.abs(reading.humidity - last.humidity);
const vibDelta = Math.abs(reading.vibration - last.vibration);
// Forward chỉ khi thay đổi vượt ngưỡng
if (tempDelta > 0.5 || humidDelta > 2.0 || vibDelta > 10.0) {
this.lastSent.set(reading.deviceId, reading);
return true;
}
return false; // Bỏ qua — không đủ đáng kể
}
}
// Sử dụng trên edge gateway
const filter = new EdgeDataFilter();
const readings = getIncomingSensorReadings(); // Từ MQTT subscription
for (const reading of readings) {
if (filter.shouldForward(reading)) {
publishToCloud(reading); // Forward lên cloud MQTT broker
}
// Âm thầm bỏ qua readings không đáng kể
}Kết quả: Nếu nhiệt độ dao động ±0.1°C, bạn gửi một reading thay vì hàng trăm. Giảm thường thấy: 70-95% ít messages hơn.
Pattern 2: Edge Aggregation
Thay vì gửi mọi raw reading, tính toán bản tóm tắt cục bộ:
interface AggregatedReading {
deviceId: string;
windowStart: number;
windowEnd: number;
temperature: {
min: number;
max: number;
avg: number;
count: number;
};
humidity: {
min: number;
max: number;
avg: number;
count: number;
};
}
class EdgeAggregator {
private windows: Map<string, SensorReading[]> = new Map();
private windowDuration = 60_000; // Cửa sổ 1 phút
addReading(reading: SensorReading): void {
const key = reading.deviceId;
if (!this.windows.has(key)) {
this.windows.set(key, []);
}
this.windows.get(key)!.push(reading);
}
flush(deviceId: string): AggregatedReading | null {
const readings = this.windows.get(deviceId);
if (!readings || readings.length === 0) return null;
const temps = readings.map(r => r.temperature);
const humids = readings.map(r => r.humidity);
const result: AggregatedReading = {
deviceId,
windowStart: readings[0].timestamp,
windowEnd: readings[readings.length - 1].timestamp,
temperature: {
min: Math.min(...temps),
max: Math.max(...temps),
avg: temps.reduce((a, b) => a + b, 0) / temps.length,
count: temps.length,
},
humidity: {
min: Math.min(...humids),
max: Math.max(...humids),
avg: humids.reduce((a, b) => a + b, 0) / humids.length,
count: humids.length,
},
};
this.windows.set(deviceId, []); // Reset cửa sổ
return result;
}
}
// Mỗi 60 giây, flush aggregates lên cloud
setInterval(() => {
for (const deviceId of aggregator.getAllDeviceIds()) {
const summary = aggregator.flush(deviceId);
if (summary) {
publishToCloud(summary); // 1 message thay vì 60
}
}
}, 60_000);60 raw readings mỗi phút → 1 aggregated message. Cloud nhận min/max/avg — đủ cho dashboards và trend analysis.
Pattern 3: Threshold Alerting
Phản ứng với điều kiện quan trọng ngay lập tức tại edge mà không cần chờ cloud:
interface Alert {
deviceId: string;
type: 'warning' | 'critical';
metric: string;
value: number;
threshold: number;
timestamp: number;
message: string;
}
interface ThresholdRule {
metric: string;
warningThreshold: number;
criticalThreshold: number;
direction: 'above' | 'below';
}
class EdgeAlertEngine {
private rules: ThresholdRule[] = [
{ metric: 'temperature', warningThreshold: 35, criticalThreshold: 45, direction: 'above' },
{ metric: 'temperature', warningThreshold: 5, criticalThreshold: 0, direction: 'below' },
{ metric: 'vibration', warningThreshold: 50, criticalThreshold: 80, direction: 'above' },
{ metric: 'humidity', warningThreshold: 80, criticalThreshold: 95, direction: 'above' },
];
// Debounce: không spam alerts
private lastAlert: Map<string, number> = new Map();
private cooldownMs = 30_000; // 30 giây cooldown mỗi device+metric
evaluate(reading: SensorReading): Alert[] {
const alerts: Alert[] = [];
for (const rule of this.rules) {
const value = reading[rule.metric as keyof SensorReading] as number;
if (typeof value !== 'number') continue;
const key = `${reading.deviceId}:${rule.metric}:${rule.direction}`;
const lastTime = this.lastAlert.get(key) || 0;
if (Date.now() - lastTime < this.cooldownMs) continue;
let triggered = false;
let type: 'warning' | 'critical' = 'warning';
let threshold = rule.warningThreshold;
if (rule.direction === 'above') {
if (value >= rule.criticalThreshold) {
triggered = true;
type = 'critical';
threshold = rule.criticalThreshold;
} else if (value >= rule.warningThreshold) {
triggered = true;
type = 'warning';
}
} else {
if (value <= rule.criticalThreshold) {
triggered = true;
type = 'critical';
threshold = rule.criticalThreshold;
} else if (value <= rule.warningThreshold) {
triggered = true;
type = 'warning';
}
}
if (triggered) {
this.lastAlert.set(key, Date.now());
alerts.push({
deviceId: reading.deviceId,
type,
metric: rule.metric,
value,
threshold,
timestamp: Date.now(),
message: `${rule.metric} ${rule.direction} ${threshold}: ${value}`,
});
}
}
return alerts;
}
}
// Trên edge gateway
const alertEngine = new EdgeAlertEngine();
function processReading(reading: SensorReading) {
const alerts = alertEngine.evaluate(reading);
for (const alert of alerts) {
// Phản ứng cục bộ — không cần cloud
if (alert.type === 'critical') {
triggerLocalBuzzer();
activateEmergencyShutdown(alert.deviceId);
}
// Cũng forward alert lên cloud để logging
publishToCloud(alert);
}
}Lợi ích chính: Còi báo kêu trong < 5ms thay vì chờ 200ms+ cho cloud round trip.
Pattern 4: Store-and-Forward
Buffer dữ liệu cục bộ khi mạng sập, flush khi khôi phục:
import * as fs from 'fs';
import * as path from 'path';
interface QueuedMessage {
topic: string;
payload: string;
timestamp: number;
retries: number;
}
class StoreAndForward {
private queue: QueuedMessage[] = [];
private maxQueueSize = 10_000;
private persistPath: string;
private isOnline = true;
constructor(persistDir: string) {
this.persistPath = path.join(persistDir, 'message-queue.json');
this.loadFromDisk(); // Khôi phục sau khi restart
}
async send(topic: string, payload: string): Promise<void> {
if (this.isOnline) {
try {
await this.publishToCloud(topic, payload);
return; // Gửi thành công
} catch {
this.isOnline = false;
console.log('Mạng sập — chuyển sang store-and-forward');
}
}
// Lưu cục bộ
this.enqueue({ topic, payload, timestamp: Date.now(), retries: 0 });
}
private enqueue(msg: QueuedMessage): void {
if (this.queue.length >= this.maxQueueSize) {
// Bỏ messages cũ nhất không quan trọng (FIFO eviction)
this.queue.shift();
}
this.queue.push(msg);
this.saveToDisk(); // Sống sót qua device restarts
}
async flush(): Promise<void> {
if (this.queue.length === 0) return;
console.log(`Flushing ${this.queue.length} queued messages...`);
const failed: QueuedMessage[] = [];
for (const msg of this.queue) {
try {
await this.publishToCloud(msg.topic, msg.payload);
} catch {
if (msg.retries < 3) {
msg.retries++;
failed.push(msg);
} else {
console.log(`Bỏ message sau 3 lần retry: ${msg.topic}`);
}
}
}
this.queue = failed;
this.saveToDisk();
if (failed.length === 0) {
this.isOnline = true;
console.log('Tất cả queued messages đã flush thành công');
}
}
private saveToDisk(): void {
fs.writeFileSync(this.persistPath, JSON.stringify(this.queue));
}
private loadFromDisk(): void {
try {
const data = fs.readFileSync(this.persistPath, 'utf-8');
this.queue = JSON.parse(data);
console.log(`Đã load ${this.queue.length} messages từ disk`);
} catch {
this.queue = [];
}
}
private async publishToCloud(topic: string, payload: string): Promise<void> {
// Actual MQTT/HTTP publish lên cloud broker
throw new Error('Not implemented — dùng MQTT client của bạn ở đây');
}
}
// Sử dụng
const saf = new StoreAndForward('/var/edge/data');
// Hoạt động bình thường — gửi hoặc queue tự động
await saf.send('factory/zone-1/temperature', JSON.stringify(reading));
// Kiểm tra kết nối định kỳ và flush
setInterval(async () => {
if (await checkConnectivity()) {
await saf.flush();
}
}, 30_000); // Kiểm tra mỗi 30 giâyKiến Trúc Fog Computing
Fog computing nằm giữa edge và cloud — thường là on-premise servers hoặc gateways tổng hợp dữ liệu từ nhiều edge devices. Hãy nghĩ nó như một hub xử lý khu vực.
Khi Nào Cần Fog
Edge devices bị giới hạn — một Raspberry Pi không thể chạy ML models phức tạp hay tương quan dữ liệu từ 500 sensors. Nhưng gửi mọi thứ lên cloud thì lãng phí. Tầng fog xử lý:
- Tương quan liên thiết bị: "Nhiệt độ tăng đột biến ở sensor A có liên quan đến rung tăng ở sensor B không?"
- ML inference khu vực: Chạy pre-trained models trên on-prem GPU server
- Dashboard cục bộ: Công nhân trên sàn nhà máy cần xem real-time mà không phụ thuộc internet
- Dịch giao thức: Bridge thiết bị MQTT, CoAP, và Modbus thành luồng dữ liệu thống nhất
Triển Khai Fog Node
Một fog node thường chạy như service trên on-premise server, thu thập từ nhiều edge devices:
import mqtt from 'mqtt';
interface ZoneData {
zone: string;
devices: Map<string, SensorReading[]>;
lastAggregation: number;
}
class FogNode {
private zones: Map<string, ZoneData> = new Map();
private edgeBroker: mqtt.MqttClient;
private cloudBroker: mqtt.MqttClient;
constructor(edgeBrokerUrl: string, cloudBrokerUrl: string) {
// Kết nối MQTT broker cục bộ (edge devices publish tại đây)
this.edgeBroker = mqtt.connect(edgeBrokerUrl);
// Kết nối cloud MQTT broker (fog publishes aggregates lên đây)
this.cloudBroker = mqtt.connect(cloudBrokerUrl);
this.setupSubscriptions();
this.startAggregationLoop();
}
private setupSubscriptions(): void {
// Subscribe tất cả zone data từ edge devices
this.edgeBroker.subscribe('factory/+/+/data');
this.edgeBroker.on('message', (topic, message) => {
// topic: factory/{zone}/{device}/data
const parts = topic.split('/');
const zone = parts[1];
const deviceId = parts[2];
const reading: SensorReading = JSON.parse(message.toString());
this.ingestReading(zone, deviceId, reading);
});
}
private ingestReading(zone: string, deviceId: string, reading: SensorReading): void {
if (!this.zones.has(zone)) {
this.zones.set(zone, {
zone,
devices: new Map(),
lastAggregation: Date.now(),
});
}
const zoneData = this.zones.get(zone)!;
if (!zoneData.devices.has(deviceId)) {
zoneData.devices.set(deviceId, []);
}
zoneData.devices.get(deviceId)!.push(reading);
// Chạy cross-device anomaly detection
this.detectCrossDeviceAnomalies(zone);
}
private detectCrossDeviceAnomalies(zone: string): void {
const zoneData = this.zones.get(zone);
if (!zoneData) return;
// Ví dụ: tương quan nhiệt độ và rung giữa các thiết bị
const latestReadings: SensorReading[] = [];
for (const [, readings] of zoneData.devices) {
if (readings.length > 0) {
latestReadings.push(readings[readings.length - 1]);
}
}
const avgTemp = latestReadings.reduce((sum, r) => sum + r.temperature, 0)
/ latestReadings.length;
const maxVibration = Math.max(...latestReadings.map(r => r.vibration));
// Alert cấp zone: nhiệt độ cao + rung cao = có thể hỏng thiết bị
if (avgTemp > 40 && maxVibration > 60) {
this.cloudBroker.publish(
`alerts/factory/${zone}/anomaly`,
JSON.stringify({
type: 'cross_device_anomaly',
zone,
avgTemp,
maxVibration,
timestamp: Date.now(),
message: `Zone ${zone}: nhiệt độ cao (${avgTemp.toFixed(1)}°C) + rung (${maxVibration})`,
})
);
}
}
private startAggregationLoop(): void {
// Mỗi 5 phút, aggregate và gửi lên cloud
setInterval(() => {
for (const [zone, zoneData] of this.zones) {
const summary = this.aggregateZone(zone, zoneData);
this.cloudBroker.publish(
`factory/${zone}/summary`,
JSON.stringify(summary)
);
// Reset device readings sau aggregation
for (const [deviceId] of zoneData.devices) {
zoneData.devices.set(deviceId, []);
}
zoneData.lastAggregation = Date.now();
}
}, 5 * 60_000);
}
private aggregateZone(zone: string, data: ZoneData) {
const deviceSummaries: Record<string, any> = {};
for (const [deviceId, readings] of data.devices) {
if (readings.length === 0) continue;
const temps = readings.map(r => r.temperature);
deviceSummaries[deviceId] = {
readingCount: readings.length,
temperature: {
min: Math.min(...temps),
max: Math.max(...temps),
avg: temps.reduce((a, b) => a + b, 0) / temps.length,
},
};
}
return {
zone,
windowStart: data.lastAggregation,
windowEnd: Date.now(),
deviceCount: data.devices.size,
devices: deviceSummaries,
};
}
}
// Khởi động fog node
const fog = new FogNode(
'mqtt://local-broker:1883', // Edge devices publish tại đây
'mqtt://cloud-broker:8883' // Fog publishes aggregates lên đây
);Các Lựa Chọn Edge Runtime
Khi quyết định chạy code tại edge, bạn cần một runtime — cách deploy, quản lý, và cập nhật phần mềm trên edge devices. Đây là so sánh các lựa chọn chính:
Bảng So Sánh
| Tính Năng | AWS Greengrass | Azure IoT Edge | K3s | Docker on Edge |
|---|---|---|---|---|
| Nhà cung cấp | AWS | Microsoft | Rancher/SUSE | Docker Inc |
| Tích hợp cloud | Sâu AWS (IoT Core, Lambda, S3) | Sâu Azure (IoT Hub, Functions) | Bất kỳ (cloud-agnostic) | Bất kỳ |
| Mô hình lập trình | Lambda functions, containers | Modules (Docker containers) | Pods (Kubernetes) | Containers |
| Hardware tối thiểu | 1 GHz ARM, 128 MB RAM | 1 GHz x86/ARM, 256 MB RAM | 512 MB RAM, 1 CPU | 512 MB RAM |
| OTA updates | Tích hợp (Greengrass deployments) | Tích hợp (IoT Hub deployments) | Helm charts, kubectl | docker compose pull |
| Messaging cục bộ | Local MQTT broker + IPC | Route messages giữa modules | Tùy chọn (tự cài) | Tùy chọn |
| Hỗ trợ offline | Có (local Lambda execution) | Có (modules chạy cục bộ) | Có (pods chạy cục bộ) | Có |
| Quản lý thiết bị | AWS IoT Device Shadow | Azure Device Twin | Manual hoặc Rancher | Manual |
| Chi phí | Free tier + trả cho cloud services | Free tier + trả cho cloud services | Miễn phí (open source) | Miễn phí (open source) |
| Phù hợp nhất | Đội ngũ dùng AWS | Đội ngũ dùng Azure | Cloud-agnostic, đội Kubernetes | Deployments đơn giản, prototypes |
AWS IoT Greengrass
Greengrass cho phép bạn chạy Lambda functions và Docker containers trên edge devices, với tích hợp liền mạch vào AWS IoT Core:
# greengrass-deployment.yaml
# Greengrass V2 component deployment
RecipeFormatVersion: "2020-01-25"
ComponentName: com.factory.temperature-filter
ComponentVersion: "1.0.0"
Manifests:
- Platform:
os: linux
Lifecycle:
run: |
python3 {artifacts:path}/filter.py
Artifacts:
- URI: s3://my-bucket/components/filter.py
ComponentDependencies:
aws.greengrass.Nucleus:
VersionRequirement: ">=2.0.0"# filter.py — Greengrass V2 component
import json
import awsiot.greengrasscoreipc as ipc
ipc_client = ipc.connect()
def on_message(event):
message = json.loads(event.message.payload.decode())
temperature = message.get('temperature', 0)
# Chỉ forward readings đáng kể lên cloud
if temperature > 35 or temperature < 5:
ipc_client.publish_to_iot_core(
topic_name=f"factory/alerts/{message['deviceId']}",
qos='AT_LEAST_ONCE',
payload=json.dumps({
'alert': 'temperature_anomaly',
'value': temperature,
'deviceId': message['deviceId'],
})
)
# Subscribe topic MQTT cục bộ
ipc_client.subscribe_to_topic(
topic='factory/+/temperature',
on_stream_event=on_message,
)Azure IoT Edge
Azure IoT Edge chạy containerized modules trên edge devices, quản lý qua Azure IoT Hub:
// deployment.json — Azure IoT Edge deployment manifest
{
"modulesContent": {
"$edgeAgent": {
"properties.desired": {
"modules": {
"temperatureFilter": {
"type": "docker",
"settings": {
"image": "myregistry.azurecr.io/temp-filter:1.0",
"createOptions": "{\"HostConfig\":{\"Privileged\":true}}"
},
"status": "running",
"restartPolicy": "always"
}
}
}
},
"$edgeHub": {
"properties.desired": {
"routes": {
"filterToCloud": "FROM /messages/modules/temperatureFilter/outputs/alerts INTO $upstream",
"sensorToFilter": "FROM /messages/modules/sensor/* INTO BrokeredEndpoint(\"/modules/temperatureFilter/inputs/data\")"
}
}
}
}
}K3s — Kubernetes Nhẹ Tại Edge
K3s là bản phân phối Kubernetes được CNCF chứng nhận, xây dựng cho edge và IoT. Nó gọt Kubernetes xuống một binary duy nhất dưới 100 MB:
# Cài K3s trên edge device (một lệnh duy nhất)
curl -sfL https://get.k3s.io | sh -
# Kiểm tra đang chạy
sudo k3s kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# edge-gw-01 Ready control-plane,master 30s v1.28.4+k3s1# edge-processor.yaml — Deploy edge processing dưới dạng Kubernetes pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: edge-data-processor
namespace: iot
spec:
replicas: 1
selector:
matchLabels:
app: edge-processor
template:
metadata:
labels:
app: edge-processor
spec:
containers:
- name: processor
image: myregistry/edge-processor:1.0
resources:
limits:
cpu: "500m"
memory: "256Mi"
requests:
cpu: "100m"
memory: "128Mi"
env:
- name: MQTT_BROKER
value: "mqtt://mosquitto:1883"
- name: CLOUD_BROKER
value: "mqtts://cloud-broker:8883"
volumeMounts:
- name: edge-data
mountPath: /var/edge/data
volumes:
- name: edge-data
hostPath:
path: /var/edge/data
type: DirectoryOrCreate
---
# Local MQTT broker dưới dạng Kubernetes service
apiVersion: apps/v1
kind: Deployment
metadata:
name: mosquitto
namespace: iot
spec:
replicas: 1
selector:
matchLabels:
app: mosquitto
template:
metadata:
labels:
app: mosquitto
spec:
containers:
- name: mosquitto
image: eclipse-mosquitto:2
ports:
- containerPort: 1883
volumeMounts:
- name: config
mountPath: /mosquitto/config
volumes:
- name: config
configMap:
name: mosquitto-config
---
apiVersion: v1
kind: Service
metadata:
name: mosquitto
namespace: iot
spec:
selector:
app: mosquitto
ports:
- port: 1883
targetPort: 1883Tại sao K3s cho IoT edge?
- Binary duy nhất, ~100 MB — chạy trên Raspberry Pi
- Không bị lock-in cloud vendor
- Full Kubernetes API — dùng Helm, kubectl, GitOps
- Tích hợp SQLite thay vì etcd (nhẹ hơn)
- Tự động TLS, traefik ingress đi kèm
Docker Compose on Edge
Cho deployments đơn giản hơn mà Kubernetes thì quá mức cần thiết:
# docker-compose.edge.yml
version: "3.8"
services:
mosquitto:
image: eclipse-mosquitto:2
ports:
- "1883:1883"
volumes:
- ./mosquitto/config:/mosquitto/config
- mosquitto-data:/mosquitto/data
restart: unless-stopped
edge-processor:
image: myregistry/edge-processor:1.0
environment:
- MQTT_BROKER=mqtt://mosquitto:1883
- CLOUD_BROKER=mqtts://cloud.example.com:8883
- STORE_PATH=/data/queue
volumes:
- edge-data:/data
depends_on:
- mosquitto
restart: unless-stopped
node-red:
image: nodered/node-red:latest
ports:
- "1880:1880"
volumes:
- nodered-data:/data
restart: unless-stopped
volumes:
mosquitto-data:
edge-data:
nodered-data:# Deploy trên edge gateway
docker compose -f docker-compose.edge.yml up -d
# Cập nhật một service
docker compose -f docker-compose.edge.yml pull edge-processor
docker compose -f docker-compose.edge.yml up -d edge-processor
# Kiểm tra trạng thái
docker compose -f docker-compose.edge.yml psChiến Lược Sync Edge-to-Cloud
Edge và cloud cần đồng bộ — câu hỏi là bao nhiêu và bao lâu một lần.
So Sánh Chiến Lược
| Chiến Lược | Độ Tươi Dữ Liệu | Bandwidth | Độ Phức Tạp | Phù Hợp Nhất |
|---|---|---|---|---|
| Streaming liên tục | Real-time | Cao | Thấp | Deployments nhỏ, dữ liệu quan trọng |
| Upload batch định kỳ | Phút đến giờ | Thấp | Trung bình | Telemetry, dữ liệu không quan trọng |
| Upload theo sự kiện | Khi thay đổi | Trung bình | Trung bình | Alerts, thay đổi trạng thái |
| Hybrid (stream + batch) | Hỗn hợp | Trung bình | Cao | Hệ thống production |
Triển Khai Hybrid Sync
Hầu hết hệ thống production dùng cách tiếp cận hybrid — stream dữ liệu quan trọng, batch phần còn lại:
type Priority = 'critical' | 'normal' | 'low';
interface SyncConfig {
critical: { mode: 'stream' }; // Real-time
normal: { mode: 'batch'; intervalMs: number }; // Mỗi 5 phút
low: { mode: 'batch'; intervalMs: number }; // Mỗi 30 phút
}
class HybridSync {
private config: SyncConfig = {
critical: { mode: 'stream' },
normal: { mode: 'batch', intervalMs: 5 * 60_000 },
low: { mode: 'batch', intervalMs: 30 * 60_000 },
};
private normalBatch: QueuedMessage[] = [];
private lowBatch: QueuedMessage[] = [];
private cloudClient: mqtt.MqttClient;
constructor(cloudBrokerUrl: string) {
this.cloudClient = mqtt.connect(cloudBrokerUrl);
// Flush batch định kỳ
setInterval(() => this.flushBatch('normal'), this.config.normal.intervalMs);
setInterval(() => this.flushBatch('low'), this.config.low.intervalMs);
}
async sync(topic: string, payload: string, priority: Priority): Promise<void> {
const message: QueuedMessage = { topic, payload, timestamp: Date.now(), retries: 0 };
switch (priority) {
case 'critical':
// Stream ngay lập tức
await this.publishNow(message);
break;
case 'normal':
this.normalBatch.push(message);
break;
case 'low':
this.lowBatch.push(message);
break;
}
}
private async publishNow(msg: QueuedMessage): Promise<void> {
this.cloudClient.publish(msg.topic, msg.payload, { qos: 1 });
}
private flushBatch(priority: 'normal' | 'low'): void {
const batch = priority === 'normal' ? this.normalBatch : this.lowBatch;
if (batch.length === 0) return;
// Gửi dưới dạng batch nén duy nhất
const batchPayload = JSON.stringify({
batchId: crypto.randomUUID(),
priority,
count: batch.length,
messages: batch,
});
this.cloudClient.publish(`batch/${priority}`, batchPayload, { qos: 1 });
// Xóa batch
if (priority === 'normal') {
this.normalBatch = [];
} else {
this.lowBatch = [];
}
}
}
// Sử dụng
const sync = new HybridSync('mqtts://cloud-broker:8883');
// Alert quan trọng — stream ngay lập tức
await sync.sync('alerts/factory/zone-1', alertPayload, 'critical');
// Telemetry bình thường — batch mỗi 5 phút
await sync.sync('telemetry/zone-1', telemetryPayload, 'normal');
// Environmental logs — batch mỗi 30 phút
await sync.sync('logs/zone-1', logPayload, 'low');Edge ML Inference
Một trong những edge patterns mạnh mẽ nhất — chạy pre-trained machine learning models trực tiếp trên edge devices để dự đoán real-time mà không phụ thuộc cloud.
Tại Sao ML Tại Edge?
| Yếu Tố | Cloud ML | Edge ML |
|---|---|---|
| Latency | 100-500ms | 5-50ms |
| Quyền riêng tư | Dữ liệu rời premises | Dữ liệu ở local |
| Chi phí | Trả theo inference | Chi phí hardware một lần |
| Kết nối | Bắt buộc | Không cần |
| Kích thước model | Không giới hạn | Bị giới hạn (phạm vi MB) |
Các Use Cases Edge ML Phổ Biến
- Anomaly detection: Dự đoán hỏng thiết bị từ pattern rung
- Computer vision: Phát hiện lỗi trên dây chuyền sản xuất
- Sound classification: Phát hiện tiếng máy bất thường
- Predictive maintenance: Ước tính tuổi thọ còn lại (RUL)
Edge Inference với TensorFlow Lite
# edge_inference.py — Chạy trên Raspberry Pi hoặc Jetson Nano
import numpy as np
import json
import paho.mqtt.client as mqtt
# TensorFlow Lite — tối ưu cho edge devices
import tflite_runtime.interpreter as tflite
class EdgeAnomalyDetector:
def __init__(self, model_path: str):
# Load model đã quantize (thường 1-10 MB)
self.interpreter = tflite.Interpreter(model_path=model_path)
self.interpreter.allocate_tensors()
self.input_details = self.interpreter.get_input_details()
self.output_details = self.interpreter.get_output_details()
# Sliding window các readings gần đây
self.window_size = 60 # 60 giây dữ liệu
self.buffer = []
def add_reading(self, reading: dict) -> dict | None:
"""Thêm reading và trả về dự đoán anomaly nếu window đầy."""
self.buffer.append([
reading['temperature'],
reading['humidity'],
reading['vibration'],
reading['pressure'],
])
if len(self.buffer) < self.window_size:
return None # Chưa đủ dữ liệu
if len(self.buffer) > self.window_size:
self.buffer.pop(0) # Sliding window
return self.predict()
def predict(self) -> dict:
"""Chạy inference trên window hiện tại."""
# Chuẩn bị input tensor
input_data = np.array([self.buffer], dtype=np.float32)
self.interpreter.set_tensor(self.input_details[0]['index'], input_data)
# Chạy inference
self.interpreter.invoke()
# Lấy output
output = self.interpreter.get_tensor(self.output_details[0]['index'])
anomaly_score = float(output[0][0])
return {
'anomaly_score': anomaly_score,
'is_anomaly': anomaly_score > 0.8, # Ngưỡng
'confidence': min(anomaly_score, 1.0 - anomaly_score) * 2,
}
# Tích hợp MQTT
detector = EdgeAnomalyDetector('models/anomaly_detector.tflite')
client = mqtt.Client()
def on_message(client, userdata, msg):
reading = json.loads(msg.payload.decode())
result = detector.add_reading(reading)
if result and result['is_anomaly']:
# Publish anomaly alert cục bộ và lên cloud
alert = {
'type': 'ml_anomaly',
'deviceId': reading['deviceId'],
'score': result['anomaly_score'],
'confidence': result['confidence'],
'timestamp': reading['timestamp'],
}
client.publish('alerts/anomaly', json.dumps(alert))
print(f"⚠️ Phát hiện bất thường: score={result['anomaly_score']:.3f}")
client.on_message = on_message
client.connect('localhost', 1883)
client.subscribe('sensors/+/data')
client.loop_forever()Flow Cập Nhật Edge ML Model
Models cần cập nhật định kỳ khi bạn train trên thêm dữ liệu ở cloud:
Offline-First Design Patterns
Edge devices phải giả định mạng sẽ sập. Thiết kế offline-first coi kết nối là bonus, không phải yêu cầu bắt buộc.
Nguyên Tắc
- Xử lý local-first — Mọi quyết định quan trọng xảy ra cục bộ
- Idempotent operations — An toàn khi replay lúc kết nối lại
- Conflict resolution — Xử lý dữ liệu cũ một cách graceful
- Graceful degradation — Giảm chức năng, không bao giờ crash
Kiến Trúc Offline-First
Connectivity State Machine
type ConnectivityState = 'online' | 'degraded' | 'offline';
class ConnectivityManager {
private state: ConnectivityState = 'online';
private failedPings = 0;
private maxFailedPings = 3;
private listeners: ((state: ConnectivityState) => void)[] = [];
async checkConnectivity(): Promise<ConnectivityState> {
try {
const start = Date.now();
await fetch('https://cloud-broker.example.com/health', {
signal: AbortSignal.timeout(5000),
});
const latency = Date.now() - start;
this.failedPings = 0;
if (latency > 2000) {
this.setState('degraded');
} else {
this.setState('online');
}
} catch {
this.failedPings++;
if (this.failedPings >= this.maxFailedPings) {
this.setState('offline');
} else {
this.setState('degraded');
}
}
return this.state;
}
private setState(newState: ConnectivityState): void {
if (this.state !== newState) {
console.log(`Kết nối: ${this.state} → ${newState}`);
this.state = newState;
this.listeners.forEach(fn => fn(newState));
}
}
onStateChange(listener: (state: ConnectivityState) => void): void {
this.listeners.push(listener);
}
getState(): ConnectivityState {
return this.state;
}
}
// Sử dụng
const connectivity = new ConnectivityManager();
connectivity.onStateChange((state) => {
switch (state) {
case 'online':
console.log('Kết nối đầy đủ — streaming lên cloud');
sync.flushAllQueues();
break;
case 'degraded':
console.log('Suy giảm — batch mode, chỉ critical');
sync.switchToBatchMode();
break;
case 'offline':
console.log('Offline — xử lý cục bộ only');
sync.pauseCloudSync();
break;
}
});
// Kiểm tra mỗi 30 giây
setInterval(() => connectivity.checkConnectivity(), 30_000);Giải Quyết Conflict Dữ Liệu
Khi edge device kết nối lại, dữ liệu cục bộ có thể conflict với trạng thái cloud (ví dụ, một operator khác đã thay đổi cấu hình):
interface VersionedData<T> {
data: T;
version: number;
lastModified: number;
source: 'edge' | 'cloud';
}
type ConflictStrategy = 'cloud-wins' | 'edge-wins' | 'latest-wins' | 'merge';
class ConflictResolver {
resolve<T>(
local: VersionedData<T>,
remote: VersionedData<T>,
strategy: ConflictStrategy
): VersionedData<T> {
// Không có conflict — versions khớp
if (local.version === remote.version) return local;
switch (strategy) {
case 'cloud-wins':
return remote;
case 'edge-wins':
return local;
case 'latest-wins':
return local.lastModified > remote.lastModified ? local : remote;
case 'merge':
// Logic merge tùy chỉnh — phụ thuộc vào kiểu dữ liệu
return this.mergeData(local, remote);
default:
return remote; // Default an toàn
}
}
private mergeData<T>(
local: VersionedData<T>,
remote: VersionedData<T>
): VersionedData<T> {
// Cho sensor configs: dùng cloud cho thresholds, edge cho calibration
const merged = {
...remote.data, // Cloud settings được ưu tiên
...local.data, // Edge overrides (ví dụ: calibration cục bộ)
};
return {
data: merged as T,
version: Math.max(local.version, remote.version) + 1,
lastModified: Date.now(),
source: 'edge',
};
}
}Lỗi Thường Gặp Của Người Mới
Lỗi 1: Gửi Mọi Thứ Lên Cloud
❌ 10.000 sensors → 172 GB/ngày raw data → cloud → $$$
✅ 10.000 sensors → edge filter → 5 GB/ngày summaries → cloud → $Cách sửa: Bắt đầu với câu hỏi "cloud thực sự cần gì?" Thường thì là aggregates, alerts, và tóm tắt mỗi giờ — không phải mọi raw reading.
Lỗi 2: Không Có Chiến Lược Offline
❌ Mạng sập → edge device báo lỗi → mất dữ liệu
✅ Mạng sập → store-and-forward → auto-flush khi kết nối lạiCách sửa: Coi mạng là không đáng tin cậy từ ngày đầu. Triển khai store-and-forward trước khi bạn nghĩ mình cần — bạn sẽ cần.
Lỗi 3: Over-Engineering Edge
❌ Chạy Kubernetes + Kafka + PostgreSQL trên Raspberry Pi
✅ Chạy Node.js process đơn giản + SQLite + MQTT trên Raspberry PiCách sửa: Khớp runtime với hardware. Pi có 1-8 GB RAM — Docker Compose thường là quá đủ rồi. Dành Kubernetes cho on-prem servers ở tầng fog.
Lỗi 4: Bỏ Qua Edge Security
❌ Edge device chạy root, không TLS, password mặc định, ports mở
✅ Least privilege, mTLS, credentials riêng mỗi thiết bị, firewall rulesCách sửa: Edge devices có thể tiếp cận vật lý — chúng cần nhiều security hơn, không phải ít. Chúng ta sẽ nói chi tiết về IoT security trong bài viết tương lai.
Lỗi 5: Không Validate Model Tại Edge
❌ Push ML model mới → thay thế cái cũ ngay → predictions sai
✅ Push ML model mới → chạy shadow cùng cái cũ → validate → swapCách sửa: Luôn chạy models mới ở shadow mode trước. So sánh outputs với model hiện tại trong giai đoạn validation trước khi chuyển.
Tóm Tắt và Bài Học Chính
✅ Edge computing giảm latency, bandwidth, và phụ thuộc cloud — xử lý gần nguồn
✅ Edge, fog, và cloud là các tầng riêng biệt — edge cho lọc, fog cho phối hợp, cloud cho phân tích toàn cục
✅ Data filtering tại edge tiết kiệm 70-95% bandwidth — chỉ forward thay đổi đáng kể và aggregates
✅ Store-and-forward là thiết yếu — buffer dữ liệu cục bộ khi mạng sập, flush khi kết nối lại
✅ Threshold alerting tại edge cho phản hồi dưới 10ms — quan trọng cho an toàn và bảo vệ thiết bị
✅ Khớp runtime với hardware — Docker Compose cho deployments đơn giản, K3s cho đội Kubernetes, Greengrass/IoT Edge cho đội dùng cloud vendor
✅ Edge ML inference chạy trong 5-50ms — anomaly detection, phát hiện lỗi, predictive maintenance không cần cloud
✅ Thiết kế offline-first từ ngày đầu — coi kết nối là bonus, không phải yêu cầu bắt buộc
✅ Validate ML models ở shadow mode — không bao giờ hot-swap model mà không comparison testing
Tiếp Theo
Bây giờ bạn đã hiểu xử lý diễn ra ở đâu — edge, fog, hay cloud — đã đến lúc đào sâu vào giao thức cung cấp sức mạnh cho hầu hết giao tiếp IoT.
Tiếp: IOT-5: Deep Dive: MQTT — Giao Thức Messaging IoT — MQTT v3.1.1 vs v5.0, topic design patterns, QoS trade-offs chuyên sâu, broker clustering (EMQX, HiveMQ), authentication và authorization, và scaling MQTT cho hàng triệu kết nối.
Bài Viết Liên Quan
- Lộ Trình IoT Patterns & Strategies — Tổng quan series 12 bài và lộ trình học tập
- Nền Tảng IoT: Kiến Trúc & Giao Thức — Nền tảng: kiến trúc phân tầng, loại thiết bị, kết nối
- Giao Thức Truyền Thông IoT: MQTT, CoAP, AMQP & WebSocket — Deep dive giao thức với cài đặt thực hành
- Lộ Trình Học Docker & Kubernetes — Nền tảng containerization cho edge deployments
- Giải Thích Kiến Trúc Server-Client — Nền tảng cho distributed system patterns
Đây là Phần 4 của series IoT Patterns & Strategies. Bắt đầu từ đầu hoặc nhảy đến bất kỳ chủ đề nào bạn quan tâm.
📬 Đăng ký nhận bản tin
Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.
Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.
💬 Comments
Đăng nhập để bình luận
Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.