Linux 服务器运维指南

本文整理了服务器日常运维中最常用的操作,涵盖日志排查、系统监控、部署工具、自动化运维等核心场景,方便快速查阅。
一、日志查看与管理
tail - 实时追踪日志
最常用的日志命令
tail -f 是线上排查问题的第一选择,可以实时追踪日志输出。
# 实时追踪日志(最常用)
tail -f /var/log/app.log
# 实时追踪并高亮关键字
tail -f /var/log/app.log | grep --color=auto "ERROR"
# 显示最后 200 行
tail -n 200 /var/log/app.log
# 从第 1000 行开始显示到末尾
tail -n +1000 /var/log/app.log
# 同时追踪多个日志文件
tail -f /var/log/app.log /var/log/error.logtail -f 与 tail -F 的区别
tail -f:追踪文件描述符,文件被删除重建后会失效tail -F:追踪文件名,文件被删除重建后会自动重新打开(推荐用于日志轮转场景)
less - 交互式日志浏览
比 cat 更适合查看大文件
less 支持前后翻页、搜索关键字,不会一次性加载整个文件到内存。
less /var/log/app.log| 快捷键 | 作用 |
|---|---|
Space / f | 向下翻一页 |
b | 向上翻一页 |
/keyword | 向下搜索关键字 |
?keyword | 向上搜索关键字 |
n / N | 下一个 / 上一个匹配 |
g / G | 跳到文件开头 / 结尾 |
F | 进入追踪模式(类似 tail -f) |
q | 退出 |
grep - 日志过滤
# 搜索关键字并显示前后 5 行上下文
grep -C 5 "ERROR" /var/log/app.log
# 搜索多个关键字(OR 关系)
grep -E "ERROR|WARN|FATAL" /var/log/app.log
# 排除某些内容
grep -v "HealthCheck" /var/log/app.log
# 统计错误出现次数
grep -c "ERROR" /var/log/app.log
# 递归搜索目录下所有日志
grep -rn "OutOfMemory" /var/log/
# 按时间范围过滤(配合 sed)
sed -n '/2024-01-15 10:00/,/2024-01-15 11:00/p' /var/log/app.logjournalctl - systemd 日志
管理 systemd 服务的日志利器
现代 Linux 发行版中,systemd 管理的服务日志统一由 journalctl 查看。
# 查看指定服务的日志
journalctl -u nginx.service
# 实时追踪
journalctl -u app.service -f
# 查看今天的日志
journalctl -u app.service --since today
# 按时间范围查询
journalctl -u app.service --since "2024-01-15 10:00" --until "2024-01-15 12:00"
# 只看错误级别
journalctl -u app.service -p err
# 查看内核日志
journalctl -k
# 查看磁盘占用并清理
journalctl --disk-usage
journalctl --vacuum-size=500Mlogrotate - 日志轮转
不配置日志轮转,磁盘迟早被撑满
生产环境必须配置日志轮转策略,避免单个日志文件无限增长。
# /etc/logrotate.d/app
/var/log/app/*.log {
daily # 每天轮转
missingok # 日志不存在不报错
rotate 30 # 保留 30 个归档
compress # gzip 压缩旧日志
delaycompress # 延迟一个周期再压缩
notifempty # 空文件不轮转
create 0640 root root
sharedscripts
postrotate
systemctl reload app.service > /dev/null 2>&1 || true
endscript
}# 手动测试轮转配置(不实际执行)
logrotate -d /etc/logrotate.d/app
# 手动强制执行轮转
logrotate -f /etc/logrotate.d/appI/O 重定向 - 日志输出控制
理解文件描述符
Linux 中每个进程默认有三个文件描述符:
- 0 (stdin):标准输入
- 1 (stdout):标准输出(正常日志)
- 2 (stderr):标准错误输出(错误日志)
重定向就是改变这些输出的去向。
基础重定向
# 标准输出重定向到文件
command > output.log # 覆盖写入(等价于 1>)
command >> output.log # 追加写入(等价于 1>>)
# 标准错误重定向到文件
command 2> error.log # 错误输出覆盖写入
command 2>> error.log # 错误输出追加写入
# stdout 和 stderr 分别写入不同文件
command > output.log 2> error.log
# stdout 和 stderr 都写入同一个文件
command > all.log 2>&1 # ✅ 正确写法:先重定向 stdout,再把 stderr 指向 stdout
command >> all.log 2>&1 # 追加模式
# 等价简写(Bash 4+)
command &> all.log # 覆盖
command &>> all.log # 追加2>&1 的顺序很重要
# ✅ 正确:stderr 跟随 stdout 一起写入 file.log
command > file.log 2>&1
# ❌ 错误:stderr 指向的是 stdout 的原始位置(终端),不会写入文件
command 2>&1 > file.log2>&1 的意思是"把文件描述符 2 指向文件描述符 1 当前指向的位置",所以必须放在 > 之后。
丢弃输出(/dev/null)
# 丢弃标准输出(只看错误)
command > /dev/null
# 丢弃错误输出(只看正常输出)
command 2> /dev/null
# 丢弃所有输出(静默执行)
command > /dev/null 2>&1
command &> /dev/null # 等价简写
# 典型应用场景
# crontab 中避免产生邮件
*/5 * * * * /opt/scripts/check.sh > /dev/null 2>&1
# 判断命令是否可用,不关心输出
if command -v docker > /dev/null 2>&1; then
echo "Docker is installed"
fi
# curl 静默检测服务是否存活
curl -sf http://localhost:3000/health > /dev/null 2>&1 && echo "OK" || echo "FAIL"实用组合
# 后台运行 + 日志重定向(nohup 经典用法)
nohup python app.py > /var/log/app.log 2>&1 &
# 正常日志和错误日志分开记录
nohup python app.py > /var/log/app.log 2> /var/log/app_error.log &
# 管道中只传递 stdout,丢弃 stderr
command 2>/dev/null | grep "pattern"
# 管道中同时传递 stdout 和 stderr
command 2>&1 | grep "pattern"
# tee:同时输出到屏幕和文件
command 2>&1 | tee output.log # 覆盖
command 2>&1 | tee -a output.log # 追加
# 将 stderr 转为 stdout 后用 tee 记录(适合调试)
./deploy.sh 2>&1 | tee deploy_$(date +%Y%m%d_%H%M%S).log文件描述符进阶用法
# 自定义文件描述符
exec 3> custom.log # 打开 fd 3 写入 custom.log
echo "message" >&3 # 写入 fd 3
exec 3>&- # 关闭 fd 3
# 交换 stdout 和 stderr
command 3>&1 1>&2 2>&3 3>&-
# Here Document 重定向(写入多行内容到文件)
cat > /etc/nginx/conf.d/app.conf << 'EOF'
server {
listen 80;
server_name example.com;
}
EOF
# Here String
grep "error" <<< "$LOG_CONTENT"cat / tac / head - 基础文件查看
# cat: 查看完整文件(适合小文件)
cat /var/log/app.log
cat -n file.log # 显示行号
cat file1 file2 > merged # 合并文件
# tac: 倒序查看(排查最新日志)
tac /var/log/app.log | head -50
# head: 查看文件开头
head -n 100 /var/log/app.log
# 显示文件的第 3000~3999 行
cat file.log | tail -n +3000 | head -n 1000
# 显示文件的第 1000~3000 行
cat file.log | head -n 3000 | tail -n +1000注意
> 是覆盖写入,>> 是追加写入,生产环境务必区分!
二、系统监控与性能分析
系统概览
# 系统运行时间、负载
uptime
# 输出: 14:30 up 35 days, load averages: 0.52 1.23 1.05
# 内存使用(-h 人类可读格式)
free -h
# 磁盘空间
df -h
# 目录大小排序(排查磁盘占用)
du -sh /var/* | sort -rh | head -20负载理解
Load Average 的三个数字分别代表 1/5/15 分钟的平均负载。一般来说:
- 负载 < CPU 核心数:系统健康
- 负载 = CPU 核心数:满载运行
- 负载 > CPU 核心数:存在排队,需要关注
top / htop - 进程监控
top
# 进入 top 后的快捷键:
# P - 按 CPU 排序
# M - 按内存排序
# T - 按运行时间排序
# k - 杀死进程(输入 PID)
# 1 - 显示每个 CPU 核心的使用率
# q - 退出# 安装
yum install -y htop # CentOS
apt install -y htop # Ubuntu
# 使用
htop
# htop 优势:
# - 彩色界面,更直观
# - 支持鼠标操作
# - 可以横向/纵向滚动
# - 支持树状视图(F5)网络监控
# 查看端口占用
ss -tlnp # 推荐,比 netstat 更快
netstat -tlnp # 经典命令
# 查看网络连接状态统计
ss -s
# 查看指定端口的连接
ss -tlnp | grep :80
# 实时网络流量监控
iftop # 需安装
nethogs # 按进程查看流量,需安装
# DNS 查询
dig example.com
nslookup example.com
# 测试端口连通性
telnet 192.168.1.1 80
nc -zv 192.168.1.1 80
# 路由追踪
traceroute example.com
mtr example.com # 更强大的替代品磁盘 I/O 监控
# I/O 使用情况(每 2 秒刷新)
iostat -x 2
# 查看哪些进程在读写磁盘
iotop
# 查看文件系统的 inode 使用(inode 耗尽也会导致无法创建文件)
df -i三、进程管理
进程查看与终止
# 查看所有进程
ps aux
# 查看进程树
ps auxf
pstree -p
# 按名称查找进程
ps aux | grep nginx
pgrep -a nginx
# 优雅终止进程
kill PID # 发送 SIGTERM(15),允许进程清理
kill -9 PID # 发送 SIGKILL(9),强制终止(最后手段)
# 批量终止
pkill -f "python app.py"
killall nginx慎用 kill -9
kill -9 不会给进程清理资源的机会,可能导致数据损坏、锁文件残留等问题。应优先使用 kill PID,等待几秒后进程仍未退出再考虑 -9。
后台运行与 systemd
# 后台运行,输出重定向到文件
nohup python app.py > /var/log/app.log 2>&1 &
# 查看后台任务
jobs -l# 创建服务文件 /etc/systemd/system/app.service
[Unit]
Description=My Application
After=network.target
[Service]
Type=simple
User=www
WorkingDirectory=/opt/app
ExecStart=/usr/bin/python3 app.py
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target# 重新加载配置
systemctl daemon-reload
# 启动 / 停止 / 重启
systemctl start app.service
systemctl stop app.service
systemctl restart app.service
# 设置开机自启
systemctl enable app.service
# 查看服务状态
systemctl status app.service
# 查看所有失败的服务
systemctl --failed四、部署工具
Supervisor - 进程管理
适用场景
适合管理需要持续运行的应用进程,自动重启崩溃的服务,比 nohup 更可靠。
# 安装
pip install supervisor
# 生成默认配置
echo_supervisord_conf > /etc/supervisord.conf; /etc/supervisord.d/app.ini
[program:myapp]
directory=/opt/app
command=/usr/bin/python3 app.py
user=www
autostart=true
autorestart=true
startsecs=10
startretries=3
redirect_stderr=true
stdout_logfile=/var/log/supervisor/myapp.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10# 常用操作
supervisorctl reread # 重新读取配置(不重启进程)
supervisorctl update # 应用配置变更(只启停有变化的进程)
supervisorctl reload # 重启 supervisord 及所有进程(慎用)
supervisorctl status # 查看所有进程状态
supervisorctl start myapp # 启动
supervisorctl stop myapp # 停止
supervisorctl restart myapp # 重启
supervisorctl tail -f myapp # 查看日志Nginx - Web 服务器与反向代理
server {
listen 80;
server_name example.com;
# 强制跳转 HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}upstream backend {
# 加权轮询(max_fails 和 fail_timeout 为健康检查参数)
server 192.168.1.10:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 weight=2 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 weight=1 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_500;
}
}server {
listen 80;
server_name example.com;
root /var/www/dist;
# gzip 压缩
gzip on;
gzip_types text/plain text/css application/json
application/javascript text/xml;
gzip_min_length 1024;
# SPA 路由回退
location / {
try_files $uri $uri/ /index.html;
}
# API 代理
location /api/ {
proxy_pass http://127.0.0.1:8080/;
}
}# Nginx 常用操作
nginx -t # 测试配置语法
nginx -s reload # 平滑重载配置
nginx -s stop # 快速停止
nginx -T # 查看当前生效的完整配置修改配置后务必先测试
每次修改 Nginx 配置后,先执行 nginx -t 确认语法正确,再执行 nginx -s reload。直接 reload 可能导致服务中断。
Docker - 容器化部署
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=db
depends_on:
db:
condition: service_healthy
restart: always
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
db:
image: mysql:8.0
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 3
volumes:
db_data:# 镜像操作
docker build -t myapp:v1.0 . # 构建镜像
docker images # 查看镜像列表
docker rmi image_id # 删除镜像
docker image prune -a # 清理无用镜像
# 容器操作
docker run -d --name myapp -p 3000:3000 myapp:v1.0
docker ps # 查看运行中的容器
docker ps -a # 查看所有容器(含已停止)
docker logs -f --tail 100 myapp # 查看日志
docker exec -it myapp sh # 进入容器
docker stop myapp # 停止容器
docker rm myapp # 删除容器
# compose 操作
docker compose up -d # 后台启动所有服务
docker compose down # 停止并删除所有服务
docker compose logs -f app # 查看指定服务日志
docker compose ps # 查看服务状态
# 清理磁盘空间
docker system prune -a --volumes # ⚠️ 会删除所有未使用的资源
docker system df # 查看 Docker 磁盘占用Docker 磁盘清理要谨慎
docker system prune -a --volumes 会删除所有停止的容器、未使用的镜像和卷数据。生产环境请先用 docker system df 确认,避免误删数据卷。
Kubernetes (K8s) - 容器编排
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: production
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: registry.example.com/myapp:v1.0
ports:
- containerPort: 3000
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 20apiVersion: v1
kind: Service
metadata:
name: myapp-svc
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 3000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- example.com
secretName: tls-secret
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-svc
port:
number: 80# 资源查看
kubectl get pods -n production # 查看 Pod
kubectl get svc,ing -n production # 查看 Service 和 Ingress
kubectl describe pod myapp-xxx -n production # Pod 详情
kubectl top pods -n production # 资源使用情况
# 日志与调试
kubectl logs -f myapp-xxx -n production # 查看日志
kubectl logs myapp-xxx -n production --previous # 查看崩溃前的日志
kubectl exec -it myapp-xxx -n production -- sh # 进入容器
# 部署操作
kubectl apply -f deployment.yaml # 应用配置
kubectl rollout status deployment/myapp # 查看发布进度
kubectl rollout history deployment/myapp # 查看发布历史
kubectl rollout undo deployment/myapp # 回滚到上一版本
# 扩缩容
kubectl scale deployment myapp --replicas=5
kubectl autoscale deployment myapp --min=2 --max=10 --cpu-percent=80五、运维自动化
Jenkins - CI/CD 流水线
持续集成/持续部署
Jenkins 是最常用的开源 CI/CD 工具,通过 Pipeline 实现自动化构建、测试和部署。
// Jenkinsfile (声明式流水线)
pipeline {
agent any
environment {
REGISTRY = 'registry.example.com'
IMAGE = "${REGISTRY}/myapp:${BUILD_NUMBER}"
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'https://github.com/org/repo.git'
}
}
stage('Install & Test') {
steps {
sh 'npm ci'
sh 'npm run test'
sh 'npm run lint'
}
}
stage('Build Image') {
steps {
sh "docker build -t ${IMAGE} ."
sh "docker push ${IMAGE}"
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh """
kubectl set image deployment/myapp \
myapp=${IMAGE} -n production
kubectl rollout status deployment/myapp \
-n production --timeout=300s
"""
}
}
}
post {
failure {
// 发送通知(邮件/钉钉/飞书等)
echo 'Pipeline failed!'
}
success {
echo 'Pipeline succeeded!'
}
}
}Ansible - 批量运维
无需在目标机器安装 Agent
Ansible 通过 SSH 连接远程主机执行操作,无需预装客户端,上手简单。
; /etc/ansible/hosts
[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11
[dbservers]
db1 ansible_host=192.168.1.20
[all:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/id_rsa# deploy.yml - 批量部署应用
---
- name: Deploy Application
hosts: webservers
become: yes
vars:
app_version: "v1.2.0"
app_dir: /opt/app
tasks:
- name: Pull latest code
git:
repo: https://github.com/org/repo.git
dest: "{{ app_dir }}"
version: "{{ app_version }}"
- name: Install dependencies
npm:
path: "{{ app_dir }}"
production: yes
- name: Restart application
systemd:
name: app
state: restarted
daemon_reload: yes
- name: Wait for service to be healthy
uri:
url: "http://localhost:3000/health"
status_code: 200
register: health_result
until: health_result.status == 200
retries: 10
delay: 3# 常用命令
ansible all -m ping # 测试所有主机连通性
ansible webservers -m shell -a "free -h" # 批量执行命令
ansible-playbook deploy.yml # 执行 Playbook
ansible-playbook deploy.yml --check # 模拟执行(不实际操作)
ansible-playbook deploy.yml --limit web1 # 只在 web1 上执行
ansible-playbook deploy.yml -e "app_version=v1.3.0" # 传入变量Crontab - 定时任务
# 编辑当前用户的定时任务
crontab -e
# 查看当前用户的定时任务
crontab -l
# 查看指定用户的定时任务
crontab -l -u wwwCron 表达式速查
┌───────────── 分钟 (0-59)
│ ┌───────────── 小时 (0-23)
│ │ ┌───────────── 日 (1-31)
│ │ │ ┌───────────── 月 (1-12)
│ │ │ │ ┌───────────── 星期 (0-7,0和7都是周日)
│ │ │ │ │
* * * * * command# 常用定时任务示例
# 每天凌晨 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 0 1 * * find /var/log/app -name "*.log" -mtime +30 -delete定时任务注意事项
- 命令务必使用绝对路径,cron 的 PATH 环境变量与登录 shell 不同
- 输出务必重定向,否则会通过邮件发送给用户,占用磁盘
- 建议在脚本中加入文件锁,防止上一次未执行完时重复启动
六、文件传输与同步
scp - 安全文件传输
# 上传文件到远程服务器
scp local-file.tar.gz user@host:/opt/deploy/
# 下载远程文件到本地
scp user@host:/var/log/app.log ./
# 传输整个目录
scp -r ./dist/ user@host:/var/www/
# 指定端口
scp -P 2222 file.tar.gz user@host:/opt/rsync - 增量同步(推荐)
比 scp 更高效
rsync 只传输变化的部分,支持断点续传,适合大量文件的同步。
# 同步本地目录到远程(注意末尾 / 的区别)
rsync -avz ./dist/ user@host:/var/www/dist/
# 带删除(保持远程与本地完全一致)
rsync -avz --delete ./dist/ user@host:/var/www/dist/
# 排除某些文件
rsync -avz --exclude='node_modules' --exclude='.git' ./ user@host:/opt/app/
# 显示传输进度
rsync -avz --progress ./large-file.tar.gz user@host:/opt/
# 模拟执行(不实际传输,查看会同步哪些文件)
rsync -avzn ./dist/ user@host:/var/www/dist/| 参数 | 作用 |
|---|---|
-a | 归档模式(保留权限、时间等) |
-v | 详细输出 |
-z | 传输时压缩 |
-n | 模拟执行(dry-run) |
--delete | 删除目标中多余的文件 |
--progress | 显示传输进度 |
七、SSH 管理
免密登录配置
# 1. 生成密钥对(如果还没有)
ssh-keygen -t ed25519 -C "your-email@example.com"
# 2. 将公钥复制到远程主机
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
# 3. 测试连接
ssh user@hostSSH Config 简化连接
# ~/.ssh/config
Host prod
HostName 192.168.1.10
User deploy
Port 22
IdentityFile ~/.ssh/id_ed25519
Host staging
HostName 192.168.1.20
User deploy
# 跳板机配置
Host internal
HostName 10.0.0.5
User admin
ProxyJump prod配置后可直接使用别名连接:
ssh prod # 代替 ssh deploy@192.168.1.10
scp file.txt staging:/opt/ # 代替 scp file.txt deploy@192.168.1.20:/opt/
ssh internal # 通过 prod 跳转连接 internalSSH 隧道
# 本地端口转发:访问本地 3307 等于访问远程的 MySQL
ssh -L 3307:localhost:3306 user@db-server
# 远程端口转发:让远程服务器的 8080 转发到本地 3000
ssh -R 8080:localhost:3000 user@remote-server
# SOCKS 代理
ssh -D 1080 user@proxy-server八、安全加固
防火墙管理
# 查看状态
firewall-cmd --state
firewall-cmd --list-all
# 开放端口
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp
# 移除端口
firewall-cmd --permanent --remove-port=8080/tcp
# 重新加载规则
firewall-cmd --reload# 启用防火墙
ufw enable
# 查看状态
ufw status verbose
# 开放端口
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow from 192.168.1.0/24 to any port 22
# 拒绝端口
ufw deny 3306/tcp
# 删除规则
ufw delete allow 8080/tcp# 查看规则
iptables -L -n -v
# 开放端口
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# 拒绝指定 IP
iptables -A INPUT -s 10.0.0.5 -j DROP
# 只允许特定 IP 访问 SSH
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
# 保存规则(CentOS)
service iptables savefail2ban - 防暴力破解
# 安装
yum install -y fail2ban # CentOS
apt install -y fail2ban # Ubuntu# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3# 常用操作
systemctl enable --now fail2ban
fail2ban-client status # 查看总体状态
fail2ban-client status sshd # 查看 SSH 监控详情
fail2ban-client set sshd unbanip 1.2.3.4 # 手动解封 IP用户与权限
# 创建用户并配置
useradd -m -s /bin/bash deploy
passwd deploy
# 添加 sudo 权限
usermod -aG sudo deploy # Ubuntu
usermod -aG wheel deploy # CentOS
# 限制 root 远程登录
# 编辑 /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no # 禁用密码登录,仅允许密钥
# 重启 SSH 服务
systemctl restart sshd禁用 root 远程登录前
确保已经配置好普通用户的 SSH 密钥登录和 sudo 权限,否则你将无法再登录服务器!
九、SSL 证书管理
Let's Encrypt + Certbot(免费证书)
推荐方案
Let's Encrypt 提供免费的 SSL 证书,配合 Certbot 可以实现自动申请和续签,是目前最主流的免费 HTTPS 方案。
# 安装 Certbot
yum install -y certbot python3-certbot-nginx # CentOS
apt install -y certbot python3-certbot-nginx # Ubuntu# 自动申请证书并配置 Nginx
certbot --nginx -d example.com -d www.example.com
# Certbot 会自动:
# 1. 申请证书
# 2. 修改 Nginx 配置,添加 SSL 相关指令
# 3. 设置 HTTP → HTTPS 重定向# 只申请证书,不修改 Nginx 配置
certbot certonly --nginx -d example.com
# 使用 standalone 模式(需要先停止占用 80 端口的服务)
certbot certonly --standalone -d example.com
# 使用 DNS 验证(适合内网或无法通过 HTTP 验证的场景)
certbot certonly --manual --preferred-challenges dns -d example.com# 申请通配符证书(需要 DNS 验证)
certbot certonly --manual --preferred-challenges dns \
-d example.com -d "*.example.com"
# 证书文件位置
# /etc/letsencrypt/live/example.com/fullchain.pem (证书链)
# /etc/letsencrypt/live/example.com/privkey.pem (私钥)# 查看已有证书
certbot certificates
# 测试自动续签(不实际执行)
certbot renew --dry-run
# 手动续签
certbot renew
# 强制续签(证书未过期也续签)
certbot renew --force-renewal自动续签
Certbot 安装后会自动创建 systemd timer 或 cron job 进行续签检查。可以验证:
# 检查 systemd timer
systemctl list-timers | grep certbot
# 或检查 cron
cat /etc/cron.d/certbot证书有效期为 90 天,Certbot 会在到期前 30 天内自动续签。
手动证书配置
# Nginx SSL 最佳实践配置
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# 安全参数
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# HSTS(强制 HTTPS,谨慎开启)
add_header Strict-Transport-Security "max-age=63072000" always;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
}证书到期监控
生产环境建议加一个定时检测脚本,避免证书意外过期:
# 检查证书过期时间
echo | openssl s_client -connect example.com:443 2>/dev/null | \
openssl x509 -noout -dates十、Vim 基础操作
服务器必备技能
几乎所有 Linux 服务器都预装了 Vim,修改配置文件、查看日志时离不开它。掌握基础操作即可应对大部分场景。
三种模式
| 模式 | 进入方式 | 作用 |
|---|---|---|
| Normal(普通模式) | Esc | 移动光标、删除、复制、粘贴 |
| Insert(插入模式) | i / a / o | 输入文本 |
| Command(命令模式) | : | 保存、退出、搜索替换 |
保存与退出
:w # 保存
:q # 退出(未修改时)
:wq # 保存并退出
:q! # 强制退出不保存
:wq! # 强制保存并退出(对 vim -R 只读模式打开的文件)
ZZ # 快捷保存退出(Normal 模式下)移动与编辑
# 移动
gg # 跳到文件开头
G # 跳到文件末尾
:100 # 跳到第 100 行
0 / $ # 跳到行首 / 行尾
w / b # 按单词前进 / 后退
Ctrl+f / Ctrl+b # 向下 / 向上翻页
# 编辑
i # 在光标前插入
a # 在光标后插入
o / O # 在下方 / 上方新开一行
dd # 删除整行
yy # 复制整行
p # 粘贴
u # 撤销
Ctrl+r # 重做搜索与查找
/keyword # 向下搜索
?keyword # 向上搜索
n / N # 下一个 / 上一个匹配
* # 向下搜索光标所在单词
# # 向上搜索光标所在单词
# 取消搜索高亮
:noh
# 搜索正则表达式
/error\|warn # 搜索 error 或 warn
/^server # 搜索以 server 开头的行
/port\s*= # 搜索 port = (中间可有空格)
# 跳到匹配的括号(适合排查配置文件嵌套)
% # 光标在 { 上按 %,跳到对应的 }替换
# 基础替换
:s/old/new/ # 替换当前行第一个匹配
:s/old/new/g # 替换当前行所有匹配
:%s/old/new/g # 替换全文所有匹配
:%s/old/new/gc # 替换全文,逐个确认(y/n/a/q)
# 范围替换
:10,20s/old/new/g # 替换第 10~20 行
:.,$s/old/new/g # 从当前行到文件末尾
:'<,'>s/old/new/g # 替换 Visual 模式选中的区域
# 正则替换
:%s/port\s*=\s*\d\+/port = 8080/g # 替换所有 port = xxx
:%s/\r//g # 删除 Windows 换行符 ^M
:%s/\s\+$//g # 删除行尾空格
# 带确认的替换提示:y(替换) n(跳过) a(全部替换) q(退出) l(替换并退出)缩进与格式化
单行 / 计数缩进(Normal 模式)
>> # 当前行右缩进一级
<< # 当前行左缩进一级
3>> # 当前行起共 3 行右缩进
5<< # 当前行起共 5 行左缩进
>G # 从当前行到文件末尾全部右缩进
<gg # 从当前行到文件开头全部左缩进选中多行缩进(Visual 模式,最常用)
以「选中第 10~19 行并右缩进」为例,完整操作步骤:
# 方法一:行选择模式(V)
:10 # 1. 先跳到第 10 行
V # 2. 按 V 进入行选择(Visual Line)模式,当前行高亮
9j # 3. 按 9j 向下选中 9 行(共选中 10 行,即第 10~19 行)
> # 4. 按 > 右缩进一级(按 < 则左缩进)
. # 5.(可选)按 . 重复上一次缩进操作,继续缩进一级
# 方法二:直接指定行范围(Command 模式)
:10,19> # 第 10~19 行右缩进一级
:10,19> 3 # 第 10~19 行右缩进三级(> 后加数字指定级数)
:10,19< # 第 10~19 行左缩进一级
# 方法三:用搜索选中区域
/server # 搜索跳到 server 所在行
V # 进入行选择模式
/} # 搜索到 } 所在行(自动扩展选区)
> # 右缩进选中区域Visual 模式下连续缩进
选中后按 > 缩进一次就会退出 Visual 模式。如果需要连续缩进多级:
- 方式一:缩进后按
.重复操作(推荐) - 方式二:缩进后按
gv重新选中上次的区域,再按>
自动缩进
= # Visual 模式下对选中区域自动缩进(按语法对齐)
gg=G # 全文自动格式化缩进
10== # 当前行起 10 行自动缩进缩进设置
:set tabstop=4 # Tab 显示为 4 个空格宽度
:set shiftwidth=4 # >> / << 的缩进宽度
:set expandtab # 用空格代替 Tab
:set autoindent # 新行自动继承上一行缩进
:retab # 将文件中已有的 Tab 转换为空格编辑配置文件的推荐设置
打开 Nginx、YAML 等配置文件时,可以一次性设置:
:set tabstop=4 shiftwidth=4 expandtab autoindent或写入 ~/.vimrc 永久生效。
其他高频操作
展开查看更多实用操作
# 显示行号
:set number # 显示绝对行号
:set relativenumber # 显示相对行号(方便 5j、10k 跳转)
# 多行编辑(批量注释/取消注释)
# macOS Terminal.app 中若 Ctrl+v 被拦截,可用 Ctrl+q 代替
Ctrl+v # 进入列选择(Visual Block)模式
j/k # 上下选择行
I # 在选中列前插入(输入 # 即可批量注释)
Esc # 应用到所有选中行
# 批量取消注释
Ctrl+v → 选中 # 列 → d
# 删除操作
dw # 删除一个单词
d$ / D # 删除到行尾
d0 # 删除到行首
dG # 删除到文件末尾
:.,$d # 删除当前行到文件末尾
# 复制粘贴
yy # 复制当前行
5yy # 复制当前行起的 5 行
p / P # 在下方 / 上方粘贴
# 语法高亮
:syntax on
:set filetype=nginx # 手动指定文件类型高亮
# 对比两个文件
vimdiff file1.conf file2.conf
# 以 sudo 权限保存(忘记用 sudo 打开时)
:w !sudo tee %
# 打开文件时直接跳到指定行
vim +100 /etc/nginx/nginx.conf十一、包管理器
yum / dnf(CentOS / RHEL / Fedora)
# 搜索软件包
yum search nginx
# 安装
yum install -y nginx
# 卸载
yum remove nginx
# 查看已安装的包
yum list installed | grep nginx
# 查看包信息
yum info nginx
# 查看文件属于哪个包
yum provides /usr/sbin/nginx
# 更新所有包
yum update -y
# 清理缓存
yum clean all
# 查看可用的仓库
yum repolistCentOS 8+ 使用 dnf
dnf 是 yum 的下一代版本,命令用法基本一致,直接将 yum 替换为 dnf 即可。
apt(Ubuntu / Debian)
# 更新软件源索引(安装前务必执行)
apt update
# 搜索
apt search nginx
# 安装
apt install -y nginx
# 卸载(保留配置)
apt remove nginx
# 卸载(同时删除配置)
apt purge nginx
# 查看已安装的包
apt list --installed | grep nginx
# 查看包信息
apt show nginx
# 更新所有包
apt upgrade -y
# 清理不再需要的依赖
apt autoremove -y
# 清理下载的包缓存
apt cleanapt update vs apt upgrade
apt update:只更新软件源索引,不安装任何东西apt upgrade:根据索引升级已安装的软件包
安装新软件前务必先执行 apt update,否则可能找不到包或安装旧版本。
通用技巧
# 查看某个命令由哪个包提供
which nginx && rpm -qf $(which nginx) # CentOS
which nginx && dpkg -S $(which nginx) # Ubuntu
# 查看包安装了哪些文件
rpm -ql nginx # CentOS
dpkg -L nginx # Ubuntu
# 添加第三方仓库(以 Docker 为例)
# CentOS
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# Ubuntu
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"十二、数据库常用操作
MySQL
# 连接数据库
mysql -u root -p
mysql -u root -p -h 192.168.1.20 -P 3306
# 查看数据库列表
SHOW DATABASES;
# 查看表结构
USE mydb;
SHOW TABLES;
DESC users;
# 查看当前连接数
SHOW PROCESSLIST;
# 查看数据库大小
SELECT table_schema AS 'Database',
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)'
FROM information_schema.tables
GROUP BY table_schema;# 备份单个数据库
mysqldump -u root -p mydb > mydb_backup.sql
# 备份所有数据库
mysqldump -u root -p --all-databases > all_backup.sql
# 备份并压缩
mysqldump -u root -p mydb | gzip > mydb_$(date +%Y%m%d).sql.gz
# 恢复数据库
mysql -u root -p mydb < mydb_backup.sql
# 从压缩文件恢复
gunzip < mydb_20240115.sql.gz | mysql -u root -p mydb# 查看慢查询是否开启
SHOW VARIABLES LIKE 'slow_query%';
# 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2; -- 超过 2 秒记录
# 查看慢查询日志
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# 分析查询执行计划
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
# 查看表索引
SHOW INDEX FROM users;Redis
# 连接 Redis
redis-cli
redis-cli -h 192.168.1.20 -p 6379 -a password
# 基础操作
PING # 测试连接
INFO # 查看服务器信息
INFO memory # 查看内存使用
DBSIZE # 查看 key 数量
# 键操作
KEYS pattern* # 搜索 key(生产环境慎用!)
SCAN 0 MATCH pattern* COUNT 100 # 安全的 key 扫描
TTL keyname # 查看过期时间
TYPE keyname # 查看值类型
# 监控
MONITOR # 实时监控所有命令(调试用)
SLOWLOG GET 10 # 查看最近 10 条慢查询
INFO clients # 查看客户端连接信息
CLIENT LIST # 查看所有客户端连接生产环境禁止使用 KEYS *
KEYS * 会遍历所有 key,在数据量大时会阻塞 Redis 导致服务不可用。请使用 SCAN 命令代替。
# 持久化操作
BGSAVE # 手动触发 RDB 快照
BGREWRITEAOF # 手动触发 AOF 重写
LASTSAVE # 查看最后一次 RDB 时间
# 数据备份
redis-cli BGSAVE && cp /var/lib/redis/dump.rdb /backup/redis/
# 清空数据(危险操作!)
FLUSHDB # 清空当前数据库
FLUSHALL # 清空所有数据库十三、Shell 脚本基础
运维自动化的基石
掌握 Shell 脚本基础,可以将重复性运维操作封装为脚本,配合 Crontab 实现自动化。
变量与字符串
#!/bin/bash
# 变量赋值(等号两边不能有空格)
APP_NAME="myapp"
APP_PORT=3000
LOG_DIR="/var/log/${APP_NAME}"
# 命令结果赋值
CURRENT_DATE=$(date +%Y%m%d)
IP_ADDR=$(hostname -I | awk '{print $1}')
# 字符串操作
FILE="backup_2024.tar.gz"
echo "${FILE%.tar.gz}" # 去掉后缀 → backup_2024
echo "${FILE##*.}" # 获取扩展名 → gz
echo "${FILE/2024/2025}" # 替换 → backup_2025.tar.gz条件判断
#!/bin/bash
# 文件判断
if [ -f "/etc/nginx/nginx.conf" ]; then
echo "Nginx config exists"
fi
if [ -d "/var/log/app" ]; then
echo "Log directory exists"
fi
# 字符串判断
if [ -z "$VAR" ]; then
echo "VAR is empty"
fi
if [ "$ENV" = "production" ]; then
echo "Running in production"
fi
# 数值比较
if [ "$COUNT" -gt 100 ]; then
echo "Count exceeds 100"
fi| 文件判断 | 说明 | 数值比较 | 说明 |
|---|---|---|---|
-f | 文件存在 | -eq | 等于 |
-d | 目录存在 | -ne | 不等于 |
-r | 可读 | -gt | 大于 |
-w | 可写 | -lt | 小于 |
-x | 可执行 | -ge | 大于等于 |
-s | 文件非空 | -le | 小于等于 |
循环
#!/bin/bash
# 遍历列表
for server in web1 web2 web3; do
echo "Deploying to $server..."
ssh deploy@$server "cd /opt/app && git pull && systemctl restart app"
done
# 遍历文件
for file in /var/log/app/*.log; do
echo "Processing $file ($(wc -l < "$file") lines)"
done
# while 循环(等待服务启动)
MAX_RETRY=30
COUNT=0
while [ $COUNT -lt $MAX_RETRY ]; do
if curl -sf http://localhost:3000/health > /dev/null; then
echo "Service is healthy!"
break
fi
COUNT=$((COUNT + 1))
echo "Waiting... ($COUNT/$MAX_RETRY)"
sleep 2
done实用脚本示例
部署脚本
#!/bin/bash
set -euo pipefail
APP_DIR="/opt/app"
BACKUP_DIR="/opt/backup"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
echo "=== Starting deployment at $TIMESTAMP ==="
# 1. 备份当前版本
echo "[1/4] Backing up current version..."
tar -czf "$BACKUP_DIR/app_$TIMESTAMP.tar.gz" -C "$APP_DIR" .
# 2. 拉取最新代码
echo "[2/4] Pulling latest code..."
cd "$APP_DIR"
git pull origin main
# 3. 安装依赖
echo "[3/4] Installing dependencies..."
npm ci --only=production
# 4. 重启服务
echo "[4/4] Restarting service..."
systemctl restart app
# 5. 健康检查
sleep 3
if curl -sf http://localhost:3000/health > /dev/null; then
echo "=== Deployment successful! ==="
else
echo "=== Health check failed, rolling back... ==="
tar -xzf "$BACKUP_DIR/app_$TIMESTAMP.tar.gz" -C "$APP_DIR"
systemctl restart app
exit 1
fi日志清理脚本
#!/bin/bash
# 清理超过 N 天的日志文件
LOG_DIRS=("/var/log/app" "/var/log/nginx")
KEEP_DAYS=30
for dir in "${LOG_DIRS[@]}"; do
if [ -d "$dir" ]; then
COUNT=$(find "$dir" \( -name "*.log" -o -name "*.log.gz" \) \
-mtime +$KEEP_DAYS | wc -l)
find "$dir" \( -name "*.log" -o -name "*.log.gz" \) \
-mtime +$KEEP_DAYS -delete
echo "Cleaned $COUNT files from $dir"
fi
done
# 清理 Docker 日志(如果使用 Docker)
if command -v docker &> /dev/null; then
docker system prune -f --filter "until=720h"
echo "Docker cleanup done"
fi服务器巡检脚本
#!/bin/bash
# 服务器状态巡检,输出报告
echo "=========================================="
echo " Server Health Report - $(date)"
echo "=========================================="
echo -e "\n--- System Info ---"
uname -a
uptime
echo -e "\n--- CPU & Load ---"
top -bn1 | head -5
echo -e "\n--- Memory ---"
free -h
echo -e "\n--- Disk Usage ---"
df -h | grep -v tmpfs
echo -e "\n--- Top 5 Memory Processes ---"
ps aux --sort=-%mem | head -6
echo -e "\n--- Top 5 CPU Processes ---"
ps aux --sort=-%cpu | head -6
echo -e "\n--- Network Connections ---"
ss -s
echo -e "\n--- Failed Services ---"
systemctl --failed
echo -e "\n--- Recent Errors (last 1h) ---"
journalctl -p err --since "1 hour ago" --no-pager | tail -20
echo -e "\n=========================================="
echo " Report Complete"
echo "=========================================="十四、实用脚本速查
快速排查清单
# 服务器卡顿时的排查顺序
uptime # 1. 看负载
free -h # 2. 看内存
df -h # 3. 看磁盘
top -bn1 | head -20 # 4. 看 CPU 和进程
ss -tlnp # 5. 看端口和连接
dmesg | tail -20 # 6. 看内核日志
journalctl -p err --since today # 7. 看今天的错误日志常用一行命令
# 查找最近 24 小时内修改的文件
find /opt/app -type f -mtime -1
# 查找大于 100M 的文件
find / -type f -size +100M -exec ls -lh {} \;
# 查看 TCP 连接状态分布
ss -ant | awk '{print $1}' | sort | uniq -c | sort -rn
# 批量替换文件内容
find /opt/app -name "*.conf" -exec sed -i 's/old-domain/new-domain/g' {} \;
# 实时查看网络连接数
watch -n 1 "ss -s"
# 快速创建指定大小的测试文件
dd if=/dev/zero of=test.file bs=1M count=100