介绍
Status Desk 正常情况下每分钟刷新一次小型缓存。但一个陌生的上传程序也在向内部收集器发送合成训练请求。停止它似乎有效,它却会回来。你的任务是移除重复启动它的触发器,同时保留正常状态刷新。
你只需要基本终端操作能力。Status Server 是目标容器内已授权的管理员终端,起始目录为 /srv/status。Status Desk 显示收集器的真实计数。两个容器共享训练 instance 的内核。所有数据和请求均无害;本实验处理 cron 重复启动机制,并不代表完整的生产主机调查。
观察反复出现的程序
在这一步中,你将停止正在运行的上传程序,并理解为什么终止一个实例并没有消除原因。
打开 Status Desk。refreshes 统计成功的缓存刷新请求,应每分钟增长。uploads 统计异常训练请求,通常每五秒一次。修复后历史计数仍然保留。重新加载页面获取最新值。
切换到 Status Server。进程是正在运行的程序。PID 标识当前实例;程序路径与参数描述其行为。ps 列出进程;-e 选择全部进程,-o 指定 PID、父进程 PID(PPID)和完整命令及参数这些列。
ps -eo pid,ppid,args
找到 /usr/bin/python3 /opt/status/uploader.py upload。Python 是解释器,/opt/status/uploader.py 是程序文件,upload 选择训练上传操作。附近的 flock 进程持有锁,防止 cron 同时启动多个副本。不要干扰缓存刷新程序。
信号是发送给进程的请求,TERM 请求正常终止。下面的 pkill 使用 -f 匹配完整命令行;引号中的 ^ 和 $ 分别限定开头与结尾,因此只选择这个 Python 工作进程,不会选中父进程或当前 shell。执行一次:
pkill -TERM -f '^/usr/bin/python3 /opt/status/uploader.py upload$'
没有输出是正常现象。如果没有匹配进程,应重新列出进程,而不是将匹配范围扩大为全部 Python 进程。等待一秒让进程退出,然后再次查看:
sleep 1
ps -eo pid,ppid,args
Python 上传进程应该消失。检查点确认原始实例已终止,并不表示主机已经清理完成。
sleep 按指定秒数暂停。等待 80 秒,确保跨过至少一个真实分钟边界,再查看进程:
sleep 80
ps -eo pid,ppid,args
相同路径和 upload 参数应以新 PID 出现。父进程链现在包含调度器的任务启动进程 CRON。新 PID 表示一次新执行,而不是另一个底层任务。若环境繁忙使调度延迟,再等 20 秒后重新查看。重新加载 Status Desk:上传恢复,合法刷新也继续。接下来需要找到程序的启动来源。
找到并禁用重复启动的触发器
在这一步中,你将把返回的进程与特定调度关联起来,只禁用该条目,保留合法任务。
Cron 是守护进程,即在后台运行并启动计划命令的服务。用户 crontab 保存单个账户的任务。当前终端是训练容器内部的 root,因此 crontab -l 列出目标账户的调度,不影响平台账户。
crontab -l
应看到:
* * * * * /usr/bin/flock -n /run/uploader.lock /usr/bin/python3 /opt/status/uploader.py upload
前五个字段依次是分钟、小时、月内日期、月份、星期。* 表示所有值,因此 * * * * * 表示每分钟执行。后面的文本是命令。flock -n 获取非阻塞锁:如果另一个上传进程持有锁,重复调用就会退出。这里的目标与刚才停止的进程一致。
系统调度也保存在 /etc/cron.d/ 中。ls 列出文件,cat 输出短文件内容,目录后的 * 会展开为其中的文件。
ls /etc/cron.d
cat /etc/cron.d/*
status-cache 文件包含:
* * * * * root /usr/bin/python3 /opt/status/cache-refresh.py refresh
与用户 crontab 不同,系统 cron 文件在五个时间字段和命令之间增加账户字段,此处为 root。这条允许的命令更新 Status Desk。仅仅执行时间相似,并不能说明任务未获授权。软件包自带的其他维护条目可以保留。
为了安全编辑,先将当前用户调度复制到工作文本文件。> 将列表写入文件,若已有旧副本则覆盖。编辑前只执行一次,重复执行会覆盖草稿。
crontab -l > /srv/status/root.cron
nano 打开文本编辑器。在上传条目行首添加 #。Cron 忽略以 # 开头的注释行。保留原有文本便于恢复,其他条目不变。
nano /srv/status/root.cron
用方向键移动到行首,输入 #,按 Ctrl+O、Enter 保存,再按 Ctrl+X 退出。将编辑后的文件路径作为 crontab 参数进行安装:
crontab /srv/status/root.cron
crontab -l
上传条目现在应以 # 开头。Cron 自动重新加载调度,无需重启。只在工作副本中添加注释还不够,必须安装。如果改错行,重新打开草稿,恢复内容并再次安装。当前上传进程可能仍在运行,因为移除调度不会终止已启动的进程。
移除运行中的副本并验证恢复
在这一步中,你将完成清理,在完整调度周期内比较异常活动与业务功能。
使用相同的精确匹配模式停止当前 Python 上传进程:
pkill -TERM -f '^/usr/bin/python3 /opt/status/uploader.py upload$'
若它已经不存在,没有匹配项的退出状态是预期现象,可以继续。等待一秒使工作进程退出:
sleep 1
curl 发起网络请求。-sS 隐藏进度条但保留错误信息。这个 URL 指向内部收集器容器,可从 Status Server 访问。/state 返回当前计数及最近请求的 Unix 时间戳。
curl -sS http://collector:8080/state
记下 uploads、refreshes 和 last_refresh,然后让真实 cron 执行:
sleep 80
curl -sS http://collector:8080/state
uploads 必须不变,refreshes 和 last_refresh 则应增大。重新加载 Status Desk 会看到相同事实。历史上传仍作为证据保留;清空计数只是隐藏历史,并未修复重复启动。
再次查看进程:
ps -eo pid,ppid,args
cron -f 应仍然存在;-f 让容器的守护进程以前台方式运行。上传程序应该持续消失。检查点会独立观察另一个 80 秒周期,因此评分需约 90 秒。它只读取配置与实时状态。
如果上传增长,重新检查两个 cron 位置,确认已经安装带注释的用户 crontab。如果刷新停止,恢复允许的 status-cache 条目。不要停止 cron 或删除所有调度来掩盖现象:状态服务需要这些任务。本次修复移除了已经演示的 cron 机制,并不能证明真实主机不存在其他持久化机制。
总结
你区分了进程实例和启动来源,沿父进程链找到 cron,查看用户与系统调度,禁用了单个异常触发器,并确认在没有新增上传的一分钟内,实际业务刷新仍持续运行。



