一台刚开通的 Debian 12 服务器,默认状态其实是"半成品":root 能直接登录、SSH 密码认证开着、时区大概率还停在 UTC、时间同步服务不一定在跑、防火墙一条规则都没有、日志里很快会塞满各种扫描记录。我第一次给生产环境做初始化的时候图省事,只改了个密码就上了业务,结果三天后翻/var/log/auth.log,发现几万条爆破尝试,那一刻才真正理解"服务器初始化配置"这件事不是走流程,而是把一台公网上的裸机变成"能放心交钥匙"的状态。
这篇内容讲的是一次完整的 Debian 12 服务器初始化配置流程,从拿到 IP 开始,到最终形成一份可以反复复用的初始化脚本。涉及账号体系、SSH 加固、时间同步、软件源、基础工具链、防火墙、内核参数、磁盘与 Swap 规划这些环节。不管你是刚租了一台云服务器想搭个人服务,还是手里管着几台小规模集群的运维,这套流程都能直接用,也可以按需裁剪。我尽量把每一步"为什么这么做"讲清楚,因为参数抄错比不配更麻烦。
1. 初始化到底在解决什么问题:整体思路拆解
1.1 从"能连上"到"能用"之间差的那些东西
很多人对初始化的理解停留在"装几个软件",但实际上真正需要解决的是四类问题。第一类是可达性,也就是我能稳定、安全地连上这台机器,而不是每次登录都提心吊胆怕被爆破;第二类是一致性,即这台机器上的时区、时间、语言环境、文件编码符合预期,不会出现日志时间对不上、定时任务跑错点的情况;第三类是可观测与可恢复,日志有人管、磁盘有空间、系统崩了能查到原因;第四类是可复制性,同样的事情再来一台机器,我能五分钟做完而不是回忆半小时。
这四类问题的优先级是有先后的。可达性永远排第一,因为如果 SSH 配置改崩了,你连不进去,后面所有工作都无从谈起。这也是为什么我强烈建议在动 SSH 配置之前先开一个备用会话,保持登录状态不退出,等新的会话验证通过再关掉旧的。这个习惯救过我至少三次。
1.2 我的四层顺序模型
把上面这些拆开,我习惯按下面这个顺序推进,顺序错了会给自己找麻烦:
| 层级 | 处理内容 | 为什么排这个位置 |
|---|---|---|
| 第一层 | 账号、sudo、SSH 密钥与 sshd 配置 | 先锁住入口,后续所有操作都在安全通道里做 |
| 第二层 | 主机名、时区、时间同步、DNS | 时间不对会导致日志错乱、证书校验失败、集群心跳异常 |
| 第三层 | 软件源、系统更新、基础工具链 | 有了工具才能排查问题,这是后续所有操作的弹药 |
| 第四层 | 防火墙、内核参数、磁盘与 Swap、监控 | 前面稳了再收紧策略,避免把自己关在门外 |
举个具体的例子说明为什么时间同步要排在软件安装之前:Debian 默认时区是 UTC,如果你先装了数据库、后面才改时区,某些数据库的日志文件里会混着两种时间戳,排查问题时能把人逼疯。而如果先改时区再装,从一开始就是一致的。
1.3 哪些步骤可以省,哪些绝对不能省
不是所有场景都需要全套流程。个人玩具机、内网测试机、生产业务机,标准完全不同。我的判断标准是这样的:只要这台机器有公网 IP,SSH 加固、防火墙、fail2ban 这三样一个都不能少,因为公网上的扫描是全自动的,从开通到第一波爆破通常不超过半小时。而如果只是内网跳板或者本地虚拟机,防火墙可以放宽,时间同步用默认的 systemd-timesyncd 也够用。
至于磁盘规划、内核参数调优这类,我建议个人用户先跳过,等真正遇到性能瓶颈再回头处理,提前优化往往是在优化一个不存在的瓶颈。下面几节我会重点讲公网机器必须做的部分,内网场景可以按标注跳过。
一个我自己踩过的坑:第一次配置云服务器时,我在没确认云厂商安全组规则的情况下先开了 ufw 并且只放行了 22 端口,结果把 HTTPS 流量全挡了,业务健康检查直接失败。本机防火墙和云平台安全组是两层,要一起看。
2. 账号体系与 SSH 加固:把门先锁好
2.1 root 直登还是普通用户加 sudo
Debian 12 安装完成后默认是可以 root 登录的(云镜像通常也保留了 PermitRootLogin 的默认值)。我的做法是:保留 root 账号但禁止 SSH 登录,日常操作全部走普通用户 + sudo。这么做有两个实际好处,一是审计日志里能看出"是谁执行的",而不是一片 root;二是降低误操作风险,因为删根目录这种命令,普通用户会被 sudo 拦一道。
创建运维账号的命令很直白:
adduser deploy usermod -aG sudo deployadduser是 Debian 系的交互式脚本,会顺带创建家目录、提示设置密码,比useradd省心。加进sudo组之后,Debian 12 默认的/etc/sudoers里已经有%sudo ALL=(ALL:ALL) ALL这一行,不需要额外改。如果你想让某个账号免密 sudo,可以放到/etc/sudoers.d/下单独一个文件:
echo 'deploy ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/90-deploy chmod 440 /etc/sudoers.d/90-deploy注意:
/etc/sudoers和/etc/sudoers.d/里的文件权限必须是 0440,否则 sudo 会直接拒绝加载。这个报错信息很隐晦,我第一次遇到时查了半小时。
2.2 SSH 密钥登录的完整落地过程
密钥登录的流程分三步:本地生成、公钥上传、服务端配置。
本地生成建议用 ed25519,比 RSA 短、比 RSA 快,OpenSSH 9.x 也早已默认支持:
ssh-keygen -t ed25519 -C "deploy@my-server" -f ~/.ssh/id_ed25519_debian12上传公钥最省事的方式是ssh-copy-id:
ssh-copy-id -i ~/.ssh/id_ed25519_debian12.pub deploy@1.2.3.4这一步执行完,会往目标机的~/.ssh/authorized_keys里追加内容,并且自动处理目录权限。如果你手动上传,务必确认权限:
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys chown -R deploy:deploy ~/.ssh权限不对是密钥登录失败最常见的原因,sshd 对"组可写""其他人可读"非常敏感,一旦不符合要求会直接忽略这个 key,而且不会告诉你原因。
2.3 sshd_config 逐条说明与验证方法
Debian 12 的/etc/ssh/sshd_config里有一行Include /etc/ssh/sshd_config.d/*.conf,我习惯把自定义项放到独立文件里,方便日后对比和回滚:
# /etc/ssh/sshd_config.d/99-custom.conf PermitRootLogin no PasswordAuthentication no KbdInteractiveAuthentication no PubkeyAuthentication yes MaxAuthTries 3 LoginGraceTime 20 ClientAliveInterval 300 ClientAliveCountMax 2逐条说下取舍。PermitRootLogin no是切断最直接的攻击路径;PasswordAuthentication no关掉密码认证,这一条要确认密钥能正常登录之后再改;KbdInteractiveAuthentication no是配合上一条,因为 PAM 可能绕过密码认证;MaxAuthTries 3限制单次连接的认证次数;LoginGraceTime 20把默认的 120 秒缩短,减少闲置连接占用;ClientAliveInterval和CountMax控制空闲会话自动断开,防止忘关的终端一直挂着。
改完之后不要急着重启 sshd,先做语法检查:
sshd -t更关键的是确认最终生效值,因为 sshd 的规则是"第一个出现的同名指令生效",而 Include 的位置会影响到谁先谁后:
sshd -T | grep -E 'permitrootlogin|passwordauthentication|pubkeyauthentication'确认输出符合预期后再重载:
systemctl reload ssh然后再开一个新终端验证登录,成功之后才关闭当前会话。这个"新开验证"的动作我强烈建议固化成肌肉记忆。
3. 网络、主机名与时间同步:让机器"对得上表"
3.1 主机名与 hosts 的连带影响
主机名看着是小事,但很多分布式组件默认用主机名做节点标识,一旦后面改了名字,注册到集群里的旧记录会变成僵尸。所以主机名要在装业务之前一次定好:
hostnamectl set-hostname web-prod-01Debian 12 使用 systemd,hostnamectl会自动更新/etc/hostname。同时要检查/etc/hosts,至少要有一行把主机名映射到本机地址:
127.0.1.1 web-prod-01这个127.0.1.1是 Debian 系的传统,作用是让本机解析自己的主机名时不需要依赖 DNS。如果没有这一行,某些程序启动时会卡在主机名解析上几秒钟,这种延迟很难定位。验证方式:
hostname -f能正确返回完整主机名就对了。
3.2 静态 IP 与 DNS 的配置方式选择
Debian 12 默认用/etc/network/interfaces(ifupdown),但云镜像通常已经装好了 cloud-init,由它接管网络配置。这里有个容易踩的坑:手动改/etc/network/interfaces可能被 cloud-init 覆盖。判断方法:
cloud-init status ls /etc/cloud/cloud.cfg.d/如果是云环境,我一般不动底层配置,而是通过云控制台改安全组和弹性 IP。如果是自建机房或者虚拟机,就老老实实写 interfaces 文件:
auto ens3 iface ens3 inet static address 192.168.1.10/24 gateway 192.168.1.1 dns-nameservers 223.5.5.5 119.29.29.29DNS 这块,Debian 12 默认没有装 systemd-resolved,/etc/resolv.conf是实际生效的文件。但如果你装了 cloud-init 或者 NetworkManager,它们可能会接管。检查方式:
ls -l /etc/resolv.conf如果它指向/run/systemd/resolve/...,说明有东西在管;如果是普通文件,就可以直接编辑。
3.3 时间同步:chrony 的配置与选择理由
Debian 12 默认带的是systemd-timesyncd,它能满足基本需求,但功能比较薄。我一般换成chrony,原因有三个:一是它支持更精细的源选择和统计,二是默认源连不上时能更快切换到备用源,三是它能作为时间源对外服务,方便内网其他机器同步。
apt install -y chrony systemctl enable --now chrony安装 chrony 时会自动与 timesyncd 冲突,从而把它停掉。配置国内可用的时间服务器:
# /etc/chrony/chrony.conf 追加 server ntp.aliyun.com iburst server ntp1.aliyun.com iburst server cn.pool.ntp.org iburst server time1.cloud.tencent.com iburstiburst的含义是首次同步时快速发 4 个包,把同步时间从几十秒缩短到几秒。配置完重启并验证:
systemctl restart chrony chronyc sources -v chronyc trackingsources -v里每个源前面有个符号,^*表示当前选中的同步源,^+表示备用候选。tracking里的System time偏差值如果在毫秒级就正常。
3.4 时区设置的连带影响
时区要和时间同步一起处理。Debian 12 改时区:
timedatectl set-timezone Asia/Shanghai timedatectl status输出的三行关键信息要都确认:Local time是本地时间、Universal time是 UTC、System clock synchronized: yes、NTP service: active。
一个实际教训:我曾经在一台机器上先改了时区但没管 NTP,结果容器里跑的应用日志时间和宿主机差了 8 小时,定位了大半天。改时区之后一定要跑一遍
timedatectl确认同步状态,不要只看时间对不对。
4. 软件源与基础工具链:把弹药备齐
4.1 sources.list 的结构与 Debian 12 的变化
Debian 12 引入了新的 deb822 格式,文件放在/etc/apt/sources.list.d/下,扩展名是.sources。相比传统的一行式写法,它的可读性和可维护性更好:
Types: deb URIs: http://deb.debian.org/debian Suites: bookworm bookworm-updates Components: main contrib non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg Types: deb URIs: http://security.debian.org/debian-security Suites: bookworm-security Components: main contrib non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg这里要注意non-free-firmware这个组件,它是 Debian 12 新增的,固件包从non-free里独立出来了。如果你用的是默认的云镜像,通常已经配好了。上游地址如果访问速度不理想,可以按自己所在网络的实际情况换成响应更快的地址,改完用apt update验证连通性即可。
4.2 系统更新策略:全自动还是手动
apt update && apt full-upgrade -y是标准动作,但"是否让系统自动装安全更新"是个需要思考的决策:
| 策略 | 适合场景 | 风险 |
|---|---|---|
| 全手动 | 生产核心业务 | 容易漏掉安全补丁 |
| 只自动装安全更新 | 大多数生产环境 | 极小概率引入兼容问题 |
| 全自动升级 | 个人测试机 | 内核升级后可能需重启 |
我通常选第二种,装unattended-upgrades并只启用安全源:
apt install -y unattended-upgrades dpkg-reconfigure --priority=low unattended-upgrades配置文件在/etc/apt/apt.conf.d/50unattended-upgrades,默认已经只勾选了安全更新。如果你的业务对重启敏感,记得在同一目录下的20auto-upgrades里确认自动清理旧内核的行为,避免/boot被塞满。
4.3 必装工具清单与理由
工具不在多,在于"出事的时候手边有家伙"。我的基础清单是这些:
apt install -y curl wget vim htop git tmux unzip rsync jq \ mtr-tiny dnsutils lsof iotop sysstat ca-certificates每一项都有明确用途:curl和wget做连通性测试;vim是因为 vi 在 Debian 上其实是 vim-tiny,很多功能缺失;tmux是长任务保命工具,SSH 断了任务还在;jq处理 JSON 输出;mtr-tiny是 traceroute 加 ping 的合体,排查网络问题比单独用两个命令高效;dnsutils提供dig;lsof查端口占用;iotop和sysstat用于磁盘和系统性能回溯,尤其是sysstat会留下历史数据,事后排查非常有用。
提示:Debian 12 默认不装
net-tools,也就是没有ifconfig和netstat。建议直接用ip addr和ss -tulnp替代,这两个命令的信息量更大。如果实在习惯老命令,装net-tools也无妨,只是要注意它已经多年不更新了。
5. 防火墙、内核参数与基础加固
5.1 ufw 与 nftables 的选择逻辑
Debian 12 底层是 nftables,ufw只是它的一个易用前端。个人和小团队场景我推荐 ufw,因为规则可读性好、不容易写错;如果是复杂规则集(多网段、NAT、限速),直接写 nftables 更合适。
基础规则这样写:
apt install -y ufw ufw default deny incoming ufw default allow outgoing ufw allow 22/tcp ufw allow 80/tcp ufw allow 443/tcp ufw enable ufw status verbose顺序很重要:先把要放行的端口写进去,最后再 enable。如果先 enable 再想加规则,而你恰好只放行了 22,那自己还能连上;但如果你连 22 都没放行,就直接失联了。云环境下还要注意,云平台安全组和本机 ufw 是两层过滤,两边都要放行才能通。
5.2 fail2ban 的正确配置位置
SSH 加固之后,爆破依然会来,只是打不进来。fail2ban 的作用是把反复尝试的 IP 封掉,减少日志噪音和连接资源占用。
apt install -y fail2banDebian 12 的 fail2ban 有个变化:默认配置里没有启用任何 jail,而且日志后端需要用 systemd。所以自定义配置要写清楚:
# /etc/fail2ban/jail.d/sshd.local [sshd] enabled = true backend = systemd maxretry = 5 findtime = 10m bantime = 1h重启后验证:
systemctl restart fail2ban fail2ban-client status sshd输出的Banned IP list如果已经有内容,说明它开始工作了。bantime我通常设 1 小时,太长容易误伤自己的动态 IP,太短又没威慑力。
5.3 内核安全参数与生效方式
/etc/sysctl.d/下放自定义配置,避免污染/etc/sysctl.conf:
# /etc/sysctl.d/99-hardening.conf net.ipv4.tcp_syncookies = 1 net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.all.accept_source_route = 0 net.ipv4.icmp_echo_ignore_broadcasts = 1 kernel.dmesg_restrict = 1 fs.protected_hardlinks = 1 fs.protected_symlinks = 1这几个参数的意义:tcp_syncookies缓解 SYN 洪泛;rp_filter做反向路径校验,减少伪造源地址的包;关闭 ICMP 重定向和源路由,防止路由被篡改;icmp_echo_ignore_broadcasts忽略广播 ping;dmesg_restrict让非特权用户看不到内核日志,减少信息泄露;后两个是防止硬链接和符号链接被滥用提权。
应用并验证:
sysctl --system sysctl net.ipv4.tcp_syncookies注意sysctl --system会按文件名顺序加载,数字前缀决定优先级,所以用99-能保证最后生效。
6. 磁盘、Swap 与文件系统规划
6.1 磁盘现状查看与在线扩容
拿到机器先看磁盘布局,这一步很多人跳过,结果后面发现根分区只有 20G:
lsblk -f df -hT云盘扩容的典型场景是:控制台上把云盘从 40G 调到 100G,但系统里看到的还是 40G。这时候需要先扩展分区再扩文件系统:
growpart /dev/vda 1 resize2fs /dev/vda1 # ext4 # xfs_growfs / # xfs 用这个growpart来自cloud-guest-utils包,有些镜像没预装。ext4 支持在线扩容,不用卸载;xfs 也支持,但命令不同。这个差别如果搞混,会得到一堆看不懂的报错。
6.2 Swap 到底要配多大
Swap 大小没有统一答案,取决于内存容量和业务类型。下面这个表是我在实际项目中常用的经验值:
| 物理内存 | 建议 Swap | 说明 |
|---|---|---|
| ≤ 2 GB | 2 GB | 内存紧张,Swap 是保命线 |
| 2 - 8 GB | 等于内存 | 兼顾突发流量 |
| 8 - 64 GB | 4 - 8 GB | 主要防 OOM,不做日常使用 |
| > 64 GB | 4 GB 或不配 | 更依赖监控和限流 |
如果你用的是云盘(延迟比本地盘高),我建议把vm.swappiness调低到 10 左右,让系统尽量用内存,只有在实在不够时才动用 Swap。配置方式:
sysctl -w vm.swappiness=10 echo 'vm.swappiness = 10' > /etc/sysctl.d/99-swap.conf6.3 用 Swap 文件代替 Swap 分区的完整步骤
现在主流做法是用文件代替分区,灵活且不用重新分区:
fallocate -l 4G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo '/swapfile none swap sw 0 0' >> /etc/fstab free -h swapon --show四个关键点:fallocate比dd快很多;权限必须是 600,否则 swap 会拒绝启用;mkswap生成的文件头不能少;写进/etc/fstab才能开机自动挂载。
常见坑:如果你用的是 Btrfs 文件系统,
fallocate创建的 swap 文件会导致系统挂起。Btrfs 下需要用dd或者干脆单独划一个分区。这个坑我是在一次线上事故里学到的,代价不小。
6.4 数据盘挂载与 fstab 的正确写法
如果有独立数据盘,挂载时用 UUID 而不是设备名,因为设备名在重启后可能变化:
blkid /dev/vdb1 mkdir -p /data echo 'UUID=xxxx-xxxx /data ext4 defaults,noatime 0 2' >> /etc/fstab mount -a df -h /datanoatime表示不记录文件访问时间,能减少写入量,对 SSD 寿命和性能都有好处。mount -a会按 fstab 全部挂载一遍,如果这一步报错,千万不要重启,因为 fstab 写错会导致系统进不了正常启动流程,得进救援模式修。
7. 常见问题与排查速查
7.1 五类高频故障的定位思路
第一类,SSH 连不上。按顺序排查:云安全组是否放行、systemctl status ssh是否运行、ss -tlnp | grep 22端口是否监听、sshd -T配置是否正常、journalctl -u ssh -n 50看最近日志。这个顺序是从外到内,能最快排除大范围问题。
第二类,改了配置导致服务起不来。sshd、chrony 这类服务都有语法检查命令(sshd -t、chronyc相关检查),改完先验证再重启,是最省时间的好习惯。
第三类,时间对不上。先看timedatectl,如果NTP service显示 inactive,说明同步服务没跑。再看chronyc sources -v,如果所有源都是^?,说明都连不上,多半是 UDP 123 被防火墙挡住了。
第四类,磁盘满了。df -h只能看到分区级别,用du -sh /* 2>/dev/null | sort -h逐层找。日志文件是最常见的原因,/var/log/journal如果没限制大小,能涨到几个 G。
第五类,更新之后行为变了。Debian 的更新相对保守,但内核升级后需要重启才会生效,uname -r和dpkg -l | grep linux-image对比一下就知道有没有待生效的内核。
7.2 排查速查表
| 现象 | 最可能原因 | 快速验证命令 |
|---|---|---|
| SSH 超时无响应 | 安全组或防火墙拦截 | ss -tlnp、云控制台 |
| SSH 提示 Permission denied | 密钥权限或认证方式 | sshd -T、检查 authorized_keys 权限 |
| 服务启动失败 | 配置语法错误 | journalctl -u 服务名 -n 50 |
| 时间偏移超过 1 秒 | NTP 未同步 | chronyc tracking |
| 磁盘写入慢 | Swap 频繁或磁盘满 | free -h、iostat -x 1 |
| apt 报 GPG 错误 | 密钥过期或源不匹配 | apt update完整输出 |
| 主机名解析慢 | /etc/hosts 缺本机映射 | time hostname -f |
8. 把流程固化成可复用的初始化脚本
8.1 脚本设计要过的三道坎
第一道坎是幂等性。脚本要能重复执行而不出错,比如创建用户前先判断是否存在,追加配置前先检查是否已存在。最简单的方式是用id -u deploy &>/dev/null || adduser ...这种短路写法。
第二道坎是失败即停。脚本开头一定要加set -euo pipefail,任何一个命令失败就立即退出,避免在错误状态下继续改配置,把小问题滚成大问题。
第三道坎是不要自动重启 SSH 服务。脚本可以改配置文件,但最后一步的重载应该留给人来确认,或者至少把新旧配置对比打印出来。
8.2 脚本骨架示例
#!/usr/bin/env bash set -euo pipefail ADMIN_USER="${ADMIN_USER:-deploy}" PUBKEY="${PUBKEY:-}" log() { printf '[%s] %s\n' "$(date +%H:%M:%S)" "$*"; } # 1. 时区与主机名 timedatectl set-timezone Asia/Shanghai # 2. 创建运维账号 if ! id -u "$ADMIN_USER" &>/dev/null; then adduser --disabled-password --gecos "" "$ADMIN_USER" usermod -aG sudo "$ADMIN_USER" fi # 3. 注入公钥 if [[ -n "$PUBKEY" ]]; then install -d -m 700 -o "$ADMIN_USER" -g "$ADMIN_USER" "/home/$ADMIN_USER/.ssh" echo "$PUBKEY" > "/home/$ADMIN_USER/.ssh/authorized_keys" chmod 600 "/home/$ADMIN_USER/.ssh/authorized_keys" chown "$ADMIN_USER:$ADMIN_USER" "/home/$ADMIN_USER/.ssh/authorized_keys" fi # 4. 安装基础工具 export DEBIAN_FRONTEND=noninteractive apt update apt install -y chrony ufw fail2ban curl git tmux jq mtr-tiny # 5. SSH 加固(只写配置,不自动重启) cat > /etc/ssh/sshd_config.d/99-custom.conf <<'EOF' PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes MaxAuthTries 3 LoginGraceTime 20 EOF sshd -t # 6. 防火墙 ufw default deny incoming ufw default allow outgoing ufw allow 22/tcp ufw --force enable log "初始化完成,请用新会话验证 SSH 后再重载 sshd"用的时候把公钥通过环境变量传进去就行:
PUBKEY="$(cat ~/.ssh/id_ed25519_debian12.pub)" bash init.sh脚本执行完之后,我会再手动跑一遍sshd -T确认配置,然后开新终端登录,成功之后再systemctl reload ssh。
9. 一些个人经验与后续可扩展的方向
这套流程我用在不同规模的环境里,最后沉淀下来几条体会。第一,配置文件的每一处修改都要能回滚,我习惯在改之前cp xxx xxx.bak.$(date +%F),看起来土,但真出事的时候一分钟就能恢复。第二,不要迷信一键脚本,脚本能帮你省时间,但不能替你理解每一步在做什么,尤其是防火墙和 SSH 这两块,出问题的代价是失联。第三,初始化做完要留一份记录,我用一个简单的 Markdown 文件记下主机名、IP、时区、开了哪些端口、装了哪些服务,半年后再接手的时候能省掉大量考古时间。
后续如果这套流程要扩展,我会往两个方向走:一是接上配置管理工具,把脚本变成声明式的 playbook,几十台机器一起管的时候差异才算真正可控;二是加一层基础监控,哪怕只是 node_exporter 加一个简单的 Grafana 面板,也能让"磁盘什么时候开始涨的""内存什么时候开始抖"这类问题从猜测变成查数据。不过在只有一两台机器的时候,把上面这些做完其实已经能覆盖九成以上的日常需求了。