跳至内容
liuzhen932 的小窝
返回

[备忘] 运维参考手册

本文是个人服务运维的唯一参考源,不是教程。所有记录以此刻实际情况为准,每次改基础设施必须同步更新这里,否则下次出事你就等着对着半年前的配置挠头吧。

0x00 操作系统与初始配置

操作系统标准

新机器统一装服务商提供的最新 Debian 稳定版。现在能买到的机器,要么 Debian 13,要么 Debian 12。优先级:

  1. Debian 13(首选,能用新的就别用旧的)
  2. Debian 12(备选,部分服务商模板更新慢)
  3. 连 Debian 12 都提供不了的厂商直接拉黑

装机后必做清单

拿新机器第一步:

# 1. 主机名 & hosts
hostnamectl hostname <name>
echo "127.0.1.1 <fqdn> <hostname>" >> /etc/hosts

# 2. 时区 & NTP
timedatectl set-timezone Asia/Shanghai
timedatectl set-ntp true

# 3. 基础包
apt-get update && apt-get install -y curl wget git vim sudo htop net-tools \
  dnsutils ca-certificates

# 4. SSH 加固

# 5. 防火墙
ufw default deny incoming
ufw default allow outgoing
ufw limit 55822/tcp
ufw enable

# 6. 换源
# 见 0x0C 章节

# 7. BBR
sysctl net.ipv4.tcp_congestion_control

# 8. 装 Docker
# 见 0x06 章节
# 部分机器可跳过

这 8 步做完机器才算达到上线标准。缺任何一步都不要往上面跑服务。

Homelab 物理服务器

配置如下:

项目规格
CPUIntel Xeon E5 系列,48 核心
内存128 GB DDR3
存储盘 12 TB SSD,无 RAID
存储盘 22 TB HDD,无 RAID
虚拟化平台Proxmox VE 8,单节点集群
客户端类型KVM 主力 + LXC 跑基础设施
LXC 备份每日双异地备份
KVM 备份目前裸奔 —— 需要补上 restic 备份
PBS未部署(计划中)
公网接入IPv6 公网 + IPv4 NAT1
外部桥接vmbr0(直通公网)
内网通信vnet1
功耗~180 W
运行时间>2 年,稳如老狗

SSH 配置

文件:/etc/ssh/sshd_config

参数备注
Port55822非标端口是第一道防线,防脚本小子的
PermitRootLoginprohibit-passwordRoot 允许密钥登录,禁用密码
PubkeyAuthenticationyes
PasswordAuthenticationno密码登录一律关掉,别犹豫
UsePAMyes部分服务需要
AuthenticationMethodspublickey只认公钥

密钥路径:~/.ssh/authorized_keys

防火墙规则

管理工具:ufw(有在想迁到 nftables,但目前 ufw 除了难用和傻逼没出过问题,所以优先级不高)

方向端口协议描述
入站55822TCP0.0.0.0/0, ::/0SSH 远程管理
入站80TCP0.0.0.0/0, ::/0HTTP(重定向用)
入站443TCP0.0.0.0/0, ::/0HTTPS
入站179TCP对等 IP 段BGP(Bird)
入站51820UDP指定对端WireGuard 隧道

其余端口由 Docker iptables 集成或反向代理负责放行。ufw 只管主机层面的出入口,应用层面交给反代和 WAF。

内核参数

# /etc/sysctl.d/99-bbr.conf
# BBR 拥塞控制 —— 新内核默认就有
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
# /etc/sysctl.d/98-conntrack.conf
# conntrack 表容量 —— 默认 65536 对大多数场景够用
net.netfilter.nf_conntrack_max = 262144
net.netfilter.nf_conntrack_buckets = 65536

日常管理

0x01 命名规范

主机命名规则

格式:[分组-]国家-类型-标识符

示例:infra-us-kvm-vultrprod-jp-kvm-ali

这套规则最大的价值:看名字就知道机器在哪、跑在什么上面、是谁家的。出事了随手 ssh hostname 不用翻笔记。

标签规范

标签用途
infra基础设施(DNS、监控、反向代理)
prod生产业务
dev开发测试
bgpBGP 路由器
s3存储节点

0x02 DNS 管理

托管商

域名分散托管于多家主流 DNS 提供商:

分散托管的原因:鸡蛋不放一个篮子里,单家出问题不影响所有域名解析。代价是管理复杂度增加了,所以必须上 DNSControl 等 IaC 方案。

Zone 管理

工作流程:

  1. 修改 DNSControl 配置(dnsconfig.js
  2. 提交到 Git
  3. Forge Actions 自动执行 dnscontrol push 同步到各 DNS 服务商

禁止手动在 DNS 面板修改记录,否则不一致将造成服务停机

DDNS

ddns-go 的 Web 管理界面很直观,配好之后自动轮询公网 IP 变化并更新 DNS 记录。接入多个 DNS 提供商也支持。

0x03 SSL/TLS 证书续签方案

ACME 客户端

客户端用途部署方式自动化方式
CaddyCaddy 反代的域名证书Caddy 内置自动申请与续期,无需任何配置
Certimate非 Caddy 托管的域名Docker内置流水线编排,自动推送部署

ACME 账户邮箱:tls#932.moe

Caddy 自动续期

Caddy 的 ACME 客户端是内置的,只要有:

example.com {
    reverse_proxy 172.17.0.1:8080
}

Certimate 管理

对于不用 Caddy 的服务(OpenResty、自建 API 等),用 Certimate 编排:

  1. 证书申请(支持 HTTP-01 / DNS-01 challenge)
  2. 证书保存
  3. 自动部署到目标服务器(通过 SCP / API / 文件写入等方式)

0x04 BGP 路由策略

资源

项目
ASNAS213891
IPv6 段2a0e:aa07:e150::/44
有效 /4810+ 个
IPv4
RIRRIPE NCC
网络类型Educational / Research
流量特征100–1000 Mbps,大部分出站
RPKI ROA全局启用
Looking Glasshttps://lg.932.moe/
Peering 策略Selective

Upstream

ASN名称IPv6
AS53667FranTech Solutions (BuyVM)
AS20473Vultr
AS34927iFog GmbH
AS16509AWS
AS204211忘记了
AS209533iFog GmbH — BGPTunnel
AS213605Liu HaoRan
AS212895Johannes Ernst
AS41051Freetransit Project

对等 ASN

除上行和 IXP 成员外,还维持了少量直接对等:

ASN名称
AS198025Steve87
AS210440AKAERE NETWORKS

其他都不认识。

社区标记

用于路由策略标记:

社区含义
213891:1:10Learned from Peers(从对等处学来的)
213891:1:11Learned from Upstreams(从上游学来的)
213891:2:XLearned from AS X
213891:10:XLearned at router X(多路由器场景区分来源)

BIRD2 配置

托管于私有仓库:https://git.6l.ink/liuzhen932/as213891.conf

BIRD 配置用 Git 管理最大的好处是回滚。某次改策略把路由搞崩了,git revert 然后 birdc configure,没有版本控制的配置就是定时炸弹。

运维

# 查看 BGP 会话状态
birdc show protocols

# 查看路由表
birdc show route

# 重新加载配置(不中断会话)
birdc configure

# 查看特定前缀的传播情况
birdc show route for 2a0e:aa07:e150::/44 all

0x05 OID 分配表

目前没有用 SNMP 监控任何东西,Prometheus 生态更香。

0x06 容器与编排

运行环境

项目
Docker 版本始终用最新版
Compose 版本始终用最新版
运行模式root 模式
Compose 文件路径/opt/<项目名>/compose.yml
网络模式bridge 为主,部分服务用 host 减少损耗

Docker 安装脚本

curl -fsSL https://get.docker.com | sh

容器化服务清单

所有自定义镜像托管于 docker.io/liuzhen932/*,私有镜像除外。

反向代理

镜像名称说明
app-openrestyOpenResty主力反代 + WAF
app-caddyCaddy次要反代
app-typechoTypecho博客
app-blessing-skin-serverBlessing SkinMinecraft 皮肤站
app-openstatusOpenStatus状态页
app-opengrokOpenGrok代码搜索
app-rustpadRustpad协作编辑器

网络 & 隧道

镜像名称说明
app-cloudflaredCloudflare TunnelCF 隧道,用于没有公网 IP 的机器
app-derperDERP RelayTailscale DERP 中继节点
app-warpCloudflare WARPWARP 出口
app-torTorTor 出口节点

BGP & 网络监控

镜像名称说明
bird2-dockerBird2容器化部署 BIRD
dn42-dockerDN42DN42 实验网络
app-bgpalerterBGPalerterBGP 异常检测
app-xenoeyeXenoeyeNetflow 流量分析
app-fastnetmonFastNetMonDDoS 检测

Compose 配置规范

所有服务统一目录结构:

/opt/<项目名>/
├── compose.yml      # 服务定义
├── .env             # 环境变量
└── data/            # 持久化数据

日志配置

每个 compose.yml 里建议加上:

services:
  app:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

更新策略

cd /opt/<项目>
docker compose pull && docker compose up -d

为什么不全自动更新?全自动滚动更新一旦出问题很难定位。个人运维场景,手动跑一条命令比半夜被告警吵醒划算。Diun 负责通知你有新版本,收到通知后挑个方便的时间手动更新。

Docker daemon 配置

// /etc/docker/daemon.json
{
  "features": { "buildkit": true },
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

全局限制日志大小,防止日志撑爆磁盘。

0x07 端口与服务映射

公网入站

端口协议服务备注
80TCPHTTP → 301 跳转 443Caddy/OpenResty 负责
443TCPHTTPS(Caddy / OpenResty)主力 Web 流量入口
179TCPBGP(Bird)仅限对等 IP
55822TCPSSH自定义端口
51820UDPWireGuard隧道互联

其余端口(如各服务管理端口)按需放行,但只对特定来源 IP 开放。基本原则:默认全关,按需放行

反向代理

角色配置路径
Caddy/opt/caddy/Caddyfile
OpenResty/opt/openresty/nginx.conf + conf.d/*.conf

OpenResty 负责主要流量的反向代理和 WAF;Caddy 负责次要服务和新项目的快速部署,优势是零配置 HTTPS。

Tailscale

Tailscale 作为运维通道使用,不暴露到公网:

节点角色用途
认证节点Tailscale 网络入口,各服务器通过 Tailscale SSH 管理
DERP 中继自建 DERP 节点,降低对 Tailscale 官方中继的依赖
客户端本地 PC / 手机通过 Tailscale 接入内网服务

Tailscale 最大的价值:不管机器在哪个国家、有没有公网 IP、NAT 类型是什么,只要它能联网,Tailscale 就能把它拉进你的网络。IPv4 NAT 后面裸 SSH ?还是 Tailscale 吧。

0x08 关键服务备忘

邮件

没有自建邮件服务器。自建邮件是运维界公认的深坑——IP 信誉、反 spam 配置、DKIM/SPF/DMARC、各大厂商拒收……为个人博客搞一套不值当。

项目方案
入站转发Cloudflare Email Routing(CF 收邮件后转发到目标邮箱)
发信依托第三方服务的 SMTP(具体看服务需要)
个人邮箱阿里邮箱 / 飞书邮箱 / QQ 邮箱

CI/CD

项目
平台Forgejo Actions
Runner自建的 Forgejo Runner,运行在 homelab
仓库自建 Forgejo 实例(git.6l.ink

自动化流水线:

日志

项目
集中日志平台ELK + OpenObserve(全自建)
采集方式Filebeat / Vector 从各节点采集

日志是出事之后唯一的上帝视角。平时没人看,但真出问题了没日志你连从哪查起都不知道。

现状:ELK 和 OpenObserve 已经部署,但还没把所有服务的日志都接入。持续完善中。

0x09 数据持久化与备份

重要数据路径

所有持久化数据都在各服务的 /opt/<项目名>/ 目录下:

/opt/<项目名>/
├── compose.yml    # 服务定义
├── .env           # 环境变量
├── data/          # 数据库、文件存储等持久化数据
└── config/        # 配置文件(如果是卷映射出来的)

备份现状

环境工具频率范围
Homelab LXC自定义脚本 + rsync/scp每日全量备份,推送双异地存储
Homelab KVM(其中一台)restic 0.18.1每日全量 /opt 目录
云 VPS尚无备份

这个是最大短板。云 VPS 上的数据目前没有统一备份方案。理论上服务商有快照功能,但快照不是备份——机房起火你的快照也跟着烧了。TODO:restic 覆盖所有云 VPS。

Restic 备份命令(快速参考)

# 初始化仓库
restic init --repo s3:https://<endpoint>/<bucket>

# 创建快照
restic --repo <repo> backup /opt

# 查看快照
restic --repo <repo> snapshots

# 恢复
restic --repo <repo> restore latest --target /

凭据存储

类型存储位置备注
CI/CD 密钥Infisical密钥管理平台,支持环境变量注入
应用密钥.env 文件或 compose.yml明文写入 compose.yml 仅限无外网的内部服务
备份密钥离线存储 + 密码管理器备份加密密钥不能跟备份放在一起

跟凭据有关的核心原则:.env 文件不入库compose.yml 里用 ${VAR} 引用环境变量,.env 写本地 .gitignore 里。不小心 push 了密钥怎么办?一个字:换。

0x0A 监控与告警

监控栈

组件用途部署方式
Prometheus指标采集与存储Docker(homelab)
Grafana可视化与告警面板Docker(homelab)
AlertManager告警路由与通知Docker(homelab)
Node Exporter主机指标采集每台机器上跑一个
cAdvisor容器指标采集每台 Docker 主机上跑一个
Blackbox Exporter外部探活Docker(homelab)

告警渠道

渠道状态备注
ntfy启用主要通知渠道,推送到手机
SMTP启用邮件告警,备用渠道
QQ已废弃之前用 QQ 机器人,后来弃了

ntfy 是真的好用。手机装个 App,收到告警推送直接点开看。比邮件快,比 QQ 稳定,而且不依赖国内生态。

关键告警阈值

指标告警阈值处理方式
磁盘使用率> 80% WARN, > 90% CRIT清理日志、扩容、或迁移数据
内存使用率> 90%检查 OOM 日志,看是否需要加内存
CPU 使用率> 90% 持续 5 分钟查哪个进程在吃 CPU
SSL 证书到期< 14 天自动续签(Caddy / Certimate),如果续签失败则手动排查
Ping 丢包> 10% 持续 3 次采样检查网络链路
BGP 会话中断任何会话 Down检查对端状态和网络连通性
Docker 容器状态容器退出(非预期)查容器日志,恢复服务

告警处理流程

  1. 收到告警 → 确认告警真实性(不是误报)
  2. 如果是误报 → 调优告警规则,避免下次再误报
  3. 如果是真问题 → 按 SOP 处理
  4. 处理完 → 记录到本文的对应章节
  5. 如果是同类问题第二次出现 → 考虑自动化修复

0x0B 安全防护

WAF — 两层防护

方案参考
边缘(Edge)Cloudflare WAFCloudflare WAF 规则
源站(Origin)OpenResty (Nginx) WAFOpenResty WAF 规则

Cloudflare WAF 挡掉大部分常见攻击流量(SQL 注入、XSS、路径遍历等)。但不要把安全全押在 CF 上——

为什么源站还要 WAF:CF 不是万能的。有些人直接扫源站 IP 绕过 CDN,或者你的服务有一部分没走 CF(API 直连、非 Web 服务等)。源站 WAF 是最后一道防线。

fail2ban & CrowdSec

组件应用场景封禁时长备注
fail2banSSH 暴力破解1 hour[sshd] jail
CrowdSecNginx 恶意请求4 hoursnginx-http 场景

两者的区别:fail2ban 只管自己机器上的日志;CrowdSec 有社区威胁情报网络,一台机器发现恶意 IP 会同步给其他用户。

实际效果:fail2ban 主要是挡住了 SSH 扫描脚本。CrowdSec 的数据更有价值,能识别到一些普通的 WAF 规则没挡住的异常请求模式。

登录审计

# 近期 SSH 登录记录
last -n 20

# sudo 执行记录
journalctl -t sudo --since "7 days ago"

# 所有登录失败的 IP
lastb | awk '{print $3}' | sort | uniq -c | sort -rn | head -10

# 当前登录用户
who

SSH 日志是安全排查的第一站。lastb 看暴力破解的来源,last 看有没有陌生 IP 成功登录。

0x0C 软件源/镜像加速

apt

大厂内网的机器用服务商提供的内部镜像源(延迟最低,带宽不限),其余机器统一用 中科大 USTC 镜像。

# /etc/apt/sources.list — USTC 镜像(Debian 12 Bookworm)
deb https://mirrors.ustc.edu.cn/debian/ bookworm main contrib non-free
deb https://mirrors.ustc.edu.cn/debian-security bookworm-security main contrib non-free
deb https://mirrors.ustc.edu.cn/debian/ bookworm-updates main contrib non-free

Debian 13(Trixie)发布后把 bookworm 替换为 trixie 即可。

pip

# ~/.pip/pip.conf
[global]
index-url = https://mirrors.ustc.edu.cn/pypi/web/simple
trusted-host = mirrors.ustc.edu.cn

npm

npm config set registry https://mirrors.ustc.edu.cn/npm/

Docker

Docker 镜像源在 daemon.json 里配置,但目前没配国内的 mirror。在大陆的机器可能 pull 得慢,考虑加一行 "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]

USTC 镜像是目前国内最靠谱的开源镜像站之一,维护团队靠谱、更新及时、容量够大。清华 TUNA 也可以做备选。

0x0D 续费与到期日历

域名

域名说明注册商到期提醒
932.moe主力域名,个人站点、邮箱Cloudflare到期前 30 天邮件提醒
liuzhen932.top备用域名,博客、Abuse 联系阿里云到期前 30 天邮件提醒
6l.ink短链接/Git 服务Cloudflare到期前 30 天邮件提醒
其余 30+ 域名分散注册多家服务商需要统一管理

30 多个域名分散在多家注册商,这就是个管理噩梦。目前靠邮箱收续费提醒,真漏了一个就完了。TODO:搞个域名管理看板或脚本,统一监控到期日。

VPS / 服务器

各厂商续费策略:大部分按年付(便宜)、少数按月付。

厂商续费周期备注
BuyVM月付随时可停
Vultr按小时用了付钱,不用停
阿里云年付大促时囤的
其他月/年付见各厂商账单

VPS 续费没有统一的日历管理。到期的钱不多,但忘了续导致服务停摆就尴尬了。目前靠手机日历设提醒。

SSL 证书

全部 Let’s Encrypt,Caddy 和 Certimate 自动续期。没有手动过期风险,只需要确保 ACME 客户端正常运行。

0x0E GitOps / IaC 速查

配置仓库

仓库用途管理工具
git.6l.ink/liuzhen932/dnsDNS 配置DNSControl
git.6l.ink/liuzhen932/as213891.confBGP 配置Bird + Git

部署流程

# DNS 变更
修改 dnsconfig.js git push Forge Actions 自动 dnscontrol push

# BGP 变更
修改 bird.conf git push 登录机器 birdc configure

# 服务更新
cd /opt/<项目>
docker compose pull
docker compose up -d

# 配置文件更改
修改 compose.yml config 文件 docker compose up -d(重建容器)

敏感文件管理

类型管理方式
CI/CD 密钥Infisical
应用密钥.env 文件或 compose.yml 明文

原则:所有敏感信息都不进 Git。.envcompose.yml 里的明文密码只用在 homelab 内网机器上,云 VPS 上必须用 Infisical 或环境变量注入。

0x0F 服务依赖关系

启动顺序

数据库(PostgreSQL / MySQL / SQLite)
  → 后端服务(API / Web 应用)
    → 反向代理(Caddy / OpenResty)
      → 监控(Prometheus / Grafana / AlertManager)

反过来,关机顺序:监控 → 反代 → 后端 → 数据库。

故障影响矩阵

服务挂了影响范围恢复优先级
反向代理所有 Web 服务不可达P0
数据库依赖该 DB 的所有服务不可用P0
DNS域名解析失败,Tailscale/内网 IP 仍可访问P1
BGP部分路由失效,具体看影响哪些前缀P1
监控栈不影响核心业务,但无法收到告警P2
镜像仓库无法拉取新镜像,已运行的容器不受影响P5

0x10 灾难恢复 SOP

服务器完全挂了的恢复步骤

  1. 从提供商控制台进入救援模式(Rescue Mode / Live CD)
  2. 挂载系统盘 mount /dev/sdaX /mnt
  3. 检查文件系统 fsck /dev/sdaX
  4. 从备份恢复 /opt 目录数据 restic --repo <repo> restore latest --target /mnt/opt
  5. 重装系统(同版本 Debian)
  6. 执行装机后必做清单(见 0x00)
  7. 安装 Docker
  8. 恢复 compose.yml 和 .env
  9. docker compose up -d 恢复所有服务
  10. 验证各服务正常运行

恢复后的验证清单

# 1. SSH 能登
ssh -p 55822 root@<host>

# 2. 反代正常运行
curl -I https://<domain>

# 3. 数据库连接正常
docker compose exec db pg_isready  # PostgreSQL 为例

# 4. 备份已恢复
restic --repo <repo> snapshots

# 5. 监控正常
curl http://localhost:9090/api/v1/query?query=up

# 6. DNS 解析正常
dig <domain>

离线入口

方式可用条件备注
Tailscale SSHTailscale 可达优先尝试,只要没把 Tailscale 的流量堵了就能进
提供商 VNC / IPMI有控制台权限救援模式入口
备用 SSH 端口 55822防火墙正常在防火墙规则中显式放行

灾难恢复最重要的是恢复前想清楚先恢复什么。数据库 → 反代 → 服务,按这个顺序恢复最快能让用户访问到服务。不要试图把所有服务同时恢复,先让核心业务跑起来,其他的慢慢来。

预防性措施

除了这条文档本身,以下措施能显著减少灾难恢复的工作量:

  1. 所有 compose.yml 放在 Git 仓库里
  2. 关键配置截图/存一份离线备份
  3. 定期做恢复演练
  4. 多地域备份
  5. 备份失败要有告警

分享这篇文章:

上一篇
liuzhen932 提供的公益服务
下一篇
世界 IPv6 日,不只是个纪念日

人机验证:请刷新页面以加载评论区