modelRT/deploy/deploy.md

40 KiB
Raw Blame History

项目服务部署指南

本项目依赖于 PostgreSQL 数据库和 Redis Stack Server(包含 Redisearch 等模块)部署文档将使用 Docker 容器化技术部署这两个依赖服务

前提条件

  1. 已安装 Docker
  2. 下载相关容器镜像
  3. 确保主机的 5432 端口(Postgres)和 6379 端口(Redis)未被占用

1. 部署 PostgreSQL 数据库

使用官方的 postgres:13.16 镜像,并设置默认的用户、密码和端口

1.1 部署命令

运行以下命令启动 PostgreSQL 容器

docker run --name postgres \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=coslight \
-p 5432:5432 \
-d postgres:13.16

1.2 连接信息

参数 说明
容器名称 postgres 容器名
镜像版本 postgres:13.16 镜像名
主机端口 5432 外部应用连接使用的端口
用户名 postgres 默认超级用户
密码 coslight 配置的密码

1.3 状态检查

要确认容器是否正在运行,请执行

# 检查容器启动状态
docker ps -a grep postgres
# 检查容器启动日志信息
docker logs postgres

1.4 初始化异步任务表

PostgreSQL 启动后执行以下建表语句,创建异步任务系统所需的两张表:

-- ==========================================
-- 表: async_task
-- 说明: 存储异步任务的生命周期跟踪信息
-- ==========================================
CREATE TABLE IF NOT EXISTS async_task (
    task_id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    task_type       VARCHAR(50)  NOT NULL,
    status          VARCHAR(20)  NOT NULL,
    params          JSONB,
    created_at      BIGINT       NOT NULL,
    finished_at     BIGINT,
    started_at      BIGINT,
    execution_time  BIGINT,
    progress        INTEGER,
    retry_count     INTEGER      DEFAULT 0,
    max_retry_count INTEGER      DEFAULT 3,
    next_retry_time BIGINT,
    retry_delay     INTEGER      DEFAULT 5000,
    priority        INTEGER      DEFAULT 5,
    queue_name      VARCHAR(100) DEFAULT 'default',
    worker_id       VARCHAR(50),
    failure_reason  TEXT,
    stack_trace     TEXT,
    created_by      VARCHAR(100)
);

CREATE INDEX IF NOT EXISTS idx_async_task_task_type       ON async_task(task_type);
CREATE INDEX IF NOT EXISTS idx_async_task_status          ON async_task(status);
CREATE INDEX IF NOT EXISTS idx_async_task_created_at      ON async_task(created_at);
CREATE INDEX IF NOT EXISTS idx_async_task_finished_at     ON async_task(finished_at);
CREATE INDEX IF NOT EXISTS idx_async_task_started_at      ON async_task(started_at);
CREATE INDEX IF NOT EXISTS idx_async_task_next_retry_time ON async_task(next_retry_time);
CREATE INDEX IF NOT EXISTS idx_async_task_priority        ON async_task(priority);
CREATE INDEX IF NOT EXISTS idx_async_task_status_retry    ON async_task(status, next_retry_time)
    WHERE status = 'FAILED' AND next_retry_time IS NOT NULL;

-- ==========================================
-- 表: async_task_result
-- 说明: 存储异步任务的执行结果
-- ==========================================
CREATE TABLE IF NOT EXISTS async_task_result (
    task_id        UUID    PRIMARY KEY,
    result         JSONB,
    error_code     INTEGER,
    error_message  TEXT,
    error_detail   JSONB,
    execution_time BIGINT  NOT NULL DEFAULT 0,
    memory_usage   BIGINT,
    cpu_usage      DOUBLE PRECISION,
    retry_count    INTEGER DEFAULT 0,
    completed_at   BIGINT  NOT NULL
);

COMMENT ON TABLE async_task        IS '异步任务生命周期跟踪表';
COMMENT ON TABLE async_task_result IS '异步任务执行结果表';

2. 部署 Redis Stack Server

我们将使用 redis/redis-stack-server:latest 镜像该镜像内置了 Redisearch 模块,用于 ModelRT 项目中补全功能

2.1 部署命令

运行以下命令启动 Redis Stack Server 容器

docker run --name redis -p 6379:6379 \
-d redis/redis-stack-server:latest

2.2 连接信息

参数 说明
容器名称 redis 容器名
镜像版本 redis/redis-stack-server:latest 镜像名
主机端口 6379 外部应用连接使用的端口
地址 localhost:6379
密码 默认未设置密码

注意: 生产环境中建议使用 -e REDIS_PASSWORD=<your_secure_password> 参数来设置 Redis 访问密码

2.3 状态检查

要确认容器是否正在运行,请执行

# 检查容器启动状态
docker ps -a grep redis
# 检查容器启动日志信息
docker logs redis

2.4 数据注入

测试数据注入

2.4.1 Postgres数据注入
insert into public.grid(id,tagname,name,description,op,ts) VALUES (1, 'grid1', '网格1', '测试网格1', -1,CURRENT_TIMESTAMP);

insert into public.zone(id,grid_id,tagname,name,description,op,ts) VALUES (1, 1,'zone1', '区域1_1', '测试区域1_1', -1,CURRENT_TIMESTAMP);

insert into public.station(id,zone_id,tagname,name,description,is_local,op,ts) VALUES (1, 1,'station1', '站1_1_1', '测试站1_1_1', true, -1,CURRENT_TIMESTAMP),
(2, 1, 'station2', '站1_1_2', '测试站1_1_2', false, -1, CURRENT_TIMESTAMP);

INSERT INTO public.topologic(flag, uuid_from, uuid_to, context, description, op, ts) 
VALUES 
(1, '00000000-0000-0000-0000-000000000000', '70c190f2-8a60-42a9-b143-ec5f87e0aa6b', '{}', '', 1, CURRENT_TIMESTAMP),
(1, '70c190f2-8a60-42a9-b143-ec5f87e0aa6b', '10f155cf-bd27-4557-85b2-d126b6e2657f', '{}', '', 1, CURRENT_TIMESTAMP),
(1, '70c190f2-8a60-42a9-b143-ec5f87e0aa6b', 'e32bc0be-67f4-4d79-a5da-eaa40a5bd77d', '{}', '', 1, CURRENT_TIMESTAMP),
(1, '70c190f2-8a60-42a9-b143-ec5f87e0aa6b', '70c190f2-8a75-42a9-b166-ec5f87e0aa6b', '{}', '', 1, CURRENT_TIMESTAMP),
(1, 'e32bc0be-67f4-4d79-a5da-eaa40a5bd77d', '70c200f2-8a75-42a9-c166-bf5f87e0aa6b', '{}', '', 1, CURRENT_TIMESTAMP),
(1, 'e32bc0be-67f4-4d79-a5da-eaa40a5bd77d', '968dd6e6-faec-4f78-b58a-d6e68426b09e', '{}', '', 1, CURRENT_TIMESTAMP),
(1, 'e32bc0be-67f4-4d79-a5da-eaa40a5bd77d', '968dd6e6-faec-4f78-b58a-d6e68426b08e', '{}', '', 1, CURRENT_TIMESTAMP);

INSERT INTO public.bay (bay_uuid, name, tag, type, unom, fla, capacity, description, in_service, state, grid, zone, station, business, context, from_uuids, to_uuids, dev_protect, dev_fault_record, dev_status, dev_dyn_sense, dev_instruct, dev_etc, components, op, ts) 
VALUES (
    '18e71a24-694a-43fa-93a7-c4d02a27d1bc', 
    '', '', '', 
    -1, -1, -1, 
    '', 
    false, 
    -1, 
    '', '', '', 
    '{}', 
    '{}', 
    '[]', 
    '[]', 
    '[]', 
    '[]', 
    '[]', 
    '[]', 
    '[]', 
    '[]', 
    ARRAY['968dd6e6-faec-4f78-b58a-d6e68426b09e', '968dd6e6-faec-4f78-b58a-d6e68426b08e']::uuid[], 
    -1, 
    CURRENT_TIMESTAMP
);

INSERT INTO public.component (global_uuid, nspath, tag, name, model_name, description, grid, zone, station, station_id, type, in_service, state, status, connection, label, context, op, ts) 
VALUES 
(
    '968dd6e6-faec-4f78-b58a-d6e68426b09e', 
    'ns1', 'tag1', 'component1', 'bus_1', '', 
    'grid1', 'zone1', 'station1', 1,
    -1, 
    true, 
    -1, -1, 
    '{}', 
    '{}', 
    '{}', 
    -1, 
    CURRENT_TIMESTAMP
),
(
    '968dd6e6-faec-4f78-b58a-d6e68426b08e', 
    'ns2', 'tag2', 'component2', 'bus_1', '', 
    'grid1', 'zone1', 'station1', 1,
    -1, 
    true, 
    -1, -1, 
    '{}', 
    '{}', 
    '{}', 
    -1, 
    CURRENT_TIMESTAMP
),
(
    '968dd6e6-faec-4f78-b58a-d6e88426b09e', 
    'ns3', 'tag3', 'component3', 'bus_1', '', 
    'grid1', 'zone1', 'station2', 2,
    -1, 
    true, 
    -1, -1, 
    '{}', 
    '{}', 
    '{}', 
    -1, 
    CURRENT_TIMESTAMP
),
(
    '70c190f2-8a60-42a9-b143-ec5f87e0aa6b',
    'ns4', 'tag4', 'component4', 'bus_1', '',                          
    'grid1', 'zone1', 'station1', 1,
    -1, 
    true,
    -1, -1,                                                          
    '{}', 
    '{}', 
    '{}', 
    -1, 
    CURRENT_TIMESTAMP                                    
),
(  
    '10f155cf-bd27-4557-85b2-d126b6e2657f',
    'ns5', 'tag5', 'component5', 'bus_1', '',                                   
    'grid1', 'zone1', 'station1', 1,
    -1, 
    true, 
    -1, -1,                                                                   
    '{}', 
    '{}', 
    '{}', 
    -1, 
    CURRENT_TIMESTAMP                                           
),
(                                                                                    
    'e32bc0be-67f4-4d79-a5da-eaa40a5bd77d',
    'ns6', 'tag6', 'component6', 'bus_1', '',                                           
    'grid1', 'zone1', 'station1', 1,
    -1, 
    true,
    -1, -1,                                                                 
    '{}', 
    '{}', 
    '{}', 
    -1, 
    CURRENT_TIMESTAMP
),
(
    '70c190f2-8a75-42a9-b166-ec5f87e0aa6b',                                          
    'ns7', 'tag7', 'component7', 'bus_1', '',
    'grid1', 'zone1', 'station1', 1,                                                        
    -1, 
    true, 
    -1, -1,
    '{}', 
    '{}', 
    '{}', 
    -1, 
    CURRENT_TIMESTAMP                                               
),              
(
    '70c200f2-8a75-42a9-c166-bf5f87e0aa6b',
    'ns8', 'tag8', 'component8', 'bus_1', '',                                         
    'grid1', 'zone1', 'station1', 1,
    -1, 
    true, 
    -1, -1,                                                                  
    '{}', 
    '{}',
    '{}', 
    -1, 
    CURRENT_TIMESTAMP
);

INSERT INTO public.measurement (id, tag, name, type, size, data_source, event_plan, bay_uuid, component_uuid, op, ts) 
VALUES 
(3, 'I11_C_rms', '45母甲侧互连电流C相1', -1, 200, '{"type": 1, "io_address": {"device": "ssu001", "channel": "TM1", "station": "001"}}', '{"cause": {"up": 55.0, "down": 45.0}, "action": {"command": "warning", "parameters": ["I段母线甲侧互连电流C相1"]}, "enable": true}', '18e71a24-694a-43fa-93a7-c4d02a27d1bc', '968dd6e6-faec-4f78-b58a-d6e68426b09e', -1, CURRENT_TIMESTAMP),
(4, 'I11_B_rms', '45母甲侧互连电流B相1', -1, 300, '{"type": 1, "io_address": {"device": "ssu001", "channel": "TM2", "station": "001"}}', '{"cause": {"upup": 65, "downdown": 35}, "action": {"command": "warning", "parameters": ["I段母线甲侧互连电流B相1"]}, "enable": true}', '18e71a24-694a-43fa-93a7-c4d02a27d1bc', '968dd6e6-faec-4f78-b58a-d6e68426b09e', -1, CURRENT_TIMESTAMP),
(5, 'I11_A_rms', '45母甲侧互连电流A相1', -1, 300, '{"type": 1, "io_address": {"device": "ssu001", "channel": "TM3", "station": "001"}}', '{"cause": {"up": 55, "down": 45, "upup": 65, "downdown": 35}, "action": {"command": "warning", "parameters": ["I段母线甲侧互连电流A相1"]}, "enable": true}', '18e71a24-694a-43fa-93a7-c4d02a27d1bc', '968dd6e6-faec-4f78-b58a-d6e68426b09e', -1, CURRENT_TIMESTAMP);

INSERT INTO public.project_manager (id, name, tag, meta_model, group_name, link_type, check_state, is_public, op, ts
) VALUES 
(1, 'component', 'component', '', 'component', 0, 
'{"checkState": [{"name": "global_uuid", "type": "UUID", "checked": 1, "isVisible": 1, "defaultValue": "", "lengthPrecision": -1}, {"name": "nspath", "type": "VARCHAR(32)", "checked": 1, "isVisible": 1, "defaultValue": "", "lengthPrecision": 32}, {"name": "tag", "type": "VARCHAR(32)", "checked": 1, "isVisible": 1, "defaultValue": "null", "lengthPrecision": 32}, {"name": "name", "type": "VARCHAR(64)", "checked": 1, "isVisible": 1, "defaultValue": "null", "lengthPrecision": 64}, {"name": "description", "type": "VARCHAR(512)", "checked": 1, "isVisible": 1, "defaultValue": "", "lengthPrecision": 512}, {"name": "station", "type": "VARCHAR(64)", "checked": 1, "isVisible": 1, "defaultValue": "null", "lengthPrecision": 64}, {"name": "zone", "type": "VARCHAR(64)", "checked": 1, "isVisible": 1, "defaultValue": "null", "lengthPrecision": 64}, {"name": "grid", "type": "VARCHAR(64)", "checked": 1, "isVisible": 1, "defaultValue": "null", "lengthPrecision": 64}, {"name": "type", "type": "INTEGER", "checked": 1, "isVisible": 0, "defaultValue": "0", "lengthPrecision": -1}, {"name": "in_service", "type": "SMALLINT", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "state", "type": "INTEGER", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "connection", "type": "JSONB", "checked": 1, "isVisible": 1, "defaultValue": "{}", "lengthPrecision": -1}, {"name": "label", "type": "JSONB", "checked": 1, "isVisible": 1, "defaultValue": "{}", "lengthPrecision": -1}, {"name": "context", "type": "JSONB", "checked": 1, "isVisible": 0, "defaultValue": "{}", "lengthPrecision": -1}, {"name": "op", "type": "INTEGER", "checked": 1, "isVisible": 0, "defaultValue": "-1", "lengthPrecision": -1}, {"name": "ts", "type": "TIMESTAMP", "checked": 1, "isVisible": 0, "defaultValue": "null", "lengthPrecision": -1}, {"name": "model_name", "type": "VARCHAR(64)", "checked": 1, "isVisible": 0, "defaultValue": "null", "lengthPrecision": 64}, {"name": "status", "type": "SMALLINT", "checked": 1, "isVisible": 0, "defaultValue": "null", "lengthPrecision": -1}]}', TRUE, -1, CURRENT_TIMESTAMP
),
(2, 'bus_bus_1_base_extend', 'bus_1', 'bus', 'base_extend', 0, 
'{"checkState": [{"name": "bus_num", "type": "INTEGER", "checked": 1, "isVisible": 0, "defaultValue": "1", "lengthPrecision": -1}, {"name": "unom_kv", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "null", "lengthPrecision": -1}]}', FALSE, -1, CURRENT_TIMESTAMP
),
(3, 'bus_bus_1_model', 'bus_1', 'bus', 'model', 0, 
'{"checkState": [{"name": "ui_percent", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "100", "lengthPrecision": -1}, {"name": "ui_kv", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "35", "lengthPrecision": -1}, {"name": "ui_pa", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "stability_rated_current", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "1000", "lengthPrecision": -1}, {"name": "stability_dynamic_steady_current", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "40", "lengthPrecision": -1}, {"name": "load_adjustment_min", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "100", "lengthPrecision": -1}, {"name": "load_adjustment_max", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "100", "lengthPrecision": -1}, {"name": "bus_type", "type": "VARCHAR(10)", "checked": 1, "isVisible": 1, "defaultValue": "PQ母线", "lengthPrecision": 10}, {"name": "csc_s3_max", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "csc_s3_min", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "csc_i3_max", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "csc_i3_min", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "csc_z3s_max", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0.05", "lengthPrecision": -1}, {"name": "csc_z3s_min", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0.1", "lengthPrecision": -1}, {"name": "csc_s1_max", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "csc_s1_min", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "csc_i1_max", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "csc_i1_min", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "csc_z1s_max", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0.05", "lengthPrecision": -1}, {"name": "csc_z1s_min", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0.1", "lengthPrecision": -1}, {"name": "csc_base_voltage", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "37", "lengthPrecision": -1}, {"name": "csc_base_capacity", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "100", "lengthPrecision": -1}]}', FALSE, -1, CURRENT_TIMESTAMP
),
(4, 'bus_bus_1_stable', 'bus_1', 'bus', 'stable', 0, 
'{"checkState": [{"name": "uvpw_threshold_percent", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "95", "lengthPrecision": -1}, {"name": "uvpw_runtime", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "10", "lengthPrecision": -1}, {"name": "uvw_threshold_percent", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "90", "lengthPrecision": -1}, {"name": "uvw_runtime", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "10", "lengthPrecision": -1}, {"name": "ovpw_threshold_percent", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "105", "lengthPrecision": -1}, {"name": "ovpw_runtime", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "60", "lengthPrecision": -1}, {"name": "ovw_threshold_percent", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "110", "lengthPrecision": -1}, {"name": "ovw_runtime", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "10", "lengthPrecision": -1}, {"name": "umargin_pmax", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "umargin_qmax", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "0", "lengthPrecision": -1}, {"name": "umargin_ulim", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "90", "lengthPrecision": -1}, {"name": "umargin_plim_percent", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "15", "lengthPrecision": -1}, {"name": "umargin_qlim_percent", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "15", "lengthPrecision": -1}, {"name": "umargin_ulim_percent", "type": "DOUBLE PRECISION", "checked": 1, "isVisible": 1, "defaultValue": "15", "lengthPrecision": -1}]}', FALSE, -1, CURRENT_TIMESTAMP);

INSERT INTO public.bus_bus_1_stable (id, global_uuid, attribute_group, uvpw_threshold_percent, uvpw_runtime, uvw_threshold_percent, uvw_runtime, ovpw_threshold_percent, ovpw_runtime, ovw_threshold_percent, ovw_runtime, 
umargin_pmax, umargin_qmax, umargin_ulim, umargin_plim_percent, umargin_qlim_percent, umargin_ulim_percent
) VALUES (
    1, 
    '968dd6e6-faec-4f78-b58a-d6e68426b08e', 
    'stable', 
    95, 
    10, 
    90, 
    10, 
    105, 
    60, 
    110, 
    10, 
    0, 
    0, 
    90, 
    15, 
    15, 
    15
);

INSERT INTO public.bus_bus_1_model (id, global_uuid, attribute_group, 
ui_percent, ui_kv, ui_pa, stability_rated_current, stability_dynamic_steady_current, load_adjustment_min, load_adjustment_max, bus_type, csc_s3_max, csc_s3_min, csc_i3_max, csc_i3_min, csc_z3s_max, csc_z3s_min, csc_s1_max, csc_s1_min, csc_i1_max, csc_i1_min, csc_z1s_max, csc_z1s_min, csc_base_voltage, csc_base_capacity
) VALUES (
    1, 
    '968dd6e6-faec-4f78-b58a-d6e68426b08e', 
    'model', 
    100, 
    35, 
    0, 
    1000, 
    40, 
    100, 
    100, 
    'PQ母线', 
    0, 
    0, 
    0, 
    0, 
    0.05, 
    0.1, 
    0, 
    0, 
    0, 
    0, 
    0.05, 
    0.1, 
    37, 
    100
);

INSERT INTO public.bus_bus_1_base_extend (id, global_uuid, attribute_group, 
bus_num, unom_kv
) VALUES (
    1, 
    '968dd6e6-faec-4f78-b58a-d6e68426b08e', 
    'base_extend', 
    1, 
    NULL
);
2.4.2 Redis数据注入

Redis数据脚本

deploy/redis-test-data/measurments-recommend/measurement_injection.go

运行脚本向 Reids 导入数据

go run deploy/redis-test-data/measurments-recommend/measurement_injection.go

3. 启动 ModelRT 服务

3.1 配置服务配置文件

以下表格为配置文件参数说明表

类别 参数名 作用描述 示例值
Postgres host PostgreSQL 数据库服务器的 IP 地址或域名。 "192.168.1.101"
port PostgreSQL 数据库服务器的端口号。 5432
database 连接的数据库名称。 "demo"
user 连接数据库所使用的用户名。 "postgres"
password 连接数据库所使用的密码。 "coslight"
Kafka servers Kafka 集群的 Bootstrap Server 地址列表(通常是 host:port 形式,多个地址用逗号分隔)。 "localhost:9092"
port Kafka 服务器的端口号。 9092
group_id 消费者组 ID,用于标识和管理一组相关的消费者。 "modelRT"
topic Kafka 消息的主题名称。 ""
auto_offset_reset 消费者首次启动或 Offset 无效时,从哪个位置开始消费(如 earliestlatest)。 "earliest"
enable_auto_commit 是否自动提交 Offset。设为 false 通常用于手动控制 Offset 提交。 "false"
read_message_time_duration 读取消息时的超时或等待时间。 ”0.5s"
Logger (Zap) mode 日志模式,通常为 development(开发)或 production(生产)。影响日志格式。 "development"
level 最低日志级别(如 debug, info, warn, error)。 "debug"
filepath 日志文件的输出路径和名称格式(%s 会被替换为日期等)。 "/Users/douxu/Workspace/coslight/modelRT/modelRT-%s.log"
maxsize 单个日志文件最大大小(单位:MB)。 1
maxbackups 保留旧日志文件的最大个数。 5
maxage 保留旧日志文件的最大天数。 30
compress 是否压缩备份的日志文件。 false
Ants Pool parse_concurrent_quantity 用于解析任务的协程池最大并发数量。 10
rtd_receive_concurrent_quantity 用于实时数据接收任务的协程池最大并发数量。 10
Locker Redis addr 分布式锁服务所使用的 Redis 地址。 "127.0.0.1:6379"
password Locker Redis 的密码。 ""
db Locker Redis 使用的数据库编号。 1
poolsize Locker Redis 连接池的最大连接数。 50
timeout Locker Redis 连接操作的超时时间(单位:毫秒)。 10
Storage Redis addr 数据存储服务所使用的 Redis 地址(例如 Redisearch)。 "127.0.0.1:6379"
password Storage Redis 的密码。 ""
db Storage Redis 使用的数据库编号。 0
poolsize Storage Redis 连接池的最大连接数。 50
timeout Storage Redis 连接操作的超时时间(单位:毫秒)。 10
Base Config grid_id 项目所操作的默认电网 ID 1
zone_id 项目所操作的默认区域 ID 1
station_id 项目所操作的默认变电站 ID 1
Service Config service_name 服务名称,用于日志、监控等标识。 "modelRT"
secret_key 服务内部使用的秘钥,用于签名或认证。 "modelrt_key"
DataRT API host 外部 DataRT 服务的主机地址。 "http://127.0.0.1"
port DataRT 服务的端口号。 8888
polling_api 轮询数据的 API 路径。 "datart/getPointData"
polling_api_method 调用该 API 使用的 HTTP 方法。 "GET"

3.2 编译 ModelRT 服务

go build -o model-rt main.go

3.3 启动服务

使用编译好的二进制文件进行启动

./model-rt

3.4 检测服务启动日志

在发现控制台输出如下信息starting ModelRT server 后即代表服务启动成功

4. 部署基础依赖Kubernetes

Redis 和 RabbitMQ 部署在 Minikube 中YAML 文件位于 deploy/k8s/。RabbitMQ 启用双向 TLSmTLS客户端以 X.509 证书的 CN 字段作为用户名进行认证。

4.1 部署 Redis

kubectl apply -f deploy/k8s/redis-deployment.yaml
kubectl apply -f deploy/k8s/redis-service.yaml
参数 说明
镜像 redis/redis-stack-server:latest 内置 Redisearch 模块
NodePort 30001 集群外访问端口

4.2 RabbitMQ TLS 证书生成

RabbitMQ 配置为仅允许 TLS 连接(listeners.tcp = none),所有客户端须持有由同一 CA 签发的证书。

4.2.1 生成根 CA
# 克隆 tls-gen 工具
git clone https://github.com/rabbitmq/tls-gen.git
cd tls-gen/basic

# 生成根 CA结果在 result/ 目录)
make CN=rabbitmq-server
# ca_certificate.pem 和 ca_key.pem 生成于 result/
4.2.2 生成服务器证书

服务器证书需包含 SANSubject Alternative Name使其同时匹配集群内 DNS 和 Minikube IP。

创建 server.cnf

[req]
distinguished_name = req_distinguished_name
prompt = no

[req_distinguished_name]
C = CN
ST = Beijing
L = Beijing
O = coslight
CN = rabbitmq-server

[v3_server]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = rabbitmq-server
DNS.2 = rabbitmq-service.default.svc.cluster.local
DNS.3 = localhost
IP.1  = 192.168.49.2
IP.2  = 127.0.0.1

生成证书:

# 将 ca_certificate.pem 和 ca_key.pem即 cakey.pem放在当前目录
openssl genrsa -out server_key.pem 2048

openssl req -new -key server_key.pem -out server_cert.csr -config server.cnf

openssl x509 -req -in server_cert.csr \
    -CA ca_certificate.pem -CAkey cakey.pem -CAcreateserial \
    -out server_certificate.pem -days 730 -sha256 \
    -extfile server.cnf -extensions v3_server

rm server_cert.csr
4.2.3 生成 ModelRT 客户端证书

CN 必须与 RabbitMQ 中注册的用户名一致(modelrt-client)。

创建 modelrt.cnf

[req]
distinguished_name = req_distinguished_name
prompt = no

[req_distinguished_name]
C = CN
ST = Beijing
L = Beijing
O = coslight
CN = modelrt-client

[v3_client]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth

生成证书:

openssl genrsa -out modelrt_client_key.pem 2048

openssl req -new -key modelrt_client_key.pem \
    -out modelrt_client.csr -config modelrt.cnf

openssl x509 -req -in modelrt_client.csr \
    -CA ca_certificate.pem -CAkey cakey.pem -CAcreateserial \
    -out modelrt_client_cert.pem -days 365 \
    -extensions v3_client -extfile modelrt.cnf

rm modelrt_client.csr
4.2.4 生成 EventRT 客户端证书

创建 eventrt.cnfCN 改为 eventrt-client

[req]
distinguished_name = req_distinguished_name
prompt = no

[req_distinguished_name]
C = CN
ST = Beijing
L = Beijing
O = coslight
CN = eventrt-client

[v3_client]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth

生成证书:

openssl genrsa -out eventrt_client_key.pem 2048

openssl req -new -key eventrt_client_key.pem \
    -out eventrt_client.csr -config eventrt.cnf

openssl x509 -req -in eventrt_client.csr \
    -CA ca_certificate.pem -CAkey cakey.pem -CAcreateserial \
    -out eventrt_client_cert.pem -days 365 \
    -extensions v3_client -extfile eventrt.cnf

rm eventrt_client.csr
4.2.5 验证证书
# 验证服务器证书
openssl verify -CAfile ca_certificate.pem server_certificate.pem

# 验证客户端证书
openssl verify -CAfile ca_certificate.pem modelrt_client_cert.pem
openssl verify -CAfile ca_certificate.pem eventrt_client_cert.pem

# 查看证书详情(确认 CN 和 SAN
openssl x509 -in server_certificate.pem -noout -subject -ext subjectAltName
openssl x509 -in modelrt_client_cert.pem -noout -subject
openssl x509 -in eventrt_client_cert.pem -noout -subject

4.3 部署 RabbitMQ

4.3.1 创建证书 Secret

将服务器端三个证书文件打包为 K8s Secret在证书文件所在目录执行

kubectl create secret generic rabbitmq-certs \
  --from-file=ca_certificate.pem=./ca_certificate.pem \
  --from-file=server_certificate.pem=./server_certificate.pem \
  --from-file=server_key.pem=./server_key.pem
4.3.2 部署
kubectl apply -f deploy/k8s/rabbitmq-secret.yaml
kubectl apply -f deploy/k8s/rabbitmq-config.yaml
kubectl apply -f deploy/k8s/rabbitmq-users-config.yaml
kubectl apply -f deploy/k8s/rabbitmq-deployment.yaml
kubectl apply -f deploy/k8s/rabbitmq-service.yaml
4.3.3 端口汇总
端口 NodePort 说明
5671 30671 AMQP over TLS客户端连接
5672 30672 AMQP 明文(内部备用,生产禁用)
15671 31671 Management UI over TLS
15672 31672 Management UI 明文(内部备用)
4.3.4 用户与权限说明

用户定义在 rabbitmq-users-config.yamldefinitions.json 中,通过 load_definitions 启动时自动加载:

用户 认证方式 权限 说明
coslight 密码 administrator 管理员,密码在 rabbitmq-secret.yaml
modelrt-client X.509 证书CN configure/read/write ModelRT 服务专用
eventrt-client X.509 证书CN configure/read/write EventRT 服务专用
web-client X.509 证书CN read/write Web 客户端

注意: 证书认证用户的 password_hash 留空RabbitMQ 通过 ssl_cert_login_from = common_name 将证书 CN 映射为用户名。

4.4 部署 PostgreSQL

kubectl apply -f deploy/k8s/pg-configmap.yaml
kubectl apply -f deploy/k8s/pg-pvc.yaml
kubectl apply -f deploy/k8s/pg-statefulset.yaml
kubectl apply -f deploy/k8s/pg-service.yaml
参数 说明
镜像 postgres:13.16 PostgreSQL 13.16
NodePort 30432 集群外访问端口
数据库 demo ConfigMap 中 POSTGRES_DB
用户名 postgres ConfigMap 中 POSTGRES_USER
密码 coslight ConfigMap postgres-config 中配置,生产环境迁移至 Secret
存储 2Gi PVC postgres-data
4.4.1 等待 Pod 就绪
kubectl wait --for=condition=ready pod -l app=postgres --timeout=120s
4.4.2 初始化异步任务表

PostgreSQL 就绪后执行 1.4 节的建表 SQL可通过以下方式进入容器执行

# 交互式 psql
kubectl exec -it $(kubectl get pod -l app=postgres -o jsonpath='{.items[0].metadata.name}') \
  -- psql -U postgres -d demo

# 或将 SQL 文件通过管道一次性执行
kubectl exec -i $(kubectl get pod -l app=postgres -o jsonpath='{.items[0].metadata.name}') \
  -- psql -U postgres -d demo < /path/to/init.sql
4.4.3 状态检查
kubectl get pods -l app=postgres
kubectl logs -l app=postgres --tail=30
4.4.4 清理
kubectl delete -f deploy/k8s/pg-service.yaml \
               -f deploy/k8s/pg-statefulset.yaml \
               -f deploy/k8s/pg-pvc.yaml \
               -f deploy/k8s/pg-configmap.yaml

4.5 部署 MongoDB

kubectl apply -f deploy/k8s/mongodb-secret.yaml
kubectl apply -f deploy/k8s/mongodb-pvc.yaml
kubectl apply -f deploy/k8s/mongodb-statefulset.yaml
kubectl apply -f deploy/k8s/mongodb-service.yaml
参数 说明
镜像 mongo:7.0 MongoDB 7.0
NodePort 30017 集群外访问端口
用户名 admin Root 管理员
密码 coslight Secret mongodb-secret 中配置,生产环境请替换强密码
存储 2Gi PVC mongodb-data

注意: 密码存储在 mongodb-secret.yamlstringData 中,生产环境应替换为强密码,并避免将明文密码提交至版本库。

4.5.1 等待 Pod 就绪
kubectl wait --for=condition=ready pod -l app=mongodb --timeout=120s
4.5.2 连接验证
kubectl exec -it $(kubectl get pod -l app=mongodb -o jsonpath='{.items[0].metadata.name}') \
  -- mongosh -u admin -p coslight --authenticationDatabase admin
4.5.3 状态检查
kubectl get pods -l app=mongodb
kubectl logs -l app=mongodb --tail=30
4.5.4 清理
kubectl delete -f deploy/k8s/mongodb-service.yaml \
               -f deploy/k8s/mongodb-statefulset.yaml \
               -f deploy/k8s/mongodb-pvc.yaml \
               -f deploy/k8s/mongodb-secret.yaml

5. 部署 ModelRTKubernetes

所有资源部署在 default 命名空间YAML 文件位于 deploy/k8s/

5.1 构建并推送镜像

# 在项目根目录执行
docker build -f deploy/dockerfile/modelrt.Dockerfile -t coslight/modelrt:latest .

# 推送到镜像仓库(或直接加载到 Minikube
minikube image load coslight/modelrt:latest

5.2 创建客户端证书 Secret

在 RabbitMQ TLS 证书生成完成后(见 4.2),进入证书文件所在目录执行:

sh deploy/k8s/modelrt-certs-secret.sh

该脚本等价于:

kubectl create secret generic modelrt-certs \
  --from-file=ca_certificate.pem=./ca_certificate.pem \
  --from-file=modelrt_client_cert.pem=./modelrt_client_cert.pem \
  --from-file=modelrt_client_key.pem=./modelrt_client_key.pem

5.3 部署

kubectl apply -f deploy/k8s/modelrt-secret.yaml
kubectl apply -f deploy/k8s/modelrt-configmap.yaml
kubectl apply -f deploy/k8s/modelrt-deployment.yaml
kubectl apply -f deploy/k8s/modelrt-service.yaml

5.4 配置说明

配置项 方式 说明
postgres.password Secret modelrt-secret 不写入 ConfigMap
service.secret_key Secret modelrt-secret 不写入 ConfigMap
RabbitMQ 客户端证书 Secret modelrt-certs 挂载至 /app/configs/certs/
config.yaml 其余配置 ConfigMap modelrt-config 所有 host 已替换为 K8s service 名
K8S_NAMESPACE / K8S_NODE_NAME Downward API 注入至日志全局字段

注意: modelrt-configmap.yamlpostgres.passwordservice.secret_key 留空,实际值由容器启动时的环境变量 POSTGRES_PASSWORD / SERVICE_SECRET_KEY 注入,应用需读取这两个环境变量覆盖 config 中的空值。若应用当前仅读取文件配置,可直接将值填入 modelrt-secret.yaml 并在 ConfigMap 中引用,或在 ConfigMap 中直接填写。

5.5 状态检查

# 查看 Pod 状态
kubectl get pods -l app=modelrt

# 查看启动日志
kubectl logs -l app=modelrt --tail=50

# 查看 Service
kubectl get svc modelrt-service

5.6 端口汇总

NodePort 说明
30080 ModelRT HTTP APISSH 隧道本地端口 8080

5.7 清理

kubectl delete -f deploy/k8s/modelrt-service.yaml \
               -f deploy/k8s/modelrt-deployment.yaml \
               -f deploy/k8s/modelrt-configmap.yaml \
               -f deploy/k8s/modelrt-secret.yaml
kubectl delete secret modelrt-certs

6. 部署可观测性栈Kubernetes

Kubernetes 集群中部署 Jaeger(链路追踪)+ Loki + Promtail + Grafana(日志可视化)。所有资源部署在 default 命名空间,YAML 文件位于 deploy/k8s/

6.1 部署 Jaeger

kubectl apply -f deploy/k8s/jaeger-deployment.yaml
kubectl apply -f deploy/k8s/jaeger-service.yaml

6.2 部署 Loki

kubectl apply -f deploy/k8s/loki-configmap.yaml
kubectl apply -f deploy/k8s/loki-pvc.yaml
kubectl apply -f deploy/k8s/loki-deployment.yaml
kubectl apply -f deploy/k8s/loki-service.yaml

6.3 部署 Promtail

kubectl apply -f deploy/k8s/promtail-rbac.yaml
kubectl apply -f deploy/k8s/promtail-configmap.yaml
kubectl apply -f deploy/k8s/promtail-daemonset.yaml

6.4 部署 Grafana

kubectl apply -f deploy/k8s/grafana-configmap.yaml
kubectl apply -f deploy/k8s/grafana-deployment.yaml
kubectl apply -f deploy/k8s/grafana-service.yaml

6.5 一键部署

kubectl apply -f deploy/k8s/jaeger-deployment.yaml \
              -f deploy/k8s/jaeger-service.yaml \
              -f deploy/k8s/loki-configmap.yaml \
              -f deploy/k8s/loki-pvc.yaml \
              -f deploy/k8s/loki-deployment.yaml \
              -f deploy/k8s/loki-service.yaml \
              -f deploy/k8s/promtail-rbac.yaml \
              -f deploy/k8s/promtail-configmap.yaml \
              -f deploy/k8s/promtail-daemonset.yaml \
              -f deploy/k8s/grafana-configmap.yaml \
              -f deploy/k8s/grafana-deployment.yaml \
              -f deploy/k8s/grafana-service.yaml

6.6 状态检查

# 查看所有 Pod 状态
kubectl get pods

# 查看所有 Service 及 NodePort
kubectl get svc

6.7 端口汇总

服务 NodePort 访问地址 说明
Jaeger UI 31686 http://<NodeIP>:31686 链路追踪查询界面
Loki 31100 http://<NodeIP>:31100 日志 HTTP API
Grafana 31000 http://<NodeIP>:31000 可视化界面,账号 admin / coslight
OTLP gRPC 31317 <NodeIP>:31317 ModelRT OTel 上报地址gRPC
OTLP HTTP 31318 http://<NodeIP>:31318 ModelRT OTel 上报地址HTTP

6.8 清理

kubectl delete -f deploy/k8s/

7. Mac 本地访问SSH 隧道)

ModelRT / EventRTMac 本地运行时,依赖的 RabbitMQRedisJaegerLokiGrafana 均部署在 Ubuntu 宿主机(192.168.1.101)上的 Minikube192.168.49.2)中。由于 Minikube 网络不直接对外暴露,需通过 SSH 本地端口转发建立访问隧道。

7.1 网络拓扑

Mac 本地端口  ──SSH隧道──▶  Ubuntu 宿主机 (192.168.1.101)  ──▶  Minikube NodePort (192.168.49.2)

7.2 建立隧道

ssh -L 5432:192.168.49.2:30432 \
    -L 27017:192.168.49.2:30017 \
    -L 5671:192.168.49.2:30671 \
    -L 15671:192.168.49.2:31671 \
    -L 6379:192.168.49.2:30001 \
    -L 4318:192.168.49.2:31318 \
    -L 16686:192.168.49.2:31686 \
    -L 3100:192.168.49.2:31100 \
    -L 3000:192.168.49.2:31000 \
    douxu@192.168.1.101

如需后台静默运行(不占用终端):

ssh -fN \
    -L 5432:192.168.49.2:30432 \
    -L 27017:192.168.49.2:30017 \
    -L 5671:192.168.49.2:30671 \
    -L 15671:192.168.49.2:31671 \
    -L 6379:192.168.49.2:30001 \
    -L 4318:192.168.49.2:31318 \
    -L 16686:192.168.49.2:31686 \
    -L 3100:192.168.49.2:31100 \
    -L 3000:192.168.49.2:31000 \
    douxu@192.168.1.101

7.3 端口映射说明

Mac 本地端口 Minikube NodePort 服务 说明
5432 30432 PostgreSQL 数据库连接 localhost:5432
27017 30017 MongoDB 数据库连接 localhost:27017
5671 30671 RabbitMQ AMQP ModelRT / EventRT 消息队列连接
15671 31671 RabbitMQ Management RabbitMQ 管理界面 http://localhost:15671
6379 30001 Redis 分布式锁 / 数据存储
4318 31318 OTLP HTTP OTel Trace 上报Jaeger Collector
16686 31686 Jaeger UI 链路追踪查询 http://localhost:16686
3100 31100 Loki 日志查询 API
3000 31000 Grafana 可视化界面 http://localhost:3000

注意: 隧道建立后,本地配置文件中所有服务地址均填 localhost:<本地端口>,无需修改即可在 Mac 上直接运行服务。

7.4 关闭隧道

前台运行时直接 Ctrl+C;后台运行时查找并终止进程:

# 找到 ssh 隧道进程
ps aux | grep "ssh -fN"
# 终止(替换为实际 PID
kill <PID>

8. 后续操作(停止与清理)

8.1 停止容器

docker stop postgres redis

8.2 删除容器(删除后数据将丢失)

docker rm postgres redis