阶段2(下):运维专题 Docker/Nginx/Linux/CI-CD/Git 的课程、企业场景、面试题和可运行示例
This commit is contained in:
1 parent
5eb67c5d08
commit
b8010a74f3
42 files changed
+3533
-55
No files matched your search
@@ -0,0 +1,65 @@
|
||||
"""分析 Nginx 访问日志:状态码分布、请求最多的 IP、最慢的接口、5xx 明细。
|
||||
|
||||
日志格式:Nginx combined 格式末尾追加 $request_time(秒),对应配置:
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent '
|
||||
'"$http_referer" "$http_user_agent" $request_time';
|
||||
运行:python analyze.py [日志文件],默认分析同目录的 sample_access.log
|
||||
等价的命令行写法见 Linux 专题课程(awk / sort / uniq)。
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter, defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
LINE = re.compile(
|
||||
r'(?P<ip>\S+) \S+ \S+ \[(?P<time>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+) [^"]*" '
|
||||
r'(?P<status>\d{3}) (?P<bytes>\d+) "[^"]*" "[^"]*" (?P<rt>[\d.]+)'
|
||||
)
|
||||
|
||||
|
||||
def parse(path: Path):
|
||||
for n, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
|
||||
m = LINE.match(line)
|
||||
if not m:
|
||||
print(f"第 {n} 行格式不匹配,跳过", file=sys.stderr)
|
||||
continue
|
||||
d = m.groupdict()
|
||||
d["path"] = d["path"].split("?")[0] # 去掉查询参数,按接口聚合
|
||||
d["rt"] = float(d["rt"])
|
||||
yield d
|
||||
|
||||
|
||||
def analyze(rows: list[dict]) -> dict:
|
||||
by_api: dict[str, list[float]] = defaultdict(list)
|
||||
for r in rows:
|
||||
by_api[f'{r["method"]} {r["path"]}'].append(r["rt"])
|
||||
slowest = sorted(((max(v), k) for k, v in by_api.items()), reverse=True)[:3]
|
||||
return {
|
||||
"status": Counter(r["status"] for r in rows),
|
||||
"top_ips": Counter(r["ip"] for r in rows).most_common(3),
|
||||
"slowest": [(k, t) for t, k in slowest],
|
||||
"errors_5xx": [(r["time"], r["status"], r["path"], r["rt"]) for r in rows if r["status"].startswith("5")],
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
path = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).with_name("sample_access.log")
|
||||
rows = list(parse(path))
|
||||
res = analyze(rows)
|
||||
print(f"共 {len(rows)} 条请求")
|
||||
print("状态码分布:", dict(sorted(res["status"].items())))
|
||||
print("请求最多的 IP:", res["top_ips"])
|
||||
print("最慢的接口(最大耗时):", res["slowest"])
|
||||
print("5xx 明细:")
|
||||
for e in res["errors_5xx"]:
|
||||
print(" ", *e)
|
||||
|
||||
if path.name == "sample_access.log": # 用示例日志自检
|
||||
assert res["status"]["401"] == 3 and res["status"]["429"] == 1
|
||||
assert res["top_ips"][0] == ("198.51.100.7", 4)
|
||||
assert res["slowest"][0] == ("GET /api/reports/monthly", 30.001)
|
||||
assert [e[1] for e in res["errors_5xx"]] == ["504", "502"]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
# 不自动运行:线上问题的“第一分钟”快照,只读命令,不修改任何东西
|
||||
# 用法:bash diag.sh [进程名关键字] > diag-$(date +%F-%H%M).txt
|
||||
set -uo pipefail
|
||||
|
||||
keyword="${1:-}"
|
||||
section() { printf '\n===== %s =====\n' "$1"; }
|
||||
|
||||
section "时间和负载"
|
||||
date
|
||||
uptime
|
||||
|
||||
section "CPU 占用最高的进程"
|
||||
ps -eo pid,ppid,user,%cpu,%mem,etime,cmd --sort=-%cpu | head -n 10
|
||||
|
||||
section "内存"
|
||||
free -h
|
||||
|
||||
section "磁盘空间和 inode"
|
||||
df -h -x tmpfs -x devtmpfs
|
||||
df -i -x tmpfs -x devtmpfs | awk 'NR == 1 || $5+0 > 80'
|
||||
|
||||
section "监听端口"
|
||||
ss -lntp 2>/dev/null | head -n 30
|
||||
|
||||
section "TCP 连接状态统计"
|
||||
ss -ant | awk 'NR > 1 {count[$1]++} END {for (s in count) print s, count[s]}'
|
||||
|
||||
if [[ -n "$keyword" ]]; then
|
||||
section "进程:$keyword"
|
||||
pgrep -af -- "$keyword" || echo "没有找到进程"
|
||||
fi
|
||||
|
||||
section "最近的内核日志(OOM 等)"
|
||||
if kernel_log="$(journalctl -k --since "1 hour ago" --no-pager 2>/dev/null)" || kernel_log="$(dmesg 2>/dev/null)"; then
|
||||
grep -iE 'oom|killed process' <<<"$kernel_log" | tail -n 20 || echo "最近没有 OOM 记录"
|
||||
else
|
||||
echo "无权限读取内核日志"
|
||||
fi
|
||||
@@ -0,0 +1,10 @@
|
||||
203.0.113.5 - - [23/Oct/2026:10:00:01 +0800] "GET /api/orders?page=1 HTTP/1.1" 200 512 "-" "Mozilla/5.0" 0.023
|
||||
203.0.113.5 - - [23/Oct/2026:10:00:02 +0800] "GET /api/orders?page=2 HTTP/1.1" 200 498 "-" "Mozilla/5.0" 0.031
|
||||
198.51.100.7 - - [23/Oct/2026:10:00:02 +0800] "POST /api/login HTTP/1.1" 401 64 "-" "curl/8.5.0" 0.004
|
||||
198.51.100.7 - - [23/Oct/2026:10:00:02 +0800] "POST /api/login HTTP/1.1" 401 64 "-" "curl/8.5.0" 0.004
|
||||
198.51.100.7 - - [23/Oct/2026:10:00:03 +0800] "POST /api/login HTTP/1.1" 401 64 "-" "curl/8.5.0" 0.005
|
||||
198.51.100.7 - - [23/Oct/2026:10:00:03 +0800] "POST /api/login HTTP/1.1" 429 31 "-" "curl/8.5.0" 0.001
|
||||
192.0.2.10 - - [23/Oct/2026:10:00:04 +0800] "GET /api/reports/monthly HTTP/1.1" 504 167 "-" "Mozilla/5.0" 30.001
|
||||
192.0.2.10 - - [23/Oct/2026:10:00:40 +0800] "GET /api/reports/monthly HTTP/1.1" 200 20480 "-" "Mozilla/5.0" 12.874
|
||||
203.0.113.9 - - [23/Oct/2026:10:00:41 +0800] "GET /api/orders/42 HTTP/1.1" 502 157 "-" "Mozilla/5.0" 0.002
|
||||
203.0.113.9 - - [23/Oct/2026:10:00:42 +0800] "GET /api/orders/42 HTTP/1.1" 200 233 "-" "Mozilla/5.0" 0.018
|
||||
Reference in new issue
Block a user