这两年做AI应用的朋友应该都能感受到,模型本身的能力提升很快,真正耗精力的反而是怎么把模型能力安全、稳定地开放给外部使用。我最近在云服务器上把 Deepseek Harness 完整跑了一遍,从最初的空机器到最后公网鉴权访问全链路打通,整个过程大概花了一天时间,其中有几个环节的坑很有意思。这篇就基于我的实操记录,把部署思路、具体命令、配置要点和排障经验一并整理出来,既是给自己留档,也希望给准备做类似部署的团队省点时间。
简单说,Deepseek Harness 是一个面向 Deepseek 模型请求的服务封装层,也可以理解成一个轻量模型网关。它把上游模型 API 密钥、用户请求鉴权、限流控制、日志审计都收敛到一个统一入口,对外只暴露一个 HTTPS 地址。如果你经常写各种小工具、内部系统、自动化脚本,又不想每次都把模型 Key 散落在各个客户端里,那这套部署思路就特别有用。这篇文章适合有基础 Linux 操作能力、打算把 AI 能力开放给公网的开发者,哪怕是第一次做服务部署,跟着步骤也能跑通。
1. 部署前的核心认知与方案选型
1.1 这个部署到底要解决什么问题
先讲一个很常见的场景。你写了一个对话机器人,或者一个内容分析的定时任务,放在自己电脑上跑得好好的,但同事、朋友、另一个系统也想调用。你会怎么做?大概率是把模型 API Key 直接发给对方,或者写成公共接口放在服务器上。前者的问题很直接:Key 一旦传出去,泄露风险和额度风险立刻变成不可控的。后者的问题更隐蔽,裸奔在公网上的接口会被扫描器盯上,很快就会被各种爬虫和恶意请求打爆。
所以真正要解决的不仅是“把服务跑起来”,而是“把服务以受控的方式开放出去”。Deepseek Harness 在这里承担的就是一个守门员的角色,所有请求先经过它,它确认请求方身份合法、请求频率在允许范围内,才转发给上游模型服务。这样一来,模型 Key 始终只保存在服务器端,外部使用者拿到的只是 Harness 签发的临时凭证,思路和公司内部统一走网关出口是一致的。
从部署形态上看,这个方案是三层结构。最外层是 Nginx 反向代理,负责终止 HTTPS 加密连接;中间层是 Harness 应用本身,负责鉴权、限流和转发;最底层是 Deepseek 模型 API,Harness 通过官方接口和你配置的模型 Key 完成实际调用。每一层只干一件事,出了问题也容易定位。
1.2 公网访问为什么要加一层鉴权
可能有人觉得,反正 Deepseek API 本身也是走公网的,我直接把 Harness 部署起来,对外暴露端口不就行了?不是这么回事。Harness 只是把服务封装好了,但公网流量不设防的话,等于把你家的门牌号写在广场上,任何人都能来敲门。
公网扫描器对常见端口 8080、8888、3000 这些非常敏感,部署当天就可能被扫描到。一旦扫描到你在跑一个看起来像 API 的服务,接下来就是各种试探请求,比如尝试未授权访问、注入、爆破。如果没有鉴权层,不仅你的服务器资源会被白白消耗,模型额度也会被薅走。公网鉴权不是可选项,是强制项。
鉴权方案的选择我建议按规模来。个人项目或者小团队内部系统,用 API Key 最简单,一个请求头就能解决问题。需要支持多个用户、且要区分用户权限,用 JWT 更合适,Harness 在验证令牌的同时还能解析出用户身份。企业级多服务接入,才需要考虑 OAuth2 这类完整授权协议。我这个部署用的是 JWT 加 API Key 的组合,JWT 面向外部调用方,API Key 作为内部服务之间的通信凭证,在灵活性和安全性之间比较平衡。
1.3 整体架构怎么搭才不绕路
架构图在心里过一遍,实际操作就不会乱。我的规划是:一台云服务器,装了 Docker 和 Nginx。Docker 里跑两个容器,一个是 Harness 主服务,负责处理请求;一个是 Redis,用来做限流统计。Nginx 直接跑在宿主机上,监听 443 端口,把符合条件的请求转发给宿主机的 127.0.0.1:8080,也就是 Harness 容器映射出来的端口。
为什么要放 Redis?因为限流需要一个共享计数器。将来 Harness 扩容到多个实例,Redis 依然能保证计数准确。现在只有单实例,也许有人觉得没必要,但从一开始就把限流基础打好,后面扩展就少一次改造。而且 Redis 本身很轻,占的内存可以忽略不计。
选择 Docker 而不是直接装二进制,最大的好处是环境隔离和回滚方便。删除再创建容器比卸载一堆依赖要干净得多,升级 Harness 版本也只需要拉新镜像重启服务。唯一要注意的是容器网络配置,Harness 容器内连接 Redis 用容器名解析,而外部访问映射出来的端口只监听宿主机回环地址,不允许公网直连。
2. 云服务器准备与运行环境搭建
2.1 服务器配置怎么选不踩坑
先说结论:普通业务量的公网接入场景,2 核 4G 内存、40G SSD、5M 带宽是足够的。Harness 本身只是做请求转发,不跑模型推理,CPU 和内存的消耗远没有想象中大。真正的瓶颈通常在带宽和上游 API 的响应延迟上,模型生成文本是一个流式过程,长回答会让连接保持较长时间,5M 带宽应对几十个并发用户基本没问题。
我看到不少教程推荐最低 1 核 2G,这种配置其实比较紧张。Linux 系统本身占接近 500M,Docker 和 Nginx 又吃掉一部分,Redis 再占 100M 左右,留给 Harness 的余量就很有限了。一旦访问量上来,OOM 是大概率事件。既然都是要长期跑的服务,多花一点预算把内存翻到 4G,后面能少处理很多麻烦。
操作系统选 Ubuntu 22.04 LTS,理由很简单:软件源更新及时,Docker 和 Nginx 的安装资料最多,遇到问题更容易搜到解决方案。服务器买到手之后,先做一次系统更新,把底层补丁打好,再开始装东西。注意更新完后重启一下,确保内核也在新版本上。
2.2 基础软件栈安装
我的安装习惯是先装 Docker 和 Nginx,再打开防火墙。整个过程大概这样:
sudo apt update && sudo apt upgrade -y sudo apt install -y docker.io docker-compose-v2 nginx git curl ufw sudo systemctl enable --now docker sudo systemctl enable --now nginx装完后检查一下版本,确认 Docker 和 Docker Compose 都可用:
docker --version docker compose version这里说一下为什么用 docker-compose-v2。新版的 Docker Compose 已经是 Go 写的独立插件,执行命令是docker compose,不带横杠。网上很多旧教程写的是docker-compose,如果你安装的版本不支持,会很难受。用 apt 直接装的 docker-compose-v2 基本都能正常工作。
在装 Nginx 的时候有个容易忽略的细节:Ubuntu 默认的 Nginx 启动后,会把default站点绑定在 80 端口。在证书还没申请好之前,这个页面可以先放着,不需要马上动它。如果一上来就急着删掉 default 站点,配置有疏漏可能导致 Nginx 都启动不了。
2.3 SSH 安全加固
这一步很多人会跳过,但我强烈建议做,因为服务器一旦暴露公网,SSH 端口是首先被爆破的目标。
我平时是这样加固的:修改 SSH 默认端口、禁止密码登录、只允许密钥登录、禁止 root 直接登录。
先在本地电脑生成密钥对:
ssh-keygen -t ed25519 -C "your_email@example.com"然后把公钥追加到服务器的 authorized_keys。改 SSH 配置的时候先别急着退出会话,要新开一个终端验证能登录,再决定要不要关掉密码登录。改错配置导致 SSH 连不上,只能去云厂商控制台用网页终端救回来,那个体验很痛苦。
修改端口这一步有个连带影响:后面配防火墙的时候,一定要记住把新端口放行。我踩过一次坑是把 SSH 端口改成 2222,然后 ufw 只放行了 22,结果重启后自己把门锁外面了,只能去控制台操作。从那以后我学乖了,每次改端口和防火墙规则都是两步一起配,改完立刻验证,绝不在没验证之前断开当前连接。
3. Deepseek Harness 安装与鉴权配置
3.1 获取程序与目录规划
我习惯把所有应用统一放在/opt目录下,结构清晰也方便备份。这次的项目目录规划是:
/opt/deepseek-harness/ ├── docker-compose.yml ├── .env ├── config.yaml └── logs/.env文件存放环境变量,config.yaml是 Harness 的主配置。这样的好处是配置文件和其他文件分离,备份也只备份几个小文件,非常干净。
获取 Harness 程序这一步,有人喜欢用 git clone 拉仓库,有人喜欢下载编译好的二进制包。我的建议是用发布包,因为程序依赖关系复杂的时候,直接下载二进制包比编译省事得多。发布包通常包含一个可执行文件和默认配置模板,放到指定目录改一改就能用。
3.2 鉴权模块配置:JWT 与 API Key
鉴权是整个部署的核心,配置时必须想清楚每个参数的含义。先看我最开始用的配置文件片段:
server: listen: 127.0.0.1:8080 auth: mode: jwt jwt_secret: "这里放随机生成的密钥" token_expire: 2h api_keys: - name: internal key: "sk-harness-internal-xxxx" rate_limit: enabled: true redis_addr: redis:6379 qps: 5 burst: 10 upstream: deepseek_api_key: "sk-deepseek-真实Key" base_url: "https://api.deepseek.com"JWT 密钥不能随便写个字符串,要足够长、足够随机。我通常用这条命令生成:
openssl rand -base64 48生成出来的内容直接填进去,保护 JWT 签名的安全性。token_expire设的是 2 小时,这个值要根据使用场景调:如果调用方是长期跑批任务,两个小时太短,每次都要重新获取令牌很麻烦;如果是网页应用后端,短期令牌更安全。我建议从 2 小时开始跑,后面根据实际情况调整。
api_keys这一段是内部服务互调的凭据。比如你有一个定时任务,每天用 API Key 方式调用 Harness,就不需要走 JWT 的签发流程。把 Key 配好,请求时加在 Header 里就能通过鉴权。
rate_limit里 qps 表示每秒允许的请求数,burst 表示突发时允许的瞬时请求上限。5 QPS 是我测试时的初始值,一个对话应用基本够用,如果被恶意刷还能用 Redis 的计数把请求挡在外面。
3.3 打通模型上游通道
Harness 的职责是转发请求给模型 API,所以要先把上游通路验证好。先到模型平台申请 API Key,然后在服务器上测试连通性。
curl https://api.deepseek.com/v1/models \ -H "Authorization: Bearer sk-你的Key"这一步能确认两件事:一是服务器能正常访问 Deepseek API,没有网络层面的问题;二是 Key 本身有效。如果这里返回错误,后面的 Harness 配置再怎么调都是白搭,因为问题在源头。
打通之后,把真实 Key 填进 Harness 配置的upstream.deepseek_api_key字段。这里有个关键细节:Harness 内部转发时,会把调用方带过来的鉴权信息替换成上游模型的 Key,直接换成自己的身份去请求模型。对调用方来说,他们只知道 Harness 的服务地址,根本接触不到模型平台的真实 Key。
敏感配置建议不要直接写在 config.yaml 里。像 JWT 密钥和模型 Key,我通常会通过环境变量注入。Docker Compose 里可以这样定义:
services: harness: environment: DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY}.env文件里存真实值,config.yaml里用占位符引用。这样整个配置文件即使被截取到,也不会把核心密钥暴露出去。
3.4 本地试运行验证
配置完成后先在本机验证,不要急着开放公网。用 Docker Compose 启动服务:
cd /opt/deepseek-harness docker compose up -d然后查看日志,确认启动没有报错:
docker compose logs -f harness启动正常后,先测试健康检查接口。Harness 一般会提供/healthz接口,不经过鉴权,直接用 curl 访问:
curl http://127.0.0.1:8080/healthz返回一个表示正常的 JSON 就说明服务活着。接下来再测试鉴权后的模型请求,先拿到 JWT,再带 Token 请求对话接口:
curl http://127.0.0.1:8080/chat/completions \ -H "Authorization: Bearer 得到的Token" \ -H "Content-Type: application/json" \ -d '{"model":"deepseek-chat","messages":[{"role":"user","content":"你好"}]}'这里需要说明一点:不同版本的 Harness,接口路径可能不完全一样。有的版本叫/v1/chat/completions,有的叫/chat/completions,具体以你自己用的版本文档为准。本地验证通过后再进行下一步,这样可以保证后面挂公网时,问题不会出在 Harness 本身。
4. HTTPS 反向代理与公网安全暴露
4.1 域名解析与 SSL 证书申请
公网访问必须走 HTTPS,原因不用多讲,明文传输的 API 请求在链路上很容易被中间人截获,JWT 一旦被偷走,整个鉴权体系就形同虚设。
第一步先把域名解析到服务器 IP。在域名解析控制台添加一条 A 记录,主机记录填api,记录值填服务器的公网 IP。解析生效大概需要几分钟,可以用ping api.你的域名.com确认。
证书申请我用的 acme.sh 脚本,流程简单。先在服务器上安装脚本,然后执行申请命令。申请过程中需要验证你对域名的控制权,常见做法是临时在 80 端口放一个验证文件,Nginx 能访问到就行。所以前面提到安全组放行 80 端口,是为了让证书验证流量进得来,验证完成后可以把 80 端口从防火墙规则里移掉,只保留 443。
申请成功后,证书文件会下载到指定目录,Nginx 配置引用即可。acme.sh 会自动配置续期任务,快到过期时会自动重新申请,这个不用自己盯着。
4.2 Nginx 反向代理配置实操
Nginx 配置是整个公网暴露的关键环节。我的站点配置如下:
server { listen 443 ssl http2; server_name api.你的域名.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; client_max_body_size 10m; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 300s; } } server { listen 80; server_name api.你的域名.com; return 301 https://$host$request_uri; }几个容易踩坑的地方说一下。
proxy_pass后面必须是http://127.0.0.1:8080,不能写http://localhost:8080。如果 Nginx 配置了 IPv6 解析,localhost 可能被解析成::1,而 Harness 只监听了 IPv4 的 127.0.0.1,这样会导致 502。
client_max_body_size 10m是给大请求体用的。默认只允许 1M,如果传一段很长的上下文文本给模型,请求体很容易超过 1M,Nginx 会直接拒绝。10M 对于对话类应用足够,如果要做文档分析,可能要更大。
proxy_read_timeout 300s也很关键。模型生成文本是流式的,一次完整回答可能超过几十秒,如果 Nginx 默认的 60 秒超时还没有收到上游响应,就会主动断连。设成 300 秒给长回复留足时间。
配置写完后测试语法:
nginx -t确认没问题再重新加载:
nginx -s reload4.3 防火墙与安全组策略
防火墙策略要遵循最小暴露原则。云服务器的防护分两层:云厂商控制台里的安全组,和服务器内的 ufw。
安全组规则我只放行三个端口:SSH 端口(改成什么就放什么)、80 端口(证书续期和 HTTP 跳转用)、443 端口(HTTPS 流量)。其他端口一律不放,尤其是 Harness 映射出来的 8080,绝对不能让公网直接访问。曾经有朋友把 8080 端口开到公网,结果 API Key 被扫描到,一夜之间额度被刷爆,这个教训希望大家不用体验。
服务器内 ufw 的命令这样配:
sudo ufw allow 2222/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable这里要注意,如果 SSH 端口改了,一定要先放行新端口再开启 ufw。开启后ufw status看一眼当前规则,确认没问题再退出当前 SSH 会话,防止把自己锁在外面。
5. systemd 守护进程与日志管理
5.1 用 systemd 管理 Harness 服务
Docker Compose 虽然能通过docker compose up -d启动服务,但如果服务器重启了,容器并不会自己恢复,除非每个容器都配置了自动重启策略。
我既在容器层面配置了restart: always,也用 systemd 把整个 Docker Compose 服务包了一层。这样相当于上了双保险,服务器开机后 systemd 会把 compose 启动起来,compose 又会拉起容器。
在/etc/systemd/system/deepseek-harness.service下新建服务文件:
[Unit] Description=Deepseek Harness Stack Requires=docker.service After=docker.service network-online.target [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=/opt/deepseek-harness ExecStart=/usr/bin/docker compose up -d ExecStop=/usr/bin/docker compose down ExecReload=/usr/bin/docker compose restart [Install] WantedBy=multi-user.target启用并启动服务:
sudo systemctl daemon-reload sudo systemctl enable --now deepseek-harness用 systemd 管理的好处不只是开机自启,还能通过systemctl status deepseek-harness查看服务状态,通过journalctl -u deepseek-harness -f看日志输出。这个管理方式是运维视角,适合长期运行的服务,比单纯依赖 Docker 原生策略更清晰。
Type 选oneshot是因为docker compose up -d命令执行完就返回,不会一直占着前台。加RemainAfterExit=yes让 systemd 认为服务仍然处于活跃状态,不会执行完就标记成死掉。这个细节很重要,少了它服务状态显示会不正确。
5.2 日志采集与轮转
日志管理是很多人忽略的部分,直到磁盘满了服务莫名其妙挂掉才想起来。Docker 容器默认的日志驱动是 json-file,如果不限制大小,日积月累会占用不少磁盘空间。
我的做法是修改 Docker 守护进程配置,在/etc/docker/daemon.json里加上日志轮转:
{ "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "3" } }改完重启 Docker:
sudo systemctl restart docker这个配置的意思是每个容器产生的日志文件单个最大 50M,保留 3 个,超出部分自动清理。对于 Harness 这种记录请求日志的服务,50M 足够保留好几天的日志,方便排查问题,又不会把磁盘撑爆。
注意,daemon.json 的修改只对之后创建的容器生效,已有的容器还是按旧配置运行。如果你是在容器已经跑起来之后才加的配置,需要重建容器才能应用。
日志采集还可以接日志平台,但小项目没必要。journalctl和 Docker 日志配合已经足够日常排查,真需要回溯某段时间的请求,直接在日志文件里 grep 就行。
6. 公网鉴权访问的完整验证链路
6.1 客户端调用示例
全部部署完成后,用客户端的视角完整走一遍流程,确认公网链路是真的通了,而且鉴权确实生效。
先获取 JWT。不同 Harness 版本签发 Token 的方式略有差异,有的提供/auth/token接口,有的需要用管理工具生成,这里以提供接口的方式为例:
curl -X POST https://api.你的域名.com/auth/token \ -H "Content-Type: application/json" \ -d '{"api_key":"sk-harness-internal-xxxx"}'拿到 Token 后,调用对话接口:
curl https://api.你的域名.com/v1/chat/completions \ -H "Authorization: Bearer 得到的Token" \ -H "Content-Type: application/json" \ -d '{ "model": "deepseek-chat", "messages": [ {"role": "system", "content": "你是一个有用的助手"}, {"role": "user", "content": "你好,请介绍一下自己"} ] }'如果配置没问题,几秒钟之后就能收到模型返回的 JSON 响应。这个调用看似简单,背后其实经过了完整的鉴权、限流、转发、上游请求、返回映射全链路。
走公网调用和本地调用的区别在于证书验证。第一次用 curl 请求时加个-v参数,能看到 TLS 握手过程,确认证书有效、域名匹配。如果证书验证不通过,curl 会直接报错,这时候要先查证书配置,不要盲目怀疑 Harness。
6.2 安全与限流效果验证
验证通过不代表安全配置就到位了,还要主动测试几个异常场景。
第一个是不带 Token 请求。应该收到 401 响应,提示缺少身份凭证。如果收到了 200,说明鉴权根本没用,要立刻回去检查配置。
第二个是带无效 Token。随便改一个字符再请求,同样应该返回 401。JWT 有签名校验,篡改过的 Token 不可能通过验证,这个天然的安全特性正是它比普通字符串 Key 强的地方。
第三个是限流生效。我用一个循环脚本快速发送请求,超过配置的 QPS 后应该收到 429 响应,提示请求过于频繁:
for i in $(seq 1 20); do curl -s -o /dev/null -w "%{http_code}\n" \ https://api.你的域名.com/v1/chat/completions \ -H "Authorization: Bearer 有效Token" \ -H "Content-Type: application/json" \ -d '{"model":"deepseek-chat","messages":[{"role":"user","content":"测试"}]}' done观察返回状态码分布,如果正常应该是前几个请求 200,后面开始出现 429。这里有个小细节:限流统计的是 Harness 层面的请求,不是 Nginx 层面的,所以即使用不同的 IP 来请求,依然会被计数器挡下来。这个设计保证了即使别人拿到了 Token,也无法无限刷你的额度。
7. 常见问题与排查技巧实录
7.1 401 鉴权失败怎么查
公网访问时遇到 401 是最常见的问题,但 401 本身只是结果,原因可能出在好几个地方。
先查 Token 是否过期。JWT 里包含过期时间,过期后生成新 Token 即可,不用动任何服务配置。再查 Harness 的 JWT 密钥是否被改过。如果改过配置里 secret,旧 Token 全部失效,这是正常的,不是故障。
还有一种情况是调用方用了 API Key 方式,但把 Key 拼错了位置。API Key 一般放在Authorization头里,格式通常是Authorization: ApiKey sk-harness-xxx。不同版本 Harness 要求的格式可能不同,要仔细看日志里记录的真实报错信息,不要只根据客户端返回结果猜。
查看 Harness 日志有个技巧:先grep 401找到失败请求日志,然后看失败原因字段。日志里通常会写明是 unauthorized token 还是 invalid api_key,定位方向一下子就清楚了。
7.2 502 Bad Gateway 的排查路径
502 Bad Gateway 说明 Nginx 把请求转发到了 Harness,但 Harness 没有正确返回响应。最常见的三个原因分别是容器没起来、端口没对上、上游模型服务超时。
先用docker compose ps看容器状态,如果服务没启动,看日志找原因。如果容器正常运行,就要检查 Nginx 的proxy_pass地址和端口是否与容器映射端口一致。这里有一个很经典的错误:Harness 容器映射到宿主机的端口被错误写成了 8081,但 Nginx 还指到 8080,结果必是 502。
还有一个隐蔽的原因是上游请求超时。模型 API 响应时间长于 Nginx 的proxy_read_timeout,连接会被切断。这种情况日志里会有上游超时记录,不是容器问题,调整超时时间就行。
排查 502 的通用方法是逐层试探:先curl 127.0.0.1:8080/healthz看本地服务是否正常,再curl -v走一遍公网域名看在哪一层断的。每多走一层验证一步,很快就能定位。
7.3 内存不足与 OOM 处理
服务器内存不够的表现常见有两种:服务进程被杀掉,或者系统整体变卡。dmesg | tail -n 20如果看到 OOM 字样,就说明杀进程的是系统内存管理器。
小内存跑多个容器确实容易遇到这个问题,尤其是同时开了 Redis 和 Harness,还挂着 Nginx。解决方案优先是加内存,毕竟 4G 内存的机器也不贵。暂时不能加的话,可以给系统增加一点 Swap 空间应急:
sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfileSwap 只适合应急,不适合长期跑。Harness 这类 IO 密集型的服务如果频繁使用 Swap,响应延迟会明显上升。根本解法还是把内存规格提上去,或者把不用的容器清理掉。
日常监控可以用docker stats查看每个容器的资源占用,发现内存持续上涨再处理。我在实际使用中还会挂一个简单的资源监控脚本,超过阈值就通知自己,避免服务悄悄挂掉还不知道。
7.4 排查速查表
实践下来很多问题有固定的判断路径,整理一个速查表方便对照:
| 现象 | 可能原因 | 快速排查动作 |
|---|---|---|
| 401 Unauthorized | Token 过期或非法 | 重新生成 JWT,检查 API Key 格式 |
| 403 Forbidden | 请求来源被拒绝 | 查看 Nginx 访问日志,检查 IP 策略 |
| 429 Too Many Requests | 触发限流 | 检查 rate_limit 配置,等待计数重置 |
| 502 Bad Gateway | 容器未运行或端口错误 | 检查容器状态,核对 proxy_pass 端口 |
| 504 Gateway Timeout | 上游响应超时 | 调大 proxy_read_timeout,检查模型服务 |
| 连接被拒绝 | 防火墙未放行或服务未监听 | 检查 ufw 规则,确认监听地址 |
| 证书报错 | 证书过期或域名不匹配 | 检查证书有效期,确认 ssl_certificate 路径 |
| 日志占满磁盘 | 未配置日志轮转 | 配置 daemon.json 日志限制,清理日志 |
最后说一点个人体会。整套部署流程看起来环节不少,但真正花时间的地方不在命令本身,而在每一个“想当然”的细节。比如端口是监听 0.0.0.0 还是 127.0.0.1,Nginx 的 localhost 会不会解析成 IPv6,证书续期任务有没有真正配上,这些平时不起眼的地方,每个都能让人折腾好一阵。建议第一次部署时每一步都验证完再进入下一步,本地全部跑通再挂公网,能少走很多回头路。这套架构跑稳之后,后续加用户、改限流、换模型都只改配置就行,底子打得对,扩展就很省心。