服务器初始化
一台新服务器交给应用使用时,需要确定几个具体事实:谁能修改系统,应用以哪个用户运行,数据实际写入哪块文件系统,依赖从哪里访问,故障信息保存在哪里。这些选择会写入账号数据库、网络配置、挂载表和服务文件,并在以后每次启动时重新生效。
管理入口 应用运行
公钥 → 管理账号 → sudo 制品 + 配置 + 运行用户
↓
系统依赖 systemd 启动进程
时间 / DNS / CA / 软件源 ├─ 读取代码和配置
├─ 写入指定状态目录
持久配置 ├─ 在许可的地址和端口监听
网络 / fstab / unit └─ 输出日志、受资源限制
└────────── 重启后重新建立 ──────────┘下面的命令以 Ubuntu Server 24.04 LTS、Bash、systemd 为环境,操作者是已经可以 sudo 的管理用户。实验需要一台允许重启的专用虚拟机;普通 Docker 容器适合测试文件权限和命令,不能替代主机的网络管理、systemd 启动和重启测试。RHEL 系通常采用 DNF、SELinux 和 firewalld,不能直接照搬 Ubuntu 的全部配置。
首次 SSH 连接、公钥和主机指纹操作见 SSH;Linux 文件、进程和权限的基础关系见 Linux 系统基础。已有业务运行的机器应按变更窗口逐项处理,不执行新机的一组默认设置。
识别机器、配置来源和管理身份
先看现有状态
在服务器的管理用户终端执行:
id
sudo -l
cat /etc/os-release
ps -p 1 -o comm=
hostnamectl
ip -br address
ip route
lsblk -o NAME,SIZE,TYPE,FSTYPE,UUID,MOUNTPOINTS
df -hT
free -h
sudo ss -lntup
systemctl --failed --no-pagersudo -l 列出当前账号可以执行的提权命令。PID 1 应为 systemd;如果输出是 bash、tini 等,后面的主机服务实验换到虚拟机执行。lsblk 展示设备与文件系统,df 展示已挂载文件系统的占用,二者要对照看:一块已经连接但尚未挂载的盘不会出现在 df 中。
监听端口中若已经有数据库、Web 服务或监控代理,应先查明用途。网络、账号、目录不能再按“空机器”重建。磁盘容量、CPU 架构或发行版与应用要求不符时,先调整资源或制品选择。
云镜像通常还有 cloud-init。安装了它的机器执行:
cloud-init status --long如果仍在初始化,等它完成并检查错误后再改同一批配置。主机名、用户、公钥、软件源和网络可能来自镜像元数据、cloud-init 或配置管理平台;手工改了生成文件,上游再次运行时可能覆盖它。永久修改应进入负责该配置的源头。cloud-init 模块说明列出了相关字段与执行频率。
为每次高风险修改保存独立备份
配置备份保存在 root 专有目录。在同一管理终端创建目录,并保存返回路径:
BACKUP_DIR=$(sudo mktemp -d /root/server-init.XXXXXXXX)
printf '%s\n' "$BACKUP_DIR"
sudo cp -a /etc/fstab "$BACKUP_DIR/fstab"
sudo cp -a /etc/ssh "$BACKUP_DIR/ssh"
sudo cp -a /etc/sudoers "$BACKUP_DIR/sudoers"
sudo cp -a /etc/sudoers.d "$BACKUP_DIR/sudoers.d"mktemp -d 创建不可预测且仅 root 可访问的目录。重新登录后,变量不会自动保留,需要用此前记录的实际路径重新设置 BACKUP_DIR。不要把配置、私钥或凭据备份上传到公开工单。
恢复应针对刚改的文件,并重新执行该组件的语法检查和生效操作;复制备份本身不会自动改变运行中的服务。SSH、防火墙和网络变更前,保留当前管理会话,同时准备能独立工作的控制台或救援入口。旧会话保持连接,只能帮助修复,不能验证新连接是否仍被允许。
管理、发布和应用运行使用不同权限
| 身份 | 需要的操作 | 典型权限 |
|---|---|---|
| 管理用户 | 安装包、改 unit、挂载磁盘、维护系统 | 经授权使用 sudo,必要时等价于 root |
| 发布用户或流水线 | 上传、切换指定应用制品 | 仅写发布区;受控发布程序完成切换 |
| 应用服务用户 | 读制品和配置,写自己的状态 | 无交互登录,不得修改 unit 或启动程序 |
Ubuntu 新建完整管理账号时,可执行 sudo adduser opsadmin,再执行 sudo usermod -aG sudo opsadmin。该账号的 sudo 组权限就是系统管理权限。发布账号不应因为名称叫 deploy 就自动加入 sudo 组或 Docker 组。
授予指定服务重启权限也要检查间接控制权:用户如果能改该服务的 root 启动脚本、unit 或读取后执行的配置,就可能借“仅允许重启”执行任意 root 命令。受限 sudo 规则应连同文件所有者和允许参数一起审查。用 sudo visudo -f /etc/sudoers.d/规则文件 编辑,再用 sudo visudo -c 检查所有入口;语法与匹配规则见 Ubuntu sudoers 手册。
SSH 收紧要先在新连接中验证目标用户的公钥登录和所需 sudo 权限,再修改认证策略。禁止密码认证时还需考虑 keyboard-interactive、PAM 和 Match 条件;直接沿用 SSH 服务端配置与验证,不要另建一套互相覆盖的配置。
接通时间、解析和软件依赖
主机名与时间分别承担什么职责
主机名用于本机识别、日志和资产关联;给服务器改名不会自动创建 DNS 记录。只有主机名由当前管理员维护时,才执行例如:
sudo hostnamectl set-hostname app-node-01
hostnamectl --static
getent hosts app-node-01hostnamectl --static 应返回新名称,getent 检查操作系统解析链能否解析它。解析无输出时,根据真实地址规划更新 DNS 或 /etc/hosts,不要把公网地址、回环地址和服务发现记录任意互换。cloud-init 管理的主机名则调整其输入,避免重启后改回。
时间同步决定系统时钟与时间源的偏差;时区主要决定时间如何显示。团队统一用 UTC 的主机可设置:
sudo timedatectl set-timezone UTC
timedatectl status日志显示时间不同,并不必然是时钟漂移。真正的偏差会影响证书有效期判断、令牌校验和跨机器事件排序。先确认正在运行的校时服务:
systemctl is-active systemd-timesyncd.service
systemctl is-active chrony.service未安装或未运行的那一项可以返回非零。Ubuntu 24.04 使用 timesyncd 的机器,查看 timedatectl timesync-status;使用 chrony 的机器,查看 chronyc tracking 和 chronyc sources -v。不要为统一命令再安装第二套校时程序。Ubuntu 校时说明解释了 timesyncd 的工作与状态查看。
内网使用指定 NTP 时,以已经启用 timesyncd 的机器为例:用 sudo install -d -m 755 /etc/systemd/timesyncd.conf.d 创建目录,以 sudoedit /etc/systemd/timesyncd.conf.d/60-site.conf 保存以下文件,将名称替换为实际批准的时间源:
[Time]
NTP=ntp.internal.example.com
FallbackNTP=sudo systemctl restart systemd-timesyncd.service
timedatectl timesync-status
timedatectl show -p NTPSynchronized等待取得样本后,应能看到选定的服务器和 NTPSynchronized=yes。如果仍选择其他时间源,还要检查 DHCP 或链路配置提供的 NTP 服务器;全局文件不是唯一来源。服务 active 但尚未同步时,查解析、路由、时间源服务及 UDP 123 的往返策略,而不是仅查看本机有没有监听 123。chrony 的源配置与 ^* 被选中、^+ 参与合并等标记见 chrony 文档。
新机明显走时错误应在应用接入前修正;已有数据库或定时任务运行时,不随意执行强制跳时命令。先确认允许的维护方式,避免时钟回退影响业务。
DNS 要改在实际管理它的地方
readlink -f /etc/resolv.conf
resolvectl status
getent ahosts archive.ubuntu.comUbuntu 使用 systemd-resolved 时,/etc/resolv.conf 可能指向包含 127.0.0.53 的 stub 文件。这个回环地址由本机解析服务接收查询,实际 DNS 服务器在 resolvectl status 的各链路下查看。手工覆盖生成文件会绕过或破坏原配置关系。
网络由 Netplan 管理时,用 sudo netplan get 确认接口名、renderer 和合并后的配置;同时查看 /etc/netplan/ 下文件的生成说明。下面只展示单网卡、networkd、DHCPv4 环境中的对应配置;接口 ens3 和 DNS 地址必须换成实际值,并合并进原文件,保留原有路由、MTU、IPv6 等设置:
network:
version: 2
renderer: networkd
ethernets:
ens3:
dhcp4: true
dhcp4-overrides:
use-dns: false
nameservers:
addresses: [10.20.0.53, 10.20.0.54]use-dns: false 控制 DHCPv4 提供的 DNS;双栈网络还要按实际 DHCPv6、RA 和 renderer 处理 IPv6 DNS,不能据此声称所有 DNS 来源都已关闭。字段与合并规则见 Netplan YAML 配置。
备份即将编辑的 Netplan 文件后,在仍有控制台入口的情况下执行:
sudo netplan generate
sudo netplan try --timeout 120generate 先检查并生成配置,不切换网络。try 临时应用后等待确认;在另一个终端新建 SSH 连接,并验证内部域名和软件源,成功后才确认保留。超时或取消后,核对路由、DNS 和磁盘上的 YAML 是否恢复;失败时从备份恢复配置,重新检查。不要在失败后紧接一条无条件 netplan apply 把错误设置重新应用。Netplan try 文档也提示了回退状态需要核对。
NetworkManager 管理的主机应修改连接 profile;企业内网名称交给企业解析器,公共 DNS 未必能解析,也可能使内部名称泄漏到外部。
软件源、代理和 CA 共同决定能否安装
Ubuntu 24.04 的源可能位于 /etc/apt/sources.list.d/ubuntu.sources,不能只检查旧的 sources.list。确认发行版代号、镜像源维护者、签名配置与网络可达后执行:
sudo apt-get update
apt-cache policy curl ca-certificates python3
sudo apt-get install --no-install-recommends curl ca-certificates python3交互确认安装计划,关注包来源、将被删除的包和可能重启的服务。update 更新索引,install 安装选定包;发行版升级另有步骤,不能由更换源地址顺带完成。Ubuntu 包管理文档提供操作和包状态查询。
常见依赖失败按以下位置判断:
| 报错 | 优先检查 | 修正后重试 |
|---|---|---|
| 名称无法解析 | getent ahosts、链路 DNS、内网解析权限 | 同一个主机名解析与安装命令 |
| 连接超时 | 路由、代理要求、出口 ACL;不要先关闭防火墙 | 原目标和原协议 |
| 证书验证失败 | 系统时间、证书名称和链、企业 CA | 不带跳过校验选项的请求 |
| 仓库签名或 Release 不匹配 | 源地址、发行版代号、受信任签名钥匙 | apt-get update 无对应错误 |
| 下载成功但安装缺依赖 | 架构、版本、依赖闭包与目标机已装包 | 在相同基线的新环境重新安装 |
如果网络必须经过代理,分别确认当前终端、sudo 后的环境、APT 和未来 systemd 服务的代理配置。只在终端执行 export HTTPS_PROXY=... 不会自动给系统服务配置代理。代理账号和令牌不应出现在可读的命令历史或公开输出中。
直连被允许时,可以用不继承 curl 默认配置的请求辅助判断:
curl -q --noproxy '*' --fail-with-body --connect-timeout 5 --max-time 20 \
-I https://archive.ubuntu.com/ubuntu/要求使用代理的网络则显式传入已批准的 --proxy,不要把直连失败解释成源站故障。curl 参数语义见 curl 官方手册。
企业 CA 通过安全渠道交付并核对指纹后,Ubuntu 可将单张 PEM 格式 CA 证书安装为 /usr/local/share/ca-certificates/company-root.crt,再执行 sudo update-ca-certificates。从管理用户当前目录的已核对文件安装:
sudo install -o root -g root -m 644 company-root.crt \
/usr/local/share/ca-certificates/company-root.crt
sudo update-ca-certificates信任根证书会扩大系统信任范围,不能为消除报错随意导入对端提供的文件。Java、容器镜像或独立运行时还可能使用自己的证书库,需要分别检查。Ubuntu CA 信任库文档说明了系统级安装方法。
完全离线时,准备同发行版、同架构、同版本约束的受信任本地仓库或完整依赖包集合,连同校验信息和原版本一起交付。联网机器执行一次 apt download 得到的几个包可能依赖其本机已有组件;应在相同基线的干净环境安装复验。apt-get -f install 仍可能需要仓库,不能当作离线修复保证。RHEL 系使用 DNF 及其仓库签名机制,按 DNF 管理文档准备源和包,不将两种包管理器写成失败后互相替代的命令。
分开制品、配置和可写数据
目录所有者决定应用能修改什么
应用目录可以采用下面的分工,具体容量和路径随部署方式调整:
/opt/myapp/releases/ 发布制品:运行用户只读
/opt/myapp/current 当前版本链接:由受控发布程序切换
/etc/myapp/ 运行配置:管理员维护,按需授予应用读取
/var/lib/myapp/ 应用状态:仅应用和管理员可写
/var/log/myapp/ 文件日志:仅采用文件日志时创建
/run/myapp/ PID/socket 等临时状态:重启后重建不要对整个 /opt、/var 或共享数据盘执行递归 chown app。给应用整个制品目录写权限,会使进程漏洞进一步变成启动文件被篡改;把凭据设成所有用户可读,也不能由“目录只有管理员常用”解释为安全。
下面的探针采用独立服务用户 initprobe。先检查名称是否已经被使用:
getent passwd initprobe
getent group initprobe两项均无结果时创建;若已有结果,先核对其用途,不复用未知账号:
sudo useradd --system --user-group --no-create-home \
--home-dir /nonexistent --shell /usr/sbin/nologin initprobe
id initprobeUID/GID 由系统分配,不要求每台机器相同;共享存储、容器挂载或备份还原要求固定数字身份时,需要提前统一。nologin 阻止常规交互 shell,服务仍可通过 User=initprobe 运行。
挂载成功之前,不向目标目录写业务数据
根文件系统足以承载小型无状态服务时,可以使用系统默认目录;持续增长的业务数据、容器存储或日志需要另做容量规划。独立磁盘解决容量和管理隔离,不自动提供异机备份。
为已有文件系统安排挂载前,先通过设备序列号、云盘标识或虚拟机磁盘配置确认目标,再核对 UUID、类型、已有挂载及数据用途:
lsblk -o NAME,SIZE,TYPE,FSTYPE,UUID,MOUNTPOINTS,MODEL,SERIAL
sudo blkid
findmnt/dev/vdb 只是设备名,可能变化;无签名也不等于允许销毁。需要新建文件系统的专用空盘,按 Linux 磁盘与文件系统操作逐步确认设备和格式化授权,不把格式化塞进通用初始化脚本。
假设已确认一个允许接入的 ext4 文件系统,拟挂载到 /srv/appdata。先创建空挂载点并检查有无文件:
sudo install -d -o root -g root -m 755 /srv/appdata
sudo find /srv/appdata -mindepth 1 -maxdepth 1 -print发现已有文件时停止,查明它们的用途。挂载会遮住原目录内容,而不会替你把数据搬到新盘。确认目录为空后,用 sudoedit /etc/fstab 加入一行,将 UUID 替换为 blkid 的实际值:
UUID=实际文件系统UUID /srv/appdata ext4 defaults,nofail 0 2这里选择 nofail,让该盘缺失时主机仍有机会正常启动,以便远程修复;使用数据盘的应用稍后单独设置启动保护。要求整机必须等待该盘的系统应采用不同启动策略。字段和启动选项见 fstab 手册与 systemd.mount 手册。
sudo findmnt --verify --verbose
sudo systemctl daemon-reload
sudo mount /srv/appdata
findmnt --mountpoint /srv/appdata -o TARGET,SOURCE,FSTYPE,OPTIONS
df -hT /srv/appdatafindmnt --verify 检查挂载表,不实际完成挂载;mount 只操作这一个明确目标。最后核对 SOURCE、类型和预期盘身份。findmnt -T /srv/appdata 在目录没有独立挂载时也可能显示根文件系统,所以这里用 --mountpoint 检查挂载点本身。
确认后才在新文件系统下创建应用子目录,并只授权该子目录。挂载失败则停止依赖服务,恢复刚改的 fstab 条目。如果自备份以来只有本次修改,可以先比较再恢复整份文件:
sudo diff -u "${BACKUP_DIR:?}/fstab" /etc/fstabdiff 返回 1 表示有差异,并非读取失败。确认只有预期变更后执行:
sudo cp -a "${BACKUP_DIR:?}/fstab" /etc/fstab
sudo findmnt --verify --verbose
sudo systemctl daemon-reload如果还有其他人的有效变更,只撤销自己的条目。恢复 fstab 不会卸载已经挂载的盘;卸载前要停止实际使用者并确认没有打开文件,不使用强制卸载掩盖问题,也不带着未知挂载错误重启。
容器主机还要区分镜像层、卷和日志
Docker 的镜像、容器可写层、命名卷和日志会占用宿主机空间。新主机应先确定存储位置,再创建业务容器;已运行主机修改数据目录则涉及停机、完整迁移、所有权和回滚,不只是改一个 JSON 字段。
docker info --format '{{.DockerRootDir}}'
docker system df这些命令需已有 Docker 管理权限。不要为了读两项状态,给应用用户加入 root 等价的 Docker 管理组。data-root 的覆盖范围还取决于使用的存储后端;containerd image store 有独立存储位置,应按 Docker daemon 配置确认。安装与迁移详见 Docker Engine。
给日志驱动配置大小和文件数量限制,采用与日志采集方式兼容的驱动;守护进程默认值变化通常只影响新建容器,已有容器需要核对并按计划重建。Docker 日志配置列出这些行为。数据库卷和备份未识别前,不执行通用清理命令释放空间。
用一个服务检查身份、资源和启动条件
保存 unit,并完成第一次请求
探针使用已经安装的 Python 3 标准库,在回环地址提供一份固定文本。它用于检查系统配置,不接收业务写入,也不作为生产 HTTP 服务。先确认名称、文件和端口没有被占用:
systemctl cat baseline-probe.service
sudo ls -ld /var/lib/baseline-probe
sudo ss -lntp 'sport = :18080'新实验中前两项应提示不存在,最后一项没有监听记录。若已存在,查明来源后换用独立实验机,不覆盖未知文件。用 sudoedit /etc/systemd/system/baseline-probe.service 保存:
[Unit]
Description=Host initialization probe
[Service]
Type=exec
User=initprobe
Group=initprobe
StateDirectory=baseline-probe
StateDirectoryMode=0750
WorkingDirectory=/var/lib/baseline-probe
ExecStartPre=/usr/bin/sh -c 'echo baseline-ready > /var/lib/baseline-probe/index.html'
ExecStart=/usr/bin/python3 -u -m http.server 18080 --bind 127.0.0.1 --directory /var/lib/baseline-probe
Restart=on-failure
RestartSec=3
UMask=0027
LimitNOFILE=4096
TasksMax=64
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.targetunit 由 root 维护,两个启动命令都以 initprobe 运行。StateDirectory 让 systemd 创建并授权 /var/lib/baseline-probe;ProtectSystem=strict 将服务视角的文件系统设为只读时,这个受管理状态目录仍可写。PrivateTmp 提供独立临时目录,NoNewPrivileges 限制后续执行获得新权限。参数规则见 systemd.exec 手册。
这里没有 network-online.target:绑定回环地址的探针不需要等外部网络。真正依赖远端服务的应用还需处理依赖暂时不可达;启动顺序本身无法保证数据库、DNS 或业务账号已经可用。
sudo systemd-analyze verify /etc/systemd/system/baseline-probe.service
sudo systemctl daemon-reload
sudo systemctl enable --now baseline-probe.service
systemctl status baseline-probe.service --no-pager
curl -q --noproxy '*' --fail-with-body --connect-timeout 3 --max-time 5 \
http://127.0.0.1:18080/语法检查无相关错误后再启动。正常状态为 active (running),请求正文为:
baseline-readyType=exec 确认启动程序已成功执行,监听端口可能稍后才就绪;刚启动的请求若连接失败,查看状态与日志,确认没有启动错误后再请求。HTTP 返回成功且正文匹配,说明此次请求读到了指定状态文件;它不涉及数据库、公网入口或真实业务功能。
对照 unit 与实际进程
systemctl show baseline-probe.service \
-p User -p MainPID -p LimitNOFILE -p LimitNOFILESoft -p TasksMax
PID=$(systemctl show baseline-probe.service -p MainPID --value)
test "$PID" -gt 0
ps -p "$PID" -o user,pid,comm
sudo grep 'Max open files' "/proc/$PID/limits"
sudo stat -c '%U:%G %a %n' /var/lib/baseline-probe \
/var/lib/baseline-probe/index.html若 test 失败,服务没有有效主进程,应停止执行后面的进程检查。正常情况下,进程用户为 initprobe,文件描述符软硬上限均为 4096;目录为 750,新建文本文件在 UMask=0027 下为 640。
交互终端运行的 ulimit -n 属于该 shell 及其子进程。由系统 systemd 启动的服务不会因为你改了这个终端的值而跟着变化:
| 配置位置 | 主要作用对象 | 查看实际结果 |
|---|---|---|
shell 的 ulimit | 该 shell 后续启动的子进程 | 子进程 /proc/PID/limits |
PAM limits.conf | 经相关 PAM 会话建立的登录进程 | 新登录后的限制 |
unit 的 LimitNOFILE | 该服务启动的进程 | unit 属性与实际 PID 的 limits |
unit 的 TasksMax | 该服务 cgroup 内的任务,包括线程 | systemctl show 与 cgroup 计数 |
Linux 的 LimitNPROC 按真实 UID 计数,不能简单当作“该服务最多几个进程”;服务级线程/进程数量控制通常看 TasksMax。cgroup 控制见 systemd 255 资源控制定义。
4096 和 64 是这个小探针的演示配置。Java 服务的连接池、线程池和运行时线程会改变需求,不照搬该值;先测正常峰值与异常增长,再留合理余量。出现 Too many open files 时还需检查描述符是否泄漏,增大上限只会延后泄漏导致的失败。
修改 unit 后,daemon-reload 只让管理器重读文件;需要在允许中断的时机重启服务,重新取 PID、检查限制和请求结果。不要继续读旧 PID 的输出作为新配置结果。
数据盘缺失时拒绝启动
只要 /srv/appdata 的挂载消失,这个路径就会露出根文件系统上的普通目录。如果应用继续向其中写数据,根盘可能被写满;数据盘恢复挂载后,这批误写文件又会被遮住。
对于必须使用该盘的真实应用,在它的 unit 的 [Unit] 中声明:
[Unit]
RequiresMountsFor=/srv/appdata
AssertPathIsMountPoint=/srv/appdataRequiresMountsFor 建立到访问该路径所需挂载的依赖和顺序;AssertPathIsMountPoint 再检查该路径本身确实是挂载点。如果 fstab 中对应条目被误删,只依赖父目录的挂载仍可能满足,第二项用于拦住这个遗漏。二者都不验证盘内业务数据是否正确,也不是持续监测 I/O 健康的替代品。条件、断言和挂载依赖见 systemd.unit 手册。
可以在专用虚拟机中用一个空 tmpfs 验证启动保护,不接触任何块设备。这个实验路径与真实数据盘无关,内容随卸载消失。确认 /srv/init-mount-probe 尚不存在,然后创建:
sudo install -d -o root -g root -m 755 /srv/init-mount-probe
sudo install -d -m 755 /etc/systemd/system/baseline-probe.service.d
sudoedit /etc/systemd/system/baseline-probe.service.d/30-mount-test.conf在该文件保存:
[Unit]
RequiresMountsFor=/srv/init-mount-probe
AssertPathIsMountPoint=/srv/init-mount-probe先不挂载,执行反例:
sudo systemctl stop baseline-probe.service
sudo systemctl daemon-reload
sudo systemctl start baseline-probe.service
systemctl show baseline-probe.service -p ActiveState -p AssertResult
sudo journalctl -u baseline-probe.service -n 15 --no-pagerstart 应返回非零,AssertResult=no,日志指向挂载断言失败,服务未进入运行。不要只依赖某个 status 文案;配置断言拒绝启动与进程启动后崩溃是不同阶段。
再挂载测试文件系统,使用相同服务重试:
sudo mount -t tmpfs -o size=16m,mode=0755 tmpfs /srv/init-mount-probe
findmnt --mountpoint /srv/init-mount-probe
sudo systemctl start baseline-probe.service
curl -q --noproxy '*' --fail-with-body --max-time 5 http://127.0.0.1:18080/此时应该得到 baseline-ready。这组正反结果检查的是“挂载条件是否影响启动”;探针文件仍在原状态目录中,不以 tmpfs 模拟持久存储。
实验完成后先停服务、卸载空 tmpfs,再删除本次创建的精确配置文件和空目录:
sudo systemctl stop baseline-probe.service
sudo umount /srv/init-mount-probe
sudo rm /etc/systemd/system/baseline-probe.service.d/30-mount-test.conf
sudo rmdir /srv/init-mount-probe
sudo systemctl daemon-reload
sudo systemctl start baseline-probe.service任一步失败都先检查原因,不跳过卸载而继续删路径。撤掉临时断言后再进行后面的重启测试;真实应用的持久数据盘保护应保留。
只开放计划中的网络入口
监听地址先于防火墙规则
探针只监听 127.0.0.1:18080,外部客户端不应能直接访问。查看:
sudo ss -lntp 'sport = :18080'若本机回环请求成功,但远端访问该端口失败,这与配置一致,不需要放行防火墙。正式服务按入口设计绑定内网地址或由反向代理接入:应用端口只对代理或内网客户端可达,公网通常访问单独的 HTTPS 入口。
IPv4 的 0.0.0.0 和 IPv6 的 :: 是不同监听形式;确认应用是否双栈以及系统的 IPv6 策略。Docker 发布端口还经过转发与 NAT 路径,应按 Docker 包过滤与防火墙说明检查,不能仅根据主机 UFW 入站规则判断容器是否公开。
在新 Ubuntu 主机上启用 UFW
Ubuntu 常用 UFW,RHEL 系常用 firewalld,底层可能由 nftables 执行。先识别当前管理工具和已有规则,保持一个明确的配置入口,不叠加一套手写规则再要求另一套前端管理它。
以下操作仅用于确认尚未启用 UFW、没有其他防火墙管理器的新实验主机;已有规则的服务器逐条变更。管理 SSH 假设监听 TCP 22,且已准备控制台。安装后先检查和备份:
sudo apt-get install ufw
sudo ufw status verbose
sudo cp -a /etc/ufw "$BACKUP_DIR/ufw"
sudo cp -a /etc/default/ufw "$BACKUP_DIR/ufw-default"把 ADMIN_CIDR 换成服务器实际看到的管理出口地址或受信任管理网段;经过堡垒机、VPN 或 NAT 时,不一定是电脑自己的地址。示例文档网段不会提供真实接入权限。
ADMIN_CIDR='192.0.2.10/32'
sudo ufw allow proto tcp from "$ADMIN_CIDR" to any port 22
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw show added
sudo ufw enable在确认提示前核对源地址与实际 SSH 端口。随后从预期管理网络新建、不复用旧连接的 SSH 会话,验证登录和 sudo,并查看 sudo ufw status numbered。有 IPv6 管理路径时必须另外配置实际 IPv6 来源。只保留当前会话而没有新连接成功,不继续收紧规则。Ubuntu UFW 文档给出了源地址和端口规则用法。
新连接失败时,从仍有效的管理会话或控制台检查监听、实际来源和规则。这个实验原来是 UFW 未启用,因此可执行 sudo ufw disable 恢复原启用状态,再修改错误规则;已有防火墙的生产主机应恢复原规则,不能套用“全部禁用”。不要开启宽泛的公网规则来代替定位。
业务端口在服务、认证和入口方案已经确定后逐项开放。云安全组仍是独立的一层,相关路径见 云服务器与安全组。服务器出站策略若为默认拒绝,还需为实际 DNS、NTP、软件仓库、监控与业务依赖列出目的地;上面的允许出站策略不适合直接替换这种受限网络。
保留日志、监控和可恢复配置
日志需要跨过一次重启
探针的标准输出和错误进入 journal:
sudo journalctl -u baseline-probe.service -n 30 --no-pager
sudo journalctl --disk-usage
sudo journalctl --list-boots没有持久 journal 的主机,重启后可能只剩当前启动的日志。需要保留本机历史时,先检查现有配置和空间,以 sudo install -d -m 755 /etc/systemd/journald.conf.d 创建目录,用 sudoedit /etc/systemd/journald.conf.d/60-retention.conf 保存:
[Journal]
Storage=persistent
SystemMaxUse=256M
SystemKeepFree=512M
RuntimeMaxUse=64M这组容量用于小型实验机;生产按日志速率、保留时间和根盘预算调整。SystemMaxUse 控制持久日志使用量,SystemKeepFree 为文件系统保留空间;它们不会替其他写入者兜底,也不能保证磁盘永不写满。journald.conf 手册说明了持久与临时日志配置。
sudo systemctl restart systemd-journald.service
sudo journalctl --flush
sudo journalctl --disk-usage
logger -t init-baseline 'before-reboot-check'
sudo journalctl -t init-baseline -n 1 --no-pager重启 journald 本身不等于重启应用。后面使用上一启动周期的标记验证持久保存,而不只看 /var/log/journal 目录存在。
采用文件日志的应用,应优先使用应用自己的滚动机制,或用 logrotate 配合应用重新打开文件。copytruncate 在复制与截断之间存在丢失日志的窗口,而且该方式下 create 不承担重新创建当前文件的职责;不能不加区分地复制一份通用模板。参数见 logrotate 官方手册。安全事件与关键业务日志还要发送到独立系统,避免本机损坏或被入侵时同时失去历史。
监控既要看采集,也要看通知
最初需要关注 CPU 与负载、内存和交换、文件系统空间与 inode、磁盘 I/O、网络错误,以及关键服务的存活和请求结果。对磁盘告警同时关联挂载点和来源,避免数据盘掉线后仅监测到根盘的剩余空间。
采用 Prometheus 时,按 Node Exporter 官方接入安装适合平台的版本,以受限身份运行,限制监听地址和采集端来源。它提供主机指标;应用失败率、数据库状态和外部请求成功率仍需对应采集与探测。
如果已配置 Node Exporter 监听本机默认端口,可在服务器执行:
curl -q --noproxy '*' --fail-with-body --max-time 5 \
http://127.0.0.1:9100/metrics应看到指标文本和 node_ 指标。之后到实际监控端确认目标为 UP、样本持续更新,执行一条批准的测试告警并确认通知到达值班渠道。只在本机读取 metrics,还缺少远端采集和告警接收两段。输出中的挂载路径、设备和主机标签可能泄漏内部信息,不直接贴到公开页面。
更新、备份和初始化脚本如何维护
安全更新需要明确维护窗口、服务重启和内核重启的安排。Ubuntu 新机先查看待更新包,再按计划更新;自动更新策略用 Ubuntu 自动更新文档配置,不为避免意外重启永久关闭安全补丁。更新完成后用 uname -r 确认实际运行内核,而不只看新内核包已安装。
配置备份至少包含系统配置差异、应用 unit、包与版本信息、挂载关系和权限。机密信息独立加密保存;不要把凭据放进普通 Git 仓库。位于同一主机的 /backup 目录适合临时存放,不承担整机丢失后的恢复职责。
有业务数据时,再按数据类型选择备份:普通配置可以复制,正在写入的数据库需要数据库一致性备份或经过协调的快照。恢复测试应在隔离目标上完成,避免恢复出的服务使用生产短信、支付和回调凭据。配置恢复与业务数据恢复是两项不同的操作。
一套可重复初始化流程,应把输入写清楚:发行版、目标账号、目录所有者、软件源、时间源、允许的管理来源及所需服务。重复执行时检查现有值,匹配则不改,不匹配则报告差异;“文件已存在就忽略”会保留错误权限,“失败后 || true”会把半成品当作成功。
批量自动化适合接管已验证的包、目录和配置文件操作。设备格式化、既有数据迁移、身份收紧和网络切换分别保留明确授权与恢复流程。用配置管理工具之前先在一台专用机器验证内容,再扩展到一小组,避免把一次手工失误同步到所有主机。
重启、排障与恢复
重启后重新检查持久配置
先确认临时挂载实验已清理,fstab 检查通过,SSH 新连接正常,救援入口可用,且当前主机允许中断。在服务器执行:
sudo systemctl rebootSSH 断开符合预期。等待系统启动后从新的连接登录,执行:
hostnamectl --static
systemctl --failed --no-pager
systemctl is-enabled baseline-probe.service
systemctl is-active baseline-probe.service
curl -q --noproxy '*' --fail-with-body --max-time 5 http://127.0.0.1:18080/
sudo journalctl -b -1 -t init-baseline --no-pager
sudo journalctl -b -u baseline-probe.service -n 20 --no-pager服务应为 enabled、active,HTTP 正文仍为 baseline-ready,上一启动周期能够查到 before-reboot-check。还要复查实际使用的 NTP 同步、内部 DNS、数据盘来源和防火墙;没有独立数据盘的主机不需要制造一个挂载检查目标。
正式应用替代探针后,再检查应用的运行账号、数据读写、依赖访问和真实请求。机器通过系统检查,只是具备部署条件,不能据此宣布业务已经可用。
服务启动失败时,先确定失败阶段
sudo systemctl status baseline-probe.service --no-pager
sudo journalctl -b -u baseline-probe.service -n 50 --no-pager
systemctl cat baseline-probe.service| 观察结果 | 影响与定位 | 修正和复测 |
|---|---|---|
AssertResult=no、未产生主进程 | 启动条件没有满足;查精确挂载点及 fstab 生成的依赖 | 恢复正确挂载,不删除真实保护;重新启动并请求 |
203/EXEC | 程序未成功执行;查路径、可执行权限、解释器及挂载选项 | 修正程序或 unit;重载、启动,检查实际 PID |
217/USER | 服务身份设置失败;查 getent passwd initprobe 及组 | 恢复预期身份,不改为 root 掩盖错误 |
Permission denied | 对指定文件或目录的访问失败 | 查目录逐级权限、unit 沙箱及 AppArmor/SELinux 拒绝,再做最小授权 |
Address already in use | 目标端口已被其他进程占用 | 用 ss 找到所有者,调整计划;不直接杀未知进程 |
| 启动限速触发 | 多次失败重启已达到限制 | 先修根因,再 sudo systemctl reset-failed baseline-probe.service 后启动 |
查看路径权限可用 namei -l /var/lib/baseline-probe/index.html。用 sudo -u initprobe 测文件权限只模拟用户身份,不复现 unit 的完整挂载沙箱;最终还需由服务自身启动复测。RHEL 系还应检查 SELinux 拒绝记录和文件标签,不以永久关闭 SELinux 作为默认修复;Ubuntu 则检查应用是否受 AppArmor profile 约束。
改错 unit 时,恢复此前保存的该文件或移走刚增加的特定 drop-in,运行 systemd-analyze verify、daemon-reload 和服务重启,再对照新 PID 的状态。不能把删除整个 drop-in 目录当作通用回滚,它可能含有别人的设置。
网络改坏与数据盘异常采用不同恢复入口
SSH 不通时,先从客户端区分名称解析失败、连接超时、拒绝连接和认证失败,详细步骤见 SSH。通过控制台检查实际地址、路由、监听和防火墙。如果错误来自 Netplan,恢复刚改的文件并检查其生成源,确认磁盘配置正确后重新应用;如果来自 UFW,恢复相应来源规则。不要同时修改两层后失去定位依据。
数据盘异常时,先停止写入者,检查 findmnt --mountpoint、lsblk -f 和内核日志,区分盘未连接、挂载配置错误、文件系统错误或只读保护。只读通常需要处理存储或文件系统原因,不直接强制 remount 为可写。文件系统修复需要明确设备、备份和离线状态,按 Linux 存储排障执行。
如果应用已经误写根盘目录,恢复挂载会遮住那些文件。先停止应用,在安全卸载或独立维护环境下分别检查根盘目录与数据盘内容,决定保留和合并方式;不要为了清空间直接删除目录。
移除实验探针
完成重启与观察后,移除探针服务文件,但先保留生成内容供检查:
sudo systemctl disable --now baseline-probe.service
sudo rm /etc/systemd/system/baseline-probe.service
sudo systemctl daemon-reload
sudo ss -lntp 'sport = :18080'监听记录应消失。确认该账号和目录仅用于实验后,再删除精确文件、空目录与账号:
sudo rm /var/lib/baseline-probe/index.html
sudo rmdir /var/lib/baseline-probe
sudo rmdir /etc/systemd/system/baseline-probe.service.d
sudo userdel initprobe只有执行过挂载实验才会有上述 drop-in 目录;未执行时略过对应 rmdir。目录非空时先检查剩余文件,不升级成递归删除。日志、系统更新、管理访问和真实数据盘配置不随探针一起撤销。
权威资料与规范地址
主机身份与依赖
- cloud-init 模块与配置频率:https://docs.cloud-init.io/en/latest/reference/modules.html
- sudoers 语法与授权:https://manpages.ubuntu.com/manpages/noble/en/man5/sudoers.5.html
- Ubuntu 时间同步:https://ubuntu.com/server/docs/how-to/networking/timedatectl-and-timesyncd/
- chrony 配置与状态:https://chrony-project.org/documentation.html
- Netplan YAML:https://netplan.readthedocs.io/en/stable/netplan-yaml/
- Netplan try 与回退:https://netplan.readthedocs.io/en/stable/netplan-try/
- Ubuntu 包管理:https://ubuntu.com/server/docs/how-to/software/package-management/
- Ubuntu CA 信任库:https://ubuntu.com/server/docs/how-to/security/install-a-root-ca-certificate-in-the-trust-store/
- curl 参数:https://curl.se/docs/manpage.html
- RHEL 9 DNF:https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/managing_software_with_the_dnf_tool/
挂载与服务运行
- fstab:https://manpages.ubuntu.com/manpages/noble/en/man5/fstab.5.html
- systemd.mount:https://manpages.ubuntu.com/manpages/noble/en/man5/systemd.mount.5.html
- systemd.exec:https://manpages.ubuntu.com/manpages/noble/en/man5/systemd.exec.5.html
- systemd.unit:https://manpages.ubuntu.com/manpages/noble/en/man5/systemd.unit.5.html
- systemd 255 资源控制:https://github.com/systemd/systemd/blob/v255/man/systemd.resource-control.xml
- Docker daemon 与存储:https://docs.docker.com/engine/daemon/
- Docker 日志驱动:https://docs.docker.com/engine/logging/configure/
网络、日志和持续维护
- Ubuntu UFW:https://ubuntu.com/server/docs/how-to/security/firewalls/
- Docker 防火墙路径:https://docs.docker.com/engine/network/packet-filtering-firewalls/
- journald.conf:https://manpages.ubuntu.com/manpages/noble/en/man5/journald.conf.5.html
- logrotate:https://github.com/logrotate/logrotate/blob/main/logrotate.8.in
- Node Exporter 接入:https://prometheus.io/docs/guides/node-exporter/
- Ubuntu 自动更新:https://ubuntu.com/server/docs/how-to/software/automatic-updates/
