diff --git a/COURSE_TASKS.json b/COURSE_TASKS.json index a392ef6..59ffc32 100644 --- a/COURSE_TASKS.json +++ b/COURSE_TASKS.json @@ -699,9 +699,16 @@ "takeaways": [ "学完后应能做到:理解 Linux 中的进程概念,知道如何查看系统正在运行什么。", "易错提醒:只会看进程名,不会看状态", - "迁移场景:确认服务进程是否存在" + "迁移场景:确认服务进程是否存在", + "形成分层排障顺序,而不是遇到问题就随手试命令。" ], - "after_class": "课后建议:回到真实或模拟环境里,再用 ps、top 做一次独立练习,并尝试自己解释每条输出的含义。" + "after_class": "课后建议:回到真实或模拟环境里,再用 ps、top 做一次独立练习,并尝试自己解释每条输出的含义。", + "troubleshooting_flow": [ + "先确认服务对应的进程是否存在", + "再看进程状态、CPU、内存占用是否异常", + "如果进程存在但服务不可用,再继续看端口和日志", + "不要把“有进程”误判成“服务正常”" + ] }, { "id": "m4_l2_disk_memory", @@ -767,9 +774,16 @@ "takeaways": [ "学完后应能做到:掌握查看磁盘使用、目录占用和内存情况的基础方法。", "易错提醒:只会看总磁盘,不会看哪个目录占用大", - "迁移场景:排查磁盘已满" + "迁移场景:排查磁盘已满", + "形成分层排障顺序,而不是遇到问题就随手试命令。" ], - "after_class": "课后建议:回到真实或模拟环境里,再用 df、du、free 做一次独立练习,并尝试自己解释每条输出的含义。" + "after_class": "课后建议:回到真实或模拟环境里,再用 df、du、free 做一次独立练习,并尝试自己解释每条输出的含义。", + "troubleshooting_flow": [ + "先用 df 确认是哪个文件系统空间不足", + "再用 du 逐层定位哪个目录占用最大", + "必要时结合 find 找出大文件", + "清理前先确认文件用途与是否还能用于排障" + ] }, { "id": "m4_l3_mount_history", @@ -835,9 +849,16 @@ "takeaways": [ "学完后应能做到:建立系统运行时间、挂载结构与命令习惯认知。", "易错提醒:不看历史重复犯错", - "迁移场景:查看机器是否重启过" + "迁移场景:查看机器是否重启过", + "形成分层排障顺序,而不是遇到问题就随手试命令。" ], - "after_class": "课后建议:回到真实或模拟环境里,再用 uptime、mount、history 做一次独立练习,并尝试自己解释每条输出的含义。" + "after_class": "课后建议:回到真实或模拟环境里,再用 uptime、mount、history 做一次独立练习,并尝试自己解释每条输出的含义。", + "troubleshooting_flow": [ + "先看 uptime 确认系统是否近期重启", + "再看 mount 判断关键目录属于哪个挂载点", + "最后回看 history 了解最近做过什么变更", + "把系统状态和操作历史结合起来看" + ] } ] }, @@ -904,9 +925,16 @@ "学完后应能做到:理解 Linux 服务的查看、启动、停止和重启。", "易错提醒:改完配置却忘记重启服务", "迁移场景:排查服务没起来", - "服务问题先看状态,再决定下一步看日志、端口还是配置。" + "服务问题先看状态,再决定下一步看日志、端口还是配置。", + "形成分层排障顺序,而不是遇到问题就随手试命令。" ], - "after_class": "课后建议:回到真实或模拟环境里,再用 systemctl 做一次独立练习,并尝试自己解释每条输出的含义。" + "after_class": "课后建议:回到真实或模拟环境里,再用 systemctl 做一次独立练习,并尝试自己解释每条输出的含义。", + "troubleshooting_flow": [ + "先看 systemctl status,确认服务到底是不是 running", + "再看是否有明显的启动失败或退出提示", + "如果状态异常,再进入日志层和端口层", + "不要一上来就盲目 restart 多次" + ] }, { "id": "m5_l2_journalctl", @@ -966,9 +994,16 @@ "学完后应能做到:理解如何查看服务日志和系统日志。", "易错提醒:只看应用日志,不看 systemd 日志", "迁移场景:查看服务启动失败原因", - "日志不是越多越好,关键是缩小范围看最近、看目标服务。" + "日志不是越多越好,关键是缩小范围看最近、看目标服务。", + "形成分层排障顺序,而不是遇到问题就随手试命令。" ], - "after_class": "课后建议:回到真实或模拟环境里,再用 journalctl 做一次独立练习,并尝试自己解释每条输出的含义。" + "after_class": "课后建议:回到真实或模拟环境里,再用 journalctl 做一次独立练习,并尝试自己解释每条输出的含义。", + "troubleshooting_flow": [ + "先限定服务名缩小日志范围", + "优先看最近几十行,不要一开始把范围拉太大", + "定位到关键报错后再回溯上下文", + "边操作边用 -f 观察实时变化" + ] }, { "id": "m5_l3_process_control", @@ -1030,9 +1065,16 @@ "学完后应能做到:理解如何控制进程和让任务脱离终端运行。", "易错提醒:直接粗暴 kill 掉关键进程", "迁移场景:结束卡死进程", - "进程控制的重点是知道为什么结束、结束谁、结束后系统会怎样。" + "进程控制的重点是知道为什么结束、结束谁、结束后系统会怎样。", + "形成分层排障顺序,而不是遇到问题就随手试命令。" ], - "after_class": "课后建议:回到真实或模拟环境里,再用 kill、pkill、nohup 做一次独立练习,并尝试自己解释每条输出的含义。" + "after_class": "课后建议:回到真实或模拟环境里,再用 kill、pkill、nohup 做一次独立练习,并尝试自己解释每条输出的含义。", + "troubleshooting_flow": [ + "确认要处理的是哪个进程", + "评估结束进程会不会影响业务", + "优先选择合理方式终止,不要默认暴力 kill -9", + "需要后台运行任务时再考虑 nohup" + ] } ] }, @@ -1111,9 +1153,16 @@ "学完后应能做到:理解网卡、IP 和连通性的基本概念。", "易错提醒:能 ping 通就以为服务一定可用", "迁移场景:确认机器是否有正确 IP", - "网络排查第一步是先确认链路和地址,再看更上层。" + "网络排查第一步是先确认链路和地址,再看更上层。", + "形成分层排障顺序,而不是遇到问题就随手试命令。" ], - "after_class": "课后建议:回到真实或模拟环境里,再用 ip addr、ifconfig、ping 做一次独立练习,并尝试自己解释每条输出的含义。" + "after_class": "课后建议:回到真实或模拟环境里,再用 ip addr、ifconfig、ping 做一次独立练习,并尝试自己解释每条输出的含义。", + "troubleshooting_flow": [ + "先确认机器有没有拿到正确 IP", + "再用 ping 验证基础连通性", + "如果 ping 不通,优先怀疑网络层或地址层问题", + "如果 ping 通,再继续检查端口和应用层" + ] }, { "id": "m6_l2_ss_curl", @@ -1186,9 +1235,16 @@ "学完后应能做到:建立监听端口和服务请求验证的能力。", "易错提醒:只看页面打不开,不查监听", "迁移场景:查服务是否监听端口", - "监听正常不代表业务正常,请求失败也不一定是服务没启动。" + "监听正常不代表业务正常,请求失败也不一定是服务没启动。", + "形成分层排障顺序,而不是遇到问题就随手试命令。" ], - "after_class": "课后建议:回到真实或模拟环境里,再用 ss、netstat、curl、wget 做一次独立练习,并尝试自己解释每条输出的含义。" + "after_class": "课后建议:回到真实或模拟环境里,再用 ss、netstat、curl、wget 做一次独立练习,并尝试自己解释每条输出的含义。", + "troubleshooting_flow": [ + "先看端口是否监听", + "再用 curl 验证应用层是否有返回", + "如果监听正常但请求异常,再结合日志判断应用问题", + "如果根本没监听,先回到服务状态层排查" + ] }, { "id": "m6_l3_name_route", @@ -1256,9 +1312,16 @@ "学完后应能做到:建立链路定位和名称解析基础认知。", "易错提醒:把 DNS 问题误判成应用问题", "迁移场景:排查域名异常", - "命令定位、解析路径和网络链路,都是“看不见的问题”的排查入口。" + "命令定位、解析路径和网络链路,都是“看不见的问题”的排查入口。", + "形成分层排障顺序,而不是遇到问题就随手试命令。" ], - "after_class": "课后建议:回到真实或模拟环境里,再用 traceroute、dig、which、whereis 做一次独立练习,并尝试自己解释每条输出的含义。" + "after_class": "课后建议:回到真实或模拟环境里,再用 traceroute、dig、which、whereis 做一次独立练习,并尝试自己解释每条输出的含义。", + "troubleshooting_flow": [ + "先确认命令或服务实际路径", + "再检查域名解析是否正确", + "必要时查看路由链路是否异常", + "把“命令路径 / DNS / 路由”当成三类不同问题" + ] } ] }, @@ -1903,9 +1966,17 @@ "学完后应能做到:建立“先服务、再端口、再日志、再请求”的排查顺序。", "易错提醒:只看浏览器打不开,不看服务状态", "迁移场景:应用服务无法访问", - "服务不可用时,排障要按层进行:服务 → 进程 → 端口 → 日志 → 请求。" + "服务不可用时,排障要按层进行:服务 → 进程 → 端口 → 日志 → 请求。", + "形成分层排障顺序,而不是遇到问题就随手试命令。" ], - "after_class": "课后建议:回到真实或模拟环境里,再用 systemctl、ps、ss、journalctl、curl 做一次独立练习,并尝试自己解释每条输出的含义。" + "after_class": "课后建议:回到真实或模拟环境里,再用 systemctl、ps、ss、journalctl、curl 做一次独立练习,并尝试自己解释每条输出的含义。", + "troubleshooting_flow": [ + "服务状态:systemctl status", + "进程状态:ps / 进程是否存在", + "端口监听:ss 或 netstat", + "日志定位:journalctl / 应用日志", + "请求验证:curl 直接打本机或接口" + ] }, { "id": "m10_l2_disk_full", @@ -1966,9 +2037,16 @@ "学完后应能做到:建立从 df 到 du 再到 find 的磁盘问题定位思路。", "易错提醒:只看 df 不继续追目录", "迁移场景:排查磁盘 100%", - "磁盘排查的关键是先找文件系统,再找目录,再找大文件。" + "磁盘排查的关键是先找文件系统,再找目录,再找大文件。", + "形成分层排障顺序,而不是遇到问题就随手试命令。" ], - "after_class": "课后建议:回到真实或模拟环境里,再用 df、du、find、sort 做一次独立练习,并尝试自己解释每条输出的含义。" + "after_class": "课后建议:回到真实或模拟环境里,再用 df、du、find、sort 做一次独立练习,并尝试自己解释每条输出的含义。", + "troubleshooting_flow": [ + "文件系统层:df -h", + "目录层:du -sh", + "文件层:find + sort", + "处理层:确认是否可删、是否要备份、是否影响排障" + ] }, { "id": "m10_l3_login_fail", @@ -2030,9 +2108,17 @@ "学完后应能做到:把身份、权限、日志三者串起来理解。", "易错提醒:只怀疑密码错误,不看日志", "迁移场景:SSH 登录失败", - "登录失败排查要把身份、日志和权限一起看,不能只猜密码。" + "登录失败排查要把身份、日志和权限一起看,不能只猜密码。", + "形成分层排障顺序,而不是遇到问题就随手试命令。" ], - "after_class": "课后建议:回到真实或模拟环境里,再用 whoami、id、passwd、grep、tail 做一次独立练习,并尝试自己解释每条输出的含义。" + "after_class": "课后建议:回到真实或模拟环境里,再用 whoami、id、passwd、grep、tail 做一次独立练习,并尝试自己解释每条输出的含义。", + "troubleshooting_flow": [ + "身份层:whoami / id / 当前用户是谁", + "账户层:账号是否存在、是否被限制", + "权限层:文件和脚本权限是否正确", + "日志层:auth.log / 相关认证日志", + "不要只盯着“密码错了”一个方向" + ] } ] } diff --git a/index.html b/index.html index 6395e63..ae5f602 100644 --- a/index.html +++ b/index.html @@ -272,6 +272,11 @@
+
+

排障链路 / 处理顺序

+ +
+

课后总结

@@ -380,6 +385,7 @@ renderList('conceptList', lesson.concepts || []); renderList('pitfallList', lesson.pitfalls || []); renderList('scenarioList', lesson.scenarios || []); + renderList('flowList', lesson.troubleshooting_flow || []); renderList('takeawayList', lesson.takeaways || []); document.getElementById('classicView').textContent = lesson.classic_view || '教材视角:先理解问题,再选择命令。'; document.getElementById('afterClass').textContent = lesson.after_class || ''; diff --git a/sandbox.py b/sandbox.py index 158e085..2f53c8d 100644 --- a/sandbox.py +++ b/sandbox.py @@ -548,6 +548,7 @@ class LinuxSandbox: canned = { "clear": "[screen cleared]", "id": "uid=1000(sandbox_user) gid=1000(sandbox_user) groups=1000(sandbox_user),999(docker)", + "uptime": " 10:00:00 up 5 days, 2 users, load average: 0.10, 0.12, 0.08", "w": " 10:00:00 up 5 days, 2 users, load average: 0.10, 0.12, 0.08\nUSER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT\nsandbox pts/0 localhost 10:00 1:20 0.01s 0.00s bash", "last": "sandbox_user pts/0 localhost Mon Mar 6 10:04 still logged in\nreboot system boot 5.15.0 Mon Mar 6 10:00", "passwd": "Changing password for sandbox_user... (simulated)", @@ -612,6 +613,8 @@ class LinuxSandbox: return canned["whereis"] if cmd_name == "dig": return canned.get("dig", ";; ANSWER SECTION:\nexample.com. 300 IN A 93.184.216.34") + if cmd_name in {"uptime", "lsof", "sort", "uniq", "cut", "awk", "sed"}: + return canned.get(cmd_name, f"{cmd_name}: simulated") if cmd_name == "export" and args and "=" in args[0]: key, value = args[0].split("=", 1) self.env[key] = value