k8s:DaemonSet 与 StatefulSet 的 使用

DaemonSet 与 StatefulSet 的使用

DaemonSet 的使用
通过该控制器的名称我们可以看出它的用法:Daemon,就是用来部署守护进程的,DaemonSet 用于在每个Kubernetes 节点中将守护进程的副本作为后台进程运行,说白了就是在每个节点部署一个Pod 副本,当节点加入到Kubernetes 集群中,Pod 会被调度到该节点上运行,当节点从集群只能够被移除后,该节点上的这个Pod 也会被移除,当然,如果我们删除DaemonSet ,所有和这个对象相关的Pods都会被删除。

在哪种情况下我们会需要用到这种业务场景呢?其实这种场景还是比较普通的,比如:

  1. 集群存储守护程序,如glusterd、ceph要部署在每个节点上以提供持久性存储;
  2. 节点监视守护进程,如Prometheus 监控集群,可以在每个节点上运行一个node-exporter进程来收集监控节点的信息;
  3. 日志收集守护程序,如fluentd或logstash,在每个节点上运行以收集容器的日志

这里需要特别说明的一个就是关于DaemonSet 运行的Pod 的调度问题,正常情况下,Pod 运行在哪个节点上是由 Kubernetes 的调度器策略来决定的,然而,由 DaemonSet 控制器创建的Pod 实际上提前已经确定了在哪个节点上了(Pod 创建时指定了 .spec.nodeName),所以:

  1. DaemonSet 并不关心一个节点的unshedulable 字段,
  2. DaemonSet 可以创建Pod ,即使调度器还没有启动,这点非常重要。
kind: DaemonSet
apiVersion: apps/v1
metadata:
  name: nginx-ds
  labels:
    k8s-app: nginx
spec:
  selector:
    matchLabels:
      k8s-app: nginx
  template:
    metadata:
      labels:
        k8s-app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - name: http
          containerPort: 80
[root@master ~]# kubectl  get pods -o wide
NAME                                      READY   STATUS      RESTARTS   AGE     IP             NODE    NOMINATED NODE   READINESS GATES
nginx-ds-t6ngt                            1/1     Running     0          108s    10.244.1.47    node1   <none>           <none>
nginx-ds-v8td6                            1/1     Running     0          108s    10.244.3.35    node2   <none>           <none>

然后我们可以观察下Pod 被分布到了每个节点上:

StatefulSet 的使用
在学习 StatefulSet这种控制器之前,我们就得先弄明白一个概念:什么是有状态服务?什么是无状态服务?

  1. 无状态服务(Stateless Service):该服务运行的实例不会在本地存储需要持久化的数据,并且多个实例对于同一个请求响应的结果是完全一致的,比如WordPress 实例,我们是不是可以同时启动多个实例,但是我们访问任意一个实例得到的结果都是一样的。因为他唯一需要持久化的数据是存储在MySQL数据库中的,所以我们可以说WordPress 这个应用是无状态服务,但是MySQL数据库就不是了,因为他需要把数据持久化到本地。
  2. 有状态服务(Stateful Service):就和上面的概念是对立的了,该服务运行的实例需要在本地存储持久化数据,比如上面的MySQL数据库,你现在运行在节点A,那么他的数据就存储在节点A上面的,如果这个时候你把该服务迁移到节点B去的话,那么就没有之前的数据了,因为他需要去对应的数据目录里面恢复数据,而此时没有任何数据。

比如我们常见的 WEB 应用,是通过 session来保持用户的登录状态的,如果我们将session持久化到节点上,那么该应用就是一个有状态的服务了,因为我现在登录进来你把我的session持久化到节点A上了,下次我登录的时候可能会将请求路由到节点B上去了,但是节点B上根本就没有我当前的session数据,就会被认为是未登录状态了,这样就导致我前后两次请求得到的结果不一致了。所以一般为了横向扩展,我们都会把这类 WEB 应用改成无状态的服务,怎么改?将 session数据存入一个公共的地方,比如redis里面,是不是就可以了,对于一些客户端请求API 的情况,我们就不使用session来保持用户状态,改成用token也是可以的。

无状态服务利用我们前面的Deployment 或者RC 都可以很好的控制,对应有状态服务,需要考虑的细节就要多很多了,容器化应用程序最困难的任务之一,就是设计有状态分布式组件的部署体系结构。由于无状态组件可能没有预定义的启动顺序、集群要求、点对点 TCP 连接、唯一的网络标识符、正常的启动和终止要求等,因此可以很容易地进行容器化。诸如数据库,大数据分析系统,分布式 key/value 存储可能有复杂的分布式体系结构,都可能会用到上述功能。为此,Kubernetes 引入了StatefulSet资源来支持这种复杂的需求。StatefulSet类似于 ReplicaSet ,但是它可以处理Pod 的启动顺序,为

保留每个Pod 的状态设置唯一标识,同时具有以下功能:

  1. 稳定的、唯一的网络标识符
  2. 稳定的、持久化的存储
  3. 有序的、优雅的部署和缩放
  4. 有序的、优雅的删除和终止
  5. 有序的、自动滚动更新

创建StatefulSet
接下来演示下 StatefulSet对象的使用方法,在开始之前,我们先准备两个1G的存储卷(PV),

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
  labels:
    release: stable
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  hostPath:
    path: /tmp/data

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002
  labels:
    release: stable
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  hostPath:
    path: /tmp/data

[root@master ~]# kubectl  get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                    STORAGECLASS         REASON   AGE
pv001                                      1Gi        RWO            Recycle          Available                                                          28s
pv002                                      1Gi        RWO            Recycle          Available                                                          28s

  1. capacity:容量
  2. accessModes:访问模式
    ReadWriteOnce —— 该volume只能被单个节点以读写的方式映射 RWO
    ReadOnlyMany —— 该volume可以被多个节点以只读方式映射 ROX
    ReadWriteMany —— 该volume只能被多个节点以读写的方式映射 RWX
  3. persistentVolumeReclaimPolicy:回收策略
    Retain:手动回收
    Recycle:需要擦除后才能再使用
    Delete:相关联的存储资产,如AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷都会被删除

然后我们使用 StatefulSet来创建一个 Nginx 的 Pod,对于这种类型的资源,我们一般是通过创建一个Headless Service 类型的服务来暴露服务,将 clusterIP 设置为None就是一个无头的服务:(statefulset-demo.yaml)

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
    role: stateful
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
       app: nginx
       role: stateful
  template:
    metadata:
      labels:
        app: nginx
        role: stateful
    spec:
      containers:
      - name: nginx
        image: cnych/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
   spec:
     accessModes: [ "ReadWriteOnce"]
     resources:
       requests:
         storage: 1Gi

上面的 YAML 文件中和volumeMounts 进行关联的是一个新的属性:volumeClaimTemplates,该属性会自动声明一个 pvc 对象和 pv 进行关联:

检查 Pod 的顺序索引
对于一个拥有 N 个副本的 StatefulSet,Pod 被部署时是按照 {0…N-1}的序号顺序创建的。在第一个终端中我们可以看到如下的一些信息:

[root@master ~]# kubectl get pods -w -l  role=stateful
web-0   0/1     Pending       0          0s
web-0   0/1     Pending       0          0s
web-0   0/1     ContainerCreating   0          0s
web-0   1/1     Running             0          19s
web-1   0/1     Pending             0          1s
web-1   0/1     Pending             0          1s
web-1   0/1     ContainerCreating   0          1s
web-1   1/1     Running             0          19s

请注意在 web-0 Pod 处于 Running 和 Ready 状态后 web-1 Pod 才会被启动。

如同 StatefulSets 概念中所提到的, StatefulSet 中的 Pod 拥有一个具有稳定的、独一无二的身份标志。这个标志基于 StatefulSet 控制器分配给每个Pod 的唯一顺序索引。 Pod 的名称的形式为-。web StatefulSet 拥有两个副本,所以它创建了两个Pod:web-0 和 web-1。

使用稳定的网络身份标识
每个 Pod 都拥有一个基于其顺序索引的稳定的主机名。使用 kubectl exec在每个 Pod 中执行 hostname 。

[root@master ~]# for i in 0 1 ;do kubectl exec web-$i -- hostname;done
web-0
web-1

然后我们使用 kubectl run 运行一个提供 nslookup 命令的容器。通过对Pod 的主机名执行 nslookup,你可以检查他们在集群内部的 DNS 地址。

[root@master ~]# kubectl run -it --image busybox:1.28.4 dns-test --restart=Never --rm sh
If you don't see a command prompt, try pressing enter.
/ # nslookup web-0.nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.3.38 10-244-3-38.pvc-port.default.svc.cluster.local
/ # nslookup web-1.nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.1.50 10-244-1-50.pvc-port.default.svc.cluster.local

删除重建后可以看到Pod 的序号、主机名、SRV条目和记录名称没有改变,但和
Pod 相关联的 IP 地址可能会发生改变。所以说这就是为什么不要在其他应用中使用 StatefulSet 中的 Pod 的 IP 地址进行连接,这点很重要。一般情况下我们直接通过 SRV 记录连接就行:web-0.nginx、web-1.nginx,因为他们是稳定的,并且当你的 Pod 的状态变为 Running 和 Ready 时,你的应用就能够发现它们的地址。

当然 StatefulSet 还拥有其他特性,在实际的项目中,我们还是很少回去直接通过 StatefulSet 来部署我们的有状态服务的,除非你自己能够完全能够hold 住,对于一些特定的服务,我们可能会使用更加高级的 Operator 来部署,比如 etcd-operator、prometheus-operator 等等,这些应用都能够很好的来管理有状态的服务,而不是单纯的使用一个 StatefulSet 来部署一个 Pod就行,因为对于有状态的应用最重要的还是数据恢复、故障转移等等

联级删除
kubectl delete sts web
非联级删除
kubectl delete sts web --cascade=false #独立pod进程无法重建

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值