AI Infra 训练营
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
HiHuo 主站
GitHub
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
HiHuo 主站
GitHub
  • Day 0 · 环境与硬件

    • Day 0:5 节点裸 Ubuntu → K8s 装机基线
  • Week 1:K8s 内核 + 周边基础设施

    • Day 1:3 CP HA 集群 + CNI 选型 + DNS 调优
    • Day 2: 控制面 deep dive + etcd 内核 + chaos drill
    • Day 3: CRD + Operator (kubebuilder 从 0 写)
    • Day 4: Storage 主线 + Cilium 二探
    • Day 5: Volume Expansion + 安全主线
    • Day 6: 调度 + 观测主线 + Day 2 遗留修复
    • Day 7: Harbor + ArgoCD + Cilium Service Mesh
  • Week 2:制品 + GitOps + AI Infra + 综合

    • Day 8 主线 — AI Infra: GPU + k3s + vLLM + Qwen2.5
    • Day 8 主线 — AI Infra 尝试 1 (跨 WAN GPU 加入主集群)
    • Day 8 (alt) — AlertManager 真接入 + PrometheusRule 实战
    • Day 8: CI Infrastructure — Gitea + Jenkins + Kaniko
    • Day 9: Triton + GPU Metrics + 推理性能对比
    • Day 10: MIG + 量化 + HPA Custom Metrics
    • Day 11: AI Agent 业务端到端 — 把 Day 1-10 全部串起来
    • Day 12: 灾难恢复 + 生产事故注入
    • Day 13: LLM Operator + 联邦 + Mesh + RAG
    • Day 14: CKA/CKS 真题演练 + 14 天 Bootcamp 终极总结

Day 0:5 节点裸 Ubuntu → K8s 装机基线

拿到 5 台裸 Ubuntu VPS 之后,在 kubeadm 跑起来之前应该做什么。30-45 分钟内把 SSH / 主机名 / 时间 / 基线工具全部理顺,后面 14 天不会因为基础设施层面的坑被打断。

整篇围绕 4 步基线展开,每步都给「为什么这么做」和「一个真实踩过的坑」。


环境前置

5 台节点跑 Ubuntu 22.04 LTS,单机 8C / 8G RAM / KVM,内网在同一个 /24:

节点内网 IP别名角色
k8s-cp-110.0.24.28m1控制面(init master)
k8s-cp-210.0.24.29m2控制面
k8s-cp-310.0.24.30m3控制面
k8s-w-110.0.24.31m4worker
k8s-w-210.0.24.32m5worker

为什么 3 CP + 2 W:3 CP 是 etcd raft 的最小 quorum(能容忍 1 个 down);只起 1 CP 学不到 leader election 和选主时序,面试常被追问「你做过 HA 控制面吗」。


第 1 步:探机器底子

装 K8s 之前 5 分钟,需要确认 3 件事:虚拟化类型、cgroup 版本、默认服务是否有干扰。

# 1. 虚拟化类型 —— 必须是 kvm / vmware / hyperv 等真虚拟化
systemd-detect-virt              # 期望:kvm
systemd-detect-virt --container  # 期望:none(不是 LXC 套娃)

# 2. cgroup 版本 —— K8s 1.25+ 在 v2 下更稳
stat -f -c %T /sys/fs/cgroup     # 期望:cgroup2fs

# 3. IDC 默认监控 / 后台服务
systemctl list-unit-files --state=enabled | grep -iE 'agent|aliyun|aegis|nezha|monitor'
ls /opt /usr/local               # 大部分情况应该是空
ss -lntp                         # 监听端口只应有 22 + 53(systemd-resolved)

# 4. 磁盘真实拓扑
lsblk -nbo NAME,SIZE,TYPE,MOUNTPOINT

LXC 容器里跑 K8s 极难 —— cgroup namespace 是共享的,modprobe 不可用,kubelet 装不上。一些小 IDC 把 LXC 当 VPS 卖,外层 systemd-detect-virt 看不出,内层 --container 才会暴露。检测到 LXC 直接换机器,不要硬怼。

lsblk 是装机第一个该跑的命令,不要只看 df -h /。df 只显示已挂载的文件系统;lsblk 显示所有块设备,可能有大盘没挂载、或者挂在莫名其妙的 /www 路径上等着 Day 1 处理。


第 2 步:SSH 免密 + 硬化

新机器还是密码登录,先用 sshpass 把公钥推过去,验证 key auth 工作后再关密码。顺序不能反,否则把自己锁在外面。

2.1 推公钥(幂等)

PUBKEY=$(cat ~/.ssh/id_rsa.pub)
for ip in 10.0.24.28 10.0.24.29 10.0.24.30 10.0.24.31 10.0.24.32; do
  sshpass -p "$IP_PWD" ssh -o StrictHostKeyChecking=accept-new root@$ip "
    mkdir -p /root/.ssh && chmod 700 /root/.ssh
    grep -qxF '$PUBKEY' /root/.ssh/authorized_keys 2>/dev/null \
      || echo '$PUBKEY' >> /root/.ssh/authorized_keys
    chmod 600 /root/.ssh/authorized_keys
  "
done

两个细节:

  • grep -qxF + || 实现幂等:已存在不重复 append。x 整行匹配,F 不当 regex 解析(公钥里有 +// 等特殊字符),q 静默。
  • StrictHostKeyChecking=accept-new:首次自动信任并写入 known_hosts,比 =no 安全 —— 这是 TOFU(Trust On First Use)模型,只在第一次接受。

2.2 真坑:IDC 默认关了 PubkeyAuthentication

公钥已经在 authorized_keys 里,ssh root@ip 'echo OK' 仍然 Permission denied。

排查命令:

sshpass -p "$IP_PWD" ssh root@$ip 'sshd -T | grep -i pubkey'
# pubkeyauthentication no   <- 根因

这家 IDC 的 Ubuntu 模板默认把公钥认证关了(可能是为了方便 console 重置密码)。修复:

ssh root@$ip "
  sed -i 's/^#*PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
  # Ubuntu 22 还有 override 目录,要一并改
  for f in /etc/ssh/sshd_config.d/*.conf; do
    [ -f \"\$f\" ] && grep -q PubkeyAuthentication \"\$f\" && \
      sed -i 's/^PubkeyAuthentication.*/PubkeyAuthentication yes/' \"\$f\"
  done
  sshd -t && systemctl reload sshd
"

两点教训:

  1. 改 sshd 必看 /etc/ssh/sshd_config.d/*.conf,这里的设置 override 主配置。「改了主文件不生效」十有八九是这个原因。
  2. 任何时候 reload sshd 必须先 sshd -t 验证语法,否则配错了把自己锁外面。

2.3 关密码登录

key auth 验证可用后(ssh -o BatchMode=yes root@ip 'echo OK' 成功),再关密码登录:

ssh root@$ip "
  sed -i \
    -e 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' \
    -e 's/^#*PermitRootLogin.*/PermitRootLogin prohibit-password/' \
    -e 's/^#*KbdInteractiveAuthentication.*/KbdInteractiveAuthentication no/' \
    /etc/ssh/sshd_config
  for f in /etc/ssh/sshd_config.d/*.conf; do
    [ -f \"\$f\" ] && sed -i 's/^PasswordAuthentication.*/PasswordAuthentication no/' \"\$f\"
  done
  sshd -t && systemctl reload sshd
"

2.4 本地 SSH alias

cat >> ~/.ssh/config <<'EOF'
# === k8s-bootcamp ===
Host k8s-cp-1 m1
  HostName 10.0.24.28
  User root
  IdentityFile ~/.ssh/id_rsa
# ... 其余 4 段同理
EOF

# 用 ssh -G 验证 alias 解析(不实际连接)
ssh -G k8s-cp-1 | grep -E "^(hostname|user|identityfile)"

ssh -G <alias> 是 ssh config 调试的关键命令 —— 它 dump 解析后的最终配置但不实际连接,可以验证 alias 写对没。直接 ssh <alias> 测试万一连失败,原因可能是网络也可能是 config,分不清。


第 3 步:系统升级 + 装基线工具

ssh root@$ip 'bash -s' <<'EOF'
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq

apt-get install -y --no-install-recommends \
  curl wget ca-certificates apt-transport-https gnupg lsb-release \
  vim jq tmux htop iotop \
  tcpdump dnsutils iperf3 mtr-tiny net-tools \
  chrony \
  git unzip zip rsync less \
  bash-completion \
  ipset conntrack \
  python3-pip

# 升级时遇到 conf 冲突保留旧的(防 sshd_config 被覆盖)
apt-get upgrade -y -o Dpkg::Options::="--force-confold"
EOF

关键包速记:

包用途
ipset conntrackkube-proxy 必需
dnsutils(含 dig / nslookup)CoreDNS 调试
tcpdump网络故障抓包
iperf3跨节点带宽测试、CNI 数据面对比
chronyNTP 同步 —— etcd raft 对时间漂移敏感
jq解析 JSON 输出,几乎每天用

远程脚本 No.1 陷阱:ssh ... 'bash -s' <<'EOF' 必须用单引号包 EOF。双引号或不加引号会让本地 shell 提前 eval 掉 $(...) 和 $var,远端实际跑到的命令变成空。SRE / DevOps 写远程脚本踩这个坑的概率是 100%,记牢。


第 4 步:hostname / hosts / 关无用服务 / 时间同步

HOSTNAME=k8s-cp-1  # 各机器自己的名字

ssh root@$ip "
  # 1. hostname
  hostnamectl set-hostname $HOSTNAME

  # 2. /etc/hosts(全集群一致)
  cat > /etc/hosts <<'HOSTS'
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback

10.0.24.28 k8s-cp-1 m1
10.0.24.29 k8s-cp-2 m2
10.0.24.30 k8s-cp-3 m3
10.0.24.31 k8s-w-1  m4
10.0.24.32 k8s-w-2  m5
HOSTS
  # Ubuntu sudo 要求 hostname 能解析到 127.0.1.1
  echo '127.0.1.1 $HOSTNAME' >> /etc/hosts

  # 3. 时区
  timedatectl set-timezone Asia/Shanghai

  # 4. 关无用服务 + 启 chrony
  for svc in ModemManager pollinate lxd-agent multipathd iscsid \
             unattended-upgrades apport \
             apt-daily.timer apt-daily-upgrade.timer; do
    systemctl disable --now \$svc 2>/dev/null || true
  done
  rm -f /etc/cron.daily/apport

  systemctl enable --now chrony
  chronyc -a makestep
"

必须禁的服务

服务为什么必须禁
unattended-upgrades最重要。自动升级可能在任意时刻装新内核 / kubelet,K8s 不允许 minor 跨版本(1.28 → 1.30 必须经过 1.29),一旦自动升错版本整个集群可能在你睡觉的时候炸。这是 K8s 第一禁忌,所有生产集群必关。
multipathd / iscsidSAN 多路径 / iSCSI 存储用,本地盘场景无用。留着会偶发干扰 device-mapper 设备命名,影响 PV 挂载。
apportUbuntu crash reporter,每次进程 crash 写 /var/crash/,占盘但没人会把这些 crash 发回 Canonical。
lxd-agent pollinate ModemManager桌面 / 容器模板留下来的服务,服务器场景毫无意义。

lxd-agent.service 在 Ubuntu 模板里默认 enabled —— 别看到 enabled 就以为这是 LXC 容器,要靠 systemd-detect-virt 判定虚拟化类型,systemctl enabled ≠ running。

为什么必须装 chrony

etcd 用 raft 协议,leader 给 follower 发心跳带 timestamp。节点间时差 > electionTimeout(默认 1 秒),follower 会误判 leader 失联触发重新选主。生产环境见过节点时间漂移 5 秒导致 etcd 反复选主、apiserver 几小时不稳定的场景 —— 装机第一天就装好 chrony 不要省。

chronyc -a makestep 是首次硬跳同步(直接把时间调对),而不是慢慢 slew(每秒只调几 ppm)。装机时用 makestep,运行中让 chrony 自己 slew。

为什么不用 systemd-timesyncd:它只支持 SNTP 模式(无 drift 校准)。chrony / ntpd 是 NTP 全协议,实现 PLL/FLL drift 校准,在虚拟化环境(VM 时间易漂移)更稳。


一键验证

5 步做完后跑下面这条命令逐项核对。11 个状态全跑在一次 SSH 连接里,5 台同时验:

for h in m1 m2 m3 m4 m5; do
  ssh $h "
    echo hostname:  \$(hostname)
    echo timezone:  \$(timedatectl show --property=Timezone --value)
    echo chrony:    \$(chronyc tracking 2>/dev/null | awk '/Reference/{print \$NF,\$(NF-1)}')
    echo sshd-pw:   \$(sshd -T | awk '/^passwordauthentication /{print \$2}')
    echo sshd-pk:   \$(sshd -T | awk '/^pubkeyauthentication /{print \$2}')
    echo unatt:     \$(systemctl is-enabled unattended-upgrades 2>&1)
    echo jq:        \$(which jq)
    echo conntrack: \$(which conntrack)
    echo disk-root: \$(df -h / | awk 'NR==2{print \$4}') free
    echo peer:      \$(getent hosts k8s-cp-2 | head -c 30)
  "
done

正确状态:

  • sshd-pw: no —— 密码登录关了
  • sshd-pk: yes —— 公钥登录开了
  • unatt: disabled —— 自动升级禁了
  • chrony 行有合理的 Reference 服务器(不是 0.0.0.0)
  • peer: 行能解析出 10.0.24.29 k8s-cp-2 m2(/etc/hosts 写对了)

验证脚本要一次连接 + 多输出,不要 for 项目; do ssh; done 起 11 个 ssh,又慢又对 sshd 不友好。

getent hosts <name> 比 dig <name> 更适合测 /etc/hosts —— getent 走 nsswitch(先 files 后 dns),反映真实解析顺序;dig 只走 DNS。


典型坑速查

现象根因修复
key 推过去了但 ssh 还是 Permission deniedsshd_config 默认 PubkeyAuthentication no(IDC 模板乱改)改 sshd_config 和 sshd_config.d/*.conf 两个地方,再 reload
改 sshd 主配置不生效/etc/ssh/sshd_config.d/*.conf 里有 override两个目录都要看,sshd -t 验证再 reload
远程 heredoc 用双引号,$(hostname) 被本地 shell 展开双引号内的 $() 会被外层 bash 先 eval用单引号 <<'EOF'
改 hostname 后 sudo 报 unable to resolve/etc/hosts 没有 127.0.1.1 <hostname>补上 127.0.1.1 $(hostname)
chronyc tracking 报 506 Cannot talk to daemonchrony 没启动systemctl enable --now chrony
sshpass 并发太多,部分机器返回 Permission deniedsshd 速率限制 MaxStartups 10:30:100错开几秒 / 减少并发 / 改 MaxStartups

面试常见题

Q1:你接手一台新机器准备装 K8s,前 30 分钟做什么?

四步:

  1. 探虚拟化和内核 —— systemd-detect-virt 必须是 kvm / vmware 等真虚化(LXC 难装),uname -r 看内核 ≥ 4.18(Cilium / br_netfilter 要求)
  2. SSH 免密 + 禁密码 + 禁 unattended-upgrades —— 防自动升级炸集群
  3. 装 chrony 同步时间 —— etcd raft 对时差敏感
  4. 改 hostname + /etc/hosts —— K8s join 用 hostname 解析

深问点:为什么必须禁 unattended-upgrades? —— 避免自动升级 kubelet / containerd 跨 minor 版本,K8s 不支持。

Q2:SSH 公钥配了但登不上,从哪里查?

三层查:

  1. 客户端:ssh -v root@ip 看协商哪个 key、IdentityFile 路径对不对
  2. 服务端:sshd -T | grep -i pubkey 看 PubkeyAuthentication 是否 yes
  3. 文件权限:authorized_keys 必须 600,.ssh 必须 700,owner 必须 root

实战踩的坑:小 IDC 模板默认 PubkeyAuthentication no,本地 key 拷过去后登不上,排查 30 分钟才发现是服务端默认就关了。

Q3:5 台机器初始化,你怎么并发 + 防 SSH 限流?

  • 少量(≤ 10 台):ssh ... & + wait 并发即可
  • 多机:Ansible(幂等 + 模块化)+ Mitogen 插件(连接复用加速 5-10×)
  • sshd 默认 MaxStartups 10:30:100,大批量要先改这个或错开几秒
  • 远程脚本永远用 ssh root@ip 'bash -s' <<'EOF' 单引号保护

Q4:为什么生产 K8s 节点必须装 chrony?

etcd 用 raft 协议,leader 给 follower 发心跳带 timestamp,节点之间时差 > electionTimeout(默认 1 秒)会误判 leader 失联触发重新选主。线上见过节点时间漂移 5 秒导致 etcd 反复选主、apiserver 几小时不稳定。

深问:用 systemd-timesyncd 行不行? —— 能用但只能 SNTP 模式(无 drift 校准)。chrony / ntpd 是 NTP 全协议,实现 PLL/FLL drift 校准,在 VM 时间易漂移的环境下更稳。

Q5:装 K8s 之前你不会做哪些事?

  • 不装 docker —— K8s 1.24+ 已去 dockershim,装 docker 还要走 cri-dockerd 适配层,白绕。直接装 containerd
  • 不启 swap —— K8s 准入条件,kubeadm 1.22+ 默认 fail
  • 不开 SELinux enforcing(RHEL 系)—— 要 permissive 或 disabled
  • 不在 K8s 节点装监控 agent —— 会跟 cAdvisor 端口冲突 / 重复采集

延伸

  • vdb 那 100G 怎么用 —— 这台机器 IDC 默认把 100G 数据盘挂在 /www,Day 1 第一件事就是 remount 到 /var/lib/containerd(containerd 的 image 缓存是 K8s 节点最吃盘的部分)。
  • SSH ProxyJump —— 如果以后要给同事访问,加一台 bastion + ProxyJump 配置,5 台机器只开 bastion 的 22 公网,其他全走内网。比 VPN 轻量,生产常用。
  • cloud-init 模板化 —— 部分 IDC 支持开机注入 cloud-init,把 Day 0 整套配置做进装机模板,新机 5 分钟内即用。
  • 替代方案 —— 如果以后要做 immutable infra,看看 Talos OS(K8s 原生 OS,无 systemd,只跑 K8s)。这次走的是「传统 Ubuntu + K8s」的常规姿势,Talos 是另一条线。

下一步

Day 0 结束后这 5 台机器具备:任意机器之间 0 密码 SSH、hostname / /etc/hosts 一致、时间同步、自动升级关闭、基线工具齐全。

Day 1 继续:kubeadm 初始化 3 CP HA + 2 worker,装 Calico / Cilium CNI 并对比,CoreDNS 调优 + node-local-dns。

在 GitHub 上编辑此页