# Windows 操作 NAS Docker · 命令手册 针对本项目的实际环境整理(身份证识别 API 部署在飞牛 fnOS)。 ## 本机参数 | 项 | 值 | |---|---| | NAS IP | `10.1.1.163` | | 项目目录 | `/vol1/1000/Docker/idcard-api` | | compose 项目名 | `icd`(镜像名 `icd-idcard-api`) | | 容器名 | `idcard-api` | | 服务端口 | `8000`(容器内 8000 映射到宿主机 8000) | | 宿主机用户 | `owen`(**不在 docker 组,docker 命令都要 `sudo`**) | --- ## 一、三个操作平面(最容易搞错的地方) ``` ┌─────────────────────────────────────────────┐ │ ① Windows PowerShell(你的电脑) │ │ 能做:curl.exe 测接口、WinSCP 传文件 │ │ 不能:任何 docker 命令 │ │ 提示符:PS C:\Users\Administrator> │ ├─────────────────────────────────────────────┤ │ ② NAS 宿主机(SSH 连进去) │ │ 能做:docker / docker compose / sudo / 文件操作 │ │ 登录:ssh owen@10.1.1.163 │ │ 提示符:owen@youjiala:/$ │ ├─────────────────────────────────────────────┤ │ ③ 容器内部(fnOS Docker 界面「终端」按钮) │ │ 能做:容器内的 ls / grep / cat / env │ │ 不能:docker 命令(容器里没装 docker CLI) │ │ 提示符:root@c5bbd0b03ba1:/# │ └─────────────────────────────────────────────┘ ``` **判断口诀**:命令里有 `docker` 或要动 NAS 上的文件 → 平面 ②;只是想试试接口通不通 → 平面 ①;想偷懒看容器里某个文件 → 平面 ③(但别在里面敲 docker)。 --- ## 二、快速判断表 | 你要做的事 | 在哪里执行 | 需要登录吗 | |---|---|---| | 测接口通不通(curl) | Windows PowerShell | ❌ 不需要 | | 上传/覆盖代码文件 | Windows(WinSCP / 资源管理器) | ❌ 不需要(SFTP) | | `docker ps` / `docker logs` | NAS 宿主机 | ✅ `ssh owen@10.1.1.163` | | `docker compose build/up` | NAS 宿主机 | ✅ SSH | | 看镜像创建时间 | NAS 宿主机 | ✅ SSH | | 改 `.env` | WinSCP 或 SSH 均可 | 视习惯 | | 查看容器内 `main.py` | NAS 宿主机 `docker exec` | ✅ SSH | | 快速瞄一眼容器内文件 | fnOS Docker 界面「终端」 | ❌ 界面点开即可 | | 加 docker 组免 sudo | NAS 宿主机 | ✅ SSH | --- ## 三、Windows 连接方式 ### SSH 登录宿主机 ```powershell ssh owen@10.1.1.163 # 首次会问 yes/no,输 yes,然后输 owen 的密码 # 退出:exit ``` 看到提示符 `owen@youjiala:/$` 就说明进去了。 ### 传文件(WinSCP / FileZilla) - 协议:`SFTP` - 主机:`10.1.1.163`,端口 `22` - 用户:`owen` - 目标目录:`/vol1/1000/Docker/idcard-api` 或者在 fnOS 网页的「我的文件 → Docker → idcard-api」里直接上传。 ### 一次性免 sudo(推荐做一次) ```bash sudo usermod -aG docker $USER newgrp docker # 当前会话立即生效,不用退出重登 ``` 之后所有 docker 命令都不用加 `sudo` 了。本文档仍按**带 sudo** 写,两种都能用。 --- ## 四、命令速查 ### 1. 测试接口(Windows PowerShell) > ⚠️ PowerShell 里 `curl` 是 `Invoke-WebRequest` 的别名,**必须写 `curl.exe`**。 ```powershell # 健康检查 curl.exe -s http://10.1.1.163:8000/api/v1/idcard/health # 根路径(服务信息 + 端点清单) curl.exe -s http://10.1.1.163:8000/ # 识别(带 token) curl.exe -X POST http://10.1.1.163:8000/api/v1/idcard/recognize ^ -H "Authorization: Bearer 你的token" ^ -F "file=@C:/path/idcard.jpg" # 识别(旧版 X-API-Key 头) curl.exe -X POST http://10.1.1.163:8000/api/v1/idcard/recognize ^ -H "X-API-Key: 你的key" ^ -F "file=@C:/path/idcard.jpg" # 反向验证:不带 token 应该 401 curl.exe -X POST http://10.1.1.163:8000/api/v1/idcard/recognize ^ -F "file=@C:/path/idcard.jpg" ``` > `^` 是 CMD 的续行符。PowerShell 用反引号 `` ` ``,或者直接写成一行。 ### 2. 首次部署(SSH) ```bash # ① 建目录(WinSCP 已传过文件可跳过) mkdir -p /vol1/1000/Docker/idcard-api cd /vol1/1000/Docker/idcard-api # ② 确认文件都在 ls -la # ③ 配 .env(必须有 .env,.env.example 不生效) cp .env.example .env vi .env # 或 WinSCP 直接编辑 # ④ 检查 compose 解析结果 sudo docker compose -p icd config | grep -A2 API_TOKENS # ⑤ 首次构建(会装 numpy/onnxruntime,3-5 分钟) sudo docker compose -p icd up -d --build # ⑥ 跟踪日志 sudo docker logs -f idcard-api ``` ### 3. 日常更新代码(SSH) ```bash cd /vol1/1000/Docker/idcard-api sudo docker compose -p icd up -d --build # 增量构建,10-30 秒 ``` 或者用项目自带脚本(会校验镜像里的代码真的更新了): ```bash bash /vol1/1000/Docker/idcard-api/deploy.sh ``` **只改了 `.env` 或 `docker-compose.yml`**(不需要 build): ```bash sudo docker compose -p icd up -d ``` **怀疑缓存坏了**(全量重建,3-5 分钟): ```bash sudo docker compose -p icd build --no-cache sudo docker compose -p icd up -d --force-recreate ``` ### 4. 排查"改了代码没生效" 这是本项目最容易踩的坑:**fnOS 的「重新部署」只重建容器,不重建镜像**。 ```bash # ① 容器里的代码是新版吗?0=旧代码,≥1=新代码 sudo docker exec idcard-api grep -c auth_enabled /app/app/main.py # ② 镜像什么时候建的? sudo docker images --format "{{.Repository}}:{{.Tag}} {{.ID}} 创建于 {{.CreatedSince}}" | grep -i idcard # ③ compose 项目真实工作目录(和你上传的目录一致吗?) sudo docker inspect idcard-api --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}' # ④ 环境变量注入情况 sudo docker exec idcard-api env | grep -E "API_TOKENS|API_KEY|CLIENT_SECRET" # ⑤ 容器状态与端口 sudo docker ps --filter name=idcard-api ``` ### 5. 日常运维(SSH) ```bash # 实时日志 sudo docker logs -f idcard-api # 最近 50 行 sudo docker logs --tail 50 idcard-api # 过滤调用记录(看谁在调、耗时多少) sudo docker logs --tail 100 idcard-api | grep "recognize by=" # 内存占用(关键:1C2G 机器要盯这个) sudo docker stats --no-stream idcard-api # 重启(不重新读 .env,改配置请用 up -d) sudo docker restart idcard-api # 停掉并删除容器 + 镜像 sudo docker compose -p icd down --rmi local # 进容器内部 shell(在宿主机执行) sudo docker exec -it idcard-api bash ``` ### 6. 容器内部(fnOS 界面「终端」里执行) ```bash grep -c auth_enabled /app/app/main.py # 代码版本 ls -l --time-style=long-iso /app/app/ # 文件时间 echo $API_TOKENS # 环境变量 ``` > 这里**不能**跑 `docker` 命令。要跑 docker,回宿主机 SSH。 --- ## 五、完整流程:从改代码到生效 ```bash # ① Windows:改本地文件(WorkBuddy 生成) # ② Windows:WinSCP 上传到 /vol1/1000/Docker/idcard-api # 注意保持目录结构:app/、Dockerfile、docker-compose.yml 等 # ③ SSH 登录 NAS ssh owen@10.1.1.163 # ④ 构建并启动 cd /vol1/1000/Docker/idcard-api sudo docker compose -p icd up -d --build # ⑤ 校验镜像里的代码真的换了(关键!别跳过) sudo docker exec idcard-api grep -c 你的新特征字符串 /app/app/main.py # ⑥ 退出 SSH,回 Windows 测接口 exit ``` ```powershell curl.exe -s http://10.1.1.163:8000/api/v1/idcard/health ``` --- ## 六、错误对照表(本对话踩过的坑) | 报错 | 在哪发生 | 原因 | 解决 | |---|---|---|---| | `docker: command not found` | 容器内部 | 在容器里跑 docker 命令 | 回宿主机 SSH,或去掉 `docker exec` 前缀用容器内路径 | | `permission denied ... docker.sock` | 宿主机 | 用户不在 docker 组 | 命令前加 `sudo`,或 `usermod -aG docker` | | `401 Unauthorized ... docker.fnnas.com` | 宿主机 build | fnOS 加速源不授权,且会覆盖 daemon.json | Dockerfile 用 `ARG BASE_IMAGE=国内仓库全路径`,绕开 daemon 配置 | | `could not find ... dockerfile` | 宿主机 build | Dockerfile 首行 `# syntax=` 触发前端镜像拉取 | 删掉该行 | | health 返回缺字段 / 行为没变化 | Windows 测接口 | 镜像没重建,跑的还是旧镜像 | 必须 `up -d --build`,不能只「重新部署」 | | `.env` 改了不生效 | 宿主机 | ①文件叫 `.env.example` ②只 `restart` 了 | 创建真正的 `.env`,然后 `up -d`(不是 restart) | | `curl: 无法将...识别为 cmdlet` | Windows | `curl` 是 PowerShell 别名 | 用 `curl.exe` | | `\r: command not found` | 宿主机 | Windows CRLF 换行 | `sed -i 's/\r$//' 脚本名.sh` | | 识别返回空,前端提示"未识别到信息" | C# 调用方 | 401 被静默吞掉(没检查状态码) | C# 加鉴权头 + 检查 `IsSuccessStatusCode` | --- ## 七、两条铁律 1. **改代码必须 build**。`docker compose up -d`(不带 `--build`)在镜像已存在时不会重新构建,只重建容器。代码改动一点都进不去。 2. **`-p icd` 不能省**。项目名不是目录名,不带这个参数会创建新项目,容器和镜像都对不上。