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