diff --git a/content/zh/docs/tasks/administer-cluster/manage-resources/memory-default-namespace.md b/content/zh/docs/tasks/administer-cluster/manage-resources/memory-default-namespace.md index 2a1345364c..a88e74255d 100644 --- a/content/zh/docs/tasks/administer-cluster/manage-resources/memory-default-namespace.md +++ b/content/zh/docs/tasks/administer-cluster/manage-resources/memory-default-namespace.md @@ -2,35 +2,55 @@ title: 为命名空间配置默认的内存请求和限制 content_type: task weight: 10 +description: >- + 为命名空间定义默认的内存资源限制,在该命名空间中每个新建的 Pod 都会被配置上内存资源限制。 --- +本章介绍如何为{{< glossary_tooltip text="命名空间" term_id="namespace" >}}配置默认的内存请求和限制。 + +一个 Kubernetes 集群可被划分为多个命名空间。 +如果你在具有默认内存[限制](/zh/docs/concepts/configuration/manage-resources-containers/#requests-and-limits) +的命名空间内尝试创建一个 Pod,并且这个 Pod 中的容器没有声明自己的内存资源限制, +那么{{< glossary_tooltip text="控制面" term_id="control-plane" >}}会为该容器设定默认的内存限制。 -本文介绍怎样给命名空间配置默认的内存请求和限制。 -如果在一个有默认内存限制的命名空间创建容器,该容器没有声明自己的内存限制时, -将会被指定默认内存限制。 Kubernetes 还为某些情况指定了默认的内存请求,本章后面会进行介绍。 ## {{% heading "prerequisites" %}} -{{< include "task-tutorial-prereqs.md" >}} {{< version-check >}} +{{< include "task-tutorial-prereqs.md" >}} +在你的集群里你必须要有创建命名空间的权限。 + 你的集群中的每个节点必须至少有 2 GiB 的内存。 @@ -52,12 +72,14 @@ kubectl create namespace default-mem-example ## 创建 LimitRange 和 Pod -这里给出了一个限制范围对象的配置文件。该配置声明了一个默认的内存请求和一个默认的内存限制。 +以下为 {{< glossary_tooltip text="LimitRange" term_id="limitrange" >}} 的示例清单。 +清单中声明了默认的内存请求和默认的内存限制。 {{< codenew file="admin/resource/memory-defaults.yaml" >}} @@ -71,19 +93,20 @@ kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults.yaml --n ``` -现在,如果在 default-mem-example 命名空间创建容器,并且该容器没有声明自己的内存请求和限制值, -它将被指定默认的内存请求 256 MiB 和默认的内存限制 512 MiB。 +现在如果你在 default-mem-example 命名空间中创建一个 Pod, +并且该 Pod 中所有容器都没有声明自己的内存请求和内存限制, +{{< glossary_tooltip text="控制面" term_id="control-plane" >}} +会将内存的默认请求值 256MiB 和默认限制值 512MiB 应用到 Pod 上。 -下面是具有一个容器的 Pod 的配置文件。 -容器未指定内存请求和限制。 +以下为只包含一个容器的 Pod 的清单。该容器没有声明内存请求和限制。 {{< codenew file="admin/resource/memory-defaults-pod.yaml" >}} @@ -106,7 +129,7 @@ kubectl get pod default-mem-demo --output=yaml --namespace=default-mem-example ``` 输出内容显示该 Pod 的容器有 256 MiB 的内存请求和 512 MiB 的内存限制。 @@ -134,14 +157,14 @@ kubectl delete pod default-mem-demo --namespace=default-mem-example ``` ## 声明容器的限制而不声明它的请求会怎么样? -这里给出了包含一个容器的 Pod 的配置文件。该容器声明了内存限制,而没有声明内存请求: +以下为只包含一个容器的 Pod 的清单。该容器声明了内存限制,而没有声明内存请求。 {{< codenew file="admin/resource/memory-defaults-pod-2.yaml" >}} @@ -164,8 +187,8 @@ kubectl get pod default-mem-demo-2 --output=yaml --namespace=default-mem-example ``` 输出结果显示容器的内存请求被设置为它的内存限制相同的值。注意该容器没有被指定默认的内存请求值 256MiB。 @@ -178,15 +201,15 @@ resources: ``` ## 声明容器的内存请求而不声明内存限制会怎么样? -这里给出了一个包含一个容器的 Pod 的配置文件。该容器声明了内存请求,但没有内存限制: +以下为只包含一个容器的 Pod 的清单。该容器声明了内存请求,但没有内存限制: {{< codenew file="admin/resource/memory-defaults-pod-3.yaml" >}} @@ -209,12 +232,12 @@ kubectl get pod default-mem-demo-3 --output=yaml --namespace=default-mem-example ``` -输出结果显示该容器的内存请求被设置为了容器配置文件中声明的数值。 -容器的内存限制被设置为 512MiB,即命名空间的默认内存限制。 +输出结果显示所创建的 Pod 中,容器的内存请求为 Pod 清单中声明的值。 +然而同一容器的内存限制被设置为 512MiB,此值是该命名空间的默认内存限制值。 ``` resources: @@ -227,27 +250,45 @@ resources: ## 设置默认内存限制和请求的动机 -如果你的命名空间有资源配额,那么默认内存限制是很有帮助的。 -下面是一个例子,通过资源配额为命名空间设置两项约束: +如果你的命名空间设置了内存 {{< glossary_tooltip text="资源配额" term_id="resource-quota" >}}, +那么为内存限制设置一个默认值会很有帮助。 +以下是内存资源配额对命名空间的施加的三条限制: + +* 命名空间中运行的每个 Pod 中的容器都必须有内存限制。 + (如果为 Pod 中的每个容器声明了内存限制, + Kubernetes 可以通过将其容器的内存限制相加推断出 Pod 级别的内存限制)。 + +* 内存限制用来在 Pod 被调度到的节点上执行资源预留。 + 预留给命名空间中所有 Pod 使用的内存总量不能超过规定的限制。 + +* 命名空间中所有 Pod 实际使用的内存总量也不能超过规定的限制。 -* 运行在命名空间中的每个容器必须有自己的内存限制。 -* 命名空间中所有容器的内存使用量之和不能超过声明的限制值。 +When you add a LimitRange: - -如果一个容器没有声明自己的内存限制,会被指定默认限制,然后它才会被允许在限定了配额的命名空间中运行。 +当你添加 LimitRange 时: + +如果该命名空间中的任何 Pod 的容器未指定内存限制, +控制面将默认内存限制应用于该容器, +这样 Pod 可以在受到内存 ResourceQuota 限制的命名空间中运行。 {{< caution >}} [动态 kubelet 配置](https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/281-dynamic-kubelet-configuration) -已经废弃不建议使用。请选择其他方法将配置分发到集群中的节点。 +功能在 Kubernetes 1.22 版本弃用,并在 1.24 版本中移除。 +请选择其他方法将配置分发到集群中的节点。 {{< /caution >}} [动态 kubelet 配置](https://github.com/kubernetes/enhancements/issues/281) -允许你通过部署一个所有节点都会使用的 ConfigMap -达到在运行中的 Kubernetes 集群中更改 kubelet 配置的目的。 +允许你通过部署并配置{{< glossary_tooltip text="节点" term_id="node" >}}使用的 +{{< glossary_tooltip text="ConfigMap" term_id="configmap" >}}, +达到更改正在运行的 Kubernetes 集群的 {{< glossary_tooltip text="kubelet" term_id="kubelet" >}} 配置的目的。 -{{< warning >}} -所有 kubelet 配置参数都可以被动态更改,但对某些参数来说这类更改是不安全的。 -在决定动态更改参数之前,你需要深刻理解这个改动将会如何影响集群的行为。 -在将变更扩散到整个集群之前,你需要先在小规模的节点集合上仔细地测试这些配置变动。 -特定字段相关的配置建议可以在文档 -[`KubeletConfiguration`](/docs/reference/config-api/kubelet-config.v1beta1/)中找到。 -{{< /warning >}} - -## {{% heading "prerequisites" %}} +请在 [早期版本的文档](https://v1-23.docs.kubernetes.io/zh/docs/tasks/administer-cluster/reconfigure-kubelet/) 中找到有关此功能的文档。 -你需要一个 Kubernetes 集群。 -你还需要 `kubectl`,[安装](/zh/docs/tasks/tools/#kubectl)并配置好与集群的通信。 -{{< version-check >}} -确保你使用的 `kubectl` 版本与集群 [兼容](/releases/version-skew-policy/)。 +## 不再使用动态 Kubelet 配置 + +这里没有跨不同的 Kubernetes 发行版替换这个功能的建议方法。 +如果你使用托管 Kubernetes 版本, +请咨询托管 Kubernetes 的供应商,以获得自定义 Kubernetes 的最佳实践。 +如果你使用的是 `kubeadm`,请参考 +[使用 kubeadm 配置集群中的每个 kubelet](/zh/docs/setup/production-environment/tools/kubeadm/kubelet-integration/)。 -在某些例子中使用了命令行工具 [jq](https://stedolan.github.io/jq/)。 -你并不一定需要 `jq` 才能完成这些任务,因为总是有一些手工替代的方式。 - -针对你重新配置的每个节点,你必须设置 kubelet 的标志 -`-dynamic-config-dir`,使之指向一个可写的目录。 - - +为了停止使用动态 Kubelet 配置功能, +应该使用替代机制分发 kubelet 配置文件。 +为了使配置生效,必须更新配置文件并重新启动 kubelet。 +请参考[通过配置文件设置 Kubelet 参数](/zh/docs/tasks/administer-cluster/kubelet-config-file/)。 -## 重配置 集群中运行节点上的 kubelet - -### 基本工作流程概览 - - -在运行中的集群中配置 kubelet 的基本工作流程如下: - -1. 编写一个包含 kubelet 配置的 YAML 或 JSON 文件。 -2. 将此文件包装在 ConfigMap 中并将其保存到 Kubernetes 控制平面。 -3. 更新 kubelet 所在节点对象以使用此 ConfigMap。 - - -每个 kubelet 都会在其各自的节点对象上监测(Watch)配置引用。当引用更改时,kubelet 将下载新的配置文件, -更新本地引用指向该文件,然后退出。 -为了使该功能正常地工作,你必须运行操作系统级别的服务管理器(如 systemd), -它将会在 kubelet 退出后将其重启。 -kubelet 重新启动时,将开始使用新配置。 - - -新配置将会完全地覆盖 `--config` 所提供的配置,并被命令行标志覆盖。 -新配置中未指定的值将收到适合配置版本的默认值 -(e.g. `kubelet.config.k8s.io/v1beta1`),除非被命令行标志覆盖。 - - -节点 kubelet 配置状态可通过 `node.spec.status.config` 获取。 -一旦你更新了一个节点去使用新的 ConfigMap, -就可以通过观察此状态来确认该节点是否正在使用预期配置。 - - -本文中使用命令 `kubectl edit` 来编辑节点,还有其他的方式可以修改节点的规约, -比如更利于脚本化工作流程的 `kubectl patch`。 - - -本文仅仅讲述在单节点上使用每个 ConfigMap。请注意对于多个节点使用相同的 ConfigMap -也是合法的。 - - -{{< warning >}} -尽管通过就地更新 ConfigMap 来更改配置是 *可能的*。 -但是这样做会导致所有使用该 ConfigMap 配置的 kubelet 同时更新。 -更安全的做法是按惯例将 ConfigMap 视为不可变更的,借助于 -`kubectl` 的 `--append-hash` 选项逐步把更新推广到 `node.spec.configSource`。 -{{< /warning >}} - - -### 节点鉴权器的自动 RBAC 规则 - -以前,你需要手动创建 RBAC 规则以允许节点访问其分配的 ConfigMap。节点鉴权器现在 -能够自动配置这些规则。 - - -### 生成包含当前配置的文件 - -动态 kubelet 配置特性允许你为整个配置对象提供一个重载配置,而不是靠单个字段的叠加。 -这是一个更简单的模型,可以更轻松地跟踪配置值的来源,更便于调试问题。 -然而,相应的代价是你必须首先了解现有配置,以确保你只更改你打算修改的字段。 - - -组件 kubelet 从其配置文件中加载配置数据,不过你可以通过设置命令行标志 -来重载文件中的一些配置。这意味着,如果你仅知道配置文件的内容,而你不知道 -命令行重载了哪些配置,你就无法知道 kubelet 的运行时配置是什么。 - - -因为你需要知道运行时所使用的配置才能重载之,你可以从 kubelet 取回其运行时配置。 -你可以通过访问 kubelet 的 `configz` 末端来生成包含节点当前配置的配置文件; -这一操作可以通过 `kubectl proxy` 来完成。 -下一节解释如何完成这一操作。 - - -{{< caution >}} -组件 `kubelet` 上的 `configz` 末端是用来协助调试的,并非 kubelet 稳定行为的一部分。 -请不要在产品环境下依赖此末端的行为,也不要在自动化工具中使用此末端。 -{{< /caution >}} - - -关于如何使用配置文件来配置 kubelet 行为的更多信息可参见 -[通过配置文件设置 kubelet 参数](/zh/docs/tasks/administer-cluster/kubelet-config-file) -文档。 - - -#### 生成配置文件 - - -{{< note >}} -下面的任务步骤中使用了 `jq` 命令以方便处理 JSON 数据。为了完成这里讲述的任务, -你需要安装 `jq`。如果你更希望手动提取 `kubeletconfig` 子对象,也可以对这里 -的对应步骤做一些调整。 -{{< /note >}} - - -1. 选择要重新配置的节点。在本例中,此节点的名称为 `NODE_NAME`。 -2. 使用以下命令在后台启动 kubectl 代理: - - ```shell - kubectl proxy --port=8001 & - ``` - -3. 运行以下命令从 `configz` 端点中下载并解压配置。这个命令很长,因此在复制粘贴时要小心。 - **如果你使用 zsh**,请注意常见的 zsh 配置要添加反斜杠转义 URL 中变量名称周围的大括号。 - 例如:在粘贴时,`${NODE_NAME}` 将被重写为 `$\{NODE_NAME\}`。 - 你必须在运行命令之前删除反斜杠,否则命令将失败。 - - ```bash - NODE_NAME="the-name-of-the-node-you-are-reconfiguring"; curl -sSL "http://localhost:8001/api/v1/nodes/${NODE_NAME}/proxy/configz" | jq '.kubeletconfig|.kind="KubeletConfiguration"|.apiVersion="kubelet.config.k8s.io/v1beta1"' > kubelet_configz_${NODE_NAME} - ``` - - -{{< note >}} -你需要手动将 `kind` 和 `apiVersion` 添加到下载对象中,因为它们不是由 `configz` 末端 -返回的。 -{{< /note >}} - - -#### 修改配置文件 - -使用文本编辑器,改变上述操作生成的文件中一个参数。 -例如,你或许会修改 QPS 参数 `eventRecordQPS`。 - - -#### 把配置文件推送到控制平面 - -用以下命令把编辑后的配置文件推送到控制平面: - -```bash -kubectl -n kube-system create configmap my-node-config \ - --from-file=kubelet=kubelet_configz_${NODE_NAME} \ - --append-hash -o yaml -``` - - -下面是合法响应的一个例子: - -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - creationTimestamp: 2017-09-14T20:23:33Z - name: my-node-config-gkt4c2m4b2 - namespace: kube-system - resourceVersion: "119980" - selfLink: /api/v1/namespaces/kube-system/configmaps/my-node-config-gkt4c2m4b2 - uid: 946d785e-998a-11e7-a8dd-42010a800006 -data: - kubelet: | - {...} -``` - - -你会在 `kube-system` 命名空间中创建 ConfigMap,因为 kubelet 是 Kubernetes 的系统组件。 - - -`--append-hash` 选项给 ConfigMap 内容附加了一个简短校验和。 -这对于先编辑后推送的工作流程很方便, -因为它自动并确定地为新 ConfigMap 生成新的名称。 -在以下示例中,包含生成的哈希字符串的对象名被称为 `CONFIG_MAP_NAME`。 - - -#### 配置节点使用新的配置 - -```bash -kubectl edit node ${NODE_NAME} -``` - - -在你的文本编辑器中,在 `spec` 下增添以下 YAML: - -```yaml -configSource: - configMap: - name: CONFIG_MAP_NAME - namespace: kube-system - kubeletConfigKey: kubelet -``` - - -你必须同时指定 `name`、`namespace` 和 `kubeletConfigKey` 这三个属性。 -`kubeletConfigKey` 这个参数通知 kubelet ConfigMap 中的哪个键下面包含所要的配置。 - - -#### 观察节点开始使用新配置 - -用 `kubectl get node ${NODE_NAME} -o yaml` 命令读取节点并检查 `node.status.config` 内容。 -状态部分报告了对应 `active`(使用中的)配置、`assigned`(被赋予的)配置和 -`lastKnownGood`(最近已知可用的)配置的配置源。 - -- `active` 是 kubelet 当前运行时所使用的版本。 -- `assigned` 参数是 kubelet 基于 `node.spec.configSource` 所解析出来的最新版本。 -- `lastKnownGood` 参数是 kubelet 的回退版本;如果在 `node.spec.configSource` 中 - 包含了无效的配置值,kubelet 可以回退到这个版本。 - - -如果用本地配置部署节点,使其设置成默认值,这个 `lastKnownGood` 配置可能不存在。 -在 kubelet 配置好后,将更新 `lastKnownGood` 为一个有效的 `assigned` 配置。 -决定如何确定某配置成为 `lastKnownGood` 配置的细节并不在 API 保障范畴, -不过目前实现中采用了 10 分钟的宽限期。 - - -你可以使用以下命令(使用 `jq`)过滤出配置状态: - -```bash -kubectl get no ${NODE_NAME} -o json | jq '.status.config' -``` - - -以下是一个响应示例: - -```json -{ - "active": { - "configMap": { - "kubeletConfigKey": "kubelet", - "name": "my-node-config-9mbkccg2cc", - "namespace": "kube-system", - "resourceVersion": "1326", - "uid": "705ab4f5-6393-11e8-b7cc-42010a800002" - } - }, - "assigned": { - "configMap": { - "kubeletConfigKey": "kubelet", - "name": "my-node-config-9mbkccg2cc", - "namespace": "kube-system", - "resourceVersion": "1326", - "uid": "705ab4f5-6393-11e8-b7cc-42010a800002" - } - }, - "lastKnownGood": { - "configMap": { - "kubeletConfigKey": "kubelet", - "name": "my-node-config-9mbkccg2cc", - "namespace": "kube-system", - "resourceVersion": "1326", - "uid": "705ab4f5-6393-11e8-b7cc-42010a800002" - } - } -} -``` - - -如果你没有安装 `jq`,你可以查看整个响应对象,查找其中的 `node.status.config` -部分。 - - -如果发生错误,kubelet 会在 `Node.Status.Config.Error` 中显示出错误信息的结构体。 -错误可能出现在列表[理解节点状态配置错误信息](#understanding-node-config-status-errors)中。 -你可以在 kubelet 日志中搜索相同的文本以获取更多详细信息和有关错误的上下文。 - - -#### 做出更多的改变 {#make-more-changes} - -按照下面的工作流程做出更多的改变并再次推送它们。 -你每次推送一个 ConfigMap 的新内容时,kubectl 的 `--append-hash` 选项都会给 -ConfigMap 创建一个新的名称。 -最安全的上线策略是首先创建一个新的 ConfigMap,然后更新节点以使用新的 ConfigMap。 - - -#### 重置节点以使用其本地默认配置 - -要重置节点,使其使用节点创建时使用的配置,可以用 -`kubectl edit node $ {NODE_NAME}` 命令编辑节点,并删除 `node.spec.configSource` -字段。 - - -#### 观察节点正在使用本地默认配置 - -在删除此字段后,`node.status.config` 最终变成空,所有配置源都已重置为 `nil`。 -这表示本地默认配置成为了 `assigned`、`active` 和 `lastKnownGood` 配置, -并且没有报告错误。 - - - - -## `kubectl patch` 示例 - -你可以使用几种不同的机制来更改节点的 configSource。 - -本例使用`kubectl patch`: - -```bash -kubectl patch node ${NODE_NAME} -p "{\"spec\":{\"configSource\":{\"configMap\":{\"name\":\"${CONFIG_MAP_NAME}\",\"namespace\":\"kube-system\",\"kubeletConfigKey\":\"kubelet\"}}}}" -``` - -## 了解 Kubelet 如何为配置生成检查点 - -当为节点赋予新配置时,kubelet 会下载并解压配置负载为本地磁盘上的一组文件。 -kubelet 还记录一些元数据,用以在本地跟踪已赋予的和最近已知良好的配置源,以便 -kubelet 在重新启动时知道使用哪个配置,即使 API 服务器变为不可用。 -在为配置信息和相关元数据生成检查点之后,如果检测到已赋予的配置发生改变,则 kubelet 退出。 -当 kubelet 被 OS 级服务管理器(例如 `systemd`)重新启动时,它会读取新的元数据并使用新配置。 - - -当记录的元数据已被完全解析时,意味着它包含选择一个指定的配置版本所需的所有信息 --- 通常是 `UID` 和 `ResourceVersion`。 -这与 `node.spec.configSource` 形成对比,后者通过幂等的 `namespace/name` 声明来标识 -目标 ConfigMap;kubelet 尝试使用此 ConfigMap 的最新版本。 - - -当你在调试节点上问题时,可以检查 kubelet 的配置元数据和检查点。kubelet 的检查点目录结构是: - - - -```none -- --dynamic-config-dir (用于管理动态配置的根目录) -|-- meta - | - assigned (编码后的 kubeletconfig/v1beta1.SerializedNodeConfigSource 对象,对应赋予的配置) - | - last-known-good (编码后的 kubeletconfig/v1beta1.SerializedNodeConfigSource 对象,对应最近已知可用配置) -| - checkpoints - | - uid1 (用 uid1 来标识的对象版本目录) - | - resourceVersion1 (uid1 对象 resourceVersion1 版本下所有解压文件的目录) - | - ... - | - ... -``` - - -## 理解 `Node.Status.Config.Error` 消息 {#understanding-node-config-status-errors} - -下表描述了使用动态 kubelet 配置时可能发生的错误消息。 -你可以在 kubelet 日志中搜索相同的文本来获取有关错误的其他详细信息和上下文。 - - - -{{< table caption = "理解 node.status.config.error 消息" >}} - 错误信息 | 可能的原因 -:----------------| :---------------- -failed to load config, see Kubelet log for details | kubelet 可能无法解析下载配置的有效负载,或者当尝试从磁盘中加载有效负载时,遇到文件系统错误。 -failed to validate config, see Kubelet log for details | 有效负载中的配置,与命令行标志所产生的覆盖配置以及特行门控的组合、配置文件本身、远程负载被 kubelet 判定为无效。 -invalid NodeConfigSource, exactly one subfield must be non-nil, but all were nil | 由于 API 服务器负责对 node.spec.configSource 执行验证,检查其中是否包含至少一个非空子字段,这个消息可能意味着 kubelet 比 API 服务器版本低,因而无法识别更新的源类型。 -failed to sync: failed to download config, see Kubelet log for details | kubelet 无法下载配置数据。可能是 node.spec.configSource 无法解析为具体的 API 对象,或者网络错误破坏了下载。处于此错误状态时,kubelet 将重新尝试下载。 -failed to sync: internal failure, see Kubelet log for details | kubelet 遇到了一些内部问题,因此无法更新其配置。 例如:发生文件系统错误或无法从内部缓存中读取对象。 -internal failure, see Kubelet log for details | 在对配置进行同步的循环之外操作配置时,kubelet 遇到了一些内部问题。 - -{{< /table >}} - -## {{% heading "whatsnext" %}} - - -- [使用配置文件设置 kubelet 参数](/zh/docs/tasks/administer-cluster/kubelet-config-file)说明了配置 kubelet 的方法。 -- 阅读 Node 的参考文档,包括 [.spec](/docs/reference/kubernetes-api/cluster-resources/node-v1/#NodeSpec) 里的 `configSource` 字段 -- 查阅[`KubeletConfiguration`](/docs/reference/config-api/kubelet-config.v1beta1/)文献进一步了解 kubelet - 配置信息。 \ No newline at end of file +请注意,从 v1.24 开始 `DynamicKubeletConfig` 特性门控无法在 kubelet 上设置, +因为不会生效。在 v1.26 之前 API 服务器和控制器管理器不会移除该特性门控。 +这是专为控制面支持有旧版本 kubelet 的节点以及满足 [Kubernetes 版本偏差策略](/releases/version-skew-policy/)。 \ No newline at end of file