一、文件管理与高效查询
1.1 基础命令的高级用法
ls --- 不只是列文件
bash
ls -lah # -l 详细 -a 含隐藏 -h 人类可读大小
ls -lt # 按修改时间排序(最新在前)
ls -lS # 按文件大小排序(最大在前)
ls -lR /etc/nginx/ # 递归列出目录树
ls -ld /var/log # 只看目录本身的权限,不列内容
cp --- 保留属性是关键
bash
cp -r src/ dst/ # 递归复制目录
cp -a src/ dst/ # 归档模式 = -r + 保留权限/时间戳/符号链接(部署时必用)
cp -p file.conf bak/ # 保留原文件的权限和时间戳
cp --backup=numbered a.conf /etc/ # 如果目标存在,自动生成编号备份 a.conf.~1~
实战场景 :迁移网站目录时,用
cp -a而不是cp -r,否则文件权限全变成当前用户的,Nginx 可能 403。
mv --- 重命名与批量移动
bash
mv old.conf new.conf # 重命名
mv *.log /var/log/archive/ # 批量移动
mv -n source target # --no-clobber,目标存在时不覆盖(安全)
1.2 深度搜索
find --- 最强文件搜索
bash
# 按名称
find /etc -name "*.conf" # 精确匹配
find /etc -iname "*.CONF" # 忽略大小写
# 按时间(运维排查核心)
find /var/log -mtime -1 # 最近 1 天内修改过的文件
find /var/log -mmin -30 # 最近 30 分钟内修改过的
find /tmp -atime +7 # 超过 7 天没被访问的(清理用)
# 按大小
find / -size +100M -type f # 找出大于 100MB 的文件
find / -size +100M -type f 2>/dev/null | head -20 # 忽略权限报错
# 按权限(安全审计)
find / -perm -4000 -type f 2>/dev/null # 找所有 SUID 文件(提权风险点)
find /home -perm 777 -type f # 找权限全开的文件
# 按用户
find /home -user deploy -name "*.sh" # 找 deploy 用户的脚本
# 组合操作(find + xargs 是黄金搭档)
find /var/log -name "*.log" -mtime +30 | xargs rm -f # 删 30 天前的日志
find /app -name "*.java" | xargs grep -l "TODO" # 在所有 java 文件中搜 TODO
find /tmp -type f -empty | xargs -I {} rm {} # 删除所有空文件
find . -name "*.sh" -exec chmod +x {} \; # 给所有 sh 加执行权限
# xargs 的 -I {} 模式(文件名含空格时安全处理)
find . -name "*.bak" -print0 | xargs -0 rm -f # -print0 + -0 处理特殊文件名
find vs xargs vs -exec:
-exec {} \;每找到一个文件执行一次命令(慢)-exec {} +批量传递文件名(快,但部分命令不支持)| xargs最灵活,支持并行xargs -P 4
grep --- 递归搜索字符串
bash
grep -r "password" /etc/ # 递归搜索目录下所有文件
grep -rn "listen 80" /etc/nginx/ # -n 显示行号
grep -ri "error" /var/log/syslog # -i 忽略大小写
grep -rl "127.0.0.1" /etc/ # -l 只输出文件名(不输出匹配内容)
grep -c "Failed" /var/log/auth.log # -c 统计匹配行数
# 正则表达式
grep -E "^root|^admin" /etc/passwd # -E 扩展正则,匹配以 root 或 admin 开头
grep -P "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" access.log # -P Perl正则,匹配 IP
grep -v "^#" /etc/ssh/sshd_config | grep -v "^$" # 过滤注释和空行,看有效配置
# 上下文显示(排查日志必备)
grep -A 5 "ERROR" app.log # 匹配行 + 后5行
grep -B 3 "ERROR" app.log # 前3行 + 匹配行
grep -C 3 "ERROR" app.log # 前后各3行
locate --- 秒级查找(依赖索引)
bash
apt install mlocate
updatedb # 手动更新索引(自动每天更新一次)
locate nginx.conf # 瞬间找到
locate -i "readme" # 忽略大小写
find vs locate:find 实时扫磁盘(慢但准),locate 查索引(快但可能过时)。生产环境排查用 find,日常查找用 locate。
1.3 性能监控
htop --- 进程监控(替代 top)
bash
apt install htop
htop
# htop 内快捷键:
# F5 树状视图(看父子进程关系)
# F6 选择排序字段(CPU/MEM/TIME)
# F9 发送信号(Kill 进程)
# F4 过滤进程名
# u 按用户过滤
iotop --- 磁盘 I/O 监控
bash
apt install iotop
iotop -oP # -o 只显示有 IO 的进程,-P 显示进程而非线程
# 场景:服务器突然变慢,用 iotop 看是哪个进程在疯狂读写磁盘
磁盘占用排查
bash
df -h # 查看各分区使用率(第一步:哪个分区满了)
df -ih # 查看 inode 使用率(小文件太多也会满)
du -sh /var/* # 第二步:哪个目录占用大
du -sh /var/log/* # 第三步:继续往下定位
du -ah /var/log/ | sort -rh | head -20 # 按大小排序,看 Top 20
# ncdu --- 交互式磁盘占用分析(运维神器)
apt install ncdu
ncdu /var # 可视化目录树,按大小排序,d 键可直接删除
磁盘满了的标准排查流程 :
df -h→ 定位分区 →du -sh逐层定位 →ncdu可视化确认 → 清理。
二、用户权限与 SSH 安全加固
2.1 用户管理
adduser vs useradd
bash
# adduser(推荐,交互式,自动做所有事)
adduser deploy
# 自动:创建家目录、复制 /etc/skel 模板、设置密码、创建同名组
# useradd(底层命令,什么都不做除非你指定)
useradd deploy # 不创建家目录!不设密码!
useradd -m -s /bin/bash deploy # -m 创建家目录 -s 指定 shell
passwd deploy # 手动设密码
规范 :交互操作用
adduser,脚本自动化用useradd -m -s /bin/bash。
赋予 sudo 权限
方法一:加入 sudo 组(推荐)
bash
usermod -aG sudo deploy # -a 追加 -G 指定组
# 验证
su - deploy
sudo whoami # 输出 root 即成功
方法二:sudoers.d 独立文件(架构师推荐,见第三章)
bash
echo "deploy ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/deploy
chmod 440 /etc/sudoers.d/deploy
方法三:visudo 直接编辑(不推荐,容易冲突)
bash
visudo # 千万别用 vim /etc/sudoers,visudo 有语法检查
# 在 root ALL=(ALL:ALL) ALL 下面加:
# deploy ALL=(ALL:ALL) ALL
用户管理常用操作
bash
id deploy # 查看用户 UID、GID、所属组
groups deploy # 查看用户所在的组
userdel -r olduser # 删除用户并删除家目录
usermod -l newname oldname # 改用户名
passwd -l deploy # 锁定用户(禁止登录)
passwd -u deploy # 解锁用户
chage -l deploy # 查看密码过期策略
2.2 SSH 安全加固
SSH 配置文件路径:/etc/ssh/sshd_config
关闭 Root 登录
bash
vim /etc/ssh/sshd_config
# 找到并修改:
PermitRootLogin no
# 重启 SSH(不会断开当前连接)
systemctl restart sshd
操作顺序很重要:先创建普通用户并确认能 sudo → 再关闭 root 登录。否则你会把自己锁在外面。
恢复 Root 登录(开发环境)
bash
vim /etc/ssh/sshd_config
PermitRootLogin yes # 或 PermitRootLogin prohibit-password(只允许密钥登录)
systemctl restart sshd
风险警告 :生产环境永远不要开 Root 登录。如果必须远程 root 操作,用普通用户登录后
sudo -i。
密钥对登录(禁用密码)
bash
# === 在你的本机(Mac/Windows)操作 ===
ssh-keygen -t ed25519 -C "deploy@myserver" # 生成密钥对(ed25519 比 rsa 更安全更短)
# 默认保存在 ~/.ssh/id_ed25519(私钥)和 ~/.ssh/id_ed25519.pub(公钥)
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@服务器IP # 把公钥传到服务器
# 等价于手动操作:把公钥内容追加到服务器的 ~/.ssh/authorized_keys
# === 验证密钥登录成功后,再禁用密码登录 ===
vim /etc/ssh/sshd_config
PasswordAuthentication no # 禁用密码登录
PubkeyAuthentication yes # 确保公钥登录开启
systemctl restart sshd
SSH 加固完整配置清单
bash
# /etc/ssh/sshd_config 推荐配置
Port 22222 # 改掉默认端口(减少扫描)
PermitRootLogin no # 禁止 root 登录
PasswordAuthentication no # 禁用密码
PubkeyAuthentication yes # 启用密钥
MaxAuthTries 3 # 最大尝试次数
ClientAliveInterval 300 # 5分钟无操作发心跳
ClientAliveCountMax 2 # 2次心跳无响应断开
AllowUsers deploy admin # 白名单(只允许这些用户 SSH 登录)
三、架构师必备:服务配置规范(.d 模式)
3.1 为什么用 .d 目录?
传统方式:所有配置写在一个文件里(如 /etc/sudoers、/etc/nginx/nginx.conf)。
问题:
- 多人/多服务修改同一文件 → 冲突
- 自动化工具(Ansible/Terraform)写配置 → 容易覆盖手动修改
- 系统升级时 → 提示合并冲突
.d 模式的设计哲学:
主配置文件(只读模板)
└── .d 目录/
├── 00-base.conf ← 基础配置
├── 10-security.conf ← 安全团队维护
├── 20-app.conf ← 应用团队维护
└── 99-override.conf ← 临时覆盖
优势:
- 配置解耦:每个功能/团队一个文件,互不干扰
- 热更新友好:新增/删除一个文件即可,不碰主配置
- 自动化友好:Ansible 直接 copy 一个文件进去,不需要解析和修改主配置
- 回滚简单:删掉那个文件就回滚了
- 升级安全:系统升级覆盖主配置不影响 .d 目录里的自定义配置
文件加载顺序:通常按文件名字母序加载,后加载的覆盖先加载的。所以用数字前缀控制优先级。
3.2 各目录实战
/etc/sudoers.d/ --- 用户授权
bash
# 不要直接改 /etc/sudoers,用独立文件
vim /etc/sudoers.d/deploy
# 文件内容: 这样deploy用户以后sudo 命令之后不需要输入密码
deploy ALL=(ALL) NOPASSWD:ALL
# 权限必须是 440,否则 sudo 会拒绝加载
chmod 440 /etc/sudoers.d/deploy
# 验证语法
visudo -cf /etc/sudoers.d/deploy
为什么不直接改 /etc/sudoers?
场景:你用 Ansible 管理 50 台服务器的 sudo 权限
- 改主文件:每次要解析文件内容,找到对应行,替换。容易出错。
- .d 模式:直接 copy 一个文件过去。删除权限就删文件。干净利落。
/etc/nginx/conf.d/ --- 虚拟主机配置
bash
# Nginx 主配置 /etc/nginx/nginx.conf 中有这行:
# include /etc/nginx/conf.d/*.conf;
# 每个站点一个文件
vim /etc/nginx/conf.d/blog.conf
nginx
server {
listen 80;
server_name blog.example.com;
location / {
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;
}
}
bash
# 检查语法
nginx -t
# 重载(不中断服务)
systemctl reload nginx
# 禁用某个站点:改后缀即可
mv /etc/nginx/conf.d/blog.conf /etc/nginx/conf.d/blog.conf.disabled
systemctl reload nginx
规范 :一个域名/站点一个 .conf 文件。禁用时改后缀为
.disabled而不是删除。
/etc/systemd/system/ --- 自定义服务
bash
vim /etc/systemd/system/myapp.service
ini
[Unit]
Description=My Spring Boot Application
After=network.target mysql.service
Wants=mysql.service
[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/java -jar -Xms512m -Xmx1024m /opt/myapp/app.jar --spring.profiles.active=prod
ExecStop=/bin/kill -SIGTERM $MAINPID
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
# 安全加固
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/opt/myapp/logs /opt/myapp/data
[Install]
WantedBy=multi-user.target
bash
systemctl daemon-reload # 重新加载 unit 文件(每次改完必须执行)
systemctl enable myapp # 开机自启
systemctl start myapp # 启动
systemctl status myapp # 查看状态
journalctl -u myapp -f # 实时看日志
journalctl -u myapp --since "1 hour ago" # 看最近1小时日志
关键字段:
After:在哪些服务之后启动(启动顺序)Wants:弱依赖(依赖的服务挂了,自己还能启动)Requires:强依赖(依赖的服务挂了,自己也停)Restart=on-failure:进程异常退出时自动重启RestartSec=10:重启间隔 10 秒,避免疯狂重启
/etc/sysctl.d/ --- 内核参数调优
bash
vim /etc/sysctl.d/99-custom.conf
ini
# 网络调优(高并发服务器)
net.core.somaxconn = 65535 # TCP 监听队列最大长度
net.ipv4.tcp_max_syn_backlog = 65535 # SYN 半连接队列
net.ipv4.ip_local_port_range = 1024 65535 # 可用端口范围
net.ipv4.tcp_tw_reuse = 1 # 允许 TIME_WAIT 复用
net.ipv4.tcp_fin_timeout = 15 # FIN_WAIT_2 超时缩短
# 文件描述符
fs.file-max = 2097152 # 系统级最大文件句柄数
# 内存
vm.swappiness = 10 # 尽量少用 swap(数据库服务器建议设 1)
vm.overcommit_memory = 1 # Redis 要求(fork 时不检查内存)
bash
sysctl --system # 加载 /etc/sysctl.d/ 下所有配置
sysctl -p /etc/sysctl.d/99-custom.conf # 只加载指定文件
sysctl net.core.somaxconn # 验证某个参数当前值
四、系统安全加固:Fail2ban 专项
4.1 原理
Fail2ban 的工作方式:
监控日志文件(如 /var/log/auth.log)
↓ 正则匹配失败的登录尝试
发现某 IP 在 findtime 内失败了 maxretry 次
↓
调用 iptables/nftables 封禁该 IP,持续 bantime
↓ bantime 到期
自动解封
4.2 安装与基本结构
bash
apt install fail2ban
systemctl enable fail2ban
配置文件的覆盖关系(这里也是 .d 思想):
/etc/fail2ban/jail.conf ← 默认配置(系统包自带,升级会被覆盖,不要改)
/etc/fail2ban/jail.local ← 你的自定义配置(覆盖 jail.conf 中的同名项)
/etc/fail2ban/jail.d/*.conf ← 模块化配置(覆盖 jail.conf 和 jail.local)
加载优先级 :jail.conf → jail.local → jail.d/*.conf(后加载的覆盖前面的)
规范 :永远只改
jail.local或jail.d/下的文件。jail.conf视为只读。
4.3 实战配置
bash
vim /etc/fail2ban/jail.local
ini
[DEFAULT]
# 封禁时长(-1 = 永久)
bantime = 1h
# 观察时间窗口
findtime = 10m
# 最大失败次数
maxretry = 5
# 封禁动作(Ubuntu 22.04 默认用 nftables)
banaction = nftables-multiport
# 解封后如果又失败,递增封禁时长
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 1w
# 白名单(永远不封的 IP)
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8
# 邮件通知(可选)
# destemail = admin@example.com
# sender = fail2ban@example.com
# action = %(action_mwl)s
[sshd]
enabled = true
port = ssh,22222 # 如果改了 SSH 端口,这里也要加
logpath = /var/log/auth.log
maxretry = 3 # SSH 更严格,3次就封
bantime = 2h
4.4 常用命令
bash
systemctl restart fail2ban # 重启服务
fail2ban-client status # 查看所有 jail 状态
fail2ban-client status sshd # 查看 SSH jail 详情(当前封了几个 IP)
fail2ban-client set sshd unbanip 1.2.3.4 # 手动解封某 IP
fail2ban-client set sshd banip 1.2.3.4 # 手动封禁某 IP
# 查看封禁日志
tail -f /var/log/fail2ban.log
# 查看当前所有被封的 IP
fail2ban-client banned
4.5 添加其他防护(示例:Nginx 防爆破)
bash
vim /etc/fail2ban/jail.d/nginx.conf
ini
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
[nginx-botsearch]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2
五、定时任务的多维实现
5.1 三种方案对比
| 特性 | Crontab | Systemd Timer | at |
|---|---|---|---|
| 用途 | 周期性任务 | 周期性任务 | 一次性任务 |
| 日志 | 自己处理(重定向到文件) | 自动进 journal (journalctl) |
邮件通知 |
| 依赖管理 | 无 | 支持(After=, Requires=) | 无 |
| 错过执行 | 丢失(关机期间的不补) | 可配置补执行(Persistent=true) | 丢失 |
| 精度 | 分钟级 | 秒级 | 分钟级 |
| 资源控制 | 无 | 支持 cgroup(限 CPU/内存) | 无 |
| 适用场景 | 简单脚本、传统习惯 | 生产服务、需要监控的任务 | 临时任务 |
结论:新项目用 Systemd Timer,老项目维护用 Crontab,临时用 at。
5.2 Crontab
系统级 vs 用户级
bash
# 用户级(以当前用户身份执行)
crontab -e # 编辑当前用户的 crontab
crontab -l # 查看
crontab -r # 删除所有(危险!)
# 系统级(可以指定用户)
vim /etc/crontab
# 格式多了一个"用户"字段:
# 分 时 日 月 周 用户 命令
crontab 背后的存储机制
crontab -e 本质就是在编辑一个文件,存储位置:
/var/spool/cron/crontabs/用户名
bash
crontab -e # 编辑 → 实际打开 /var/spool/cron/crontabs/当前用户
crontab -l # 查看 → 实际 cat 那个文件
crontab -r # 删除 → 实际删掉那个文件(所有定时任务全没!)
cron 守护进程会定期扫描 /var/spool/cron/crontabs/ 目录,发现文件变化就自动加载新的定时任务。
为什么不直接 vim 那个文件?
crontab -e编辑时有语法检查,保存时如果格式错误会提示。直接 vim 没有这层保护,写错了 cron 不报错也不执行,排查起来很痛苦。
用户级 crontab -e |
系统级 | |
|---|---|---|
| 文件位置 | /var/spool/cron/crontabs/用户名 |
/etc/crontab 和 /etc/cron.d/ |
| 谁执行 | 当前用户 | 可以指定用户 |
| 编辑方式 | crontab -e(有语法检查) |
直接 vim(无语法检查) |
| 格式区别 | 5 个时间字段 + 命令 | 5 个时间字段 + 用户 + 命令 |
语法
┌──────── 分 (0-59)
│ ┌────── 时 (0-23)
│ │ ┌──── 日 (1-31)
│ │ │ ┌── 月 (1-12)
│ │ │ │ ┌ 周几 (0-7, 0和7都是周日)
│ │ │ │ │
* * * * * command
实战示例
bash
# 每天凌晨 3 点备份数据库
0 3 * * * /opt/scripts/backup-db.sh >> /var/log/backup.log 2>&1
# 每 5 分钟检查服务状态
*/5 * * * * /opt/scripts/health-check.sh
# 每周一早上 9 点发报告
0 9 * * 1 /opt/scripts/weekly-report.sh
# 每月 1 号清理日志
0 2 1 * * find /var/log/myapp -name "*.log" -mtime +30 -delete
关键 :
>> /var/log/xx.log 2>&1必须加。Crontab 默认没有输出,出了问题你什么都看不到。
Crontab 排错
bash
# crontab 执行环境和你终端不同!PATH 很少
# 脚本里要写完整路径
/usr/bin/python3 /opt/scripts/job.py # 不要写 python3 job.py
# 查看 cron 执行日志
grep CRON /var/log/syslog
# 验证 cron 服务在运行
systemctl status cron
5.3 Systemd Timer(推荐)
一个 Timer 需要两个文件:timer 文件 + service 文件。
示例:每天凌晨 3 点备份
bash
vim /etc/systemd/system/backup-db.service
ini
[Unit]
Description=Database Backup
[Service]
Type=oneshot
User=deploy
ExecStart=/opt/scripts/backup-db.sh
# 资源限制(crontab 做不到)
MemoryMax=512M
CPUQuota=50%
bash
vim /etc/systemd/system/backup-db.timer
ini
[Unit]
Description=Run database backup daily
[Timer]
OnCalendar=*-*-* 03:00:00 # 每天 3:00:00
Persistent=true # 如果错过了(比如关机),开机后立即补一次
RandomizedDelaySec=300 # 随机延迟 0-300 秒(避免多台服务器同时备份)
AccuracySec=1min # 精度
[Install]
WantedBy=timers.target
bash
systemctl daemon-reload
systemctl enable --now backup-db.timer # 启用并立即激活
# 管理命令
systemctl list-timers --all # 查看所有 timer 及下次执行时间
systemctl status backup-db.timer # 查看 timer 状态
journalctl -u backup-db.service # 查看执行日志(这就是比 crontab 强的地方)
systemctl start backup-db.service # 手动触发一次(测试用)
OnCalendar 常用写法
ini
OnCalendar=hourly # 每小时
OnCalendar=daily # 每天 00:00
OnCalendar=weekly # 每周一 00:00
OnCalendar=*-*-* 03:00:00 # 每天 3 点
OnCalendar=Mon *-*-* 09:00:00 # 每周一 9 点
OnCalendar=*-*-01 02:00:00 # 每月 1 号 2 点
OnCalendar=*:0/5 # 每 5 分钟
# 验证时间表达式
systemd-analyze calendar "*-*-* 03:00:00"
systemd-analyze calendar "Mon *-*-* 09:00:00"
5.4 at --- 一次性任务
bash
apt install at
systemctl enable --now atd
# 10 分钟后执行
echo "/opt/scripts/cleanup.sh" | at now + 10 minutes
# 今晚 11 点执行
at 23:00 <<'EOF'
/usr/bin/systemctl restart myapp
echo "Restarted at $(date)" >> /var/log/restart.log
EOF
# 明天下午 3 点
echo "reboot" | at 3:00 PM tomorrow
# 管理
atq # 查看队列中待执行的任务
atrm 3 # 删除编号为 3 的任务
典型场景:服务器维护窗口,提前安排凌晨 3 点重启,然后下班。
附录:运维速查表
常用排查组合拳
bash
# 服务器变慢了?
htop # 看 CPU/内存 谁占高
iotop -oP # 看磁盘 IO 谁在写
ss -tlnp # 看端口和连接数
dmesg -T | tail -50 # 看内核消息(OOM Killer?)
# 磁盘满了?
df -h # 哪个分区满了
du -sh /var/* | sort -rh | head # 哪个目录大
find /var/log -size +100M # 哪个文件大
journalctl --disk-usage # systemd 日志占了多少
journalctl --vacuum-size=500M # 清理到 500M
# 某个端口被谁占了?
ss -tlnp | grep :8080
lsof -i :8080
# 网络连不上?
ping 8.8.8.8 # 网络通不通
curl -v https://example.com # HTTP 层通不通
dig example.com # DNS 解析正不正常
traceroute example.com # 路由哪一跳断了
ss -s # 连接状态统计(TIME_WAIT 多不多)
系统信息一览
bash
uname -a # 内核版本
lsb_release -a # Ubuntu 版本
hostnamectl # 主机名、系统、内核、架构
free -h # 内存使用
lscpu # CPU 信息
lsblk # 块设备(磁盘/分区)
ip addr # 网卡和 IP
cat /etc/os-release