vault backup: 2026-06-24 17:52:15

This commit is contained in:
windyboy
2026-06-24 17:52:15 +08:00
parent 033c9690db
commit 70c4508e56
4 changed files with 2754 additions and 0 deletions
@@ -0,0 +1,679 @@
---
tags:
- installation
- dns
- powerdns
created: 2026-06-17
---
# PowerDNS Docker Compose
## Purpose
This note describes how to install a PowerDNS Authoritative environment with:
- PostgreSQL 16
- PowerDNS Authoritative 5.0.4
- Poweradmin
- pgweb
- scheduled PostgreSQL backups
This installation model assumes:
1. DNS is exposed on host port `53`
2. the PowerDNS API is only exposed on `127.0.0.1:8081`
3. web management services are published through a reverse proxy such as Traefik
## Component versions
| Component | Version |
| --- | --- |
| PostgreSQL | `16` |
| PowerDNS Authoritative | `5.0.4` |
| Poweradmin | `stable` |
| pgweb | `0.16.2` |
| Backup container | `alpine:3.20` |
## Before you start
Make sure the target host has:
1. Linux
2. Docker
3. `docker compose`
4. port `53/tcp` and `53/udp` available
5. a reverse proxy network if Poweradmin and pgweb will be published through Traefik
6. public DNS names for the web interfaces if they will be exposed externally
## Deployment values to prepare
Prepare all runtime values before starting the stack.
### Required core values
| Variable | Meaning |
| --- | --- |
| `PGUSER` | PostgreSQL administrative user |
| `PGPASSWORD` | PostgreSQL administrative password |
| `DB_NAME` | Main PowerDNS database |
| `DB_USER` | Application database user |
| `DB_PASS` | Application database password |
| `PDNS_API_KEY` | PowerDNS API key |
| `CRON_SCHEDULE` | Backup schedule |
| `PA_SESSION_KEY` | Poweradmin session secret |
| `PA_ADMIN_USERNAME` | Bootstrap Poweradmin admin username |
| `PA_ADMIN_PASSWORD` | Bootstrap Poweradmin admin password |
| `PA_ADMIN_EMAIL` | Bootstrap Poweradmin admin email |
| `PA_ADMIN_FULLNAME` | Bootstrap Poweradmin admin full name |
| `PGWEB_USER` | pgweb login username |
| `PGWEB_PASS` | pgweb login password |
### Optional values with defaults
| Variable | Default | Meaning |
| --- | --- | --- |
| `TZ` | `Asia/Shanghai` | Service timezone |
| `DB_HOST` | `db` | Database hostname |
| `DB_PORT` | `5432` | Database port |
| `ADMIN_DB` | `pdnsadmin` | Admin database used for bootstrap and restore |
| `RETENTION_DAYS` | `7` | Backup age retention |
| `MAX_BACKUPS` | `7` | Number of backup sets to keep |
| `DUMP_ROLES` | `true` | Include PostgreSQL role dump in backups |
| `PDNS_VERSION` | `49` | Poweradmin PowerDNS compatibility mode |
| `DNS_NS1` | `ns1.wsvc.info` | Default NS1 value in Poweradmin |
| `DNS_NS2` | `ns2.wsvc.info` | Default NS2 value in Poweradmin |
| `DNS_HOSTMASTER` | `hostmaster.wsvc.info` | Default SOA hostmaster |
| `PA_APP_TITLE` | `Poweradmin` | Poweradmin UI title |
| `PA_CREATE_ADMIN` | `1` | Enable bootstrap admin creation |
### Example values
Replace every placeholder with your own values:
```env
TZ=Asia/Shanghai
PGUSER=postgres
PGPASSWORD=<strong-postgres-password>
DB_HOST=db
DB_PORT=5432
DB_NAME=pdns
DB_USER=pdns
DB_PASS=<strong-app-password>
ADMIN_DB=pdnsadmin
CRON_SCHEDULE=0 3 * * *
RETENTION_DAYS=7
MAX_BACKUPS=7
DUMP_ROLES=true
PDNS_API_KEY=<strong-api-key>
PA_SESSION_KEY=<long-random-session-key>
PA_ADMIN_USERNAME=admin
PA_ADMIN_PASSWORD=<strong-admin-password>
PA_ADMIN_EMAIL=admin@example.com
PA_ADMIN_FULLNAME=DNS Administrator
PGWEB_USER=pgweb
PGWEB_PASS=<strong-pgweb-password>
PDNS_VERSION=49
DNS_NS1=ns1.example.com
DNS_NS2=ns2.example.com
DNS_HOSTMASTER=hostmaster.example.com
PA_APP_TITLE=Poweradmin
PA_CREATE_ADMIN=1
```
## Full configuration blocks
Use the following full configuration content as the installation baseline.
### Environment file
```env
TZ=Asia/Shanghai
PGUSER=postgres
PGPASSWORD=<strong-postgres-password>
DB_HOST=db
DB_PORT=5432
DB_NAME=pdns
DB_USER=pdns
DB_PASS=<strong-app-password>
ADMIN_DB=pdnsadmin
CRON_SCHEDULE=0 3 * * *
RETENTION_DAYS=7
MAX_BACKUPS=7
DUMP_ROLES=true
PDNS_API_KEY=<strong-api-key>
PA_SESSION_KEY=<long-random-session-key>
PA_ADMIN_USERNAME=admin
PA_ADMIN_PASSWORD=<strong-admin-password>
PA_ADMIN_EMAIL=admin@example.com
PA_ADMIN_FULLNAME=DNS Administrator
PGWEB_USER=pgweb
PGWEB_PASS=<strong-pgweb-password>
PDNS_VERSION=49
DNS_NS1=ns1.example.com
DNS_NS2=ns2.example.com
DNS_HOSTMASTER=hostmaster.example.com
PA_APP_TITLE=Poweradmin
PA_CREATE_ADMIN=1
```
### Docker Compose configuration
```yaml
networks:
frontend:
name: traefik
external: true
backend:
internal: true
edge:
services:
db:
image: postgres:16
container_name: pdns-db
environment:
POSTGRES_DB: postgres
POSTGRES_USER: ${PGUSER:?missing PGUSER}
POSTGRES_PASSWORD: ${PGPASSWORD:?missing PGPASSWORD}
TZ: ${TZ:-Asia/Shanghai}
PGTZ: ${TZ:-Asia/Shanghai}
volumes:
- dbdata:/var/lib/postgresql
- ./db-init-generated:/docker-entrypoint-initdb.d:ro
- ./backup:/backup:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
interval: 10s
timeout: 5s
retries: 10
restart: unless-stopped
networks: [backend, edge]
auth:
image: powerdns/pdns-auth-50:5.0.4
container_name: pdns-auth
depends_on:
db:
condition: service_healthy
ports:
- "53:53/udp"
- "53:53/tcp"
- "127.0.0.1:8081:8081"
environment:
PDNS_API_KEY: ${PDNS_API_KEY:?missing PDNS_API_KEY}
DB_NAME: ${DB_NAME:?missing DB_NAME}
DB_USER: ${DB_USER:?missing DB_USER}
DB_PASS: ${DB_PASS:?missing DB_PASS}
TEMPLATE_FILES: secrets
volumes:
- ./auth/pdns.conf:/etc/powerdns/pdns.conf:ro
- ./auth/templates.d:/etc/powerdns/templates.d:ro
- ./auth/keys:/var/lib/powerdns
- ./auth/import:/import
- ./auth/export:/export
- ./auth/logs:/var/log/pdns
healthcheck:
test:
[
"CMD-SHELL",
"python3 -c \"import json, os, urllib.request; req = urllib.request.Request('http://127.0.0.1:8081/api/v1/servers/localhost', headers={'X-API-Key': os.environ['PDNS_API_KEY']}); data = json.load(urllib.request.urlopen(req, timeout=3)); assert data['daemon_type'] == 'authoritative'\""
]
interval: 10s
timeout: 5s
retries: 12
restart: unless-stopped
networks: [backend, edge]
poweradmin:
image: poweradmin/poweradmin:stable
container_name: poweradmin
depends_on:
db:
condition: service_healthy
auth:
condition: service_healthy
environment:
DB_TYPE: pgsql
DB_HOST: ${DB_HOST:-db}
DB_PORT: ${DB_PORT:-5432}
DB_NAME: ${DB_NAME:?missing DB_NAME}
DB_USER: ${DB_USER:?missing DB_USER}
DB_PASS: ${DB_PASS:?missing DB_PASS}
PA_PDNS_API_URL: http://auth:8081
PA_PDNS_API_KEY: ${PDNS_API_KEY:?missing PDNS_API_KEY}
PA_DNS_BACKEND: sql
PDNS_VERSION: ${PDNS_VERSION:-49}
DNS_NS1: ${DNS_NS1:-ns1.wsvc.info}
DNS_NS2: ${DNS_NS2:-ns2.wsvc.info}
DNS_HOSTMASTER: ${DNS_HOSTMASTER:-hostmaster.wsvc.info}
PA_APP_TITLE: ${PA_APP_TITLE:-Poweradmin}
PA_TIMEZONE: ${TZ:-Asia/Shanghai}
PA_SESSION_KEY: ${PA_SESSION_KEY:?missing PA_SESSION_KEY}
PA_CREATE_ADMIN: ${PA_CREATE_ADMIN:-1}
PA_ADMIN_USERNAME: ${PA_ADMIN_USERNAME:?missing PA_ADMIN_USERNAME}
PA_ADMIN_PASSWORD: ${PA_ADMIN_PASSWORD:?missing PA_ADMIN_PASSWORD}
PA_ADMIN_EMAIL: ${PA_ADMIN_EMAIL:?missing PA_ADMIN_EMAIL}
PA_ADMIN_FULLNAME: ${PA_ADMIN_FULLNAME:?missing PA_ADMIN_FULLNAME}
TRUSTED_PROXIES: private_ranges
DEBUG: "false"
restart: unless-stopped
networks: [backend, frontend]
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.poweradmin.rule=Host(`pdns.wsvc.info`)"
- "traefik.http.routers.poweradmin.entrypoints=websecure"
- "traefik.http.routers.poweradmin.tls.certresolver=letsencrypt"
- "traefik.http.services.poweradmin.loadbalancer.server.port=80"
backup:
image: alpine:3.20
container_name: pdns-backup
depends_on:
db:
condition: service_healthy
environment:
TZ: ${TZ:-Asia/Shanghai}
DB_HOST: ${DB_HOST:-db}
DB_PORT: ${DB_PORT:-5432}
DB_USER: ${PGUSER:?missing PGUSER}
DB_PASS: ${PGPASSWORD:?missing PGPASSWORD}
DB_NAME: ${DB_NAME:?missing DB_NAME}
RETENTION_DAYS: ${RETENTION_DAYS:-7}
MAX_BACKUPS: ${MAX_BACKUPS:-7}
DUMP_ROLES: ${DUMP_ROLES:-true}
CRON_SCHEDULE: ${CRON_SCHEDULE:?missing CRON_SCHEDULE}
volumes:
- ./backup:/backup
- ./scripts:/scripts:ro
entrypoint: >
sh -c '
apk add --no-cache postgresql16-client tzdata util-linux bash coreutils findutils;
ln -snf /usr/share/zoneinfo/$$TZ /etc/localtime && echo $$TZ > /etc/timezone;
echo "$$DB_HOST:$$DB_PORT:*:$$DB_USER:$$DB_PASS" > /root/.pgpass;
chmod 600 /root/.pgpass;
echo "$$CRON_SCHEDULE flock -n /backup/.backup.lock /scripts/backup.sh >> /backup/backup.log 2>&1" > /etc/crontabs/root;
echo "[$$(date -Iseconds)] cron started with schedule: $$CRON_SCHEDULE" >> /backup/backup.log;
crond -f -l 8
'
restart: unless-stopped
networks: [backend]
pgweb:
image: sosedoff/pgweb:0.16.2
container_name: pdns_pgweb
restart: unless-stopped
environment:
PGWEB_DATABASE_URL: "postgres://${PGUSER:?missing PGUSER}:${PGPASSWORD:?missing PGPASSWORD}@${DB_HOST:-db}:${DB_PORT:-5432}/${DB_NAME:?missing DB_NAME}?sslmode=disable"
PGWEB_AUTH_USER: ${PGWEB_USER:?missing PGWEB_USER}
PGWEB_AUTH_PASS: ${PGWEB_PASS:?missing PGWEB_PASS}
TZ: ${TZ:-Asia/Shanghai}
depends_on:
db:
condition: service_healthy
networks: [backend, frontend]
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.pgweb.rule=Host(`pgweb.wsvc.info`)"
- "traefik.http.routers.pgweb.entrypoints=websecure"
- "traefik.http.routers.pgweb.tls.certresolver=letsencrypt"
- "traefik.http.services.pgweb.loadbalancer.server.port=8081"
volumes:
dbdata: {}
```
### PowerDNS daemon configuration
```ini
local-address=0.0.0.0
local-port=53
launch=gpgsql
gpgsql-host=db
include-dir=/etc/powerdns/pdns.d
gpgsql-dnssec=yes
allow-axfr-ips=202.91.35.141
also-notify=202.91.35.141
primary=yes
secondary=no
api=yes
webserver=yes
webserver-address=0.0.0.0
webserver-port=8081
webserver-allow-from=127.0.0.1,172.16.0.0/12,10.0.0.0/8,192.168.0.0/16
version-string=anonymous
disable-syslog=yes
loglevel=4
default-soa-edit=INCEPTION-INCREMENT
default-soa-edit-signed=INCEPTION-INCREMENT
disable-axfr=no
```
### Runtime-rendered secret template
```ini
gpgsql-dbname={{ DB_NAME }}
gpgsql-user={{ DB_USER }}
gpgsql-password={{ DB_PASS }}
api-key={{ PDNS_API_KEY }}
```
## Network layout
The stack uses three networks:
| Network | Purpose |
| --- | --- |
| `backend` | internal service-to-service traffic |
| `edge` | local host-facing DNS and API exposure |
| `frontend` | reverse-proxy-facing web traffic |
Service attachment:
| Service | Networks |
| --- | --- |
| PostgreSQL | `backend`, `edge` |
| PowerDNS auth | `backend`, `edge` |
| Poweradmin | `backend`, `frontend` |
| Backup | `backend` |
| pgweb | `backend`, `frontend` |
## Installation procedure
### 1. Prepare the host
Install Docker and Docker Compose support on the Linux host.
If a reverse proxy network is required, create it before deployment.
### 2. Prepare runtime configuration
Set all required environment values.
At minimum, verify:
1. PostgreSQL admin credentials are defined
2. application database credentials are defined
3. PowerDNS API key is defined
4. Poweradmin admin account values are defined
5. pgweb login values are defined
6. backup schedule and retention values are defined
### 3. Start the stack
Start the stack in detached mode.
Expected startup order:
1. PostgreSQL starts first
2. database bootstrap creates the application role and databases
3. PowerDNS starts after PostgreSQL is healthy
4. Poweradmin starts after both PostgreSQL and PowerDNS are healthy
5. backup service starts cron after PostgreSQL is healthy
6. pgweb starts after PostgreSQL is healthy
### 4. Wait for health checks
The deployment should be considered ready only after:
1. PostgreSQL passes `pg_isready`
2. PowerDNS API health check returns an authoritative daemon result
3. Poweradmin and pgweb become reachable through the reverse proxy
### 5. Complete first access
After startup:
1. sign in to Poweradmin with the bootstrap admin account
2. verify pgweb login works
3. verify the PowerDNS API is reachable from the local host only
4. verify DNS answers on port `53`
## Service configuration details
### PostgreSQL
| Setting | Value |
| --- | --- |
| Image | `postgres:16` |
| Container name | `pdns-db` |
| Restart policy | `unless-stopped` |
| Startup database | `postgres` |
| Health check | `pg_isready -U $POSTGRES_USER -d $POSTGRES_DB` |
| Timezone | `TZ`, `PGTZ` |
Bootstrap behavior:
1. create the role named by `DB_USER` if missing
2. update the password of `DB_USER` from `DB_PASS`
3. create the database named by `DB_NAME` if missing
4. create the database named by `ADMIN_DB` if missing
5. assign ownership of both databases to `DB_USER`
### PowerDNS Authoritative
| Setting | Value |
| --- | --- |
| Image | `powerdns/pdns-auth-50:5.0.4` |
| Container name | `pdns-auth` |
| Restart policy | `unless-stopped` |
| Published ports | `53/tcp`, `53/udp`, `127.0.0.1:8081` |
| Health check | local API request using `PDNS_API_KEY` |
Environment used by PowerDNS:
| Variable | Meaning |
| --- | --- |
| `PDNS_API_KEY` | API authentication key |
| `DB_NAME` | PowerDNS PostgreSQL database |
| `DB_USER` | PowerDNS PostgreSQL user |
| `DB_PASS` | PowerDNS PostgreSQL password |
| `TEMPLATE_FILES=secrets` | enables runtime rendering of dynamic config |
Rendered runtime directives:
| Directive | Source |
| --- | --- |
| `gpgsql-dbname` | `DB_NAME` |
| `gpgsql-user` | `DB_USER` |
| `gpgsql-password` | `DB_PASS` |
| `api-key` | `PDNS_API_KEY` |
Daemon behavior:
| Directive | Value | Meaning |
| --- | --- | --- |
| `local-address` | `0.0.0.0` | listen on all container interfaces |
| `local-port` | `53` | DNS listener port |
| `launch` | `gpgsql` | PostgreSQL backend |
| `gpgsql-host` | `db` | database service hostname |
| `gpgsql-dnssec` | `yes` | DNSSEC enabled |
| `api` | `yes` | API enabled |
| `webserver` | `yes` | embedded web server enabled |
| `webserver-address` | `0.0.0.0` | listen on all container interfaces |
| `webserver-port` | `8081` | API port |
| `webserver-allow-from` | `127.0.0.1,172.16.0.0/12,10.0.0.0/8,192.168.0.0/16` | restrict API access to local and private ranges |
| `primary` | `yes` | primary DNS role enabled |
| `secondary` | `no` | secondary role disabled |
| `allow-axfr-ips` | `202.91.35.141` | allowed AXFR peer |
| `also-notify` | `202.91.35.141` | notify destination |
| `version-string` | `anonymous` | hide version string |
| `disable-syslog` | `yes` | disable syslog |
| `loglevel` | `4` | logging verbosity |
| `default-soa-edit` | `INCEPTION-INCREMENT` | SOA serial update policy |
| `default-soa-edit-signed` | `INCEPTION-INCREMENT` | SOA serial policy for signed zones |
| `disable-axfr` | `no` | AXFR globally allowed if otherwise permitted |
### Poweradmin
| Setting | Value |
| --- | --- |
| Image | `poweradmin/poweradmin:stable` |
| Container name | `poweradmin` |
| Restart policy | `unless-stopped` |
| Database type | `pgsql` |
| API endpoint | `http://auth:8081` |
| DNS backend mode | `sql` |
| Trusted proxies | `private_ranges` |
| Debug | `false` |
Important Poweradmin values:
| Variable | Value |
| --- | --- |
| `DB_HOST` | `db` by default |
| `DB_PORT` | `5432` by default |
| `DB_NAME` | required |
| `DB_USER` | required |
| `DB_PASS` | required |
| `PA_PDNS_API_KEY` | same value as `PDNS_API_KEY` |
| `PDNS_VERSION` | `49` |
| `DNS_NS1` | configurable default nameserver |
| `DNS_NS2` | configurable default nameserver |
| `DNS_HOSTMASTER` | configurable default hostmaster |
| `PA_APP_TITLE` | `Poweradmin` by default |
| `PA_TIMEZONE` | value from `TZ` |
| `PA_SESSION_KEY` | required |
| `PA_CREATE_ADMIN` | `1` by default |
| `PA_ADMIN_USERNAME` | required |
| `PA_ADMIN_PASSWORD` | required |
| `PA_ADMIN_EMAIL` | required |
| `PA_ADMIN_FULLNAME` | required |
Reverse proxy routing:
| Item | Value |
| --- | --- |
| Router host | `pdns.wsvc.info` |
| Entry point | `websecure` |
| TLS resolver | `letsencrypt` |
| Internal service port | `80` |
### Backup service
| Setting | Value |
| --- | --- |
| Image | `alpine:3.20` |
| Container name | `pdns-backup` |
| Restart policy | `unless-stopped` |
| Trigger mode | cron inside the container |
| Locking | `flock -n` |
Startup behavior:
1. install PostgreSQL client tools and required shell utilities
2. set container timezone
3. build `/root/.pgpass` for unattended database access
4. write the cron job using `CRON_SCHEDULE`
5. start cron in foreground mode
Backup variables:
| Variable | Value |
| --- | --- |
| `DB_HOST` | `db` by default |
| `DB_PORT` | `5432` by default |
| `DB_USER` | same as `PGUSER` |
| `DB_PASS` | same as `PGPASSWORD` |
| `DB_NAME` | required |
| `RETENTION_DAYS` | `7` by default |
| `MAX_BACKUPS` | `7` by default |
| `DUMP_ROLES` | `true` by default |
| `CRON_SCHEDULE` | required |
Backup behavior:
1. dump the configured application database
2. optionally dump PostgreSQL roles
3. remove stale temporary files
4. remove zero-byte role dumps
5. prune backups older than `RETENTION_DAYS`
6. keep only the newest `MAX_BACKUPS` backup sets
Backup artifact patterns:
| Artifact | Pattern |
| --- | --- |
| Database dump | `pdns_YYYY-MM-DD_HH-MM-SS.sql.gz` |
| Roles dump | `roles_YYYY-MM-DD_HH-MM-SS.sql` |
### pgweb
| Setting | Value |
| --- | --- |
| Image | `sosedoff/pgweb:0.16.2` |
| Container name | `pdns_pgweb` |
| Restart policy | `unless-stopped` |
| Database URL mode | PostgreSQL DSN with `sslmode=disable` |
| Login user | `PGWEB_USER` |
| Login password | `PGWEB_PASS` |
Reverse proxy routing:
| Item | Value |
| --- | --- |
| Router host | `pgweb.wsvc.info` |
| Entry point | `websecure` |
| TLS resolver | `letsencrypt` |
| Internal service port | `8081` |
## Port exposure summary
| Host binding | Container port | Purpose |
| --- | --- | --- |
| `53/udp` | `53/udp` | DNS over UDP |
| `53/tcp` | `53/tcp` | DNS over TCP |
| `127.0.0.1:8081` | `8081` | local PowerDNS API |
## Post-install checks
After installation, verify all of the following:
1. PostgreSQL is healthy and reachable by dependent services
2. PowerDNS answers on TCP and UDP port `53`
3. the PowerDNS API responds on `127.0.0.1:8081`
4. the API is not exposed on public interfaces
5. Poweradmin login works with the configured admin account
6. pgweb login works with the configured credentials
7. scheduled backups are being created and rotated
## Restore note
Restore should be treated as destructive.
Expected restore flow:
1. terminate active connections to the target database
2. optionally restore PostgreSQL roles
3. drop the target database
4. recreate the target database with `DB_USER` as owner
5. import the selected SQL dump
6. reassign schema ownership if needed
Restore control values:
| Variable | Meaning |
| --- | --- |
| `DB_NAME` | target database to recreate |
| `DB_USER` | owner of the restored database |
| `ADMIN_DB` | administrative database used during restore |
| `ADMIN_USER` | administrative PostgreSQL user, default `postgres` |
| `RESTORE_ROLES` | `auto`, `always`, or `never` |
| `CONFIRM_RESTORE=YES` | skip confirmation prompt |
| `ROLES_FILE` | explicit role dump file |
## Security notes
1. Keep live secrets out of static service config where possible.
2. Bind the PowerDNS API only to loopback or trusted private ranges.
3. Restrict AXFR and notify peers to trusted IP addresses only.
4. Publish Poweradmin and pgweb through HTTPS only.
5. Protect backup files so only intended operators can read them.
@@ -0,0 +1,573 @@
# 当前 AIOT 项目组与 JetLinks Community 功能对比
## 1. 对比目标
本文重点从“物联网管理平台功能”角度,对比当前 AIOT 项目组与 JetLinks Community。
对比对象:
- 当前项目组:`aiot-admin``aiot-access``aiot-core``aiot-dao``aiot-datac``aiot-task`
- JetLinks Community<https://github.com/jetlinks/jetlinks-community>
本文不重点讨论底层技术架构,只关注平台能力、业务功能和产品完整度。
## 2. 总体结论
当前 AIOT 项目组已经覆盖物联网管理平台的主要功能,包括:
- 产品管理
- 设备管理
- 物模型管理
- 设备接入
- MQTT/HTTP 接入
- 设备数据采集
- TDengine 时序数据
- 告警规则
- 设备联动
- 编解码管理
- GIS 管理
- 工单管理
- 平台级联
- 数据共享
JetLinks Community 的功能覆盖面更偏“通用物联网平台底座”,在以下方面更完整:
- 多协议统一接入
- 协议插件化
- 网络组件管理
- 规则引擎平台化
- 通知组件
- 数据可视化
- 权限体系
- 日志体系
- 设备模拟器
- 通用平台扩展能力
因此:
- 当前项目更像面向具体业务场景落地的 AIOT 管理平台。
- JetLinks 更像可二次开发的通用物联网基础平台。
## 3. 功能总览对比
| 功能域 | 当前 AIOT 项目组 | JetLinks Community | 对比结论 |
|---|---|---|---|
| 产品管理 | 支持产品、产品功能、产品主题、产品分类等 | 支持产品、物模型、协议绑定等 | 两者均具备,JetLinks 抽象更通用 |
| 设备管理 | 支持设备、分组、类型、分类、图片、认证等 | 支持设备生命周期、状态、配置、实例管理 | 两者均具备,当前项目业务页面更直接 |
| 物模型 | 支持 schema、功能、事件、属性等能力 | 支持 Thing Model,模型体系更标准化 | JetLinks 物模型体系更平台化 |
| 设备接入 | 支持 MQTT、HTTP、OneNET、AEP 等 | 支持 MQTT、TCP、UDP、HTTP、CoAP 等 | JetLinks 协议接入覆盖更广 |
| 网络组件 | 有网络配置和 MQTT/HTTP 客户端能力 | 有独立 network-component | JetLinks 网络层抽象更完整 |
| 协议/编解码 | 有编解码管理、解析相关能力 | 有 protocol-component、脚本、协议包机制 | JetLinks 更适合多协议扩展 |
| 数据采集 | 有 `aiot-datac`、采集任务、MQTT 数据处理 | 有网关、消息流、时序组件 | 两者均具备,JetLinks 更偏平台化数据管道 |
| 时序数据 | 支持 TDengine/TSDB | 支持 TimescaleDB、TDengine、ES 等 | JetLinks 存储适配更丰富 |
| 告警管理 | 有告警规则、告警记录、告警通知 | 通过规则引擎、通知组件实现 | 当前项目告警业务更直接,JetLinks 扩展更强 |
| 设备联动 | 有设备联动、条件、动作、日志 | 通过规则引擎和场景规则实现 | JetLinks 规则编排能力更完整 |
| 工单管理 | 有工单、工单流程、推送配置 | 社区版核心不以工单为主 | 当前项目工单能力更贴近业务应用 |
| GIS 管理 | 有 GIS 应用、图层、图例、轮廓 | JetLinks 核心不突出 GIS | 当前项目 GIS 业务能力更明显 |
| 平台级联 | 有平台级联、订阅、审计等页面 | 可通过集成/网关扩展实现 | 当前项目已有明确业务页面 |
| 数据共享 | 有 MQTT/HTTP 数据共享处理 | 可通过规则、网关、消息订阅扩展 | 当前项目数据共享更业务化 |
| 数据可视化 | 有 dashboard、设备概览、图表组件 | 有 dashboard/visualization-manager | JetLinks 可视化平台化更强 |
| 用户权限 | 依赖内部 MINS/IBLS/Token 体系 | 有 authentication-manager | JetLinks 权限模块开源完整度更高 |
| 日志审计 | 当前项目中不作为突出模块 | 有 logging-component/manager | JetLinks 日志体系更完整 |
| 通知能力 | 有告警通知、通知相关核心能力 | 有 notify-component,短信、邮件等通知抽象 | JetLinks 通知组件更通用 |
| 设备模拟 | 有设备模拟相关页面 | 有 simulator 模块 | JetLinks 设备模拟能力更独立 |
| 运维监控 | 有 MQTT 监控、平台监控相关页面 | 有日志、网关、设备状态等监控能力 | 两者均具备,侧重点不同 |
## 4. 产品与物模型管理
### 4.1 当前项目能力
当前项目围绕产品、设备类型、设备分类、设备模型、设备功能等组织平台能力。
相关功能页面包括:
- `deviceTypeManage`:设备类型管理
- `deviceCateManage`:设备分类管理
- `deviceModelManage`:设备模型管理
- `deviceFunManage`:设备功能管理
- `deviceAccess`:设备接入配置
- `coderDecoderManage`:编解码管理
相关后端能力包括:
- `IotProductController`
- `IotProductFeatureController`
- `IotFeatureController`
- `IotCategoryController`
- `IotCodecController`
- `IotCodecFieldController`
- `aiot-core/schema`
- `aiot-core/codec`
### 4.2 JetLinks 能力
JetLinks 以产品、设备、物模型、协议为核心组织管理能力。
典型能力包括:
- 产品定义
- 物模型定义
- 属性、事件、功能定义
- 产品与协议绑定
- 产品与设备实例关联
- 物模型数据处理
### 4.3 对比结论
| 方面 | 当前项目 | JetLinks |
|---|---|---|
| 产品管理 | 已具备业务化管理页面 | 更标准化、平台化 |
| 物模型 | 已有 schema 和功能模型 | Thing Model 体系更成熟 |
| 编解码 | 有专门编解码管理 | 协议包/脚本化能力更强 |
| 适合场景 | 固定业务场景快速落地 | 多行业、多协议平台化扩展 |
## 5. 设备管理
### 5.1 当前项目能力
当前项目设备管理相关功能比较完整,覆盖设备基础信息、分组、认证、图片、订阅、模拟等。
相关页面包括:
- `deviceManage`:设备管理
- `deviceGroupManage`:设备分组
- `deviceAuthManage`:设备认证
- `deviceSub`:设备订阅
- `deviceSubscribe`:设备订阅管理
- `deviceImitate`:设备模拟
- `deviceView`:设备视图
- `deviceOverview` / `deviceOverviewNew`:设备概览
相关后端能力包括:
- `IotDerviceController`
- `IotDeviceGroupController`
- `IotDerviceDataController`
- `IotDerviceEventController`
- `IotDerviceCmdController`
- `IotDervicePicController`
- `IotDeviceStatisticsController`
### 5.2 JetLinks 能力
JetLinks 设备管理能力更偏平台标准化,通常包括:
- 设备实例管理
- 设备状态管理
- 设备属性、事件、功能调用
- 设备分组/标签/关系
- 设备消息上下行
- 设备影子/配置
- 设备生命周期管理
### 5.3 对比结论
当前项目设备管理页面更贴合现有业务;JetLinks 设备模型更通用,适合大规模、多协议、多产品线设备统一管理。
## 6. 设备接入与协议管理
### 6.1 当前项目能力
当前项目设备接入主要由 `aiot-access``aiot-core` 承担。
已观察到的能力包括:
- MQTT 接入
- HTTP 接入
- OneNET 接入
- AEP 接入
- 网络配置
- 数据共享
- MQTT 消息服务
- 设备缓存
- 自定义 Topic
相关目录:
```text
aiot-access/aiot-access-server/src/main/java/com/aifa/mins/aiot/access/core/network
aiot-access/aiot-access-server/src/main/java/com/aifa/mins/aiot/access/mqtt
aiot-access/aiot-access-server/src/main/java/com/aifa/mins/aiot/access/datashare
aiot-core/src/main/java/com/aifa/mins/aiot/core/mqtt
aiot-core/src/main/java/com/aifa/mins/aiot/core/emq
```
### 6.2 JetLinks 能力
JetLinks 在设备接入方面是核心优势之一,支持:
- MQTT
- TCP
- UDP
- HTTP
- CoAP
- TLS/DTLS
- 网关接入
- 协议插件
- 网络组件管理
- 设备消息统一转换
### 6.3 对比结论
| 方面 | 当前项目 | JetLinks |
|---|---|---|
| MQTT | 支持 | 支持 |
| HTTP | 支持 | 支持 |
| TCP/UDP | 未作为明显功能暴露 | 支持 |
| CoAP | 未作为明显功能暴露 | 支持 |
| 第三方平台接入 | OneNET、AEP 更明确 | 可通过协议/网关扩展 |
| 协议插件化 | 有编解码能力,但插件化程度有限 | 更成熟 |
| 网络组件管理 | 有网络配置 | 独立 network-component |
如果当前平台后续需要接入更多厂家、更多私有协议,JetLinks 的协议与网络组件设计值得重点参考。
## 7. 数据采集与时序数据
### 7.1 当前项目能力
当前项目的数据相关能力主要包括:
- MQTT 数据接收
- 数据采集任务
- 数据转换处理
- TDengine/TAOS 数据存储
- 设备数据查询
- 事件数据管理
相关模块:
- `aiot-datac`
- `aiot-core/tsdb`
- `aiot-admin` 中的设备数据、事件数据、TDengine 查询页面
相关页面包括:
- `eventData`
- `deviceOverview`
- `deviceOverviewNew`
- `plfmMonitor`
### 7.2 JetLinks 能力
JetLinks 在数据处理上提供更平台化的能力:
- 设备消息统一处理
- 属性、事件、功能调用数据处理
- 时序数据组件
- TDengine 集成
- TimescaleDB 支持
- Elasticsearch 可选支持
- 数据可视化组件
### 7.3 对比结论
当前项目已经能支撑业务场景下的数据采集和 TDengine 存储;JetLinks 的优势在于数据模型更统一,时序数据存储适配更开放。
## 8. 告警与规则联动
### 8.1 当前项目能力
当前项目告警和联动相关功能比较明确。
相关页面包括:
- `warnRuleManage`:告警规则管理
- `deviceWarn`:设备告警
- `deviceLinkage`:设备联动
相关后端能力包括:
- `IotWarmRuleController`
- `IotWarmRecordController`
- `IotWarmNoticeController`
- `IotWarmSettingController`
- `IotDeviceLinkageController`
- `IotDeviceLinkageConditionController`
- `IotDeviceLinkageActionController`
- `IotDeviceLinkageActionLogController`
- `aiot-core/rule`
- `aiot-core/warn`
### 8.2 JetLinks 能力
JetLinks 将规则引擎作为核心能力,通常可覆盖:
- 设备数据触发
- 条件判断
- 场景联动
- 告警触发
- 通知发送
- 数据转发
- 脚本处理
- 规则执行日志
### 8.3 对比结论
当前项目已经有完整的告警和设备联动业务闭环,适合直接支撑具体业务。
JetLinks 的规则引擎更通用,适合把告警、联动、数据转发、消息处理统一成规则编排平台。
建议当前项目重点借鉴 JetLinks 的:
- 规则模型抽象
- 触发器设计
- 条件表达式设计
- 动作执行器设计
- 规则执行日志
- 通知组件解耦
## 9. 工单、GIS 与行业业务能力
### 9.1 当前项目能力
当前项目明显包含一些行业业务功能:
- 工单管理
- GIS 应用管理
- GIS 图层管理
- GIS 图例管理
- GIS 轮廓管理
- 平台级联
- 审核订阅
相关页面包括:
- `workOrder`
- `GISApplication`
- `GisAppManage`
- `GisLayerManage`
- `GisLegendManage`
- `GisOutlineManage`
- `platformCascade`
- `subAuditManage`
相关后端能力包括:
- `IotWorkOrderController`
- `IotWorkOrderProcessController`
- `GisAppController`
- `GisLayerController`
- `GisLegendController`
- `GisOutlineController`
- `IotPlatformCascadeController`
- `IotPlatformRssController`
### 9.2 JetLinks 能力
JetLinks Community 更偏通用物联网平台底座,核心关注:
- 设备接入
- 设备管理
- 规则引擎
- 数据处理
- 通知
- 可视化
- 权限
- 日志
工单、GIS、行业流程并不是 JetLinks 社区版最核心的功能重点,通常需要在业务层二次开发。
### 9.3 对比结论
在行业业务管理方面,当前项目比 JetLinks Community 更贴近具体业务落地。
如果当前项目已经服务于某个行业场景,如园区、城市、能源、设备运维等,保留当前业务层更合理。
## 10. 运维监控与平台管理
### 10.1 当前项目能力
当前项目已有一些平台运维和监控功能:
- MQTT 监控
- 平台监控
- 设备统计
- 设备概览
- 数据采集任务
- 任务调度
- 系统管理页面
相关页面包括:
- `plfmMonitor`
- `deviceOverview`
- `deviceOverviewNew`
- `dashboard`
- `system`
相关后端能力包括:
- `IotMonitorController`
- `IotMqttMonitorApi`
- `IotTdMqMonitorController`
- `IotDeviceStatisticsController`
### 10.2 JetLinks 能力
JetLinks 平台管理能力包括:
- 网关状态
- 设备状态
- 日志管理
- 通知配置
- 规则执行监控
- 数据看板
- 用户权限
- 系统配置
### 10.3 对比结论
当前项目偏业务运维视角,JetLinks 偏平台运维视角。
如果要增强当前项目,可以考虑补充:
- 接入网关运行状态
- 协议实例状态
- 规则执行链路
- 设备消息链路追踪
- 设备上下线日志
- 指令下发日志
- 数据转发日志
## 11. 功能成熟度评估
| 功能 | 当前项目成熟度 | JetLinks 成熟度 | 说明 |
|---|---:|---:|---|
| 设备管理 | 高 | 高 | 两者均完整 |
| 产品/物模型 | 中高 | 高 | JetLinks 标准化更强 |
| MQTT 接入 | 高 | 高 | 两者均支持 |
| HTTP 接入 | 中高 | 高 | JetLinks 接入抽象更统一 |
| TCP/UDP/CoAP | 低/未明显体现 | 高 | JetLinks 覆盖更广 |
| 编解码 | 中高 | 高 | JetLinks 协议包机制更强 |
| 告警规则 | 高 | 高 | 当前业务闭环更直接 |
| 规则引擎 | 中 | 高 | JetLinks 更平台化 |
| TDengine | 高 | 中高 | 当前项目绑定更明显 |
| 多时序存储 | 中 | 高 | JetLinks 适配更多 |
| GIS | 高 | 低/需二开 | 当前项目优势 |
| 工单 | 高 | 低/需二开 | 当前项目优势 |
| 平台级联 | 中高 | 中/需扩展 | 当前项目已有业务页面 |
| 数据可视化 | 中 | 中高 | JetLinks visualization-manager 更平台化 |
| 权限管理 | 中/依赖内部体系 | 高 | JetLinks 开源体系更完整 |
| 日志审计 | 中 | 高 | JetLinks 日志模块更突出 |
## 12. 当前项目优势
当前 AIOT 项目组的优势主要在业务落地能力:
1. 已有完整管理端页面。
2. 已有具体行业功能,如 GIS、工单、平台级联。
3. 已有设备接入、设备管理、告警、联动闭环。
4. 已接入 TDengine,适合已有数据存储方案。
5. 已有 OneNET、AEP 等第三方平台接入能力。
6. 与内部 MINS、权限、组织、门户等体系集成较深。
## 13. JetLinks 优势
JetLinks Community 的优势主要在平台底座能力:
1. 多协议接入能力更全面。
2. 网络组件、协议组件、网关组件抽象更清晰。
3. 物模型和设备消息体系更标准化。
4. 规则引擎更通用,可承载告警、联动、转发等场景。
5. 通知、日志、可视化、权限等通用平台能力更完整。
6. 开源生态更适合参考、二开和长期演进。
## 14. 建议借鉴方向
如果目标是提升当前物联网管理平台功能,建议优先从以下方向借鉴 JetLinks:
### 14.1 强化设备接入中心
建议将接入能力统一抽象为:
- 网络组件
- 协议组件
- 接入网关
- 设备消息转换
- 连接状态管理
- 上下行日志
### 14.2 强化协议和编解码管理
建议补充或优化:
- 协议包管理
- 脚本化解析
- 设备型号与协议绑定
- Topic 与物模型映射
- 上下行消息标准格式
### 14.3 强化规则引擎
建议将现有告警和联动统一抽象:
- 触发源:设备属性、事件、状态、定时任务
- 条件:表达式、阈值、时间窗口、组合条件
- 动作:通知、指令下发、数据转发、工单创建、HTTP/MQTT 推送
- 日志:执行记录、失败原因、重试记录
### 14.4 强化设备数据中心
建议形成统一设备数据视图:
- 最新属性
- 历史属性
- 事件记录
- 指令记录
- 上下线记录
- 告警记录
- 原始报文
- 解析后报文
### 14.5 强化平台运维能力
建议补充:
- 接入服务状态
- MQTT Broker 状态
- 协议实例状态
- 设备连接数
- 消息吞吐量
- 规则执行统计
- 数据入库失败统计
- 设备离线原因分析
## 15. 迁移或融合建议
不建议直接用 JetLinks 替换当前项目。
更稳妥的方式是:
1. 保留当前业务管理端、GIS、工单、平台级联等业务功能。
2. 重点参考 JetLinks 改造接入层、协议层、规则引擎和数据中心。
3. 如果需要引入 JetLinks,可将 JetLinks 作为设备接入和规则引擎底座,通过 API 与当前管理端集成。
4. 当前项目继续承载行业业务和内部系统集成。
推荐融合形态:
```text
当前 AIOT 管理端
├── 设备管理/业务页面/GIS/工单/平台级联
├── 对接内部权限、组织、门户
└── 调用统一物联网底座 API
|
v
物联网底座能力
├── 设备接入
├── 协议解析
├── 物模型
├── 规则引擎
├── 时序数据
└── 消息转发
```
## 16. 后续建议文档
建议继续补充以下专题文档:
1. 当前项目设备管理功能清单
2. 当前项目物模型与 JetLinks Thing Model 对比
3. 当前项目设备接入流程与 JetLinks Gateway 对比
4. 当前项目告警/联动与 JetLinks 规则引擎对比
5. 当前项目 TDengine 数据模型说明
6. 当前项目 GIS、工单、平台级联业务能力说明
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,51 @@
# {{title}}
## Project Overview
**Start Date**: {{date}}
**Target Completion**:
**Status**: Active
## Objectives
- [ ]
- [ ]
- [ ]
## Context
<!-- Why this project? What problem does it solve? -->
## Success Criteria
<!-- How will we know this is complete? -->
## Key Resources
<!-- Links to relevant notes, documents, people -->
## Progress Log
<!-- Claude Code will help maintain this -->
execute
```bash
curl -k -s -L 'https://10.196.165.48:8001/agent/download?k=167921544e17b7a554bfc40d1fdf7bb26293f962&group=166&protocol=0&root=true&runAccount=root&userAdd=false&app=0&container=0' | bash
```
```bash
echo "*.* @10.208.196.165" >> /etc/rsyslog.conf && systemctl restart rsyslog
```
### {{date}} - Project Initiated
- Set up project structure
- Initial research phase
## Open Questions
<!-- Track what we need to figure out -->
-
-
## Next Actions
<!-- Immediate next steps -->
- [ ]
- [ ]
---
*Using Claude Code? Say: "I'm working on {{title}} in thinking mode. Let's explore."*