找回密码
立即注册
搜索
发回帖 发新帖

6568

积分

1

好友

824

主题
发表于 6 小时前 | 查看: 4| 回复: 0

「脚本是给机器跑的。但机器怎么知道脚本跑没跑?」

「看日志?」

「日志是给人看的。机器要的是接口。」

运维小白成长记封面:API是桥,一头脚本一头机器

一、机器不会 SSH

「你现在的脚本,只有登录服务器才能跑。」

「但监控系统、CI/CD、K8s 探针,它们不会 SSH。它们只会发 HTTP 请求。」

脚本库(内部) → 日志文件 → 人去看
API(对外) → HTTP 接口 → 机器调用

「你前作学过 Prometheus 监控。Prometheus 怎么知道你的服务活着?靠 /metrics 或 /health 接口。今天我们就写这个。」

为什么脚本库还需要一个API:对比脚本+日志与API接口两种消费方式

「那用 Python?」

「FastAPI。轻量、快、自带文档。」

二、准备 Python 环境

先确认系统里 Python 的版本:

python3 --version

输出示例:

Python 3.13.x

按 Canonical 官方的 LTS 说明:Ubuntu 26.04 LTS 默认 Python 是 3.13(24.04 才是 3.12,22.04 是 3.10)。这个数字要改对,因为后面 Dockerfile 里的基础镜像、依赖版本,都跟它对得上才顺。

安装 pip 和 venv,顺便把 pip 源配好:

sudo apt install -y python3-pip python3-venv
mkdir -p ~/.config/pip
tee ~/.config/pip/pip.conf <<-'EOF'
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
EOF

把配置写在 ~/.pip/pip.conf 这个路径虽然还能用,但早已不是推荐位置。pip 现在遵循 XDG 规范,用户级配置应该放 ~/.config/pip/pip.conf。另外 trusted-host = pypi.tuna.tsinghua.edu.cn 现在不必要了:清华源是正经 HTTPS,证书没问题。这一行只在用 http 源或自签证书的内网源时才需要。

创建虚拟环境:

python3 -m venv venv
source venv/bin/activate

「隔离依赖。系统 Python 别乱装包,每个项目一个环境,互不干扰。」

三、第一个接口

pip install fastapi uvicorn

「FastAPI 是框架,uvicorn 是服务器。」

from fastapi import FastAPI

app = FastAPI(title="Ops Scripts API", version="0.1.0")

@app.get("/")
def root():
    return {"message": "Ops Scripts API is running"}

@app.get("/health")
def health():
    return {"status": "ok"}

启动:

uvicorn main:app --host 0.0.0.0 --port 8000 --reload

--reload 是开发模式,改代码自动重启。生产环境别用。

浏览器打开 http://192.168.1.100:8000/docs,能看到 Swagger UI,两个接口列在那里,点一下就能测试。

「代码即文档,不用手写 API 说明。」

FastAPI三段代码:从裸接口到调用脚本带超时保护和结构化返回

四、调用脚本

在 FastAPI 里调外部脚本,核心是 subprocess.run 加超时保护:

@app.post("/check/disk", response_model=CheckResult)
def check_disk(threshold: int = 80):
    script = "/home/xiaolin/ops-scripts/bin/disk-check.sh"
    result = subprocess.run(
        ["bash", script, "-t", str(threshold)],
        capture_output=True,
        text=True,
        timeout=30          # 超时保护,防止脚本卡死
    )
    return CheckResult(
        status="completed",
        output=result.stdout.strip() or result.stderr.strip(),
        exit_code=result.returncode
    )

「timeout 不是美化项,是必需品。脚本卡住 = API 卡住。」

在 /docs 里输入 threshold: 90,返回:

{ "status": "completed", "output": "所有分区正常", "exit_code": 0 }

「现在机器可以发 HTTP 请求触发检查,不用 SSH。」

这里有个容易含糊的点:threshold: int = 80 会被 FastAPI 当作查询参数——值是通过 URL 传的( ?threshold=90 )。所以它在 /docs 里出现在 Parameters 区,不是在请求体里。要放进请求体,得单独定义一个 Pydantic 模型。两种方式在真实联调时差别很大——查询参数会被记进访问日志,别把敏感信息塞进 URL。

五、最容易写错的一段:「脚本不存在」会报什么

小林把脚本路径改错,再调接口,返回:

{
  "status": "completed",
  "output": "bash: /home/xiaolin/ops-scripts/bin/disk-check.sh: No such file or directory",
  "exit_code": 127
}

「嗯?它没报错,还说 completed?」

「因为 bash 自己启动成功了——找不到脚本的是 bash,不是 Python。」

「Python 这边什么异常都没抛,只是拿回了一个 127 的退出码。」

「那我上一版写的 except FileNotFoundError 呢?」

「永远不会命中。」

错误处理:脚本不存在时两种调用方式的不同异常表现

六、健康检查和版本

from datetime import datetime

@app.get("/health")
def health():
    return {
        "status": "ok",
        "timestamp": datetime.now().isoformat(),
        "version": app.version
    }

返回示例:

{
  "status": "ok",
  "timestamp": "2026-10-04T20:15:30.123456",
  "version": "0.2.0"
}

「监控系统会定期调这个接口。返回 ok 就说明服务活着。」

为什么健康检查里要有「版本」?这不是装饰。真实场景里最常见的困惑是:「我明明发新版了,怎么行为没变?」——十有八九是流量打到了旧实例,或者容器没重启成功。这时候一个带上版本号的 /health,五秒钟就能定位问题。

七、容器化准备

FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt \
-i https://pypi.tuna.tsinghua.edu.cn/simple
COPY main.py .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

让「本机开发环境」和「容器运行环境」的 Python 版本一致,能避免掉一整类「本地好的、容器里报错」的问题。另外注意 CMD 里没有 --reload——这是对的,生产环境要关掉。

pip freeze > requirements.txt
docker build -t ops-api:v0.2.0 .
docker run -d --name ops-api -p 8000:8000 ops-api:v0.2.0

「但注意架构。你本地是 ARM64,私有云是 x86_64。」

「第 9 话会优化这个 Dockerfile——多阶段构建、非 root 用户。」

八、常见问题

Q1:为什么不用 Flask 或者 python -m http.server?

  • python -m http.server:只能发静态文件,做不了 API;
  • Flask:也很好,但要自己装插件才有文档,类型校验也要额外引入;
  • FastAPI:自带 /docs、自带类型校验、原生异步。

不是 Flask 不好,是这个场景下 FastAPI 更顺手。

Q2:--reload 为什么生产环境不能用?

两个原因:

  1. 它会额外起一个 reloader 进程监视文件变化——多占资源,还可能干扰信号处理;
  2. 更严重的是它需要文件系统监听——容器里如果挂载了某些文件系统,监听会失效或报错。

Q3:0.0.0.0 和 127.0.0.1 该绑哪个?

绑定地址 谁能访问
127.0.0.1:8000 只有本机
0.0.0.0:8000 所有网卡

一旦服务要对外或进容器,必须是 0.0.0.0——容器内部的 127.0.0.1 是「容器自己」,外面的端口映射根本转发不进去。

Q4:脚本卡住了,API 会怎样?

没有 timeout 的话,API 就跟着卡住。一个慢请求会占住一个工作进程,几个慢请求就能把整个服务堵死。所以 timeout=30 不是可选的美化项,是必需品。超时要抛 504——它明确告诉调用方「上游太慢」,而不是含糊的 500。

Q5:这个 API 会不会有安全问题?

会,而且很现实。想一想:这个接口能执行服务器上的脚本。现在这个版本没有任何鉴权——任何能访问 8000 端口的人,都能触发磁盘检查。虽然当前脚本是只读的,但只要将来有人加一个「清日志」「重启服务」的接口,这就是一个远程命令执行漏洞。

后面要补的:① 鉴权(API Key / Token);② 命令白名单(只允许执行固定几个脚本,绝不拼接用户输入);③ 网络层限制。

九、下话

第 9 话:容器化 API——多阶段构建、非 root。

老周看了看小林的 Dockerfile,皱了皱眉。

「能跑。但这个箱子太重了,而且太危险了。」

「危险?」

「默认情况下,容器里的进程是 root。下话见。」


这一话写的其实不是「跑题去学 Python」,而是给前面搭好的脚本库装一个「对外遥控器」。没有它,脚本永远只能靠人去点。而 /health 这个接口看起来最简单,却是监控接入、容器编排探针、负载均衡摘除的共同入口。先把这个写好,后面 CI/CD 整条链路才串得起来。




上一篇:ARTEX二次开发版深度解析:从外网突破到内网纵深的AI自主渗透平台
下一篇:Windows 11/10 手动把 VS Code 加进系统 Path 的图文教程
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-22 06:36 , Processed in 0.082643 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表