Skip to content

Linux 服务器运维指南

作者:Atom
字数统计:8.5k 字
阅读时长:36 分钟

本文整理了服务器日常运维中最常用的操作,涵盖日志排查、系统监控、部署工具、自动化运维等核心场景,方便快速查阅。

一、日志查看与管理

tail - 实时追踪日志

最常用的日志命令

tail -f 是线上排查问题的第一选择,可以实时追踪日志输出。

bash
# 实时追踪日志(最常用)
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.log
tail -f 与 tail -F 的区别
  • tail -f:追踪文件描述符,文件被删除重建后会失效
  • tail -F:追踪文件名,文件被删除重建后会自动重新打开(推荐用于日志轮转场景)

less - 交互式日志浏览

比 cat 更适合查看大文件

less 支持前后翻页、搜索关键字,不会一次性加载整个文件到内存。

bash
less /var/log/app.log
快捷键作用
Space / f向下翻一页
b向上翻一页
/keyword向下搜索关键字
?keyword向上搜索关键字
n / N下一个 / 上一个匹配
g / G跳到文件开头 / 结尾
F进入追踪模式(类似 tail -f)
q退出

grep - 日志过滤

bash
# 搜索关键字并显示前后 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.log

journalctl - systemd 日志

管理 systemd 服务的日志利器

现代 Linux 发行版中,systemd 管理的服务日志统一由 journalctl 查看。

bash
# 查看指定服务的日志
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=500M

logrotate - 日志轮转

不配置日志轮转,磁盘迟早被撑满

生产环境必须配置日志轮转策略,避免单个日志文件无限增长。

bash
# /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
}
bash
# 手动测试轮转配置(不实际执行)
logrotate -d /etc/logrotate.d/app

# 手动强制执行轮转
logrotate -f /etc/logrotate.d/app

I/O 重定向 - 日志输出控制

理解文件描述符

Linux 中每个进程默认有三个文件描述符:

  • 0 (stdin):标准输入
  • 1 (stdout):标准输出(正常日志)
  • 2 (stderr):标准错误输出(错误日志)

重定向就是改变这些输出的去向。

基础重定向

bash
# 标准输出重定向到文件
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 的顺序很重要

bash
# ✅ 正确:stderr 跟随 stdout 一起写入 file.log
command > file.log 2>&1

# ❌ 错误:stderr 指向的是 stdout 的原始位置(终端),不会写入文件
command 2>&1 > file.log

2>&1 的意思是"把文件描述符 2 指向文件描述符 1 当前指向的位置",所以必须放在 > 之后。

丢弃输出(/dev/null)

bash
# 丢弃标准输出(只看错误)
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"

实用组合

bash
# 后台运行 + 日志重定向(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
文件描述符进阶用法
bash
# 自定义文件描述符
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 - 基础文件查看

bash
# 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

注意

> 是覆盖写入,>> 是追加写入,生产环境务必区分!

二、系统监控与性能分析

系统概览

bash
# 系统运行时间、负载
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 - 进程监控

bash
top

# 进入 top 后的快捷键:
# P - 按 CPU 排序
# M - 按内存排序
# T - 按运行时间排序
# k - 杀死进程(输入 PID)
# 1 - 显示每个 CPU 核心的使用率
# q - 退出
bash
# 安装
yum install -y htop    # CentOS
apt install -y htop    # Ubuntu

# 使用
htop

# htop 优势:
# - 彩色界面,更直观
# - 支持鼠标操作
# - 可以横向/纵向滚动
# - 支持树状视图(F5)

网络监控

bash
# 查看端口占用
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 监控

bash
# I/O 使用情况(每 2 秒刷新)
iostat -x 2

# 查看哪些进程在读写磁盘
iotop

# 查看文件系统的 inode 使用(inode 耗尽也会导致无法创建文件)
df -i

三、进程管理

进程查看与终止

bash
# 查看所有进程
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

bash
# 后台运行,输出重定向到文件
nohup python app.py > /var/log/app.log 2>&1 &

# 查看后台任务
jobs -l
ini
# 创建服务文件 /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
bash
# 重新加载配置
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 更可靠。

bash
# 安装
pip install supervisor

# 生成默认配置
echo_supervisord_conf > /etc/supervisord.conf
ini
; /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
bash
# 常用操作
supervisorctl reread              # 重新读取配置(不重启进程)
supervisorctl update              # 应用配置变更(只启停有变化的进程)
supervisorctl reload              # 重启 supervisord 及所有进程(慎用)
supervisorctl status              # 查看所有进程状态
supervisorctl start myapp         # 启动
supervisorctl stop myapp          # 停止
supervisorctl restart myapp       # 重启
supervisorctl tail -f myapp       # 查看日志

Nginx - Web 服务器与反向代理

nginx
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";
    }
}
nginx
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;
    }
}
nginx
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/;
    }
}
bash
# Nginx 常用操作
nginx -t                          # 测试配置语法
nginx -s reload                   # 平滑重载配置
nginx -s stop                     # 快速停止
nginx -T                          # 查看当前生效的完整配置

修改配置后务必先测试

每次修改 Nginx 配置后,先执行 nginx -t 确认语法正确,再执行 nginx -s reload。直接 reload 可能导致服务中断。

Docker - 容器化部署

dockerfile
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"]
yaml
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:
bash
# 镜像操作
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) - 容器编排

yaml
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: 20
yaml
apiVersion: 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
bash
# 资源查看
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 实现自动化构建、测试和部署。

groovy
// 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 连接远程主机执行操作,无需预装客户端,上手简单。

ini
; /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
yaml
# 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
bash
# 常用命令
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 - 定时任务

bash
# 编辑当前用户的定时任务
crontab -e

# 查看当前用户的定时任务
crontab -l

# 查看指定用户的定时任务
crontab -l -u www

Cron 表达式速查

┌───────────── 分钟 (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 0 1 * * find /var/log/app -name "*.log" -mtime +30 -delete

定时任务注意事项

  1. 命令务必使用绝对路径,cron 的 PATH 环境变量与登录 shell 不同
  2. 输出务必重定向,否则会通过邮件发送给用户,占用磁盘
  3. 建议在脚本中加入文件锁,防止上一次未执行完时重复启动

六、文件传输与同步

scp - 安全文件传输

bash
# 上传文件到远程服务器
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 只传输变化的部分,支持断点续传,适合大量文件的同步。

bash
# 同步本地目录到远程(注意末尾 / 的区别)
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 管理

免密登录配置

bash
# 1. 生成密钥对(如果还没有)
ssh-keygen -t ed25519 -C "your-email@example.com"

# 2. 将公钥复制到远程主机
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host

# 3. 测试连接
ssh user@host

SSH Config 简化连接

bash
# ~/.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

配置后可直接使用别名连接:

bash
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 跳转连接 internal

SSH 隧道

bash
# 本地端口转发:访问本地 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

八、安全加固

防火墙管理

bash
# 查看状态
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
bash
# 启用防火墙
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
bash
# 查看规则
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 save

fail2ban - 防暴力破解

bash
# 安装
yum install -y fail2ban          # CentOS
apt install -y fail2ban          # Ubuntu
ini
# /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
bash
# 常用操作
systemctl enable --now fail2ban
fail2ban-client status            # 查看总体状态
fail2ban-client status sshd       # 查看 SSH 监控详情
fail2ban-client set sshd unbanip 1.2.3.4  # 手动解封 IP

用户与权限

bash
# 创建用户并配置
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 方案。

bash
# 安装 Certbot
yum install -y certbot python3-certbot-nginx    # CentOS
apt install -y certbot python3-certbot-nginx     # Ubuntu
bash
# 自动申请证书并配置 Nginx
certbot --nginx -d example.com -d www.example.com

# Certbot 会自动:
# 1. 申请证书
# 2. 修改 Nginx 配置,添加 SSL 相关指令
# 3. 设置 HTTP → HTTPS 重定向
bash
# 只申请证书,不修改 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
bash
# 申请通配符证书(需要 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    (私钥)
bash
# 查看已有证书
certbot certificates

# 测试自动续签(不实际执行)
certbot renew --dry-run

# 手动续签
certbot renew

# 强制续签(证书未过期也续签)
certbot renew --force-renewal

自动续签

Certbot 安装后会自动创建 systemd timer 或 cron job 进行续签检查。可以验证:

bash
# 检查 systemd timer
systemctl list-timers | grep certbot

# 或检查 cron
cat /etc/cron.d/certbot

证书有效期为 90 天,Certbot 会在到期前 30 天内自动续签。

手动证书配置

nginx
# 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;
}

证书到期监控

生产环境建议加一个定时检测脚本,避免证书意外过期:

bash
# 检查证书过期时间
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(命令模式):保存、退出、搜索替换

保存与退出

bash
:w                # 保存
:q                # 退出(未修改时)
:wq               # 保存并退出
:q!               # 强制退出不保存
:wq!              # 强制保存并退出(对 vim -R 只读模式打开的文件)
ZZ                # 快捷保存退出(Normal 模式下)

移动与编辑

bash
# 移动
gg                # 跳到文件开头
G                 # 跳到文件末尾
:100              # 跳到第 100 行
0 / $             # 跳到行首 / 行尾
w / b             # 按单词前进 / 后退
Ctrl+f / Ctrl+b   # 向下 / 向上翻页

# 编辑
i                 # 在光标前插入
a                 # 在光标后插入
o / O             # 在下方 / 上方新开一行
dd                # 删除整行
yy                # 复制整行
p                 # 粘贴
u                 # 撤销
Ctrl+r            # 重做

搜索与查找

bash
/keyword          # 向下搜索
?keyword          # 向上搜索
n / N             # 下一个 / 上一个匹配
*                 # 向下搜索光标所在单词
#                 # 向上搜索光标所在单词

# 取消搜索高亮
:noh

# 搜索正则表达式
/error\|warn      # 搜索 error 或 warn
/^server          # 搜索以 server 开头的行
/port\s*=         # 搜索 port = (中间可有空格)

# 跳到匹配的括号(适合排查配置文件嵌套)
%                 # 光标在 { 上按 %,跳到对应的 }

替换

bash
# 基础替换
: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 模式)

bash
>>                # 当前行右缩进一级
<<                # 当前行左缩进一级
3>>               # 当前行起共 3 行右缩进
5<<               # 当前行起共 5 行左缩进
>G                # 从当前行到文件末尾全部右缩进
<gg               # 从当前行到文件开头全部左缩进

选中多行缩进(Visual 模式,最常用)

以「选中第 10~19 行并右缩进」为例,完整操作步骤:

bash
# 方法一:行选择模式(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 重新选中上次的区域,再按 >

自动缩进

bash
=                 # Visual 模式下对选中区域自动缩进(按语法对齐)
gg=G              # 全文自动格式化缩进
10==              # 当前行起 10 行自动缩进

缩进设置

bash
:set tabstop=4          # Tab 显示为 4 个空格宽度
:set shiftwidth=4       # >> / << 的缩进宽度
:set expandtab          # 用空格代替 Tab
:set autoindent         # 新行自动继承上一行缩进
:retab                  # 将文件中已有的 Tab 转换为空格

编辑配置文件的推荐设置

打开 Nginx、YAML 等配置文件时,可以一次性设置:

bash
:set tabstop=4 shiftwidth=4 expandtab autoindent

或写入 ~/.vimrc 永久生效。

其他高频操作

展开查看更多实用操作
bash
# 显示行号
: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)

bash
# 搜索软件包
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 repolist

CentOS 8+ 使用 dnf

dnfyum 的下一代版本,命令用法基本一致,直接将 yum 替换为 dnf 即可。

apt(Ubuntu / Debian)

bash
# 更新软件源索引(安装前务必执行)
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 clean

apt update vs apt upgrade

  • apt update:只更新软件源索引,不安装任何东西
  • apt upgrade:根据索引升级已安装的软件包

安装新软件前务必先执行 apt update,否则可能找不到包或安装旧版本。

通用技巧

bash
# 查看某个命令由哪个包提供
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

sql
# 连接数据库
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;
bash
# 备份单个数据库
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
sql
# 查看慢查询是否开启
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

bash
# 连接 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 命令代替。

bash
# 持久化操作
BGSAVE                            # 手动触发 RDB 快照
BGREWRITEAOF                      # 手动触发 AOF 重写
LASTSAVE                          # 查看最后一次 RDB 时间

# 数据备份
redis-cli BGSAVE && cp /var/lib/redis/dump.rdb /backup/redis/

# 清空数据(危险操作!)
FLUSHDB                           # 清空当前数据库
FLUSHALL                          # 清空所有数据库

十三、Shell 脚本基础

运维自动化的基石

掌握 Shell 脚本基础,可以将重复性运维操作封装为脚本,配合 Crontab 实现自动化。

变量与字符串

bash
#!/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

条件判断

bash
#!/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小于等于

循环

bash
#!/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

实用脚本示例

部署脚本
bash
#!/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
日志清理脚本
bash
#!/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
服务器巡检脚本
bash
#!/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 "=========================================="

十四、实用脚本速查

快速排查清单

bash
# 服务器卡顿时的排查顺序
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. 看今天的错误日志

常用一行命令

bash
# 查找最近 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