
简介这是一套面向计算机专业毕业设计场景的自动化运维平台源码基于Python与Django开发适合需要完成Web运维系统课题的学生也适合希望熟悉Django项目实战的开发者。平台围绕服务器监控、日志分析、任务调度、配置管理、权限控制与RESTful API等功能模块展开可帮助管理员集中处理常见运维任务。压缩包共1132个文件包含191个Python文件、223个HTML模板、184个JS脚本、144个CSS样式及较多PNG等静态资源整体13.78MB目录结构完整其中还包含环境配置、启动脚本和文档说明便于本地运行与部署。目前已有311人学习下载。通过这套代码读者可梳理Django的MVT架构、ORM数据交互、定时任务调度逻辑及前后端配合方式同时获得一套可复用的运维平台基础框架便于二次开发或作为毕业设计答辩材料。1. 为什么说PythonDjango撑起自动化运维是最稳妥的组合先把这个zip解压后的价值看明白运维最烦的不是故障处理而是每天在重复的登录、查日志、改配置里耗掉大半天。当你拿到一个「基于PythonDjango的自动化运维平台.zip」解压后通常会看到manage.py、requirements.txt、按业务拆开的app目录——这不是玩具演示而是一套把资产、任务、告警、权限收拢到浏览器里的正经系统。Django自带的后台管理、用户认证和ORM恰好是运维平台最需要又最不想从零写的东西自动化运维平台的核心诉求是「少人工」而Django的admin、auth、migrate这些能力本身就是少人工的活例子。这篇笔记适合刚转自动化的运维、想用Python做内部系统的开发也适合想评估Django值不值得当运维开发主框架的人。2. 从.zip到能访问的Web平台环境搭建、依赖安装与数据库初始化完整步骤2.1 先确认Python和虚拟环境版本不对后面全是坑很多zip传到你手上时写它的人用的是什么Python版本你并不知道。最稳妥的做法是先确认本机环境。在终端里敲python --version python3 --version如果你在Windows上装了Python但从官网下载后没勾选「Add Python to PATH」那么python命令可能直接报错这时候要用py -V或者去pycharm里看解释器路径。在Linux上则会遇到python指向Python 2、python3才是Python 3的老问题所以后面所有命令里我都会写成python还是python3你按本机实际来。确认版本之后强烈建议建一个虚拟环境不要直接往系统Python里装依赖。这是Python项目最常见也最值得养成的习惯项目A要Django 3.2项目B要Django 4.2混在一个环境里就是翻车现场。python -m venv venv # Windows 激活 venv\Scripts\activate # Linux / macOS 激活 source venv/bin/activate激活后命令行前面会出现(venv)后面pip装什么都只影响这个虚拟环境。pycharm或vscode配python环境时也直接把解释器指到venv/bin/python或venv\Scripts\python.exe这样编辑器里的代码补全和调试走的都是同一份依赖。2.2 解压与依赖安装requirements.txt是第一个黑匣子把zip解压后先看根目录。一个典型的Django项目里一定有这几样manage.py所有命令的入口、requirements.txt依赖清单、一个与项目同名的配置包里面是settings.py、以及按功能拆分的app目录。我第一次拿到这类zip时第一反应不是急着跑而是先打开requirements.txt看它锁了哪些库。通常你会在里面看到这些依赖在运维平台里干什么DjangoWeb框架本身路由、ORM、admin、authdjangorestframework提供API接口给前端或外部系统调用celery异步任务队列跑耗时命令和定时巡检redisCelery的broker和结果后端paramiko用SSH登录服务器并执行命令requests调外部接口比如钉钉、企业微信通知channels如果需要WebSocket实时推送会看到它安装命令很简单但有个容易卡住的问题直接pip install -r requirements.txt可能会因为网络问题装到一半失败。国内环境建议先配置镜像源或者单独装某个装不上的库pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple如果某个库老是编译报错比如paramiko的依赖cryptography在Windows上装不上去官网下一个对应Python版本的wheel再pip install比硬改代码省事得多。装完执行pip list对照requirements.txt逐项核对别让依赖问题混到后面排查流程里。注意requirements.txt只写了直接依赖没有版本锁的库装出来可能是新版。Django项目对版本很敏感如果一个zip里Django 3.2对应的代码用到url()函数而你装了Django 4.xmigrate阶段就会报错。碰到这种情况先看代码里是否用了旧API再决定降版本还是改代码。2.3 数据库迁移与超级用户让Django自己建出全套表结构依赖装完接下来做的事是让Django把数据库表建出来。Django的ORM把模型定义在models.py里migrate命令根据模型自动建表、改表。初始化入口是settings.py里的INSTALLED_APPS——里面自带admin、auth、sessions这些内置应用首次migrate会把它们的表一起建好。python manage.py migrate python manage.py createsuperuser python manage.py checkmigrate会输出一串「Applying admin.0001_initial... OK」看到OK就是成功。默认数据库是SQLite文件叫db.sqlite3好处是零配置、解压即用缺点是并发写入能力弱适合开发和几十人的内部平台。如果你要换成MySQL或PostgreSQL得先改settings.py里的DATABASES配置装对应的数据库驱动然后在建表前把数据库手动创建好之后再执行上面的命令。createsuperuser会依次问你用户名、邮箱、密码。这个账号进的是Django自带的admin后台运维平台最常见的权限管理入口就在这里。manage.py check是个经常被人忽略的命令它在不跑服务的情况下检查项目配置有没有明显错误我习惯每次改完settings.py都先跑一下比直接启动再报错省时间多了。2.4 启动与登录验证runserver起来后先测这三件事一切就绪后启动开发服务器python manage.py runserver 0.0.0.0:80000.0.0.0表示监听所有网卡这样局域网里其他机器也能通过http://你的IP:8000访问。如果只想本机调试用127.0.0.1:8000更安全。起来后浏览器访问http://127.0.0.1:8000/admin/用刚才的超级用户登录。前几分钟别急着点功能先测三件事第一admin页面能否正常加分页显示说明静态文件和服务没白屏。第二点进一个资产列表或任务列表随便点一个查看详情确认数据库里的数据能被读出来。第三盯着runserver的日志看有没有红色报错尤其是404和500。本地跑不起来的项目直接部署到生产环境只会更惨。如果登录后跳转卡住或者静态资源加载不出来先看这两处settings.py里DEBUG True时Django自己处理静态文件关闭DEBUG后就必须靠Nginx或whitenoise来管静态文件。开发阶段保持DEBUGTrue登录页能正常显示一般就没问题。3. 拆解Django运维平台的核心模块资产管理、任务执行与权限控制的代码路径3.1 后台自带的管理能力为什么先白拿一套Admin跑通后很多人第一反应是「这界面真朴素」。但朴素不代表功能弱。Django自带的admin后台本质上是一套不需要你写页面就能增删改查的管理系统。运维平台最需要的资产管理、账号维护、任务记录查询直接注册进admin就能用。最常见的做法是在每个app的admin.py里注册模型# assets/admin.py from django.contrib import admin from .models import Asset, Task admin.register(Asset) class AssetAdmin(admin.ModelAdmin): list_display (hostname, ip, status, updated_at) search_fields (hostname, ip) admin.site.register(Task)list_display决定列表页显示哪些列search_fields提供右上角的搜索框。对Asset这种数量可能上千的模型这两个参数几乎是必须的不然找一台机器要靠翻页体验很差。对自动化运维平台来说admin的价值不只是「能用」它还是开发的脚手架你的自定义页面还没写好的时候先用admin把数据维护起来业务数据先流动起来再慢慢补正式界面。这也是Django做内部系统最大的优势——先白拿一套完整后台而不是从空页面开始画。3.2 资产与任务的ORM模型表结构里藏着平台的数据流拆解这类平台最先看的是models.py。运维平台最核心的两张表一张记录「我能连哪些机器」一张记录「我在机器上跑了什么」。这两张表通常是这样的关系# assets/models.py from django.db import models class Asset(models.Model): hostname models.CharField(max_length64, uniqueTrue, verbose_name主机名) ip models.GenericIPAddressField(verbose_nameIP地址) ssh_port models.IntegerField(default22, verbose_nameSSH端口) account models.CharField(max_length32, defaultroot, verbose_name登录账号) status models.CharField(max_length16, choices( (online, 在线), (offline, 离线)), defaultonline) created_at models.DateTimeField(auto_now_addTrue) class Task(models.Model): name models.CharField(max_length128, verbose_name任务名) asset models.ForeignKey(Asset, on_deletemodels.CASCADE, related_nametasks, verbose_name目标资产) command models.TextField(verbose_name命令内容) status models.CharField(max_length16, choices( (pending, 待执行), (running, 执行中), (success, 成功), (failed, 失败)), defaultpending) output models.TextField(blankTrue, verbose_name执行输出) created_at models.DateTimeField(auto_now_addTrue)ForeignKey把任务挂到资产上on_deletemodels.CASCADE表示资产被删它名下的任务记录也被删。这里要慎重资产误删连带任务记录全没如果有审计需求应该把on_delete改成PROTECT或者干脆留一个is_deleted软删除字段。这两张表已经足够说明平台的数据流先录入资产再创建任务任务选择目标资产执行后把结果写回output和status。你在界面上看到的资产列表和任务列表底层就是这两个模型的查询。理解了这个数据流后面加任何功能都不会跑偏。3.3 任务执行的两种路线subprocess直跑与Celery异步的取舍自动化运维平台的核心功能是执行任务。最常见的实现有两种区别很大。第一种直接在Django视图里用subprocess跑命令# tasks/services.py import subprocess def run_command(cmd, timeout30): try: result subprocess.run( cmd, shellTrue, capture_outputTrue, textTrue, timeouttimeout ) return result.returncode, result.stdout, result.stderr except subprocess.TimeoutExpired: return 124, , command timed out这样写简单直接短命令没问题但有两个隐患。一是shellTrue有命令注入风险如果cmd是从页面输入的用户拼一个; rm -rf xx进去后果自负。二是在本机执行命令而不是登录到资产服务器上执行很多场景不适用。更合理的做法是用paramiko的SSHClient去目标机器上执行或者用Fabric/Ansible这类封装好的库。第二种是把任务丢给Celery异步执行。Django请求-响应模型里视图函数必须等任务跑完再返回而一条命令可能跑几分钟用户会一直转圈。先配好Redis再定义任务# tasks/tasks.py from celery import shared_task from .services import run_command shared_task def run_task(task_id): task Task.objects.get(idtask_id) task.status running task.save() code, stdout, stderr run_command(task.command, timeout60) task.output stdout stderr task.status success if code 0 else failed task.save()视图里run_task.delay(task_id)就返回了剩下的在Celery worker里慢慢跑。这个改动让页面不再卡死也为批量执行、定时巡检留了口子。代价是多维护一个worker进程和一个Redis实例。对自动化运维平台我认为这个代价必须付subprocess直跑只适合内部验证。3.4 权限与审计Django Auth之外还要补什么Django自带一套完整的用户体系User模型、登录会话、组、权限。运维平台默认就该用这套而不是自己建user表再踩一遍认证坑。登录逻辑最常见的写法# users/views.py from django.contrib.auth import login, authenticate def user_login(request): if request.method POST: username request.POST.get(username) password request.POST.get(password) user authenticate(request, usernameusername, passwordpassword) if user: login(request, user) # 登录成功后session_id 会写入 cookie return redirect(/admin/) return render(request, users/login.html)login调用后Django会把session_id种到浏览器cookie里请求头带上这个cookie后端就能识别是谁。如果你做的是前后端分离前端要保存一个token常见做法是用djangorestframework自带的TokenAuthentication登录接口返回一个token字符串前端每次请求在Header里带Authorization: Token xxxx。这与cookie设置token是两套体系别混着用。权限之外运维平台必须补审计。谁在什么时间对哪台机器执行了什么命令这条记录比命令本身的输出更重要。做法很简单加一个OperationLog模型在任务执行入口记录request.user、任务ID、时间戳。没有审计的运维平台出了事查不到人比没有平台还麻烦。4. 改成你自己的运维平台新增资产字段、部署通知与WebSocket实时推送的落地改法4.1 用startapp扩展新模块给平台加一块「发布记录」的完整流程拿到平台之后很快会遇到第一个需求「给我加一个发布记录记录每次上线了什么版本。」 这在Django里是一个标准的app扩展流程python manage.py startapp publish命令会生成publish目录里面有models.py、views.py、admin.py等文件。但注意生成app后Django还不知道它的存在必须手动在settings.py的INSTALLED_APPS里加一行# settings.py INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, # ... assets, tasks, publish, # 新增 ]然后写模型、做迁移python manage.py makemigrations publish python manage.py migratemakemigrations根据models.py里的变化生成迁移文件migrate把变更落到数据库。这个两步走是Django的逻辑先记录「要改什么」再执行「怎么改」。如果你看到同事只在models.py里加字段而不跑migrate数据库里永远不会有这个字段页面一访问就报「no such column」。之后在publish/admin.py里注册admin后台就出现「发布记录」菜单了。整个流程走一遍你会理解Django内部为什么把「改表结构」做得这么重——它拿一套带历史的迁移记录把数据库结构变更纳入了版本管理生产环境升级时按迁移文件顺序执行不会因为谁手工改了一列就崩掉。4.2 通知接入把执行结果推给钉钉/邮件的最小代码任务执行完用户不可能一直盯着列表刷新。最常见的自动化工况是任务失败时把结果推送出来。钉钉群机器人是最省事的通道只需要一个webhook地址和一个requests.post# common/notify.py import requests def send_dingtalk(webhook_url, content): payload { msgtype: text, text: {content: content}, } try: resp requests.post(webhook_url, jsonpayload, timeout5) return resp.status_code 200 except requests.exceptions.RequestException: return False使用它时在Celery任务里失败时调用from common.notify import send_dingtalk if task.status failed: send_dingtalk( https://oapi.dingtalk.com/robot/send?access_tokenxxx, f任务 {task.name} 执行失败资产 {task.asset.hostname} )注意webhook地址里含有token千万别写进git仓库建议放到settings.py里的环境变量或者本地配置文件中。timeout5是必要的通知通道挂了不能反过来拖垮任务执行流程。邮件同理用Django内置的django.core.mail.send_mail配置好EMAIL_HOST、EMAIL_HOST_USER、EMAIL_HOST_PASSWORD后调用即可。这一步的收益立竿见影任务失败后不用盯屏群消息会炸出来。4.3 WebSocket实时推送后台有数据前端自动弹的任务状态很多运维平台做到后面会希望前端页面不要靠手动刷新就能看到任务状态变化。Django默认的HTTP是请求-响应模式服务端没法主动往浏览器推数据。标准的补法是引入Channels让Django具备WebSocket能力。首先把channels加进requirements.txt并安装然后在asgi.py里配置# ops_platform/asgi.py import os from channels.routing import ProtocolTypeRouter, URLRouter from django.core.asgi import get_asgi_application os.environ.setdefault(DJANGO_SETTINGS_MODULE, ops_platform.settings) application ProtocolTypeRouter({ http: get_asgi_application(), websocket: URLRouter([]), # 后面挂路由 })再加一个消费者把任务状态推送出去# tasks/consumers.py import json from channels.generic.websocket import AsyncWebsocketConsumer class TaskConsumer(AsyncWebsocketConsumer): async def connect(self): self.group_name task_status await self.channel_layer.group_add(self.group_name, self.channel_name) await self.accept() async def task_update(self, event): await self.send(text_datajson.dumps({ task_id: event[task_id], status: event[status], }))然后在Celery任务状态变更时调用channel_layer.group_send所有连到task_status的前端页面就会收到消息。前端用浏览器原生WebSocket就可以const ws new WebSocket(ws://127.0.0.1:8000/ws/tasks/); ws.onmessage (event) { const data JSON.parse(event.data); updateTaskStatus(data.task_id, data.status); };这个改动的核心思路是把状态更新从「前端问服务端要」改成「服务端主动推给前端」响应速度从轮询间隔变成毫秒级。代价是生产环境要加一个支持WebSocket的服务器比如Daphne或UvicornGunicorn本身不处理WebSocket。本地开发测试时runserver会自动带上Channels的开发server所以开发阶段感觉不到这个差别上线前一定要把部署命令换成支持WebSocket的ASGI server。4.4 调整settings.py的七处必改配置不管拿到的zip长什么样settings.py始终是第一个需要通读的文件。我按经验列一份必改清单配置项必改原因本地建议值ALLOWED_HOSTS不配置局域网访问直接DisallowedHost[*]开发生产写域名DEBUG生产环境True会暴露完整报错栈开发True部署后FalseTIME_ZONE影响任务记录和定时任务的时间Asia/ShanghaiLANGUAGE_CODEadmin界面语言zh-hansDATABASESSQLite换MySQL/PostgreSQL的入口默认SQLite先跑通STATIC_ROOT关DEBUG后静态文件收集目录部署时collectstatic用INSTALLED_APPS加不加新app、第三方组件都在这里按业务追加还有一处容易被忽略如果用了CeleryCELERY_TIMEZONE要单独设Django改了TIME_ZONE不会自动同步到Celery。定时任务如果时间不对十有八九是这里没设成Asia/Shanghai。每次改settings.py保存后跑一遍python manage.py check比等到启动时被报错教育老实得多。5. 上线部署避坑5个让Django运维平台翻车的常见问题与排查方案5.1 开发服务器能跑局域网访问不了ALLOWED_HOSTS与端口之谜现象本地runserver 127.0.0.1:8000一切正常换成局域网IPhttp://192.168.1.20:8000访问浏览器显示DisallowedHost或干脆连接超时。原因Django的安全机制要求请求Header里的Host必须在ALLOWED_HOSTS里默认只有localhost和127.0.0.1你拿局域网IP访问自然被拒如果是连接超时那就是监听地址或防火墙挡住了。解决先把settings.py里ALLOWED_HOSTS加上本机局域网IP或直接[*]再把runserver启动参数改成0.0.0.0:8000只监听127.0.0.1的话外部网络根本连不进来。最后确认服务器防火墙放行了8000端口。按这个顺序排查95%的局域网访问问题都在这三处。5.2 migrate总是报错迁移历史与脏库的教训现象执行python manage.py migrate时报InconsistentMigrationHistory或者提示某张表已存在。原因最常见是数据库目录里已经有一个旧版db.sqlite3里面残留了旧结构的表或者项目交付时把别人的迁移文件带了过来而数据库里没有对应的迁移历史记录。Django的migrate按迁移文件逐一执行发现表存在但迁移记录里没有这一条就会报不一致。解决开发环境最省事的方案是把db.sqlite3改名备份后重新migrate——这是后悔药但前提是你确认没有需要保留的数据。生产环境千万别直接删库先python manage.py showmigrations看哪些标记为[X]、哪些没有把缺失或多余的迁移文件整理干净再执行migrate --fake或手工补表。我在生产上这么操作时每次都先备份数据库文件再动手备份文件名带上日期至少能回到改之前的状态。5.3 任务执行页面转圈卡死subprocess没有超时把请求线程堵住现象点「执行任务」后浏览器一直转圈等了几分钟也没返回再点其他页面也卡住。原因如果任务执行用的是一开始那种subprocess.run且没有timeout参数一条卡死的命令就会一直占用视图线程。Django开发服务器默认是单进程多线程其中一个线程被占死其他请求排队表现就是整个平台假死。解决给subprocess加硬性超时——timeout30加异常处理超时就往任务表里写失败状态再进一步把任务执行扔给Celery视图只提交任务就返回。这是从根上解决问题任何运维命令都不该占用Web请求线程。上线后如果还有人用subprocess直跑长任务我建议直接code review拦下来。5.4 Celery任务迟迟不执行时区、队列与worker没启动的三重排查现象页面提交任务后状态一直是pending数据库里能看到任务但Celery就是不消费。原因第一反应不是代码问题而是Celery worker根本没启动。很多人只启动了runserver却忘了执行celery -A ops_platform worker --loglevelinfo。第二个常见原因是CELERY_TIMEZONE没设成Asia/Shanghai定时任务按UTC算时间总差8小时。第三个是Redis没启动worker启动时报connection refused任务根本进不了队列。解决先确认Redis在跑再启动worker然后看worker日志有没有「Received task」字样。定时任务如果依赖celery beat那要单独启动调度器worker和beat是两个进程。排查顺序我建议是进程在不在 - 队列通不通 - 时区对不对。走一遍九成问题都在这三关上。5.5 POST请求被403拦下CSRF与Token设置的两个连招现象前端表单提交POST请求返回403 CSRF verification failed用POSTMAN调接口也一样。原因Django默认开启CSRF中间件要求所有POST、PUT、DELETE请求都携带CSRF token否则拒绝。浏览器里模板表单如果忘了写{% csrf_token %}就会踩这个前后端分离的场景前端没从cookie里取csrftoken塞进请求头也会被拦。解决模板渲染的表单form里加{% csrf_token %}一行即可。前后端分离在JavaScript里读cookie里的csrftoken放进请求头X-CSRFToken同时保证CSRF_COOKIE_NAME和前端读取的名字一致。如果用的是djangorestframework通常配合TokenAuthentication登录换token之后的请求头带Authorization: Token xxx绕过CSRF校验。这里要记住cookie设置token和CSRF token是两个不同的东西一个管身份一个管防伪造别混用。6. 让平台真正扛起日常正确性验证、巡检一致性比对与一组收尾技巧把平台部署上线只是开始真正让它扛起日常要验证两件事平台看到的和服务器真实情况一不一致以及核心任务执行结果是否可复现。验证方法很简单。先在平台上对一台机器执行hostname date然后自己SSH上去跑同样的命令对比输出是否一致。这个动作能同时验证paramiko连接、命令执行、结果回写这三段链路。我还会定期抽查任务表里的output字段看有没有被截断或只能拿到半截内容——这是paramiko读取超时最典型的症状。巡检一致性比对是运维平台的灵魂平台说这个资产在线真的在线吗写一个独立于Django的脚本把平台数据库里的资产IP导出来批量ping或SSH握手再和platform里的状态字段做diff不一致的列成清单。这个脚本不值得天天跑一周一次就够了但每次跑都能揪出几个僵尸资产。我在生产上还养成了一个习惯每次改完settings.py先manage.py check再跑一遍上述对比脚本双重确认后再让平台接真实任务。生产部署不要用runserver。常见做法是gunicorn ops_platform.wsgi:application -w 4 -b 0.0.0.0:8000如果用了WebSocket就换成Daphne。再前面挂Nginx处理静态文件和反向代理。数据库记得定时备份SQLite直接备份db.sqlite3文件PostgreSQL用pg_dump备份日期留7天的轮转这是遇到误删后唯一的后悔药。回看整个平台Django这套组合在中小团队里的性价比确实很高一个后端开发加一个运维就能把资产管理、任务执行、通知、审计串起来。成本不在写代码而在把运维规范拆成Django的模型和流程。希望这篇笔记能帮你在部署和改造的路上少踩几个坑。本文还有配套的精品资源点击获取