刚拿到一台干净的 Debian 13.7 云服务器,第一件事不是部署应用,而是把"门"关好。09 §2 已讲过基础 SSH 登录,本节给出生产化最小配置全集:一套做完,这台机器才配跑业务。
ssh-ed25519 AAAA...。下面所有命令默认以刚创建的普通用户(如 deploy)+ sudo 执行,不再用 root 直接登录。先确认本地公钥已写入服务器 ~/.ssh/authorized_keys 且能免密登录,再改配置——否则会把自己锁在门外。
# 1) 本地(Windows PowerShell)测试免密登录,成功后再继续 ssh deploy@服务器IP # 2) 服务器上编辑 SSH 配置 sudo nano /etc/ssh/sshd_config # 确保以下三行(取消注释、改值): PubkeyAuthentication yes PasswordAuthentication no PermitRootLogin no # 3) 校验语法并重载(不是重启,避免踢掉当前连接) sudo sshd -t sudo systemctl reload ssh # 4) 另开一个新终端测试免密登录成功后,再关掉旧终端
铁律:改 sshd_config 后永远不要关掉当前已登录的终端,新开一个终端验证能登录,再退旧的。这是运维第一课,能救你无数次被锁门外。
sudo apt update && sudo apt install -y ufw # 默认:拒绝入站、放行出站 sudo ufw default deny incoming sudo ufw default allow outgoing # 只放行:SSH(22)、HTTP(80)、HTTPS(443) sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable # 会提示 y/n,输入 y sudo ufw status verbose
数据库端口(3306/5432/6379)一律不对公网开放——它们只监听 127.0.0.1,应用本机直连(见 §4)。
sudo apt install -y fail2ban # 写本地覆盖配置(不动官方 jail.conf) sudo tee /etc/fail2ban/jail.d/sshd.local >/dev/null <<'EOF' [sshd] enabled = true port = 22 maxretry = 5 bantime = 1h findtime = 10m EOF sudo systemctl enable --now fail2ban sudo fail2ban-client status sshd # 查看已封的 IP
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades # 选"是",安全补丁自动装
这一行做完,安全类 apt 补丁会在后台自动安装并按需重启服务,不用你手动追 CVE。
1GB 内存的机器,一次 apt 编译或数据库突发就可能 OOM。加 2G swap 当"最后一道缓冲",不是替代内存。
sudo fallocate -l 2G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # 开机自动挂载 echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab # 降低 swap 倾向(默认 60 太激进,1GB 机器设 10) echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf sudo sysctl -p free -h # 确认 Swap 行出现 2.0Gi
工作台在 11 模块是一个 Go 后端(Gin v1.12 + GORM v1.31)。部署到 Linux 的关键:在 Windows 本地交叉编译出一个纯静态 Linux 二进制,scp 上传,用 systemd 托管。服务器上不需要装 Go、不需要 gcc——这就是 CGO_ENABLED=0 + 纯 Go 驱动的红利。
工作台 SQLite 用的是 glebarez/sqlite(底层 modernc.org/sqlite,纯 Go 实现),所以整个二进制零 CGO 依赖,可完美交叉编译。MySQL/PG 同理用 go-sql-driver/mysql、pgx(均为纯 Go)。
# 本地 Windows PowerShell,进入工作台项目根目录(含 go.mod) cd D:\your\workbench # 交叉编译:目标 linux/amd64,禁用 CGO,strip 掉调试信息减小体积 $env:CGO_ENABLED=0 $env:GOOS="linux" $env:GOARCH="amd64" go build -ldflags "-s -w" -o workbench ./cmd/server # 产物是单个文件 workbench(约 8–15MB),无任何动态库依赖 dir workbench
GOARCH 改成 arm64 即可,其余步骤完全一样。判断方法:服务器上跑 uname -m,输出 x86_64 用 amd64,输出 aarch64 用 arm64。# 本地 PowerShell:上传二进制到服务器临时目录 scp .\workbench deploy@服务器IP:/tmp/workbench # 服务器上建立生产目录约定 sudo mkdir -p /opt/workbench # 程序本体 sudo mkdir -p /var/log/workbench # 日志(本方案日志走 journald,此目录留给文件型日志可选) sudo mkdir -p /etc/workbench # 配置文件(.env) sudo mkdir -p /var/lib/workbench # 数据文件(SQLite db、上传文件) # 移动并赋权:程序归 root,运行用户用专用系统用户 app sudo useradd -r -s /usr/sbin/nologin app || true sudo mv /tmp/workbench /opt/workbench/workbench sudo chmod +x /opt/workbench/workbench sudo chown -R app:app /opt/workbench /var/log/workbench /var/lib/workbench
| 路径 | 放什么 | 说明 |
|---|---|---|
/opt/workbench/ | 二进制本体 | 程序文件。新版本上传为 workbench.v2,再原子替换,便于回滚(见 §2.5) |
/etc/workbench/.env | 配置(密钥/DB DSN) | 权限 600、属 app。绝不进 git、不打进二进制 |
/var/lib/workbench/ | SQLite 数据库文件、上传附件 | 数据与程序分离,备份只盯这个目录 |
/etc/systemd/system/workbench.service | systemd unit | 服务定义文件,见下 |
| journald | 运行日志 | journalctl -u workbench,无需自建 logrotate(见 §2.6) |
sudo tee /etc/systemd/system/workbench.service >/dev/null <<'EOF' [Unit] Description=Workbench Go Service (11 module) After=network.target [Service] Type=simple User=app Group=app WorkingDirectory=/opt/workbench # 通过 EnvironmentFile 注入配置(.env 里写 DB_DSN、JWT_SECRET、PORT 等) EnvironmentFile=/etc/workbench/.env ExecStart=/opt/workbench/workbench # 崩溃自动拉起 Restart=on-failure RestartSec=3 # 优雅停机:给 Go 的 context 取消最多 10 秒收尾时间 TimeoutStopSec=10 KillSignal=SIGTERM # 日志直接进 journald StandardOutput=journal StandardError=journal SyslogIdentifier=workbench # 资源与文件描述符兜底 LimitNOFILE=65535 # ---- 安全加固(可选但推荐,逐行取消注释即可)---- NoNewPrivileges=true PrivateTmp=true ProtectSystem=full ProtectHome=true # ReadWritePaths=/var/lib/workbench # 若开了 ProtectSystem=strict 才需要显式放行数据目录 [Install] WantedBy=multi-user.target EOF
sudo tee /etc/workbench/.env >/dev/null <<'EOF' PORT=8080 GIN_MODE=release DB_DSN=/var/lib/workbench/workbench.db JWT_SECRET=换成一串足够长的随机字符串 EOF sudo chmod 600 /etc/workbench/.env sudo chown app:app /etc/workbench/.env
sudo systemctl daemon-reload # 改了 unit 文件后必须执行 sudo systemctl enable --now workbench # 启动 + 开机自启(关键!对应验收"重启自启") sudo systemctl status workbench # 看运行状态、最近日志 sudo systemctl restart workbench # 改了配置/换了二进制后重启 sudo systemctl stop workbench # 停止 journalctl -u workbench -f # 实时跟踪日志(最重要的排查入口) journalctl -u workbench --since "10 min ago" # 看最近 10 分钟
11 模块的工作台已实现 signal.NotifyContext(ctx, SIGINT, SIGTERM)(见 10 模块并发章与 11 模块优雅停机小节)。验证:
# 1) 启动服务,确认监听 sudo systemctl restart workbench curl http://127.0.0.1:8080/healthz # 应返回 ok # 2) 触发停止,观察日志里是否打印"正在关闭..."、连接排空后才退出 sudo systemctl stop workbench journalctl -u workbench --since "1 min ago" # 3) 回滚:新版本先传为 v2,验证失败就切回旧版(原子替换) sudo systemctl stop workbench sudo cp /opt/workbench/workbench /opt/workbench/workbench.v1.bak # (上传新二进制到 /tmp/workbench.v2 后) sudo cp /tmp/workbench.v2 /opt/workbench/workbench sudo chmod +x /opt/workbench/workbench sudo chown app:app /opt/workbench/workbench sudo systemctl start workbench curl http://127.0.0.1:8080/healthz # 失败则:sudo cp /opt/workbench/workbench.v1.bak /opt/workbench/workbench && sudo systemctl start workbench
本方案用 journald(systemd 自带),不需要额外配置 logrotate——journald 默认按大小/时间自动轮转并压缩。一行调整即可:
# 限制 journal 总上限(1GB 机器别让日志吃光磁盘) sudo journalctl --vacuum-size=200M # 永久生效:编辑 /etc/systemd/journald.conf,设 SystemMaxUse=200M,再 sudo systemctl restart systemd-journald
结论:程序日志 = journald(journalctl -u workbench -f);访问日志也由 Gin 输出到 stdout 进 journald。1GB 机器不要再叠一套文件日志 + logrotate,那是重复造轮子。
Go 服务只监听 127.0.0.1:8080(不对公网),前面挂一个反向代理对外开 80/443,负责:静态文件分发、HTTPS 终止、把 /api 转发给后端。下面给 Nginx stable 1.30.5 与 Caddy v2.11.4 两套完整配置,二选一。
sudo apt install -y nginx
# Debian 13.7 apt 源里的 nginx 为 stable 线;版本核对:nginx -v
server {
listen 80;
server_name your-domain.com; # 换成你的域名(需先解析到本机 IP)
root /var/www/workbench; # Vue3 前端打包产物(12 模块 pnpm build 输出 dist)
index index.html;
# gzip 压缩
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_min_length 1k;
# 后端 API 反代
location /api/ {
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;
}
# 健康检查直接打后端
location /healthz {
proxy_pass http://127.0.0.1:8080;
}
# 静态资源缓存(Vue 打包带 hash 的 js/css)
location ~* \.(?:js|css|png|jpg|svg|woff2?)$ {
expires 7d;
add_header Cache-Control "public";
}
# 前端单页应用回退到 index.html
location / {
try_files $uri $uri/ /index.html;
}
}
# 启用站点 + 放前端产物 + 校验重载 sudo ln -s /etc/nginx/sites-available/workbench /etc/nginx/sites-enabled/ sudo rm -f /etc/nginx/sites-enabled/default sudo mkdir -p /var/www/workbench # 本地把 12 模块 pnpm build 的 dist/* 用 scp 传到 /var/www/workbench sudo nginx -t && sudo systemctl reload nginx
sudo apt install -y certbot python3-certbot-nginx # 自动改 nginx 配置、申请并部署 Let's Encrypt 证书、配自动续期 sudo certbot --nginx -d your-domain.com sudo systemctl status certbot.timer # 确认自动续期定时器在跑 sudo certbot renew --dry-run # 测试续期是否正常
# 官方安装脚本(Debian 13.7) sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list sudo apt update && sudo apt install -y caddy caddy version # 核对 v2.11.4
your-domain.com {
# 一句自动 HTTPS:Caddy 自动申请、自动续期 Let's Encrypt 证书
# 前端静态文件
root * /var/www/workbench
encode gzip
file_server
# API 反代到 Go 后端
handle /api/* {
reverse_proxy 127.0.0.1:8080
}
# 健康检查
handle /healthz {
reverse_proxy 127.0.0.1:8080
}
# SPA 回退
try_files {path} /index.html
}
sudo systemctl reload caddy # 改完 Caddyfile 后重载;证书全自动
| 维度 | Nginx stable 1.30.5 | Caddy v2.11.4 |
|---|---|---|
| 配置复杂度 | 较长,location/proxy_pass/gzip/cache 逐条写 | 极短,核心反代一行 reverse_proxy |
| HTTPS | 需 certbot 配合,多一步 | 内置自动申请 + 自动续期,零配置 |
| 生态/文档 | 最广,全网资料、招聘、运维惯例都是它 | 较新,资料少但官方文档清晰 |
| 内存占用 | 轻量(常驻约 5–15MB) | 轻量(Go 单二进制,约 10–20MB) |
| 1GB 机器 | 都够用,任选其一,不要同时装两个抢 80/443 | 同左 |
| 建议 | 要走职场、要跟主流运维接轨 → 选它 | 只想最少配置跑起来、域名已解析 → 选它 |
选择建议:学习/求职主线选 Nginx(生态全、面试常问);纯个人小站想省心选 Caddy(一句 auto HTTPS)。二选一,别两个都装——它们都抢 80/443 端口,必出 §7 的 "Port 80 in use"。
四数据库的完整安装、SQL、Go 集成、低配调优分别在 16(MySQL)/ 17(PostgreSQL)/ 18(Redis)/ 19(SQLite3)模块各自的"低配调优"小节。本节不重复长篇,只给 1GB 服务器部署时的统一原则 + 每条关键配置 + systemd 下的注意点。
| 数据库 | 版本 | 1GB 机器关键配置(1 条) | 完整内容见 |
|---|---|---|---|
| MySQL | 8.4.12 LTS | innodb_buffer_pool_size = 128M(默认 128M 即可,1GB 机器不要再调大;max_connections=50) | 见本系列 16 MySQL 专题的"低配部署/调优"小节 |
| PostgreSQL | 18.6 | shared_buffers = 128MB、work_mem = 4MB(1GB 机器按物理内存 1/8 估 shared_buffers) | 见本系列 17 PostgreSQL 专题的"低配部署/调优"小节 |
| Redis | 8.10.2 | maxmemory = 128mb + maxmemory-policy = allkeys-lru(防缓存吃光内存被 OOM) | 见本系列 18 Redis 专题的"持久化/低配"小节 |
三者均通过发行版 apt 原生安装(sudo apt install mysql-server / postgresql / redis-server),自动注册为 systemd 服务并开机自启,不需要 Docker。安装后的初始账号、建库、建用户、Go DSN 写法,分别见 16/17/18 模块,本页不再展开。
工作台 11 模块默认就是 SQLite(glebarez/sqlite 纯 Go 驱动,见 19 模块)。它没有独立服务,就是一个 .db 文件,跟着 Go 进程走。systemd 下只有两个注意点:
# 1) db 文件放在数据目录,属主是运行用户 app(不是 root) sudo chown -R app:app /var/lib/workbench # .env 里 DB_DSN=/var/lib/workbench/workbench.db # 2) 务必开启 WAL 模式(19 模块详述),提升并发读、减少锁等待 # 在应用首次连接后执行一次(或迁移里开启): # PRAGMA journal_mode=WAL;
sudo chown -R app:app /var/lib/workbench。"没演练过恢复的备份等于没有备份"。本节给一套完整可复制的备份脚本:覆盖 SQLite(VACUUM INTO)、MySQL(mysqldump)、PostgreSQL(pg_dump)、Redis(RDB 拷贝),自动保留最近 N 份、写日志,挂到 cron 每天跑一次。
/var/backups/workbench/ ├── sqlite/ # SQLite 备份(按日期 .db) ├── mysql/ # MySQL dump(.sql.gz) ├── pgsql/ # PostgreSQL dump(.dump.gz) ├── redis/ # Redis RDB 拷贝 └── backup.log # 每次备份的日志
sudo mkdir -p /var/backups/workbench/{sqlite,mysql,pgsql,redis}
sudo chown -R app:app /var/backups/workbench # 让备份脚本能写
sudo tee /usr/local/bin/workbench-backup.sh >/dev/null <<'EOF' #!/usr/bin/env bash # 工作台备份脚本:覆盖 SQLite/MySQL/PG/Redis,保留最近 7 份 set -euo pipefail DEST=/var/backups/workbench DATE=$(date +%Y%m%d-%H%M%S) KEEP=7 # 每类保留最近 7 份 LOG=$DEST/backup.log log(){ echo "[$(date '+%F %T')] $*" >> "$LOG"; } log "==== 备份开始 ====" # ---- 1) SQLite:用 VACUUM INTO 得到一致性快照(不锁库)---- if [ -f /var/lib/workbench/workbench.db ]; then sqlite3 /var/lib/workbench/workbench.db "VACUUM INTO '$DEST/sqlite/workbench-$DATE.db'" log "SQLite ok -> sqlite/workbench-$DATE.db" fi # ---- 2) MySQL:mysqldump(按需取消注释,填库名/账号)---- # if command -v mysqldump >/dev/null; then # mysqldump -u root --single-transaction workbench \ # | gzip > "$DEST/mysql/workbench-$DATE.sql.gz" # log "MySQL ok -> mysql/workbench-$DATE.sql.gz" # fi # ---- 3) PostgreSQL:pg_dump(按需取消注释)---- # if command -v pg_dump >/dev/null; then # sudo -u postgres pg_dump workbench | gzip > "$DEST/pgsql/workbench-$DATE.dump.gz" # log "PostgreSQL ok -> pgsql/workbench-$DATE.dump.gz" # fi # ---- 4) Redis:拷贝 RDB 文件(缓存可重建,这里仅留个快照)---- if [ -f /var/lib/redis/dump.rdb ]; then cp /var/lib/redis/dump.rdb "$DEST/redis/dump-$DATE.rdb" log "Redis ok -> redis/dump-$DATE.rdb" fi # ---- 5) 每类只保留最近 KEEP 份,删除更老的 ---- for dir in sqlite mysql pgsql redis; do ls -1t "$DEST/$dir"/* 2>/dev/null | tail -n +$((KEEP+1)) | xargs -r rm -f done log "==== 备份完成,磁盘占用:$(du -sh $DEST | cut -f1) ====" EOF sudo chmod +x /usr/local/bin/workbench-backup.sh # SQLite 备份需要 sqlite3 客户端:sudo apt install -y sqlite3
sudo crontab -e # 加入这一行:每天 03:17 执行(避开整点高峰) 17 3 * * * /usr/local/bin/workbench-backup.sh # 手动跑一次验证(这是必做动作,不要等 cron) sudo /usr/local/bin/workbench-backup.sh cat /var/backups/workbench/backup.log ls -lh /var/backups/workbench/sqlite/
本机备份只能防"误删/程序坏",防不了"整台机器被服务商回收/磁盘损坏"。加一步异地:用 rclone 或 rsync 把 /var/backups/workbench/ 每天同步一份到对象存储(如 S3 兼容存储)或另一台机器。在备份脚本末尾加一行 rclone copy /var/backups/workbench/ remote:workbench-backup 即可,rclone 的配置见其官方入门(本系列不展开,一句带过)。
ls -t /var/backups/workbench/sqlite/ | head -1。sudo systemctl stop workbench。sudo cp /var/backups/workbench/sqlite/workbench-XXXX.db /var/lib/workbench/workbench.db && sudo chown app:app /var/lib/workbench/workbench.db。gunzip -c workbench-XXXX.sql.gz | mysql workbench / gunzip -c workbench-XXXX.dump.gz | sudo -u postgres psql workbench。sudo systemctl start workbench,curl http://127.0.0.1:8080/healthz,登录工作台看数据是否是备份时点的状态。验收硬指标:你能在 15 分钟内从一份 SQLite 备份把数据完整恢复回来。做不到这一条,§8 的"恢复演练成功"就不通过。
1GB 机器不装 Prometheus + Grafana 全家桶(光这俩常驻就吃两三百 MB)。本节给"系统自带 + 一个 cron 脚本"的轻量方案,足够个人工作台用。
| 层 | 做法 | 命令/配置 |
|---|---|---|
| ① 进程存活 | systemd 自动拉起(Restart=on-failure 已配) | systemctl status workbench |
| ② 健康检查 | cron 每分钟 curl /healthz,失败记日志 | 见下脚本 |
| ③ 日志排查 | 出问题先 journalctl | journalctl -u workbench -f |
| ④ 内存告警 | cron 定时 free 阈值判断,超阈值写告警日志 | 见下一句话 |
sudo tee /usr/local/bin/workbench-healthcheck.sh >/dev/null <<'EOF' #!/usr/bin/env bash # 每分钟探活 /healthz;失败则记录并尝试重启服务 URL="http://127.0.0.1:8080/healthz" LOG="/var/log/workbench/health.log" if ! curl -sf --max-time 5 "$URL" >/dev/null; then echo "[$(date '+%F %T')] HEALTH CHECK FAILED, restarting..." >> "$LOG" systemctl restart workbench fi EOF sudo chmod +x /usr/local/bin/workbench-healthcheck.sh # 挂 cron:每分钟跑一次 sudo crontab -e # * * * * * /usr/local/bin/workbench-healthcheck.sh
# 可用内存低于 100MB 就写一行告警日志(1GB 机器红线约 100MB) # crontab 里加: 0 * * * * [ "$(free -m | awk '/Mem:/{print $7}')" -lt 100 ] && echo "[$(date)] LOW MEM $(free -m | grep Mem)" >> /var/log/workbench/memwarn.log
卡住了先查这张表。左列是你会在屏幕上看到的原文,中列是原因,右列是照做的解法。
| 报错原文 / 现象 | 原因 | 解决 |
|---|---|---|
permission denied(启动二进制 / 打开 db) | 二进制没 +x,或数据目录/db 文件属主是 root 而非 app | sudo chmod +x /opt/workbench/workbench;sudo chown -R app:app /var/lib/workbench /opt/workbench |
bind: address already in use / Port 80 in use | 80/443/8080 被别的进程占了(Nginx 与 Caddy 同时装、旧进程没退) | sudo ss -ltnp | grep ':80' 找到占用进程;二选一保留,sudo systemctl stop/disable 另一个 |
certbot 验证失败 Challenge failed | 域名没解析到本机 IP;或 ufw 没放行 80;或 80 被别的服务占了 | 先 dig your-domain.com 确认 A 记录指向本机 IP;sudo ufw allow 80;确保 80 端口是 nginx/caddy 在听 |
Failed to start workbench.service: Unit not found. | unit 文件放错目录,或没 daemon-reload | 确认文件在 /etc/systemd/system/workbench.service;执行 sudo systemctl daemon-reload |
ExecStart: permission denied(二进制不可执行) | 上传的二进制丢了执行权限,或文件系统挂了 noexec | sudo chmod +x;确认 /opt 没挂 noexec(mount | grep opt) |
main.go: out of memory / dmesg 里 Out of memory: Killed process | 1GB 内存被吃光,内核 OOM Killer 杀进程 | 确认 §1.5 的 2G swap 已加;按 §4 压低 MySQL/PG/Redis 内存上限;用 journalctl -k | grep -i oom 定位被杀的是谁 |
journalctl -u workbench 日志为空 | 程序日志没输出到 stdout/stderr,或 unit 里没配 StandardOutput | 确认 unit 有 StandardOutput=journal;确认 Go 程序日志打到 stdout(不要只写文件);sudo systemctl restart workbench 后再看 |
502 Bad Gateway(Nginx/Caddy) | 反代后端 127.0.0.1:8080 没在跑或崩了 | systemctl status workbench 看后端是否 active;curl 127.0.0.1:8080/healthz 直连测试;看 journalctl 后端日志 |
404 / API 全 404(proxy_pass 路径错误) | Nginx proxy_pass http://127.0.0.1:8080; 带不带尾部斜杠,导致 /api/ 被错误改写路径 | 后端路由若就是 /api/xxx,用 proxy_pass http://127.0.0.1:8080;(不带 URI 部分、路径原样转发);改完 nginx -t && reload |
too many open files(文件描述符用尽) | systemd 默认 LimitNOFILE 偏小,高并发打满 | unit 里加 LimitNOFILE=65535,daemon-reload 后重启服务 |
| 日志/时间差 8 小时,日志时间戳不对 | 服务器时区是 UTC,没设成 Asia/Shanghai | sudo timedatectl set-timezone Asia/Shanghai,重启服务 |
| 备份脚本 cron 里不执行,但手动跑正常 | cron 环境变量极简(PATH 不含自定义路径)、或脚本里用了相对路径/交互式命令 | 脚本内一律用绝对路径;set -euo pipefail;先 sudo bash -x /usr/local/bin/workbench-backup.sh 看哪行挂;检查 backup.log 有无报错 |
| 改完配置不生效(systemctl restart 后行为依旧) | 改了 .env 但没重启进程,或改了 unit 没 daemon-reload | 改 .env → systemctl restart;改 unit 文件 → 必须先 daemon-reload 再 restart |
| SSH 突然连不上(改 sshd 后被锁门外) | sshd_config 改错了(如 PubkeyAuthentication no) | 这就是 §1.1 强调"别关旧终端"的原因;用云服务商控制台的 VNC/网页终端登录回滚配置,sudo sshd -t 校验后再 reload |
这是本模块的结业标准,也是 08 总纲 M1"工作台跑通"与阶段二"服务器生产化部署"在部署侧的最终落地。逐项打勾,全部通过才算完成。
| # | 验收项(怎么测) | 对应章节 |
|---|---|---|
| 1 | 服务器重启后工作台自启:sudo reboot,重连后 systemctl is-active workbench 返回 active,且 curl 127.0.0.1:8080/healthz 返回 ok | §2.4(enable --now) |
| 2 | HTTPS 可访问:浏览器打开 https://your-domain.com,证书有效(小绿锁),登录 + 增删改查正常,/api 反代通 | §3(Nginx 或 Caddy) |
| 3 | 手动触发一次备份与恢复演练成功:跑 workbench-backup.sh 生成备份;按 §5.5 停服务→恢复→启动,数据回到备份时点 | §5.3 / §5.5 |
| 4 | /healthz 监控脚本有输出:/var/log/workbench/health.log 存在;故意停掉后端,一分钟内脚本记录 FAILED 并自动重启 | §6.1 |
| 5 | 安全地基到位:密码登录与 root 直登被拒;ufw 只放行 22/80/443;fail2ban 运行;2G swap 生效 | §1 |
| 6 | 回滚可用:换一版二进制,验证失败能在一分钟内切回旧版正常运行 | §2.5 |
| 7 | 内存健康:free -h 下,工作台 + 数据库 + 代理常驻后,可用内存仍有富余,无 OOM 记录 | §1.5 / §4 |
全系列到此闭环:08 给路线 → 09 给环境与版本 → 10/11 把工作台写出来 → 12–19 把前端与四个数据库补齐 → 20 把这一切搬上一台 1GB 真实服务器、做成生产形态。7 项验收全过,你就走完了 08 总纲里"M1 + 阶段二"的全部部署环节,可以向阶段三的企业级模式进发。
完整版本总表见 09 环境搭建与版本清单。本模块涉及的服务器侧组件版本如下,均来自全系列 versions.md(2026-10-14 核验)。
| 组件 | 锁定版本 | 来源 |
|---|---|---|
| Debian(服务器首选) | 13.7 "trixie" stable | https://www.debian.org/releases/ |
| Ubuntu(备选) | 26.04.1 LTS(官方要求 1.5GB 内存起,1GB 不推荐) | https://ubuntu.com/download/server |
| Nginx(生产锁 stable) | stable 1.30.5 | https://nginx.org/en/download.html |
| Caddy | v2.11.4 | https://github.com/caddyserver/caddy/releases |
| systemd | 随发行版 | —(不用 supervisor/docker) |
| Go(交叉编译工具链) | 1.27.2(1.27.x 线) | https://go.dev/dl/ |
| MySQL / PostgreSQL / Redis | 8.4.12 LTS / 18.6 / 8.10.2 | 见 16/17/18 模块来源表 |
| SQLite(纯 Go 驱动 modernc.org/sqlite) | 3.53.4 / modernc v1.60.1 | 见 19 模块来源表 |
| Docker | 不用于 1GB 服务器(仅本机开发) | 09 §3 部署心智 |
| 本模块与全系列的路线/部署心智五原则/里程碑口径来源:08 闭关总纲、07 编程主线决策报告 | file:///D:/02-Docs/金融知识/08-闭关总纲.html · 07-编程主线决策报告.html |
| 工作台单 Go 二进制、CGO_ENABLED=0 交叉编译、纯 Go SQLite 驱动(glebarez/modernc) | 见本系列 11 工作台实战 · https://pkg.go.dev/github.com/glebarez/sqlite |
| Debian 13.7 trixie stable(2026-09-12 更新) | https://www.debian.org/releases/ |
| Nginx stable 1.30.5(2026-09-15) | https://nginx.org/en/download.html |
| Caddy v2.11.4(2026-06-03)与自动 HTTPS 机制 | https://github.com/caddyserver/caddy/releases |
| systemd unit 字段(Restart/EnvironmentFile/StandardOutput/ProtectSystem 等) | https://www.freedesktop.org/software/systemd/man/systemd.service.html |
| Let's Encrypt / certbot 自动证书与续期 | https://certbot.eff.org/ |
| MySQL 8.4 / PG 18 / Redis 8 / SQLite 3.53 低配调优具体参数 | 见本系列 16/17/18/19 模块对应小节 |
(编者整理:本页的目录约定、备份脚本、监控脚本、报错对照表、验收清单为基于上述官方文档与 08/11 模块契约的运维实践设计,无外部独立来源。)