本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
每周 Kubernetes 社区环聊笔记 - 2015 年 4 月 10 日
每周,Kubernetes 贡献者社区都会通过 Google Hangouts 进行虚拟会议。我们希望所有感兴趣的人都能了解这个论坛讨论了什么。
议程
- kubectl 工具、滚动更新、部署、命令式命令。
- Downward API / 环境变量替换,以及可能的先决条件/依赖项。
会议纪要
1. kubectl 改进
使其更易于使用,完成滚动更新,引入更高级别的部署概念。
滚动更新
当前
可以通过文件替换一个 RC 为另一个 RC。
没有明确的回滚支持,可以通过对旧版本进行滚动更新来实现。
我们通过 RC 上的注释来跟踪所需的实例数量;回滚情况下无法正常工作,因为不对称。
需要不可变镜像 ID;目前没有与镜像、版本对应的 UUID,所以如果有人在上面推送,你会重新拉取;在 API 服务器中,我们应该将镜像转换为 UUID(尽可能接近边缘)。
最好能自动生成新的 RC,而不是让用户更新它(例如,当更改容器的镜像标签等时;目前需要更改 RC 名称和标签值;可以自动化生成新的 RC)。
将 RC 视为“宠物”而非“牲畜”。
"从 v1 滚动到 v2"(或 v2 滚动到 v1)——对于大多数人来说已经足够了。不关心过去发生了什么。
我们提供 Ansible 可以调用的模块来完成任务。
如何跟踪多个模板;目前我们使用多个 RC。
如果我们有一个部署控制器;部署配置生成运行滚动更新的 pod;触发器是基于级别的镜像仓库更新。
替代短期方案:创建旧 RC 的克隆作为新 RC,调整计数使新 RC 变为旧 RC,反之亦然,将前一个命名的 RC(宠物)降至零,然后使用新模板将其恢复(这与 Borg 进行作业更新的方式非常相似)。
- 如果我们无论如何都想要部署,这样做值得吗?是的,因为我们已经有很多概念了;需要简化。
部署控制器跟踪多个模板,这是滚动更新和金丝雀部署所需的。
创建新对象的唯一原因是将进程从客户端转移到服务器端吗?
可能不需要将其作为 API 对象;应该提供一种非 API 对象,仅在客户端侧的体验。
现在需要一种体验,所以需要在客户端完成,因为对象在 1.0 之前不会落地。
为那些只想使用 RC 的人提供简化的体验。
回滚如何工作:ctrl-c,回滚 v2 到 v1。回滚模式可以在人们脑海中形成。两种回滚:我已经处于稳定状态并想回滚,以及我进行了金丝雀部署,但按下 ctrl-c 后如何删除金丝雀部署(例如新版本失败)。ctrl-c 可能不起作用。删除金丝雀控制器及其 pod。希望有一个命令可以同时删除 pod(有 -- kbectl stop)。不重用名称的理由是:当你向前推进时,你可以停止新的东西,这样就没问题,而如果你替换旧的并创建了一个副本,如果你按下 ctrl-c,你就没有什么可以停止的了。但你可以等到最后再翻转名称,使用命名约定以便了解发生了什么等等。
两种不同的体验:(1)我正在使用版本控制,本周有上周发布版本历史,用两个文件进行滚动更新 -> 创建 v2,??? v1,没有宠物 - 进入版本控制的世界,其中有累积历史记录;(2)命令式 kubectl v1 v2,系统负责细节,这就是我们使用快照模式的地方。
其他命令式命令
run-container(或只用 run):在命令行上指定命令,使其更类似于 docker run;但不支持多容器 pod。
--forever 与否(通过简单命令进行一次性执行)。
希望它能交互式运行 - run -it 并在集群中运行,但您有一个交互式终端与您的进程交互。
命令行参数如何工作。可以说多次 --image。cobra 会支持吗?在 Openshift 中,我们有巧妙的语法来分组参数。不适用于真正的结构化参数。
替代方案:创建 pod;添加容器添加容器...;运行 pod -- 构建,直到“运行 pod”才运行对象。
-- 分隔容器参数。
创建一个 pod,在运行它之前对其进行变异 - 就像初始化器模式一样。
类型发现
如果我们运行一个命令,有时它创建 RC,有时不创建,那么如果用户想要删除他们通过运行创建的任何东西,他们如何知道要删除什么?
bburns 提出一个建议,如果你执行停止、删除等命令,不要指定类型;让 kubectl 自己判断。
替代方案:允许您定义从名称到一组资源类型的别名,例如,删除所有,它将遵循该别名(所有可以表示某个命名空间中的所有内容,或未限定范围等) - 某人明确地将某些内容添加到集合中,而不是像节点那样意外出现。
希望将其扩展到允许工具指定自己的别名(而不仅仅是用户);例如,resize 可以说我可以处理 RC,delete 可以说我可以处理所有东西,等等,这样我们就可以自动完成这些事情,而无需用户指定内容。但需要正确的机制。
resourcebuilder 具有根据我们如何适应目标命令来执行这种扩展的概念。例如,如果你想向 pod 和 RC 添加卷,你需要一些东西来找到 pod 模板并更改它。这其中有搜索部分(删除 nginx -> 你必须弄清楚它们指的是哪个对象),然后命令可以说我有一个 pod,我知道如何处理 pod。
替代启发式方法:如果所有命令的默认目标都是部署会怎样。kubectl run -> 部署。工作量太大,清理现有 CLI 更容易。为此敞开大门。宏对象可以,但要使其工作需要更多工作。最终将需要索引来提高效率。可以更多地依赖 Swagger 来告诉我们类型。
2. paul/向下 API:环境变量替换
- 创建临时环境变量,如字符串,例如 k8s_pod_name,该变量将由系统在对象中替换。
- 允许人们创建引用 k8s 对象字段的环境变量,而无需从容器内部查询 API;在某些情况下,可以从容器内部查询 API(例如传递对象名称、命名空间);例如,sidecar 容器需要这个来从 API 服务器拉取数据。
- 另一个类似的建议:不是像环境变量那样的名称,而是使用类似 JSON-path 的语法来引用对象字段名称;例如,$.metadata.name 引用当前对象的名称,也许有一种语法可以引用相关对象,如 pod 所在的节点。类似 JSON path 语法的优点是它不那么随意。缺点是您只能引用对象的字段。
- 对于两者,如果填充环境变量,那么缺点是字段只有在容器创建时才设置。但这耦合度最低——现成的容器,容器不需要知道如何与 k8s API 对话。将 k8s 概念保留在控制平面中。
- 我们正在趋向于类似 JSON path 的方法。但需要原型或至少更深入的提案进行演示。
- paul:一个变体是,除了 value 字段,环境变量还可以有不同的来源,你可以在其中插入,例如,你用来描述对象字段的语法;另一个来源是描述主机信息的来源。有部分原型。镜像和控制平面之间有清晰的分离。可以将源思想用于卷插件。
- 用例:为 sidecar 容器提供联系 API 服务器的信息。
- 用例:传递唯一标识符或使用 UID 作为唯一标识符。
- clayton:对于更复杂的事情,为每个 pod 提供 rocket 或 gce 元数据服务;大多数容器都希望找到服务的端点。
3. 前提条件/依赖关系
- 当您创建与服务通信的 pod 时,只有当您按正确顺序创建对象时,服务环境变量才会填充。如果使用 DNS,问题会少一些,但有些应用程序很脆弱。如果它们依赖的服务不存在,可能会崩溃,可能需要很长时间才能重新启动。建议设置前提条件,阻止启动 pod,直到它们依赖的对象存在。
- 如果我们要求人们声明他们想要哪些环境变量,或者在 pod、RC 或对象级别设置依赖机制,说明此对象在此其他对象存在之前不会激活,则自动推断。
- 可以使用事件钩子吗?只有应用程序所有者知道其依赖关系或服务何时准备好提供服务。
- 一个建议是使用 pre-start 钩子。另一个是前提条件探测 - pre-start 钩子可以进行探测。当我访问此服务地址或 IP 时是否有任何响应,然后探测失败。可以在 pre-start 钩子中实现。比 post-start 更实用。是 rkt 规范的一部分。有阶段 0、1、2。目前在 docker 中很难做到,在 rocket 中很容易。
- 容器中的 pre-start 钩子:如果使用 pre-start 钩子实现,容器可能会有一个锁,直到满足某些任意条件,这将如何影响 readiness 探测?如果有一个钩子,kubelet 何时运行 readiness/liveness 探测,必须有一些补偿。Systemd 对进程生命周期的各个阶段都有超时。
- 如果我们采用容器的黑盒模型,pre-start 是有意义的;如果容器规范对进程模型更具描述性,就像 systemd 那样,那么 kubelet 是否需要更多地了解进程模型才能做正确的事情?
- 理想情况下,容器内部会发出消息,说明已完成所有预启动操作。systemd 的 sdnotify 就是这样做的。你告诉 systemd 你已完成,它会通知其他依赖项你已启动。
- 但是……有人可以在他们的容器内部实现前提条件。这使得在不更改镜像的情况下更容易调整应用程序。替代方案是只提供一个他们自己如何做的模式,但我们不为他们做。