个人学习与运维笔记不涉及商业推广。文中不含任何购买链接、推荐用语配置和命令均为个人实操记录环境与参数按自己的需求酌情调整。一、写在前面过去一年多我把自己一台入门云服务器一步步折腾成了一组带有多地域就近接入、入口高可用、数据同步和故障演练能力的小架构。过程中最大的感受是单个技术点都好搜难的是不知道它们之间什么关系、该按什么顺序学。举几个我自己踩过的例子一上来就配 Keepalived结果发现会话没做一致性用户一刷新就掉登录监控先上了 Grafana却没配告警规则磁盘写满了都没人知道多地域节点铺好了才发现数据库还写死在一处异地读总是旧数据演练脚本写得很漂亮第一次真出事时探活脚本自己先挂了。这些问题有一个共同点它们都不是某个组件不会配而是整套东西的顺序和关系没理顺。所以这篇文章做一件事——把这套东西按学习顺序整理成一份路径图每一阶段讲清楚解决什么问题、核心知识是什么、关键命令有哪些。文末还有一张命令速查总表和指标总表可以当手册用。配套的详细实操记录我已经拆成了 18 篇独立的笔记本文只做索引和要点提炼具体每一步的完整配置在那 18 篇里。二、我的实验环境项目配置服务器提供商LuckVM香港节点2 核 2G × 3Web × 2 入口香港 GPU 节点RTX 4090 × 124G 显存美西节点2 核 2G × 1异地副本系统Ubuntu 22.04 LTS虚拟化KVM数据库MySQL 8.0 / Redis 7对象存储MinIO监控Prometheus Grafana AlertmanagerDNSBIND 9自建权威 GeoIP 视图说明一下上面这些是我日常一直在用的 LuckVM 机器规格都是最入门的那档——2 核 2G、5M 带宽。选这个配置不是因为它快恰恰相反是因为它小在这么小的机器上能把整套东西跑稳换到更大的机器上只会更轻松。如果你用的是别家的机器本文的原理、命令、参数全都一样不受影响。三、这套架构到底在解决什么问题先给一张全景图后面所有内容都挂在这张图上。用户请求 │ ▼ ┌──────────────────────────────┐ │ DNS 层自建权威 GeoIP 视图│ │ 国内 → 香港节点 │ │ 北美 → 美西节点 │ └──────────────┬───────────────┘ │ 返回就近节点 IP ▼ ┌──────────────────────────────┐ │ 入口层Keepalived VIP 漂移 │ │ 主备双机VIP 挂在活着的那台 │ └──────────────┬───────────────┘ │ ▼ ┌──────────────────────────────┐ │ 负载层Nginx upstream │ │ 多台 Web轮询 故障摘除 │ └──────────────┬───────────────┘ │ ┌────────────┴────────────┐ ▼ ▼ ┌─────────────┐ ┌──────────────┐ │ 应用层 │ │ GPU 推理层 │ │ PHP / Node │─────────▶│ vLLM / Ollama │ │ WordPress │ 内网调用 │ 模型 API │ └──────┬──────┘ └──────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ 数据层MySQL 主从 / Redis 复制 / MinIO │ │ 本地 异地副本自动备份 │ └─────────────────────────────────────┘ ▲ │ 采集 ┌──────┴──────────────────────────────┐ │ 可观测层Prometheus Grafana 告警 │ │ 主机指标 GPU 指标 业务探活 │ └─────────────────────────────────────┘这张图从上到下是请求的流向从下到上是支撑的顺序。学习的时候反过来按支撑顺序学最顺。四、学习路径总览七个阶段阶段主题解决的核心问题对应篇目一地基机器干净、安全、能用第 1-3 篇二应用落地站点/服务真正跑起来第 4-6 篇三可观测出问题能看见、能告警第 7-8 篇四GPU 算力把一张卡用明白第 9-10 篇五分层扩展从单机到多机第 11-12 篇六多地域从单地域到多地第 13-16 篇七收口让架构长期活着第 17-18 篇下面逐阶段展开。每一阶段我都会给出核心知识 关键命令 常见坑方便直接当复习材料用。五、阶段一地基篇 —— 把单机做扎实解决什么问题一台新机器到手最常见的三件事被暴力破解、配置乱七八糟、环境不统一。这个阶段就是把地基打平。核心知识1. SSH 安全是第一道门改端口只是减少噪音真正管用的是禁密码、只留密钥# /etc/ssh/sshd_configPasswordAuthentication no PermitRootLogin no PubkeyAuthenticationyesMaxAuthTries3LoginGraceTime30改完先别退出用新开会话验证能登再systemctl restart sshd否则容易把自己锁在外面。2. 防火墙默认拒绝sudoufw default deny incomingsudoufw default allow outgoingsudoufw allow22/tcpsudoufw allow80,443/tcpsudoufwenable3. Fail2ban 挡暴力破解# /etc/fail2ban/jail.local [sshd] enabled true maxretry 3 bantime 3600 findtime 6004. 自动安全更新sudoaptinstallunattended-upgrades-ysudodpkg-reconfigure--prioritylow unattended-upgrades5. 内核参数很多高并发问题其实在这里# /etc/sysctl.d/99-tuning.confnet.core.somaxconn65535net.ipv4.tcp_max_syn_backlog8192net.ipv4.tcp_tw_reuse1net.ipv4.ip_local_port_range1024065000fs.file-max1000000常见坑坑后果解法改 SSH 后没验证就重启自己进不去新会话验证后再重启tcp_tw_recycle照抄旧教程NAT 环境丢包该参数已废弃别开文件句柄没调高并发报 too many open filesfs.file-maxulimit -n六、阶段二应用落地篇 —— 从能跑到跑得好解决什么问题环境好了接下来是让东西真正跑起来Web 服务、数据库、应用本身。这一步决定了能不能提供服务。核心知识1. Nginx 是这一切的入口# 开启压缩 gzip on; gzip_types text/css application/javascript application/json; gzip_min_length 1k; # 静态资源缓存 location ~* \.(jpg|png|css|js|woff2)$ { expires 30d; add_header Cache-Control public, immutable; }2. 反向代理 HTTPSlocation / { proxy_pass http://127.0.0.1:8080; 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; }X-Forwarded-For这几个头一定要传否则后端拿到的永远是 127.0.0.1日志和风控全废。3. MySQL 小内存调优2G 机器的关键# /etc/mysql/mysql.conf.d/mysqld.cnf innodb_buffer_pool_size 512M innodb_log_file_size 128M max_connections 100 table_open_cache 400 tmp_table_size 32M max_heap_table_size 32M2G 的机器上innodb_buffer_pool_size给到 512M 是甜点给太大反而触发 OOM。4. 慢查询是性能第一现场slow_query_log 1 long_query_time 1 slow_query_log_file /var/log/mysql/slow.log排查用mysqldumpslow-st /var/log/mysql/slow.log|head-205. 备份是底线mysqldump --single-transaction--routines--triggers\-uroot -p$PASSmydb|gzip/backup/mydb_$(date%F).sql.gz--single-transaction保证 InnoDB 一致性快照不会锁表。常见坑坑后果解法反代没传真实 IP日志全是 127.0.0.1四个X-Forwarded-*头innodb_buffer_pool_size过大系统 OOM killer留足给 OS 的内存备份写在同机同盘机器挂了备份也没了异地/异盘备份七、阶段三可观测性篇 —— 让系统说话解决什么问题服务跑起来了但你不知道它现在健不健康、磁盘会不会下周一写满。这个阶段是给系统装上仪表盘和报警器。核心知识1. 监控三件套的分工组件角色类比Node Exporter采集主机指标传感器Prometheus拉取 存储 判规则仪表 大脑Grafana可视化显示屏Alertmanager通知 抑制喇叭2. 告警规则的关键是持续时间groups:-name:hostrules:-alert:DiskWillFillIn4Hoursexpr:predict_linear(node_filesystem_avail_bytes[6h],4*3600) 0for:10mlabels:{severity:warning}-alert:HighCPUexpr:100-avg by(instance)(rate(node_cpu_seconds_total{modeidle}[5m])) * 10085for:5mfor: 5m的意思是持续 5 分钟满足才告警这一条能挡掉 90% 的抖动误报。没配for的告警凌晨三点能把你叫醒十次。3. 磁盘预测比磁盘阈值有用得多用predict_linear预测多久后写满比超过 90% 就告警提前得多留给你的处理时间从几小时变成几天。4. 探活Blackbox-job_name:http-probemetrics_path:/probeparams:{module:[http_2xx]}static_configs:-targets:[https://example.com/healthz]relabel_configs:-source_labels:[__address__]target_label:__param_target-target_label:__address__replacement:127.0.0.1:9115常见坑坑后果解法只配置了面板没配告警出事了没人知道规则文件必须有告警没配for告警风暴一律加持续时间只监控主机不看业务服务挂了主机还健康加 Blackbox 探活八、阶段四GPU 算力篇 —— 一张卡的榨干指南解决什么问题如果机器上有 GPU玩法完全不同显存怎么分、模型怎么共存、GPU 状态怎么看。这一步把 GPU 从一块硬件变成一个可调度的资源。核心知识1. 显存被谁吃了显存占用 模型权重 KV Cache 激活/临时 CUDA 上下文开销关键认知大模型推理时显存的大头往往是 KV Cache不是权重。2. KV Cache 估算这是最容易低估的部分KV Cache ≈ 2 × 层数 × 注意力头数 × 头维度 × 序列长度 × 精度字节 × batch实测感受7B 模型上下文从 4K 拉到 32K总占用能从 6.6G 涨到 13.6G直接翻倍。3. vLLM 显存精调四参数vllm serve Qwen2.5-7B-Instruct\--gpu-memory-utilization0.90\--max-model-len8192\--max-num-seqs16\--swap-space44. Ollama 多模型共存环境变量OLLAMA_MAX_LOADED_MODELS2# 同时驻留几个模型OLLAMA_NUM_PARALLEL2# 每个模型几路并发OLLAMA_KEEP_ALIVE30m# 闲置多久卸载5. GPU 监控# 用 DCGM Exporter 暴露指标给 Prometheusdockerrun-d--gpusall-p9400:9400\nvcr.io/nvidia/k8s/dcgm-exporter:3.3.5-3.3.1-ubuntu22.04常用指标DCGM_FI_DEV_GPU_UTIL利用率、DCGM_FI_DEV_FB_USED显存、DCGM_FI_DEV_GPU_TEMP温度。常见坑坑后果解法只按权重体积估显存一跑就 OOM一定要算 KV Cachemax-model-len拉满显存被 KV 吃光按实际需求设僵尸进程占显存不释放显存泄漏假象nvidia-smi找 PIDkill 后确认释放九、阶段五架构分层与横向扩展篇 —— 从单机到多机解决什么问题单机再优化也有天花板。这个阶段把架构拆开让扛不住和挂掉这两件事都有解。核心知识1. 分层把 Web 和 GPU 拆开把 GPU 推理服务从 Web 里拆出来用 OpenAI 兼容接口通信# GPU 机器上vLLM 暴露 OpenAI 兼容端点vllm serve /models/Qwen2.5-7B-Instruct--port8000# Web 机器上Nginx 反代过去location /v1/{proxy_pass http://10.0.0.20:8000/v1/;}这样生成 5 秒的模型响应多一层反代也就多几十毫秒可忽略但换来的是两层可以独立扩缩、独立重启。2. 流式输出必须关掉缓冲大模型的打字机效果是 SSE 流式输出Nginx 默认会缓冲必须关location /v1/ { proxy_pass http://10.0.0.20:8000/v1/; proxy_buffering off; proxy_cache off; proxy_read_timeout 300s; proxy_set_header Connection ; proxy_http_version 1.1; chunked_transfer_encoding on; add_header X-Accel-Buffering no; }少任何一条前端都会卡住不吐字或最后一次性全出来。3. 负载均衡多台 Webupstream web_backend { least_conn; server 10.0.0.11:8080 max_fails3 fail_timeout10s; server 10.0.0.12:8080 max_fails3 fail_timeout10s; keepalive 32; } server { location / { proxy_pass http://web_backend; proxy_next_upstream error timeout http_502 http_503; proxy_http_version 1.1; proxy_set_header Connection ; } }keepalive 32这行很容易被漏掉。实测不开 keepalive后端TIME_WAIT能涨到上万开了之后稳定在几十个。4. 横向扩展的前提是无状态会话别放本机。三选一粘性会话ip_hash——简单但容错差一台挂了那批用户全断共享 SessionRedis——通用做法无状态 JWT——最彻底但带不了太多状态。常见坑坑后果解法有状态服务直接多开用户刷新就掉登录先做无状态化upstream 没配 keepalive后端端口被 TIME_WAIT 占满keepaliveConnection 只做了 Web 层多机入口还是单点下一步做入口热备十、阶段六多地域篇 —— 从单地域到多地解决什么问题Web 多机解决了扛得住但用户还是都要连到同一个地方机房整体挂了一样全停。这个阶段解决就近和异地。核心知识1. 就近接入GeoDNS用 BIND 的视图view GeoIP让不同地区的用户解析到不同节点# named.conf 里的 geoip ACL acl cn { geoip country CN; }; view cn { match-clients { cn; }; recursion no; zone example.com { type master; file /etc/bind/zones/db.example.com.cn; }; };BIND 会按视图顺序取第一个命中的所以视图顺序很重要。另外记得装libmaxminddb和bind9-dlz相关依赖并在named.conf.options里指定geoip-directory。2. 至少两台 NS一台权威 DNS 就是新的单点。用 TSIG 做主从同步tsig-keygen-aHMAC-SHA256 transfer-key从服务器上zone example.com { type slave; masters { 10.0.0.10 key transfer-key; }; file /var/cache/bind/db.example.com; };3. TTL 决定切换速度DNS 切换天生是分钟级的探测周期探活多久一次 TTL缓存多久过期叠加。想让切换快TTL 就得短但 TTL 太短会加大权威压力。一般业务记录 60s、不常变的 300s 比较平衡。4. 跨地域数据同步MySQL 主从GTID 半同步CHANGEREPLICATIONSOURCETOSOURCE_HOST10.1.0.10,SOURCE_AUTO_POSITION1,SOURCE_SSL1;Redisreplicaof 写安全replicaof 10.1.0.10 6379 min-replicas-to-write 1 min-replicas-max-lag 10min-replicas-to-write是关键——副本跟不上就拒绝写宁可不可写也不要脏写。对象存储MinIO 站点复制mcadmin replicateaddhk us常见坑坑后果解法GeoIP 库没放对目录视图不生效确认GeoIP2-Country.mmdb路径只有一台 NS权威单点至少两台 TSIG 同步主库单写还允许异地写数据冲突写集中一处异地只读十一、阶段七收口篇 —— 让架构活着解决什么问题前面六个阶段是把东西搭起来这最后一个阶段是让这套东西在真出问题时真的能顶用并且能被人接手。核心知识1. 故障演练主动打坏它搭好了不代表生效。用tc netem模拟网络劣化、用iptables模拟断网# 模拟 200ms 延迟 5% 丢包tc qdiscadddev eth0 root netem delay 200ms loss5%# 模拟整机断网iptables-AINPUT-s10.0.0.20-jDROP演练必须配护栏用split_clients只让一小部分流量进演练路径别让全量用户陪你做实验。2. Runbook出事时不用现想把什么告警 → 看什么 → 做什么写成手册。例如告警第一步第二步第三步VIP 漂移异常查 Keepalived 日志查网卡/ARP手动指定优先级复制延迟高查网络 RTT查主库负载临时降级为异步磁盘将满查大文件清日志/临时文件扩容或迁移3. 巡检 SOP日/周/月三档日看告警和容量趋势周看备份是否可恢复月做一次恢复演练。4. 配置即代码所有配置放 Git改动走改文件 →nginx -t校验 → 平滑 reloadnginx-tnginx-sreload常见坑坑后果解法从不做恢复演练备份是坏的都不知道定期真恢复一次演练没有护栏演练变真故障split_clients限流量配置手改不留痕出问题查不出谁改的配置进 Git十二、域名与寻址约定整套架构里有一个贯穿始终的小约定所有节点之间、所有对外服务一律用域名寻址不写裸 IP。理由很实际换机器、加节点、异地切换时只要改一行解析不用去翻每一份配置。比如数据库地址写db.example.com而不是10.1.0.10主从切换时改解析即可应用侧完全无感。这里说明一下落地方式example.com是你自己的域名api、db、grafana这类是二级域名比如api.qq.com、db.baidu.com、www.luckvm.com都可以通常在域名管理后台加几条 A 记录就解析过来了不需要额外申请什么。一个简单的命名规范用途命名示例说明对外主站app.example.com指向入口 VIPAPIapi.example.com指向 API 网关数据库db-primary.example.com/db-standby.example.com主从各一条监控grafana.example.com/prom.example.com可加访问限制十三、核心指标速查总表场景指标参考值香港到国内延迟RTT38~55 ms香港到北美延迟RTT310~380 ms入口热备切换耗时2.8~5.4 sWeb 单机宕机摘除耗时1~3 s双机 LB 提升QPS1850 → 4180含 keepaliveupstream 长连接TIME_WAIT上万 → 几十数据库复制延迟跨地域130~200 ms≈RTTRedis 写安全副本落后上限10 s监控栈总内存2 核 2G 机器~358 MBDCGM 内存占用单实例~65 MB2G 机器磁盘 IOPS随机读写数万级WordPress 优化后首页 TTFB812 ms → 38 ms十四、常用命令速查总集系统与安全# 查看监听端口ss-tulnp# 查看谁在占文件句柄lsof-n|awk{print $2}|sort|uniq-c|sort-nr|head# 系统资源top-b-n1|head-20free-hdf-hNginxnginx-t# 校验配置nginx-sreload# 平滑重载tail-f/var/log/nginx/access.logawk{print $1}access.log|sort|uniq-c|sort-nr|head# 访问最多的 IPMySQLmysql-eSHOW PROCESSLIST# 当前连接mysql-eSHOW ENGINE INNODB STATUS\G# InnoDB 状态mysql-eSHOW REPLICA STATUS\G# 复制状态mysqldumpslow-st slow.log|head-20# 慢查询排行Redisredis-cli INFO replication# 复制信息redis-cli--latency# 延迟redis-cli slowlog get10# 慢日志GPUnvidia-smi nvidia-smi --query-gpuutilization.gpu,memory.used,temperature.gpu--formatcsvfuser-v/dev/nvidia*# 谁在用 GPUDNSdigns1.example.com example.com shortdig8.8.8.8 example.com trace named-checkzone example.com /etc/bind/zones/db.example.com rndc reload mmdblookup--fileGeoIP2-Country.mmdb--ip1.2.3.4监控curl-slocalhost:9090/api/v1/targets|jq.data.activeTargets[].healthcurl-slocalhost:9100/metrics|grepnode_loadDockerdockerstats --no-streamdockercomposepsdockercompose logs--tail100servicedockersystem prune-f网络诊断mtr-rwzbc1001.1.1.1# 路由追踪tc qdisc show dev eth0ip-slinkshow eth0十五、常见误区与学习建议五个高频误区误区一先上工具不先想顺序一上来就装 Prometheus、配 Keepalived结果底层会话还没做一致性。顺序比工具重要。误区二把配置了当成生效了高可用不是配出来的是练出来的。没演练过的高可用只是声明了意图。误区三只盯着单机优化单机调到极致也就多扛一点。架构上一处合理的拆分收益往往超过十处软件调优。误区四忽略了小机器的心理约束在小机器上做设计会自然逼着你做正确的取舍不能靠堆资源解决。这是好事。误区五文档不写三个月后回看自己搭的东西跟看别人的一样陌生。边做边写。学习建议按阶段走不跳步地基不稳上面全是沙上塔每步都留一个可回滚的版本用 Git 管配置改坏了随时回用最小的机器学2 核 2G 能跑通的东西大机器只会更简单每加一个组件问一句它挂了会怎样单点是高可用的唯一敌人。十六、跨篇踩坑总集把这一年多最花时间的坑汇总一下按最容易踩排序#坑一句话解法1会话没做一致性就上多机先无状态化再横向扩2upstream 漏配 keepalivekeepaliveConnection 3SSE 流式没关缓冲proxy_buffering off全套4只按权重估显存必须算 KV Cache5告警没配for一律加持续时间6磁盘告警用固定阈值用predict_linear预测7DNS 只有一台权威至少两台 TSIG8异地允许双写写集中一处异地只读9备份从不做恢复演练定期真恢复一次10演练没有流量护栏split_clients限影响面十七、后续计划这套东西目前跑得还算稳后面想继续探索的方向把故障演练常态化做成定时任务观察长期趋势而不是出事了才临时演练把配置彻底 IaC 化现在还是 Git rsync想试试 Ansible给 GPU 层也做多地域目前 GPU 只在香港异地推理还没铺把这套东西的文档再收一遍写成一份真正能交接的 runbook。附录本篇配套的 18 篇实操笔记索引篇号主题关键词1RTX 4090 云 GPU 搭建 SD 与大模型推理GPU、Stable Diffusion、vLLM2Linux 服务器初始化安全加固SSH、Fail2ban、ufw3Nginx 完整配置实战反代、HTTPS、缓存、限流4MySQL 配置优化与备份my.cnf、慢查询、mysqldump5WordPress 性能优化PHP-FPM、OPcache、Redis6GitHub Actions Docker 自动部署CI/CD、镜像、回滚7自建 S3 对象存储与自动备份MinIO、媒体库分离8服务器监控告警体系Prometheus、Grafana、Alertmanager9GPU 服务器监控与多实例管理DCGM Exporter、nvidia-smi10GPU 显存规划与多模型共存KV Cache、vLLM、Ollama11Web 与 GPU 推理服务拆分部署SSE、反代、API 网关12Web 层负载均衡与横向扩容upstream、会话一致性13Nginx Keepalived 双机热备VRRP、VIP 漂移14多地域 GeoDNS 就近接入BIND、GeoIP、视图15跨地域数据同步与一致性MySQL 主从、Redis 复制16多地域故障演练与混沌工程netem、护栏、runbook17多地域架构总收口与 Runbook架构图、SOP、巡检18本篇学习路径、命令速查如果你也是从小机器开始折腾这条路希望这份路径图能帮你少走点弯路。整个系列的核心其实就一句话先让它跑起来再让它看得见然后让它挂不了最后让它被人管得住。顺序对了每一步都不难。