1.stateful解决有状态应用的核心

  • 拓扑状态
    应用的多个实例并不完全对等,必须按照某种顺序进行启动。
  • 存储状态
    应用的数据状态需要被记录,即使发生重新创建等操作后,仍能维持原有的存储状态。

2.consul有状态应用部署在k8s的流程步骤

consul集群的分布式协议算法采用的是raft协议,这意味着必然有leader、follow节点,每个节点对应的状态不一样,涉及到选举过程,各个参与选举或者加入集群的节点地址需要固定或通过类似dns、vip的方式访问到。要解决访问可达的问题,就需要用到headless Service

2.1 k8s中service怎么被访问的。
  • 第一种是vip的方式,即虚拟IP,通过虚拟ip的方式绑定到该service代理的某一个pod上。

  • 第二种方式是DNS的方式,对于k8s的服务注册与发现机制,不同应用pod之间互访可以通过 s v c . {svc}. svc.{namespace}.svc.cluster.local进行访问。这个访问类似VIP的方式,后面绑定的是一堆pod地址。通过负载均衡机制进行访问。如果需要访问到特定的pod,就需要headless机制,此时可以通过 p o d − n a m e . s v c . {pod-name}.{svc}. podname.svc.{namespace}.svc.cluster.local的定位到该pod。

2.2 headless service创建
kind: Service
apiVersion: v1
metadata:
  name: consul
  namespace: default
  labels:
    name: consul
spec:
  ports:
    - name: http
      protocol: TCP
      port: 8500
      targetPort: 8500
    - name: https
      protocol: TCP
      port: 8443
      targetPort: 8443
    - name: rpc
      protocol: TCP
      port: 8400
      targetPort: 8400
    - name: serflan-tcp
      protocol: TCP
      port: 8301
      targetPort: 8301
    - name: serflan-udp
      protocol: UDP
      port: 8301
      targetPort: 8301
    - name: serfwan-tcp
      protocol: TCP
      port: 8302
      targetPort: 8302
    - name: serfwan-udp
      protocol: UDP
      port: 8302
      targetPort: 8302
    - name: server
      protocol: TCP
      port: 8300
      targetPort: 8300
    - name: consuldns
      protocol: TCP
      port: 8600
      targetPort: 8600
  selector:
    k8s-app: consul
  clusterIP: None

headless service本质上也是一个标准service的yaml文件,关键点在于clusterIP: None配置,这意味着该service无法通过类似VIP的方式进行访问,而是通过DNS的方式暴露代理的具体的pod。service里面还有一个配置项需要注意,selector意味着该选择器选中的pod都会被service代理。通过headless service解决了pod访问的问题。

2.3 pv与pvc创建

有状态应用除了访问可达的问题外,存储状态的解决也是重要的一个环节。
以consul为例,consul支持配置中心的能力,配置key-value信息后需要落盘持久化,容器是无状态应用,应用重启后,该容器的磁盘数据会被清空。意味着consul挂了,被k8s重新拉起后,该配置信息就丢失了。很多时候在k8s通过hostpath把数据持久化到宿主机,但pod节点飘到哪个节点并不可控,所以一般需要额外的数据存储介质来对这部分状态进行保存。一般情况下,公司会有相关的存储平台,这里通过NFS(文件共享存储),来实现consul的持久化存储。
在服务器172.253.67.226搭建了NFS,共享目录/home/sharedata,NFS的搭建比较简单,这里就不做详细说明。

  • pvc创建

PVC可以理解为持久化存储的接口,它描述了持久化存储相关的属性,但不做具体实现。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: nfs
spec:
 storageClassName: manual
 resources:
   requests:
     storage: 1Gi
 capacity:
   storage: 1Gi
 accessModes: 
   - ReadWriteMany
  • pv创建

PV可以认为是pvc的存储实现部分。

apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs
spec:
storageClassName: manual
capacity:
  storage: 1Gi
accessModes: 
  - ReadWriteMany
nfs:
  server: 172.253.67.226
  path: /home/sharedata

PV与PVC的绑定是通过storageClassName。

2.4 StatefulSet创建
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: consul
spec:
  serviceName: consul
  replicas: 3
  selector:
    matchLabels:
      k8s-app: consul
  template: 
    metadata:
      labels:
        k8s-app: consul
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: k8s-app
                    operator: In
                    values:
                      - consul
              topologyKey: kubernetes.io/hostname
      terminationGracePeriodSeconds: 10
      containers:
      - name: consul
        image: consul:latest
        args:
             - "agent"
             - "-server"
             - "-bootstrap-expect=3"
             - "-ui"
             - "-config-file=/consul/config"
             - "-data-dir=/consul/data"
             - "-log-file=/consul/log"
             - "-bind=0.0.0.0"
             - "-client=0.0.0.0"
             - "-advertise=$(PODIP)"
             - "-retry-join=consul-0.consul.$(NAMESPACE).svc.cluster.local"
             - "-retry-join=consul-1.consul.$(NAMESPACE).svc.cluster.local"
             - "-retry-join=consul-2.consul.$(NAMESPACE).svc.cluster.local"
             - "-domain=cluster.local"
             - "-disable-host-node-id"
        volumeMounts:
            - name: nfs
              mountPath: /consul/data
              subPathExpr: data/$(PODNAME)
            - name: nfs
              mountPath: /consul/config
              subPathExpr: config/$(PODNAME)
        env:
            - name: PODIP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
            - name: NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: PODNAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
        ports:
            - containerPort: 8500
              name: ui-port
            - containerPort: 8400
              name: alt-port
            - containerPort: 53
              name: udp-port
            - containerPort: 8443
              name: https-port
            - containerPort: 8080
              name: http-port
            - containerPort: 8301
              name: serflan
            - containerPort: 8302
              name: serfwan
            - containerPort: 8600
              name: consuldns
            - containerPort: 8300
              name: server
      volumes:
        - name: nfs
          persistentVolumeClaim:
            claimName: nfs

核心的逻辑是,StatefulSet的pod名字状态是固定的,以pod名字作为目录名挂载到共享目录,这样pod不管什么原因导致重新创建,原有的存储状态不会被丢失。

有几个关键属性需要解释一下

  • podAntiAffinity,反亲和性,意味着被匹配到的pod会基于topologyKey进行反亲和,如上述配置表示,标签k8s-app为consul的pod不会被调度到同一个node节点。
  • args:-retry-join,这个是作为consul的启动参数一部分,对应的值consul-0.consul.$(NAMESPACE).svc.cluster.local即我们说的headless方式,pod无论飘到哪里,stateful的pod名是固定的,那么就能重新加入到集群。
    通过执行上述yaml文件后,kubectl apply -f *.yaml,此时consul集群已经部署成功了。
    通过执行命令
kubectl get pod consul-0 -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
consul-0   1/1     Running   0          3d    10.244.3.40   k8s-node3   <none>           <none>

执行curl命令

curl 10.244.3.40:8500
得到返回结果,意味着部署已经成功
<a href="/ui/">Moved Permanently</a>.

怎么被外部访问,后续章节再进行说明。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐