4.13 Linux 服务器初始化标准流程
预计阅读时间:11 分钟
📖 目录
学习目标
学完本章后,你将能够:
- 理解服务器初始化标准化的必要性——一致性、可审计、自动化基础
- 按规范设置主机名、时区、系统更新和 swap
- 配置 UFW 防火墙和 SSH 加固
- 创建管理员用户并部署 SSH 密钥认证
- 安装并配置 fail2ban、监控 agent 和备份脚本
- 编写一份完整的一键初始化脚本
前置知识
- 1.7:用户与权限管理 用户与权限管理——创建管理员用户与 sudo 授权的基础
- 1.8:软件包管理 软件包管理——apt 安装/更新系统软件的前提
- 2.11:SSH 深入 SSH 深入——SSH 密钥认证与远程连接配置
- 3.12:系统监控与告警 系统监控与告警——理解 node_exporter 与监控 agent 的作用
核心知识
- 主机名规范——
${角色}-${环境}-${编号}.${域名},如web-prod-01.example.com - 时区与 NTP——
timedatectl设置时区,chrony/systemd-timesyncd同步时间 - unattended-upgrades——自动安装安全更新,减少漏洞窗口
- swap——磁盘模拟内存,防止 OOM;云服务器通常建议关闭或设置固定大小
- UFW 默认拒绝——白名单防火墙:只放行明确需要的端口
- SSH 加固——禁用 root 登录、禁用密码认证、仅密钥登录、限 MaxAuthTries
- 管理员用户——非 root 用户 + sudo 权限 + SSH 公钥部署
- fail2ban——日志驱动的动态防火墙,自动封禁暴力破解 IP
- node_exporter——Prometheus 生态的节点指标暴露器(CPU/内存/磁盘/网络)
- 备份三要素——备份什么(文件/DB)、存哪里(本地/远程)、怎么恢复(验证流程)
知识关联
- 前置依赖:1.7:用户与权限管理 用户与权限、1.8:软件包管理 软件包管理、2.11:SSH 深入 SSH 深入
- 后续影响:4.1:Ansible 自动化 Ansible 自动化运维(初始化脚本写成 Ansible playbook 后可批量执行),4.14:运维故障案例集 运维故障案例集(不合规的初始化是故障根源之一)
- 安全基础:5.1:Linux 安全加固 Linux 安全加固——本章的 SSH 加固、fail2ban、UFW、sysctl 参数全部源自 5.1:Linux 安全加固
- 深入阅读:3.12:系统监控与告警 系统监控方案、3.13:备份策略与恢复 备份策略与恢复、5.5:防火墙实战 nftables 防火墙
原理讲解
为什么要标准化
假设团队有 5 个人,每人用自己的方式初始化新服务器:A 用 SSH 默认端口、B 改成了 2222、C 忘了开防火墙、D 的备份只存本地磁盘、E 的 root 密码贴在 Wiki 上。三个月后出故障排查时,每台服务器的配置都不一样,无法快速定位问题。
标准化的价值体现在三个维度:
- 一致性(Consistency)——每台服务器从诞生起就遵循同一套配置基线,消除"这台和那台不一样"的隐患
- 审计轨迹(Audit Trail)——初始化过程可记录、可复现。出了问题可以回答"这台机器最初是怎么配的"
- 自动化基础(Automation Foundation)——有了标准流程,就能写成脚本 -> 做成 Ansible role -> 纳入 CI/CD Pipeline
10 步初始化流程概览
| 步骤 | 操作 | 目的 |
|---|---|---|
| Step 1 | 设置主机名 | 标识服务器角色,便于管理 |
| Step 2 | 配置时区与时间同步 | 日志时间一致,避免时间偏差 |
| Step 3 | 系统更新 + 自动安全更新 | 修复已知漏洞 |
| Step 4 | 配置 swap | 内存压力缓冲 |
| Step 5 | 创建管理员用户 + 部署 SSH 密钥 | 安全远程管理 |
| Step 6 | SSH 加固 | 缩小攻击面 |
| Step 7 | 配置 UFW 防火墙 | 默认拒绝入站流量 |
| Step 8 | 安装 fail2ban | 动态防护暴力破解 |
| Step 9 | 安装监控 agent | 采集性能指标 |
| Step 10 | 配置备份 | 数据恢复能力 |
示例代码
以下是一个完整的 Ubuntu 24.04 一键初始化脚本。按阶段组织,可直接在 root 用户下执行。建议先逐段理解,再整体运行。
完整初始化脚本
#!/bin/bash
set -euo pipefail
# ==============================================
# Stage 0: 变量定义 & 前置检查
# ==============================================
NEW_HOSTNAME="web-prod-01.example.com"
TIMEZONE="Asia/Shanghai"
ADMIN_USER="ops"
ADMIN_SSH_KEY="ssh-ed25519 AAAAC3... your-public-key-here"
SWAP_SIZE_MB=2048
# 仅在非 root 时提示
if [ "$(id -u)" -ne 0 ]; then
echo "请以 root 身份运行" >&2
exit 1
fi
# ==============================================
# Stage 1: 主机名
# ==============================================
echo "[1/10] 设置主机名..."
hostnamectl set-hostname "$NEW_HOSTNAME"
sed -i "/127.0.1.1/d" /etc/hosts
echo "127.0.1.1 $NEW_HOSTNAME" >> /etc/hosts
# ==============================================
# Stage 2: 时区与时间同步
# ==============================================
echo "[2/10] 配置时区与时间同步..."
timedatectl set-timezone "$TIMEZONE"
timedatectl set-ntp true
# 验证
timedatectl status # 输出: Local time: Wed 2026-07-30 02:00:00 CST
# ==============================================
# Stage 3: 系统更新 & 自动安全更新
# ==============================================
echo "[3/10] 系统更新..."
apt update && apt upgrade -y
apt install -y unattended-upgrades
dpkg-reconfigure -f noninteractive unattended-upgrades
# ==============================================
# Stage 4: Swap 配置
# ==============================================
echo "[4/10] 配置 swap..."
if swapon --show | grep -q .; then
echo "swap 已存在,跳过"
else
fallocate -l ${SWAP_SIZE_MB}M /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
# 降低 swappiness(优先使用内存)
sysctl vm.swappiness=10 # 输出: vm.swappiness = 10
echo 'vm.swappiness=10' > /etc/sysctl.d/99-swap.conf
fi
# ==============================================
# Stage 5: 创建管理员用户 & SSH 密钥
# ==============================================
echo "[5/10] 创建管理员用户..."
if id "$ADMIN_USER" &>/dev/null; then
echo "用户 $ADMIN_USER 已存在"
else
useradd -m -s /bin/bash -G sudo "$ADMIN_USER"
passwd -l "$ADMIN_USER" # 锁定密码(仅密钥登录)
fi
mkdir -p /home/$ADMIN_USER/.ssh
echo "$ADMIN_SSH_KEY" > /home/$ADMIN_USER/.ssh/authorized_keys
chmod 700 /home/$ADMIN_USER/.ssh
chmod 600 /home/$ADMIN_USER/.ssh/authorized_keys
chown -R $ADMIN_USER:$ADMIN_USER /home/$ADMIN_USER/.ssh
# ==============================================
# Stage 6: SSH 加固
# ==============================================
echo "[6/10] SSH 加固..."
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
sed -i 's/^#\?MaxAuthTries.*/MaxAuthTries 3/' /etc/ssh/sshd_config
sed -i 's/^#\?ClientAliveInterval.*/ClientAliveInterval 300/' /etc/ssh/sshd_config
sed -i 's/^#\?ClientAliveCountMax.*/ClientAliveCountMax 2/' /etc/ssh/sshd_config
# 白名单:仅允许管理员用户登录
echo "AllowUsers $ADMIN_USER" >> /etc/ssh/sshd_config
ssh-keygen -A # 确保主机密钥存在 # 输出: ssh-keygen: generating new host keys: RSA DSA ECDSA ED25519
sshd -t && systemctl reload sshd # 输出: (无错误则静默成功)
# ==============================================
# Stage 7: UFW 防火墙
# ==============================================
echo "[7/10] 配置 UFW 防火墙..."
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp comment 'SSH'
# 按需放行其他端口,例如:
# ufw allow 80/tcp comment 'HTTP'
# ufw allow 443/tcp comment 'HTTPS'
ufw --force enable
ufw status verbose # 输出: Status: active\nLogging: on (low)\nDefault: deny (incoming)...
# ==============================================
# Stage 8: fail2ban
# ==============================================
echo "[8/10] 安装 fail2ban..."
apt install -y fail2ban
cat > /etc/fail2ban/jail.local << 'F2BEOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
maxretry = 3
bantime = 86400
F2BEOF
systemctl enable --now fail2ban # 输出: Created symlink /etc/systemd/system/multi-user.target.wants/fail2ban.service
# ==============================================
# Stage 9: 监控 agent(node_exporter)
# ==============================================
echo "[9/10] 安装 node_exporter..."
NODE_EXPORTER_VER="1.9.0"
wget -q "https://github.com/prometheus/node_exporter/releases/download/v${NODE_EXPORTER_VER}/node_exporter-${NODE_EXPORTER_VER}.linux-amd64.tar.gz"
tar xzf "node_exporter-${NODE_EXPORTER_VER}.linux-amd64.tar.gz"
cp "node_exporter-${NODE_EXPORTER_VER}.linux-amd64/node_exporter" /usr/local/bin/
rm -rf "node_exporter-${NODE_EXPORTER_VER}.linux-amd64"*
# 创建 systemd 单元
cat > /etc/systemd/system/node_exporter.service << 'NEEOF'
[Unit]
Description=Prometheus Node Exporter
After=network.target
[Service]
Type=simple
User=nobody
Group=nogroup
ExecStart=/usr/local/bin/node_exporter
Restart=on-failure
[Install]
WantedBy=multi-user.target
NEEOF
systemctl daemon-reload
systemctl enable --now node_exporter # 输出: Created symlink /etc/systemd/system/multi-user.target.wants/node_exporter.service
# ==============================================
# Stage 10: 备份配置
# ==============================================
echo "[10/10] 配置备份..."
mkdir -p /var/backups/scripts
cat > /var/backups/scripts/backup.sh << 'BKPEOF'
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/var/backups/daily"
DATE=$(date +%F)
mkdir -p "$BACKUP_DIR"
# 备份关键配置
tar czf "$BACKUP_DIR/etc-$DATE.tar.gz" /etc/ssh/sshd_config /etc/fail2ban /etc/ufw /etc/sysctl.d/
# 备份应用数据(示例)
# tar czf "$BACKUP_DIR/app-$DATE.tar.gz" /var/www /var/lib/mysql
# 保留最近 30 天
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +30 -delete
# 推送远程(示例:rsync to backup-server)
# rsync -avz "$BACKUP_DIR/" backup@10.0.0.100:/backups/$(hostname)/
BKPEOF
chmod +x /var/backups/scripts/backup.sh
# 写入 cron(每天凌晨 2:00 执行)
echo "0 2 * * * root /var/backups/scripts/backup.sh" > /etc/cron.d/server-backup
# ==============================================
echo "✅ 初始化完成!建议重启服务器以确认所有配置生效。"
echo "⚠️ 在断开当前 SSH 前请用另一个终端验证密钥登录:"
echo " ssh ${ADMIN_USER}@"
常见错误
| 错误表现 | 根因 | 正确做法 |
|---|---|---|
| 运行初始化脚本后 SSH 断开无法再连接 | SSH 加固后密钥未部署正确即退出 | Step 5 和 Step 6 必须先验证新用户密钥登录再 reload sshd;脚本中建议在末尾重置 SSHD 连接前留出验证窗口 |
ufw enable 后 SSH 被拒 | UFW 默认 deny incoming 生效,但忘记 allow 22 | ufw allow 22/tcp 之后再 ufw enable |
| fail2ban 把自己封了 | 开发机从同一 IP 频繁测试触发规则 | 在 jail.local 中加入 ignoreip = 你的本机IP |
| swap 文件权限错误导致无法使用 | fallocate 生成的文件权限为 644 | 必须 chmod 600 /swapfile,否则 mkswap 会告警或拒绝 |
| node_exporter 无法绑定端口 | 低版本运行 User=nobody 时权限不足 | 使用 CapabilityBoundingSet=CAP_NET_BIND_SERVICE 或允许 nobody 监听非特权端口 |
| 时间同步失效,日志时间偏差大 | 仅设了时区,未启用 NTP 或 chrony 未运行 | timedatectl set-ntp true 或安装 chrony 并 systemctl enable --now chrony |
最佳实践
| 实践 | 原理 | 示例 |
|---|---|---|
| 主机名纳入 CMDB | 每台服务器的主机名、IP、用途、负责人记录到 CMDB 或 Ansible inventory | web-prod-01 ansible_host=10.0.0.1 role=web env=prod |
| 初始化脚本版本化管理 | 脚本存入 Git 仓库,变更走 PR 审查 | git tag v1.2.0 对应某次基线变更 |
| SSH CA 替代手动部署密钥 | 服务器端配置 TrustedUserCAKeys,用户证书集中签发,吊销即时生效 | TrustedUserCAKeys /etc/ssh/ca.pub |
| swap 根据内存大小动态计算 | 物理内存 < 2GB → swap = 2x 内存;2-8GB → swap = 1x;> 8GB → swap = 4-8GB | 脚本中根据 free -m 结果自动计算 SWAP_SIZE_MB |
| 备份必须验证恢复 | 没有验证的备份等于没有备份 | 每月执行一次恢复演练:tar xzf backup.tar.gz -C /tmp/restore-test |
| 初始化日志留存 | 记录首次初始化的完整输出,便于后续排查"基线漂移" | bash init.sh | tee /var/log/init-$(date +%F).log |
练习题
- (概念)标准化初始化的三个核心价值是什么?为什么说没有标准化就无法自动化?
- (概念)为什么新服务器要先更新系统再对外开放?如果顺序反过来会有什么风险?
- (实操)在一台干净的 Ubuntu 虚拟机上运行本章的初始化脚本。在运行 Step 6(SSH 加固)之前,故意将
ADMIN_SSH_KEY设为空字符串,然后运行到结束——会出现什么情况?如何修复? - (实操)修改脚本中的 UFW 规则,使其同时放行 HTTP (80) 和 HTTPS (443),并在防火墙状态输出中用
comment标注每条规则的作用。验证ufw status numbered输出。 - (🔍 挑战)将本章的 bash 初始化脚本改写成 Ansible playbook(参见 4.1:Ansible 自动化)。要求:使用
hostnamemodule、ufwmodule、usermodule,且 SSH 密钥通过authorized_keymodule 管理。运行 playbook 实现幂等初始化。
点击查看答案
- 一致性(每台服务器配置相同)、审计轨迹(初始化过程可复现)、自动化基础(标准流程才能写成脚本/Ansible)。没有标准化就无法用同一套自动化工具管理所有机器。
- 新服务器可能有已知漏洞,先更新再对外开放可缩小被攻击窗口。若顺序反过来——开放后再更新——服务器在更新完成前可能已被扫描和入侵。
- SSH 加固(Step 6)会关闭密码认证和 root 登录,但
ADMIN_SSH_KEY为空,导致无任何有效认证方式,SSH 连接永久断开。修复:通过云控制台 VNC/串口登录,重新设置密钥并修复 sshd_config。 - 添加
ufw allow 80/tcp comment 'HTTP'和ufw allow 443/tcp comment 'HTTPS'。运行ufw status numbered应看到每条规则前有序号和 comment 字段。 - Ansible playbook 用
hostname:module、ufw:module、user:module、authorized_key:module、fail2ban:等。幂等性由各 module 自身的 check mode 保证,多次执行结果一致。
学习检查点
学完本章后,请检验自己是否掌握以下内容:
| 检查项 | 自测问题 | 验证方法 |
|---|---|---|
| 概念理解 | 能用自己的话解释服务器初始化的目的和关键步骤 | 尝试向他人讲解 |
| 命令操作 | 能不查文档完成服务器初始化脚本编写、配置执行 | 在终端实际执行 |
| 原理掌握 | 能说出服务器初始化的安全考虑和最佳实践 | 画出流程图 |
| 故障排查 | 能独立排查初始化脚本执行失败、配置错误 | 模拟故障并修复 |
| 最佳实践 | 能说明为什么需要为服务器初始化配置审计和验证 | 对比不同方案 |
本章总结
服务器初始化标准化是运维自动化的第一块基石。10 步流程覆盖了从基础配置(主机名、时区、swap)到安全加固(SSH、UFW、fail2ban)再到持续运维(监控、备份)的完整链路。脚本化之后,一台新服务器从"裸机"到"可上线"的时间可以从小时级压缩到分钟级,且每台服务器的配置保持一致。
速查表
| 命令/操作 | 用途 |
|---|---|
hostnamectl set-hostname <名> | 设置主机名 |
timedatectl set-timezone Asia/Shanghai | 设置时区 |
timedatectl set-ntp true | 启用 NTP 时间同步 |
ufw default deny incoming | 防火墙默认拒绝入站 |
ufw allow 22/tcp | 放行 SSH 端口 |
ufw --force enable | 启用防火墙(非交互) |
fail2ban-client status sshd | 查看 SSH 封禁统计 |
sshd -t | 检查 sshd_config 语法 |
systemctl reload sshd | 重载 SSH 配置 |
passwd -l <用户> | 锁定用户密码(仅密钥登录) |
vm.swappiness=10 | 降低 swap 倾向性 |
学习路径建议
- 学完本章后建议阅读 5.1:Linux 安全加固 Linux 安全加固(理解每项安全配置的原理)
- 批量初始化多台服务器 → 学习 4.1:Ansible 自动化 Ansible 自动化运维
- 搭建完整的监控体系 → 学习 3.12:系统监控与告警 系统监控方案
- 设计可靠的备份策略 → 学习 3.13:备份策略与恢复 备份策略与恢复
- 进阶:将初始化流程打包为 Packer image + Terraform 部署
延伸阅读
- CIS Ubuntu Linux Benchmark——安全基线权威参考
- Ubuntu Server Guide——官方服务器配置指南
- Prometheus Node Exporter 官方指南
- fail2ban 官方文档
- How To Secure A Linux Server(开源指南)
- 推荐书籍:《UNIX 和 Linux 系统管理手册》第 4 版
- 本书章节:1.7:用户与权限管理 用户与权限管理 · 1.8:软件包管理 软件包管理 · 2.11:SSH 深入 SSH 深入 · 3.12:系统监控与告警 系统监控与告警 · 3.13:备份策略与恢复 备份策略与灾难恢复方案 · 4.1:Ansible 自动化 Ansible 自动化配置管理入门 · 5.1:Linux 安全加固 Linux 服务器安全加固指南 · 5.5:防火墙实战 iptables/nftables 防火墙实战
常见问题
新服务器初始化顺序有最佳实践吗?
推荐顺序:① 更新系统(apt update && apt upgrade -y);② 设置主机名和时区(timedatectl set-timezone Asia/Shanghai);③ 创建管理员用户并添加 SSH 密钥;④ 配置 SSH 加固(端口、PermitRootLogin no、PubkeyAuthentication yes);⑤ 安装和配置防火墙(ufw);⑥ 安装 fail2ban;⑦ 开启自动安全更新;⑧ 配置监控(Prometheus node_exporter)。
服务器开了哪些端口怎么快速检查?
ss -tlnp(TCP 监听)和 ss -ulnp(UDP 监听)显示所有开放端口和对应的进程。nmap localhost 从外部视角扫描本机开放端口。netstat -tulpn 是传统方式(需 apt install net-tools)。建议关闭不需要的服务对应端口,减少攻击面。
Swap 分区设多大合适?
旧规则是 RAM × 2。现代服务器推荐:RAM < 2GB 设 2×RAM,RAM 2-8GB 设等于 RAM,RAM > 8GB 设 4-8GB(或 0 如果不需要休眠)。K8s 节点建议禁用 swap(kubelet 要求)。云服务器可以创建 swap 文件替代分区,方便动态调整大小。