---
url: /courses/cloud-platform-build-management/lab-15/index.md
---
# 实训 15：K3s 工作负载、Service 与扩缩容

## 1. 任务情境

课程团队准备把一个静态站点交给 Kubernetes 管理。你需要在教师指定的隔离集群中完成单副本部署，证明 Service 可以访问页面，再把副本扩到 3。随后注入一次“Service 选不中 Pod”的可恢复故障，按证据定位并恢复，最后只清理本课命名空间。

本实训不要求在个人低配 WSL 或云主机 B 上安装 K3s。教师演示环境为本地 k3d/K3s；学生只操作教师授权的上下文和 `cloud-course-15` 命名空间。

## 2. 环境与安全边界

| 项目 | 课堂要求 |
|---|---|
| 控制端 | Windows 11 的 WSL，或教师准备的本地终端 |
| 集群 | 教师指定的隔离 K3s/k3d 集群 |
| 命名空间 | `cloud-course-15` |
| 工作负载 | `lesson15-web` |
|镜像 | `nginx:1.27-alpine` |
| 允许变更 | 本课清单、Deployment 副本数、本课 Service 选择器 |
| 禁止操作 | 其他命名空间、系统组件、未知集群、全局删除、kubeconfig 上传 |

开始前完成四项确认：

1. 教师已经明确本课 kubecontext；
2. 当前上下文不是生产集群或其他课程集群；
3. 只操作 `cloud-course-15`；
4. 报告不会包含 kubeconfig、令牌、证书、完整地址和其他命名空间数据。

## 3. 起始文件

将 `starter/` 复制到个人实验目录：

```bash
[WSL] mkdir -p "$HOME/cloud-course/lab-15"
```

创建本课个人目录。

```bash
[WSL] cp -R starter/. "$HOME/cloud-course/lab-15/"
```

复制清单和验收脚本。不要移动教师原始文件。

```bash
[WSL] cd "$HOME/cloud-course/lab-15"
```

进入本课目录。后续相对路径都以这里为起点。

目录应包含：

```txt
lab-15/
├── manifests/
│   ├── namespace.yaml
│   ├── configmap.yaml
│   ├── deployment.yaml
│   ├── service.yaml
│   └── kustomization.yaml
├── verify.sh
└── cleanup.sh
```

## 4. 步骤一：上下文与节点预检

```bash
[WSL] kubectl config current-context
```

记录当前上下文。若与教师公布名称不同，立即停止，不执行 `apply`。

```bash
[WSL] kubectl cluster-info
```

检查 API Server 是否可访问。报告遮盖完整地址，只保留“可访问/不可访问”和集群类型。

```bash
[WSL] kubectl get nodes -o wide
```

预期至少一个节点为 `Ready`。记录版本、角色和容器运行时；不要把 Ready 写成应用已经可用。

## 5. 步骤二：阅读并预检清单

```bash
[WSL] kubectl kustomize manifests > lesson15-rendered.yaml
```

把所有资源展开到临时文件，便于整体检查。该文件不包含凭据，但提交前仍只截取本课对象。

```bash
[WSL] grep -E '^(kind:|  name:|  namespace:|  replicas:|  image:|  type:)' \
  lesson15-rendered.yaml
```

查看对象类型、名称、命名空间、副本、镜像和 Service 类型。预期副本为 1、Service 为 ClusterIP。

```bash
[WSL] kubectl apply --dry-run=client -k manifests
```

执行客户端预检。全部对象显示 `created (dry run)` 后才能继续。

检查点 1：

* 命名空间是否只为 `cloud-course-15`；
* Deployment 与 Service 的标签是否一致；
* 是否设置 readinessProbe 和 livenessProbe；
* 是否设置 CPU、内存 requests/limits；
* 是否没有 NodePort、LoadBalancer 和公网地址。

## 6. 步骤三：部署单副本

```bash
[WSL] kubectl apply -k manifests
```

提交四类对象。记录 created/configured 结果。

```bash
[WSL] kubectl rollout status deployment/lesson15-web \
  -n cloud-course-15 --timeout=120s
```

等待 Deployment 完成。超时后不要反复 apply，转到第 9 节排查。

```bash
[WSL] kubectl get pod,service -n cloud-course-15 -o wide
```

预期一个 Pod 为 `1/1 Running`，Service 类型为 ClusterIP。

```bash
[WSL] kubectl get deployment lesson15-web -n cloud-course-15
```

预期 `READY` 为 `1/1`、`AVAILABLE` 为 1。

截图 1 应包含节点、Pod、Service 和明确的本地/授权环境说明。

## 7. 步骤四：验证 Service 路径

```bash
[WSL] kubectl get endpointslice -n cloud-course-15 \
  -l kubernetes.io/service-name=lesson15-web -o wide
```

预期 EndpointSlice 中有一个端点。若为 `<none>`，先比 Service selector 与 Pod labels。

```bash
[WSL] kubectl exec -n cloud-course-15 deploy/lesson15-web -- \
  wget -q -O - http://lesson15-web
```

从 Pod 内访问 Service DNS。预期 HTML 中同时出现：

```txt
K3s service ready
deployment=lesson15-web
```

截图 2 应包含请求命令、页面关键内容和 EndpointSlice。报告注明这只证明集群内部路径。

## 8. 步骤五：从 1 扩容到 3

```bash
[WSL] kubectl scale deployment/lesson15-web \
  -n cloud-course-15 --replicas=3
```

把期望副本改为 3。`scaled` 不是最终验收。

```bash
[WSL] kubectl rollout status deployment/lesson15-web \
  -n cloud-course-15 --timeout=120s
```

等待三个副本达到可用。

```bash
[WSL] kubectl get deployment,pods -n cloud-course-15 -o wide
```

记录 Deployment 3/3、三个 Pod 的就绪状态和不同 Pod IP。

```bash
[WSL] kubectl get endpointslice -n cloud-course-15 \
  -l kubernetes.io/service-name=lesson15-web -o wide
```

预期端点由一个增加到三个。它说明 Service 选择了三个就绪 Pod，不代表流量一定平均。

```bash
[WSL] bash verify.sh
```

运行自动验收。全部为 PASS 后保留结果。

截图 3 应包含 Deployment 3/3、三个就绪 Pod 和三个端点。

## 9. 步骤六：注入并恢复 Service 选择器故障

先保存原值：

```bash
[WSL] kubectl get service lesson15-web -n cloud-course-15 -o yaml \
  > lesson15-service.before.yaml
```

确认备份文件非空。

```bash
[WSL] test -s lesson15-service.before.yaml && echo "[PASS] service baseline saved"
```

预期输出 PASS。

注入故障：

```bash
[WSL] kubectl patch service lesson15-web -n cloud-course-15 \
  --type=merge -p '{"spec":{"selector":{"app.kubernetes.io/name":"wrong-label"}}}'
```

只改变本课 Service 的选择器。

```bash
[WSL] kubectl get service,pods,endpointslice \
  -n cloud-course-15 --show-labels
```

观察 Service 仍存在、Pod 仍 Running，但 selector 与 labels 不同，端点为空。

```bash
[WSL] kubectl exec -n cloud-course-15 deploy/lesson15-web -- \
  wget -T 3 -q -O - http://lesson15-web
```

请求预期超时或失败。保留退出状态和原始现象，不要反复重启 Pod。

恢复：

```bash
[WSL] kubectl apply -f manifests/service.yaml
```

用课程原始清单恢复 Service。`lesson15-service.before.yaml` 含服务端生成字段，仅用于保留原值和现象；资源变化后直接 apply 它可能发生 `resourceVersion` 冲突。

```bash
[WSL] kubectl get endpointslice -n cloud-course-15 \
  -l kubernetes.io/service-name=lesson15-web -o wide
```

等待三个端点重新出现。

```bash
[WSL] bash verify.sh
```

沿原来的上下文、对象、端点和 HTTP 路径完成回归。

## 10. 常见故障排查

### 10.1 Pod 长时间 Pending

```bash
[WSL] kubectl describe pod -n cloud-course-15 POD_NAME
```

把 `POD_NAME` 换成实际名称。查看 Events 是否提示资源不足、节点不可调度或约束不满足。

### 10.2 ImagePullBackOff

```bash
[WSL] kubectl get pods -n cloud-course-15
```

先确认具体 Pod。

```bash
[WSL] kubectl describe pod -n cloud-course-15 POD_NAME
```

查看镜像名和拉取错误。不要在聊天、截图或命令历史中粘贴镜像仓库密码。

### 10.3 Running 但 0/1

```bash
[WSL] kubectl describe pod -n cloud-course-15 POD_NAME
```

检查 Readiness probe 失败的路径、端口和状态码。

```bash
[WSL] kubectl logs -n cloud-course-15 POD_NAME --tail=30
```

查看应用最近日志。日志无错误也不能替代探针和请求验证。

### 10.4 Service 没有端点

```bash
[WSL] kubectl get service lesson15-web -n cloud-course-15 \
  -o jsonpath='{.spec.selector}{"\n"}'
```

打印 Service 选择器。

```bash
[WSL] kubectl get pods -n cloud-course-15 --show-labels
```

显示 Pod 标签。逐字段比较，不要重启整个集群。

## 11. 恢复与清理

如果故障练习尚未恢复，先执行：

```bash
[WSL] kubectl apply -f manifests/service.yaml
```

用版本控制中的原始清单恢复 Service。

```bash
[WSL] bash verify.sh
```

确认扩容后的完整路径通过。

```bash
[WSL] bash cleanup.sh
```

脚本只删除 `cloud-course-15` 命名空间，并检查是否返回 NotFound。

```bash
[WSL] kubectl get namespaces
```

复核其他命名空间仍在。不要删除不认识的资源。

## 12. 人工检查

自动脚本之外，教师还会检查：

* 上下文与命名空间边界是否正确；
* 单副本与三副本证据是否来自同一隔离环境；
* Service 请求是否确实返回本课页面；
* 故障是否保留原始现象、原配置和恢复回归；
* 是否把 ClusterIP 写成了集群内部入口；
* 是否删除本课命名空间且未影响其他资源；
* Word 是否不含 kubeconfig、令牌、证书和完整地址。

## 13. 报告与提交

填写：

```txt
班级_学号_姓名_第15次课_K3s入门报告.docx
```

至少包含：

1. 环境门禁和 kubecontext 核对；
2. 清单结构与标签关系；
3. 节点、Pod、Deployment、Service 的分层证据；
4. Service 内部访问结果；
5. 从 1 到 3 的扩容前后对比；
6. 选择器故障、证据、恢复和同路回归；
7. 自动验收与人工检查；
8. 清理结果和证明范围。

只上传 Word 到智慧职教“第15次课”。不要上传 `lesson15-rendered.yaml`、`lesson15-service.before.yaml`、kubeconfig、证书、令牌或完整终端历史。
