ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI 软件开发实战教程(十八):让程序能部署,也能恢复

AI 软件开发实战教程(十八):让程序能部署,也能恢复 系列第 18 篇功能和浏览器测试完成以后用 TDD 补齐首次管理员、可配置端口、正式运行、后台心跳、脱敏日志、加密备份与匿名指标同时避免把“写好脚本”说成“已经生产验证”。前十七篇已经让邻行完成从邀请注册到双方确认、提醒、删除和移动浏览器 QA 的闭环。此时最危险的一句话是“功能都做完了部署一下就能用了。”开发环境里只要执行runserverDjango 会顺手提供静态文件后台任务也可以在另一个终端人工启动数据库坏了大不了重建测试数据。真实用户环境没有这些便利。K10 要回答的是服务怎样启动样式怎样进入镜像谁执行迁移后台任务死了怎么知道日志会不会泄露微信号备份究竟能不能恢复以及试用数据是否值得以牺牲隐私为代价。先把生产事实写成失败测试这一轮没有先写 Docker 配置而是先加测试要求正式环境通过 WhiteNoise 提供压缩并带指纹的静态文件JSON 日志只接受明确列出的字段即使程序误传微信号、喵码和密码也不能输出Worker 每次循环都更新数据库心跳运行快照只能输出积压、延迟、状态计数和心跳年龄备份必须加密保留期不能超过 30 天恢复脚本没有“这是隔离数据库”的显式确认就拒绝执行镜像必须真的复制模板和样式迁移必须是单独的一次性任务本地端口可以覆盖不能因为 8000 被其他项目占用就访问到错误应用首个系统管理员只能创建一次密码不能进入日志重复部署不能重置密码或把普通成员静默提升为管理员。第一次运行测试直接报错日志格式器、心跳模型和运行快照都不存在。这是预期的红灯。部署文档初稿完成后真实运行又暴露了第二轮红灯。开发命令把端口写死8000 已经被另一个 Uvicorn 项目占用随后尝试的 18000 也被同一项目使用。邻行启动失败后浏览器访问旧端口仍然得到 JSON 404很容易让人误以为邻行路由坏了。与此同时部署指南只写了交互式createsuperuser没有回答自动部署怎样安全产生第一个管理员。这说明部署测试不能只检查“文件存在”。还要检查一个新人能否按文档从空数据库走到首次登录。首次管理员不是写死一个默认密码最省事的做法是在代码里创建admin/admin。它也最危险镜像、README、日志和所有安装实例都会共享同一个公开密码。邻行增加了幂等的bootstrap_admin命令。“幂等”是指重复执行结果保持一致账号不存在 → 使用一次性环境密码或生成随机强密码写入 0600 文件 → 创建 is_staff is_superuser 系统管理员 账号已经是系统管理员 → 报告 unchanged不改密码 同名普通成员已经存在 → 拒绝执行不静默提权密码明文不会出现在标准输出。Web 和 Worker 启动都不会调用这个命令因此扩容、重启和版本更新不会改变管理员身份。K10 完成后项目又把容易混淆的配置入口进一步分开。本地make dev-bootstrap-admin不读取根目录.env随机密码写入var/.initial_admin_passwordDocker Compose 只读取.env.compose源码生产部署只读取/etc/linxing/linxing.env。两种生产部署没有临时注入密码时随机密码都写入受保护的/var/lib/linxing/.initial_admin_password。如果通过LINXING_INIT_ADMIN_PASSWORD提供一次性密码应在首次登录并改成长期密码后从对应的生产配置中删除。deploy/env/中的文件只是模板不能复制成根目录.env作为共享配置。对应的 TDD 红灯先证明四件事尚未成立配置密码会创建管理员、命令重复运行不会重置密码、随机密码文件权限是0600、同名普通成员不会被提升。命令不存在时四条测试全部失败实现后四条同时转绿。端口是运行配置不是代码常量邻行本地默认端口调整为 18001同时保留覆盖入口makedev-runmakedev-runPORT18002Docker 使用LINXING_HTTP_PORT控制宿主机回环端口容器内部仍监听 8000源码部署则让 systemd 的 Gunicorn 绑定端口和 Nginx 上游保持一致。端口冲突时先确认占用者不应为了启动当前项目就误停另一个项目。更重要的是浏览器能返回内容不代表当前应用启动成功。必须同时确认启动终端没有报错、监听进程属于正确项目、首页特征正确健康端点也来自同一个服务。首次部署是一条完整链路管理员补齐后首次部署顺序才真正闭合配置秘密 → 启动 PostgreSQL → 执行迁移 → 初始化系统管理员 → 登录后台创建社区和地点 → 生成首个邀请 → 启动 Web 与 Worker → 验证 live、ready 和 Worker 心跳迁移和管理员初始化都是一次性运维任务。把它们塞进每个 Web 进程启动看起来自动实际上会在多实例并发启动时制造竞争也让一次普通重启意外拥有修改账号的权限。镜像能启动不代表页面完整检查现有 Dockerfile 时发现它只复制了src/和manage.py。邻行的模板与 CSS 在仓库根目录因此根本不会进入镜像。Gunicorn 本身又不负责像开发服务器那样提供静态资源。修复包含三层把templates/、static/和运维脚本复制进镜像构建镜像时执行collectstatic生成带内容指纹的静态清单在生产设置中让 WhiteNoise 紧跟 Django 安全中间件提供压缩静态资源。这里又遇到一次有价值的红灯。最初把压缩清单存储放进基础设置后33 个页面测试同时失败因为测试环境没有预先生成静态清单。最终把中间件留在通用配置把严格清单存储收紧到生产和镜像构建配置。方向正确不代表配置层级正确全量回归帮助我们找到了边界。后台任务不能只看“进程还在”原 Worker 只负责取一条提醒。即使生命周期截止和删除保留已有管理逻辑正式循环却没有调用它们。进程列表显示“正在运行”业务状态仍可能永远不结束。新的每次循环按同一时刻执行写入心跳 → 扫描截止与过期 → 执行删除保留 → 处理一条提醒成功时清空错误类型失败时记录异常类别并继续让进程以错误退出而不是吞掉问题。runtime_snapshot可以看到心跳是否超过 60 秒、待发送数量、最老积压年龄、各种投递状态、逾期信息数量和最大截止延迟。快照不包含业务正文和个人字段。监控需要知道“有四条提醒积压了五分钟”不需要知道这四个人是谁、去哪里或微信号是什么。日志采用允许名单不采用事后遮盖常见做法是先记录完整请求再用正则表达式遮盖密码。这很脆弱字段名称可能变化Cookie、请求体和第三方地址也可能包含秘密。邻行反过来做。日志格式器只接受请求编号、请求方法、安全路径、状态码、耗时、内部用户/社区编号、Worker 名称、是否处理任务和异常类别。其他字段即使被代码放进日志记录也会被丢弃。请求编号会写回X-Request-ID以后用户报告错误时可以定位同一次请求客户端传入的编号必须符合短而安全的字符规则否则重新生成避免把任意文本带进日志。备份的目标不是“每天产生一个文件”备份脚本使用 PostgreSQL 自定义格式导出通过独立口令立即加密权限限制为当前用户并只在专用目录轮换符合固定命名的加密文件。保留期默认 30 天超过 30 会直接拒绝。恢复比备份更危险。恢复脚本要求目标是隔离数据库并且必须显式设置ALLOW_ISOLATED_RESTOREyes临时解密文件使用随机名称和0600权限退出时清除。但是本节点没有声称恢复已经通过。当前机器已有pg_dump和pg_restore客户端但没有 Docker 或可用的 PostgreSQL 17 服务只能验证脚本结构、安全门和 Shell 语法无法完成真实隔离恢复。项目因此把恢复演练表保留为NOT RUN。正式试用前必须在隔离实例完成解密、恢复、迁移、匿名计数、加密字段抽查和环境销毁。“备份成功”只说明写出了文件“恢复演练成功”才说明这个文件可能在事故中有用。试用指标不等于页面监控产品规划需要知道发布是否形成候选、候选是否进入联系方式交换、双方是否确认以及截止时是否仍然没有候选。这些事实已经存在业务表里不需要录制用户页面。export_pilot_metrics只输出聚合计数与比率有效发布、出现候选的发布、第三方接受的候选提醒、联系方式交换、双方确认、实际同行正向反馈和截止无候选。分母为零时比率输出null不会把尚未测量伪装成 0%。注册页放弃、提醒码页放弃和复制后是否真的加微信目前无法可靠计算。首版宁可承认不知道也不接入会话回放或采集输入内容。若真实试用证明某个断点很重要再设计不含内容、有限保留的业务事件。本节点结果K10 的平台中立代码与文档经过补充后完成可覆盖端口、幂等管理员初始化、首次社区数据顺序、生产静态资源、代理 HTTPS 设置、一次性迁移服务、Worker 心跳、生命周期与保留调度、允许名单日志、运行快照、加密备份/隔离恢复脚本和匿名试用指标都有自动测试。本地静态清单生成成功快速测试与静态检查通过。真实 Docker 镜像、PostgreSQL 就绪、恢复演练、TLS、WebKit、微信双平台和外部隐私法律审查仍未通过。因此开发节点可以收尾项目状态应是“代码完成等待外部门禁”而不是“已经上线”。下一步不再由 AI 擅自跨越需要产品负责人提供可访问的 HTTPS 环境与两部真机完成 Gate B再由群管理员和隐私责任人完成 Gate C。关键代码与操作下面的部署契约测试只检查公开结构不读取或输出任何真实密码deftest_production_config_sources_remain_separate():composePath(compose.yaml).read_text()source_examplePath(deploy/env/linxing.env.example).read_text()assertenv_file: .env.composeincomposeassertDJANGO_SETTINGS_MODULElinxing.settings.productioninsource_exampleassertLINXING_INITIAL_ADMIN_PASSWORD_FILEinsource_exampleassert/var/lib/linxing/insource_example验证命令make bugfix TESTtests/operations/test_runtime_contract.py这条测试证明配置入口没有重新混在一起真实部署还必须验证文件属主、权限、服务账号和隔离恢复。本篇验证摘要本地开发、Docker Compose 和 Linux 源码部署使用彼此独立的配置入口Docker 只读取.env.compose源码生产部署只读取/etc/linxing/linxing.env两种生产部署的随机初始管理员密码都写入受限的/var/lib/linxing/.initial_admin_password迁移和管理员初始化是显式的一次性任务不随每个 Web 或 Worker 进程重复执行静态资源、Worker 心跳、允许名单日志、加密备份和匿名指标已有自动测试真实 PostgreSQL 恢复、TLS、WebKit 和微信双平台仍未完成当前结论是代码就绪而非已经上线。附录相关工具与仓库gstack仓库garrytan/gstack地址https://github.com/garrytan/gstackdev-harness仓库Dev-Wiki/dev-harness地址https://github.com/Dev-Wiki/dev-harnessUI UX Pro Max Skill仓库nextlevelbuilder/ui-ux-pro-max-skill地址https://github.com/nextlevelbuilder/ui-ux-pro-max-skill
返回列表