k8s搭建有状态应用consul
k8s搭建有状态应用consul
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}. pod−name.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>.
怎么被外部访问,后续章节再进行说明。
更多推荐
所有评论(0)