外观
项目 14:自动化运维综合项目验收
约 3266 字大约 11 分钟
综合项目ShellPythonAnsible
2026-06-06
项目目标
本课是课程的综合验收项目。从 WSL Ubuntu 控制端出发,通过 Docker 容器模拟 3 台服务器节点,综合运用 Shell、Ansible 和 Kubernetes 三大技术栈,依次完成服务器初始化、Kubernetes 集群部署,并将第 2-3 课的 2048 小游戏发布到 K8s 集群中。
环境说明:WSL Ubuntu 作为唯一的控制端(运行所有 Ansible、kubectl、Shell 脚本);三台 Docker 容器(privileged: true)运行在 WSL 内,模拟 K8s 集群节点;所有网络通信通过 localhost 端口映射完成。
最终提交完整的 auto-ops-final 项目目录、运行日志、项目报告、拓扑图和答辩截图。
Docker 边界说明
综合项目仍然先按第 12-13 课命令实测 Docker 三节点环境。若第 13 课已经完成 kubeadm init 和 worker join,但在 Ready 阶段遇到 Flannel 镜像解包、API Server 反复拒绝连接或控制面 Pod 反复重建等 Docker/OrbStack 嵌套限制,本课的 K8s 应用部分至少要完成 manifest dry-run 校验;浏览器访问截图改到虚拟机、云主机或教师预置好的 Ready 集群中完成。
一、项目全景
本课是整个课程的收官之战。你需要把前面 13 课学到的技能串联成一条完整的自动化流水线:
1.1 技术栈回顾
| 技术 | 在项目中的作用 | 参考课时 |
|---|---|---|
| Shell | WSL 环境脚本、密钥管理、辅助工具 | 第 1-8 课 |
| Python | 可选:部署脚本、日志解析、API 调用 | 第 4 课 |
| Ansible | 服务器初始化、K8s 节点准备、集群部署 | 第 9-13 课 |
| Kubernetes | 2048 应用容器化部署、Service 暴露 | 第 12-13 课 |
| Docker | 容器环境模拟(可选:2048 镜像构建) | 第 3、7 课 |
1.2 验收架构图
┌─────────────────────────────────────────────────────────┐
│ WSL Ubuntu 控制端 │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────────┐ │
│ │ SSH 密钥 │ │ Ansible │ │ kubectl + kubeconfig │ │
│ └──────────┘ └────┬─────┘ └──────────┬───────────┘ │
└─────────────────────┼───────────────────┼───────────────┘
│ │
┌───────────┼───────────────────┼───────────┐
│ ▼ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ K8s 集群 (3 节点) │ │
│ │ ┌─────────┐ ┌──────┐ ┌──────┐ │ │
│ │ │ Master │ │ Wkr1 │ │ Wkr2 │ │ │
│ │ │ contain │ │ cont │ │ cont │ │ │
│ │ └─────────┘ └──────┘ └──────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────┐ │ │
│ │ │ 2048 Game (Deployment x1) │ │ │
│ │ │ Service (NodePort: 30080) │ │ │
│ │ └─────────────────────────────┘ │ │
│ └─────────────────────────────────────┘ │
└───────────────────────────────────────────┘二、项目目录结构
问题:本课综合验收项目的顶层目录名是什么?
auto-ops-final/
├── README.md # 项目说明与使用指南
├── scripts/ # Shell 辅助脚本
│ ├── setup-keys.sh # SSH 密钥生成与分发
│ └── env-check.sh # 环境检查脚本
├── ansible/
│ ├── site.yml # 服务器初始化入口
│ ├── deploy_k8s.yml # K8s 集群部署入口
│ ├── inventory.ini # 主机清单
│ ├── ansible.cfg
│ ├── group_vars/
│ │ └── all.yml
│ └── roles/
│ ├── common/ # 基础软件
│ ├── security/ # SSH 安全
│ ├── firewall/ # 防火墙
│ ├── runtime/ # fail2ban
│ ├── k8s_prepare/ # K8s 节点准备
│ ├── container_runtime/ # containerd
│ ├── k8s_install/ # K8s 组件安装
│ ├── k8s_master/ # 控制平面初始化
│ ├── k8s_worker/ # Worker 加入
│ └── k8s_network/ # CNI 部署
├── k8s/ # K8s 应用部署清单
│ ├── 2048-deployment.yaml
│ └── 2048-service.yaml
├── outputs/ # 运行日志
│ ├── init-server-run.log
│ ├── k8s-prepare.log
│ ├── deploy-k8s.log
│ ├── admin.conf # kubeconfig
│ └── screenshots/ # 截图目录
├── docs/ # 项目文档
│ ├── report.md # 项目报告
│ ├── topology.png # 拓扑图
│ └── troubleshooting.md # 问题记录
└── docker/ # Docker 测试环境(可选)
├── Dockerfile
└── docker-compose.yml2.1 文件职责清单
| 文件 | 内容 | 验收要点 |
|---|---|---|
README.md | 项目概述、环境要求、一键执行命令 | 能否让另一个人照着 README 成功执行 |
scripts/setup-keys.sh | 自动生成和分发 SSH 密钥 | 幂等性:密钥已存在时不覆盖 |
scripts/env-check.sh | 检查 Ansible、kubectl、Python 版本 | 检查 Python >= 3.8, Ansible >= 2.12 |
ansible/site.yml | 服务器初始化 | 按 common → security → firewall → runtime 顺序 |
ansible/deploy_k8s.yml | K8s 集群部署 | 4 个 Play 按依赖顺序,hosts 分组正确 |
k8s/2048-deployment.yaml | 2048 应用的 Deployment 定义 | 正确的镜像、端口、副本数 |
k8s/2048-service.yaml | 2048 应用的 Service 定义 | NodePort 类型,端口映射正确 |
docs/report.md | 项目报告 | 含目标、过程、结果、反思 |
docs/topology.png | 网络拓扑图 | 标明各节点 IP、端口、角色 |
三、2048 应用的 K8s 部署
第 2-3 课使用 Shell 脚本在单台服务器上部署了 2048 小游戏。现在把它迁移到 K8s 集群中,利用 K8s 的自动恢复、负载均衡和滚动更新能力。
3.1 Deployment 定义
问题:Kubernetes 中用于管理无状态应用副本的控制器资源类型叫什么?
apiVersion: apps/v1
kind: Deployment
metadata:
name: game-2048
spec:
replicas: 2
selector:
matchLabels:
app: game-2048
template:
metadata:
labels:
app: game-2048
spec:
containers:
- name: game-2048
image: public.ecr.aws/l6m2t8p7/docker-2048:latest
ports:
- containerPort: 80| 字段 | 含义 | 本课取值 |
|---|---|---|
replicas | Pod 副本数 | 2(验证负载均衡) |
image | 容器镜像 | public.ecr.aws/l6m2t8p7/docker-2048:latest,课堂可直接拉取;如果你已推送自己的镜像,可以替换为个人镜像地址 |
containerPort | 容器监听端口 | 80(Nginx 默认端口) |
Deployment 自动恢复与扩缩容演示
Kubernetes 如何根据声明式配置(replicas)维持期望状态并实现负载均衡
👑 控制平面 (Controller)
2048-deployment.yaml
spec: replicas: 2 selector: app: game-2048🔄
Reconciliation Loop
(期望状态 vs 实际状态)
(期望状态 vs 实际状态)
🖥️ 工作节点集群 (Nodes)
📦game-2048-abcde
running
10.244.2.195
📦game-2048-fghij
running
10.244.3.199
3.2 Service 定义
问题:Kubernetes 中用于在集群每个节点上暴露端口的 Service 类型叫什么?
apiVersion: v1
kind: Service
metadata:
name: game-2048-svc
spec:
type: NodePort
selector:
app: game-2048
ports:
- port: 80 # Service 端口
targetPort: 80 # Pod 端口
nodePort: 30080 # 节点端口(30000-32767 范围)NodePort 让 2048 可以通过任何集群节点的 IP + 30080 端口访问。在 Docker 模拟环境中,需要将 30080 端口映射到宿主机。
Service NodePort 流量路由演示
外部流量如何通过任何一个节点的 30080 端口访问到内部的 2048 Pod
http://...
等待访问...
Kubernetes Cluster
k8s-worker1 (172.28.0.3)
⚙️
kube-proxy
iptables (NodePort: 30080)
iptables (NodePort: 30080)
📦Pod A (10.244.1.5:80)
k8s-worker2 (172.28.0.4)
⚙️
kube-proxy
iptables (NodePort: 30080)
iptables (NodePort: 30080)
📦Pod B (10.244.2.8:80)
3.3 部署与验证
# 使用从 master 拉取的 kubeconfig
export KUBECONFIG=outputs/admin.conf
# 部署 2048
kubectl apply -f k8s/2048-deployment.yaml
kubectl apply -f k8s/2048-service.yaml
# 查看部署状态
kubectl get deployments
kubectl get pods -o wide
kubectl get svc game-2048-svc
# 从浏览器访问 http://<任一节点IP>:30080
# Docker 环境:http://localhost:30080(需要在 docker-compose 中映射端口)如果第 13 课在 Docker 嵌套环境的 Ready 阶段卡住,先不要跳过应用清单检查。只要有可用的 API Server,就执行 dry-run,确认 Kubernetes 能识别资源:
kubectl apply --dry-run=client --validate=false -f k8s/2048-deployment.yaml
kubectl apply --dry-run=client --validate=false -f k8s/2048-service.yaml如果 API Server 已经不可用,kubectl apply --dry-run=client 也可能因为无法发现 API 资源而失败。这时先做离线 YAML 解析,确认清单至少是结构正确的:
python3 - <<'PY'
from pathlib import Path
import yaml
for file in ["k8s/2048-deployment.yaml", "k8s/2048-service.yaml"]:
data = yaml.safe_load(Path(file).read_text(encoding="utf-8"))
print(f"{file}: {data['kind']} {data['metadata']['name']} ok")
PY离线 YAML 解析不能替代真实集群部署;最终浏览器访问截图仍需要在 Ready 集群中完成。
四、一键执行脚本
为了满足"一条命令完成全部部署"的要求,项目根目录可以提供一个 run-all.sh:
问题:本课综合项目中用于'一键完成全部部署'的总入口脚本叫什么?
#!/bin/bash
set -euo pipefail
echo "========================================="
echo " 自动化运维综合项目 - 一键部署"
echo "========================================="
STEP=1
TOTAL=4
# 步骤 1: 环境检查
echo "[${STEP}/${TOTAL}] 环境检查..."
bash scripts/env-check.sh
STEP=$((STEP + 1))
# 步骤 2: 服务器初始化
echo "[${STEP}/${TOTAL}] 服务器初始化..."
cd ansible
ansible-playbook site.yml | tee ../outputs/init-server-run.log
cd ..
STEP=$((STEP + 1))
# 步骤 3: K8s 集群部署
echo "[${STEP}/${TOTAL}] K8s 集群部署..."
cd ansible
ansible-playbook deploy_k8s.yml | tee ../outputs/deploy-k8s.log
cd ..
STEP=$((STEP + 1))
# 步骤 4: 部署 2048 应用
echo "[${STEP}/${TOTAL}] 部署 2048 应用..."
export KUBECONFIG=outputs/admin.conf
kubectl apply -f k8s/2048-deployment.yaml
kubectl apply -f k8s/2048-service.yaml
echo ""
echo "========================================="
echo " 部署完成!"
echo "========================================="
echo "验证命令:"
echo " kubectl get nodes"
echo " kubectl get pods"
echo " kubectl get svc"
echo " 浏览器访问: http://localhost:30080"生产环境建议
run-all.sh 适合课堂教学和演示。在生产环境中,建议将初始化、K8s 部署和应用部署拆分为独立的 CI/CD 阶段,每个阶段有独立的审批和回滚机制。
五、项目报告模板
docs/report.md 应包含以下章节:
5.1 报告结构
# 自动化运维综合项目报告
## 一、项目概述
- 项目目标
- 技术栈
- 环境要求
## 二、系统架构
- 网络拓扑图(附 `topology.png`)
- 角色划分(Master / Worker)
- 端口规划表
## 三、实施过程
- 步骤 1:服务器初始化(含关键截图)
- 步骤 2:K8s 节点准备(含关键截图)
- 步骤 3:K8s 集群部署(含关键截图)
- 步骤 4:2048 应用部署(含访问效果截图)
## 四、遇到的问题与解决
| 问题描述 | 原因分析 | 解决方法 |
| --- | --- | --- |
| (至少记录 2 个) | | |
## 五、验证结果
- `kubectl get nodes` 输出
- `kubectl get pods -A` 输出
- 浏览器访问 2048 的截图
## 六、项目总结与反思
- 学会了什么
- 哪些地方可以改进
- 对自动化运维的理解5.2 端口规划表示例
| 组件 | 节点 | 端口 | 用途 |
|---|---|---|---|
| SSH | 全部节点 | 22 / 2222 | Ansible 管理和远程登录 |
| K8s API Server | Master | 6443 | kubectl 和集群组件通信 |
| NodePort (2048) | 全部节点 | 30080 | 浏览器访问 2048 |
| etcd | Master | 2379-2380 | etcd 客户端与服务端通信 |
| Flannel VxLAN | 全部节点 | 8472/udp | Pod 跨节点网络 |
六、验收标准与评分表
| 验收维度 | 分值 | 达标要求 |
|---|---|---|
| 服务器初始化 | 20% | site.yml 顺利执行,SSH 端口变更、UFW 生效、fail2ban 运行 |
| K8s 集群部署 | 30% | deploy_k8s.yml 顺利执行,kubectl get nodes 显示全部 Ready |
| 2048 应用部署 | 20% | Deployment + Service 部署成功,浏览器能访问 2048 游戏 |
| 项目目录规范 | 10% | 目录结构符合模板,README 能指导他人复现 |
| 项目报告 | 10% | 报告包含六章,有拓扑图和关键截图 |
| 现场答辩 | 10% | 能解释技术选型和故障排查,回答教师提问 |
如果 Docker/OrbStack 嵌套环境无法让节点最终 Ready,需要提交第 13 课故障证据、dry-run 或离线 YAML 解析输出,以及替代 Ready 集群中的最终部署截图。
七、提交物清单
| 提交物 | 要求 |
|---|---|
auto-ops-final/ 完整项目 | 目录结构符合模板,压缩为 zip 或 tar.gz |
README.md | 包含环境要求、一键执行命令、端口规划表 |
outputs/ 运行日志 | 至少包含 init-server-run.log 和 deploy-k8s.log |
outputs/screenshots/ | 包含 kubectl get nodes、kubectl get pods -A、浏览器访问截图 |
| Docker 边界证据(如适用) | 包含 journalctl -u kubelet、crictl ps -a、dry-run 输出,以及替代 Ready 集群截图 |
docs/report.md | 完整项目报告 |
docs/topology.png | 网络拓扑图 |
| 答辩准备 | 能回答"如果某一步失败,你如何排查?"等问题 |
八、常见故障排查
| 现象 | 可能原因 | 处理方法 |
|---|---|---|
run-all.sh 在某一步中断 | 前置步骤失败,后续步骤缺少依赖 | 查看对应步骤的 log 文件,逐一定位;修复后从中断步骤续跑 |
| K8s 部署后 2048 无法访问 | NodePort 未被映射或防火墙未放行 | Docker 环境检查 docker-compose.yml 中 30080 映射;真实环境检查 UFW 或安全组 |
Pod 一直 ImagePullBackOff | 镜像不存在或 registry 不可访问 | 检查镜像名和 tag;如在本地构建,确保所有节点都能拉取 |
Flannel 报 no unpack platforms defined | Docker/OrbStack 嵌套容器中 containerd native snapshotter 与多架构镜像解包不兼容 | 保留日志作为 Docker 边界证据;换虚拟机、云主机或教师预置集群完成 Ready 与访问验收 |
| 节点状态在 Ready 和 NotReady 之间抖动 | 网络不稳定或资源不足 | kubectl describe nodes 查看事件;Docker 环境增加内存限制 |
admin.conf 拉取失败 | fetch 模块路径或权限问题 | 检查 outputs/ 目录是否存在;确认 master Play 已成功执行 |
| 两个 worker 只有一个 Ready | Flannel Pod 未成功调度到第二个节点 | kubectl describe pod -n kube-flannel 查看事件;确认 worker 节点满足 CNI 部署条件 |
九、答辩准备指南
答辩问题示例 1:如果 kubeadm init 报 swap 未关闭,你怎么排查?
这是一个指向第 12 课知识点的综合问题。你需要展示从现象(kubeadm 错误信息)→ 原因(swap 可能只是临时关闭但 fstab 中仍存在)→ 解决(检查 swapon --show 和 /etc/fstab)→ 预防(在 k8s_prepare role 中同时处理临时关闭和永久关闭)的完整排查链路。
答辩问题示例 2:为什么选择 Ansible 而不是直接用 Shell 部署 K8s?
这是一个指向课程整体设计思路的问题。你需要对比 Shell 脚本(第 1-8 课的知识)和 Ansible(第 9-13 课的知识),从幂等性、可读性、可复用性、并行执行、Inventory 分组等维度说明选择的理由。参考答案:Ansible 的模块化和幂等性让 K8s 部署更可靠——kubeadm init 只执行一次、containerd 配置只修改关键行、所有节点的配置一致性有保证。而 Shell 脚本要做同样的事情,需要大量的 if、grep、sed 判断。
答辩问题示例 3:你的项目中哪些地方体现了"自动化运维"的工程思想?
这是一个开放性问题,重点展示你从课程中学到的思维模式:幂等性(重复执行不影响结果)、声明式配置(只描述期望状态)、关注点分离(Roles 拆分)、错误处理(block/rescue)、可观测性(日志和截图)、可复现性(README 和环境脚本)。
