前言
做了 16 年运维,从手动操作到全自动化,我深刻体会到:好的工具能让运维效率提升 10 倍。
之前分享过 Python 自动化脚本,但脚本终究是零散的。今天开始,我用一个系列,教大家从零搭建一套完整的运维管理平台。
技术栈:
- 后端:FastAPI + SQLAlchemy + MySQL
- 前端:Vue 3 + Element Plus
- 部署:Docker + Nginx
功能模块:
- 服务器管理(CMDB)
- 批量执行命令
- 自动化部署
- 监控告警
- 工单系统
本系列共 5 篇:
- 第一篇:项目架构(本文)
- 第二篇:后端开发
- 第三篇:前端开发
- 第四篇:功能实现
- 第五篇:部署上线
适合人群:
- 有 Python 基础的运维
- 想转型运维开发的同学
- 需要内部工具的团队
代码已开源: GitHub 搜索 ops-platform(文末有链接)
一、为什么做这个平台?
1.1 运维的痛点
你在工作中是否遇到过这些问题:
❌ 每天登录几十台服务器检查状态
❌ 部署应用时一台台手动操作
❌ 出了问题不知道是哪台服务器
❌ 配置信息分散在 Excel/文档里
❌ 重复性工作太多,没时间学习
如果有,那这个平台就是为你做的。
1.2 现有工具的不足
市面上的运维工具很多,但都有局限:
| 工具 | 优点 | 缺点 |
|---|---|---|
| Ansible | 功能强大 | 学习曲线陡,需要额外学习 |
| JumpServer | 堡垒机功能好 | 侧重安全,运维功能弱 |
| Zabbix | 监控强大 | 配置复杂,不够灵活 |
| 自研脚本 | 灵活 | 零散,不成体系 |
我们的目标: 做一个轻量、易用、可扩展的运维平台。
二、需求分析
2.1 核心功能
根据我 16年运维经验,一个运维平台最核心的功能是:
1. 服务器管理(CMDB)
- 服务器信息录入
- 分组/标签管理
- 快速搜索过滤
2. 批量执行
- 选择多台服务器
- 同时执行命令
- 实时查看结果
3. 自动化部署
- 上传代码/包
- 执行部署脚本
- 回滚功能
4. 监控告警
- 基础指标采集
- 阈值告警
- 通知推送
5. 工单系统
- 资源申请
- 变更审批
- 操作记录
2.2 用户角色
管理员:
- 所有权限
- 用户管理
- 系统配置
运维人员:
- 服务器管理
- 批量执行
- 部署操作
开发人员:
- 查看服务器状态
- 提交部署申请
- 查看日志
只读用户:
- 只能查看
- 不能操作
三、技术选型
3.1 后端:为什么选 FastAPI?
| 框架 | 性能 | 学习成本 | 文档 | 异步支持 |
|---|---|---|---|---|
| Flask | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ❌ |
| Django | ⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| FastAPI | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ✅ |
FastAPI 优势:
- 性能高 - 基于 Starlette,性能接近 NodeJS
- 自动文档 - 自动生成 Swagger UI
- 类型检查 - 基于 Pydantic,减少 bug
- 异步支持 - 原生支持 async/await
- 开发快 - 代码量少,开发效率高
3.2 前端:为什么选 Vue 3?
| 框架 | 学习成本 | 生态 | 性能 | 社区 |
|---|---|---|---|---|
| React | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Vue 3 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| Angular | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
Vue 3 优势:
- 上手快 - 模板语法简单
- 组件丰富 - Element Plus 组件库
- 性能好 - Composition API
- 中文友好 - 文档和社区都是中文
3.3 数据库:MySQL
- 运维数据关系复杂,适合关系型数据库
- MySQL 稳定、成熟、资料多
- 后期可扩展 Redis 缓存
3.4 部署:Docker
- 环境一致,避免"在我机器上能跑"
- 一键部署,降低维护成本
- 方便扩展和迁移
四、项目架构
4.1 整体架构图
┌─────────────────────────────────────────────────────────┐
│ Nginx (反向代理) │
└─────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Vue 前端 │ │ FastAPI 后端 │
│ (80 端口) │ │ (8000 端口) │
└─────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ MySQL │
│ (3306 端口) │
└─────────────────┘
│
▼
┌─────────────────┐
│ 目标服务器 │
│ (SSH 连接) │
└─────────────────┘
4.2 后端目录结构
ops-platform/
├── backend/ # 后端代码
│ ├── app/
│ │ ├── __init__.py
│ │ ├── main.py # FastAPI 入口
│ │ ├── config.py # 配置文件
│ │ ├── database.py # 数据库连接
│ │ ├── models/ # 数据模型
│ │ │ ├── __init__.py
│ │ │ ├── server.py # 服务器模型
│ │ │ ├── user.py # 用户模型
│ │ │ └── task.py # 任务模型
│ │ ├── schemas/ # Pydantic 模型
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ ├── user.py
│ │ │ └── task.py
│ │ ├── api/ # API 路由
│ │ │ ├── __init__.py
│ │ │ ├── servers.py
│ │ │ ├── users.py
│ │ │ └── tasks.py
│ │ ├── utils/ # 工具函数
│ │ │ ├── __init__.py
│ │ │ ├── ssh_client.py # SSH 客户端
│ │ │ └── auth.py # 认证工具
│ │ └── core/ # 核心配置
│ │ ├── __init__.py
│ │ └── security.py
│ ├── tests/ # 测试代码
│ ├── requirements.txt # Python 依赖
│ └── Dockerfile # Docker 镜像
├── frontend/ # 前端代码
│ ├── src/
│ │ ├── main.js
│ │ ├── App.vue
│ │ ├── views/ # 页面
│ │ ├── components/ # 组件
│ │ ├── router/ # 路由
│ │ ├── store/ # 状态管理
│ │ └── api/ # API 请求
│ ├── public/
│ ├── package.json
│ └── Dockerfile
├── docker-compose.yml # Docker 编排
├── README.md
└── docs/ # 文档
4.3 前端目录结构
frontend/
├── src/
│ ├── main.js # 入口文件
│ ├── App.vue # 根组件
│ ├── views/ # 页面视图
│ │ ├── Login.vue # 登录页
│ │ ├── Dashboard.vue # 首页
│ │ ├── Servers.vue # 服务器管理
│ │ ├── Tasks.vue # 任务管理
│ │ └── Settings.vue # 设置
│ ├── components/ # 公共组件
│ │ ├── Header.vue
│ │ ├── Sidebar.vue
│ │ └── Table.vue
│ ├── router/ # 路由配置
│ │ └── index.js
│ ├── store/ # Vuex 状态管理
│ │ ├── index.js
│ │ └── modules/
│ ├── api/ # API 封装
│ │ ├── request.js
│ │ ├── server.js
│ │ └── task.js
│ └── utils/ # 工具函数
│ ├── auth.js
│ └── format.js
├── public/
│ └── index.html
├── package.json
└── vite.config.js
五、数据库设计
5.1 ER 图
┌─────────────┐ ┌─────────────┐
│ users │ │ groups │
├─────────────┤ ├─────────────┤
│ id │ │ id │
│ username │ │ name │
│ password │ │ description │
│ email │ └─────────────┘
│ role │ │
│ group_id │ │
└─────────────┘ │
│ │
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ servers │ │ server_ │
├─────────────┤ │ group │
│ id │ ├─────────────┤
│ hostname │ │ server_id │
│ ip │ │ group_id │
│ port │ └─────────────┘
│ username │
│ password │
│ group_id │
└─────────────┘
│
│
▼
┌─────────────┐
│ tasks │
├─────────────┤
│ id │
│ name │
│ type │
│ server_ids │
│ command │
│ status │
│ result │
│ created_by │
│ created_at │
└─────────────┘
5.2 核心表结构
users 表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(100),
role ENUM('admin', 'ops', 'dev', 'readonly') DEFAULT 'readonly',
group_id INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
servers 表
CREATE TABLE servers (
id INT PRIMARY KEY AUTO_INCREMENT,
hostname VARCHAR(100) NOT NULL,
ip VARCHAR(50) NOT NULL,
port INT DEFAULT 22,
username VARCHAR(50) DEFAULT 'root',
password VARCHAR(255),
os VARCHAR(50),
cpu_cores INT,
memory_gb INT,
disk_gb INT,
group_id INT,
status ENUM('online', 'offline', 'maintenance') DEFAULT 'offline',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
tasks 表
CREATE TABLE tasks (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
type ENUM('command', 'deploy', 'backup') NOT NULL,
server_ids TEXT,
command TEXT,
status ENUM('pending', 'running', 'success', 'failed') DEFAULT 'pending',
result TEXT,
created_by INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
started_at TIMESTAMP NULL,
finished_at TIMESTAMP NULL
);
六、核心代码(抢先看)
6.1 FastAPI 入口
# backend/app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api import servers, users, tasks
from app.database import engine, Base
# 创建数据库表
Base.metadata.create_all(bind=engine)
app = FastAPI(
title="运维管理平台",
description="基于 FastAPI + Vue 的运维自动化平台",
version="1.0.0"
)
# 跨域配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
app.include_router(servers.router, prefix="/api/servers", tags=["服务器"])
app.include_router(users.router, prefix="/api/users", tags=["用户"])
app.include_router(tasks.router, prefix="/api/tasks", tags=["任务"])
@app.get("/")
async def root():
return {"message": "欢迎使用运维管理平台"}
6.2 SSH 客户端
# backend/app/utils/ssh_client.py
import paramiko
from typing import Tuple, Optional
class SSHClient:
def __init__(self, host: str, port: int = 22,
username: str = 'root', password: Optional[str] = None,
key_file: Optional[str] = None):
self.host = host
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if key_file:
self.ssh.connect(host, port, username, key_filename=key_file)
elif password:
self.ssh.connect(host, port, username, password)
else:
raise ValueError("必须提供 password 或 key_file")
def exec_command(self, command: str) -> Tuple[int, str, str]:
"""执行命令,返回 (退出码,输出,错误)"""
stdin, stdout, stderr = self.ssh.exec_command(command)
exit_code = stdout.channel.recv_exit_status()
return exit_code, stdout.read().decode(), stderr.read().decode()
def upload_file(self, local_path: str, remote_path: str):
"""上传文件"""
sftp = self.ssh.open_sftp()
sftp.put(local_path, remote_path)
sftp.close()
def close(self):
self.ssh.close()
6.3 批量执行任务
# backend/app/api/tasks.py
from fastapi import APIRouter, BackgroundTasks
from app.models.task import Task
from app.utils.ssh_client import SSHClient
from app.database import get_db
from concurrent.futures import ThreadPoolExecutor
router = APIRouter()
def execute_task(task_id: int, server_ids: list, command: str):
"""后台执行任务"""
db = next(get_db())
task = db.query(Task).filter(Task.id == task_id).first()
results = []
with ThreadPoolExecutor(max_workers=10) as executor:
for server_id in server_ids:
server = db.query(Server).filter(Server.id == server_id).first()
def run_on_server(s):
try:
ssh = SSHClient(s.ip, s.port, s.username, s.password)
exit_code, output, error = ssh.exec_command(command)
ssh.close()
return {
"server": s.hostname,
"success": exit_code == 0,
"output": output,
"error": error
}
except Exception as e:
return {
"server": s.hostname,
"success": False,
"error": str(e)
}
results.append(executor.submit(run_on_server, server))
# 收集结果
task.result = [r.result() for r in results]
task.status = "success" if all(r.result()["success"] for r in results) else "failed"
db.commit()
@router.post("/")
async def create_task(task: TaskCreate, background_tasks: BackgroundTasks):
"""创建批量执行任务"""
db = next(get_db())
# 创建任务记录
db_task = Task(**task.dict())
db_task.status = "pending"
db.add(db_task)
db.commit()
# 后台执行
background_tasks.add_task(
execute_task,
db_task.id,
task.server_ids,
task.command
)
return {"task_id": db_task.id, "status": "pending"}
七、开发环境搭建
7.1 后端环境
# 1. 克隆项目
git clone https://gitee.com/wgsummer/ops-platform.git
cd ops-platform/backend
# 2. 创建虚拟环境
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# 3. 安装依赖
pip install -r requirements.txt
# 4. 配置数据库
# 修改 app/config.py 中的数据库连接
# 5. 启动服务
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
requirements.txt:
fastapi==0.104.1
uvicorn[standard]==0.24.0
sqlalchemy==2.0.23
pymysql==1.1.0
paramiko==3.4.0
pydantic==2.5.0
pydantic-settings==2.1.0
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
python-multipart==0.0.6
email-validator==2.3.0
依赖说明:
fastapi- Web 框架uvicorn- ASGI 服务器sqlalchemy- ORM 框架pymysql- MySQL 驱动paramiko- SSH 库pydantic- 数据验证python-jose- JWT 认证passlib- 密码加密
7.2 前端环境
# 1. 进入前端目录
cd ops-platform/frontend
# 2. 安装依赖
npm install
# 3. 启动开发服务器
npm run dev
package.json 关键依赖:
{
"dependencies": {
"vue": "^3.3.8",
"vue-router": "^4.2.5",
"vuex": "^4.1.0",
"element-plus": "^2.4.3",
"axios": "^1.6.2"
}
}
7.3 Docker 一键启动
# 项目根目录
docker-compose up -d
docker-compose.yml:
version: '3.8'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: ops_platform
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
backend:
build: ./backend
ports:
- "8000:8000"
depends_on:
- mysql
environment:
DATABASE_URL: mysql://root:root123@mysql/ops_platform
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
nginx:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- frontend
- backend
volumes:
mysql_data:
八、API 文档
FastAPI 自动生成 Swagger UI 文档:
访问地址: http://localhost:8000/docs
主要 API 列表:
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/servers | 获取服务器列表 |
| POST | /api/servers | 添加服务器 |
| PUT | /api/servers/{id} | 更新服务器 |
| DELETE | /api/servers/{id} | 删除服务器 |
| POST | /api/tasks | 创建任务 |
| GET | /api/tasks/{id} | 获取任务状态 |
| POST | /api/users/login | 用户登录 |
九、下一步
本文介绍了项目架构,下一篇我们开始后端开发:
第二篇预告:
- 用户认证(JWT)
- 服务器管理 API
- 批量执行 API
- 数据库操作
项目进度:
- ✅ 架构设计
- ✅ 后端开发(代码已开源)
- ⏳ 前端开发(下一篇)
- ⏳ 功能实现
- ⏳ 部署上线
十、源码地址
GitHub: github.com/WGsummer/op…
Gitee(国内镜像): gitee.com/wgsummer/op…
结语
运维平台是一个系统工程,需要前后端配合。但这个系列我会尽量拆解得简单易懂,即使你没有开发经验,也能跟着做出来。
如果你觉得有用:
- 点赞支持一下
- 关注我,不错过后续文章
- 有问题评论区交流
周三更新第二篇:后端开发
(完)
本文是《FastAPI+Vue 搭建运维平台》系列第一篇,共 5 篇。
作者:运维老王,16年运维经验,专注运维自动化。
知乎/掘金:运维老王