Python后端部署排障01:连上服务器以后,先证明自己站在哪里
Python后端部署排障01:连上服务器以后,先证明自己站在哪里
程序在电脑上能运行,搬到服务器却“没反应”,第一反应常常是重装依赖。可是你可能在另一台机器,进入了另一个目录,或者正在看昨天启动的进程。排障的第一课不是背命令,而是把一句模糊的“服务器不行”改写成可以验证的事实:哪台主机、哪个用户、哪个目录、哪个进程、哪个端口,执行了什么,得到什么结果。
这套课程叫 QuoteOps。业务是一个已经有明确合同的报价服务:创建报价、读取报价、用版本号确认或取消。我们把注意力从写接口转到让接口持续可用。本专题自带完整project/,无需下载前两套课程,也不用把旧环境中的虚拟环境搬过来。报价金额用整数分,状态从草稿转向确认或取消,版本冲突返回明确错误。这些规则会成为部署是否成功的最终判断标准。
SSH 成功,只说明获得了一个远端会话
SSH 是加密的远程登录通道。客户端完成主机身份检查与用户认证后,服务器为你启动会话。它既不保证业务运行,也不保证你拥有写入任何目录的权限。下面的your-user和your-host是占位符,要替换成自己的授权账号与地址;教程不会要求把密码放进命令、脚本、笔记或截图。首次连接提示的主机指纹应通过可信渠道核对,不能把删除known_hosts当成日常修复手段。
sshyour-user@your-hosthostnameidpwdpython3--version看完输出,再说“我在服务器上”。hostname是系统报告的主机名,id给出实际 UID、GID 与附加组,pwd表示当前 shell 的工作目录。三个信息分工不同:用户名是便于人理解的标签,内核检查文件权限时主要使用数字身份;主机名也不能单独证明你身处宿主机,因为容器可以有自己的主机名。把它们一起记录,比只看命令提示符可靠。
ssh连接断开后,前台程序是否继续存在,取决于进程如何启动以及会话如何关闭。先不要用末尾一个&来冒充服务管理。它只是让 shell 不等待该作业,不自动提供重启、日志归集、优雅停止或启动依赖。第三课会用 systemd user 管理一个小进程,第七课之后再转向容器。我们先让边界清楚,再增加工具。
目录是命令的输入之一
假设脚本里打开data/quotes.db,它通常相对进程当前工作目录解析,并不天然相对脚本文件。你在仓库根目录执行和在家目录执行,可能打开两份同名数据库。看到空库后重新导数据,往往比原来的错误更麻烦。运行命令前先pwd,配置文件与数据库使用已经核对过的路径,错误信息里也记下具体目标,而不是只记“找不到文件”。
Linux 路径区分大小写,Data和data通常是两个目录;路径中的空格需要引用;~是 shell 的家目录展开,不是所有程序都认识的特殊符号。把字符串~/quoteops直接交给不执行展开的程序,可能得到意外路径。Python 使用Path(...).expanduser().resolve()时,两步分别表示展开家目录与转换到绝对路径,不应把它们误当成权限检查。
建议把本套目录放到家目录下的quoteops,进入之后检查README.md、labs/、lessons/和project/是否都在。后面的命令默认从课程根目录执行。临时实验使用系统临时目录,部署资源由课程脚本单独登记;不会通过“删掉整个工作区重来”解决局部问题。这个习惯的收益是:每次操作前,你都能指出它影响的对象。
一条命令其实有三份结果
程序有标准输出、标准错误和退出状态。正常数据通常写到 stdout,诊断通常写到 stderr,退出码给调用者一个机器可判断的结果。三者并不互相替代:程序可以打印成功字样后失败,也可以完全不打印却退出成功。上一条命令的退出码用$?查看,但它会被后续命令覆盖,所以要立刻保存。
python3-c'raise SystemExit(7)'result=$?printf'exit=%s\n'"$result"python3 labs/machine_evidence.py这里故意退出七,是为了练习“失败结果也是证据”。遇到真实失败,不要立即重复同一条命令十次。先保留错误类型、完整命令和退出码,再决定下一步。若输出需要交给程序解析,最好让 stdout 保持 JSON 之类的明确格式,把提示写到 stderr。本课的证据脚本只输出一个 JSON 对象,后续验收能够读取它,而不依赖人工看某一句话。
管道还有一个常见误会:producer | consumer的默认退出码通常来自最后一段。前一段失败,后一段仍可能成功。在 Bash 排障脚本中,set -o pipefail能让管道反映其中的失败,但它不是所有 shell 的通用开关,也不是“开启后脚本绝对安全”。先理解命令在谁那里执行,再决定如何传播失败;不要混用 PowerShell、Bash 和容器中的命令语法。
PID 和磁盘数字也有观察范围
PID 是当前进程命名空间中的标识,会复用,不是一个永久身份证。脚本打印的 PID 是这个短命脚本自己,执行完就会消失;它不是报价服务的 PID。父进程 PID 则帮助我们理解是谁启动它。稍后你会看到容器内的 PID 一和宿主机中同一进程的 PID 可以不同,这不是记录错了,而是观察位置改变了。
磁盘使用率同样要带上路径。df -h .查询当前目录所属文件系统,不能自动代表数据库目录、日志目录或容器可写层。临时文件系统还可能在内存上实现。脚本使用shutil.disk_usage(cwd),因此 JSON 中的容量明确对应cwd所在卷。字节数适合机器比较,-h的可读单位适合人查看,两者服务不同目的。
用户标签也存在细节:getpass.getuser()可能根据环境变量获取名称,所以脚本同时输出 Linux 的 UID/GID。不要用一个可以被环境变量影响的标签当成授权凭据。这里采集的信息用于观察和学习,并不是主机可信认证工具;需要严格证明运行主体时,应结合系统身份、服务配置和访问审计。
labs/machine_evidence.py
"""E01:只读当前运行环境;不把Windows结果冒充Linux证据。"""importgetpassimportjsonimportosfrompathlibimportPathimportplatformimportshutildefmain():cwd=Path.cwd().resolve()disk=shutil.disk_usage(cwd)data={"platform":platform.system(),"hostname":platform.node(),"user_label":getpass.getuser(),"uid":os.getuid()ifhasattr(os,"getuid")elseNone,"gid":os.getgid()ifhasattr(os,"getgid")elseNone,"cwd":str(cwd),"pid":os.getpid(),"parent_pid":os.getppid(),"disk":{"total_bytes":disk.total,"used_bytes":disk.used,"free_bytes":disk.free},}print(json.dumps(data,ensure_ascii=False,indent=2))if__name__=="__main__":main()脚本不创建测试文件来“测磁盘”,也不扫描整个文件系统。Path.cwd()取当前目录,os.getpid()取进程标识,disk_usage()查询文件系统统计,所有动作都是只读的。Windows 可以执行这份脚本,但平台字段会写 Windows,UID/GID 留空;这只能验证可移植采集逻辑,不能当作 Linux 权限或进程行为已经实测的证明。
宿主机、容器、你的电脑,不共用一个“本地”
后面访问127.0.0.1时,一定要读成“发起连接的那个网络环境自身”。电脑的回环地址不指向服务器,服务器的回环地址不自动指向容器。SSH 隧道会建立一个明确的转发关系:电脑某端口收到连接后,经 SSH 通道转发到服务器视角下的目标。它让个人实验可以只监听服务器本机端口,不需要临时改防火墙开放给公网。
ssh-N-L18080:127.0.0.1:18080 your-user@your-host这条命令是后续服务就绪时使用的示意:右侧端口必须替换成部署实际分配的 HTTP 端口。-N表示不启动远端命令,终端保持运行是在维护隧道。若电脑的 18080 已占用,可以只改左侧端口;远端目标不会因此改变。现在即使成功建立隧道,没有 HTTP 服务监听也仍会连接失败,这恰好说明“SSH 通”和“业务通”是两个结论。
把一次现场写成别人能复查的记录
排障记录最有用的部分不是“我试了很多办法”,而是别人可以从同一观察点重做一次。记录时先写时间和所在环境,再写工作目录与完整命令,随后保留退出码和相关输出。时间最好带时区,避免电脑显示北京时间、服务器日志显示协调世界时,最后把两段本不相邻的事件硬凑成因果关系。
遇到连接失败,也应保留失败所处阶段。名字无法解析,与用户名认证失败不同;主机密钥检查失败,与远端命令返回非零不同。前者可能连目标身份都尚未确认,后者则说明已经执行到了远端程序。不要把所有提示浓缩成“SSH 不通”,否则下一位协作者无法知道从哪层继续调查。
分享记录前只保留与问题相关的信息。环境变量列表可能包含密钥,命令行可能包含令牌,家目录名称也可能带个人信息;没有必要把它们整份贴出去。本课脚本采用明确字段,就是为了让读者先练习采集有边界的证据。记录不是越多越专业,而是足够回答当前问题、又不会制造新的信息泄露。
最后把预期和实际分成两句:例如“预期从课程根目录启动,实际工作目录是家目录”。这比“路径好像不对”更容易决定下一步。修复后重新采集同样字段,你就能比较变更前后,而不是依赖记忆中的终端画面。
今天的完成标准与 E01
本课不要求你部署完整服务。你需要能在 Linux 会话中说明当前主机、有效身份、工作目录、脚本进程与目录所属磁盘,并能解释一次非零退出码。判断依据是命令实际输出,不是截图里出现一个绿色符号。后续每一次“恢复了”,也要有相应业务或资源证据支撑。
E01:写一个只读机器证据脚本。输出 JSON,至少含系统平台、主机名、用户标签、Linux UID/GID、绝对工作目录、当前 PID、父 PID 和当前目录所属磁盘的总量/已用/可用字节。不允许通过创建大文件验证磁盘,不允许修改系统配置。执行后逐项解释它描述谁、描述哪个范围;把结果文件放在自己的实验记录目录即可。下一课给出完整参考答案,并用真实的PermissionError说明“登录成功”为什么不等于“可以写文件”。
课程目录:《Linux、网络与后端部署排障:从“接口能跑”走到“服务能维护”》 · 下一篇:第02篇