K8s环境中部署各种开发应用服务(五 )rocketMQ集群

K8s环境中部署各种开发应用服务(五 )rocketMQ集群

准备工作StorageClass与provisioner

StorageClassprovisioner我们这次给rocketMQ服务创建一套新来用

RBAC这套东西还是使用之前那套

首先在NFS服务器的挂载目录下创建一个新的文件夹rocketmq

 mkdir /mnt/data/nfsdata/rocketmq

顺便赋予权限

chmod 777 -R /mnt/data/nfsdata/rocketmq/

Kubernetes Dashboard去导入YAML内容

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-rocketmq-provisioner #这个provisioner的Deployment我们专门给rocketmq使用
  namespace: default
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              topologyKey: kubernetes.io/hostname
              labelSelector:
                matchLabels:
                  app: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: kopkop/nfs-client-provisioner-arm64:latest
          volumeMounts:
          - name: nfs-client-root
            mountPath:  /persistentvolumes
          env:
          - name: PROVISIONER_NAME
            value: nfs-rocketmq-provisioner #此处对应下面StorageClass的provisioner
          - name: NFS_SERVER
            value: 192.168.31.83 #之前部署好的NFS服务端地址
          - name: NFS_PATH
            value: /mnt/data/nfsdata/rocketmq #NFS服务主机挂载到硬盘下的目录 这里给rocketmq创建了一个目录作为rocketmq专用
      volumes:
      - name: nfs-client-root
        nfs:
          server: 192.168.31.83 #NFS服务端地址
          path: /mnt/data/nfsdata/rocketmq #NFS服务主机挂载到硬盘下 这里给rocketmq创建了一个目录作为rocketmq专用
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-rocketmq #这个存储类专门给rocketmq用
provisioner: nfs-rocketmq-provisioner #和上面provisioner的PROVISIONER_NAME内容一致
reclaimPolicy: Retain

准备工作结束

写在开始之前

RocketMQ的服务端主要有两个部分

Broker 也就是 RocketMQServer ,负责中转消息,存储消息,转发消息。这是一个有状态的服务,我们准备的NFS就是为了它

NameServer 可以说是 Broker 的注册中心,Broker启动的时候向所有NameServer进行注册。NameServer 负责监控Broker的存活状态,剔除挂掉的Broker。同时NameServer也担当了负载均衡的角色

部署RocketMQ的方式有几种

2m-noslave 多Master模式,无Slave 双主
2m-2s-sync 多Master多Slave模式,同步双写 双主双从+同步
2m-2s-async 多Master多Slave模式,异步复制 双主双从+异步

我们接下来用的是第二种

一共需要部署6个节点:
4个Broker 双主双从
2个NameServer

另外还有一个console用来可视化操作

有状态服务的迷思

前面我们部署Redis的时候提到过在Kubernetes环境下使用有状态服务的持久化存储,最后用StatefulSet来解决。

但是对于RocketMQ这类应用来讲,除了要解决持久化问题,还需要在节点重生的时候正确配置新Brokerconfig 参数,包括brokerNameNameServer IP List等。这样一来用StatefulSet就费劲了,如果说手动去部署4个固定的StatefulSet来解决的话,又失去使用Kubernetes的意义。

然后,就出现了这样一个好东西 —— Kubernetes Operator

以下内容都是抄来的

实际上 Kubernetes 开发人员也发现了这些问题,因此引入了自定义资源和控制器的概念,让开发人员可以直接用 Go 语言调用 Kubernetes API,编写自定义资源和对应的控制器逻辑来解决复杂有状态应用的管理问题,提供特定应用相关的自定义资源的这类代码组件称之为 Operator。由具备 RocketMQ 领域知识的专家编写 Operator,屏蔽了应用领域的专业知识,让用户只需要关心和定义希望达到的集群终态,这也是 Kubernetes 声明式 API 的设计哲学。

Operator 是在 Kubernetes 基础上通过扩展 Kubernetes API,用来创建、配置和管理复杂的有状态应用,如分布式数据库等。Operator 基于 Kubernetes 1.7 版本以来引入的自定义控制器的概念,在自定义资源和控制器之上构建,同时又包含了应用程序特定的领域知识。实现一个 Operator 的关键是 CRD(自定义资源)和 Controller(控制器)的设计。

Operator 站在 Kubernetes 内部视角,为应用的云原生化打开了新世界的大门。自定义资源可以让开发人员扩展添加新功能,更新现有的功能,并且可以自动执行一些管理任务,这些自定义的控制器就像 Kubernetes 原生的组件一样,Operator 可以直接使用 Kubernetes API 进行开发,也就是说他们可以根据这些控制器编写的自定义规则来创建和更改 Pods / Services、对正在运行的应用进行扩缩容。

简单来讲就是RocketMQ Operator已经准备好了全套我们部署RocketMQ集群所需要的东西。同时在部署完成之后,当节点重生的时候,RocketMQ Operator还会对新的Broker节点进行命名并且自动配置好相应的NameServer IP List

这就非常适合明明是程序员却非要折腾什么原生云同时又并不具备特别丰富的开发运维经验的这类人 (就是我)

RocketMQ Operator 官方页面在这里

RocketMQ Operator

rocketmq-operator 官方git在这里

apache / rocketmq-operator

文档非常详细

正式开始

首先我们先来到树莓派集群的Master节点主机上(零号机)
来从git上拉取整个rocketmq-operator 项目下来

git clone https://github.com/apache/rocketmq-operator.git

接着我们盯着这个画面

wp_editor_md_f71487dd0726b2640f34bdbcb4cb3184.jpg

几分钟过去了 毫无进展 习以为常的出师不利

这个就是众所周知的网络原因了

解决办法可以从国内git源去拉取,但是首先还要传上去,反正很麻烦

所以我们直接下载了这个zip包,然后从本地电脑同步到了零号机里面
wp_editor_md_d24af9497c04df4cb0f9d4f9f05523dd.jpg

同步成功就可以看到了,这时候unzip一下
wp_editor_md_399795fa632f6aee9186387208695da4.jpg

看着文件夹最后的master不顺眼 重命名一下
wp_editor_md_2fe46abc5a72f25589dd25cd267a296c.jpg

然后进入到rocketmq-operator文件夹下
wp_editor_md_8c799d48b8d9e9fe0b0ff4e64a2c1862.jpg

按照官网上说的,这个时候只要 ./install-operator.sh 执行一下就可以完成第一步的 rocketmq-operator 安装了

然而事实并不简单,我们先打开这个install-operator.sh看一眼
image-20210717004025718

可以看到其中的内容就是依次执行了以上YAML的内容

先说明一下红框外的其他文件
他们就是rocketmq-operator操作权限用到的RBAC那一套,这里没有问题

问题就出在了红框内的这个文件中

打开deploy/operator.yaml去看一眼

vi deploy/operator.yaml

image-20210717004152991

定位到 image 这里可以看到下面的镜像
然后来到docker hub上面去看一眼

image-20210717004255767

可以看到这又是一个不支持arm64的镜像

所以直接运行 ./install-operator.sh 就直接报错了

由于目前还没有搭建私有镜像仓库

因此这里我们是直接拉取下来apacherocketmq/rocketmq-operator:0.3.0-snapshot之后本地重新构建成了linux/arm64/v8平台镜像又提交到了docker hub上去

包括后面会遇到的其他几个YAML文件也有同样的问题也是这样处理的
用这里的镜像就可以
https://hub.docker.com/u/weiweiplus

image-20210717004326032

我们先把刚才那个文件中的镜像替换为这个

weiweiplus/rocketmq-operator-arm64:0.3.0-snapshot

image-20210717004350257

保存修改之后就可以去执行 install-operator.sh

cd rocketmq-operator
./install-operator.sh

执行成功之后查看一下pod

kubectl get pods

image-20210717004422208

rocketmq-operator成功运行起来了

接下来我们查看一下example这个文件夹下的内容

cd example/

image-20210717004445551

rocketmq_v1alpha1_rocketmq_cluster.yaml 这个文件的内容就是我们利用刚装好的rocketmq-operator来创建RocketMQ集群用到的YAML

我们可以通过vi编辑这个文件,修改一些配置然后通过kubectl apply -f来执行

kubectl apply -f example/rocketmq_v1alpha1_rocketmq_cluster.yaml

也可以从git上找到这个文件复制下来本地来修改,最后通过Kubernetes Dashboard里面的导入YAML来执行

example/rocketmq_v1alpha1_rocketmq_cluster.yaml

这里需要注意的是除了修改配置相关的内容,还要把镜像地址也改过来

修改后的内容如下

apiVersion: v1
kind: ConfigMap
metadata:
  name: broker-config
  namespace: default
data:
  # BROKER_MEM sets the broker JVM, if set to "" then Xms = Xmx = max(min(1/2 ram, 1024MB), min(1/4 ram, 8GB))
  BROKER_MEM: " -Xms512M -Xmx512M -Xmn128m "
  broker-common.conf: |
    # brokerClusterName, brokerName, brokerId are automatically generated by the operator and do not set it manually!!!
    deleteWhen=04
    fileReservedTime=48
    flushDiskType=ASYNC_FLUSH
    # set brokerRole to ASYNC_MASTER or SYNC_MASTER. DO NOT set to SLAVE because the replica instance will automatically be set!!!
    brokerRole=ASYNC_MASTER
---
apiVersion: rocketmq.apache.org/v1alpha1
kind: Broker
metadata:
  name: broker
  namespace: default
spec:
  # size is the number of the broker cluster, each broker cluster contains a master broker and [replicaPerGroup] replica brokers.
  size: 2
  # nameServers is the [ip:port] list of name service
  nameServers: ""
  # replicaPerGroup is the number of each broker cluster
  replicaPerGroup: 1
  brokerImage: weiweiplus/rocketmq-broker-arm64:4.5.0-alpine-operator-0.3.0
  imagePullPolicy: IfNotPresent
  resources:
    requests:
      memory: "512Mi"
      cpu: "250m"
    limits:
      memory: "1024Mi"
      cpu: "500m"
  allowRestart: true
  # storageMode can be EmptyDir, HostPath, StorageClass
  storageMode: StorageClass
  # hostPath is the local path to store data
  hostPath: /data/rocketmq/broker
  # scalePodName is [Broker name]-[broker group number]-master-0
  scalePodName: broker-0-master-0
  # env defines custom env, e.g. BROKER_MEM
  env:
    - name: BROKER_MEM
      valueFrom:
        configMapKeyRef:
          name: broker-config
          key: BROKER_MEM
  # volumes defines the broker.conf
  volumes:
    - name: broker-config
      configMap:
        name: broker-config
        items:
          - key: broker-common.conf
            path: broker-common.conf
  # volumeClaimTemplates defines the storageClass
  volumeClaimTemplates:
    - metadata:
        name: broker-storage
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: nfs-rocketmq
        resources:
          requests:
            storage: 1Gi
---
apiVersion: rocketmq.apache.org/v1alpha1
kind: NameService
metadata:
  name: name-service
  namespace: default
spec:
  # size is the the name service instance number of the name service cluster
  size: 2
  # nameServiceImage is the customized docker image repo of the RocketMQ name service
  nameServiceImage: weiweiplus/rocketmq-nameserver-arm64:4.5.0-alpine-operator-0.3.0
  # imagePullPolicy is the image pull policy
  imagePullPolicy: IfNotPresent
  # hostNetwork can be true or false
  hostNetwork: true
  #  Set DNS policy for the pod.
  #  Defaults to "ClusterFirst".
  #  Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'.
  #  DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy.
  #  To have DNS options set along with hostNetwork, you have to specify DNS policy
  #  explicitly to 'ClusterFirstWithHostNet'.
  dnsPolicy: ClusterFirstWithHostNet
  # resources describes the compute resource requirements and limits
  resources:
    requests:
      memory: "512Mi"
      cpu: "250m"
    limits:
      memory: "1024Mi"
      cpu: "500m"
  # storageMode can be EmptyDir, HostPath, StorageClass
  storageMode: StorageClass
  # hostPath is the local path to store data
  hostPath: /data/rocketmq/nameserver
  # volumeClaimTemplates defines the storageClass
  volumeClaimTemplates:
    - metadata:
        name: namesrv-storage
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: nfs-rocketmq
        resources:
          requests:
            storage: 1Gi

---

apiVersion: rocketmq.apache.org/v1alpha1
kind: Console
metadata:
  name: console
  namespace: default
spec:
  # nameServers is the [ip:port] list of name service
  nameServers: ""
  # consoleDeployment define the console deployment
  consoleDeployment:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: rocketmq-console
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: rocketmq-console
      template:
        metadata:
          labels:
            app: rocketmq-console
        spec:
          containers:
            - name: console
              image: weiweiplus/rocketmq-console-arm64:4.5.0
              ports:
                - containerPort: 8080

---

apiVersion: v1
kind: Service
metadata:
  name: rmq-console-service
  namespace: default
spec:
  ports:
  - name: rocketmq-console-port
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: rocketmq-console

---

文件的内容首先创建了一个ConfigMap
其中BROKER_MEM: ” -Xms512M -Xmx512M -Xmn128m “是需要注意一下的 如果JVM的Xmx大于当前主机的内存时就会出现OOMkilled的错误导致节点无法启动

接着创建Broker的服务 其中的size指的是集群中有几组主/从的组合,可以理解为有几个。我们需要两组所以size设置为了2
下面的replicaPerGroup说的是每一个组合中有几个,我们要做双主双从,因此每一个主/从组合中只有一个,这里replicaPerGroup设置为了1
到了下面volumeClaimTemplates这里把storageClassName设置为我们文章最开头准备的nfs-rocketmq这个存储类

下面创建NameService服务 这里的size指的就是集群中有几个NameService节点,此处设置为了2
hostNetwork: true这里给NameService节点设置了主机网络,同时配套使用dnsPolicy: ClusterFirstWithHostNet
这样一来我们的NameService就拥有了和树莓派主机同样的集群外部网络服务了
同样下面volumeClaimTemplates这里storageClassName设置为nfs-rocketmq
其实NameService本身应该是无状态服务,这里只是挂载存储了日志文件

Console这里就是部署Deployment用了1个节点提供可视化服务的应用

我们在最后加了一个ClusterIP Service 给上面的Console提供了网络服务

就这些东西了

稍微注意一下每一个服务用到的镜像修改成了arm64的
其他的像是 memory: “512Mi” cpu: “250m” 这种节点上的资源限制根据具体情况调整就好,目前来看树莓派4的8G内存也很吃紧,所以都没敢调的太大。

执行完整个YAML之后,K8s集群就会在RocketMQ Operator的法力加持下完成整个部署

我们可以再顺手创建一个Ingress指向刚才最后创建的那个rmq-console-service

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rmq-console-ingress
  namespace: default
spec:
  defaultBackend:
    service:
      name: rmq-console-service
      port:
        number: 8080
  rules:
  - host: raspi.rocketmq.console
    http:
      paths:
      - backend:
          service:
            name: rmq-console-service
            port:
              number: 8080
        path: /
        pathType: Prefix

本地电脑的hosts 再来设置一下刚刚弄的假域名

192.168.31.82 raspi.rocketmq.console

image-20210717004519706

这样通过访问 raspi.rocketmq.console 就能看到RocketMQ集群的console

image-20210717004539642
image-20210717004604828

Kubernetes Dashboard 查看一下POD
也都成功启动了起来

image-20210717004631493

我们的双主双从rocket MQ 就算是部署成功了 可喜可贺

至于从开发应用中怎么去连接到这个MQ

刚才部署NameService的时候给两个节点开了hostNetwork

从上面POD节点的图里能看到name-service-0name-service-1这两个nameservice的节点地址分别是192.168.31.84192.168.31.83

这就是两台树莓派主机的地址了,加上9876这个端口就可以连接MQ

同时由于这两个节点开启了hostNetwork,他们就拥有了和主机一样的DNS解析

也就是说通过主机名就可以直接在集群内部访问

集群内的SpringBoot的配置可以这样写

rocketmq.name-server=raspberrypi.4:9876;raspberrypi.3:9876
rocketmq.producer.group=producer
rocketmq.producer.send-message-timeout=3000

为了防止NameService重新部署后的主机不固定,我们还可以为NameService的服务再开启一个Service提供集群内部的网络,同时也为几个NameService节点提供了一个固定的统一dns域名,并实现了负载均衡

#集群内访问nameService
apiVersion: v1
kind: Service
metadata:
  name: rmqnamesrv-service
  namespace: default
spec:
  ports:
  - name: main
    port: 9876
    protocol: TCP
    targetPort: 9876
  selector:
    app: name_service

这样一来就可以通过K8s环境的CoreDNS提供的域名来访问到这个服务了,格式如下

rmqnamesrv-service.default.svc

只不过通过rocketmq-operator部署的节点并没有hostnamesubdomain
这可以去找到在上篇文章用StatefulSet创建的redis的那几个pod,打开YAML内容对比一下

image-20210717004700780

所以我们这里直接使用Service的内部域名去访问网络,而不是通过pod域名

集群内的SpringBoot的配置可以这样写

rocketmq.name-server=rmqnamesrv-service.default.svc:9876
rocketmq.producer.group=producer
rocketmq.producer.send-message-timeout=3000

可以用刚才启动的console当做一个跑在集群内的JAVA服务来测试一下域名是否有效果

image-20210717004726769

我们修改了console配置中的环境变量值

重新启动console之后工作一切正常

并且NameServerAddressList这里的内容已经变成rmqnamesrv-service的固定域名了

image-20210717004757577

结束

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注