Django+Vue3构建运维管理系统:从零到部署实战
2026/9/10 1:20:14 网站建设 项目流程

干运维的兄弟应该都体会过这种痛:服务器一多,信息全散在 Excel 和聊天记录里,谁改过密码没人说得清,线上出问题要顺着 SSH 记录一条条翻。我这次做的就是一个用 Django + Vue3 从零搭起来的前后端分离运维管理系统,把主机资产、账号口令、操作日志这些日常最头疼的东西统一收口。这个项目踩了不少坑,也沉淀了一些比较稳的写法,今天就把它完整拆开,从项目设计、后端 API、前端鉴权到最后的部署上线,一条线讲透,适合刚学完 Django 和 Vue3 基础、想做一个真正能落地项目的人参考。

1. 项目整体设计与技术选型

1.1 为什么选 Django + Vue3 这对组合

先说后端。运维系统本质上是管理内部资源的工具,核心诉求是快速开发、稳定维护,对并发要求没那么夸张。Django 在这方面有天然优势:ORM 省去一大堆 SQL 手写工作,Admin 后台可以直接当临时管理界面用,自带的认证体系和权限框架非常成熟,开发效率在同类框架里算是第一梯队。我前期调研的时候也考虑过 Spring Boot,但团队本身是 Python 技术栈,而且 Django 生态里有现成的认证、序列化、ORM 组件,从零到 MVP 的速度确实快不少。

前端选 Vue3 除了技术栈延续性,还有一个很实际的原因:Vue3 的 Composition API 在处理复杂业务逻辑时非常舒服。运维系统里有大量的表单校验、状态切换、数据联动场景,用组合式函数可以把这些逻辑全部抽离出来复用,代码不会像 Options API 那样越写越臃肿。配合 Vite 的开发体验极好,热更新几乎是秒级的,这在频繁调整表格和表单界面的时候特别加分。

前后端分离这个架构本身也有明确考量。运维系统要对接的终端用户往往分散在不同网络环境,前端静态资源可以独立部署到 CDN 或者内网 Nginx,后端只暴露 API 接口,两者的发布节奏互不干扰。改个前端样式不需要重启后端服务,新增一个接口也不用重新构建前端,交付和迭代都灵活很多。

1.2 运维系统的核心模块到底拆成什么

很多新手拿到这种项目就急着写代码,结果把主机信息和用户信息堆在一张表里,后面越改越乱。我一开始就把系统拆成了五个核心模块,边界尽量清晰:

  • 用户与权限:负责登录认证、角色权限分配,后端用户不是运维对象,而是系统的操作者,和资产管理完全分开。
  • 主机资产:记录服务器的基础信息,包括 IP、操作系统、CPU 内存规格、机房位置、责任人。
  • 账号凭证:管理服务器上的登录账号和密码,和主机资产是多对一关系,做加密存储。
  • 操作记录:记录谁在什么时间对哪台主机执行了什么命令,也就是审计日志。
  • 命令通道:通过 WebSSH 或后端执行脚本对目标主机下发命令,回收执行结果。

第一版我没有直接做很复杂的编排调度功能,而是先把资产和审计这两块地基打牢。原因很简单,资产管理是运维工作的数据底座,操作审计是安全合规的底线要求,这两个模块做好之后,后续再怎么扩展监控、发布、工单系统都有的放矢。

ops_backend/ ├── apps/ │ ├── users/ # 用户与认证 │ ├── assets/ # 主机资产 │ ├── credentials/ # 账号凭证 │ └── audit/ # 操作审计 ├── utils/ # 通用工具 ├── config/ # 项目配置 └── manage.py

这个目录结构是我后来重构的。Django 官方默认把 app 平铺在根目录,但系统模块一多,settings.pyurls.py全堆在顶层会非常乱。把 app 收拢进apps/包,再用config目录放配置文件,项目规模上来之后结构依然清晰。

2. 后端核心功能实现

2.1 开发环境与踩坑记录

创建虚拟环境这步千万别省,我见过直接把依赖装进系统 Python 然后版本冲突把环境搞崩的。我的习惯是用 virtualenv 创建独立的运行环境,Python 版本统一用 3.10,Django 选择长期维护的 4.2 LTS 版本,这一版兼容性和稳定性都经过了充分验证。

依赖安装的核心包清单:

pip install django==4.2.* pip install djangorestframework pip install django-cors-headers pip install pymysql pip install paramiko pip install pyjwt pip install cryptography

这里有几个点提醒大家注意。第一,使用 PyMySQL 连接 MySQL 的话,需要在项目__init__.py里加上pymysql.install_as_MySQLdb(),否则 Django 找不到 MySQLdb 模块。第二,cryptographyparamiko的依赖包,如果漏装,SSH 连接的时候会报未知的加密算法错误。第三,Python 3.10 以下版本对cryptography的安装可能有点麻烦,最好直接上 3.10+。

2.2 数据模型怎么设计才合理

数据模型是整个系统的地基,后面所有业务逻辑都建立在这几张表上。我第一版的设计比较朴素,后来根据真实使用场景迭代过两次,最终结构大概是这样:

# apps/assets/models.py from django.db import models class Host(models.Model): STATUS_CHOICES = ( ('online', '在线'), ('offline', '离线'), ('maintain', '维护中'), ) hostname = models.CharField('主机名', max_length=128, unique=True) ip_address = models.GenericIPAddressField('IP地址', unique=True) os_type = models.CharField('操作系统', max_length=64) cpu_cores = models.IntegerField('CPU核数', default=0) memory_size = models.IntegerField('内存大小(MB)', default=0) disk_size = models.IntegerField('磁盘大小(GB)', default=0) status = models.CharField('状态', max_length=16, choices=STATUS_CHOICES, default='online') owner = models.CharField('责任人', max_length=64, blank=True) remark = models.TextField('备注', blank=True) created_at = models.DateTimeField('创建时间', auto_now_add=True) updated_at = models.DateTimeField('更新时间', auto_now=True) class Meta: db_table = 'hosts' ordering = ['-created_at'] def __str__(self): return f'{self.hostname} ({self.ip_address})'

主机和账号凭证是一对多的关系,一台服务器可能有 root、ops、deploy 等多个账号:

class Credential(models.Model): host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='credentials') username = models.CharField('用户名', max_length=64) password = models.CharField('密码', max_length=256) port = models.IntegerField('SSH端口', default=22) is_sudo = models.BooleanField('是否具有sudo权限', default=False) created_at = models.DateTimeField('创建时间', auto_now_add=True) class Meta: db_table = 'credentials' unique_together = ('host', 'username')

密码存储这一点必须认真对待。很多练手项目直接把明文密码存进数据库,这在真实系统里绝对不允许。我这边用的是cryptography的 Fernet 对称加密,密钥单独放在环境变量里,数据库里只存密文。读取的时候在内存中解密,日志里绝对不能打印密码原文。

2.3 JWT 认证体系的落地

运维系统对内使用,我没走 Django 自带的 Session 认证,而是直接上了 JWT。前后端分离场景下,JWT 天然无状态,后端不需要维护会话记录,前端把 token 存在 localStorage,每次请求塞进 Header 就行,扩展起来也方便。

# apps/users/utils.py import jwt from datetime import datetime, timedelta from django.conf import settings def generate_token(user): payload = { 'user_id': user.id, 'username': user.username, 'exp': datetime.utcnow() + timedelta(hours=12), 'iat': datetime.utcnow(), } token = jwt.encode(payload, settings.SECRET_KEY, algorithm='HS256') return token def verify_token(token): try: payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256']) return payload except jwt.ExpiredSignatureError: return None except jwt.InvalidTokenError: return None

JWT 的使用有个细节:token 一旦签发,在过期之前无法主动作废。运维系统里如果担心员工离职后 token 还能用一段时间,可以引入一个 Redis 黑名单机制,也可以把 token 有效期缩短到几小时,配合前端定时刷新。我目前的做法是 12 小时过期,内网系统可接受。

登录接口拿用户输入的用户名密码去 Django 的authenticate校验,通过后签发 token,同时把用户基本信息返回给前端,前端再根据返回的权限码渲染菜单。

2.4 命令执行通道的坑

命令执行是运维系统最核心也最危险的功能。后端要连上远程主机跑命令,我把执行通道封装成了一个独立服务模块,便于后续扩展和维护:

# apps/audit/services.py import paramiko def exec_remote_command(host, port, username, password, command, timeout=10): client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: client.connect( hostname=host, port=port, username=username, password=password, timeout=timeout, banner_timeout=timeout ) stdin, stdout, stderr = client.exec_command(command, timeout=timeout) output = stdout.read().decode('utf-8', errors='replace') error = stderr.read().decode('utf-8', errors='replace') code = stdout.channel.recv_exit_status() return code, output, error finally: client.close()

这里踩过一个大坑:exec_command默认只返回标准输出,但如果命令执行失败或输出到 stderr,界面上只显示空白。必须主动读 stderr,而且要通过recv_exit_status()拿真正的退出码,否则你根本不知道命令到底成没成。另外一个坑是stdout.channel.recv_exit_status()必须放在read()之后调用,否则会阻塞或者拿不到状态码。

命令通道的鉴权逻辑也很关键,不是所有登录用户都能在任意机器上执行命令。我在后端做了两层校验:先确认用户有执行权限,再确认命令不在黑名单里(比如禁止rm -rf /这类危险命令)。操作记录同时落库,存下用户、目标主机、完整命令和执行结果,这就是前文说的审计能力。

3. 前端工程构建全程

3.1 从 Vite 脚手架开始的 Vue3 项目

前端这边我直接用 Vite 官方脚手架生成,相比 Vue CLI 来说启动速度快、配置简洁,而且 Vue3 官方生态已经全面转向 Vite:

npm create vite@latest ops_web -- --template vue cd ops_web npm install npm install vue-router@4 pinia axios element-plus

Element Plus 是 Vue3 生态里最成熟的中后台组件库,拿来就用,表格表单都齐活,不用自己手搓 UI,节约大量时间。我在项目里基本上把 Element Plus 的 Table、Form、Dialog、Message 这几个高频组件玩明白了,整个前端界面就没再写多少原生样式。

工程层面的关键目录:

src/ ├── api/ # 按模块封装的请求方法 ├── assets/ ├── components/ # 通用组件 ├── layout/ # 布局框架 ├── router/ # 路由配置 ├── stores/ # Pinia 状态管理 ├── utils/ # 通用工具函数 └── views/ # 页面级组件

3.2 axios 封装与 token 处理的细节

前后端分离项目里,token 处理是最容易出问题的环节。axios 如果不封装,每个页面请求都要手动带 token、手动处理 401,一旦接口多了起来就是灾难。我的统一封装方式:

// src/utils/request.js import axios from 'axios' import { ElMessage } from 'element-plus' import router from '../router' const request = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 15000 }) request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) request.interceptors.response.use( response => response.data, error => { if (error.response) { const status = error.response.status if (status === 401) { localStorage.removeItem('token') router.push('/login') ElMessage.error('登录已过期,请重新登录') } else if (status === 403) { ElMessage.error('没有操作权限') } else if (status >= 500) { ElMessage.error('服务器开小差了,请稍后重试') } else { ElMessage.error(error.response.data.detail || '请求失败') } } else { ElMessage.error('网络异常,请检查后端服务是否可用') } return Promise.reject(error) } ) export default request

拦截器解决了几个问题。统一在请求前读取 localStorage,不需要每个 API 调用手动设置 Header。后端返回 401 时自动清理本地存储并跳转登录页,避免用户停留在已失效的页面上继续做无用操作。接口报错时统一弹提示,前端业务代码里不需要到处写 try-catch 的 Message 逻辑。

这里补充一个容易踩的坑:刷新页面后 localStorage 里的 token 还在,但 Pinia 里的用户信息会被清空。所以应用初始化的时候要从 token 解析出用户基本信息,或者调用一次/api/users/me拉取最新用户资料,否则页面上的用户名会变成空的。

3.3 路由权限控制与页面守卫

前端路由不能只做跳转,还要配合权限做动态拦截。我的路由配置分为两部分:公开路由(登录页)和受保护路由(主页、资产管理、任务执行等)。路由守卫的逻辑是这样:

// src/router/index.js router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') const isLoginPage = to.path === '/login' if (!token && !isLoginPage) { next('/login') return } if (token && isLoginPage) { next('/') return } next() })

如果系统里要区分不同角色的菜单和权限,可以再引入动态路由。后端登录接口返回用户的权限码数组,前端在路由守卫里根据权限码动态addRoute注册用户有权限访问的路由,没有权限的直接渲染 404。我这个项目的权限粒度没有做到按钮级,主要是菜单和路由层面,对内部运维系统来说已经够用。

3.4 前端页面的核心逻辑写法

视图层我按照模块拆分,核心页面包括:登录页、主机资产列表、主机详情、凭证管理、操作审计列表。以主机列表页为例,关键是分页查询和状态展示:

<script setup> import { ref, onMounted } from 'vue' import { getHostList } from '../api/assets' const loading = ref(false) const hostList = ref([]) const total = ref(0) const queryParams = ref({ page: 1, pageSize: 10, keyword: '' }) const loadHosts = async () => { loading.value = true try { const res = await getHostList(queryParams.value) hostList.value = res.results total.value = res.count } finally { loading.value = false } } onMounted(loadHosts) </script>

<script setup>语法极大地简化了代码组织,ref 声明响应式变量,onMounted 做初始化加载,整个过程清晰流畅。配合 Element Plus 的 el-table 和 el-pagination,后端返回的分页数据直接绑定即可。

4. 前后端联调与上线部署

4.1 开发环境跨域问题怎么处理

前后端分离开发时,前端跑在 5173 端口,后端跑在 8000 端口,两个端口不同就是跨域。处理跨域有两个方案,我开发的阶段用的是 Vite 的代理方案,部署阶段用 Nginx 反向代理统一入口。

Vite 配置:

// vite.config.js export default defineConfig({ server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true } } } })

开发环境所有以/api开头的请求都会被 Vite 代理到后端 8000 端口,浏览器看到的请求是同源的,不会有 CORS 问题。如果你不想用代理方案,也可以在后端配上django-cors-headers,加上CORS_ALLOW_ALL_ORIGINS = True,但生产环境千万别这么干,等于把你的 API 开放给所有来源了。

4.2 项目打包与部署细节

前端构建:

npm run build

产物会生成在dist/目录,静态文件推送到服务器上的/opt/ops_web/。后端用 Gunicorn 跑 Django 服务:

gunicorn config.wsgi:application -w 4 -b 127.0.0.1:8000

Nginx 配置是部署的关键一环:

server { listen 80; server_name ops.example.com; # 前端静态资源 location / { root /opt/ops_web; index index.html; try_files $uri $uri/ /index.html; } # 后端 API 反向代理 location /api/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 静态文件 location /static/ { alias /var/www/ops_backend/static/; } }

这里try_files $uri $uri/ /index.html这一行非常重要。Vue Router 默认是 history 模式,如果没有这行配置,用户点击浏览器刷新或直接访问/assets等子路径时,Nginx 找不到对应的文件会返回 404。加了这行之后,所有前端路由会统一回退到 index.html,由前端路由接管。

数据库迁移和静态文件收集也要按顺序执行:

python manage.py makemigrations python manage.py migrate python manage.py collectstatic

部署环境我用的是云服务器 + 宝塔面板的方式,宝塔面板图形化配置 Nginx 和 MySQL 很方便,省去不少命令行操作。如果你用的是麒麟这类国产化操作系统,命令基本兼容,唯一要注意的是安装 Python 依赖时可能需要指定--index-url指向可用源。

4.3 生产环境的安全加固要点

系统上线前,安全加固这步必须做,不能省:

  • 关闭 Django 的 DEBUG 模式,改设DEBUG = False,配置ALLOWED_HOSTS为实际域名或 IP。
  • Django SECRET_KEY 不能硬编码在代码里,放到环境变量读取。
  • 数据库密码、SSH 凭证加密密钥也都从环境变量读取。
  • Nginx 层开启 HTTPS,配置 SSL 证书。
  • 操作审计日志要定期备份,至少保留 180 天以上。
  • 命令执行黑名单放在服务端,前端隐藏不等于后端安全。

5. 开发过程中遇到的坑与排查思路

5.1 线上环境高频问题速查表

下面这些问题是开发过程中真实遇过的,每一条背后都是数小时的排查,现在整理成一个速查表,大家在开发时可以直接对照排查。

现象可能原因解决办法
前端登录后刷新页面又跳回登录页token 存放在内存里,刷新后丢失token 存 localStorage,初始化时读取
接口返回 403 CSRF 验证失败前后端分离场景下未关闭 CSRF对 API 请求关闭 CSRF 中间件,改用 JWT 认证
跨域请求被拦截后端未配置 CORS 或配置错误django-cors-headers,生产环境用 Nginx 反代
中文乱码MySQL 连接未指定 utf8mb4创建数据库时设置utf8mb4,PyMySQL 连接参数加上 charset
上传的文件 Nginx 报 413Nginx 上传大小限制httpserver块加client_max_body_size 50m;
Vue Router history 模式刷新 404服务器没有配置 try_filesNginx 配置try_files $uri $uri/ /index.html;
后端改字段后前端没反应接口返回了旧的缓存检查浏览器 Network 看是否走 Service Worker,或清缓存
命令执行超时SSH 连接或命令执行超过设定 timeout适当调大 timeout,或者命令改为异步执行

5.2 排查方法论:四步定位问题

排查问题我有一套自己的方法论,分享给大家参考。第一步,先看浏览器开发者工具的 Network 面板,确认请求是否发出、返回什么状态码、响应体是什么。这一步能解决大约 60% 的问题。第二步,去后端看日志,Django 的日志默认打到终端或日志文件,重点看异常堆栈。第三步,用 curl 或 Postman 直接调接口,排除前端代码干扰。第四步,如果还查不出来,就在关键位置打日志,不要靠猜。

印象最深的一次:前端所有接口都返回 401,但登录接口正常。排查了半天,最后发现是 axios 封装里把 token 拼错了位置,写成了Authorization: Token ${token},而后端验证的是 Bearer 格式。类似这种细节,如果一开始就规范格式约定,就不会浪费这么多时间。

5.3 凭证加密的工具类封装

凭证加密我单拎出来讲一下,因为运维系统的安全水位很大程度上取决于这块设计。我用的是 Fernet 对称加密,密钥一秒钟生成一次:

from cryptography.fernet import Fernet # 生成密钥,保存到环境变量中 key = Fernet.generate_key() def encrypt_secret(plain_text): f = Fernet(settings.ENCRYPT_KEY.encode()) return f.encrypt(plain_text.encode()).decode() def decrypt_secret(cipher_text): f = Fernet(settings.ENCRYPT_KEY.encode()) return f.decrypt(cipher_text.encode()).decode()

存储层面,密码字段设计为 CharField 存密文,接口返回时默认做脱敏处理,前端显示******,只有执行命令时后端才在内存中解密。解密后的密码绝对不能打日志,Python 的日志系统要特别留意别在 debug 时顺手打了敏感字段。

6. 进阶功能:从 MVC 到复杂业务逻辑的扩展

6.1 用 ViewSet 组织 RESTful API

上面的功能基本覆盖了一个运维系统的 MVP。实际开发中,随着业务复杂度上升,API 组织结构会越来越重要。Django REST Framework 的 ViewSet 提供了一种优雅的解决方案:

# apps/assets/views.py from rest_framework import viewsets from .models import Host from .serializers import HostSerializer class HostViewSet(viewsets.ModelViewSet): queryset = Host.objects.all() serializer_class = HostSerializer pagination_class = StandardResultsSetPagination filterset_fields = ['os_type', 'status'] search_fields = ['hostname', 'ip_address']

配合设置两个关键类:

# config/settings.py REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'apps.users.authentication.JWTAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ], 'DEFAULT_PAGINATION_CLASS': 'apps.utils.pagination.StandardResultsSetPagination', 'PAGE_SIZE': 10, }

ViewSet 一组 CRUD 接口只需要定义一次,路由注册也很简单:

from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(r'hosts', HostViewSet)

一个路由直接带出列表、详情、创建、更新、删除全部接口,前后端对接的 URL 约定天然统一,开发效率提升非常显著。

6.2 异步任务与 WebSocket 场景

后面我还在系统里加了异步任务,属于进阶扩展。比如批量执行命令,如果同步跑,一个客户端请求可能要等几十秒,体验很差。我的方案是:后端收到批量任务后直接返回任务 ID,后台用 Celery 异步执行,前端通过轮询任务状态接口获知执行进度,完成后把结果存入数据库供前端拉取展示。

如果后续要做实时终端或强实时监控,可以升级为 WebSocket 方案。Django Channels 是官方生态里比较成熟的方案,但复杂度会上升不少,建议先把同步任务走通再考虑。

6.3 日志规范的统一

日志是系统里最容易被忽略却最重要的部分。我的做法是在每个业务模块从上到下定义日志策略:记录接口的访问者、访问时间、请求参数、返回状态,记录命令执行的完整链路,记录登录失败的次数和来源 IP,日记记录要包含时间戳以便追溯。Django 的 logging 配置可以整合到 settings 里,将 INFO 级别的日志输出到文件并按天轮转:

LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'file': { 'level': 'INFO', 'class': 'logging.handlers.TimedRotatingFileHandler', 'filename': '/var/log/ops_backend/app.log', 'when': 'midnight', 'backupCount': 30, 'formatter': 'verbose', }, }, 'loggers': { 'django': { 'handlers': ['file'], 'level': 'INFO', 'propagate': True, }, }, }

日志文件定期切割,保留 30 天,避免单个文件无限膨胀。

6.4 Pinia 状态管理的最佳实践

前端状态管理我用了 Pinia,比 Vuex 简洁很多。运维系统里的状态大概分三类:用户状态(登录信息、权限码)、全局 UI 状态(侧边栏折叠、主题)、业务缓存(主机列表的筛选条件)。前三类的写法:

// src/stores/user.js import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ token: localStorage.getItem('token') || '', userInfo: null }), actions: { setToken(token) { this.token = token localStorage.setItem('token', token) }, setUserInfo(info) { this.userInfo = info }, logout() { this.token = '' this.userInfo = null localStorage.removeItem('token') } } })

跟 Vuex 对比,Pinia 几乎没有学习成本,TypeScript 支持也更好。在大型中后台项目里,按业务域拆 store 是标准实践,比把所有状态堆在一个大 store 里好维护得多。

7. 最后的经验总结与后续扩展方向

说几个我实际做完这个项目后最深的感受。

第一个是关于开发节奏的。前后端分离项目最忌讳的是全写完再联调,一定要前后端并行开发、约好接口文档同步推进。我这次是先定了完整的 API 文档,后端按文档实现接口并提供 mock 数据,前端按文档直接开发页面,联调整体非常顺畅。接口文档工具我用的 apifox,支持导出、一键 mock,前后端可以基于同一份文档协作。

第二个是安全底线问题。运维系统掌握着服务器最高的管理权限,任何安全疏漏都可能被放大。代码层面要注意 SQL 注入、SSO 鉴权、命令逃逸、越权访问这些基础问题;运维层面要管控好服务器访问权限、数据库防火墙、Nginx 配置审查。密码加密和操作审计这几个模块无论如何都不能省。

第三个是可扩展性。第一版能跑通不代表系统设计就结束了,真实环境里监控告警、容器管理、CI/CD 发布这些需求随时会提过来。基础数据模型设计得稳、模块边界划分得清楚,加新功能就是顺理成章的事。当时我把数据模型设计得足够归一化,后来对接监控数据时只是增加了一张新表,主机资产表完全没动。

如果后续要继续扩展,我建议优先做这几个方向:WebSSH 在线终端、监控数据面板、告警通知(钉钉/飞书/邮件)、工单审批流。WebSSH 是运维系统最常用的功能,Django Channels 能实现;监控面板可以对接 Prometheus 的 API 拉数据;告警通知需要对接各家开放平台的消息推送接口;工单审批流则需要引入分布式任务和消息队列来做支持。这些功能都做进去之后,这套系统基本就从一个简单的资产台账,长成了真正能支撑日常运维工作的内部平台了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询