kubernetes 之DaemonSet&Job&StatefulSet
DaemonSet
DaemonSet 确保全部(或者一些)Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一 个 Pod。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod 使用 DaemonSet 的一些典型用法:
运行集群存储 daemon,例如在每个 Node 上运行 glusterd 、 ceph 在每个 Node 上运行日志收集
daemon,例如 fluentd 、 logstash 在每个 Node 上运行监控 daemon,例如 Prometheus Node
Exporter、 collectd 、Datadog 代理、New Relic 代理,或 Ganglia gmond
编写 DaemonSet 规约
必需字段和其它所有 Kubernetes 配置一样,DaemonSet 需要 apiVersion、kind 和 metadata 字段。
有关配置文件的基本信息,详见文档 deploying applications、配置容器 和 资源管理 。
DaemonSet 也需要一个 .spec 配置段。
.spec 唯一必需的字段是 .spec.template。
.spec.template 是一个 Pod 模板。 它与 Pod 具有相同的 schema,除了它是嵌套的,而且不具有
apiVersion 或 kind 字段。
除了 Pod 必需字段外,在 DaemonSet 中的 Pod 模板必须指定合理的标签(查看 Pod Selector)。
在 DaemonSet 中的 Pod 模板必须具有一个值为 Always 的 RestartPolicy,或者未指定它的值,默认是 Always。
Pod Selector
.spec.selector 字段表示 Pod Selector,它与 Job 或其它资源的 .spec.selector 的作用是相同的。
spec.selector 表示一个对象,它由如下两个字段组成:
matchLabels - 与 ReplicationController 的 .spec.selector 的作用相同。
matchExpressions - 允许构建更加复杂的 Selector,可以通过指定 key、value 列表,以及与 key 和 value 列表相关的操作符。
当上述两个字段都指定时,结果表示的是 AND 关系。
-
如果指定了 .spec.selector,必须与 .spec.template.metadata.labels 相匹配。如果没有指定,它们默认是等价的。如果与它们配置的不匹配,则会被 API 拒绝。
-
如果 Pod 的 label 与 selector 匹配,或者直接基于其它的 DaemonSet、或者 Controller(例如 ReplicationController),也不可以创建任何 Pod。 否则 DaemonSet Controller 将认为那些 Pod 是它创建的。Kubernetes 不会阻止这样做。一个场景是,可能希望在一个具有不同值的、用来测试用的节点上手动创建 Pod。
仅在某些节点上运行 Pod -
如果指定了 .spec.template.spec.nodeSelector,DaemonSet Controller 将在能够与 Node Selector 匹配的节点上创建 Pod。 类似这种情况,可以指定 .spec.template.spec.affinity,然后 DaemonSet Controller 将在能够与 Node Affinity 匹配的节点上创建 Pod。 如果根本就没有指定,则 DaemonSet Controller 将在所有节点上创建 Pod。
Daemon pod运行状态
cat daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: deamonset-example
labels:
app: daemonset
spec:
selector:
matchLabels:
name: deamonset-example
template:
metadata:
labels:
name: deamonset-example
spec:
containers:
- name: daemonset-example
image: wangyanglinux/myapp:v3
DaemonSet会忽略Node的unschedulable状态,有两种方式来指定Pod只运行在指定的Node节点上:
nodeSelector:只调度到匹配指定label的Node上
nodeAffinity:功能更丰富的Node选择器,比如支持集合操作
podAffinity:调度到满足条件的Pod所在的Node上
#首先给Node打上标签
kubectl label nodes node-01 disktype=ssd
#然后在daemonset中指定nodeSelector为disktype=ssd:
spec:
nodeSelector:
disktype: ssd
nodeAffinity目前支持两种:
requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution,
分别代表必须满足条件和优选条件。
比如下面的例子代表调度到包含标签kubernetes.io/e2e-az-name并且值为e2e-az1或e2e-az2的Node上,并且优选还带有标签another-node-label-key=another-node-label-value的Node。
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: gcr.io/google_containers/pause:2.0
podAffinity基于Pod的标签来选择Node,仅调度到满足条件Pod所在的Node上,支持podAffinity和podAntiAffinity。这个功能比较绕,以下面的例子为例:
如果一个“Node所在Zone中包含至少一个带有security=S1标签且运行中的Pod”,那么可以调度到该Node
不调度到“包含至少一个带有security=S2标签且运行中Pod”的Node上
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: kubernetes.io/hostname
containers:
- name: with-pod-affinity
image: gcr.io/google_containers/pause:2.0
除了DaemonSet,还可以使用静态Pod来在每台机器上运行指定的Pod,这需要kubelet在启动的时候指定manifest目录:
kubelet --pod-manifest-path=/etc/kubernetes/manifests
然后将所需要的Pod定义文件放到指定的manifest目录中。注意:静态Pod不能通过API Server来删除,但可以通过删除manifest文件来自动删除对应的Pod。
调度 Daemon Pod
正常情况下,Pod 运行在哪个机器上是由 Kubernetes 调度器来选择的。然而,由 Daemon Controller 创建的 Pod已经确定了在哪个机器上(Pod 创建时指定了 .spec.nodeName),因此:
DaemonSet Controller 并不关心一个节点的 unschedulable 字段。
DaemonSet Controller 可以创建 Pod,即使调度器还没有启动,这对集群启动是非常有帮助的。
Daemon Pod 关心 Taint 和 Toleration,它们会为没有指定 tolerationSeconds 的 node.kubernetes.io/not-ready 和 node.alpha.kubernetes.io/unreachable 的 Taint,
创建具有 NoExecute 的 Toleration。这确保了当 alpha 特性的 TaintBasedEvictions 被启用时,发生节点故障,比如网络分区,
这时它们将不会被清除掉(当 TaintBasedEvictions 特性没有启用,在这些场景下也不会被清除,但会因为 NodeController 的硬编码行为而被清除,而不会因为 Toleration 导致被清除)。
与 Daemon Pod 通信
与 DaemonSet 中的 Pod 进行通信,几种可能的模式如下:
通信模式 | |
---|---|
Push | 配置 DaemonSet 中的 Pod 向其它 Service 发送更新,例如统计数据库。它们没有客户端。 |
NodeIP 和已知端口 | DaemonSet 中的 Pod 可以使用 hostPort,从而可以通过节点 IP 访问到 Pod。客户端能通过某种方法知道节点 IP 列表,并且基于此也可以知道端口。 |
DNS | 创建具有相同 Pod Selector 的 Headless Service,然后通过使用 endpoints 资源或从 DNS 检索到多个 A 记录来发现 DaemonSet。 |
Service | 创建具有相同 Pod Selector 的 Service,并使用该 Service 随机访问到某个节点上的 daemon(没有办法访问到特定节点)。 |
更新 DaemonSet
-
如果修改了节点标签(Label),DaemonSet 将立刻向新匹配上的节点添加 Pod,同时删除新近不能够匹配的节点上的 Pod。
-
我们可以修改 DaemonSet 创建的 Pod。然而,不允许对 Pod 的所有字段进行更新。当下次节点(即使具有相同的名称)被创建时,DaemonSet Controller 还会使用最初的模板。
-
可以删除一个 DaemonSet。如果使用 kubectl 并指定 --cascade=false 选项,则 Pod 将被保留在节点上。然后可以创建具有不同模板的新 DaemonSet。具有不同模板的新 DaemonSet 将能够通过标签匹配并识别所有已经存在的 Pod。它不会修改或删除它们,即使是错误匹配了 Pod 模板。通过删除 Pod 或者删除节点,可以强制创建新的 Pod。
在 Kubernetes 1.6 或以后版本,可以在 DaemonSet 上 执行滚动升级。
未来的 Kubernetes 版本将支持节点的可控更新。
DaemonSet 的可替代选择
init 脚本
我们很可能希望直接在一个节点上启动 daemon 进程(例如,使用 init、upstartd、或 systemd)。这非常好,但基于 DaemonSet 来运行这些进程有如下一些好处:
像对待应用程序一样,具备为 daemon 提供监控和管理日志的能力。
为 daemon 和应用程序使用相同的配置语言和工具(如 Pod 模板、kubectl)。
Kubernetes 未来版本可能会支持对 DaemonSet 创建 Pod 与节点升级工作流进行集成。
在资源受限的容器中运行 daemon,能够增加 daemon 和应用容器的隔离性。然而,这也实现了在容器中运行 daemon,但却不能在 Pod 中运行(例如,直接基于 Docker 启动)。
裸 Pod
可能要直接创建 Pod,同时指定其运行在特定的节点上。 然而,DaemonSet 替换了由于任何原因被删除或终止的 Pod,
例如节点失败、例行节点维护、内核升级。由于这个原因,我们应该使用 DaemonSet 而不是单独创建 Pod。
静态 Pod
可能需要通过在一个指定目录下编写文件来创建 Pod,该目录受 Kubelet 所监视。这些 Pod 被称为 静态 Pod。 不像 DaemonSet,静态 Pod 不受 kubectl 和其它 Kubernetes API 客户端管理。
静态 Pod 不依赖于 apiserver,这使得它们在集群启动的情况下非常有用。 而且,未来静态 Pod 可能会被废弃掉。
Replication Controller
DaemonSet 与 Replication Controller 非常类似,它们都能创建 Pod,这些 Pod 对应的进程都不希望被终止掉(例如,Web 服务器、存储服务器)。
为无状态的 Service 使用 Replication Controller,比如前端(Frontend)服务,实现对副本的数量进行扩缩容、平滑升级,比之于精确控制 Pod 运行在某个主机上要重要得多。
需要 Pod 副本总是运行在全部或特定主机上,并需要先于其他 Pod 启动,当这被认为非常重要时,应该使用 Daemon Controller
Job/CronJob
Job
Job创建一个或多个Pod,并确保指定数量的Pod成功终止。Pod成功完成后,Job将跟踪成功完成的情况。当达到指定的成功完成次数时,任务(即Job)就完成了。删除Job将清除其创建的Pod。
一个简单的情况是创建一个Job对象,以便可靠地运行一个Pod来完成。如果第一个Pod发生故障或被删除(例如,由于节点硬件故障或节点重启),则Job对象将启动一个新的Pod。
当然还可以使用Job并行运行多个Pod。
Job完成后,不会再创建其他Pod,但是Pod也不会被删除。这样使我们仍然可以查看已完成容器的日志,以检查是否有错误、警告或其他诊断输出。Job对象在完成后也将保留下来,以便您查看其状态。
当我们删除Job对象时,对应的pod也会被删除。
在Spec中使用.spec.activeDeadlineSeconds来避免这个问题。这个参数定了等待多长时间重试失败的Job。
其他的差不多,Job对象需要apiVersion、kind和metadata字段,同样需要.spec:
Pod Template:.spec.template是.spec必要的字段,是创建Pod的模板,和pod中的模式一样,除了它是嵌套的没有apiVersion或kind,此外还需要Pod的字段,Job中的pod template必须指定合适的标签和重启策略,RestartPolicy只能设置为Never或OnFailure;
Pod Selector:.spec.selector是可选参数,一般不需要指定;
Parallel Jobs:主要有3种类型的task合适作为Job运行:
非并行Job(Non-parallel):正常只有一个Pod启动(除非Pod失败),一旦pod成功终止,Job就完成了;
固定计数的并行Job:为.spec.completions指定一个非0整数,一个Job代表多个task,在1到.spec.completions范围内的每个值都有一个成功的pod时完成;
具有work queue的并行Job:不需要指定.spec.completions(默认为.spec.parallelism),Pod之间自我协调或通过额外的Service决定在哪个Pod上运行,
一个pod可以从work queue中获取一批最多n个元素。每个Pod能够独立决定是否完成了对等的任务,因此整个Job完成。
当Job中的任意一个Pod完成task成功结束,将不会再创建新的Pod,当最后一个Pod结束时,就意味着所有的Pod都停止了,这时Job完成。
对于 non-parallel Job,可以不设置.spec.completions和.spec.parallelism参数(此时它们的默认值为1);
对于 fixed completion count Job,需要设置对应的 .spec.completions,此外也可以设置.spec.parallelism(不设置默认为1);
对于 work queue Job,.spec.completions参数不能设置,.spec.parallelism对应设置为非负整数;
.spec.parallelism表示并行度(默认为1),当指定为0时,Job将停止直到这个数值增加,但实际的并行度可能会和请求的不一样。
在固定计数的并行Job中,实际并行的Pod数量不会超过剩余数量,.spec.parallelism将会被忽略;在work queue的并行Job中在Job完成后将不会在启动新的Pod,但允许剩余Pod执行完成;
此外如果Controller创建Pod失败可能会导实际的Pod比请求的少。
【Pod和Container失败】
如果Pod失败了且.spec.template.spec.restartPolicy = “OnFailure”,Pod将会残留在节点上,但container是会重新运行的,
因此当本地重启可能需要解决这样的问题或者指定.spec.template.spec.restartPolicy = “Never”。对于失败的策略,比如由于配置文件错误的原因在重试过指定次数后直接让整个Job失败,
back-off限制值.spec.backoffLimit默认为6,和Job相关的失败Pod会由Job Controller进行back-off(每次back-off的时间间隔会逐渐增大10s、20s、40sÿ