阶段二 · 进阶与实践

实践 Lab

本篇定位

本篇是 Kubernetes 分类的动手实践总纲.每个 Lab 对应前面 1-2 篇理论知识,按复杂度递进.
建议搭建好 k3d 本地集群后,从 Lab 1 开始逐个完成.遇到卡点先翻对应理论篇,实在解决不了再看第 10 篇参考答案.

Lab 0 - 环境搭建

安装工具

# 1. 安装 kubectl
$ brew install kubectl # macOS
# 或 https://kubernetes.io/docs/tasks/tools/

# 2. 安装 k3d(轻量级 K3s in Docker)
$ brew install k3d # macOS
# 或 curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash

# 3. 安装 k9s(可选但强烈推荐的终端 UI)
$ brew install k9s

# 4. 验证
$ kubectl version --client
$ k3d version

创建本地集群

# 创建一个 3 节点集群(1 server + 2 agent)
$ k3d cluster create lab \
--servers 1 \
--agents 2 \
--port "80:80@loadbalancer" \
--port "443:443@loadbalancer"

# 验证集群
$ kubectl get nodes
NAME STATUS ROLES AGE
k3d-lab-server-0 Ready control-plane,master 30s
k3d-lab-agent-0 Ready <none> 25s
k3d-lab-agent-1 Ready <none> 25s

# k3d 自带 Traefik Ingress Controller 和 local-path StorageClass
$ kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY
local-path (default) rancher.io/local-path Delete

为什么选 k3d

  • k3d 在 Docker 容器中运行 K3s(轻量 K8s 发行版),启动速度快(10 秒内),资源占用低(每节点约 256MB)
  • 支持多节点,且自带 Ingress Controller 和 StorageClass,非常适合本地实验
  • 如果你更熟悉 kind 或 minikube 也可以使用,核心操作一致

创建实验 namespace

# 为实验创建独立 namespace,方便清理
$ kubectl create namespace lab

# 设置默认 namespace(后续命令不用每次加 -n lab)
$ kubectl config set-context --current --namespace=lab

Lab 1 - Pod 基础(对应第 02 篇)

学习目标

  • 创建和删除 Pod
  • 理解 Pod 的生命周期和状态
  • 配置探针(liveness / readiness)
  • 使用 Init Container

实验任务

Lab 1 任务清单

  1. 基础 Pod:创建一个 nginx Pod,验证它进入 Running 状态.用 kubectl exec 进入 Pod 执行 curl localhost.
  2. 多容器 Pod:创建一个 Pod 包含两个容器(nginx + busybox sidecar).busybox 每秒向共享 emptyDir 写入时间戳,nginx 通过挂载同一目录提供文件下载.
  3. 探针配置:给 nginx Pod 配置 livenessProbe(HTTP GET /)和 readinessProbe(HTTP GET /ready).故意把 readinessProbe 的路径改为不存在的 /healthz,观察 Pod 状态变化(READY 0/1 但仍 Running).
  4. Init Container:创建一个 Pod,Init Container 用 wget 下载一个文件到共享 Volume,主容器启动后读取这个文件.验证 Init Container 先于主容器执行.
  5. 资源限制:创建一个 Pod 设置 resources.requests.memory: 64Mi, limits.memory: 128Mi.在 Pod 内运行一个消耗内存的命令(如 stress),观察 OOMKilled.

验证标准

完成 Lab 1 后,你应该能:

  1. 不看文档写出一个基本 Pod YAML
  2. 解释 Pod 处于 CrashLoopBackOff / ImagePullBackOff / Pending 各自意味着什么
  3. 理解探针失败时 K8s 的行为差异(liveness 失败 → 重启,readiness 失败 → 移出 Endpoints)

Lab 2 - 工作负载控制器(对应第 03 篇)

学习目标

  • 使用 Deployment 管理无状态应用
  • 观察滚动更新过程
  • 执行回滚操作
  • 体验 StatefulSet 的有序性

实验任务

Lab 2 任务清单

  1. Deployment 基础:创建一个 Deployment(nginx:1.24, replicas=3).验证 3 个 Pod 都 Running.手动删除一个 Pod,观察是否自动重建.
  2. 滚动更新:将镜像从 nginx:1.24 更新到 nginx:1.25.同时在另一个终端 kubectl get pods -w 观察新旧 Pod 的创建/删除顺序.
  3. 调整更新策略:设置 maxSurge=0, maxUnavailable=1,再次更新到 nginx:1.26,对比滚动行为的差异(先删后建 vs 先建后删).
  4. 回滚:查看 rollout history,回滚到 revision 1(nginx:1.24).验证 Pod 镜像版本.
  5. StatefulSet 有序性:创建一个 StatefulSet(busybox, replicas=3),观察 Pod 创建顺序是否严格按 0→1→2.缩容到 1,观察删除顺序是否按 2→1.
  6. DaemonSet:创建一个 DaemonSet(busybox + sleep),验证每个 worker 节点恰好有一个 Pod.给一个节点加 Taint,观察是否有 Pod 被驱逐或无法调度.

Lab 3 - Service 与服务发现(对应第 04 篇)

学习目标

  • 创建 ClusterIP / NodePort Service
  • 验证 DNS 服务发现
  • 理解 Endpoints 与 readiness 的关系
  • 体验 Headless Service

实验任务

Lab 3 任务清单

  1. ClusterIP:为 Lab 2 的 Deployment 创建一个 ClusterIP Service(port:80, targetPort:80).启动一个临时 Pod(kubectl run tmp --image=curlimages/curl -it --rm -- sh),用 Service 名和 ClusterIP 分别 curl,验证负载均衡(多次 curl 看 hostname).
  2. DNS 发现:在临时 Pod 中用 nslookup 验证 DNS 解析.尝试完整域名 ..svc.cluster.local.
  3. NodePort:将 Service 改为 NodePort,从宿主机 curl localhost: 验证外部访问.
  4. Endpoints 联动:给 Deployment 加一个必定失败的 readinessProbe.观察 Endpoints 列表变为空.恢复探针后验证 Endpoints 重新填充.
  5. Headless Service:创建一个 Headless Service(clusterIP: None)+ StatefulSet.用 nslookup 验证返回的是多个 Pod IP 而非 ClusterIP.再用 nslookup . 验证逐 Pod DNS.

Lab 4 - Ingress 七层路由(对应第 05 篇)

学习目标

  • 创建 Ingress 实现域名路由和路径路由
  • 配置 TLS(自签名证书)
  • 使用 annotation 做路径重写

实验任务

Lab 4 任务清单

  1. 域名路由:创建两个 Deployment+Service(app-a 和 app-b,各自返回不同内容).创建 Ingress 将 a.lab.local → app-a,b.lab.local → app-b.修改 /etc/hosts 后用 curl 验证.
  2. 路径路由:创建一个 Ingress 将 lab.local/api → api-svc,lab.local/ → frontend-svc.验证不同路径到达不同后端.
  3. TLS:用 openssl 生成自签名证书,创建 tls Secret,在 Ingress 中启用 HTTPS.用 curl -k https://lab.local 验证.
  4. 路径重写:配置 rewrite-target 注解,使外部 /app/hello 到后端变为 /hello.查看后端 Nginx access log 确认路径变化.

k3d 自带 Traefik,不是 Nginx

k3d 默认安装 Traefik 作为 Ingress Controller,注解前缀和 ingress-nginx 不同.如果你想练习 Nginx Ingress 的注解(如 rewrite-target),有两种方式:

  1. 创建集群时禁用 Traefik:k3d cluster create --k3s-arg "--disable=traefik@server:0",然后手动安装 ingress-nginx
  2. 直接用 Traefik 的原生语法完成实验

Lab 5 - 配置管理(对应第 06 篇)

学习目标

  • 创建和使用 ConfigMap / Secret
  • 验证热更新行为差异
  • 理解 subPath 的限制

实验任务

Lab 5 任务清单

  1. ConfigMap 环境变量:创建 ConfigMap(APP_ENV=dev, LOG_LEVEL=debug),用 envFrom 注入 Pod.exec 进 Pod 验证环境变量.
  2. ConfigMap 文件挂载:创建包含 nginx.conf 的 ConfigMap,挂载到 nginx Pod 的 /etc/nginx/conf.d/.验证 Nginx 使用了自定义配置.
  3. 热更新验证:修改 ConfigMap 的 LOG_LEVEL 值.等 2 分钟后:1. 检查 envFrom 注入的 Pod 环境变量是否变化(预期不变);2. 检查 Volume 挂载的文件是否更新(预期更新).
  4. subPath 限制:用 subPath 挂载 ConfigMap 中的单个文件.修改 ConfigMap 后验证文件不会自动更新.
  5. Secret:创建 Secret(username/password),以 Volume 方式挂载到 Pod 的 /etc/secrets/.验证文件权限和内容(应为明文,不是 Base64).
  6. 触发滚动更新:用 kubectl rollout restart deployment/xxx 使环境变量注入的配置变更生效.

Lab 6 - 持久化存储(对应第 07 篇)

学习目标

  • 对比 emptyDir 和 PVC 的数据持久性
  • 使用 StorageClass 动态供给
  • 验证 StatefulSet + volumeClaimTemplates

实验任务

Lab 6 任务清单

  1. emptyDir 生命周期:创建 Pod + emptyDir 挂载到 /data,写入文件.删除 Pod 后重建同名 Pod,验证数据丢失.
  2. PVC 持久化:创建 PVC(storageClassName: local-path, 1Gi)+ Pod 挂载到 /data,写入文件.删除 Pod,重新创建引用同一 PVC 的 Pod,验证数据保留.
  3. 动态供给观察:创建 PVC 后用 kubectl get pv 观察 PV 是否自动创建.kubectl describe pvc 查看绑定状态和事件.
  4. StatefulSet 独立存储:创建 StatefulSet(replicas=3)+ volumeClaimTemplates.验证生成 3 个独立 PVC.在每个 Pod 中写入不同内容,重建 Pod 后验证各自数据独立保留.
  5. 缩容不删 PVC:将 StatefulSet 缩容到 1,验证 PVC 仍存在.扩回 3,验证数据自动恢复.
  6. PVC 扩容:修改 PVC 的 requests.storage(如 1Gi → 2Gi),观察 PV 容量是否跟着变化.(注意:local-path 可能不支持在线扩容,若失败则记录原因.)

Lab 7 - 综合实战:部署完整微服务(对应第 01-08 篇)

学习目标

  • 将前面所有知识串联在一个真实场景中
  • 部署一个包含前端 + API + 数据库 + 缓存的完整应用
  • 配置完整的流量链路(Ingress → Service → Pod)

实验任务

Lab 7 任务清单

  1. 部署 Redis(缓存):StatefulSet + Headless Service + PVC,单副本即可.
  2. 部署 PostgreSQL(数据库):StatefulSet + Headless Service + PVC + Secret(存储密码).
  3. 部署 API 服务:Deployment(2 副本)+ ClusterIP Service.通过 ConfigMap 注入 Redis 和 PostgreSQL 的连接信息,Secret 注入数据库密码.
  4. 部署前端:Deployment(2 副本)+ ClusterIP Service.
  5. 配置 Ingress:app.lab.local/api → api-svc,app.lab.local/ → frontend-svc.
  6. 配置 HPA:为 API 服务配置 HPA(CPU 目标 60%,min=2,max=10).
  7. 验证完整链路:从浏览器访问 http://app.lab.local,验证前端 → API → 数据库/缓存的完整调用链.
  8. 模拟故障:删除 PostgreSQL Pod,观察 StatefulSet 自动重建 + PVC 数据恢复.删除一个 API Pod,观察 Deployment 自动补齐 + Service 端点自动更新.

镜像建议

如果不想自己写应用代码,可以使用现成的镜像:

  • Redis 直接用 redis:7-alpine
  • PostgreSQL 用 postgres:16-alpine
  • API 可以用 hashicorp/http-echonginx 模拟(返回固定内容即可验证链路)
  • 前端同理用 nginx 提供静态页面

重点是练习 K8s 资源编排,不是写业务代码.

环境清理

# 清理实验 namespace(删除所有实验资源)
$ kubectl delete namespace lab

# 或者直接删除整个集群
$ k3d cluster delete lab

# 重新创建干净集群
$ k3d cluster create lab --servers 1 --agents 2 \
--port "80:80@loadbalancer" --port "443:443@loadbalancer"

小结

快速回顾

  • Lab 0:k3d 搭建 3 节点本地集群,安装 kubectl / k9s
  • Lab 1:Pod 创建,多容器,探针,Init Container,资源限制
  • Lab 2:Deployment 滚动更新/回滚,StatefulSet 有序性,DaemonSet
  • Lab 3:ClusterIP/NodePort Service,DNS 发现,Endpoints 联动,Headless
  • Lab 4:Ingress 域名/路径路由,TLS,路径重写
  • Lab 5:ConfigMap/Secret 注入方式,热更新验证,subPath 限制
  • Lab 6:emptyDir vs PVC 生命周期,动态供给,StatefulSet 独立存储,扩容
  • Lab 7:完整微服务部署(前端 + API + DB + 缓存 + Ingress + HPA)

参考答案

每个 Lab 的完整 YAML 和操作步骤详见第 10 篇-Lab 参考答案.建议先独立尝试,遇到阻塞再查阅.