单元五 Docker 发布与部署 · 第 22 课

上线前检查

一旦开放到公网,任何人都能访问你的网站,每点一次都在花你的钱。上线之前,先把门锁好、把开销限住。

本课目标

  • 让 AI 给周报助手加上访问密码和每日调用上限。
  • 完整走一遍“改代码、构建、推送、部署”的发布流程,发布 v2。
  • 按清单检查端口、密钥和文件权限。

先懂概念:公网上的风险

互联网上有大量程序在不停地扫描所有 IP 和域名。周报助手一旦能从公网访问,就会遇到:

  • 陌生人随手打开、反复点按钮,每一次都在消耗你的 API 额度;
  • 程序扫描你的服务,寻找漏洞。

两道最基本的防线:

  1. 访问密码:不知道密码的人进不来。
  2. 每日上限:就算密码泄露,也能限制每天调用 AI 的次数。计数保存在内存里,服务重启或更新后会清零,所以它只是一道临时防线,不是费用的硬上限,花费还要在账户后台关注。

跟着做

1. 让 AI 加上防护(在自己电脑上)

cd ~/ai-course/weekly-report
codex
上线前请给周报助手加两道防护:

1. 访问密码:从环境变量 APP_USER 和 APP_PASSWORD 读取。设置了 APP_PASSWORD 才开启;开启后所有页面和接口都要求 HTTP Basic 认证,/healthz 除外。比较用户名和密码时使用防时序攻击的比较方式。
2. 每日上限:从环境变量 DAILY_LIMIT 读取,默认 100。每天调用 AI 的次数超过上限后,改用规则版,并提示“今天的 AI 次数已用完,已使用规则版”。计数保存在内存里即可。

同时确认:输入长度限制和超时仍然有效,日志里不打印工作记录和密码。
先说计划,确认后再改。

2. 在本地测试

在一个新终端里临时设置密码,启动:

cd ~/ai-course/weekly-report
$env:APP_USER = "me"
$env:APP_PASSWORD = "test-password"
$env:DAILY_LIMIT = "1"
npm start
cd ~/ai-course/weekly-report
APP_USER=me APP_PASSWORD=test-password DAILY_LIMIT=1 npm start

打开 http://localhost:3000,浏览器会弹出登录框。输入错误的密码进不去,输入 me 和 test-password 才能进入。

这次把每日上限设成了 1:连续生成两次,第一次是 AI 写的,第二次应该提示“今天的 AI 次数已用完,已使用规则版”。确认后停止服务器、关掉这个终端,回到项目文件夹存档:

git add -A
git commit -m "访问密码和每日上限"

3. 发布 v2

这就是以后每次发布新版本的流程,第 25 课会整理成清单。

传到服务器(在自己电脑上):

scp -r "$HOME\ai-course\weekly-report" ai-server:
scp -r ~/ai-course/weekly-report ai-server:

构建并推送(在服务器上):

ssh ai-server

看到服务器的提示符后,再运行:

cd ~/weekly-report
docker build -t 你的用户名/weekly-report:v2 .
docker push 你的用户名/weekly-report:v2

这次直接用带用户名的全名构建,省去 docker tag 这一步。

在 .env 里加上密码和上限:

cd ~/apps/weekly-report
openssl rand -base64 18

openssl rand 会生成一串随机字符,拿它当密码。用 vim .env 在文件末尾加三行:

APP_USER=me
APP_PASSWORD=刚才生成的随机密码
DAILY_LIMIT=100

同样不要有空格和引号。把这个密码记到你的密码管理器里。

改用 v2 并重启:用 vim compose.yaml 把 image 那一行末尾的 v1 改成 v2,保存后运行:

docker compose up -d
docker compose ps

Compose 发现镜像变了,会用新镜像重建容器。

4. 验证

建立隧道(ssh -L 3000:127.0.0.1:3000 ai-server),打开 http://localhost:3000,应该先弹出登录框,输入 .env 里的用户名和密码后才能使用。

上线前检查清单

逐项检查,全部通过再进入下一单元:

检查项怎么检查应该看到
防火墙只开 22 端口云服务商控制台的防火墙页面只有 TCP 22
3000 端口只对本机开放服务器上运行 sudo ss -tlnp | grep 3000127.0.0.1:3000,而不是 0.0.0.0:3000
.env 只有自己能读ls -l ~/apps/weekly-report/.env-rw-------
镜像里没有 Key把第 19 课“确认 Key 不在镜像里”的两条命令中的镜像名换成 你的用户名/weekly-report:v2 再运行一遍两次都输出 0
代码里没有 Key在自己电脑的项目里运行 git grep --untracked -l "sk-"没有真正的 Key
访问密码已开启通过隧道打开网页弹出登录框
密码足够长看 .env 里的 APP_PASSWORD随机生成,16 位以上

最后,登录本站的账户后台,看看用量统计,了解周报助手每次调用大概花多少钱。

访问密码使用的是 HTTP Basic 认证,在没有加密的 http:// 连接上,密码是明文传输的。所以现在只通过 SSH 隧道访问;下一单元配好 HTTPS 以后,才正式对外开放。

常见问题

改了 .env,密码没生效? .env 只在容器创建时读取。改完运行 docker compose up -d,Compose 发现配置变了会重建容器;如果没有重建,运行 docker compose up -d --force-recreate。

登录框一直弹出,输入正确也进不去? 检查 .env 里等号两边有没有多余的空格,用户名和密码是否和你输入的完全一致,大小写也要一样。

网页还是 v1 的样子? 确认 compose.yaml 里改成了 v2,并且运行了 docker compose up -d;docker compose ps 的 IMAGE 一列会显示当前使用的镜像。

自检

  • 周报助手要输入密码才能使用。
  • 我完整走了一遍发布流程,服务器上运行的是 v2。
  • 上线前检查清单全部通过。
  • 我知道为什么在配好 HTTPS 之前只通过隧道访问。