先理解:Linux 命令到底在干嘛?
Linux 服务器 = 一栋办公大楼。你用 SSH 登录服务器,就像刷卡进了大楼当管理员。
你需要知道:哪个房间在干嘛(进程管理)、哪个房间漏水了(日志排查)、
哪层楼的储藏室快塞满了(磁盘检查)、大楼的门和走廊通不通(网络排查)。
Linux 命令就是你的管理工具箱。
想象一个真实的下午…
周五下午 5 点 50 分,你正准备下班。突然群里炸了:
运营@小李:"线上下单报错了!用户投诉刷屏了!"
测试@小王:"我这边也访问不了,页面一直转圈!"
主管:@你 快看看服务器什么情况
这时候你需要的不是翻教科书,而是肌肉记忆里那几条救命的 Linux 命令。
下面这些命令不是让你背的,是让你在出事的时候能 60 秒内定位问题的。
每个命令后面都标注了:什么场景用、为什么这么用。
1. Linux 常用命令速查(手把手)
记忆口诀:文件操作像在你自己电脑上整理文件夹;进程管理像任务管理器(Ctrl+Shift+Esc);磁盘检查像看手机存储空间;网络排查像测网速+ping。
📁 文件操作
什么时候用?找配置文件、看日志文件、备份配置、清理旧日志。你 90% 的文件操作就是 cd 到目录、tail -f 看日志、grep 搜报错。
| 命令 | 说明 | 示例 |
| ls -la | 列出所有文件含隐藏文件 | ls -la /home/app/ |
| cd | 切换目录 | cd /var/log && cd - |
| cp | 复制文件/目录 | cp -r config/ config_bak/ |
| mv | 移动/重命名 | mv app.jar app_20240101.jar |
| rm | 删除文件/目录 | rm -rf /tmp/old_logs/ |
| find | 查找文件 | find / -name "app.log" 2>/dev/null |
| grep | 搜索文本内容 | grep -rn "ERROR" /var/log/app/ |
| cat | 查看文件内容 | cat /etc/nginx/nginx.conf |
| tail -f | 实时查看日志尾部 | tail -f /var/log/app/app.log |
| less | 分页查看大文件 | less +F /var/log/syslog |
Bash# 常用组合:查找并统计ERROR数量
grep -c "ERROR" /var/log/app/app.log
# 查看最近100行日志
tail -n 100 /var/log/app/app.log
# 查找大于100M的日志文件
find /var/log -name "*.log" -size +100M
⚙️ 进程管理
什么时候用?服务卡住了、CPU 飙高了、端口被占了——这就是 Linux 版的"任务管理器"。先用 top 找到最占资源的进程,再用 kill 处理。
| 命令 | 说明 | 示例 |
| ps aux | 查看所有进程 | ps aux | grep java |
| top | 实时进程监控 | top -p $(pgrep -d',' java) |
| htop | 增强版top(需安装) | htop --sort=PERCENT_MEM |
| kill | 优雅终止进程 | kill 12345 |
| kill -9 | 强制杀死进程 | kill -9 12345 |
| lsof -i :8080 | 查看端口占用 | lsof -i :8080 |
| netstat -tlnp | 查看监听端口 | netstat -tlnp | grep 8080 |
Bash# 查找占用8080端口的进程并杀掉
lsof -i :8080 | grep LISTEN | awk '{print $2}' | xargs kill -9
# 查看Java进程的内存使用
ps aux | grep java | awk '{printf "PID: %s MEM: %s%% CPU: %s%%\n", $2, $4, $3}'
# 查看进程树
pstree -p | grep java
💾 磁盘相关
什么时候用?磁盘满了!这是线上最常见的事故之一——日志文件疯狂增长把磁盘写满,导致数据库写入失败、服务无响应。养成习惯:每周看一次 df -h。
| 命令 | 说明 | 示例 |
| df -h | 查看磁盘使用情况 | df -h /data |
| du -sh * | 查看目录大小 | du -sh /var/log/* |
| ncdu | 交互式磁盘分析(需安装) | ncdu /var/log |
Bash# 查看磁盘使用率,超过80%报警
df -h | awk 'NR>1 && int($5)>80 {print $1, $5, "使用率过高!"}'
# 找出最大的10个文件
du -ah /var/log | sort -rh | head -10
# 快速清理旧日志(保留7天)
find /var/log/app -name "*.log.*" -mtime +7 -delete
🌐 网络相关
什么时候用?服务之间连不上、接口超时、DNS 解析问题。最常见场景:"为什么我的 Java 服务连不上 MySQL?"——先 ping 一下看通不通,再 telnet 看 3306 端口开没开。
| 命令 | 说明 | 示例 |
| curl | HTTP请求工具 | curl -X GET http://localhost:8080/health |
| wget | 下载文件 | wget https://example.com/app.jar |
| ping | 测试网络连通性 | ping -c 4 8.8.8.8 |
| telnet | 测试端口连通性 | telnet db.example.com 3306 |
| nslookup | DNS解析查询 | nslookup api.example.com |
| ssh | 远程登录 | ssh -i key.pem user@192.168.1.100 |
| scp | 远程拷贝文件 | scp app.jar user@server:/opt/app/ |
Bash# 测试接口响应时间和状态码
curl -o /dev/null -s -w "HTTP状态: %{http_code}\n耗时: %{time_total}s\n" \
http://localhost:8080/api/health
# 批量检查多台服务器连通性
for host in 192.168.1.{1..10}; do
ping -c 1 -W 1 $host >/dev/null 2>&1 && echo "$host 在线" || echo "$host 离线"
done
# SCP上传整个目录
scp -r ./dist/ user@server:/var/www/html/
🔐 权限相关
| 命令 | 说明 | 示例 |
| chmod | 修改文件权限 | chmod 755 deploy.sh |
| chown | 修改文件所有者 | chown -R app:app /opt/app/ |
| sudo | 以管理员执行 | sudo systemctl restart nginx |
Bash# 给脚本加执行权限
chmod +x deploy.sh
# 递归修改目录所有者
chown -R www-data:www-data /var/www/html/
# 常用权限对照
# 755 = rwxr-xr-x (所有者可读写执行,其他人可读执行)
# 644 = rw-r--r-- (所有者可读写,其他人只读)
# 600 = rw------- (仅所有者可读写)
📦 压缩解压
| 命令 | 说明 | 示例 |
| tar -czf | 压缩目录为 .tar.gz | tar -czf backup.tar.gz /opt/app/ |
| tar -xzf | 解压 .tar.gz | tar -xzf backup.tar.gz -C /opt/ |
| unzip | 解压 .zip | unzip -o app.zip -d /opt/app/ |
Bash# 打包并排除日志目录
tar -czf app-backup-$(date +%Y%m%d).tar.gz \
--exclude='*.log' --exclude='logs/' /opt/app/
# 解压到指定目录
tar -xzf app-backup-20240101.tar.gz -C /opt/restore/
# 查看压缩包内容(不解压)
tar -tzf backup.tar.gz | head -20
2. Shell 脚本模板
🚀 服务管理脚本(start/stop/restart)
service.sh#!/bin/bash
# 通用Java服务管理脚本
APP_NAME="my-app"
APP_JAR="/opt/app/my-app.jar"
APP_CONF="/opt/app/application.yml"
LOG_DIR="/var/log/app"
PID_FILE="/var/run/${APP_NAME}.pid"
JAVA_OPTS="-Xms512m -Xmx2g -XX:+UseGCPrintGCDateStamps"
# 检查服务是否运行
is_running() {
if [ -f "$PID_FILE" ]; then
PID=$(cat $PID_FILE)
if kill -0 $PID 2>/dev/null; then
return 0
fi
fi
return 1
}
# 启动服务
start() {
if is_running; then
echo "[WARN] ${APP_NAME} 已在运行 (PID: $(cat $PID_FILE))"
return 1
fi
echo "[INFO] 启动 ${APP_NAME}..."
nohup java $JAVA_OPTS \
-Dspring.config.location=$APP_CONF \
-jar $APP_JAR \
>> ${LOG_DIR}/startup.log 2>&1 &
echo $! > $PID_FILE
echo "[INFO] ${APP_NAME} 启动成功 (PID: $!)"
}
# 停止服务
stop() {
if ! is_running; then
echo "[WARN] ${APP_NAME} 未在运行"
return 1
fi
PID=$(cat $PID_FILE)
echo "[INFO] 停止 ${APP_NAME} (PID: ${PID})..."
kill $PID
# 等待最多30秒
for i in $(seq 1 30); do
if ! kill -0 $PID 2>/dev/null; then
echo "[INFO] ${APP_NAME} 已停止"
rm -f $PID_FILE
return 0
fi
sleep 1
done
# 超时强制杀死
kill -9 $PID
rm -f $PID_FILE
echo "[WARN] ${APP_NAME} 强制停止"
}
# 重启
restart() {
stop
sleep 2
start
}
# 主入口
case "$1" in
start) start ;;
stop) stop ;;
restart) restart ;;
status) is_running && echo "运行中 (PID: $(cat $PID_FILE))" || echo "未运行" ;;
*) echo "用法: $0 {start|stop|restart|status}" ;;
esac
✂️ 日志切割脚本
log-rotate.sh#!/bin/bash
# 日志切割脚本 - 配合 crontab 使用
# crontab: 0 0 * * * /opt/scripts/log-rotate.sh
LOG_DIR="/var/log/app"
APP_NAME="my-app"
KEEP_DAYS=30
DATE=$(date +%Y%m%d)
# 切割日志文件
cd $LOG_DIR || exit 1
for log_file in ${APP_NAME}.log; do
if [ -f "$log_file" ]; then
# 压缩并重命名
mv $log_file ${log_file}.${DATE}
gzip ${log_file}.${DATE} &
# 创建新的空日志文件
touch $log_file
echo "[INFO] $(date) 日志切割完成: ${log_file}.${DATE}.gz"
fi
done
# 等待压缩完成
wait
# 删除过期日志
find $LOG_DIR -name "${APP_NAME}.log.*.gz" \
-mtime +$KEEP_DAYS -delete
echo "[INFO] $(date) 清理 ${KEEP_DAYS} 天前的旧日志完成"
🩺 健康检查脚本
health-check.sh#!/bin/bash
# 服务健康检查脚本 - 可配合 cron 每5分钟执行
# */5 * * * * /opt/scripts/health-check.sh
APP_NAME="my-app"
HEALTH_URL="http://localhost:8080/actuator/health"
ALERT_WEBHOOK="https://hooks.example.com/alert"
MAX_RETRY=3
RETRY_INTERVAL=10
send_alert() {
curl -s -X POST $ALERT_WEBHOOK \
-H 'Content-Type: application/json' \
-d "{\"text\":\"[ALERT] ${APP_NAME}: $1\"}" \
>/dev/null 2>&1
}
check_health() {
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
--max-time 5 $HEALTH_URL)
if [ "$HTTP_CODE" = "200" ]; then
return 0
fi
return 1
}
# 主逻辑:重试机制
FAIL_COUNT=0
for i in $(seq 1 $MAX_RETRY); do
if check_health; then
echo "[$(date)] ${APP_NAME} 健康检查通过"
exit 0
fi
FAIL_COUNT=$((FAIL_COUNT + 1))
echo "[$(date)] ${APP_NAME} 健康检查失败 (${FAIL_COUNT}/${MAX_RETRY})"
sleep $RETRY_INTERVAL
done
# 全部重试失败,发送告警
send_alert "连续 ${MAX_RETRY} 次健康检查失败,请立即处理!"
echo "[$(date)] ${APP_NAME} 告警已发送"
exit 1
3. Docker Compose 实战
Docker = 统一的快递站杯。不管什么口味(Java/Python/Node),都用同样的杯子和流程打包。客人拿到的永远是标准杯子,不需要关心里面是怎么做出来的。Docker Compose = 快递站套餐组合,一杯快递站+一份小食+一杯咖啡,一键下单全部上齐。
📝 完整 docker-compose.yml(Nginx + Java + MySQL + Redis)
docker-compose.yml# 完整的微服务编排模板
version: '3.8'
services:
# ========== Nginx 反向代理 ==========
nginx:
image: nginx:1.25-alpine
container_name: my-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/ssl:/etc/nginx/ssl
- ./html:/usr/share/nginx/html
depends_on:
- app
networks:
- app-network
restart: always
# ========== Java 后端应用 ==========
app:
build:
context: .
dockerfile: Dockerfile
container_name: my-app
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/mydb?useSSL=false
- SPRING_DATASOURCE_USERNAME=root
- SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD}
- SPRING_REDIS_HOST=redis
- SPRING_REDIS_PORT=6379
- JAVA_OPTS=-Xms512m -Xmx1g
volumes:
- app-logs:/var/log/app
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
networks:
- app-network
restart: always
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
# ========== MySQL 数据库 ==========
mysql:
image: mysql:8.0
container_name: my-mysql
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
- MYSQL_DATABASE=mydb
- TZ=Asia/Shanghai
volumes:
- mysql-data:/var/lib/mysql
- ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
networks:
- app-network
restart: always
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
# ========== Redis 缓存 ==========
redis:
image: redis:7-alpine
container_name: my-redis
ports:
- "6379:6379"
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
volumes:
- redis-data:/data
networks:
- app-network
restart: always
# ========== 数据卷 ==========
volumes:
mysql-data:
driver: local
redis-data:
driver: local
app-logs:
driver: local
# ========== 网络 ==========
networks:
app-network:
driver: bridge
🔧 Docker Compose 常用命令
| 命令 | 说明 |
| docker compose up -d | 后台启动所有服务 |
| docker compose down | 停止并删除所有容器 |
| docker compose logs -f app | 实时查看app服务日志 |
| docker compose exec app bash | 进入app容器内部 |
| docker compose ps | 查看所有服务状态 |
| docker compose restart app | 重启单个服务 |
| docker compose build --no-cache | 重新构建(不使用缓存) |
| docker compose pull | 拉取最新镜像 |
Bash# 一键部署
docker compose up -d --build
# 查看某个服务日志(最近100行)
docker compose logs --tail=100 app
# 进入MySQL容器执行SQL
docker compose exec mysql mysql -uroot -p$DB_PASSWORD mydb
# 查看容器资源使用
docker stats --no-stream
🏗️ Dockerfile 最佳实践(多阶段构建)
Dockerfile# ========== 阶段1: 构建阶段 ==========
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
# 先复制 pom.xml,利用 Docker 缓存加速依赖下载
COPY pom.xml .
RUN mvn dependency:go-offline -B
# 再复制源码并构建
COPY src ./src
RUN mvn package -DskipTests -B
# ========== 阶段2: 运行阶段 ==========
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# 安全:使用非 root 用户运行
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# 只复制构建产物,不包含源码和构建工具
COPY --from=builder /app/target/*.jar app.jar
# 创建日志目录
RUN mkdir -p /var/log/app && chown -R appuser:appgroup /var/log/app
USER appuser
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD wget -q --spider http://localhost:8080/actuator/health || exit 1
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar app.jar"]
多阶段构建让最终镜像只包含 JRE + jar 文件,通常只有 ~200MB(而非 ~800MB)。先复制 pom.xml 再复制源码,可以充分利用 Docker 层缓存,依赖不变时秒级构建。
🌍 环境变量 & 数据卷配置
.env# 环境变量文件(不提交到 Git!)
# .gitignore 中添加 .env
DB_PASSWORD=your_strong_password
REDIS_PASSWORD=your_redis_password
SPRING_PROFILES_ACTIVE=prod
数据卷说明# 命名卷(由 Docker 管理,适合持久化数据)
# mysql-data → /var/lib/docker/volumes/xxx/_data
# redis-data → /var/lib/docker/volumes/xxx/_data
# 绑定挂载(映射宿主机目录,适合配置文件)
# ./nginx/conf.d → 容器内 /etc/nginx/conf.d
# ./mysql/init.sql → 容器内初始化SQL
# 备份 MySQL 数据卷
docker compose exec mysql mysqldump -uroot -p$DB_PASSWORD mydb \
| gzip > backup_$(date +%Y%m%d).sql.gz
4. Nginx 配置模板
🔄 反向代理配置(含 WebSocket)
nginx/conf.d/app.conf# 反向代理 + WebSocket 支持
upstream backend {
server 127.0.0.1:8080;
# 可添加多个后端实现负载均衡
# server 127.0.0.1:8081;
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
# 通用反向代理配置
location / {
proxy_pass http://backend;
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;
# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 120s;
}
# WebSocket 支持
location /ws/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s; # WebSocket长连接1小时
}
# 静态资源缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
proxy_pass http://backend;
expires 7d;
add_header Cache-Control "public, immutable";
}
}
🔒 HTTPS 配置(含证书)
nginx/conf.d/ssl.conf# HTTPS 配置(Let's Encrypt / 自签证书)
server {
listen 80;
server_name api.example.com;
# HTTP 强制跳转 HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name api.example.com;
# 证书路径
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
# SSL 安全配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# HSTS(可选,强制浏览器使用HTTPS)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://backend;
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 https;
}
}
⚖️ 负载均衡配置(upstream)
nginx/conf.d/lb.conf# 负载均衡配置
upstream backend_pool {
# 轮询(默认):请求均匀分配
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
# === 其他策略 ===
# weight 权重:server 192.168.1.10:8080 weight=3;
# ip_hash 会话保持:ip_hash;
# least_conn 最少连接:least_conn;
# 健康检查(需要第三方模块)
# check interval=3000 rise=2 fall=3 timeout=1000;
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend_pool;
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_next_upstream error timeout http_502 http_503 http_504;
}
}
📄 静态文件服务配置
nginx/conf.d/static.conf# 前端静态文件服务
server {
listen 80;
server_name www.example.com;
root /usr/share/nginx/html;
index index.html;
# SPA 路由支持(Vue/React)
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源强缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off; # 静态资源不记日志
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
}
# gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1024;
}
🔍 常见问题排查
| 问题 | 原因 | 排查 & 解决 |
| 502 Bad Gateway |
后端服务挂了或未启动 |
1. curl http://localhost:8080/health 检查后端
2. docker compose ps 看容器状态
3. 检查后端日志
|
| 504 Gateway Timeout |
后端处理太慢,超时 |
1. 增大 proxy_read_timeout
2. 优化后端接口性能
3. 检查数据库慢查询
|
| 413 Request Entity Too Large |
上传文件超过默认1MB限制 |
在 server 或 location 中添加:
client_max_body_size 50m;
|
| proxy_pass 末尾斜杠问题 |
带不带 / 行为完全不同 |
proxy_pass http://backend; → 保留原始路径
proxy_pass http://backend/; → 替换掉匹配的路径
|
proxy_pass 末尾带 / 时,Nginx 会用 / 替换匹配的 location 路径。比如 location /api/ { proxy_pass http://backend/; },请求 /api/users 会转发为 /users(去掉了 /api 前缀)。不带 / 则完整保留为 /api/users。这是最常见的 Nginx 配置坑之一!
6. 日志查看技巧
🔍 tail -f + grep 实时过滤
Bash# 实时查看并过滤 ERROR 日志
tail -f /var/log/app/app.log | grep --line-buffered "ERROR"
# 同时过滤多个关键词(ERROR 或 WARN)
tail -f app.log | grep --line-buffered -E "ERROR|WARN"
# 高亮显示(加颜色)
tail -f app.log | grep --color=auto -E "ERROR|WARN|^"
# 排除某些日志(过滤掉健康检查)
tail -f app.log | grep -v "health check"
# 统计最近5分钟ERROR数量
tail -n 10000 app.log | grep "ERROR" | wc -l
📖 less 搜索和跳转
Bash - less 快捷键# 打开日志文件
less /var/log/app/app.log
# ===== 常用快捷键 =====
# /关键词 → 向下搜索(如 /ERROR)
# ?关键词 → 向上搜索
# n → 下一个匹配
# N → 上一个匹配
# G → 跳到文件末尾
# 1G → 跳到文件开头
# 100G → 跳到第100行
# F → 实时跟踪(类似 tail -f)
# Ctrl+C → 退出跟踪模式
# q → 退出 less
# 实用技巧:打开后直接跳到末尾并跟踪
less +F /var/log/app/app.log
📋 journalctl 查看系统日志
Bash# 查看某服务最近100行日志
journalctl -u nginx -n 100 --no-pager
# 实时跟踪服务日志
journalctl -u my-app -f
# 查看今天的日志
journalctl -u my-app --since today
# 查看指定时间范围的日志
journalctl -u my-app --since "2024-01-01 10:00" --until "2024-01-01 12:00"
# 查看内核日志(排查系统级问题)
journalctl -k
# 查看某进程的日志
journalctl _PID=12345
📑 多服务日志聚合查看
Bash# 同时查看多个服务日志(tmux 多窗口)
tmux new-session -d -s logs
tmux split-window -h -t logs
tmux send-keys -t logs:0.0 'tail -f /var/log/app/app.log' C-m
tmux send-keys -t logs:0.1 'tail -f /var/log/nginx/error.log' C-m
tmux attach -t logs
# 或者用 multitail 工具
multitail /var/log/app/app.log /var/log/nginx/access.log
# 合并多个日志文件,按时间排序
awk '{print FILENAME, $0}' *.log | sort -k2 | less
# Docker 多容器日志聚合
docker compose logs -f --tail=50
生产环境建议使用 ELK(Elasticsearch + Logstash + Kibana)或 Loki + Grafana 做日志聚合,支持全文搜索、可视化分析和告警。开发/测试环境用 tail/grep/less 就够了。
7. 线上紧急操作手册
线上操作原则:先恢复服务 → 再定位原因 → 最后彻底修复。任何时候都要保留现场(日志、截图),不要急于操作导致丢失关键信息。
1 🚨 服务挂了怎么办
发现告警
监控/用户反馈
→
先重启恢复
减少影响时间
→
查日志定位
找到根因
→
修复上线
验证恢复
应急命令# Step 1: 确认服务状态
ps aux | grep java
curl http://localhost:8080/actuator/health
# Step 2: 快速重启恢复(先恢复再说)
cd /opt/app && ./service.sh restart
# 或者 Docker 环境:
docker compose restart app
# Step 3: 查看启动日志,确认是否恢复
tail -f /var/log/app/startup.log
# Docker 环境:
docker compose logs -f app
# Step 4: 定位崩溃原因(查看错误日志)
grep -E "ERROR|Exception|OOM|OutOfMemory" /var/log/app/app.log | tail -50
# Step 5: 如果是OOM,查看heap dump
ls -lh /opt/app/*.hprof
# 下载到本地用 MAT 工具分析
scp user@server:/opt/app/java_pid*.hprof ~/
2 🗄️ 数据库挂了怎么办
应急命令# Step 1: 检查MySQL是否存活
systemctl status mysql
mysql -uroot -p -e "SELECT 1"
# Step 2: 查看连接数是否打满
mysql -uroot -p -e "SHOW PROCESSLIST"
mysql -uroot -p -e "SHOW STATUS LIKE 'Threads_connected'"
mysql -uroot -p -e "SHOW VARIABLES LIKE 'max_connections'"
# Step 3: 查看慢查询
mysql -uroot -p -e "SHOW FULL PROCESSLIST" | grep -v "Sleep"
# 或查看慢查询日志
tail -20 /var/log/mysql/slow.log
# Step 4: Kill 掉异常慢查询(谨慎操作!)
mysql -uroot -p -e "KILL QUERY 12345"
# Step 5: 检查连接池配置(应用侧)
grep -E "pool|connection|timeout" application.yml
# Step 6: 如果无法恢复,考虑重启MySQL
systemctl restart mysql
# 重启后检查数据完整性
mysql -uroot -p -e "CHECK TABLE users, orders;" mydb
3 💽 磁盘满了怎么办
应急命令# Step 1: 查看磁盘使用率
df -h
# Step 2: 找到占用空间最大的目录
du -sh /* | sort -rh | head -10
# 继续深入查找
du -sh /var/* | sort -rh | head -10
# Step 3: 快速清理日志(保留最近3天)
find /var/log -name "*.log.*" -mtime +3 -delete
# 清空超大日志文件(不删除文件)
truncate -s 0 /var/log/app/app.log
# Step 4: 清理Docker无用资源
docker system prune -a --volumes -f # 谨慎!会删除未使用的镜像和卷
docker image prune -f # 只清理无用镜像
# Step 5: 清理临时文件
find /tmp -type f -mtime +7 -delete
rm -rf /opt/app/tmp/*
# Step 6: 验证磁盘空间
df -h
日常预防:在日志切割脚本中加入磁盘使用率检查,超过 80% 自动告警。用 logrotate 工具自动管理日志轮转。
4 🧠 内存不够怎么办
应急命令# Step 1: 查看内存使用概况
free -h
# Step 2: 查看内存占用TOP10进程
ps aux --sort=-%mem | head -11
# Step 3: 查看Java进程堆内存
jstat -gcutil $(pgrep -f "my-app") 1000 5
# Step 4: 临时缓解 - 重启占用内存最多的非关键服务
# 先确认哪些服务可以重启
docker compose ps
# 重启指定服务
docker compose restart app
# Step 5: 如果是Java OOM,调整JVM参数
# 修改 JAVA_OPTS 后重启
# -Xms512m -Xmx2g → -Xms1g -Xmx4g(根据服务器内存调整)
# Step 6: 查看是否有内存泄漏(连续观察)
watch -n 5 "free -h | head -2"
# Step 7: 长期方案 - 垂直扩容(升级服务器内存)
# 或水平扩容(增加服务器节点)
线上服务器建议设置 swap 分区作为紧急缓冲,但 swap 会导致性能下降。正确做法是限制 JVM 堆内存为物理内存的 60-70%,给操作系统和其他进程留足空间。