From 36fce94692ebd8fb118c8a6c965a05b5f749ceb2 Mon Sep 17 00:00:00 2001 From: huty Date: Thu, 21 May 2026 11:39:30 +0900 Subject: [PATCH] docs: add Docker Compose deployment and README Add a single-service docker-compose setup with bind-mounted config / templates / apps / playbooks / data so users can iterate on inventory and intents without rebuilding the image. Dockerfile uses python:3.12-slim with tini for clean signal handling, and ships openssh-client for in- container troubleshooting. Health check hits the /health endpoint. README documents project background, the L1-L5 architecture, both local and Docker deployment paths, configuration keys, intent template extension, and the safety model. Co-Authored-By: Claude Opus 4 --- .dockerignore | 34 +++++ .gitignore | 3 + Dockerfile | 43 +++++++ README.md | 300 +++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 49 ++++++++ 5 files changed, 429 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f75f0f0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,34 @@ +# VCS / 编辑器 / 工具 +.git/ +.gitignore +.gitattributes +.vscode/ +.idea/ +.claude/ +.DS_Store +Thumbs.db + +# Python 缓存 +__pycache__/ +*.py[cod] +*.egg-info/ +.venv/ +venv/ +.pytest_cache/ +.ruff_cache/ +.mypy_cache/ + +# 本地数据 / 密钥 +data/ +*.db +*.sqlite* +.env +.env.local +config/inventory.yaml + +# 文档(保留 README 以便容器内可见,但构建时不需要) +*.md + +# 测试 & CI +tests/ +.github/ diff --git a/.gitignore b/.gitignore index 8d1a096..fd48ea7 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ data/ audit.log logs/ +# Docker +docker-compose.override.yml + # Inventory secrets (only ship the example) config/inventory.yaml !config/inventory.example.yaml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2550ea7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,43 @@ +# syntax=docker/dockerfile:1.6 +FROM python:3.12-slim AS base + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 + +WORKDIR /app + +# 基础系统依赖: +# - gcc/libffi-dev : 编译某些 wheel(如 cryptography/asyncssh 依赖) +# - openssh-client : 提供 ssh 客户端工具,便于排障 +# - tini : 优雅处理信号 +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc \ + libffi-dev \ + openssh-client \ + tini \ + && rm -rf /var/lib/apt/lists/* + +# 先安装依赖(pyproject + src 一起,以便 setuptools 发现包) +COPY pyproject.toml ./ +COPY src/ ./src/ +RUN pip install . + +# 复制运行时资源(这些目录在 compose 里默认会被卷挂载覆盖) +COPY templates/ ./templates/ +COPY config/ ./config/ +COPY apps/ ./apps/ +COPY playbooks/ ./playbooks/ + +# data/ 用于审计日志 + SQLite,运行时挂载卷 +RUN mkdir -p /app/data + +# 默认监听所有接口(容器内) +ENV OPS_HOST=0.0.0.0 \ + OPS_PORT=8000 + +EXPOSE 8000 + +ENTRYPOINT ["/usr/bin/tini", "--"] +CMD ["ops", "serve"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..aa46370 --- /dev/null +++ b/README.md @@ -0,0 +1,300 @@ +# AI App Ops Tools + +> 面向运维工程师的 **AI 驱动自然语言运维终端** —— 用一句话统一操作 Linux、Windows 等异构主机。 + +[![Python](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org) +[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) + +## 项目背景 + +日常运维中我们经常面对: + +- **多种操作系统**:Windows / Ubuntu / CentOS / Debian / macOS … +- **不同的命令体系**:`df -h` vs `Get-PSDrive`、`systemctl restart` vs `Restart-Service`、`apt` vs `yum` vs `dnf` … +- **重复劳动**:日常巡检、批量诊断、应用部署,过程难以沉淀 + +本项目尝试用 **AI + 跨 OS 意图模板** 这一组合,把"记住命令"的负担交给工具,让运维人员只关心"做什么",并把每一次操作沉淀为可复用的知识资产。 + +## 核心特性 + +- 🗣️ **自然语言操作**:基于 Claude Function Calling,用一句话完成跨主机操作 +- 🛡️ **三级风险闸门**:`READ` 自动 / `WRITE` 需确认 / `DESTRUCTIVE` 默认禁用 +- 🌐 **统一异构 OS**:同一意图(如 `check_disk_usage`)在 Linux/Windows/macOS 下自动路由到不同实现 +- 📜 **意图模板库**:所有命令以 YAML 形式版本化管理,是项目的核心知识资产 +- 🔌 **多种连接器**:SSH(Linux/Unix)、WinRM(Windows)、Local(本机测试) +- 📊 **全量审计日志**:谁、何时、用什么自然语言、对哪些主机执行了什么命令 +- 🧱 **分层架构**:从单条命令到应用部署,逐层演进无需重构 + +## 架构分层 + +``` +┌─────────────────────────────────────────────────────┐ +│ L5 策略层 │ 灰度策略、变更窗口、AI 风险预判 │ +├─────────────────────────────────────────────────────┤ +│ L4 编排层 │ 多主机有序执行、依赖、健康检查门控 │ +├─────────────────────────────────────────────────────┤ +│ L3 应用层 │ 部署 / 升级 / 回滚 = 业务原子操作 │ +├─────────────────────────────────────────────────────┤ +│ L2 操作层 │ 装包 / 改配置 / 重启服务 / 拷文件 │ +├─────────────────────────────────────────────────────┤ +│ L1 命令层 │ df / ps / 跨 OS 命令模板 ← 当前阶段 │ +└─────────────────────────────────────────────────────┘ +``` + +数据流: + +``` +用户 NL 请求 + ↓ +AI 编排 (Claude + Function Calling,只能调用已注册的意图) + ↓ +意图模板库 (按主机 OS 路由到对应实现) + ↓ +风险闸门 (READ/WRITE/DESTRUCTIVE) + ↓ +连接器 (SSH / WinRM / Local) + ↓ +审计日志 + 结果归一化 + AI 摘要 +``` + +## 项目结构 + +``` +ai-app-ops-tools/ +├── config/ +│ ├── inventory.example.yaml # 主机清单模板 +│ └── settings.yaml # 运行时策略(含 destructive 关键字) +├── templates/ # 意图模板库 ★ 核心知识资产 +│ ├── disk.yaml +│ ├── system.yaml +│ └── service.yaml +├── apps/ # 预留:L3 应用台账 +├── playbooks/ # 预留:L3-L4 部署剧本 +├── src/ops_tools/ +│ ├── config.py # Pydantic Settings +│ ├── inventory/ # 主机清单 + facts 探测 +│ ├── intents/ # 意图模板加载 +│ ├── connectors/ # local / ssh / winrm +│ ├── executor/ # 风险闸门 + 执行 + 审计 +│ ├── audit/ # SQLite 审计日志 +│ ├── ai/ # Claude 编排(Function Calling) +│ ├── api/ # HTTP API +│ ├── main.py # FastAPI 入口 +│ └── cli.py # Typer CLI +├── tests/ +├── Dockerfile +├── docker-compose.yml +├── pyproject.toml +└── .env.example +``` + +## 快速开始 + +### 方式一:本地开发 + +#### 1. 准备 Python 环境(需要 Python ≥ 3.11) + +```bash +python -m venv .venv +source .venv/bin/activate # Windows: .venv\Scripts\activate +pip install -e . +``` + +#### 2. 准备配置 + +```bash +cp .env.example .env +# 编辑 .env,至少填入 ANTHROPIC_API_KEY + +cp config/inventory.example.yaml config/inventory.yaml +# 编辑 inventory.yaml 加入你的真实主机 +``` + +#### 3. 体验 CLI + +```bash +# 列出主机 +ops hosts + +# 列出已注册的意图 +ops intents + +# 在 localhost 上执行查磁盘 +ops run check_disk_usage localhost + +# 写入类意图会被拦截,附命令预览 +ops run restart_service localhost --params '{"service": "Spooler"}' +# 加 --confirm 才会真正执行 +ops run restart_service localhost --params '{"service": "Spooler"}' --confirm + +# 自然语言对话(需 ANTHROPIC_API_KEY) +ops chat "看一下 localhost 的磁盘和内存" +``` + +#### 4. 启动 HTTP 服务 + +```bash +ops serve +# 访问 http://127.0.0.1:8000/docs 查看 OpenAPI 文档 +``` + +--- + +### 方式二:Docker Compose 部署(推荐用于服务器侧) + +#### 1. 准备配置文件 + +```bash +cp .env.example .env +cp config/inventory.example.yaml config/inventory.yaml +# 按需编辑两个文件 +``` + +#### 2. 构建 + 启动 + +```bash +docker compose up -d --build +``` + +容器启动后会自动: +- 监听 `0.0.0.0:8000`(宿主机端口可在 `.env` 中通过 `OPS_PORT` 覆盖) +- 将 `./config /templates /apps /playbooks /data` 以 bind mount 挂载进容器,**编辑这些文件无需重建镜像** +- 把审计 + SQLite 数据持久化到宿主机的 `./data` 目录 +- 自带健康检查(`/health` 端点) + +#### 3. 验证服务 + +```bash +# 健康检查 +curl http://127.0.0.1:8000/health + +# 列出主机 +curl http://127.0.0.1:8000/api/v1/hosts | jq + +# 列出意图 +curl http://127.0.0.1:8000/api/v1/intents | jq + +# 直接执行意图 +curl -X POST http://127.0.0.1:8000/api/v1/run \ + -H 'Content-Type: application/json' \ + -d '{"intent":"check_disk_usage","hosts":["localhost"]}' + +# 自然语言对话 +curl -X POST http://127.0.0.1:8000/api/v1/chat \ + -H 'Content-Type: application/json' \ + -d '{"message":"看一下生产环境的磁盘使用率"}' +``` + +#### 4. 进入容器执行 CLI + +```bash +docker compose exec ops ops hosts +docker compose exec ops ops intents +docker compose exec ops ops chat "重启 nginx 服务" +``` + +#### 5. 常用运维命令 + +```bash +docker compose logs -f ops # 看日志 +docker compose restart ops # 重启服务 +docker compose down # 停止 +docker compose up -d --build # 改代码后重建 +``` + +#### 关于 SSH 密钥 + +如果要从容器内部 SSH 到目标 Linux 主机,需要把宿主机的 SSH 密钥挂进容器。在 `docker-compose.yml` 中解开对应的 `volumes` 行: + +```yaml +# Linux / macOS +- ${HOME}/.ssh:/root/.ssh:ro + +# Windows (示例,按你的实际路径) +- C:/Users//.ssh:/root/.ssh:ro +``` + +> ⚠️ 生产环境建议使用 SSH agent forwarding、Vault、跳板机等更安全的方案,而不是直接挂载本地密钥。 + +#### 升级到 PostgreSQL(可选) + +SQLite 适合单机 MVP;如果要多实例部署或大量审计日志,把 `docker-compose.yml` 中 `db` 服务的注释解开,并修改 `OPS_DB_URL`: + +```env +OPS_DB_URL=postgresql+asyncpg://ops:changeme@db:5432/ops +``` + +并在 `pyproject.toml` 中加入 `asyncpg`。 + +## 配置说明 + +### `.env` 关键项 + +| 变量 | 说明 | 默认 | +|---|---|---| +| `ANTHROPIC_API_KEY` | Anthropic API Key(chat 功能必需) | 空 | +| `OPS_MODEL_MAIN` | 主模型 | `claude-sonnet-4-6` | +| `OPS_MODEL_FAST` | 轻量任务模型 | `claude-haiku-4-5-20251001` | +| `OPS_DB_URL` | 审计数据库 URL | `sqlite+aiosqlite:///./data/ops.db` | +| `OPS_AUTO_EXECUTE_WRITE` | WRITE 类是否自动执行 | `false` | +| `OPS_ALLOW_DESTRUCTIVE` | 是否允许 DESTRUCTIVE | `false` | +| `OPS_HOST` / `OPS_PORT` | API 监听地址 | `127.0.0.1` / `8000` | + +### `config/inventory.yaml` 主机清单 + +支持 SSH(密钥 / 密码)、WinRM、Local 三种连接方式,详细字段见 `inventory.example.yaml`。密码字段支持 `${ENV_VAR}` 形式从环境变量插值,避免明文落盘。 + +### `config/settings.yaml` 运行时策略 + +- 默认超时 +- SSH / WinRM 传输参数 +- 风险策略(如自动升级为 DESTRUCTIVE 的关键字白名单) + +## 扩展意图模板(核心知识沉淀) + +在 `templates/` 下新建 YAML 即可。模板示例: + +```yaml +intents: + - intent: check_listening_ports + description: 查看本机监听端口 + risk_level: READ + params: [] + implementations: + linux: + command: "ss -tlnp || netstat -tlnp" + windows: + command: "Get-NetTCPConnection -State Listen | Select-Object LocalAddress,LocalPort,OwningProcess | ConvertTo-Json" +``` + +**设计原则**: +1. **意图名要表达"意图",而不是命令** —— `check_listening_ports` ✅,`run_ss` ❌ +2. **风险等级保守标注** —— 不确定就标 `WRITE`,确认行为安全后再降级 +3. **参数显式声明** —— 模板里通过 `params` 列出,便于 AI 理解和参数校验 + +每加一个意图,AI 都能立刻自然语言调用 —— **意图模板库就是你的运维知识图谱**。 + +## 安全模型 + +| 设计 | 防护目标 | +|---|---| +| LLM 不能生成 shell,只能调用已注册意图 | 防幻觉、防注入 | +| 参数通过 `shlex.quote` 转义 | 命令注入 | +| 三级风险闸门 + 渲染后再分级 | 误操作放大 | +| 全量审计日志(含原始 NL 输入) | 责任追溯 | +| `inventory.yaml` 被 git 忽略 | 凭据泄漏 | +| 密码字段支持 `${ENV_VAR}` 插值 | 明文落盘 | + +> ⚠️ 当前版本面向受信任的内部使用场景;如对外暴露 API 必须叠加认证(OIDC / mTLS)和网络隔离。 + +## 路线图 + +- [x] **L1 命令层** — 跨 OS 自然语言查询(MVP,当前阶段) +- [ ] **L2 操作层** — 服务启停、装包、改配置(部分已就位) +- [ ] **L3 应用层** — 单应用部署 / 升级 / 回滚(应用台账 + 部署剧本) +- [ ] **L4 编排层** — 灰度、滚动、健康检查门控、多应用依赖 +- [ ] **L5 策略层** — 变更窗口、AI 变更风险预判 + +## License + +MIT diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..a3d6142 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,49 @@ +services: + ops: + build: + context: . + dockerfile: Dockerfile + image: ai-app-ops-tools:latest + container_name: ai-app-ops-tools + restart: unless-stopped + ports: + - "${OPS_PORT:-8000}:8000" + env_file: + - .env + environment: + # 容器内固定监听 0.0.0.0;对外端口通过上面的 ports 映射 + OPS_HOST: "0.0.0.0" + OPS_PORT: "8000" + # SQLite 数据库路径与卷挂载点一致 + OPS_DB_URL: "sqlite+aiosqlite:////app/data/ops.db" + volumes: + # 配置、模板、台账、剧本都用 bind mount,方便不重启即可编辑 + - ./config:/app/config + - ./templates:/app/templates + - ./apps:/app/apps + - ./playbooks:/app/playbooks + # 审计日志 + SQLite 持久化 + - ./data:/app/data + # SSH 密钥(按需启用;Windows 上路径需自行调整) + # - ${HOME}/.ssh:/root/.ssh:ro + healthcheck: + test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3)\""] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + +# 未来如需切换到 PostgreSQL,可解开下面的注释并修改 OPS_DB_URL +# db: +# image: postgres:16-alpine +# container_name: ai-app-ops-tools-db +# restart: unless-stopped +# environment: +# POSTGRES_USER: ops +# POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme} +# POSTGRES_DB: ops +# volumes: +# - pgdata:/var/lib/postgresql/data +# +# volumes: +# pgdata: