--- title: 在实时集群上重新配置节点的 Kubelet content_template: templates/task --- {{% capture overview %}} {{< feature-state for_k8s_version="v1.11" state="beta" >}} [动态Kubelet配置](https://github.com/kubernetes/enhancements/issues/281) 引导你在一个运行的 Kubernetes 集群上更改每一个 Kubelet 的配置,通过部署 ConfigMap 并配置每个节点来使用它。 {{< warning >}} **警告**:所有Kubelet配置参数都可以动态地更改,但这对某些参数来说是不安全的。在决定动态更改参数之前,你需要深刻理解这种变化将如何影响你的集群的行为。 在把一组节点推广到集群范围之前,都要仔细地测试这些节点上的配置变化。与配置相关的建议可以在具体的文件下找到,内联 `KubeletConfiguration` [类型文档](https://github.com/kubernetes/kubernetes/blob/release-1.11/pkg/kubelet/apis/kubeletconfig/v1beta1/types.go)。 {{< /warning >}} {{% /capture %}} {{% capture prerequisites %}} - Kubernetes v1.11 或者更高版本配置在主节点和节点上 - kubectl v1.11 或者更高版本和集群配置通信 - The Kubelet `--dynamic-config-dir` flag 必须设置在节点的可写目录上 {{% /capture %}} {{% capture steps %}} ## 在你集群中的一个实时节点上配置Kubelet ### 基本工作流程概述 在实时集群中配置 Kubelet 的基本工作流程如下: 1. 编写一个 YAML 或 JSON 的配置文件包含 Kubelet 的配置。 2. 将此文件包装在 ConfigMap 中并将其保存到 Kubernetes 控制平面。 3. 更新 Kubelet 的相应节点对象以使用此 ConfigMap。 每个 Kubelet 都会在其各自的节点对象上查看配置引用。当此引用更改时,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 *有可能* 会改变配置信息,这样会导致所有 Kubelets 所配置的 ConfigMap 同步更新。 它是更安全的去处理 ConfigMap 按照惯例不变,借助于 `kubectl`'s `--append-hash` 选项,并逐步把更新推广到 `Node.Spec.ConfigSource`。 {{< /warning >}} ### 节点授权器的自动RBAC规则 以前,您需要手动创建RBAC规则允许节点访问其分配的ConfigMaps。 节点授权器现在 自动地配置这些规则。 ### 生成包含当前配置的文件 动态 Kubelet 配置特性允许您提供覆盖对于整个配置对象,而不是每个字段的叠加。 这是一个 更简单的模型,可以更轻松地跟踪配置值的来源和调试问题。 然而,妥协是你必须从现有配置的认识开始, 以确保您只更改您打算修改的字段。 理想情况下,Kubelet 将被引导从磁盘上的一个文件,并且你可以编辑这个文件(也可以是版本控制的), 去创建第一个 Kubelet ConfigMap (参考文档 [通过配置文件设置Kubelet参数](/docs/tasks/administer-cluster/kubelet-config-file)), 目前,Kubelet 使用 **文件和命令行标志的组合** 进行引导来覆盖文件中的配置。 作为解决方法,您可以生成一个配置文件包含节点当前的组态,通过 kubectl 代理访问Kubelet服务器的 `configz` 端点。 该端点在其当前实现中,旨在被用来作为一个调试辅助工具。在生产环境下,不要依赖此端点的特性。 下面的例子使用 `jq` 命令来简化使用 JSON。按照所写的步骤,您需要安装 `jq` , 但如果您喜欢手动提取 `kubeletconfig` 子对象,您也可以完成这个任务。 #### 生成配置文件 1. 选择一个节点去重新配置,在此示例中,此节点的名称为 `NODE_NAME`。 2. 使用以下命令在后台启动 kubectl 代理: ```bash 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 >}} **注意**:You need to manually add the `kind` and `apiVersion` to the downloaded object,because they are not reported by the `configz` endpoint。 您需要手动将 `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 ``` 这是一个有效响应的例子: ```none 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: | {...} ``` ConfigMap 是在 `kube-system` 命名空间中创建的,因为 ConfigMap 配置了 Kubelet,它是 Kubernetes 的系统组件。 `--append-hash` 选项给 ConfigMap 内容附加了一个简短校验和。这对于编辑然后推送工作流程很方便,因为它 自动并确定地为新 ConfigMaps 生成新的名称。在以下示例中,包含生成的哈希名称为 `CONFIG_MAP_NAME`。 #### 用新配置创建新的节点 用下面的命令行编辑节点的参数来指向新的 ConfigMap: ```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 上的哪个 key 包含了 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` 中分配了无效的配置。 如果用本地的配置部署节点,使其设置成默认值,这个`lastKnownGood`配置可能不存在。 在 Kubelet 配置好后,将更新 `lastKnownGood` 去匹配一个有效的 `assigned` 配置。 判断如何配置 Kubelet 的细节是使`lastKnownGood`不受 API 限制,但目前实施为 10 分钟的宽限期。 您可以使用以下命令(using `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" } } } ``` 如果发生错误,Kubelet 会在 `Node.Status.Config.Error` 中显示出它的结构体。可能的错误列在 [了解节点配置错误信息](#了解节点配置错误信息)。 您可以在 Kubelet 日志中搜索相同的文本以获取更多详细信息和有关错误的上下文。 #### 做出更多的改变 按照下面的工作流程做出更多的改变并再次推送它们。你每次推送一个 ConfigMap 的新内容时,--append-hash kubectl 选项都会给 ConfigMap 创建一个新的名称。 最安全的推出策略是首先创建一个新的 ConfigMap,然后更新 节点 以使用新的 ConfigMap。 #### 重置节点以使用其本地默认配置 重置节点去使用已经配好的的配置,用 `kubectl edit node $ {NODE_NAME}` 命令编辑节点,并删除 `Node.Spec.ConfigSource` 字段。 #### 监察正在使用本地默认配置的节点 在删除此字段后,`Node.Status.Config` 最终变成空,所有配置源都已重置为 `nil`,这表示 本地默认配置是`assigned`,`active` 和 `lastKnownGood`这三个参数,没有报告错误。 {{% /capture %}} {{% capture discussion %}} ## Kubectl 补丁示例 您可以使用几种不同的机制来更改节点的 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 (root for managing dynamic config) | - meta | - assigned (encoded kubeletconfig/v1beta1.SerializedNodeConfigSource object, indicating the assigned config) | - last-known-good (encoded kubeletconfig/v1beta1.SerializedNodeConfigSource object, indicating the last-known-good config) | - checkpoints | - uid1 (dir for versions of object identified by uid1) | - resourceVersion1 (dir for unpacked files from resourceVersion1 of object with uid1) | - ... | - ... ``` ## 了解节点配置错误信息 下表描述了使用 Dynamic Kubelet 配置时可能发生的错误消息。您可以在 Kubelet 日志中搜索相同的文本 来获取有关错误的其他详细信息和上下文。
错误信息 可能的原因

无法加载配置,请参阅 Kubelet 日志了解详细信息

Kubelet可能无法解析下载配置的有效负载,或者当尝试从磁盘中加载有效负载时,遇到文件系统错误。

无法验证配置,请参阅 Kubelet 日志了解详细信息

有效负载中的配置将任何命令行标志所覆盖的和这些标志的特性们合并,包括配置文件和远程有效负载, 它们一起被 Kubelet 确定为无效。

无效的 NodeConfigSource,理应刚好一个子字段必须是非空的,但这些字段都是空的

由 API 服务器验证 Node.Spec.ConfigSource 是否包含至少一个非空子字段,可能原因是 Kubelet 比 API 服务器版本低,不识别更新的源类型。

无法同步:无法下载配置,请参阅 Kubelet 日志了解详细信息

Kubelet 无法下载配置。可能是 Node.Spec.ConfigSource 无法解析为具体的 API 对象,或者网络错误中断了下载。 当处于此错误状态时,Kubelet 将重新下载。

无法同步:内部故障,请参阅 Kubelet 日志了解详细信息

Kubelet遇到了一些内部问题,因此无法更新其配置。 例如包括文件系统错误和从内部函数缓存中读取对象。

内部故障,请参阅 Kubelet 日志了解详细信息

在配置同步循环之外操作配置时,Kubelet 遇到了一些内部问题。

{{% /capture %}}