k8s DaemonSet 介绍与实例
我们之前说k8s中使用deployment、statefulset工作负载资源来分别维护无状态和有状态应用。这篇小作文我们会学习如何使用DaemonSet
来维护一个守护进程(应用)。
一、DaemonSet是什么?
DaemonSet 是一个确保全部或者某些节点上必须运行一个 Pod的工作负载资源(守护进程),当有节点加入集群时, 也会为他们新增一个 Pod。
下面是常用的使用案例:
- 集群守护进程,如
Kured
、node-problem-detector
- 日志收集守护进程,如
fluentd
、logstash
- 监控守护进程,如promethues
node-exporter
通过创建DaemonSet 可以确保 守护进程pod 被调度到每个可用节点上运行。
二、DaemonSet 如何工作?
DaemonSet 是由控制器(controller manager)管理的 Kubernetes 工作资源对象。我们通过声明一个想要的daemonset状态,表明每个节点上都需要有一个特定的 Pod。协调控制回路会比较期望状态和当前观察到的状态。如果观察到的节点没有匹配的 Pod,DaemonSet controller将自动创建一个。可以参考之前《k8s工作流程详解》
在这个过程包括现有节点和所有新创建的节点。不过DaemonSet 控制器创建的 Pod 会被Kubernetes 调度器忽略,即DaemonSet Pods 由 DaemonSet 控制器创建和调度。这样带来的两个微妙的问题:
- Pod 行为的不一致性:正常 Pod 在被创建后等待调度时处于
Pending
状态, DaemonSet Pods 创建后不会处于Pending
状态下。 - Pod 抢占行为由默认调度器处理。启用抢占后,DaemonSet 控制器将在不考虑 Pod 优先级和抢占 的情况下制定调度决策。
所以在k8s v1.12以后DaemonSet Controller 将会向 DaemonSet 的 Pod 添加 .spec.nodeAffinity
字段,而不是 .spec.nodeName
字段,并进一步由 kubernetes 调度器将 Pod 绑定到目标节点。如果 DaemonSet 的 Pod 已经存在了 nodeAffinity
字段,该字段的值将被替换。
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchFields:
- key: metadata.name
operator: In
values:
- target-host-name
daemonset pod的默认容忍规则如下:
DaemonSet 默认在每个节点上创建一个 Pod。当然也可以使用节点选择器来限制可接受节点的数量。DaemonSet 控制器将仅在与 YAML 文件中预定义的nodeSelector字段匹配的节点上创建Pod。我们在下面会使用到。
三、DaemonSet实例
创建DaemonSet
我们只需要将前面deployment中的kind
调整为DaemonSet 就可以创建出一个DaemonSet守护进程
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: my-daemonset
spec:
selector:
matchLabels:
app: my-daemon
template:
metadata:
labels:
app: my-daemon
spec:
containers:
- name: daemonset-container
image: httpd
ports:
- containerPort : 80
通过apply应用后查看资源状态
$ kubectl get daemonset
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
my-daemonset 1 1 1 1 1 <none> 10m
由于我们minikube只有一个node 所以只建立了一个副本,在节点通过get查看到已创建出这个daemonset pod
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
my-daemonset-97z2g 1/1 Running 0 10m
在daemonset资源状态中可以看到NODE SELECTOR
的值为none
,显然我们可以通过在pod模板中添加nodeSelector
使DaemonSet 控制器仅在与Node 选择算符匹配的节点上创建出pod,接下来我们添加一个nodeSelector
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: my-daemonset
spec:
selector:
matchLabels:
app: my-daemon
template:
metadata:
labels:
app: my-daemon
spec:
containers:
- name: daemonset-container
image: httpd
ports:
- containerPort : 80
nodeSelector:
kubernetes.io/hostname: minikube
这样我们的pod只会在hostname为minikube的Node上创建DaemonSet守护进程的pod
$ kubectl get daemonset
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
my-daemonset 1 1 1 1 1 kubernetes.io/hostname=minikube 30m
除了通过nodeSelector
来控制节点调度外,还可以通过上面提到的容忍策略即tolerations
使daemonset pod 调度到“非正常“Node。
我们可以来看一个fluentd
的官方elasticsearch daemonset
源文件地址:fluentd-daemonset-elasticsearch.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-system
labels:
k8s-app: fluentd-logging
version: v1
spec:
selector:
matchLabels:
k8s-app: fluentd-logging
version: v1
template:
metadata:
labels:
k8s-app: fluentd-logging
version: v1
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1-debian-elasticsearch
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch-logging"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
- name: FLUENT_ELASTICSEARCH_SCHEME
value: "http"
# Option to configure elasticsearch plugin with self signed certs
# ================================================================
- name: FLUENT_ELASTICSEARCH_SSL_VERIFY
value: "true"
# Option to configure elasticsearch plugin with tls
# ================================================================
- name: FLUENT_ELASTICSEARCH_SSL_VERSION
value: "TLSv1_2"
# X-Pack Authentication
# =====================
- name: FLUENT_ELASTICSEARCH_USER
value: "elastic"
- name: FLUENT_ELASTICSEARCH_PASSWORD
value: "changeme"
# Logz.io Authentication
# ======================
- name: LOGZIO_TOKEN
value: "ThisIsASuperLongToken"
- name: LOGZIO_LOGTYPE
value: "kubernetes"
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
# When actual pod logs in /var/lib/docker/containers, the following lines should be used.
# - name: dockercontainerlogdirectory
# mountPath: /var/lib/docker/containers
# readOnly: true
# When actual pod logs in /var/log/pods, the following lines should be used.
- name: dockercontainerlogdirectory
mountPath: /var/log/pods
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
# When actual pod logs in /var/lib/docker/containers, the following lines should be used.
# - name: dockercontainerlogdirectory
# hostPath:
# path: /var/lib/docker/containers
# When actual pod logs in /var/log/pods, the following lines should be used.
- name: dockercontainerlogdirectory
hostPath:
path: /var/log/pods
特别之处在于,为了收集master节点上的pod日志,将会容忍fluentd
调度到master节点。其中tolerations
如下
Daemon Pods 通信
与 DaemonSet 中的 Pod 进行通信的几种模式如下:
- 推送(Push):配置 DaemonSet 中的 Pod,将更新发送到另一个服务,例如统计数据库。
- NodeIP 和已知端口:DaemonSet 中的 Pod 可以使用
hostPort
,从而可以通过节点 IP 访问到 Pod。客户端能通过某种方法获取节点 IP 列表,并且基于此也可以获取到相应的端口。比如prometheus的node-exporter。 - DNS:创建具有相同 Pod 选择算符的 无头服务 通过使用
endpoints
资源或从 DNS 中检索到多个 A 记录来发现 DaemonSet。
DaemonSet 更新
如果节点的标签被修改,DaemonSet 将立刻向新匹配上的节点添加 Pod, 同时删除不匹配的节点上的 Pod。
可以删除一个 DaemonSet。如果使用 kubectl
指定 --cascade=orphan
选项, 则 Pod 将被保留在节点上。接下来如果创建使用相同选择算符的新 DaemonSet, 新的 DaemonSet 会收养已有的 Pod。 如果有 Pod 需要被替换,DaemonSet 会根据其 updateStrategy
来替换。
比如prometheus中的node-exporter
以上是关于k8s中的DaemonSet相关内容。
参考:
希望小作文对你有些许帮助,如果内容有误请指正。
您可以随意转载、修改、发布本文,无需经过本人同意。blog:iqsing.github.io