在K8S上部署rabbitmq集群-有状态服务
★★★建议星标我们★★★
2020年Java原创面试题库连载中
RabbitMQ是目前非常热门的一款消息中间件,不管是互联网大厂还是中小企业都在大量使用。不管你是作为一名合格的开发同学还是运维同学,都有必要对RabbitMQ有所了解。但是本文并不是一篇rabbitmq入门文章,阅读需要有一定基础。
一、RabbitMQ简介
以熟悉的电商场景为例,如果商品服务和订单服务是两个不同的微服务,在下单的过程中订单服务需要调用商品服务进行扣库存操作。按照传统的方式,下单过程要等到调用完毕之后才能返回下单成功,如果网络产生波动等原因使得商品服务扣库存延迟或者失败,会带来较差的用户体验,如果在高并发的场景下,这样的处理显然是不合适的,那怎么进行优化呢?这就需要消息队列登场了。
消息队列提供一个异步通信机制,消息的发送者不必一直等待到消息被成功处理才返回,而是立即返回。消息中间件负责处理网络通信,如果网络连接不可用,消息被暂存于队列当中,当网络畅通的时候在将消息转发给相应的应用程序或者服务,当然前提是这些服务订阅了该队列。如果在商品服务和订单服务之间使用消息中间件,既可以提高并发量,又降低服务之间的耦合度。
RabbitMQ就是这样一款我们苦苦追寻的消息队列。RabbitMQ是一个开源的消息代理的队列服务器,用来通过普通协议在完全不同的应用之间共享数据。
RabbitMQ除了像兔子一样跑的很快以外,还有这些特点:
开源、性能优秀,稳定性保障
提供可靠性消息投递模式、返回模式
与Spring AMQP完美整合,API丰富
集群模式丰富,表达式配置,HA模式,镜像队列模型
保证数据不丢失的前提做到高可靠性、可用性
MQ典型应用场景:
异步处理:把消息放入消息中间件中,等到需要的时候再去处理。
流量削峰:例如秒杀活动,在短时间内访问量急剧增加,使用消息队列,当消息队列满了就拒绝响应,跳转到错误页面,这样就可以使得系统不会因为超负载而崩溃。
日志处理;
应用解耦:假设某个服务A需要给许多个服务(B、C、D)发送消息,当某个服务(例如B)不需要发送消息了,服务A需要改代码再次部署;
当新加入一个服务(服务E)需要服务A的消息的时候,也需要改代码重新部署;另外服务A也要考虑其他服务挂掉,没有收到消息怎么办?
要不要重新发送呢?是不是很麻烦,使用MQ发布订阅模式,服务A只生产消息发送到MQ,B、C、D从MQ中读取消息,需要A的消息就订阅,
不需要了就取消订阅,服务A不再操心其他的事情,使用这种方式可以降低服务或者系统之间的耦合。
二、rabbitmq集群部署
1、版本说明:
因为考虑到较早版本rabbitmq在k8s上的集群部署是使用autocluster插件去调用kubernetes apiserver来获取rabbitmq服务的endpoints,进而获取node节点信息,并自动加入集群,但是现在autocluster已不再更新了,并且只支持3.6.x版本,故而我们放弃了这种方式。
2、部署方式:
在Kubernetes上搭建RabbitMQ有4种部署方法,本文选用hostname模式
IP模式
Pod与server的DNS
Statefulset 与 Headless Service
hostname模式
3、其他说明
后端存储使用ceph,大家也可根据需要对接使用nfs
k8s: v1.18.3
docker: 19.03.12
centos: 7.7
4、rabbitmq集群部署-statefulset和持久化存储
---
#创建命名空间test2
apiVersion: v1
kind: Namespace
metadata:
name: test2
---
#RBAC 权限账号等
apiVersion: v1
kind: ServiceAccount
metadata:
name: rabbitmq
namespace: test2
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: endpoint-reader
namespace: test2
rules:
apiGroups: [""]
resources: ["endpoints"]
verbs: ["get"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: endpoint-reader
namespace: test2
subjects:
kind: ServiceAccount
name: rabbitmq
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: endpoint-reader
#service创建
kind: Service
apiVersion: v1
metadata:
name: rabbitmq-headless
namespace: test2
spec:
clusterIP: None
publishNotReadyAddresses: true
ports:
name: amqp
port: 5672
name: http
port: 15672
selector:
app: rabbitmq
---
kind: Service
apiVersion: v1
metadata:
namespace: test2
name: rabbitmq-service
spec:
ports:
name: http
protocol: TCP
port: 15672
targetPort: 15672
name: amqp
protocol: TCP
port: 5672
targetPort: 5672
selector:
app: rabbitmq
type: NodePort
#Configmap创建 rabbitmq配置文件
apiVersion: v1
kind: ConfigMap
metadata:
name: rabbitmq-config
namespace: test2
data:
enabled_plugins: |
[rabbitmq_management,rabbitmq_peer_discovery_k8s].
#启用插件rabbitmq_management和rabbitmq_peer_discovery_k8s
| :
rabbit_peer_discovery_k8s =
kubernetes.default.svc.cluster.local =
hostname =
30 =
true =
cluster_partition_handling = autoheal
queue_master_locator=min-masters
false =
default_user=admin
default_pass=admin
default_vhost=loan
# 必须设置service_name,否则Pod无法正常启动,这里设置后可以不设置statefulset下env中的K8S_SERVICE_NAME变量
# cluster_formation.k8s.service_name = rabbitmq-headless
# 必须设置hostname_suffix,否则节点不能成为集群
# rabbitmq-headless.test2.svc.cluster.local中test2为namespace名称,按需修改
.rabbitmq-headless.test2.svc.cluster.local =
0 =
2 =
1GB =
2GB =
#statefulset创建(podAntiAffinity反亲和性)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: rabbitmq
namespace: test2
spec:
serviceName: rabbitmq-headless
selector:
matchLabels:
app: rabbitmq #在apps/v1中,需与 .spec.template.metadata.label 相同,用于hostname传播访问pod
replicas: 3 #副本数3,
template:
metadata:
labels:
app: rabbitmq
annotations:
> :
{
{ :
[{ :
{ :
[{ :
"app", :
"In", :
["rabbitmq"] :
}]
},
"kubernetes.io/hostname" :
}]
}
}
spec:
serviceAccountName: rabbitmq
terminationGracePeriodSeconds: 10
containers:
name: rabbitmq
image: rabbitmq:3.8
resources:
limits:
cpu: 2
memory: 2Gi
requests:
cpu: 1
memory: 1Gi
volumeMounts:
name: config-volume
mountPath: /etc/rabbitmq
name: rabbitmq-data
mountPath: /var/lib/rabbitmq/mnesia
ports:
name: http
protocol: TCP
containerPort: 15672
name: amqp
protocol: TCP
containerPort: 5672
livenessProbe:
exec:
command: ["rabbitmqctl", "status"]
initialDelaySeconds: 60
periodSeconds: 60
timeoutSeconds: 5
readinessProbe:
exec:
command: ["rabbitmqctl", "status"]
initialDelaySeconds: 20
periodSeconds: 60
timeoutSeconds: 5
imagePullPolicy: Always
env:
name: HOSTNAME
valueFrom:
fieldRef:
fieldPath: metadata.name
name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
name: RABBITMQ_USE_LONGNAME
value: "true"
name: K8S_SERVICE_NAME
value: "rabbitmq-headless"
name: RABBITMQ_NODENAME
value: rabbit@$(HOSTNAME).$(K8S_SERVICE_NAME).$(MY_POD_NAMESPACE).svc.cluster.local
name: RABBITMQ_ERLANG_COOKIE
value: "sccookie"
volumes:
name: config-volume
configMap:
name: rabbitmq-config
items:
key: rabbitmq.conf
path: rabbitmq.conf
key: enabled_plugins
path: enabled_plugins
volumeClaimTemplates:
metadata:
name: rabbitmq-data
spec:
storageClassName: "xsky-rbd"
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 5Gi
[root@worker01 rmq]
namespace/test1 unchanged
serviceaccount/rabbitmq unchanged
role.rbac.authorization.k8s.io/endpoint-reader unchanged
rolebinding.rbac.authorization.k8s.io/endpoint-reader unchanged
service/rabbitmq-headless unchanged
service/rabbitmq-service unchanged
configmap/rabbitmq-config unchanged
statefulset.apps/rabbitmq configured
设置的web登录默认账号密码和vhost如下
default_user=admin
default_pass=admin
default_vhost=test
通过暴露的nodeport端口访问验证:http://node-ip:nodeport,如下
当然我们也可以创建ingress入口
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: rabbitmq
namespace: test1
spec:
rules:
host: rabbitmq.domain.test
http:
paths:
backend:
serviceName: rabbitmq-service
servicePort: 15672
之前,给大家发过三份Java面试宝典,这次新增了一份,目前总共是四份面试宝典,相信在跳槽前一个月按照面试宝典准备准备,基本没大问题。
《java面试宝典5.0》(初中级)
《350道Java面试题:整理自100+公司》(中高级)
《资深java面试宝典-视频版》(资深)
《Java[BAT]面试必备》(资深)
分别适用于初中级,中高级,资深级工程师的面试复习。
内容包含java基础、javaweb、mysql性能优化、JVM、锁、百万并发、消息队列,高性能缓存、反射、Spring全家桶原理、微服务、Zookeeper、数据结构、限流熔断降级等等。
获取方式:点“在看”,V信关注上述Java最全面试题库号并回复 【面试】即可领取,更多精彩陆续奉上。
看到这里,证明有所收获
必须点个在看支持呀,喵