diff --git a/content/zh-cn/blog/_posts/2025-11-25-kubernetes-configuration-best-practices.md b/content/zh-cn/blog/_posts/2025-11-25-kubernetes-configuration-best-practices.md new file mode 100644 index 0000000000000..0166290514277 --- /dev/null +++ b/content/zh-cn/blog/_posts/2025-11-25-kubernetes-configuration-best-practices.md @@ -0,0 +1,568 @@ +--- +layout: blog +title: "Kubernetes 配置最佳实践" +date: 2025-11-25T00:00:00+00:00 +slug: configuration-good-practices +evergreen: true +author: Kirti Goyal +translator: > + [Wenjun Lou](https://github.com/Eason1118) +--- + + + +配置是 Kubernetes 中看似微不足道,实则关键的事情之一。 +配置是每个 Kubernetes 工作负载的核心。 +一个缺失的引号、错误的 API 版本或错位的 YAML 缩进都可能毁掉你的整个部署。 + + +本博客汇集了经过验证的配置最佳实践。 +这些小的习惯让你的 Kubernetes 设置更干净、一致且更易于管理。 +无论你是刚刚开始还是已经在每天部署应用, +这些都是让你的集群保持稳定、让未来的你保持理智的小细节。 + + +**本博客的灵感源自最初的 Configuration Best Practices(配置最佳实践) 页面, +该页面由 Kubernetes 社区众多成员的贡献不断演进而来。** + +## 通用配置实践 {#general-configuration-practices} + + +### 使用最新的稳定 API 版本 {#use-the-latest-stable-api-version} + +Kubernetes 发展很快。旧版 API 最终会被弃用并停止工作。 +因此,在定义资源时,请确保使用最新的稳定 API 版本。 +你可以随时使用以下命令检查: + +```bash +kubectl api-resources +``` + + +这个简单的步骤可以让你避免未来的兼容性问题。 + + +### 将配置存储在版本控制中 {#store-configuration-in-version-control} + +永远不要直接从桌面应用清单文件。 +始终将它们保存在像 Git 这样的版本控制系统中,这是你的安全网。 +如果出现问题,你可以立即回滚到之前的提交、 +比较更改或重新创建集群设置,而不会惊慌。 + + +### 使用 YAML 而不是 JSON 编写配置 {#write-configs-in-yaml-not-json} + +使用 YAML 而不是 JSON 编写配置文件。 +两者在技术上都可以工作,但 YAML 对人类来说更容易。 +它更易读、更简洁,并在社区中广泛使用。 + + +YAML 在布尔值方面有一些隐藏的陷阱: +只使用 `true` 或 `false`。 +不要写 `yes`、`no`、`on` 或 `off`。 +它们可能在一个 YAML 版本中工作,但在另一个版本中会失败。 +为了安全起见,请给任何看起来像布尔值的内容加引号(例如 `"yes"`)。 + + +### 保持配置简单和最小化 {#keep-configuration-simple-and-minimal} + +避免设置 Kubernetes 已经处理的默认值。 +最小化的清单更容易调试、更易于审查,并且以后不太可能破坏东西。 + + +### 将相关对象分组在一起 {#group-related-objects-together} + +如果你的 Deployment、Service 和 ConfigMap 都属于一个应用, +请将它们放在一个清单文件中。 +这样更容易跟踪更改并将它们作为一个单元应用。 +有关此语法的示例,请参阅 +[Guestbook all-in-one.yaml](https://github.com/kubernetes/examples/blob/master/web/guestbook/all-in-one/guestbook-all-in-one.yaml) 文件。 + + +你甚至可以使用以下命令应用整个目录: + +```bash +kubectl apply -f configs/ +``` + + +一个命令,该文件夹中的所有内容都会被部署。 + + +### 添加有用的注解 {#add-helpful-annotations} + +清单文件不仅是为机器准备的,也是为人类准备的。 +使用注解来描述某些内容存在的原因或它的作用。 +一个快速的一行注释可以在以后调试时节省数小时,并允许更好的协作。 + + +最有用的注解是 `kubernetes.io/description`。 +这就像使用注释一样,只是它会被复制到 API 中, +这样其他人在你部署后也能看到它。 + + +## 管理工作负载:Pod、Deployment 和 Job {#managing-workloads-pods-deployments-and-jobs} + + +在 Kubernetes 中,一个常见的早期错误是直接创建 Pod。 +Pod 可以工作,但如果出现问题,它们不会重新调度自己。 + + +**裸 Pod**(不受控制器管理的 Pod,例如 +[Deployment](/zh-cn/docs/concepts/workloads/controllers/deployment/) 或 +[StatefulSet](/zh-cn/docs/concepts/workloads/controllers/statefulset/)) +用于测试是可以的,但在实际设置中,它们是有风险的。 + + +为什么? +因为如果托管该 Pod 的节点死亡,Pod 也会随之死亡, +Kubernetes 不会自动将其恢复。 + + +### 对应该始终运行的应用使用 Deployment {#use-deployments-for-apps-that-should-always-be-running} + +Deployment 既创建 ReplicaSet 以确保所需数量的 Pod 始终可用, +又指定替换 Pod 的策略(例如[滚动更新](/zh-cn/docs/concepts/workloads/controllers/deployment/#rolling-update-deployment)), +几乎总是比直接创建 Pod 更可取。 +你可以推出新版本,如果出现问题,可以立即回滚。 + + +### 对应该完成的任务使用 Job {#use-jobs-for-tasks-that-should-finish} + +当你需要某些东西运行一次然后停止时(如数据库迁移或批处理任务), +[Job](/zh-cn/docs/concepts/workloads/controllers/job/) 是完美的选择。 +如果 Pod 失败,它会重试,并在完成时报告成功。 + + +## Service 配置和网络 {#service-configuration-and-networking} + + +Service 是你的工作负载在集群内部(有时是外部)相互通信的方式。 +没有它们,你的 Pod 存在但无法被任何人访问。让我们确保这种情况不会发生。 + + +### 在使用它们的工作负载之前创建 Service {#create-services-before-workloads-that-use-them} + +当 Kubernetes 启动 Pod 时,它会自动为现有 Service 注入环境变量。 +因此,如果 Pod 依赖于 Service,请在其相应的后端工作负载(Deployment 或 StatefulSet) +以及任何需要访问它的工作负载**之前**创建 [Service](/zh-cn/docs/concepts/services-networking/service/)。 + + +例如,如果存在名为 foo 的 Service,所有容器将在其初始环境中获得以下变量: + +``` +FOO_SERVICE_HOST= +FOO_SERVICE_PORT= +``` + + +基于 DNS 的发现没有这个问题,但无论如何遵循它是一个好习惯。 + + +### 使用 DNS 进行 Service 发现 {#use-dns-for-service-discovery} + +如果你的集群有 DNS [安装扩展(Addon)](/zh-cn/docs/concepts/cluster-administration/addons/)(大多数都有), +每个 Service 都会自动获得一个 DNS 条目。 +这意味着你可以通过名称而不是 IP 访问它: + +```bash +curl http://my-service.default.svc.cluster.local +``` + + +这是让 Kubernetes 网络感觉神奇的特性之一。 + + +### 除非绝对必要,否则避免使用 `hostPort` 和 `hostNetwork` {#avoid-hostport-and-hostnetwork-unless-absolutely-necessary} + +你有时会在清单中看到这些选项: + +```yaml +hostPort: 8080 +hostNetwork: true +``` + + +但问题是: +它们将你的 Pod 绑定到特定节点,使它们更难调度和扩缩容。 +因为每个 <`hostIP`、`hostPort`、`protocol`> 组合必须是唯一的。 +如果你没有明确指定 `hostIP` 和 `protocol`, +Kubernetes 将使用 `0.0.0.0` 作为默认 `hostIP`,使用 `TCP` 作为默认 `protocol`。 +除非你在调试或构建网络插件之类的东西,否则请避免使用它们。 + + +如果你只需要本地访问进行测试,请尝试 [`kubectl port-forward`](/zh-cn/docs/reference/kubectl/generated/kubectl_port-forward/): + +```bash +kubectl port-forward deployment/web 8080:80 +``` + + +有关更多信息,请参阅 +[使用端口转发访问集群中的应用程序](/zh-cn/docs/tasks/access-application-cluster/port-forward-access-application-cluster/)。 +或者如果你真的需要外部访问,请使用 [`type: NodePort` Service](/zh-cn/docs/concepts/services-networking/service/#type-nodeport)。 +这是更安全、更符合 Kubernetes 原生方式的做法。 + + +### 使用无头 Service 进行内部服务发现 {#use-headless-services-for-internal-discovery} + +有时,你不想让 Kubernetes 负载均衡流量。 +你想直接与每个 Pod 通信。这就是[无头 Service](/zh-cn/docs/concepts/services-networking/service/#headless-services) 的用武之地。 + + +你通过设置 `clusterIP: None` 来创建一个。 +DNS 不是给你一个 IP,而是给你所有 Pod IP 的列表, +非常适合自己管理连接的应用程序。 + + +## 有效使用标签 {#working-with-labels-effectively} + + +[标签](/zh-cn/docs/concepts/overview/working-with-objects/labels/)是附加到 Pod 等对象的键/值对。 +标签帮助你组织、查询和分组资源。 +它们本身不做任何事情,但它们使从 Service 到 Deployment 的所有其他内容都能顺利协同工作。 + + +### 使用语义标签 {#use-semantics-labels} + +好的标签可以帮助你理解什么是什么,即使在几个月后也是如此。 +定义并使用[标签](/zh-cn/docs/concepts/overview/working-with-objects/labels/)来标识应用程序或 Deployment 的语义属性。 +例如: + +```yaml +labels: + app.kubernetes.io/name: myapp + app.kubernetes.io/component: web + tier: frontend + phase: test +``` + + +- `app.kubernetes.io/name`:应用是什么 +- `tier`:它属于哪一层(前端/后端) +- `phase`:它处于哪个阶段(测试/生产) + + +然后你可以使用这些标签来创建强大的选择算符。 +例如: + +```bash +kubectl get pods -l tier=frontend +``` + + +这将列出集群中所有前端 Pod,无论它们来自哪个 Deployment。 +基本上,你不需要手动列出 Pod 名称;你只是在描述你想要什么。 +有关此方法的示例,请参阅 [guestbook](https://github.com/kubernetes/examples/tree/master/web/guestbook/) 应用。 + + +### 使用常见的 Kubernetes 标签 {#use-common-kubernetes-labels} + +Kubernetes 实际上推荐一组[常见标签](/zh-cn/docs/concepts/overview/working-with-objects/common-labels/)。 +这是在你的不同工作负载或项目中命名事物的一种标准方式。 +遵循此约定使你的清单更清晰,这意味着诸如 [Headlamp](https://headlamp.dev/)、 +[dashboard](https://github.com/kubernetes/dashboard#introduction) 或第三方监控系统等工具 +都可以自动理解正在运行的内容。 + + +### 操作标签进行调试 {#manipulate-labels-for-debugging} + +由于控制器(如 ReplicaSet 或 Deployment)使用标签来管理 Pod, +你可以删除标签以临时"分离" Pod。 + + +示例: + +```bash +kubectl label pod mypod app- +``` + + +`app-` 部分会删除标签键 `app`。 +一旦发生这种情况,控制器将不再管理该 Pod。 +这就像将其隔离以进行检查,一种用于调试的"隔离模式"。 +要交互式地删除或添加标签,请使用 [`kubectl label`](/zh-cn/docs/reference/kubectl/generated/kubectl_label/)。 + + +然后你可以检查 Pod 日志、exec 进入 Pod,完成后手动删除 Pod。 +这是每个 Kubernetes 工程师都应该知道的超级被低估的技巧。 + + +## 实用的 kubectl 技巧 {#handy-kubectl-tips} + + +这些小技巧使你在处理多个清单文件或集群时生活变得更加轻松。 + + +### 应用整个目录 {#apply-entire-directories} + +不要一次应用一个文件,而是应用整个文件夹: + +```bash +# Using server-side apply is also a good practice +kubectl apply -f configs/ --server-side +``` + + +此命令在该文件夹中查找 `.yaml`、`.yml` 和 `.json` 文件并将它们一起应用。 +它更快、更清晰,并有助于按应用分组。 + + +### 使用标签选择算符获取或删除资源 {#use-label-selectors-to-get-or-delete-resources} + +你不总是需要逐个输入资源名称。 +相反,使用[标签选择算符](/zh-cn/docs/concepts/overview/working-with-objects/labels/#label-selectors)一次对整个组进行操作: + +```bash +kubectl get pods -l app=myapp +kubectl delete pod -l phase=test +``` + + +这在 CI/CD 流水线中特别有用,你可以在其中动态清理测试资源。 + + +### 快速创建 Deployment 和 Service {#quickly-create-deployments-and-services} + +对于快速实验,你不总是需要编写清单。 +你可以直接从 CLI 启动 Deployment: + +```bash +kubectl create deployment webapp --image=nginx +``` + + +然后将其公开为 Service: + +```bash +kubectl expose deployment webapp --port=80 +``` + + +当你想在编写完整清单之前测试某些内容时,这很棒。 +另外,有关示例,请参阅 +[使用 Service 访问集群中的应用程序](/zh-cn/docs/tasks/access-application-cluster/service-access-application-cluster/)。 + + +## 结论 {#conclusion} + + +更清晰的配置可以让集群管理员更为泰然自若。 +如果你坚持几个简单的习惯:保持配置简单和最小化、对所有内容进行版本控制、 +使用一致的标签,并避免依赖裸 Pod,你将为自己节省数小时的调试时间。 + + +最好的部分是什么? +清晰的配置保持可读性。即使在几个月后, +你或团队中的任何人都可以瞥一眼它们并确切知道发生了什么。