Kubernetes 配置最佳实践

配置是 Kubernetes 中看似微不足道,实则关键的事情之一。 配置是每个 Kubernetes 工作负载的核心。 一个缺失的引号、错误的 API 版本或错位的 YAML 缩进都可能毁掉你的整个部署。

本博客汇集了经过验证的配置最佳实践。 这些小的习惯让你的 Kubernetes 设置更干净、一致且更易于管理。 无论你是刚刚开始还是已经在每天部署应用, 这些都是让你的集群保持稳定、让未来的你保持理智的小细节。

本博客的灵感源自最初的 Configuration Best Practices(配置最佳实践) 页面, 该页面由 Kubernetes 社区众多成员的贡献不断演进而来。

通用配置实践

使用最新的稳定 API 版本

Kubernetes 发展很快。旧版 API 最终会被弃用并停止工作。 因此,在定义资源时,请确保使用最新的稳定 API 版本。 你可以随时使用以下命令检查:

kubectl api-resources

这个简单的步骤可以让你避免未来的兼容性问题。

将配置存储在版本控制中

永远不要直接从桌面应用清单文件。 始终将它们保存在像 Git 这样的版本控制系统中,这是你的安全网。 如果出现问题,你可以立即回滚到之前的提交、 比较更改或重新创建集群设置,而不会惊慌。

使用 YAML 而不是 JSON 编写配置

使用 YAML 而不是 JSON 编写配置文件。 两者在技术上都可以工作,但 YAML 对人类来说更容易。 它更易读、更简洁,并在社区中广泛使用。

YAML 在布尔值方面有一些隐藏的陷阱: 只使用 truefalse。 不要写 yesnoonoff。 它们可能在一个 YAML 版本中工作,但在另一个版本中会失败。 为了安全起见,请给任何看起来像布尔值的内容加引号(例如 "yes")。

保持配置简单和最小化

避免设置 Kubernetes 已经处理的默认值。 最小化的清单更容易调试、更易于审查,并且以后不太可能破坏东西。

如果你的 Deployment、Service 和 ConfigMap 都属于一个应用, 请将它们放在一个清单文件中。 这样更容易跟踪更改并将它们作为一个单元应用。 有关此语法的示例,请参阅 Guestbook all-in-one.yaml 文件。

你甚至可以使用以下命令应用整个目录:

kubectl apply -f configs/

一个命令,该文件夹中的所有内容都会被部署。

添加有用的注解

清单文件不仅是为机器准备的,也是为人类准备的。 使用注解来描述某些内容存在的原因或它的作用。 一个快速的一行注释可以在以后调试时节省数小时,并允许更好的协作。

最有用的注解是 kubernetes.io/description。 这就像使用注释一样,只是它会被复制到 API 中, 这样其他人在你部署后也能看到它。

管理工作负载:Pod、Deployment 和 Job

在 Kubernetes 中,一个常见的早期错误是直接创建 Pod。 Pod 可以工作,但如果出现问题,它们不会重新调度自己。

裸 Pod(不受控制器管理的 Pod,例如 DeploymentStatefulSet) 用于测试是可以的,但在实际设置中,它们是有风险的。

为什么? 因为如果托管该 Pod 的节点死亡,Pod 也会随之死亡, Kubernetes 不会自动将其恢复。

对应该始终运行的应用使用 Deployment

Deployment 既创建 ReplicaSet 以确保所需数量的 Pod 始终可用, 又指定替换 Pod 的策略(例如滚动更新), 几乎总是比直接创建 Pod 更可取。 你可以推出新版本,如果出现问题,可以立即回滚。

对应该完成的任务使用 Job

当你需要某些东西运行一次然后停止时(如数据库迁移或批处理任务), Job 是完美的选择。 如果 Pod 失败,它会重试,并在完成时报告成功。

Service 配置和网络

Service 是你的工作负载在集群内部(有时是外部)相互通信的方式。 没有它们,你的 Pod 存在但无法被任何人访问。让我们确保这种情况不会发生。

在使用它们的工作负载之前创建 Service

当 Kubernetes 启动 Pod 时,它会自动为现有 Service 注入环境变量。 因此,如果 Pod 依赖于 Service,请在其相应的后端工作负载(Deployment 或 StatefulSet) 以及任何需要访问它的工作负载之前创建 Service

例如,如果存在名为 foo 的 Service,所有容器将在其初始环境中获得以下变量:

FOO_SERVICE_HOST=<the host the Service runs on>
FOO_SERVICE_PORT=<the port the Service runs on>

基于 DNS 的发现没有这个问题,但无论如何遵循它是一个好习惯。

使用 DNS 进行 Service 发现

如果你的集群有 DNS 安装扩展(Addon)(大多数都有), 每个 Service 都会自动获得一个 DNS 条目。 这意味着你可以通过名称而不是 IP 访问它:

curl http://my-service.default.svc.cluster.local

这是让 Kubernetes 网络感觉神奇的特性之一。

除非绝对必要,否则避免使用 hostPorthostNetwork

你有时会在清单中看到这些选项:

hostPort: 8080
hostNetwork: true

但问题是: 它们将你的 Pod 绑定到特定节点,使它们更难调度和扩缩容。 因为每个 <hostIPhostPortprotocol> 组合必须是唯一的。 如果你没有明确指定 hostIPprotocol, Kubernetes 将使用 0.0.0.0 作为默认 hostIP,使用 TCP 作为默认 protocol。 除非你在调试或构建网络插件之类的东西,否则请避免使用它们。

如果你只需要本地访问进行测试,请尝试 kubectl port-forward

kubectl port-forward deployment/web 8080:80

有关更多信息,请参阅 使用端口转发访问集群中的应用程序。 或者如果你真的需要外部访问,请使用 type: NodePort Service。 这是更安全、更符合 Kubernetes 原生方式的做法。

使用无头 Service 进行内部服务发现

有时,你不想让 Kubernetes 负载均衡流量。 你想直接与每个 Pod 通信。这就是无头 Service 的用武之地。

你通过设置 clusterIP: None 来创建一个。 DNS 不是给你一个 IP,而是给你所有 Pod IP 的列表, 非常适合自己管理连接的应用程序。

有效使用标签

标签是附加到 Pod 等对象的键/值对。 标签帮助你组织、查询和分组资源。 它们本身不做任何事情,但它们使从 Service 到 Deployment 的所有其他内容都能顺利协同工作。

使用语义标签

好的标签可以帮助你理解什么是什么,即使在几个月后也是如此。 定义并使用标签来标识应用程序或 Deployment 的语义属性。 例如:

labels:
  app.kubernetes.io/name: myapp
  app.kubernetes.io/component: web
  tier: frontend
  phase: test
  • app.kubernetes.io/name:应用是什么
  • tier:它属于哪一层(前端/后端)
  • phase:它处于哪个阶段(测试/生产)

然后你可以使用这些标签来创建强大的选择算符。 例如:

kubectl get pods -l tier=frontend

这将列出集群中所有前端 Pod,无论它们来自哪个 Deployment。 基本上,你不需要手动列出 Pod 名称;你只是在描述你想要什么。 有关此方法的示例,请参阅 guestbook 应用。

使用常见的 Kubernetes 标签

Kubernetes 实际上推荐一组常见标签。 这是在你的不同工作负载或项目中命名事物的一种标准方式。 遵循此约定使你的清单更清晰,这意味着诸如 Headlampdashboard 或第三方监控系统等工具 都可以自动理解正在运行的内容。

操作标签进行调试

由于控制器(如 ReplicaSet 或 Deployment)使用标签来管理 Pod, 你可以删除标签以临时"分离" Pod。

示例:

kubectl label pod mypod app-

app- 部分会删除标签键 app。 一旦发生这种情况,控制器将不再管理该 Pod。 这就像将其隔离以进行检查,一种用于调试的"隔离模式"。 要交互式地删除或添加标签,请使用 kubectl label

然后你可以检查 Pod 日志、exec 进入 Pod,完成后手动删除 Pod。 这是每个 Kubernetes 工程师都应该知道的超级被低估的技巧。

实用的 kubectl 技巧

这些小技巧使你在处理多个清单文件或集群时生活变得更加轻松。

应用整个目录

不要一次应用一个文件,而是应用整个文件夹:

# Using server-side apply is also a good practice
kubectl apply -f configs/ --server-side

此命令在该文件夹中查找 .yaml.yml.json 文件并将它们一起应用。 它更快、更清晰,并有助于按应用分组。

使用标签选择算符获取或删除资源

你不总是需要逐个输入资源名称。 相反,使用标签选择算符一次对整个组进行操作:

kubectl get pods -l app=myapp
kubectl delete pod -l phase=test

这在 CI/CD 流水线中特别有用,你可以在其中动态清理测试资源。

快速创建 Deployment 和 Service

对于快速实验,你不总是需要编写清单。 你可以直接从 CLI 启动 Deployment:

kubectl create deployment webapp --image=nginx

然后将其公开为 Service:

kubectl expose deployment webapp --port=80

当你想在编写完整清单之前测试某些内容时,这很棒。 另外,有关示例,请参阅 使用 Service 访问集群中的应用程序

结论

更清晰的配置可以让集群管理员更为泰然自若。 如果你坚持几个简单的习惯:保持配置简单和最小化、对所有内容进行版本控制、 使用一致的标签,并避免依赖裸 Pod,你将为自己节省数小时的调试时间。

最好的部分是什么? 清晰的配置保持可读性。即使在几个月后, 你或团队中的任何人都可以瞥一眼它们并确切知道发生了什么。