一、准备知识
headless services一般结合StatefulSet来部署有状态的应用,比如kafka集群,mysql集群,zk集群等,也包括本文要部署的consul集群。
0、consul集群
consul集群的分布式协议算法采用的是raft协议,这意味着必然有leader、follow节点,每个节点对应的状态不一样,涉及到选举过程,各个参与选举或者加入集群的节点地址需要固定或通过类似dns、vip的方式访问到。
这就要求我们部署有状态服务,而不能使用无状态服务。另外需要创建Headless Service。
1、有状态服务
StatefulSet下的Pod有DNS地址,通过解析Pod的DNS可以返回Pod的IP。
StatefulSet会为关联的Pod保持一个不变的Pod Name,格式是{StatefulSet name}-{pod序号}
StatefulSet会为关联的Pod分配一个dnsName。
域名格式是:{pod-name}.{svc}.{namespace}.svc.cluster.local。
2、Headless Services
Headless Services是一种特殊的service,其spec:clusterIP表示为None,这样在实际运行时就不会被分配ClusterIP。也被称为无头服务。
第一种是vip的方式,即虚拟IP,通过虚拟ip的方式绑定到该service代理的某一个pod上。
这种是我们一般实现service的通常方式。
第二种方式是DNS的方式,对于k8s的服务注册与发现机制,不同应用pod之间互访可以通过{svc}.{namespace}.svc.cluster.local进行访问。这个访问类似VIP的方式,后面绑定的是一堆pod地址。
通过负载均衡机制进行访问。如果需要访问到特定的pod,就需要headless机制,此时可以通过 {pod-name}.{svc}.{namespace}.svc.cluster.local的定位到该pod。
比如本文consul的三个pod节点地址见下表,namespace=“default”
pod-name | serviceName | 访问地址 |
---|---|---|
consul-0 | consul | consul-0.consul.default.svc.cluster.local |
consul-1 | consul | consul-1.consul.default.svc.cluster.local |
consul-2 | consul | consul-2.consul.default.svc.cluster.local |
3、数据持久化
对consul节点的数据、配置和日志等进行持久化。
- 搭建NFS
略。
本文使用的NFS搭建在192.168.80.170上,目录是/srv/nfs/disk
- 创建PVC
创建一个名称为defalut-resources的pvc
kind: PersistentVolumeClaim
apiVersion: v1
metadata:name: defalut-resourcesnamespace: default
spec:accessModes:- ReadWriteManyresources:requests:storage: 10GistorageClassName: managed-nfs-storagevolumeMode: Filesystem
- 自动创建PV
上面的创建pvc会自动创建pv,名称为pvc-e5c5a6c9-d083-407a-86d7-25bf26e62ad4,它是一个随机值且唯一。
查看其yaml:
kind: PersistentVolume
apiVersion: v1
metadata:name: pvc-e5c5a6c9-d083-407a-86d7-25bf26e62ad4
spec:capacity:storage: 10Ginfs:server: 192.168.80.170path: >-/srv/nfs/disk/default-defalut-resources-pvc-e5c5a6c9-d083-407a-86d7-25bf26e62ad4accessModes:- ReadWriteManypersistentVolumeReclaimPolicy: DeletestorageClassName: managed-nfs-storagevolumeMode: Filesystem
status:phase: Bound
验证:
在根目录下,会创建一个目录“default-defalut-resources-pvc-e5c5a6c9-d083-407a-86d7-25bf26e62ad4”。
4、反亲和性podAntiAffinity
pod会基于topologyKey进行反亲和,如下述配置表示,标签k8s-app为consul的pod不会被调度到同一个node节点。
affinity:podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: k8s-appoperator: Invalues:- consultopologyKey: kubernetes.io/hostname
这个很有必要,也适用于其他有状态的服务部署。
二、目标
搭建三个server节点的consul集群,对内供Pod上的服务注册,对外提供UI管理。
三、搭建consul集群
1、完整的创建有状态服务yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:name: consulnamespace: default
spec:serviceName: consulreplicas: 3selector:matchLabels:k8s-app: consultemplate: metadata:labels:k8s-app: consulspec:affinity:podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: k8s-appoperator: Invalues:- consultopologyKey: kubernetes.io/hostnameterminationGracePeriodSeconds: 10containers:- name: consulimage: consul:latestimagePullPolicy: IfNotPresentargs:- "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: nfsmountPath: /consul/datasubPathExpr: data/$(PODNAME)- name: nfsmountPath: /consul/configsubPathExpr: config/$(PODNAME)env:- name: PODIPvalueFrom:fieldRef:fieldPath: status.podIP- name: NAMESPACEvalueFrom:fieldRef:fieldPath: metadata.namespace- name: PODNAMEvalueFrom:fieldRef:fieldPath: metadata.nameresources:limits:cpu: "2G"memory: "2Gi"requests:cpu: "200m"memory: "1Gi"lifecycle:preStart:exec:command:- /bin/sh- -c- consul reloadpreStop:exec:command:- /bin/sh- -c- consul leaveports:- containerPort: 8500name: ui-port- containerPort: 8400name: alt-port- containerPort: 53name: udp-port- containerPort: 8443name: https-port- containerPort: 8080name: http-port- containerPort: 8301name: serflan- containerPort: 8302name: serfwan- containerPort: 8600name: consuldns- containerPort: 8300name: servervolumes:- name: nfspersistentVolumeClaim:claimName: defalut-resources
2、创建services
kind: Service
apiVersion: v1
metadata:name: consulnamespace: defaultlabels:app: consul
spec:ports:- name: httpprotocol: TCPport: 8500targetPort: 8500- name: httpsprotocol: TCPport: 8443targetPort: 8443- name: rpcprotocol: TCPport: 8400targetPort: 8400- name: serflan-tcpprotocol: TCPport: 8301targetPort: 8301- name: serflan-udpprotocol: UDPport: 8301targetPort: 8301- name: serfwan-tcpprotocol: TCPport: 8302targetPort: 8302- name: serfwan-udpprotocol: UDPport: 8302targetPort: 8302- name: serverprotocol: TCPport: 8300targetPort: 8300- name: consuldnsprotocol: TCPport: 8600targetPort: 8600selector:k8s-app: consulclusterIP: None---
apiVersion: v1
kind: Service
metadata:name: consul-dnsnamespace: defaultlabels:app: consul
spec:selector:k8s-app: consulports:- name: dns-tcpprotocol: TCPport: 53targetPort: dns-tcp- name: dns-udpprotocol: UDPport: 53targetPort: dns-udp---
apiVersion: v1
kind: Service
metadata:name: consul-uinamespace: defaultlabels:app: consul
spec:selector:k8s-app: consulports:- name: httpport: 80targetPort: 8500
3、创建一个nodeport类型的service
这样,我们可以通过ip地址+端口的方式直接访问consul ui。
kind: Service
apiVersion: v1
metadata:name: consul-ui-ipnamespace: defaultlabels:app: consul
spec:ports:- name: consul-ui-ipprotocol: TCPport: 8500targetPort: 8500selector:k8s-app: consultype: NodePortsessionAffinity: None
四、验证
1、日志
这里以第一个节点为例,从日志里可以看出,node name是 consul-0,consul的版本是1.11.1。我们没有指定datacenter的名称,默认是dc1。
- leader节点是10.43.128.1
- follow节点是10.37.0.3和10.39.0.4
2、命令
- 命名空间是default
// 查看service,注意 headless service
kubectl get svc -n default// 针对consul ui 增加NodePort对外的访问地址
[admin@jenkins]$ kubectl get svc -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
consul ClusterIP None <none> 8500/TCP,8443/TCP,8400/TCP,8301/TCP,8301/UDP,8302/TCP,8302/UDP,8300/TCP,8600/TCP 2d20h
consul-dns ClusterIP 10.107.173.90 <none> 53/TCP,53/UDP 2d19h
consul-ui ClusterIP 10.111.174.132 <none> 80/TCP 2d19hconsul-ui-ip NodePort 10.102.74.43 <none> 8500:30487/TCP 2d17h
// 三个Pod节点启动OK
kubectl get pod -n default[admin@jenkins]$ kubectl get pod -n default
NAME READY STATUS RESTARTS AGE
consul-0 1/1 Running 0 2d17h
consul-1 1/1 Running 0 2d17h
consul-2 1/1 Running 0 2d17h
// 查看consul集群的成员列表
kubectl exec -n default consul-0 -- consul members
kubectl exec -n default consul-1 -- consul members
kubectl exec -n default consul-2 -- consul members[admin@jenkins]$ kubectl exec -n default consul-0 -- consul members
Node Address Status Type Build Protocol DC Partition Segment
consul-0 10.43.128.1:8301 alive server 1.11.1 2 dc1 default <all>
consul-1 10.37.0.3:8301 alive server 1.11.1 2 dc1 default <all>
consul-2 10.39.0.4:8301 alive server 1.11.1 2 dc1 default <all>[admin@jenkins]$ kubectl exec -n default consul-1 -- consul members
Node Address Status Type Build Protocol DC Partition Segment
consul-0 10.43.128.1:8301 alive server 1.11.1 2 dc1 default <all>
consul-1 10.37.0.3:8301 alive server 1.11.1 2 dc1 default <all>
consul-2 10.39.0.4:8301 alive server 1.11.1 2 dc1 default <all>[admin@jenkins]$ kubectl exec -n default consul-2 -- consul members
Node Address Status Type Build Protocol DC Partition Segment
consul-0 10.43.128.1:8301 alive server 1.11.1 2 dc1 default <all>
consul-1 10.37.0.3:8301 alive server 1.11.1 2 dc1 default <all>
consul-2 10.39.0.4:8301 alive server 1.11.1 2 dc1 default <all>
3、consul server node
consul的data和config持久化到本地了。
五、遇到的问题
三个consul节点加入集群失败
-
报错信息:
agent: grpc: addrConn.createTransport failed to connect to {dc1-10.43.128.1:8300 0 consul-0.dc1 }. Err :connection error: desc = “transport: Error while dialing dial tcp ->10.43.128.1:8300: operation was canceled”. Reconnecting… -
解决办法:
# 在启动consul容器的时候,增加以下语句lifecycle:preStart:exec:command:- /bin/sh- -c- consul reloadpreStop:exec:command:- /bin/sh- -c- consul leave
六、对内访问
java服务接入consul示例:
consul_acl_token=xxxxxx
consul_service_ip=10.43.128.1或10.37.0.3或10.39.0.4java -Dspring.cloud.consul.config.acl-token=$consul_acl_token \
-Dspring.cloud.consul.discovery.acl-token=$consul_acl_token \
-Dserver.ip=${LOCAL_IP} -Xms${Xms} -Xmx${Xmx} \
-XX:+HeapDumpOnOutOfMemoryError \
-DCONFIG_SERVICE_HOST=${consul_service_ip} \
-DCONFIG_SERVICE_ENABLED=true -jar xxx.jar
七、UI界面
- 访问地址:http://{ip}:30487
- 查看leader节点和follow节点
至此,三个server节点组成的consul集群就搭建完成了。