技术干货手册
整理自问答文档1、问答文档2,提取所有代码片段与技术要点,附注释说明。
目录
- 大模型推理部署
- 量化与性能优化
- 动态批处理
- 异常检测算法
- 告警阈值设计
- 告警降噪与编排
- Prometheus 监控
- ELK 日志体系
- SkyWalking 链路追踪
- CI/CD 流水线
- Ansible 自动化运维
- Python 运维脚本
- Shell 日志分析
- Linux 系统排查
- 网络基础
- Docker 与 Kubernetes
- MySQL 优化
- Redis 实战
- 根因分析
- 容量规划与成本优化
一、大模型推理部署
1.1 MindIE 推理引擎核心配置
yaml
# MindIE 推理服务配置(DeepSeek-R1 671B 多机部署)
model:
type: deepseek_r1
tensor_parallel: 8 # 单机内 Tensor 并行度,对应 8 张 NPU
pipeline_parallel: 4 # 跨机 Pipeline 并行度,4 台服务器各负责若干层
num_layers: 61 # DeepSeek-R1 共 61 层 Transformer
compute:
precision: w8a8 # 权重 INT8 + 激活 INT8 量化
enable_flash_attention: true # 启用 Flash Attention 3.0,减少显存访问
enable_rdma: true # 启用 RDMA 网络,降低跨机通信延迟
batching:
max_batch_size: 128 # 最大批次大小,太大延迟高,太小利用率低
dynamic_batching: true # 动态批处理,自动合并不同长度请求
prefill_interval: 16 # Prefill 与 Decode 阶段切换频率
memory:
kv_cache_type: paged # PagedAttention 管理 KV Cache,避免碎片
max_sequence_length: 8192
kv_cache_percent: 0.7 # 70% 显存用于 KV Cache,预留 30% buffer
serving:
request_timeout: 120 # 请求超时时间(秒)
streaming: true # 启用流式输出(SSE)
rpc:
enable_rdma: true
rdma_port: 400001.2 模型部署流程
bash
# 1. 导出模型为 MindIE 格式(JIT 编译优化)
python -m mindie.exporter --model deepseek_r1 --output /models/compiled
# 2. 启动推理服务
mindie-server --config inference.yaml
# 3. 验证服务是否正常(OpenAI 兼容接口)
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-r1","messages":[{"role":"user","content":"Hello"}],"stream":true}'注意:
enable_compile开启后首次推理有冷启动延迟,需要提前预热;kv_cache_percent设太大会 OOM,要预留 buffer。
1.3 HCCL 集合通信环境变量调优
bash
# 昇腾 NPU 多机通信关键参数
export HCCL_CONNECT_TIMEOUT=600 # 连接超时时间(秒),多机初始化较慢
export HCCL_RDMA_MEM_SIZE=65536 # RDMA 内存池大小(KB)
export HCCL_BUFF_SIZE=65536 # 通信缓冲区大小
# NCCL 兼容层参数(部分场景适用)
export NCCL_IB_TIMEOUT=22 # IB 网络超时
export NCCL_NET_GDR_LEVEL=PHB # GPU Direct RDMA 级别
export NCCL_ALGO=Ring # 通信算法:Ring 或 Tree优化前 HCCL 通信占推理总耗时 15%,调优后降至 8%,通信延迟降低约 40%。
二、量化与性能优化
2.1 W8A8 量化流程
python
# SmoothQuant 平滑异常激活通道,避免量化精度损失
# 原理:将激活值的异常通道"平滑"到权重侧,使两者分布更均匀
alpha = 0.5 # 平滑系数,0~1 之间,越大越多平滑到权重侧
# 计算平滑因子:激活标准差 / 权重标准差 的比值
smooth_factor = calculate_smooth_factor(activation_std, weight_std)
# 对权重做平滑变换
smoothed_weight = weight * (smooth_factor ** alpha)
# 量化为 INT8(范围 -128~127)
quantized_weight = quantize_to_int8(smoothed_weight)yaml
# MindIE 量化配置
quantization:
enabled: true
method: w8a8 # 权重 INT8 + 激活 INT8
weight_clip: true # 裁剪权重异常值
activation_scheme: per_token # 按 token 动态量化激活值
calibration:
method: minmax # 用最大最小值确定量化范围
samples: 5000 # 校准样本数,越多越准确
batch_size: 32量化收益:
- 显存:1.2 TB → 600 GB(减少 50%)
- 单卡吞吐:120 tokens/s → 280 tokens/s
- 总吞吐:1800 tokens/s → 2500 tokens/s(提升约 40%)
- 精度损失:< 0.5%(用 MMLU、HumanEval benchmark 验证)
2.2 Flash Attention 与算子融合
python
# Flash Attention 核心思想:分块计算 Attention,减少 HBM 读写次数
# 传统 Attention:Q*K^T 结果写回 HBM,再读出来做 Softmax,再写回
# Flash Attention:在 SRAM 内完成分块计算,大幅减少显存带宽消耗
# 在 MindIE 中启用(配置项)
compute:
enable_flash_attention: true # 启用后 Attention 计算显存访问减少约 60%三、动态批处理
3.1 动态批处理实现原理
python
import time
from collections import defaultdict
class DynamicBatcher:
"""
动态批处理器:将不同长度、不同来源的请求智能合并,
最大化 GPU/NPU 利用率,同时控制延迟。
"""
def __init__(self, max_batch_size=128, max_wait_time=0.05):
self.queue = []
self.max_batch_size = max_batch_size
self.max_wait_time = max_wait_time # 最长等待 50ms
def add_request(self, request):
self.queue.append(request)
def get_batch(self):
# 策略1:队列满了立即处理,不再等待
if len(self.queue) >= self.max_batch_size:
batch = self.queue[:self.max_batch_size]
self.queue = self.queue[self.max_batch_size:]
return batch
# 策略2:队列中最早的请求等待超时,触发处理
if self.queue and \
time.time() - self.queue[0].arrival_time > self.max_wait_time:
batch = self.queue[:self.max_batch_size]
self.queue = self.queue[self.max_batch_size:]
return batch
# 策略3:按相同前缀分组,共享 KV Cache 计算
batch = self.group_by_prefix()
if batch:
return batch
return None
def group_by_prefix(self):
"""将有相同 prompt 前缀的请求合并,只计算一次 Prefill"""
prefix_groups = defaultdict(list)
for req in self.queue:
# 用 prompt 的 hash 作为分组 key(实际应用中用前缀树)
prefix_key = hash(req.prompt[:50])
prefix_groups[prefix_key].append(req)
# 返回最大的前缀组,优先处理可以共享计算的请求
if prefix_groups:
return max(prefix_groups.values(), key=len)
return []关键参数调优参考:
| 参数 | 建议值 | 说明 |
|---|---|---|
| max_batch_size | 64~128 | 太大延迟高,太小利用率低 |
| max_wait_time | 20~50ms | 平衡延迟和吞吐量 |
| prefill_interval | 8~16 | Prefill 和 Decode 切换频率 |
四、异常检测算法
4.1 基于统计的异常检测
python
import numpy as np
def detect_anomaly_std(values: list, threshold: float = 3.0) -> list:
"""
基于标准差的异常检测(3-sigma 原则)。
超出均值 ± 3倍标准差的点视为异常。
适用:单指标、分布较正态的场景(如 CPU 使用率)。
"""
mean = np.mean(values)
std = np.std(values)
return [v for v in values if abs(v - mean) > threshold * std]
def detect_anomaly_ma(values: list, window: int = 5, threshold: float = 2.0) -> list:
"""
基于移动平均的异常检测。
当前值与近 window 个点的均值偏差超过 threshold 倍标准差时告警。
适用:有趋势变化的时序指标(如内存缓慢增长)。
"""
# 计算移动平均
ma = np.convolve(values, np.ones(window) / window, mode='valid')
anomalies = []
for i, v in enumerate(values[window:]):
if abs(v - ma[i]) > threshold * np.std(values):
anomalies.append(i + window)
return anomalies4.2 Isolation Forest 多指标异常检测
python
from sklearn.ensemble import IsolationForest
def detect_by_isolation_forest(features: list, contamination: float = 0.1) -> list:
"""
Isolation Forest 隔离森林:通过随机切分特征空间,
异常点因为"孤立"所以需要更少的切分次数。
适用:多指标联合异常检测(如 CPU + 内存 + IO 同时异常)。
contamination:预期异常比例,通常设 0.05~0.1。
"""
clf = IsolationForest(contamination=contamination, random_state=42)
predictions = clf.fit_predict(features)
# -1 表示异常,1 表示正常
return [i for i, p in enumerate(predictions) if p == -1]4.3 LSTM AutoEncoder 时序异常检测
python
class TimeSeriesAnomalyDetector:
"""
LSTM AutoEncoder:学习正常时序模式,
重建误差大的点视为异常。
适用:复杂时序模式(如业务流量的周期性波动中的异常)。
"""
def __init__(self, threshold: float = 0.05):
self.encoder = None # LSTM 编码器
self.decoder = None # LSTM 解码器
self.threshold = threshold # 重建误差阈值
def train(self, normal_data):
"""用正常数据训练,让模型学会正常模式"""
# 训练过程:编码 → 解码 → 最小化重建误差
pass
def detect(self, data) -> bool:
"""
检测输入序列是否异常。
重建误差 = 原始序列与重建序列的均方误差。
误差超过阈值则判定为异常。
"""
reconstructed = self.decoder.predict(self.encoder.transform(data))
error = np.mean((data - reconstructed) ** 2)
return error > self.threshold4.4 Prometheus 动态阈值告警规则
yaml
# 基于 predict_linear 预测趋势,提前告警
groups:
- name: dynamic_alerts
rules:
# 预测磁盘 4 小时后用满
- alert: DiskWillBeFull
expr: |
predict_linear(node_filesystem_free_bytes[1h], 4 * 3600) < 0
for: 10m
labels:
severity: warning
annotations:
summary: "磁盘预计 4 小时后用满,请及时清理"
# 动态基线:当前值超过过去 1 小时均值的 3 倍
- alert: AbnormalTrafficSpike
expr: |
rate(http_requests_total[5m]) >
3 * avg_over_time(rate(http_requests_total[5m])[1h:5m])
for: 5m
labels:
severity: warning五、告警阈值设计
5.1 分层告警阈值配置示例
yaml
# ── 基础设施层(约 50 项)──────────────────────────────
infrastructure:
cpu_utilization:
warning: 70 # 超过 70% 预警,给运维留出处理时间
critical: 85 # 超过 85% 紧急,可能影响业务
cpu_loadavg_1min:
warning: 4 # 负载超过 CPU 核数时需关注
critical: 8
memory_usage_percent:
warning: 75
critical: 90
memory_available_mb:
warning: 4096 # 可用内存低于 4GB 预警
critical: 1024 # 低于 1GB 紧急
disk_usage_percent:
root_partition:
warning: 80
critical: 90
data_partition:
warning: 70 # 数据盘更严格,提前预警
critical: 85
# ── 中间件层(约 60 项)──────────────────────────────
middleware:
mysql:
connections_active_ratio:
warning: 0.70 # 活跃连接数超过 max_connections 的 70%
critical: 0.85
slow_queries_per_sec:
warning: 10
critical: 50
redis:
memory_used_ratio:
warning: 0.70 # 已用内存超过 maxmemory 的 70%
critical: 0.85
connected_clients:
warning: 500
critical: 1000
kafka:
consumer_lag:
warning: 10000 # 消费积压超过 1 万条预警
critical: 50000
# ── 应用层(约 80 项)──────────────────────────────
application:
api_latency_p99_ms:
warning: 500
critical: 1000
api_error_rate:
warning: 0.01 # 错误率超过 1% 预警
critical: 0.05 # 超过 5% 紧急
# 推理服务专项指标
llm_ttft_ms: # 首 token 延迟
warning: 1500
critical: 3000
llm_throughput_tokens_per_sec:
warning: 1500 # 低于 1500 tokens/s 预警
critical: 800 # 低于 800 tokens/s 紧急5.2 动态阈值计算(Python 实现)
python
import numpy as np
from datetime import datetime
def dynamic_threshold(values: list, factor: float = 3.0) -> float:
"""
基于最近 100 个采样点计算动态阈值。
阈值 = 均值 + factor × 标准差。
factor 越大,阈值越宽松,误报越少。
"""
recent = values[-100:] # 只看最近 100 个点,避免历史异常影响
mean = np.mean(recent)
std = np.std(recent)
return mean + factor * std
def get_threshold_by_hour(metric_config: dict, hour: int) -> dict:
"""
按业务时段返回不同阈值。
工作时间(9~18 点)流量大,阈值适当放宽;
夜间流量小,阈值收紧,更容易发现异常。
"""
if 9 <= hour <= 18:
return metric_config['day_threshold']
else:
return metric_config['night_threshold']六、告警降噪与编排
6.1 AlertManager 告警收敛配置
yaml
# AlertManager 路由配置
route:
# 按告警名称 + 服务名分组,同组告警合并发送
group_by: ['alertname', 'service']
group_wait: 30s # 等待 30s 收集同组告警,避免逐条发送
group_interval: 5m # 同组告警最少间隔 5 分钟再次通知
repeat_interval: 4h # 持续告警每 4 小时重复通知一次
receiver: 'default'
routes:
# P1 紧急告警:立即通知,不等待
- match:
severity: critical
group_wait: 0s
receiver: 'pagerduty'
# P4 提示告警:汇总后发邮件
- match:
severity: info
group_wait: 10m
receiver: 'email-digest'
# 告警抑制:根因告警触发时,压制衍生告警
inhibit_rules:
- source_match:
alertname: DatabaseConnectionPoolFull # 根因:连接池满
target_match_re:
alertname: "APITimeout|OrderFailed" # 衍生:接口超时、下单失败
equal: ['env', 'cluster'] # 同环境同集群才抑制6.2 告警智能编排(Python 实现)
python
from collections import defaultdict
from dataclasses import dataclass
from typing import List
@dataclass
class Alert:
name: str
service: str
time: float
severity: str
class AlertOrchestrator:
"""
告警智能编排器:
1. 按时间窗口聚合相关告警
2. 基于调用链分析传播路径
3. 匹配知识图谱推断根因
4. 输出根因 + 影响范围 + 处置建议
"""
def __init__(self):
# 知识图谱:记录已知的故障传播模式
# key: 根因告警名,value: 衍生告警列表
self.knowledge_graph = {
"DatabaseConnectionPoolFull": ["APITimeout", "OrderFailed"],
"NetworkPacketLoss": ["ServiceUnavailable", "HighLatency"],
"DiskFull": ["LogWriteFailed", "AppCrash"],
}
def group_by_time_window(self, alerts: List[Alert], window_sec: int = 300):
"""将 5 分钟内的告警归为一组"""
if not alerts:
return []
groups = []
current_group = [alerts[0]]
for alert in alerts[1:]:
if alert.time - current_group[0].time <= window_sec:
current_group.append(alert)
else:
groups.append(current_group)
current_group = [alert]
groups.append(current_group)
return groups
def find_root_cause(self, alert_group: List[Alert]) -> str:
"""
在一组告警中找根因:
如果某个告警的衍生告警都在组内,则它是根因。
"""
alert_names = {a.name for a in alert_group}
for alert in sorted(alert_group, key=lambda x: x.time):
derivatives = self.knowledge_graph.get(alert.name, [])
if any(d in alert_names for d in derivatives):
return alert.name
# 找不到已知根因,返回最早的告警
return min(alert_group, key=lambda x: x.time).name七、Prometheus 监控
7.1 常用 PromQL 查询
# ── 请求量 ──────────────────────────────────────────
# 每秒请求数(5 分钟滑动窗口)
rate(http_requests_total[5m])
# 按服务聚合 QPS
sum by (service) (rate(http_requests_total[5m]))
# ── 延迟分位数 ──────────────────────────────────────
# P99 延迟(需要 Histogram 类型指标)
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
# P50 / P95 / P99 对比
histogram_quantile(0.50, rate(http_request_duration_seconds_bucket[5m]))
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
# ── 错误率 ──────────────────────────────────────────
# 5xx 错误率
rate(http_requests_total{status=~"5.."}[5m])
/ rate(http_requests_total[5m])
# ── 资源使用 ──────────────────────────────────────────
# 内存使用率
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)
/ node_memory_MemTotal_bytes * 100
# CPU 使用率(排除 idle 模式)
1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))
# 磁盘使用率
(node_filesystem_size_bytes - node_filesystem_free_bytes)
/ node_filesystem_size_bytes * 100
# ── 推理服务专项 ──────────────────────────────────────
# 推理吞吐量(tokens/s)
rate(llm_tokens_total[5m])
# 首 token 延迟 P95
histogram_quantile(0.95, rate(llm_ttft_seconds_bucket[5m]))
# NPU 利用率
npu_utilization{instance=~".*"}7.2 Recording Rules(预计算,提升查询性能)
yaml
# 将高频复杂查询预计算为新指标,避免每次查询都实时计算
groups:
- name: node_recording_rules
interval: 1m # 每分钟计算一次
rules:
# 预计算 CPU 使用率,避免每次 Dashboard 刷新都重算
- record: instance:node_cpu_utilization:rate5m
expr: |
1 - avg by (instance) (
rate(node_cpu_seconds_total{mode="idle"}[5m])
)
# 预计算内存使用率
- record: instance:node_memory_utilization:ratio
expr: |
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)
/ node_memory_MemTotal_bytes7.3 Kubernetes 集群监控部署
yaml
# kube-state-metrics:采集 K8s 对象状态(Pod、Deployment、Node 等)
apiVersion: apps/v1
kind: Deployment
metadata:
name: kube-state-metrics
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: kube-state-metrics
template:
spec:
serviceAccountName: kube-state-metrics
containers:
- name: kube-state-metrics
image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.8.0
ports:
- containerPort: 8080 # metrics 端口
- containerPort: 8081 # telemetry 端口
---
# node-exporter:DaemonSet 部署到每个节点,采集主机指标
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
spec:
selector:
matchLabels:
app: node-exporter
template:
spec:
hostNetwork: true # 使用宿主机网络,采集真实网络指标
hostPID: true # 访问宿主机进程信息
containers:
- name: node-exporter
image: prom/node-exporter:v1.6.0
args:
- --path.rootfs=/host # 挂载宿主机根目录
volumeMounts:
- name: host-root
mountPath: /host
readOnly: true
volumes:
- name: host-root
hostPath:
path: /八、ELK 日志体系
8.1 Filebeat 采集配置
yaml
# filebeat.yml - 部署在每台应用服务器,轻量采集日志
filebeat.inputs:
# 采集 Nginx 访问日志
- type: log
paths:
- /var/log/nginx/access.log
fields:
service: nginx
env: prod
log_type: access
fields_under_root: true # 字段提升到根级别,便于 ES 索引
# 采集应用 JSON 日志
- type: log
paths:
- /var/log/app/*.log
json.keys_under_root: true # JSON 日志自动解析
json.add_error_key: true # 解析失败时添加 error 字段
fields:
service: myapp
env: prod
# 输出到 Kafka(生产环境推荐,避免 ES 压力过大)
output.kafka:
hosts: ["kafka1:9092", "kafka2:9092", "kafka3:9092"]
topic: 'logs-%{[service]}' # 按服务名分 topic
partition.round_robin:
reachable_only: false
compression: gzip8.2 Logstash 日志解析 Pipeline
ruby
# logstash.conf - 解析 Nginx access log
input {
kafka {
bootstrap_servers => "kafka1:9092,kafka2:9092"
topics => ["logs-nginx"]
group_id => "logstash-nginx"
codec => "json"
}
}
filter {
# grok 解析 Nginx 日志格式
# 格式:IP - - [时间] "方法 路径 协议" 状态码 字节数 "referer" "UA"
grok {
match => {
"message" => '%{IP:client_ip} - - \[%{HTTPDATE:timestamp}\] "%{WORD:method} %{URIPATHPARAM:request} HTTP/%{NUMBER:http_version}" %{NUMBER:status_code:int} %{NUMBER:bytes:int}'
}
}
# 时间戳转换为 ES 标准格式
date {
match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
target => "@timestamp"
remove_field => ["timestamp"]
}
# IP 地理位置解析
geoip {
source => "client_ip"
target => "geoip"
}
# 添加响应时间分级标签,便于统计慢请求
if [response_time] {
if [response_time] > 3 {
mutate { add_tag => ["slow_request"] }
}
}
}
output {
elasticsearch {
hosts => ["es1:9200", "es2:9200", "es3:9200"]
index => "nginx-logs-%{+YYYY.MM.dd}" # 按天分索引,便于清理
template_name => "nginx-logs"
}
}8.3 日志规范化 JSON 格式
json
{
"timestamp": "2024-04-01T10:15:30.123Z",
"level": "ERROR",
"service": "order-service",
"instance": "order-7d8f9c-xk2p9",
"trace_id": "abc123def456",
"span_id": "def456ghi789",
"message": "Database connection failed",
"error": {
"code": "DB_CONN_ERR",
"message": "Connection refused",
"stack": "at com.example.db.Pool.getConnection..."
},
"context": {
"user_id": "12345",
"request_id": "req789",
"order_id": "ord001"
}
}规范要点:timestamp 用 ISO8601;level 只用 DEBUG/INFO/WARN/ERROR;必须带 trace_id 便于跨系统关联;禁止打印密码、密钥等敏感字段;单条日志不超过 10KB。
九、SkyWalking 链路追踪
9.1 Agent 部署配置
yaml
# Java 应用 Agent 配置(agent.config)
agent.service_name = order-service # 服务名,在 SkyWalking UI 中显示
collector.backend_service = oap-server:11800 # OAP Server 地址
agent.sample_n_per_3_secs = 3 # 每 3 秒采样 3 个请求(生产环境控制采样率)python
# Python 应用使用 OpenTelemetry 上报到 SkyWalking
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# 初始化 Tracer
provider = TracerProvider()
exporter = OTLPSpanExporter(endpoint="oap-server:11800")
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)
# 在关键函数上添加 Span
def process_order(order_id: str):
with tracer.start_as_current_span("process_order") as span:
span.set_attribute("order.id", order_id)
try:
result = do_process(order_id)
span.set_attribute("order.status", "success")
return result
except Exception as e:
span.record_exception(e)
span.set_status(trace.StatusCode.ERROR, str(e))
raise9.2 SkyWalking 告警规则
yaml
# alarm-settings.yml - 慢调用和错误率告警
rules:
# 服务响应时间 P99 超过 3 秒,持续 10 分钟
service_resp_time_rule:
metrics-name: service_resp_time
op: ">"
threshold: 3000 # 单位:毫秒
period: 10 # 检查周期:10 分钟
count: 3 # 10 分钟内超过 3 次触发
silence-period: 5 # 告警后静默 5 分钟,避免重复通知
message: "服务 {name} P99 延迟超过 3 秒"
# 服务错误率超过 5%
service_error_rate_rule:
metrics-name: service_sla
op: "<"
threshold: 9500 # SLA < 95%(即错误率 > 5%)
period: 10
count: 2
message: "服务 {name} 错误率超过 5%"十、CI/CD 流水线
10.1 GitLab CI 完整流水线
yaml
# .gitlab-ci.yml
stages:
- scan # 代码扫描
- build # 构建镜像
- test # 自动化测试
- deploy # 部署
variables:
HARBOR_ADDR: harbor.example.com
IMAGE_NAME: $HARBOR_ADDR/project/$CI_PROJECT_NAME
# 代码质量扫描(MR 时触发)
code_scan:
stage: scan
image: sonarsource/sonar-scanner-cli
script:
- sonar-scanner
-Dsonar.projectKey=$CI_PROJECT_NAME
-Dsonar.sources=src
-Dsonar.host.url=$SONAR_URL
-Dsonar.login=$SONAR_TOKEN
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# 构建 Docker 镜像并推送 Harbor
build_image:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker login -u $HARBOR_USER -p $HARBOR_PASS $HARBOR_ADDR
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHORT_SHA .
- docker push $IMAGE_NAME:$CI_COMMIT_SHORT_SHA
# 主分支额外打 latest 标签
- |
if [ "$CI_COMMIT_BRANCH" == "main" ]; then
docker tag $IMAGE_NAME:$CI_COMMIT_SHORT_SHA $IMAGE_NAME:latest
docker push $IMAGE_NAME:latest
fi
# 部署到测试环境
deploy_test:
stage: deploy
environment: test
script:
- kubectl set image deployment/$CI_PROJECT_NAME
app=$IMAGE_NAME:$CI_COMMIT_SHORT_SHA
-n test
- kubectl rollout status deployment/$CI_PROJECT_NAME -n test --timeout=5m
rules:
- if: $CI_COMMIT_BRANCH == "develop"
# 部署到生产环境(需要手动审批)
deploy_prod:
stage: deploy
environment: production
when: manual # 手动触发,需要审批
script:
- kubectl set image deployment/$CI_PROJECT_NAME
app=$IMAGE_NAME:$CI_COMMIT_SHORT_SHA
-n prod
- kubectl rollout status deployment/$CI_PROJECT_NAME -n prod --timeout=10m
rules:
- if: $CI_COMMIT_BRANCH == "main"10.2 Dockerfile 多阶段构建
dockerfile
# 多阶段构建:构建阶段和运行阶段分离,大幅减小最终镜像体积
# ── 阶段1:构建 ──────────────────────────────────────
FROM golang:1.21-alpine AS builder
WORKDIR /app
# 先复制依赖文件,利用 Docker 层缓存,依赖不变时不重新下载
COPY go.mod go.sum ./
RUN go mod download
# 再复制源码并编译
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp ./cmd/main.go
# ── 阶段2:运行 ──────────────────────────────────────
FROM alpine:3.18
# 只安装必要的 CA 证书(HTTPS 请求需要)
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
# 只从构建阶段复制编译好的二进制,不包含源码和编译工具
COPY --from=builder /app/myapp .
# 使用非 root 用户运行,提升安全性
RUN adduser -D -s /bin/sh appuser
USER appuser
EXPOSE 8080
ENTRYPOINT ["./myapp"]十一、Ansible 自动化运维
11.1 批量配置下发
yaml
# nginx_deploy.yml - 批量下发 Nginx 配置并重载
- hosts: web_servers
vars:
nginx_version: "1.24.0"
tasks:
- name: 检查 Nginx 是否安装
command: nginx -v
register: nginx_check
ignore_errors: true
- name: 安装 Nginx(如未安装)
yum:
name: "nginx-{{ nginx_version }}"
state: present
when: nginx_check.rc != 0
- name: 下发 Nginx 配置(使用 Jinja2 模板)
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
backup: yes # 自动备份旧配置
notify: reload nginx # 配置变更时触发 handler
- name: 验证 Nginx 配置语法
command: nginx -t
changed_when: false # 这个任务不算"变更"
handlers:
# handler 只在被 notify 时执行,且无论 notify 多少次只执行一次
- name: reload nginx
service:
name: nginx
state: reloaded11.2 批量应用部署
yaml
# app_deploy.yml - Java 应用批量部署
- hosts: app_servers
vars:
app_name: order-service
app_port: 8080
app_version: "{{ target_version }}" # 通过命令行传入:-e target_version=1.2.0
deploy_dir: /opt/apps/{{ app_name }}
tasks:
- name: 创建应用目录
file:
path: "{{ deploy_dir }}"
state: directory
owner: appuser
mode: '0755'
- name: 下载应用包
get_url:
url: "http://nexus.example.com/{{ app_name }}-{{ app_version }}.jar"
dest: "{{ deploy_dir }}/app-{{ app_version }}.jar"
checksum: "sha256:{{ app_checksum }}" # 校验完整性
- name: 停止旧版本
systemd:
name: "{{ app_name }}"
state: stopped
ignore_errors: true # 首次部署时服务不存在,忽略错误
- name: 更新软链接到新版本
file:
src: "{{ deploy_dir }}/app-{{ app_version }}.jar"
dest: "{{ deploy_dir }}/app.jar"
state: link
- name: 启动新版本
systemd:
name: "{{ app_name }}"
state: started
enabled: yes
daemon_reload: yes
- name: 等待服务健康检查通过
uri:
url: "http://localhost:{{ app_port }}/actuator/health"
status_code: 200
register: health_check
retries: 10
delay: 5 # 每 5 秒重试一次,最多 10 次(50 秒)
until: health_check.status == 20011.3 常用 Ad-hoc 命令
bash
# 批量查看所有主机负载
ansible all -m shell -a "uptime" -i inventory/prod
# 批量重启服务
ansible web_servers -m systemd -a "name=nginx state=restarted" -i inventory/prod
# 批量收集日志文件到本地
ansible all -m fetch -a "src=/var/log/app/error.log dest=./logs/ flat=no"
# 批量检查磁盘使用
ansible all -m shell -a "df -h | grep -v tmpfs"
# 使用 vault 加密敏感变量
ansible-vault encrypt_string 'MySecretPassword' --name 'db_password'
# 在 playbook 中使用:db_password: !vault |...十二、Python 运维脚本
12.1 自动巡检脚本
python
#!/usr/bin/env python3
"""
自动巡检脚本:检查磁盘、端口、API 健康状态,
结果汇总后可扩展为发送钉钉/邮件告警。
"""
import subprocess
import socket
import requests
from datetime import datetime
from typing import Dict, List, Tuple
class HealthChecker:
def check_disk(self) -> Dict:
"""检查磁盘使用率,超过 80% 记录告警"""
result = subprocess.run(
['df', '-h', '--output=target,pcent,size'],
capture_output=True, text=True
)
alerts = []
for line in result.stdout.strip().split('\n')[1:]:
parts = line.split()
if len(parts) >= 2:
try:
usage = int(parts[1].replace('%', ''))
if usage > 80:
alerts.append(f"{parts[0]} 使用率 {parts[1]}")
except ValueError:
continue
return {'status': 'warn' if alerts else 'ok', 'details': alerts}
def check_port(self, host: str, port: int, timeout: int = 3) -> bool:
"""检查 TCP 端口是否可达"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
try:
return sock.connect_ex((host, port)) == 0
finally:
sock.close()
def check_api(self, url: str, timeout: int = 5) -> Dict:
"""检查 HTTP 接口健康状态"""
try:
resp = requests.get(url, timeout=timeout)
return {
'status': 'ok' if resp.status_code == 200 else 'error',
'code': resp.status_code,
'latency_ms': int(resp.elapsed.total_seconds() * 1000)
}
except requests.exceptions.Timeout:
return {'status': 'error', 'reason': 'timeout'}
except Exception as e:
return {'status': 'error', 'reason': str(e)}
def run_all(self, services: List[Dict]) -> None:
"""执行全量巡检并打印报告"""
print(f"\n[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 巡检开始")
print("=" * 60)
# 磁盘检查
disk = self.check_disk()
status_icon = "✅" if disk['status'] == 'ok' else "🚨"
print(f"{status_icon} 磁盘: {disk['status']}")
for detail in disk['details']:
print(f" ⚠️ {detail}")
# 服务检查
for svc in services:
if svc['type'] == 'port':
ok = self.check_port(svc['host'], svc['port'])
icon = "✅" if ok else "🚨"
print(f"{icon} {svc['name']}: {'UP' if ok else 'DOWN'}")
elif svc['type'] == 'api':
result = self.check_api(svc['url'])
icon = "✅" if result['status'] == 'ok' else "🚨"
latency = result.get('latency_ms', '-')
print(f"{icon} {svc['name']}: {result['status']} ({latency}ms)")
if __name__ == '__main__':
checker = HealthChecker()
checker.run_all([
{'name': 'MySQL', 'type': 'port', 'host': '10.0.0.10', 'port': 3306},
{'name': 'Redis', 'type': 'port', 'host': '10.0.0.11', 'port': 6379},
{'name': 'API健康', 'type': 'api', 'url': 'http://localhost:8080/health'},
])12.2 重试装饰器
python
import time
import functools
import logging
logger = logging.getLogger(__name__)
def retry(max_attempts: int = 3, delay: float = 1.0, exceptions=(Exception,)):
"""
通用重试装饰器。
max_attempts:最大重试次数
delay:每次重试间隔(秒)
exceptions:捕获哪些异常才重试
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except exceptions as e:
last_exception = e
if attempt < max_attempts:
logger.warning(
f"{func.__name__} 第 {attempt} 次失败: {e},"
f"{delay}s 后重试..."
)
time.sleep(delay)
else:
logger.error(f"{func.__name__} 重试 {max_attempts} 次后仍失败")
raise last_exception
return wrapper
return decorator
# 使用示例
@retry(max_attempts=3, delay=2.0, exceptions=(ConnectionError, TimeoutError))
def call_external_api(url: str):
import requests
return requests.get(url, timeout=5)12.3 端口扫描工具
python
#!/usr/bin/env python3
"""
多线程端口扫描器,支持端口范围和常见服务识别。
"""
import socket
import concurrent.futures
import argparse
from datetime import datetime
COMMON_SERVICES = {
21: "FTP", 22: "SSH", 23: "Telnet", 25: "SMTP",
53: "DNS", 80: "HTTP", 110: "POP3", 143: "IMAP",
443: "HTTPS", 3306: "MySQL", 3389: "RDP",
5432: "PostgreSQL", 6379: "Redis", 8080: "HTTP-Alt",
27017: "MongoDB", 9200: "Elasticsearch", 2181: "ZooKeeper",
}
def scan_port(host: str, port: int, timeout: float = 1.0) -> Tuple[int, bool]:
"""扫描单个端口,返回 (端口号, 是否开放)"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
try:
result = sock.connect_ex((host, port))
return port, result == 0
except Exception:
return port, False
finally:
sock.close()
def scan_host(host: str, ports, max_workers: int = 100) -> List[int]:
"""并发扫描多个端口,返回开放端口列表"""
open_ports = []
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(scan_port, host, p): p for p in ports}
for future in concurrent.futures.as_completed(futures):
port, is_open = future.result()
if is_open:
open_ports.append(port)
service = COMMON_SERVICES.get(port, "Unknown")
print(f" [+] {port:5d}/tcp {service}")
return sorted(open_ports)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('host')
parser.add_argument('-p', '--ports', default='1-1000')
args = parser.parse_args()
if '-' in args.ports:
start, end = map(int, args.ports.split('-'))
ports = range(start, end + 1)
else:
ports = [int(p) for p in args.ports.split(',')]
print(f"扫描 {args.host},共 {len(list(ports))} 个端口")
open_ports = scan_host(args.host, ports)
print(f"\n共发现 {len(open_ports)} 个开放端口")十三、Shell 日志分析
13.1 Nginx 访问日志分析脚本
bash
#!/bin/bash
# nginx_analysis.sh - 分析 Nginx access.log
# 用法:bash nginx_analysis.sh [日志文件] [TOP N]
LOG_FILE=${1:-"/var/log/nginx/access.log"}
LIMIT=${2:-10}
[[ ! -f "$LOG_FILE" ]] && { echo "文件不存在: $LOG_FILE"; exit 1; }
echo "============================================"
echo "Nginx 日志分析 | $(date '+%Y-%m-%d %H:%M:%S')"
echo "文件: $LOG_FILE"
echo "============================================"
# 总体统计
total=$(wc -l < "$LOG_FILE")
unique_ips=$(awk '{print $1}' "$LOG_FILE" | sort -u | wc -l)
echo -e "\n【总体】请求数: $total | 独立IP: $unique_ips"
# TOP N 访问 IP
echo -e "\n【TOP $LIMIT 访问 IP】"
awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -"$LIMIT" | \
awk '{printf " %-20s %s 次\n", $2, $1}'
# HTTP 状态码分布
echo -e "\n【状态码分布】"
awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -rn | \
awk '{printf " %s %s 次\n", $2, $1}'
# 每分钟请求量(TOP 20)
echo -e "\n【每分钟请求量 TOP 20】"
awk '{print $4}' "$LOG_FILE" | cut -d: -f1,2 | tr -d '[' | \
sort | uniq -c | sort -rn | head -20 | \
awk '{printf " %-25s %s 次\n", $2, $1}'
# 最近 20 条 5xx 错误
echo -e "\n【最近 5xx 错误】"
awk '$9 ~ /^5[0-9][0-9]$/ {print $4, $9, $7}' "$LOG_FILE" | \
tail -20 | awk '{printf " %s | %s | %s\n", $1, $2, $3}'
echo -e "\n============================================"13.2 常用 Shell 运维命令
bash
# ── 进程排查 ──────────────────────────────────────────
# 按 CPU 使用率排序,查看 TOP 10 进程
ps aux --sort=-%cpu | head -11
# 按内存使用率排序
ps aux --sort=-%mem | head -11
# 查看 Java 进程的线程 CPU 使用(定位热点线程)
top -H -p $(pgrep -f java | head -1)
# ── IO 排查 ──────────────────────────────────────────
# 实时查看磁盘 IO(%util 接近 100% 说明 IO 瓶颈)
iostat -x 1 5
# 查看哪个进程 IO 最高
iotop -ao
# ── 网络排查 ──────────────────────────────────────────
# 统计各状态 TCP 连接数
ss -s
# 查看 80 端口连接数
ss -tn state established '( dport = :80 or sport = :80 )' | wc -l
# 测试接口各阶段耗时(DNS/TCP/SSL/TTFB/总时间)
curl -o /dev/null -s -w \
"DNS: %{time_namelookup}s\nTCP: %{time_connect}s\nSSL: %{time_appconnect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
https://example.com
# ── 日志快速分析 ──────────────────────────────────────
# 统计最近 1 小时的 ERROR 日志数量
awk -v d="$(date -d '1 hour ago' '+%Y-%m-%d %H')" '$0 ~ d && /ERROR/' app.log | wc -l
# 清空日志文件但不影响正在写入的进程(不能用 rm!)
truncate -s 0 /var/log/app/app.log
# 或者
> /var/log/app/app.log十四、Linux 系统排查
14.1 SSH 登录不上排查流程
bash
# 1. 确认网络连通性
ping -c 4 <server_ip>
# 2. 确认 SSH 端口是否监听
nc -zv <server_ip> 22
# 3. 登录物理机或带外管理(IPMI/iLO)后排查
systemctl status sshd # 查看 SSH 服务状态
systemctl restart sshd # 尝试重启
# 4. 防火墙检查
iptables -L -n | grep 22
firewall-cmd --list-all
# 5. 资源耗尽检查
free -h # 内存是否不足
df -h # 磁盘是否满了
ulimit -n # 文件描述符限制
# 6. 查看 SSH 日志
journalctl -u sshd -n 50 --no-pager
cat /var/log/secure | tail -50 # CentOS
cat /var/log/auth.log | tail -50 # Ubuntu
# 7. fail2ban 是否封禁了 IP
fail2ban-client status sshd
fail2ban-client set sshd unbanip <your_ip>14.2 服务器负载高排查
bash
# 第一步:看负载和 CPU
uptime # 1/5/15 分钟平均负载
top -b -n 1 | head -20 # 快照模式,看 CPU 占用 TOP 进程
# 第二步:判断瓶颈类型
vmstat 1 5 # si/so 高 → 内存不足在用 swap
# wa 高 → IO 等待
iostat -x 1 5 # %util 接近 100% → IO 瓶颈
iotop -ao # 哪个进程 IO 最高
# 第三步:CPU 密集型定位
ps aux --sort=-%cpu | head -11
# Java 进程热点线程
top -H -p $(pgrep -f java | head -1)
# 找到高 CPU 线程 TID,转 16 进制后在 jstack 中定位
printf '%x\n' <tid>
jstack <pid> | grep -A 20 <hex_tid>
# 第四步:内存问题
free -h
ps aux --sort=-%mem | head -11
# Java 堆内存
jstat -gc <pid> 1000 5
jmap -histo <pid> | head -30
# 第五步:网络连接数
ss -s
ss -tn state established | wc -l
netstat -an | awk '{print $6}' | sort | uniq -c | sort -rn14.3 磁盘满了快速处理
bash
# 定位大文件
du -sh /* 2>/dev/null | sort -rh | head -20
du -sh /var/log/* | sort -rh | head -10
# 找出超过 100MB 的文件
find / -size +100M -type f 2>/dev/null | xargs ls -lh | sort -k5 -rh | head -20
# 清空日志(不能 rm,要用 truncate)
truncate -s 0 /var/log/app/app.log
> /var/log/nginx/access.log
# 清理 Docker 占用
docker system prune -f
docker volume prune -f
# 清理 yum/apt 缓存
yum clean all
apt-get clean
# 查看已删除但仍被进程占用的文件(占用空间但 ls 看不到)
lsof | grep deleted | awk '{print $7, $9}' | sort -rn | head -10
# 重启对应进程即可释放十五、网络基础
15.1 TCP 三次握手 / 四次挥手
三次握手(建立连接):
Client ──── SYN(seq=x) ────────────────────> Server
Client <─── SYN-ACK(seq=y, ack=x+1) ──────── Server
Client ──── ACK(ack=y+1) ──────────────────> Server
四次挥手(关闭连接):
Client ──── FIN ────────────────────────────> Server
Client <─── ACK ──────────────────────────── Server
Client <─── FIN ──────────────────────────── Server
Client ──── ACK ────────────────────────────> Server
(Client 进入 TIME_WAIT,等 2MSL 后彻底关闭)TIME_WAIT 的意义:防止最后一个 ACK 丢失后服务端重发 FIN 时找不到连接;等待旧数据包在网络中消失,避免影响新连接。
15.2 网络访问慢排查
bash
# DNS 解析耗时
time nslookup example.com
dig example.com
# 各阶段耗时(DNS/TCP/SSL/TTFB/总时间)
curl -o /dev/null -s -w \
"DNS:%{time_namelookup}s TCP:%{time_connect}s SSL:%{time_appconnect}s TTFB:%{time_starttransfer}s Total:%{time_total}s\n" \
https://example.com
# 路由追踪
traceroute example.com
# 服务端连接数
ss -s
netstat -an | grep :80 | wc -l
# 带宽占用
iftop -i eth0分层定位思路:
- DNS 慢 → 检查 DNS 服务器配置
- TCP 连接慢 → 网络/防火墙问题
- SSL 握手慢 → 证书或服务器性能
- TTFB 慢 → 后端应用处理慢
- 内容下载慢 → 带宽瓶颈或资源过大
15.3 负载均衡算法对比
| 算法 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 轮询 | 依次分配 | 简单公平 | 不考虑负载 | 无状态服务 |
| 加权轮询 | 按权重比例 | 可手动调配 | 权重固定 | 异构服务器 |
| 最少连接 | 分给连接数最少的 | 动态适应 | 需维护状态 | 长连接场景 |
| IP Hash | 按客户端 IP 哈希 | 会话保持 | 扩缩容影响大 | 有状态服务 |
| 一致性哈希 | 哈希环,扩缩容影响最小 | 扩缩容友好 | 实现复杂 | 分布式缓存 |
十六、Docker 与 Kubernetes
16.1 Dockerfile 多阶段构建
dockerfile
# 阶段1:构建(包含编译工具,体积大)
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download # 先复制依赖,利用层缓存
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp ./cmd/main.go
# 阶段2:运行(只含二进制,体积小)
FROM alpine:3.18
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
COPY --from=builder /app/myapp .
RUN adduser -D appuser
USER appuser # 非 root 运行,提升安全性
EXPOSE 8080
ENTRYPOINT ["./myapp"]多阶段构建可将镜像从 800MB+ 压缩到 20MB 以内。
16.2 Kubernetes 常用排查命令
bash
# ── Pod 排查 ──────────────────────────────────────────
kubectl get pods -n <ns> -o wide # 查看 Pod 状态和所在节点
kubectl describe pod <pod> -n <ns> # 详细事件,排查 Pending/CrashLoop
kubectl logs <pod> -n <ns> --tail=100 # 查看日志
kubectl logs <pod> -n <ns> -c <container> --previous # 上一次崩溃的日志
kubectl exec -it <pod> -n <ns> -- /bin/sh # 进入容器
# ── 常见问题判断 ──────────────────────────────────────
# Pending → 资源不足或节点选择器不匹配
kubectl describe pod <pod> | grep -A 10 Events
# CrashLoopBackOff → 容器启动后立即退出
kubectl logs <pod> --previous # 看上次退出原因
# ImagePullBackOff → 镜像拉取失败
kubectl describe pod <pod> | grep image # 确认镜像地址和 tag
# OOMKilled → 内存超限
kubectl describe pod <pod> | grep -i oom
# ── 节点排查 ──────────────────────────────────────────
kubectl get nodes # 查看节点状态
kubectl describe node <node> # 查看节点资源和事件
kubectl top nodes # 节点资源使用(需 metrics-server)
kubectl top pods -n <ns> # Pod 资源使用
# ── 网络排查 ──────────────────────────────────────────
# 测试 Pod 间连通性
kubectl run test --image=busybox --rm -it -- wget -qO- http://<svc>.<ns>.svc.cluster.local
# 查看 Service 端点
kubectl get endpoints <svc> -n <ns>16.3 K8s 资源配置规范
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: prod
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: harbor.example.com/prod/order-service:v1.2.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "500m" # 调度依据,保证最低资源
memory: "512Mi"
limits:
cpu: "2" # 上限,防止单 Pod 抢占过多
memory: "2Gi"
readinessProbe: # 就绪探针:通过才接收流量
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe: # 存活探针:失败则重启容器
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
affinity:
podAntiAffinity: # 反亲和:同一服务的 Pod 分散到不同节点
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: order-service
topologyKey: kubernetes.io/hostname十七、MySQL 优化
17.1 慢查询分析
sql
-- 开启慢查询日志
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1; -- 超过 1 秒记录
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
-- 用 EXPLAIN 分析执行计划
EXPLAIN SELECT * FROM orders WHERE user_id = 12345 AND status = 'paid';
-- 关注:type(ALL 最差,ref/eq_ref 较好,const 最好)
-- key(实际使用的索引)
-- rows(扫描行数,越小越好)
-- Extra(Using filesort / Using temporary 是警告信号)
-- 查看当前正在执行的慢查询
SHOW PROCESSLIST;
SHOW FULL PROCESSLIST;
-- 用 pt-query-digest 分析慢查询日志
pt-query-digest /var/log/mysql/slow.log | head -10017.2 索引设计原则
sql
-- 联合索引遵循最左前缀原则
-- 索引 (a, b, c),以下查询能用到索引:
-- WHERE a=1 ✅ 用到 a
-- WHERE a=1 AND b=2 ✅ 用到 a,b
-- WHERE a=1 AND b=2 AND c=3 ✅ 用到 a,b,c
-- WHERE b=2 ❌ 跳过了 a,用不到
-- WHERE a=1 AND c=3 ✅ 只用到 a(c 跳过了 b)
-- 覆盖索引:查询字段全在索引中,无需回表
CREATE INDEX idx_user_status ON orders(user_id, status, created_at);
-- 以下查询直接从索引返回,不回表
SELECT user_id, status, created_at FROM orders WHERE user_id = 123;
-- 区分度低的字段不适合单独建索引(如 status 只有几个值)
-- 适合放在联合索引的后面
-- 查看索引使用情况
SELECT * FROM information_schema.INDEX_STATISTICS
WHERE TABLE_NAME = 'orders';
-- 找出未使用的索引
SELECT * FROM sys.schema_unused_indexes;17.3 连接池与参数优化
ini
# my.cnf 关键参数
[mysqld]
# 连接数
max_connections = 1000
max_connect_errors = 100000
# InnoDB 缓冲池(设为物理内存的 70%~80%)
innodb_buffer_pool_size = 16G
innodb_buffer_pool_instances = 8 # 每个实例 2G,减少锁竞争
# 日志
innodb_log_file_size = 2G # 越大崩溃恢复越慢,但写入性能越好
innodb_flush_log_at_trx_commit = 1 # 1=最安全,2=性能好但断电可能丢1秒数据
# 慢查询
slow_query_log = ON
long_query_time = 1
log_queries_not_using_indexes = ONbash
# 监控连接数
mysql -e "SHOW STATUS LIKE 'Threads_connected';"
mysql -e "SHOW STATUS LIKE 'Max_used_connections';"
# 监控缓冲池命中率(应 > 99%)
mysql -e "SHOW STATUS LIKE 'Innodb_buffer_pool_read%';"十八、Redis 实战
18.1 常用数据结构与场景
bash
# String:计数器、缓存、分布式锁
SET user:1001:name "张三" EX 3600 # 带过期时间
INCR page:view:count # 原子计数
SETNX lock:order:1001 1 # 分布式锁(NX=不存在才设置)
# Hash:对象存储(比 JSON 字符串更节省内存,支持单字段更新)
HSET user:1001 name "张三" age 30 city "杭州"
HGET user:1001 name
HGETALL user:1001
# List:消息队列、最新列表
LPUSH news:list "新闻标题1" # 左侧插入
LRANGE news:list 0 9 # 取前 10 条
BRPOP task:queue 30 # 阻塞式消费(超时 30s)
# Set:去重、标签、共同好友
SADD user:1001:tags "python" "k8s"
SISMEMBER user:1001:tags "python" # 是否包含
SINTER user:1001:tags user:1002:tags # 交集(共同标签)
# ZSet:排行榜、延迟队列
ZADD leaderboard 9800 "user:1001"
ZREVRANGE leaderboard 0 9 WITHSCORES # TOP 10
ZADD delay:queue <timestamp> "task:123" # 延迟队列(score=执行时间戳)18.2 缓存常见问题
缓存穿透:查询不存在的 key,每次都打到数据库
解决:① 缓存空值(TTL 短一些)
② 布隆过滤器(BloomFilter)拦截不存在的 key
缓存击穿:热点 key 过期瞬间,大量请求同时打到数据库
解决:① 互斥锁(SETNX),只让一个请求重建缓存
② 热点 key 不设过期时间,后台异步更新
缓存雪崩:大量 key 同时过期,或 Redis 宕机
解决:① 过期时间加随机抖动(base_ttl + random(0, 300))
② Redis 集群/哨兵保证高可用
③ 本地缓存(Caffeine)作为二级缓存兜底18.3 Redis 运维常用命令
bash
# 内存分析
redis-cli info memory | grep used_memory_human
redis-cli --bigkeys # 找出大 key(慎用,会扫全库)
redis-cli --memkeys # 按内存排序
# 慢查询
redis-cli config set slowlog-log-slower-than 10000 # 超过 10ms 记录
redis-cli slowlog get 10 # 查看最近 10 条慢查询
# 连接数
redis-cli info clients | grep connected_clients
# 持久化状态
redis-cli info persistence | grep -E "rdb|aof"
# 主从状态
redis-cli info replication
# 清理过期 key(生产慎用 FLUSHDB)
redis-cli --scan --pattern "session:*" | xargs redis-cli del十九、根因分析
19.1 故障定位标准流程
1. 发现告警
└── 确认影响范围(哪些服务/用户受影响)
2. 快速止血(先恢复,再分析)
└── 重启服务 / 回滚版本 / 切换流量
3. 分层排查
├── 应用层:日志 ERROR、接口错误率、响应时间
├── 中间件层:DB 慢查询、Redis 连接数、MQ 积压
├── 基础设施层:CPU/内存/磁盘/网络
└── 外部依赖:第三方接口、DNS、CDN
4. 根因确认
└── 时间线对齐:告警时间 vs 变更时间 vs 指标异常时间
5. 复盘
└── 5-Why 分析 → 改进措施 → 跟踪落地19.2 常见故障传播模式
数据库连接池耗尽
→ 应用线程全部阻塞等待连接
→ 接口响应超时
→ 前端报错 / 用户投诉
磁盘写满
→ 日志无法写入 → 应用崩溃
→ 数据库无法写 binlog → 主从同步中断
内存不足(OOM)
→ 系统 kill 进程(OOM Killer)
→ 服务不可用
→ 告警风暴(衍生告警淹没根因告警)
网络丢包
→ TCP 重传增加 → 延迟升高
→ 超时告警 → 服务降级19.3 告警根因推断(Python 实现)
python
class RootCauseAnalyzer:
"""
基于知识图谱的根因推断:
当一组告警同时出现时,找出最可能的根因。
"""
# 已知故障传播模式:根因 → 衍生告警列表
KNOWLEDGE_GRAPH = {
"DatabaseConnectionPoolFull": ["APITimeout", "OrderFailed", "SlowQuery"],
"DiskFull": ["LogWriteFailed", "AppCrash", "DBWriteError"],
"NetworkPacketLoss": ["ServiceUnavailable", "HighLatency", "TCPRetransmit"],
"MemoryOOM": ["ProcessKilled", "ServiceRestart", "HighSwap"],
}
def find_root_cause(self, active_alerts: list) -> str:
alert_set = set(active_alerts)
best_match = None
best_score = 0
for root, derivatives in self.KNOWLEDGE_GRAPH.items():
# 计算命中率:衍生告警中有多少出现在当前告警组里
hits = len(set(derivatives) & alert_set)
score = hits / len(derivatives)
if score > best_score:
best_score = score
best_match = root
return best_match if best_score > 0.3 else "Unknown"二十、容量规划与成本优化
20.1 容量评估方法
python
def estimate_capacity(current_qps: float, growth_rate: float,
months: int, safety_factor: float = 1.3) -> dict:
"""
容量预测:基于当前 QPS 和增长率,预测未来资源需求。
safety_factor:安全系数,通常 1.2~1.5(留 20%~50% 余量)
"""
future_qps = current_qps * ((1 + growth_rate) ** months)
required_qps = future_qps * safety_factor
# 假设单核可处理 200 QPS(根据实际压测结果调整)
qps_per_core = 200
required_cores = required_qps / qps_per_core
return {
"current_qps": current_qps,
"future_qps": round(future_qps, 1),
"required_qps": round(required_qps, 1),
"required_cores": round(required_cores, 1),
"months": months,
}
# 示例:当前 1000 QPS,月增长 10%,预测 6 个月后
result = estimate_capacity(1000, 0.10, 6)
# → future_qps=1771, required_qps=2302, required_cores=11.520.2 K8s HPA 自动扩缩容
yaml
# 基于 CPU 使用率自动扩缩容
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
namespace: prod
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 2 # 最少保持 2 个副本
maxReplicas: 20 # 最多扩到 20 个
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # CPU 超过 70% 触发扩容
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60 # 扩容稳定窗口 60s,避免抖动
policies:
- type: Pods
value: 4
periodSeconds: 60 # 每分钟最多扩 4 个 Pod
scaleDown:
stabilizationWindowSeconds: 300 # 缩容稳定窗口 5 分钟,避免频繁缩容
policies:
- type: Pods
value: 2
periodSeconds: 6020.3 成本优化策略
计算资源优化:
① 合理设置 requests/limits,避免资源浪费
- requests 过高 → 节点利用率低,浪费钱
- limits 过低 → 容器频繁 OOM,影响稳定性
② 使用 VPA(垂直 Pod 自动扩缩容)自动推荐合理的 requests 值
③ 离线任务使用 Spot/竞价实例,节省 60%~80% 费用
存储优化:
① 日志按天分索引,超过 30 天自动删除(ILM 策略)
② 冷数据迁移到对象存储(OSS/S3),比块存储便宜 10 倍
③ 镜像定期清理,Harbor 设置保留策略(只保留最近 10 个 tag)
网络优化:
① 同 Region 内流量走内网,避免公网流量费用
② 静态资源走 CDN,减少源站带宽
③ 合理设置 Keep-Alive,减少 TCP 连接建立开销20.4 Prometheus 容量告警规则
yaml
groups:
- name: capacity_alerts
rules:
# 磁盘预计 4 小时后用满
- alert: DiskWillBeFull
expr: |
predict_linear(node_filesystem_free_bytes[1h], 4 * 3600) < 0
for: 10m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} 磁盘预计 4 小时后用满"
# 内存使用率持续超过 85%
- alert: HighMemoryUsage
expr: |
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)
/ node_memory_MemTotal_bytes > 0.85
for: 15m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} 内存使用率超过 85%"
# K8s 节点资源超分
- alert: NodeResourceOvercommit
expr: |
sum by (node) (kube_pod_container_resource_requests{resource="cpu"})
/ kube_node_status_allocatable{resource="cpu"} > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "节点 {{ $labels.node }} CPU 请求超过可分配量的 90%"手册说明:本手册整理自项目实战问答,所有代码片段均可直接使用或按需调整。建议结合具体业务场景做参数调优,不要照搬数值。