Kubernetes 有状态集 - 将现有 ID 映射到 persistent/stateful pods
Kubernetes Stateful Sets - Mapping existing IDs to persistent/stateful pods
在此先感谢所有提供帮助的人。
你好,我有一个独特的问题,解释起来相当冗长,但我认为如果解决了我们可以扩展 Kubernetes 的用例。我想我知道如何解决它,但我不确定 Kubernetes Stateful Sets 是否支持该解决方案。让我详细说明问题的领域、问题本身,然后是我的一些示例解决方案,也许有人可以帮助填补空白。
领域Space:
- 我有一组帐户(kubernetes 外部){Account_A、Account_B、Account_C 等}
- 帐户可以随时处于活动状态或不活动状态(重要:无特定顺序)。
- 如果激活,将部署一个 pod 为该帐户提供服务,并为所有这些帐户保留一个持久卷 work-space/data。该帐户通过其唯一的 pod 标识符和 IP 进行交互。
- 如果停用,pod 将被删除,但数据仍然存在,以便下次激活时,它将绑定到相同的持久卷声明,因此可以访问其以前的数据。
- 如果重新激活,将重新部署一个 pod,它使用先前的持久卷声明来恢复处理先前会话中的数据
显然,看看可用的 Kubernetes tools/objects,带有无头服务的有状态集是解决这个问题的理想方法。它支持唯一的 pods,它们被分配了唯一的 IP,并支持持久卷。它还支持通过
动态配置持久卷
问题:
如域中所述,帐户可以按任何顺序激活,但有状态集 pods 是顺序的,这意味着 pod_1 必须处于活动状态才能使 pod_2 处于活动状态pod_3 处于活动状态等。我们不能让 pod_1 处于活动状态,而 pod_3 处于活动状态,而 pod_2 处于非活动状态。这意味着如果我启用 Account_A,然后 Account_C,将创建一个名为 pod_1 的 pod,然后将创建一个名为 pod_2 的 pod。
现在你可能会说这不是问题。我们只保留一个映射,将每个帐户映射到相对 pod_number。例如,Account_A -> pod_1 和 Account_C -> pod_2
为什么这是个问题?因为在有状态集中指定 volumeClaimTemplate 时,persistent-volume-claims 在创建时使用 pod 的名称作为它们的标识符。这意味着只有具有相同名称的 pod 才能访问相同的数据。数据(卷)是根据 pod 的名称而不是帐户绑定的。这会在帐户与其持久卷之间造成脱节。任何名称为 pod_2 的 pod 将始终具有与 pod_2 相同的数据,无论哪个帐户是 "mapped" 到 pod_2。
让我用一个例子进一步说明这一点:
1. Account_A=disabled, Account_B=disabled, Account_C=disabled (Start state, all accs disabled)
2. Account_A=enabled, Account_B=enabled, Account_C=enabled -> (All accounts are enabled)
pod_1 is created (with volume_1) and mapped to Account_A
pod_2 is created (with volume_2) and mapped to Account_B
pod_3 is created (with volume_3) and mapped to Account_C
3. Account_A=disabled, Account_B=disabled, Account_C=disabled (All Accounts are disabled)
pod_1 is deleted, volume_1 persists
pod_2 is deleted, volume_2 persists
pod_3 is deleted, volume_3 persists
4. Account_A=enabled, Account_B=disabled, Account_C=enabled (re-enable A and C but leave B disabled)
pod_1 is created (with volume_1) and mapped to Account_A (THIS IS FINE)
pod_2 is created (with **volume_2**) and mapped to Account_C (THIS IS **NOT** FINE)
你能看出问题所在吗? Account_C 现在正在使用应该属于 Account_B 的数据存储(volume_2 是由 account_b 而不是 Account_C 创建和使用的),因为volumes/claims 按名称映射到 pod 名称,pods 必须是序号,即 pod_1 然后 pod_2。
可能的解决方案
能够支持状态集中 pods 的自定义非序数名称。 (最简单最有效)
这解决了所有问题,并保留了 statefulsets 的好处和工具。我可以在启动时为我的 pods 命名,这样当一个帐户被
enabled 我只是启动一个具有该帐户名称的 pod,并且创建的卷映射到任何
具有相同名称的 pod。我已经看过但似乎无法找到执行此操作的方法。
(p.s.) 我知道状态集应该是有序的
保证,但您可以使用 "podManagementPolicy: Parallel"
关闭此功能
有什么方法可以用标签和选择器代替吗?
我是 Kubernetes 的新手,我仍然不完全理解所有的活动部分。也许有一些方法可以在我的 volumeClaim 模板中使用标签,以将卷声明附加到具有特定标签的卷。即 Account_C 映射到 pod_2 可以请求 volume_3 因为 volume_3 有一个标签:account=Account_C。我目前正在调查此事。如果有帮助,我的持久卷是使用此工具动态配置的:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client
也许我可以以某种方式修改它,以便它向它创建的持久卷添加某些标签。
放弃状态集和部署,只需将 pods 手动添加到集群
这不是一个很好的解决方案,因为根据文档,如果没有状态集或部署作为父级,pods 不应该真正存在,并且它还删除了持久卷的所有内置功能和动态卷配置等。对我来说,dealbreaker 没有 volumeClaimTemplates,它在部署时创建或绑定到现有的 volumeClaim。如果我能以某种方式重新创建它,这个解决方案就会起作用。
为我创建自定义 Kubernetes 对象
这是不理想的,因为这将是很多工作,我什至不知道从哪里开始。除了没有序号映射外,我还将重新创建与有状态集完全相同的东西。我将不得不弄清楚如何编写运算符和复制集等。对于一个相当简单的问题来说似乎有点矫枉过正。
- 从 pod 的容器中安装持久存储
这是最后的手段,因为它完全消除了对 kubernetes 的需求。这也意味着我必须将连接信息发送到 pod 中的容器,并在那里打开一整套带有安全和身份验证的蠕虫病毒。
我会更新我发现或想到的任何其他内容。感谢所有帮助过的人。
在我看来,您确信 StatefulSets
是朝着正确方向迈出的一步,但这并不完全正确。
StatefulSets
具有顺序性有两个原因:
- 创建订单
PersistentVolumeClaims
- 能够为个人创建 FQDN 端点 pods(使用无头服务)
在你的情况下,两者似乎都不是真的。每个帐户只需要稳定的存储空间。虽然您认为潜在解决方案中的 #4 最不理想,但这是最 "Kubernetes native" 的方法。
解决方案
您需要编写一个组件来管理每个帐户的 StatefulSet 甚至 Deployment。我说部署是因为您不需要为每个 pod 使用稳定的网络标识符。每个帐户 ClusterIP
服务足以进行通信。
在 Kubernetes 世界中,这些组件称为控制器(没有自定义对象)和运算符(带有自定义 objects/manages 应用程序)。
您可以从查看 operator-sdk and controller-runtime. Operator SDK aggregates commonly used functionalities on top of controller-runtime
as a framework. It also makes developers' life easier by incorporating kubebuilder 开始,它用于为自定义对象生成 CRD 和 K8S API 代码。您需要为您的自定义对象和控制器定义 struct
s。
看看 Operator SDK,您会发现创建和管理自定义对象并不难。
针对您的问题自定义基于对象的流程
这就是我根据我在您的文章中所理解的来想象您的操作员的流程。
- 一个
Account
对象映射到一个帐户。每个对象都有唯一的元数据,将其映射到其帐户。它的规范中也应该有一个 active: boolean
。
- 观察自定义
Account
对象
每当你需要创建一个新帐户时,使用 Kubernetes APIs 创建一个新的 Account
对象(将在控制器中触发一个 Add 事件)然后你的控制器应该
- Create/Update 一个
PersistentVolumeClaim
帐户
- Create/Update
Deployment
在 Pod 模板 中指定创建的卷 PVC
- Catch:当控制器重新启动时,旧对象也会收到添加事件。所以采取的行动应该是"Create or Update".
将自定义对象中的 active
字段设置为 false
以停用帐户(控制器中的修改事件),然后您的控制器应该
- 删除部署而不触及卷。
- 将
active
字段设置为 true
以重新激活帐户。 (再次修改事件)
- 使用 Pod 模板中指定的相同卷重新创建部署
- 删除
Account
对象以清理底层资源。
虽然所有这些可能不会立即变得完美,但我仍然建议您阅读 operator-sdk 的文档和示例。 IMO,这将是朝着正确方向的飞跃。
干杯!
经过几天深入学习运算符后,@Ashu 的回答是解决问题的最佳方法,并打开了 Kubernetes 的大门,可以解决人们可能想要实现的几乎任何场景。
以下是截至 2020 年初对学习算子最有用的资源:
youtube.com/watch?v=8_DaCcRMp5I learn.openshift.com/operatorframework/go-operator-podset
https://itnext.io/a-practical-kubernetes-operator-using-ansible-an-example-d3a9d3674d5b
我强烈建议在尝试创建您自己的运算符之前,全面阅读这两种资源(并同时进行编码)。另外,如果你 "newer" 到 golang,一定要通过 ansible 方法,即使你想创建自己的 golang 运算符。 Ansible 的方法更直观,并且在使用它时可以非常快速地理解概念 'clicked'。
至于 Golang 与 Ansible
Golang:控制力稍强,但复杂度、乏味度和细微差别更高
Ansible:非常直观,以 kubernetes 高级方式解决运算符问题,modular/reusable
此外,#kubernetes-operators 松弛通道非常宝贵
在此先感谢所有提供帮助的人。
你好,我有一个独特的问题,解释起来相当冗长,但我认为如果解决了我们可以扩展 Kubernetes 的用例。我想我知道如何解决它,但我不确定 Kubernetes Stateful Sets 是否支持该解决方案。让我详细说明问题的领域、问题本身,然后是我的一些示例解决方案,也许有人可以帮助填补空白。
领域Space:
- 我有一组帐户(kubernetes 外部){Account_A、Account_B、Account_C 等}
- 帐户可以随时处于活动状态或不活动状态(重要:无特定顺序)。
- 如果激活,将部署一个 pod 为该帐户提供服务,并为所有这些帐户保留一个持久卷 work-space/data。该帐户通过其唯一的 pod 标识符和 IP 进行交互。
- 如果停用,pod 将被删除,但数据仍然存在,以便下次激活时,它将绑定到相同的持久卷声明,因此可以访问其以前的数据。
- 如果重新激活,将重新部署一个 pod,它使用先前的持久卷声明来恢复处理先前会话中的数据
显然,看看可用的 Kubernetes tools/objects,带有无头服务的有状态集是解决这个问题的理想方法。它支持唯一的 pods,它们被分配了唯一的 IP,并支持持久卷。它还支持通过
动态配置持久卷问题:
如域中所述,帐户可以按任何顺序激活,但有状态集 pods 是顺序的,这意味着 pod_1 必须处于活动状态才能使 pod_2 处于活动状态pod_3 处于活动状态等。我们不能让 pod_1 处于活动状态,而 pod_3 处于活动状态,而 pod_2 处于非活动状态。这意味着如果我启用 Account_A,然后 Account_C,将创建一个名为 pod_1 的 pod,然后将创建一个名为 pod_2 的 pod。
现在你可能会说这不是问题。我们只保留一个映射,将每个帐户映射到相对 pod_number。例如,Account_A -> pod_1 和 Account_C -> pod_2
为什么这是个问题?因为在有状态集中指定 volumeClaimTemplate 时,persistent-volume-claims 在创建时使用 pod 的名称作为它们的标识符。这意味着只有具有相同名称的 pod 才能访问相同的数据。数据(卷)是根据 pod 的名称而不是帐户绑定的。这会在帐户与其持久卷之间造成脱节。任何名称为 pod_2 的 pod 将始终具有与 pod_2 相同的数据,无论哪个帐户是 "mapped" 到 pod_2。
让我用一个例子进一步说明这一点:
1. Account_A=disabled, Account_B=disabled, Account_C=disabled (Start state, all accs disabled)
2. Account_A=enabled, Account_B=enabled, Account_C=enabled -> (All accounts are enabled)
pod_1 is created (with volume_1) and mapped to Account_A
pod_2 is created (with volume_2) and mapped to Account_B
pod_3 is created (with volume_3) and mapped to Account_C
3. Account_A=disabled, Account_B=disabled, Account_C=disabled (All Accounts are disabled)
pod_1 is deleted, volume_1 persists
pod_2 is deleted, volume_2 persists
pod_3 is deleted, volume_3 persists
4. Account_A=enabled, Account_B=disabled, Account_C=enabled (re-enable A and C but leave B disabled)
pod_1 is created (with volume_1) and mapped to Account_A (THIS IS FINE)
pod_2 is created (with **volume_2**) and mapped to Account_C (THIS IS **NOT** FINE)
你能看出问题所在吗? Account_C 现在正在使用应该属于 Account_B 的数据存储(volume_2 是由 account_b 而不是 Account_C 创建和使用的),因为volumes/claims 按名称映射到 pod 名称,pods 必须是序号,即 pod_1 然后 pod_2。
可能的解决方案
能够支持状态集中 pods 的自定义非序数名称。 (最简单最有效)
这解决了所有问题,并保留了 statefulsets 的好处和工具。我可以在启动时为我的 pods 命名,这样当一个帐户被 enabled 我只是启动一个具有该帐户名称的 pod,并且创建的卷映射到任何 具有相同名称的 pod。我已经看过但似乎无法找到执行此操作的方法。
(p.s.) 我知道状态集应该是有序的 保证,但您可以使用 "podManagementPolicy: Parallel"
关闭此功能
有什么方法可以用标签和选择器代替吗?
我是 Kubernetes 的新手,我仍然不完全理解所有的活动部分。也许有一些方法可以在我的 volumeClaim 模板中使用标签,以将卷声明附加到具有特定标签的卷。即 Account_C 映射到 pod_2 可以请求 volume_3 因为 volume_3 有一个标签:account=Account_C。我目前正在调查此事。如果有帮助,我的持久卷是使用此工具动态配置的:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client 也许我可以以某种方式修改它,以便它向它创建的持久卷添加某些标签。
放弃状态集和部署,只需将 pods 手动添加到集群
这不是一个很好的解决方案,因为根据文档,如果没有状态集或部署作为父级,pods 不应该真正存在,并且它还删除了持久卷的所有内置功能和动态卷配置等。对我来说,dealbreaker 没有 volumeClaimTemplates,它在部署时创建或绑定到现有的 volumeClaim。如果我能以某种方式重新创建它,这个解决方案就会起作用。
为我创建自定义 Kubernetes 对象
这是不理想的,因为这将是很多工作,我什至不知道从哪里开始。除了没有序号映射外,我还将重新创建与有状态集完全相同的东西。我将不得不弄清楚如何编写运算符和复制集等。对于一个相当简单的问题来说似乎有点矫枉过正。
- 从 pod 的容器中安装持久存储 这是最后的手段,因为它完全消除了对 kubernetes 的需求。这也意味着我必须将连接信息发送到 pod 中的容器,并在那里打开一整套带有安全和身份验证的蠕虫病毒。
我会更新我发现或想到的任何其他内容。感谢所有帮助过的人。
在我看来,您确信 StatefulSets
是朝着正确方向迈出的一步,但这并不完全正确。
StatefulSets
具有顺序性有两个原因:
- 创建订单
PersistentVolumeClaims
- 能够为个人创建 FQDN 端点 pods(使用无头服务)
在你的情况下,两者似乎都不是真的。每个帐户只需要稳定的存储空间。虽然您认为潜在解决方案中的 #4 最不理想,但这是最 "Kubernetes native" 的方法。
解决方案
您需要编写一个组件来管理每个帐户的 StatefulSet 甚至 Deployment。我说部署是因为您不需要为每个 pod 使用稳定的网络标识符。每个帐户 ClusterIP
服务足以进行通信。
在 Kubernetes 世界中,这些组件称为控制器(没有自定义对象)和运算符(带有自定义 objects/manages 应用程序)。
您可以从查看 operator-sdk and controller-runtime. Operator SDK aggregates commonly used functionalities on top of controller-runtime
as a framework. It also makes developers' life easier by incorporating kubebuilder 开始,它用于为自定义对象生成 CRD 和 K8S API 代码。您需要为您的自定义对象和控制器定义 struct
s。
看看 Operator SDK,您会发现创建和管理自定义对象并不难。
针对您的问题自定义基于对象的流程
这就是我根据我在您的文章中所理解的来想象您的操作员的流程。
- 一个
Account
对象映射到一个帐户。每个对象都有唯一的元数据,将其映射到其帐户。它的规范中也应该有一个active: boolean
。 - 观察自定义
Account
对象 每当你需要创建一个新帐户时,使用 Kubernetes APIs 创建一个新的
Account
对象(将在控制器中触发一个 Add 事件)然后你的控制器应该- Create/Update 一个
PersistentVolumeClaim
帐户 - Create/Update
Deployment
在 Pod 模板 中指定创建的卷 - Catch:当控制器重新启动时,旧对象也会收到添加事件。所以采取的行动应该是"Create or Update".
PVC
- Create/Update 一个
将自定义对象中的
active
字段设置为false
以停用帐户(控制器中的修改事件),然后您的控制器应该- 删除部署而不触及卷。
- 将
active
字段设置为true
以重新激活帐户。 (再次修改事件)- 使用 Pod 模板中指定的相同卷重新创建部署
- 删除
Account
对象以清理底层资源。
虽然所有这些可能不会立即变得完美,但我仍然建议您阅读 operator-sdk 的文档和示例。 IMO,这将是朝着正确方向的飞跃。
干杯!
经过几天深入学习运算符后,@Ashu 的回答是解决问题的最佳方法,并打开了 Kubernetes 的大门,可以解决人们可能想要实现的几乎任何场景。
以下是截至 2020 年初对学习算子最有用的资源:
youtube.com/watch?v=8_DaCcRMp5I learn.openshift.com/operatorframework/go-operator-podset https://itnext.io/a-practical-kubernetes-operator-using-ansible-an-example-d3a9d3674d5b
我强烈建议在尝试创建您自己的运算符之前,全面阅读这两种资源(并同时进行编码)。另外,如果你 "newer" 到 golang,一定要通过 ansible 方法,即使你想创建自己的 golang 运算符。 Ansible 的方法更直观,并且在使用它时可以非常快速地理解概念 'clicked'。
至于 Golang 与 Ansible
Golang:控制力稍强,但复杂度、乏味度和细微差别更高
Ansible:非常直观,以 kubernetes 高级方式解决运算符问题,modular/reusable
此外,#kubernetes-operators 松弛通道非常宝贵