modelRT/deploy/deploy.md

37 KiB
Raw Blame History

项目依赖服务部署指南

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

前提条件

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

1. 部署 PostgreSQL 数据库

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

1.1 部署命令

运行以下命令启动 \text{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 初始化异步任务表

\text{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 镜像该镜像内置了 \text{Redisearch} 模块,用于 \text{ModelRT} 项目中补全功能

2.1 部署命令

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

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

\text{Kubernetes} 集群中部署 $\text{Jaeger}$(链路追踪)+ $\text{Loki + Promtail + Grafana}$(日志可视化)。所有资源部署在 default 命名空间,\text{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 隧道)

\text{ModelRT / EventRT}\text{Mac} 本地运行时,依赖的 $\text{RabbitMQ}$、$\text{Redis}$、$\text{Jaeger}$、$\text{Loki}$、\text{Grafana} 均部署在 \text{Ubuntu} 宿主机(192.168.1.101)上的 $\text{Minikube}$192.168.49.2)中。由于 \text{Minikube} 网络不直接对外暴露,需通过 \text{SSH} 本地端口转发建立访问隧道。

7.1 网络拓扑

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

7.2 建立隧道

ssh -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 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 服务 说明
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:<本地端口>,无需修改即可在 \text{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