停止会自行重启的任务

LinuxBeginner
立即练习

介绍

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,查看用户与系统调度,禁用了单个异常触发器,并确认在没有新增上传的一分钟内,实际业务刷新仍持续运行。