1.1 为什么是声明式:从一百次重复部署说起


1.1 为什么是声明式:从一百次重复部署说起

**声明式(Declarative)**指的是:你只描述系统应有的最终状态,不书写达到该状态的每一步操作;由系统持续比较现状与期望的差值,自动执行弥补动作。Kubernetes 的全部设计都建立在这个前提上,本节讲清它为什么必要,以及容器编排到底要解决哪四个问题。

这是全教程的起点。弄懂"为什么声明",后面六章里 API Server 的受理、调度器的选址、控制器的自愈才有存在的理由——它们都是为兑现你的一句声明而服务的部门。

从一百次重复部署说起

先还原一个真实感十足的现场。某电商平台的订单服务部署在八台虚拟机上,每次发布由值班工程师执行一套十二步的手册:登机器、停进程、备份目录、拉新版本、改四处配置、开防火墙端口、起进程、看日志确认、下一台。八台机器乘十二步,一个深夜就这样耗掉。三个月里出过两次事故:一次是第五台机器的配置少改了一行,早高峰下单失败率飙升;另一次是发布到一半进程起不来,回滚靠的是工程师的备忘录而不是系统。

问题不在于工程师不够细心,而在于命令式操作把正确性寄托在人的执行力上。命令式的三宗罪:

  • 不可重放:执行过的命令不会留下"应该是什么样"的记录,换个环境没人知道该重放哪些;
  • 不可自愈:进程半夜挂了,没有谁有义务把它拉起来,因为"应该有几个进程在跑"这件事从没被写下来;
  • 不可审计:八台机器八种状态,"哪台跑的哪个版本"只能靠人肉盘点。

容器(Docker)解决了"我机器上能跑、你机器上跑不了"的环境一致性问题,但容器本身不解决上面三条。你仍然需要有人决定:多少个容器、放在哪台机器、挂了谁管、升级怎么换。这四个问题合起来,就是容器编排——Kubernetes 的主战场。

编排要解决的四个问题

把上面的事故翻译成工程语言,编排系统要兑现四件事:

问题 手工时代 Kubernetes 的回答
复制 每台机器手动起一个 声明 replicas 副本数,控制器负责凑齐
调度 人决定放哪台机器 调度器按资源与亲和性自动选址
自愈 挂了等告警,人去重启 控制器发现副本少了就补,节点挂了就迁
升级 逐台替换,中途失败靠回滚手册 滚动更新逐个换副本,一条命令回滚

注意表格右侧的措辞:回答全部落在"声明什么"上。副本数、镜像版本、端口、资源用量,这些是你要拿主意的事;选址、重启、换副本,是集群拿主意的事。Kubernetes 的学习曲线陡,很大程度上因为要先接受这种权力让渡:你从执行者变成立法者。

我的建议是,从第一天起就适应"先写声明,再观察"的节奏,别用 kubectl create 之类的命令式捷径攒出不可复现的现场。命令式创建在实验环境很顺手,但生产环境里,不能被版本库追踪的变更就是隐患。

一次最小演练:把集群当黑盒

先不深究内部组件,把集群当成一个受理声明的黑盒,感受一次"声明进去、状态出来"。本地实验推荐 minikube,五分钟内可得一个单节点集群:

# 启动本地练习集群(首次会拉取虚拟机镜像,耐心等待) minikube start --cpus 2 --memory 4g # 预期输出节选: # Done! kubectl is now configured to use "minikube" cluster # 观察集群节点:此刻它只有一台"既是大脑又是手脚"的机器 kubectl get nodes # NAME STATUS ROLES AGE VERSION # minikube Ready control-plane 1m v1.29.0

STATUS 一列的 Ready 表示节点已就绪。单节点集群里,控制平面(做决策的部分)和工作节点(跑容器的部分)挤在同一台机器上;生产集群会把它们分开,这是第2章和第3章的主线。现在只记一个分工印象:控制平面受理并决策,工作节点执行

再体验声明式与命令式的差别。用 Docker 的思路跑订单服务,是一串动作:

# 命令式:三个动作,机器不记得你的意图 docker pull orders-api:1.4.2 docker run -d --name orders -p 8080:8080 orders-api:1.4.2 # 容器挂了,世界照旧,没有谁觉得少了什么

Kubernetes 的思路则是一份意图,交给集群:

# 声明式:一份 YAML 描述"应有 3 个副本",集群负责让它成立 kubectl apply -f orders-deploy.yaml # deployment.apps/orders-api created # 此后副本不足会被自动补齐,这是第4章的内容 # 幂等验证:同一份声明重复提交,结果不变 kubectl apply -f orders-deploy.yaml # deployment.apps/orders-api unchanged

第二条命令输出 unchanged,这是声明式的标志性现象:同一输入永远得到同一结果。第一百次执行与第一次执行同样安全,这正是手工脚本永远做不到的。

案例:订单服务的迁移评估

背景:八台虚拟机、手工十二步手册、两次事故,团队决定把订单服务(无状态 HTTP 服务,峰值每秒两千请求)迁入已有的一年期 Kubernetes 集群。

操作:第一步不写 YAML,而是先回答三个问题——需要几个副本(按峰值流量与单副本容量算出 3)、依赖什么配置(数据库地址、日志级别)、要不要持久化(无状态,暂不需要)。这三个答案分别对应后续章节里的副本数声明、ConfigMap 与存储卷。

结果:迁移后发布耗时从一晚缩短到十分钟,期间下单成功率无波动;一次宿主机故障导致副本损失,两分钟内自动补齐,业务无感。

解读:收益最大化的前提是应用本身"适合被声明"——无状态、可水平复制、健康可探测。订单服务三条全占,是理想的first candidate。

变式:若迁移对象换成购物车服务(依赖会话黏性)或数据库(有持久数据),副本数就不能简单设 3,需要分别引入 Service 会话亲和(第5章)与 StatefulSet 加 PVC(第4章、第6章)。选错模型,声明写得再漂亮也白搭。

本节要点回顾

  • 声明式的本质:写清期望结果,系统持续把现状推向期望;可重放、可自愈、可审计是它对手工命令式的三重优势;
  • 编排四问:复制、调度、自愈、升级,Kubernetes 的每个核心对象都在回答其中一问;
  • 权力让渡:副本数、镜像、端口归你管;选址、重启、补副本归集群管;
  • 幂等是试金石:同一份声明重复 apply,输出 unchanged 而不是出错;
  • 先评估再迁移:无状态、可复制、可探活的应用最适合率先上集群。

声明式的动机已经清楚,下一节把 orders-api 真正写成一份完整的 Deployment YAML——每个字段为什么存在、写错了会发生什么,逐行拆给你看。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U