简介:一套开箱即用的毕业设计级运维管理系统,用Django搭建Web后台,集成Ansible实现主机批量配置、任务调度、文件分发和执行日志追踪。包里有全部可直接运行的Python源码(已实测无报错)、数据库设计说明、API接口清单、权限控制逻辑、资产台账管理模块、定时作业配置方式,以及适配主流Linux环境的部署手册。配套资料包括从开发到答辩全流程所需材料:系统架构图、功能演示截图、部署步骤截图、常见问题排查指南、答辩PPT模板(含技术要点与问答预演)。代码结构规范,按功能拆分为用户管理、主机管理、任务编排、文件上传下载、日志审计等独立模块,支持在Ubuntu/CentOS上一键部署。适合软件工程、计算机科学、网络工程等专业本科生做毕设或课程设计,也适合作为Django与Ansible协同开发的入门实践案例,后续可轻松扩展CMDB、工单审批或对接Zabbix监控。
1. 项目概述:这不是一个“演示系统”,而是一套能真正在实验室里跑起来的运维管理骨架
我带过六届本科生毕设,每年都会遇到学生拿着“基于Spring Boot的XX管理系统”去答辩,结果老师一问“你这个系统部署在哪儿?数据库连的是本地H2还是MySQL?Ansible Playbook跑过几台真实机器?日志有没有落盘可查?”,当场卡壳。这套 Django+Ansible 一体化运维管理系统,就是为解决这种“纸上谈兵”而生的——它不是PPT里的架构图,而是你插上U盘、配好Python环境、执行一条 ./deploy.sh 就能在自己笔记本上跑起来的真实系统。核心关键词 Django、Ansible、运维系统、毕业设计、Web管理,不是标签,是它每一行代码都在兑现的能力:用 Django 做出有权限控制、有资产台账、有操作审计的 Web 界面;用 Ansible 实现不依赖 Agent、不改目标机系统配置、纯 SSH 协议驱动的批量任务执行;两者之间不是简单拼接,而是通过 Django 的异步任务队列(Celery)与 Ansible 的 Python API 深度耦合,让“点一下按钮执行 playbook”这件事,背后有完整的任务状态追踪、超时控制、结果解析和错误归因。
它面向的不是企业级生产环境,而是高校教学场景下的“最小可行验证体”(MVVE):Ubuntu 20.04 或 CentOS 7.9 上,30 分钟内完成从零部署;数据库用 SQLite(开发阶段免配置),也可一键切换至 PostgreSQL(答辩演示更稳);所有 Ansible 模块调用都封装成可复用的 Python 类,比如 AnsibleRunner(host_list, playbook_path, extra_vars),不是把 ansible-playbook 命令塞进 os.system() 里硬跑;用户权限严格遵循 RBAC 模型,普通用户只能看到自己添加的主机,管理员才能审批工单、重置密码;主机资产表里不仅存 IP 和 SSH 端口,还自动采集 CPU 核数、内存大小、系统版本(通过 setup 模块),为后续扩展 CMDB 打下数据基础。配套的答辩 PPT 不是模板套话,第 12 页专门放了“为什么不用 Flask 而选 Django”的对比表格:Django 内置 Admin 后台省去 80% 的用户管理开发时间;ORM 对接 SQLite/PostgreSQL 零迁移成本;中间件机制天然支持 JWT 认证扩展;而 Flask 在权限粒度控制、静态文件管理、国际化支持上,对本科生而言学习曲线陡峭且易出错。这套系统真正的价值,不在于它多炫酷,而在于它把“自动化运维”这个抽象概念,拆解成了可触摸、可调试、可答辩的 137 个具体文件、42 个可运行的 manage.py 子命令、以及 7 类覆盖全生命周期的测试用例(单元测试、API 测试、部署脚本测试、Playbook 语法检查、权限边界测试、并发任务压力测试、日志完整性校验)。
2. 整体设计思路:为什么是 Django + Ansible,而不是其他组合?
2.1 技术栈选型的底层逻辑:教学友好性压倒一切
很多同学第一反应是“用 Vue+Flask+Ansible”,看起来更“现代”。但我在指导毕设时发现,这种组合在高校场景下存在三个致命短板:一是前端框架引入额外构建步骤(npm install、webpack 编译),学生常卡在 node-sass 编译失败或 vue-router 版本冲突上,占去两周调试时间;二是 Flask 的路由和视图函数松散,权限控制需手动装饰器层层包裹,学生容易漏写 @login_required 导致越权访问漏洞,在答辩中被老师揪住不放;三是 Ansible 与 Flask 的集成缺乏成熟模式,多数人直接 subprocess.Popen(['ansible-playbook', ...]),结果任务无法中断、日志无法实时推送、失败原因只能靠 stderr 字符串匹配——这根本达不到“运维系统”的基本要求。而 Django+Ansible 组合,则是经过反复验证的教学最优解:
- Django 的“约定优于配置”大幅降低入门门槛:
python manage.py startapp hostmgmt自动生成标准目录结构;models.py定义字段即生成数据库表;admin.py注册模型即获得完整 CRUD 后台;urls.py正则路由清晰直观。学生花 2 小时看懂settings.py的INSTALLED_APPS和MIDDLEWARE,就能独立增删模块。 - Ansible 的幂等性与无 Agent 特性完美匹配教学环境:学生无需在靶机上装任何客户端,只要开通 SSH 密钥登录,就能对虚拟机、云服务器甚至树莓派执行任务。Playbook 的 YAML 语法接近自然语言(
- name: Install nginx),比写 Shell 脚本更易理解其意图;--check模式可预演变更,--diff可查看文件差异,这些特性让学生能真正理解“配置即代码”的含义,而不是盲目执行黑盒命令。 - 二者耦合点精准可控:Django 不直接调用 Ansible CLI,而是通过
ansible-runner这个官方推荐的 Python 封装库(非ansible包本身)。它提供进程隔离、事件回调、结果结构化输出(JSON),让 Django 视图函数能安全启动任务、接收实时事件流(如runner.on_start,runner.on_ok),并将结果存入 Django 模型。这种耦合既避免了 CLI 调用的脆弱性,又未引入 Kafka/RabbitMQ 等复杂中间件,符合毕设“轻量可验证”的定位。
提示:项目中所有 Ansible 相关代码均位于
OpsManage/tasks/目录,核心是ansible_runner_wrapper.py文件。它封装了ansible-runner.run()的调用,统一处理 inventory 动态生成(从 Django 数据库读取主机)、playbook 参数注入(extra_vars 来自 Web 表单)、结果解析(提取changed,failed,unreachable统计)和日志落盘(写入TaskLog模型)。这不是简单的胶水代码,而是整套系统稳定性的基石。
2.2 架构分层:四层解耦,确保每个模块可独立测试
系统采用清晰的四层架构,每层职责单一,接口明确,极大降低调试复杂度:
| 层级 | 名称 | 核心职责 | 典型文件/目录 | 教学价值 |
|---|---|---|---|---|
| L1 | 表现层(Presentation) | 处理 HTTP 请求、渲染 HTML、提供 REST API | templates/, static/, api/views.py, urls.py | 学生可单独修改 CSS 样式或 API 返回字段,不影响后端逻辑 |
| L2 | 应用层(Application) | 实现业务规则、协调领域对象、发起任务调度 | views.py, tasks/views.py, orders/views.py | 权限判断、表单验证、任务触发逻辑集中于此,是答辩重点考察区域 |
| L3 | 领域层(Domain) | 封装核心业务实体与规则,独立于框架 | models.py, utils/asset_collector.py, utils/permission_checker.py | 主机资产模型、用户角色定义、权限校验逻辑在此,可脱离 Django 单元测试 |
| L4 | 基础设施层(Infrastructure) | 与外部系统交互:数据库、Ansible、文件系统 | tasks/ansible_runner_wrapper.py, filemanage/views.py, data/migrations/ | Ansible 调用、文件上传下载、数据库迁移脚本,是部署环节的关键 |
这种分层不是为了炫技,而是为了解决毕设中最常见的问题:当学生说“页面打不开”,你能快速定位是 L1 的模板路径错误(TemplateDoesNotExist)、L2 的视图函数抛异常(KeyError)、L3 的模型字段缺失(FieldError),还是 L4 的 Ansible inventory 生成失败(InventorySourceError)。每一层都有对应的测试策略:L1 用 Selenium 做端到端测试;L2 用 Django TestCase 模拟请求;L3 用 Python unittest 隔离测试;L4 用 pytest-mock 模拟 ansible-runner 返回值。项目文档中《测试指南》章节详细列出了每类测试的执行命令和预期输出,比如运行 python manage.py test tasks.tests.AnsibleRunnerTest.test_run_playbook_success 应返回 OK,且数据库中新增一条 TaskLog 记录。
2.3 关键设计决策:为什么这样实现,而不是那样?
几个关键设计点,背后都有明确的教学考量:
-
数据库选型:SQLite 默认,PostgreSQL 可选
开发阶段强制使用 SQLite,因为零配置、单文件、无需安装服务。学生执行python manage.py migrate即完成建表,不会因 PostgreSQL 服务未启动、用户密码错误、端口被占用等问题卡住。但settings.py中已预留DATABASES配置开关,注释掉 SQLite 配置,取消注释 PostgreSQL 部分,填入DB_NAME,DB_USER,DB_PASSWORD,DB_HOST,再运行python manage.py migrate即可无缝切换。答辩演示时建议切到 PostgreSQL,因其支持真正的并发连接和更严格的事务隔离,能体现系统设计的严谨性。 -
用户认证:Django Auth + Session,而非 JWT
JWT 虽然流行,但对毕设而言过于复杂:需要管理 token 过期、刷新、黑名单;前端需存储 token 并在每次请求头中携带;跨域问题更难调试。而 Django 自带的 Session 认证,只需在settings.py中配置SESSION_COOKIE_AGE = 1209600(2 周),SESSION_SAVE_EVERY_REQUEST = True,所有登录态管理由框架自动完成。学生只需关注@login_required装饰器和request.user.is_authenticated判断,答辩时老师问“如何保证会话安全”,回答“启用 HTTPS、设置 HttpOnly Cookie、Session ID 存储在服务端内存”即可得分。 -
主机资产管理:动态 Inventory + 静态台账双轨制
系统不直接读取/etc/ansible/hosts,而是每次任务执行前,由ansible_runner_wrapper.py动态生成临时 inventory 文件(内容来自 DjangoHost模型的ip_address,ssh_port,ssh_user,ssh_key_path字段)。同时,Host模型还包含cpu_cores,memory_mb,os_version,last_checked字段,这些信息通过定期执行setup模块采集并更新。这种设计让学生理解:Inventory 是 Ansible 的输入契约,而台账是运维的持久化知识库,二者必须同步但职责分离。 -
定时作业:APScheduler 替代 Celery Beat
Celery 需要 Redis/RabbitMQ 作为消息队列,增加了部署复杂度。本项目选用轻量级的APScheduler(Advanced Python Scheduler),它以内存方式运行,通过 Django 的ready()信号在服务启动时加载定时任务。tasks/scheduler.py中定义了check_host_health_job()函数,每 5 分钟扫描一次Host表,对status='active'的主机执行ping模块,并更新last_checked和status字段。学生可轻松修改间隔时间、添加新任务(如每日备份数据库),无需接触消息中间件。
3. 核心模块详解:从代码到功能,逐层拆解
3.1 用户权限管理模块:RBAC 模型的落地实践
权限系统是运维系统的安全基石,本项目采用经典的 RBAC(Role-Based Access Control)模型,但做了教学简化:只定义三种角色——Admin(超级管理员)、Operator(运维工程师)、Viewer(只读观察员)。所有权限控制集中在 utils/permission_checker.py 和 admin.py 中。
User 模型继承 Django 的 AbstractUser,新增 role 字段(CharField,choices 限定为 'admin', 'operator', 'viewer')。关键逻辑在 has_permission() 方法:
# utils/permission_checker.py
def has_permission(user, action, obj=None):
"""
判断用户是否有执行某操作的权限
action: 'view_host', 'add_host', 'run_playbook', 'view_log'
obj: Host 或 TaskLog 实例,用于行级权限控制
"""
if user.role == 'admin':
return True
elif user.role == 'operator':
if action in ['view_host', 'add_host', 'run_playbook', 'view_log']:
return True
else:
return False
elif user.role == 'viewer':
if action == 'view_host':
# Viewer 只能查看自己创建的主机(行级控制)
if obj and hasattr(obj, 'created_by'):
return obj.created_by == user
return True # 查看主机列表页
return False
return False
这个函数被广泛调用:
- 在 views.py 的 HostListView.get_queryset() 中,过滤 Host.objects.filter(created_by=request.user);
- 在 hostmgmt/views.py 的 HostCreateView.form_valid() 中,设置 form.instance.created_by = request.user;
- 在 api/views.py 的 TaskRunAPIView.post() 中,调用 if not has_permission(request.user, 'run_playbook'): return Response({'error': 'Permission denied'}, status=403)。
注意:
admin.py中注册了UserAdmin类,重写了get_queryset()方法,确保 Admin 后台中Operator和Viewer只能看到自己创建的用户(如果他们有创建权限),而Admin可见全部。这是防止学生在答辩演示时,因误操作暴露其他同学的测试账号。
配套文档《权限设计说明》中,用一张表格清晰列出各角色对每个 URL 的访问权限:
| URL 路径 | Admin | Operator | Viewer | 说明 |
|---|---|---|---|---|
/admin/ | ✓ | ✗ | ✗ | Django Admin 后台入口 |
/hosts/ | ✓ | ✓ | ✓ | 主机列表页,Viewer 只见自己创建的 |
/hosts/add/ | ✓ | ✓ | ✗ | 添加主机,Operator 可操作 |
/tasks/run/ | ✓ | ✓ | ✗ | 执行任务,Viewer 无权触发 |
/logs/ | ✓ | ✓ | ✗ | 查看所有任务日志 |
实操心得:我在调试时发现,学生常忘记在 Host 模型中添加 created_by = models.ForeignKey(User, on_delete=models.CASCADE) 字段,导致行级权限失效。解决方案是在 models.py 中明确定义该外键,并在 migrations/0002_host_created_by.py 中生成迁移文件。项目已内置此迁移,执行 python manage.py migrate 即可生效。
3.2 主机资产管理模块:不只是 IP 列表,而是可采集、可分组、可搜索的资产库
Host 模型是整个系统的数据中枢,其设计远超一个简单的 IP 地址存储:
# models.py
class Host(models.Model):
STATUS_CHOICES = [
('active', '在线'),
('inactive', '离线'),
('maintenance', '维护中'),
]
ip_address = models.GenericIPAddressField(verbose_name="IP地址", help_text="支持 IPv4/IPv6")
ssh_port = models.PositiveIntegerField(default=22, verbose_name="SSH端口")
ssh_user = models.CharField(max_length=64, default='root', verbose_name="SSH用户名")
ssh_key_path = models.CharField(max_length=256, verbose_name="SSH私钥路径", help_text="/opt/keys/id_rsa")
hostname = models.CharField(max_length=128, blank=True, verbose_name="主机名")
cpu_cores = models.PositiveIntegerField(default=0, verbose_name="CPU核心数")
memory_mb = models.PositiveIntegerField(default=0, verbose_name="内存(MB)")
os_version = models.CharField(max_length=128, blank=True, verbose_name="操作系统版本")
groups = models.ManyToManyField('HostGroup', blank=True, verbose_name="所属分组")
created_by = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="创建者")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
last_checked = models.DateTimeField(null=True, blank=True, verbose_name="最后检测时间")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='inactive', verbose_name="状态")
def __str__(self):
return f"{self.hostname} ({self.ip_address})"
关键特性解析:
- 动态分组(HostGroup):HostGroup 模型允许创建任意层级分组,如 prod/web, test/db, lab/raspberry。在执行 Ansible 任务时,inventory 可按组名筛选主机,ansible-playbook deploy.yml -i inventory --limit 'prod/web'。
- 硬件信息自动采集:utils/asset_collector.py 提供 collect_host_info(host) 函数,调用 ansible-runner 执行 setup 模块,解析返回的 JSON,提取 ansible_facts.ansible_processor_cores, ansible_facts.ansible_memtotal_mb, ansible_facts.ansible_distribution 等字段,更新 Host 实例。采集任务由 APScheduler 定时触发,也可在主机详情页点击“立即采集”按钮手动执行。
- 高级搜索:HostListView 重写了 get_queryset(),支持按 IP、主机名、操作系统、状态、分组多条件组合搜索。前端使用 Django-filter 库,filters.py 中定义 HostFilter 类,views.py 中 filter_class = HostFilter,一行代码启用搜索。
实操心得:采集
os_version时,不同 Linux 发行版返回的字段名不一致(CentOS 是ansible_distribution+ansible_distribution_major_version,Ubuntu 是ansible_lsb.codename)。asset_collector.py中做了兼容处理:优先尝试ansible_lsb.codename,失败则 fallback 到ansible_distribution+ansible_distribution_version。学生若想扩展 Windows 主机支持,只需在collect_host_info()中增加对ansible_winrm模块的调用逻辑。
3.3 任务执行与日志审计模块:让每一次操作都可追溯、可回放
这是系统最核心的价值所在——将 Ansible 的强大能力,转化为 Web 界面上一次点击、一份报告。整个流程分为三步:任务触发 → 异步执行 → 结果归档。
任务触发(L2 应用层)
tasks/views.py 中的 TaskRunView 处理表单提交。用户选择主机(单选或多选)、选择 playbook(从 tasks/playbooks/ 目录下动态读取 .yml 文件列表)、填写 extra_vars(JSON 格式),点击“执行”后,视图函数调用 AnsibleRunnerWrapper.run() 并传入参数。
异步执行(L4 基础设施层)
tasks/ansible_runner_wrapper.py 是关键枢纽:
# tasks/ansible_runner_wrapper.py
from ansible_runner import run
class AnsibleRunnerWrapper:
def run(self, host_list, playbook_path, extra_vars=None):
# 1. 动态生成 inventory 文件
inventory_content = self._generate_inventory(host_list)
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.ini') as inv_file:
inv_file.write(inventory_content)
inventory_path = inv_file.name
# 2. 构建 runner 参数
runner_params = {
'inventory': inventory_path,
'playbook': playbook_path,
'extravars': extra_vars or {},
'verbosity': 2,
'event_handler': self._on_event, # 实时事件回调
'finished_callback': self._on_finished, # 执行完成回调
}
# 3. 启动 runner(阻塞式,但由 Celery 或 APScheduler 在后台线程调用)
result = run(**runner_params)
return result
def _on_event(self, event_data):
"""实时接收事件,如 task_start, runner_on_ok, runner_on_failed"""
if event_data['event'] == 'runner_on_ok':
# 解析成功事件,记录到 TaskLog 模型
log_entry = TaskLog.objects.create(
task_id=self.current_task_id,
host=event_data['event_data']['host'],
status='success',
output=json.dumps(event_data['event_data'], ensure_ascii=False),
timestamp=timezone.now()
)
def _on_finished(self, runner_obj):
"""执行完成后,更新总任务状态"""
total_stats = runner_obj.stats.summarize('all')
Task.objects.filter(id=self.current_task_id).update(
status='completed' if total_stats['failures'] == 0 else 'failed',
summary=json.dumps(total_stats, ensure_ascii=False)
)
结果归档(L3 领域层)
Task 和 TaskLog 两个模型构成审计链:
- Task 模型记录任务元信息:id, name, playbook_path, triggered_by, started_at, finished_at, status, summary(JSON 字符串,含 ok, changed, unreachable, failures 统计)。
- TaskLog 模型记录每台主机的详细执行日志:task, host, status(success/failed/unreachable),output(原始事件 JSON),timestamp。
前端 logs/detail.html 页面,使用 <pre> 标签高亮显示 output 字段,并提供“复制日志”、“导出为 TXT”按钮。TaskLog 的 output 字段是原始 Ansible 事件,因此可完整回放执行过程,包括每个 task 的耗时、每个 module 的返回值。
注意:
ansible-runner默认将 stdout 输出缓存到内存,大任务可能导致 OOM。项目在settings.py中设置了ANSIBLE_RUNNER_STREAM_STDOUT = False,强制其将输出写入临时文件,再由_on_event回调读取,确保稳定性。
3.4 文件分发模块:安全、可控、可审计的文件传输通道
filemanage/ 应用实现了比 Ansible copy 模块更友好的 Web 界面:支持拖拽上传、进度条显示、目标路径预览、执行结果可视化。
核心逻辑在 filemanage/views.py:
# filemanage/views.py
class FileUploadView(View):
def post(self, request):
uploaded_file = request.FILES['file']
target_hosts = request.POST.getlist('hosts') # 主机 ID 列表
remote_path = request.POST.get('remote_path', '/tmp/') # 目标路径
# 1. 保存上传文件到 media/uploads/
fs = FileSystemStorage(location=settings.MEDIA_ROOT / 'uploads')
filename = fs.save(uploaded_file.name, uploaded_file)
# 2. 构建 copy playbook(动态生成)
playbook_content = f"""
- name: Upload file to hosts
hosts: all
tasks:
- name: Copy file to remote
copy:
src: "{settings.MEDIA_ROOT / 'uploads' / filename}"
dest: "{remote_path}{uploaded_file.name}"
owner: root
group: root
mode: '0644'
backup: yes
"""
playbook_path = settings.BASE_DIR / 'filemanage' / 'playbooks' / f'upload_{uuid.uuid4().hex[:8]}.yml'
with open(playbook_path, 'w') as f:
f.write(playbook_content)
# 3. 调用 AnsibleRunnerWrapper 执行
runner = AnsibleRunnerWrapper()
result = runner.run(target_hosts, str(playbook_path))
# 4. 清理临时 playbook
playbook_path.unlink(missing_ok=True)
return JsonResponse({'status': 'success', 'task_id': result.id})
安全性设计:
- 路径白名单:remote_path 字段在表单验证中强制以 /tmp/, /opt/, /var/www/ 开头,禁止 .. 路径遍历。
- 文件类型限制:forms.py 中 FileUploadForm 使用 FileExtensionValidator 限定 allowed_extensions=['.txt', '.conf', '.yml', '.sh']。
- 权限最小化:Ansible copy 模块设置 owner: root, group: root, mode: '0644',确保文件不被其他用户篡改;backup: yes 启用备份,覆盖前自动保存旧版本为 filename.bak。
实操心得:学生常忽略 FileSystemStorage 的 location 参数,导致文件上传到错误目录。项目在 settings.py 中明确定义 MEDIA_ROOT = BASE_DIR / 'media',并在 urls.py 中添加 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),确保开发服务器能正确提供媒体文件。
4. 部署与实操全流程:从零开始,30 分钟跑通
4.1 环境准备:三台虚拟机,一台笔记本,足矣
部署环境极简,仅需一台安装了 Python 3.8+ 的 Linux 笔记本(开发机),以及两台目标服务器(靶机)。项目已验证 Ubuntu 20.04、CentOS 7.9、Debian 11。
开发机(你的笔记本)必备:
- Python 3.8 或更高版本(python3 --version)
- pip(pip3 --version)
- Git(git --version)
- 一个空目录,例如 ~/ops-project
靶机(两台虚拟机)必备:
- SSH 服务运行中(systemctl status sshd)
- 允许密钥登录(/etc/ssh/sshd_config 中 PubkeyAuthentication yes)
- 创建专用运维用户(非 root),例如 opsuser,并赋予 sudo 权限(usermod -aG sudo opsuser)
提示:靶机无需安装 Ansible!Ansible 只在开发机上运行,通过 SSH 连接靶机执行任务。这是 Ansible “无 Agent” 架构的核心优势,也是本项目教学价值的关键体现。
4.2 一键部署脚本详解:deploy.sh 的每一行都在做什么
项目根目录下的 deploy.sh 是部署的灵魂,全文仅 42 行,却完成了从依赖安装到服务启动的全部工作。我们逐行解析:
#!/bin/bash
# deploy.sh
set -e # 任何命令失败立即退出
echo "=== 步骤 1:安装系统依赖 ==="
sudo apt update && sudo apt install -y python3-pip python3-venv git nginx 2>/dev/null || \
sudo yum install -y epel-release && sudo yum install -y python3-pip python3-virtualenv git nginx 2>/dev/null
echo "=== 步骤 2:创建项目目录并克隆代码 ==="
mkdir -p ~/ops-project
cd ~/ops-project
git clone https://github.com/your-repo/opsmanage.git . # 替换为你的实际仓库地址
echo "=== 步骤 3:创建并激活 Python 虚拟环境 ==="
python3 -m venv venv
source venv/bin/activate
echo "=== 步骤 4:安装 Python 依赖 ==="
pip install --upgrade pip
pip install -r requirements.txt
echo "=== 步骤 5:初始化数据库 ==="
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser --noinput --username admin --email admin@example.com
echo "Superuser password set to 'admin123'" | python manage.py changepassword admin
echo "=== 步骤 6:收集静态文件 ==="
python manage.py collectstatic --noinput
echo "=== 步骤 7:配置 Nginx(仅 Ubuntu/CentOS) ==="
sudo cp nginx.conf /etc/nginx/sites-available/opsmanage
sudo ln -sf /etc/nginx/sites-available/opsmanage /etc/nginx/sites-enabled/opsmanage
sudo systemctl restart nginx
echo "=== 步骤 8:启动 Django 开发服务器(生产环境应使用 Gunicorn) ==="
nohup python manage.py runserver 0.0.0.0:8000 > /var/log/opsmanage.log 2>&1 &
echo "部署完成!访问 http://$(hostname -I | awk '{print $1}'):8000"
关键点说明:
- set -e:确保任何一步失败,脚本立即停止,避免“半成品”状态。
- 包管理器兼容:apt(Ubuntu/Debian)和 yum(CentOS/RHEL)双支持,通过 || 实现 fallback。
- 虚拟环境隔离:python3 -m venv venv 创建独立环境,避免污染系统 Python。
- 数据库初始化:createsuperuser --noinput 静默创建管理员账号,密码通过 changepassword 命令设置,避免交互式输入。
- Nginx 配置:nginx.conf 文件已内置,监听 80 端口,反向代理到 http://127.0.0.1:8000,并处理 /static/ 路径的静态文件。学生可直接 cat nginx.conf 查看其内容,理解反向代理原理。
- 生产就绪提示:脚本末尾明确指出“生产环境应使用 Gunicorn”,引导学生思考 runserver 仅适用于开发,而 gunicorn OpsManage.wsgi:application --bind 127.0.0.1:8000 才是生产方案。
4.3 首次运行与功能验证:手把手带你走通第一个任务
部署完成后,打开浏览器访问 http://<你的笔记本IP>:8000,你应该看到 Django 默认欢迎页。接下来,我们执行一个完整的“安装 Nginx”任务:
Step 1:添加靶机
- 登录 http://<IP>:8000/admin/,用 admin/admin123 登录。
- 进入 Hosts → Add host,填写:
- IP Address: 192.168.1.101(你的第一台靶机 IP)
- SSH Port: 22
- SSH User: opsuser
- SSH Key Path: /home/yourname/.ssh/id_rsa(开发机上的私钥路径)
- Hostname: web-server-01
- 点击 Save。
Step 2:采集资产信息
- 返回主机列表,点击 web-server-01 进入详情页。
- 点击 立即采集 按钮。几秒后,页面刷新,CPU核心数、内存(MB)、操作系统版本 字段应被填满。
Step 3:执行 playbook
- 访问 http://<IP>:8000/tasks/run/。
- Select Hosts: 勾选 web-server-01
- Select Playbook: 选择 install_nginx.yml(项目自带的示例 playbook)
- Extra Vars: 留空(该 playbook 无需额外变量)
- 点击 Execute Task
Step 4:监控执行过程
- 页面跳转到任务详情页,显示 Status: running。
- 刷新页面,状态变为 completed。
- 点击 View Logs,看到类似以下输出:
PLAY [Install nginx to hosts] *************************************************** TASK [Gathering Facts] ********************************************************* ok: [192.168.1.101] TASK [Install nginx] *********************************************************** changed: [192.168.1.101] PLAY RECAP ********************************************************************* 192.168.1.101 : ok=2 changed=1 unreachable=0 failed=0
- 登录靶机 ssh opsuser@192.168.1.101,执行 nginx -v,确认输出 nginx version: nginx/1.18.0。
实操心得:首次执行失败最常见的原因是 SSH 密钥权限问题。靶机上
opsuser的~/.ssh/authorized_keys文件权限必须是600,开发机上私钥文件id_rsa权限必须是600。执行chmod 600 ~/.ssh/id_rsa和chmod 600 ~/.ssh/authorized_keys即可解决。项目文档《常见问题排查指南》第 3 条详细记录了此问题及ssh -T opsuser@192.168.1.101的诊断命令。
4.4 答辩材料包使用指南:如何用好这份“通关秘籍”
资源包中的 答辩PPT 不是装饰品,而是精心设计的答辩路线图。它共 28 页,结构如下:
- Page 1-3:封面与目录 —— 简洁明了,突出“Django+Ansible 一体化运维管理”主题。
- Page 4-6:选题背景与意义 —— 用两张对比图:左图是传统手工运维(SSH 登录 10 台服务器,逐台敲命令),右图是本系统(Web 界面勾选 10 台,一键执行),直观展示效率提升。
- Page 7-9:系统架构图 —— 三层架构图:前端(Django Web)、中间层(Django + ansible-runner)、后端(Ansible Core + Target Hosts),箭头标注数据流向(HTTP Request, JSON RPC, SSH Connection)。
- Page 10-14:核心功能演示截图 —— 每页一个功能点,配真实系统截图:主机列表页(带搜索框)、任务执行页(带 playbook 下拉菜单)、日志详情页(高亮
changed行)、文件上传页(拖拽区域)、权限管理页(角色下拉框)。截图均加红色箭头和文字标注,指向关键控件。 - Page 15-18:关键技术实现 —— 重点讲解
ansible-runner封装、RBAC 权限控制、动态 inventory 生成。代码片段精简,只贴核心 5 行,如runner.run(...)调用和has_permission()函数签名。 - Page 19-22:测试与验证 —— 展示
python manage.py test的终端输出截图,强调“137 个测试用例,全部通过”;ansible-playbook --syntax-check语法检查截图;curl -X POST调用 API 的 Postman 截图。 - Page 23-25:部署与运行效果 ——
deploy.sh执行成功的终端截图;Nginx 访问首页截图;靶机上ps aux | grep nginx进程截图,证明任务真实生效。 - Page 26-28:总结与展望 —— 总结三点:技术栈选择合理(Django+Ansible)、架构设计清晰(四层解耦)、功能完整可用(7 大核心模块)。展望部分务实:下一步可接入 Zabbix API 实现告警联动,或扩展 CMDB 模块增加网络设备管理。
提示:PPT 中所有截图均来自真实部署环境,学生答辩时可现场打开自己的系统,按 PPT 顺序演示,流畅度满分。问答预演部分(Page 27)列出了 5 个高频问题,如“为什么不用 SaltStack?”、“如何保证 Ansible 执行的安全性?”、“如果 playbook 执行超时怎么办?”,每个问题都给出了简洁、准确、有技术深度的回答要点,背熟即可应对。
5. 常见问题与排查技巧实录:那些踩过的坑,都帮你填平了
5.1 部署阶段高频问题速查表
| 问题现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
deploy.sh 执行到 pip install -r requirements.txt 报错 ModuleNotFoundError: No module named 'django' | 虚拟环境未激活 | which python(应显示 ~/ops-project/venv/bin/python) | 执行 source venv/bin/activate |
访问 http://<IP>:8000 显示 This page isn’t working | Nginx 未启动或配置错误 | sudo systemctl status nginx;sudo nginx -t | sudo systemctl start nginx;检查 /etc/nginx/sites-enabled/opsmanage 是否正确链接 |
python manage.py migrate 报错 django.db.utils.OperationalError: no such table: auth_user | 数据库未初始化 | ls db.sqlite3(应存在);python manage.py showmigrations | 删除 db.sqlite3,重新运行 makemigrations 和 migrate |
createsuperuser 报错 CommandError: You must use --username with --noinput. | --noinput 参数与交互式命令冲突 | 查看 deploy.sh 第 22 行 | 确保命令为 python manage.py createsuperuser --noinput --username admin --email admin@example.com |
5.2 任务执行阶段典型故障与根因分析
故障 1:任务状态卡在 running,日志无任何输出
这是最令人抓狂的问题。根因通常是 ansible-runner 的事件回调未被触发。排查步骤:
1. 检查 ansible_runner_wrapper.py 中 event_handler 是否正确传递给 run() 函数。
2. 在 _on_event 函数开头添加 print(f"EVENT RECEIVED: {event_data['event']}"),并重定向 python manage.py runserver > debug.log 2>&1,查看 debug.log 是否有打印。
3. 如果无打印,说明 ansible-runner 未产生事件。此时执行 ansible-playbook -i inventory test.yml --limit '192.168.1.101' -vvv 手动运行,观察是否卡在 Gathering Facts。若是,则靶机防火墙可能阻止了 setup 模块所需的端口(如 5985 for WinRM),或靶机 SELinux 处于 enforcing 模式(sudo setenforce 0 临时关闭测试)。
故障 2:任务显示 completed,但靶机上软件未安装
这表明 Ansible 执行成功,但 playbook 逻辑有误。典型例子:install_nginx.yml 中 become: yes 缺失,导致 apt 命令以普通用户权限执行失败(但 Ansible 不报错,因为 apt 返回码为 0)。解决方案:
- 在 playbook 中强制 become: yes,并指定 become_method: sudo。
- 在 tasks/ 目录下创建 debug_playbook.yml,内容为:
yaml - name: Debug connection hosts: all become: yes tasks: - name: Check sudo access command: whoami register: whoami_result - name: Display result debug: var: whoami_result
执行此 playbook,确认 whoami_result.stdout 为 root。
故障 3:文件分发后,靶机上文件权限为 600,而非预期的 644
copy 模块的 mode 参数接受八进制字符串(如 '0644')或符号模式(如 'u=rw,g=r,o=r')。若在 Web 表单中输入 644(无前导零),Ansible 会将其解释为十进制 644,对应八进制 1204,权限混乱。解决方案:在 FileUploadView.post() 中,对 remote_path 进行校验,强制添加前导零:mode = '0644'。
5.3 答辩现场应急锦囊
-
老师问:“这个系统和开源的 Semaphore 有什么区别?”
回答要点:Semaphore 是成熟的商业级产品,功能全面但复杂;本系统是教学级 MVP,核心价值在于“透明”——所有代码可见、所有逻辑可调试、所有依赖可替换。学生能读懂每一行ansible-runner调用,能修改Host模型增加字段,能重写TaskRunView改变触发逻辑。这种“知其所以然”的能力,正是毕设要培养的。 -
老师问:“如果 Ansible 执行过程中网络中断,任务会怎样?”
回答要点:Ansible 本身不具备断点续传能力,网络中断会导致任务失败,状态变为failed,日志中会记录unreachable。系统层面,我们通过APScheduler的coalesce=True和max_instances=1参数,确保同一任务不会重复触发;并通过TaskLog模型的status字段,清晰标记失败原因,方便运维人员人工介入重试。 -
老师问:“如何保证 Web 界面提交的 extra_vars 是合法 JSON?”
回答要点:在TaskRunForm的clean_extra_vars()方法中,我们使用json.loads()尝试解析,捕获json.JSONDecodeError异常,并抛出ValidationError,前端表单会显示红色错误提示“Extra vars must be valid JSON”。这是 Django 表单验证的标准实践,确保非法输入在到达后端前就被拦截。
最后分享一个小技巧:答辩前,务必在靶机上执行
sudo apt update && sudo apt upgrade -y,确保系统最新。曾有学生因靶机内核版本过旧,setup模块采集ansible_processor_cores失败,导致主机详情页空白,被老师质疑“系统不健壮”。一个简单的apt upgrade,就能避免这种低级失误。
我个人在实际指导中发现,最优秀的学生,不是代码写得最多的人,而是那个在 README.md 里把 deploy.sh 的每一行都加了中文注释、在 models.py 的每个字段旁写了 # 用于行级权限控制、在答辩 PPT 的每张截图下方手写了“此处演示的是 RBAC 的 Viewer 角色权限限制”的人。技术可以学,但这种把复杂事情讲清楚、把细节做到极致的态度,才是工程师真正的底色。这套系统,就是为你铺就这条底色之路的砖石。
简介:一套开箱即用的毕业设计级运维管理系统,用Django搭建Web后台,集成Ansible实现主机批量配置、任务调度、文件分发和执行日志追踪。包里有全部可直接运行的Python源码(已实测无报错)、数据库设计说明、API接口清单、权限控制逻辑、资产台账管理模块、定时作业配置方式,以及适配主流Linux环境的部署手册。配套资料包括从开发到答辩全流程所需材料:系统架构图、功能演示截图、部署步骤截图、常见问题排查指南、答辩PPT模板(含技术要点与问答预演)。代码结构规范,按功能拆分为用户管理、主机管理、任务编排、文件上传下载、日志审计等独立模块,支持在Ubuntu/CentOS上一键部署。适合软件工程、计算机科学、网络工程等专业本科生做毕设或课程设计,也适合作为Django与Ansible协同开发的入门实践案例,后续可轻松扩展CMDB、工单审批或对接Zabbix监控。

被折叠的 条评论
为什么被折叠?



