Docker 与 Kubernetes 部署 RabbitMQ 集群(一)

发布于:2025-05-24 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、引言

**

{"type":"load_by_key","key":"auto_image_0_0","image_type":"search"}

在当今分布式系统盛行的时代,消息队列作为一种重要的中间件,承担着系统组件间异步通信、解耦以及流量削峰等关键任务 。RabbitMQ 作为消息队列领域的佼佼者,凭借其基于 AMQP 协议实现、支持多种消息模式、具备高可靠性和灵活性等特性,被广泛应用于各类项目中。无论是电商系统中订单处理与库存更新的解耦,还是金融系统中交易信息的可靠传输,RabbitMQ 都发挥着不可或缺的作用。

然而,在实际生产环境中,为了满足高可用性、高性能以及可扩展性的需求,通常需要搭建 RabbitMQ 集群。传统的裸机部署方式不仅繁琐,而且在环境一致性、资源利用率等方面存在诸多问题。随着容器技术和容器编排工具的兴起,使用 Docker 与 Kubernetes 来部署 RabbitMQ 集群成为了一种更高效、便捷的解决方案。Docker 能够将 RabbitMQ 及其依赖打包成一个可移植的容器,实现环境的标准化;而 Kubernetes 则提供了强大的容器编排和管理功能,包括自动部署、扩展、负载均衡以及故障恢复等。接下来,就让我们深入探讨如何使用 Docker 与 Kubernetes 部署 RabbitMQ 集群。

二、基础知识扫盲

(一)RabbitMQ 集群简介

RabbitMQ 集群是由多个 RabbitMQ 节点组成的集合,这些节点通过网络相互连接,协同工作以提供更强大的消息处理能力 。在 RabbitMQ 集群中,主要存在两种集群模式:普通集群和镜像集群。

普通集群模式充分利用了 Erlang 语言天生具备的集群能力。在这种模式下,各个节点共享相同的元数据,如队列结构、交换机和绑定信息等 。然而,消息并不会在所有节点上冗余存储,而是仅存在于某一个节点中。当消费者从某个节点请求消费消息,而该节点并不存储所需消息时,RabbitMQ 会在节点之间临时传输消息,将数据从存储节点传输到消费节点。例如,在一个包含三个节点的普通集群中,队列 A 可能只存储在节点 1 上,当节点 2 上的消费者请求消费队列 A 中的消息时,消息会从节点 1 传输到节点 2。这种模式虽然实现了一定程度的分布式,但存在明显的缺点:消息可靠性较低,当某个节点宕机时,该节点上的数据将无法被消费,必须等待节点恢复后才能继续处理,这可能导致消费者端无法正确应答已经消费的消息,在服务恢复后可能导致消息被重复消费;此外,如果消息未经持久化,重启后消息将会丢失 ;而且该模式不支持高可用性,当某个节点服务故障时,需要手动重启该服务才能确保该节点上的消息能够正常消费 。不过,普通集群模式的优点是实现简单,并且在一定程度上提高了消息的吞吐量,适用于对消息安全性要求不高、对性能和成本较为敏感的场景,如一些日志收集和简单的任务队列场景。

镜像集群模式是 RabbitMQ 官方提供的高可用(HA)方案,是在普通集群模式的基础上进行的增强。其本质区别在于,镜像集群模式会在镜像节点之间主动进行消息同步,而不是在客户端拉取消息时临时同步 。在这种模式下,集群内部会通过算法选举产生主节点(master)和从节点(slave) 。一旦主节点失效,集群将自动选举出新的主节点,确保整个集群的高可用性 。例如,在一个由三个节点组成的镜像集群中,每个节点都会保存相同的消息数据,当主节点发生故障时,从节点会迅速被选举为新的主节点,继续提供服务,不会导致消息丢失或服务中断。镜像集群模式大大提高了消息的可靠性和系统的可用性,即使单个节点宕机,也不会影响消息的处理 。然而,由于需要在节点之间主动同步大量消息数据,会占用较多的网络带宽,可能导致网络拥塞,影响整个集群的性能 ;并且由于每个节点都保存全量数据,对存储资源的需求也更大。因此,镜像集群模式适用于对消息可靠性和高可用性要求极高的场景,如金融交易系统、电商订单处理系统等,这些场景中数据的完整性和服务的连续性至关重要。

(二)Docker 基础入门

Docker 是一个开源的应用容器引擎,它的核心概念包括镜像(Image)和容器(Container) 。镜像是一个只读的模板,它包含了运行容器所需的所有文件、依赖项、配置和应用程序代码,就相当于一个打包好的快照,是容器的基础 。例如,官方的 Nginx 镜像就包含了 Nginx 服务器软件及其运行所需的依赖和配置文件。镜像具有只读性,一旦创建,其内容不会改变;它是静态的,不能直接运行,但包含了创建可运行容器所需的一切 。我们通常可以从 Docker Hub 等公共镜像仓库拉取镜像,也可以通过编写 Dockerfile 自行构建镜像。

容器则是镜像的运行时实例,当通过镜像启动一个容器时,它就变成了一个独立的进程,运行着我们的应用程序 。容器具有可读写的文件系统和分配的资源,它为应用程序提供了独立的执行环境,确保在不同的系统上可以一致运行 。以 Nginx 镜像为例,基于该镜像启动的容器就是一个正在运行的 Nginx 服务器实例,我们可以对其进行启动、停止、重新启动等操作,并且容器在运行时可以创建、修改和删除文件 。容器之间是相互隔离的,每个容器都有自己独立的文件系统、网络接口和进程空间,这保证了应用程序运行环境的安全性和独立性 。

使用 Docker 部署应用具有诸多优势。首先是环境一致性,Docker 将应用及其依赖打包在一起,确保了在开发、测试、生产等不同环境中运行的一致性,避免了 “在我的机器上可以运行,在服务器上就不行” 的尴尬情况 。例如,开发人员在本地开发环境中使用 Docker 容器运行应用,当部署到生产环境时,由于容器内的环境和依赖与开发环境完全一致,应用可以稳定运行。其次是高效的资源利用,Docker 容器共享宿主机的操作系统内核,与传统虚拟机相比,占用的资源更少,启动速度更快 。一个物理机可以轻松运行多个 Docker 容器,提高了硬件资源的利用率。此外,Docker 还具有易于部署和扩展的特点,通过简单的命令就可以快速创建、复制和迁移容器,方便应用的快速部署和水平扩展 。当应用的访问量增加时,可以快速启动更多的容器来分担负载。

(三)Kubernetes 基础入门

Kubernetes(简称 K8s)是一个开源的容器编排和集群管理系统,它的主要功能包括容器化应用的自动化部署、扩展、管理和自愈 。在 Kubernetes 中,有许多重要的组件协同工作,以实现这些强大的功能。

etcd 是 Kubernetes 的核心组件之一,它是一个分布式的键值对存储系统,用于保存整个集群的状态 。例如,集群中各个节点的信息、Pod 的定义和状态、服务的配置等都存储在 etcd 中。etcd 基于 Raft 一致性算法,确保了数据在多个节点之间的一致性和可靠性 ,当某个节点发生故障时,其他节点可以通过 etcd 获取最新的集群状态信息,保证集群的正常运行。

apiserver 是 Kubernetes 提供资源操作的唯一入口,它提供了 RESTful API 接口,用于接收和处理来自用户、其他组件的请求 。apiserver 还负责认证、授权、访问控制、API 注册和发现等机制,确保只有经过授权的用户和组件才能对集群资源进行操作 。例如,当用户使用 kubectl 命令行工具创建一个 Pod 时,实际上是通过 apiserver 将请求发送到集群中,并由 apiserver 对请求进行验证和处理。

controller manager 是 Kubernetes 的大脑,它负责维护集群的状态,通过 apiserver 监控整个集群的运行情况,并确保集群始终处于用户期望的状态 。controller manager 由一系列的控制器组成,如 DeploymentController 用于管理 Deployment 资源,确保 Pod 的副本数量符合预期;ReplicaSetController 负责管理 ReplicaSet,保证 Pod 的数量和健康状态 。当某个 Pod 出现故障时,controller manager 会自动创建新的 Pod 来替换它,实现集群的自愈功能。

scheduler 负责资源的调度,它监听 apiserver,查询还未分配 Node 的 Pod,然后根据预定的调度策略将 Pod 调度到合适的节点上运行 。调度策略可以考虑节点的资源利用率、节点的标签、Pod 的资源需求等因素 。例如,当有一个需要大量 CPU 资源的 Pod 创建时,scheduler 会选择一个 CPU 资源较为充足的节点来运行该 Pod,以确保 Pod 能够获得足够的资源。

kubelet 是运行在每个 Node 节点上的守护进程,它负责维护容器的生命周期,同时也负责存储卷和网络的管理 。kubelet 接收并执行来自 master 节点的指令,管理 Pod 及 Pod 中的容器 。例如,kubelet 会根据 Pod 的定义创建、启动和停止容器,监控容器的运行状态,并向 apiserver 汇报节点和容器的状态信息。

kube-proxy 负责为应用提供集群内部的服务发现和负载均衡 。在 Kubernetes 集群中,每个 Pod 都有一个独立的 IP 地址,当多个 Pod 提供相同的服务时,kube-proxy 可以将客户端的请求转发到合适的 Pod 上,实现负载均衡 。例如,当有一个 Web 应用由多个 Pod 组成时,kube-proxy 可以将用户的 HTTP 请求均匀地分发到各个 Pod 上,提高应用的可用性和性能。

Kubernetes 在容器编排和集群管理方面发挥着关键作用。它可以实现应用的自动化部署,通过定义 Deployment 等资源,Kubernetes 可以自动创建和管理 Pod,实现应用的快速上线 ;在应用需要扩展时,Kubernetes 可以根据用户设定的策略自动增加或减少 Pod 的数量,实现水平扩展 ;当集群中的节点或 Pod 出现故障时,Kubernetes 能够自动检测并进行修复,保证应用的高可用性 ;此外,Kubernetes 还提供了强大的配置管理和服务发现功能,方便用户对应用进行管理和维护 。

三、Docker 部署 RabbitMQ 集群实战

(一)环境准备

在开始使用 Docker 部署 RabbitMQ 集群之前,我们需要确保以下环境和软件已准备就绪:

  • Docker 安装:Docker 是部署的基础,它允许我们将应用程序及其依赖项打包到一个可移植的容器中。在不同的操作系统上安装 Docker 的方法略有不同。

sudo yum update

然后安装必要的依赖包,这些包用于管理 Docker 的存储卷:


sudo yum install -y yum-utils device-mapper-persistent-data lvm2

设置 Docker 的阿里云镜像仓库,以提高镜像拉取速度:


yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

最后安装 Docker 社区版:


sudo yum install docker-ce -y

安装完成后,启动 Docker 服务,并设置开机自启:


sudo systemctl start docker

sudo systemctl enable docker


sudo apt update

安装必要的软件包,用于支持 Docker 的安装和运行:


sudo apt install apt-transport-https ca-certificates curl software-properties-common

添加 Docker 的 GPG 密钥,以确保软件包的来源可靠:


curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

添加 Docker 的官方仓库:


sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

安装 Docker 社区版:


sudo apt install docker-ce -y

同样,启动 Docker 服务并设置开机自启:


sudo systemctl start docker

sudo systemctl enable docker

    • CentOS 系统:在 CentOS 系统上安装 Docker,可以使用以下命令。首先更新系统的 yum 包:
    • Ubuntu 系统:对于 Ubuntu 系统,先更新包索引:
  • RabbitMQ 镜像拉取:在安装好 Docker 后,我们需要拉取 RabbitMQ 的镜像。RabbitMQ 官方提供了多种版本的镜像,我们可以根据项目需求选择合适的版本。通常,我们会选择带有管理界面的镜像,以便于管理和监控 RabbitMQ 集群。拉取命令如下:

docker pull rabbitmq:management

此命令会从 Docker Hub 上拉取最新版本的 RabbitMQ 镜像,其中management标签表示该镜像包含 Web 管理界面,方便我们通过浏览器对 RabbitMQ 进行可视化管理。如果拉取过程中遇到网络问题,可以考虑配置国内的镜像源,如阿里云镜像源,以提高拉取速度。具体配置方法是修改/etc/docker/daemon.json文件,添加如下内容:


{

"registry-mirrors": ["https://pee6w651.mirror.aliyuncs.com"]

}

保存文件后,重新加载 Docker 配置并重启 Docker 服务:


sudo systemctl daemon-reload

sudo systemctl restart docker

然后再次执行拉取镜像的命令即可。

(二)集群搭建步骤

  1. 创建 Docker 网络:为了使多个 RabbitMQ 容器能够相互通信,我们首先需要创建一个 Docker 网络。Docker 网络提供了一个虚拟的网络环境,容器可以在其中相互发现和通信。创建网络的命令如下:

docker network create rabbitmq-cluster

这条命令创建了一个名为rabbitmq-cluster的网络,后续创建的 RabbitMQ 容器将加入这个网络。

2. 启动第一个 RabbitMQ 容器:使用以下命令启动第一个 RabbitMQ 容器,该容器将作为集群的第一个节点:


docker run -d --hostname rabbit1 --name rabbit1 --network rabbitmq-cluster -p 5672:5672 -p 15672:15672 -e RABBITMQ_NODENAME=rabbit@rabbit1 -e RABBITMQ_ERLANG_COOKIE='unique_cookie_name' -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin rabbitmq:management

命令参数解释:

  • -d:表示容器在后台运行。
  • --hostname:设置容器的主机名,这里设置为rabbit1。
  • --name:为容器指定一个名称,方便后续管理,这里名称为rabbit1。
  • --network:指定容器加入的网络,这里是我们刚刚创建的rabbitmq-cluster网络。
  • -p:端口映射,将容器内部的 5672 端口(AMQP 协议端口,用于客户端与 RabbitMQ 通信)映射到宿主机的 5672 端口,将容器内部的 15672 端口(管理界面端口)映射到宿主机的 15672 端口 ,这样我们就可以通过宿主机的 IP 和这些端口来访问 RabbitMQ 服务和管理界面。
  • -e:设置环境变量。RABBITMQ_NODENAME用于设置 RabbitMQ 节点的名称,这里设置为rabbit@rabbit1;RABBITMQ_ERLANG_COOKIE是 Erlang 节点之间通信的认证密钥,所有集群节点必须设置为相同的值,这里设置为unique_cookie_name,实际使用中应替换为一个强密码;RABBITMQ_DEFAULT_USER和RABBITMQ_DEFAULT_PASS分别设置 RabbitMQ 的默认用户名和密码,这里设置为admin。
  1. 启动第二个 RabbitMQ 容器并加入集群:启动第二个 RabbitMQ 容器,并将其加入到集群中:

docker run -d --hostname rabbit2 --name rabbit2 --network rabbitmq-cluster -p 5673:5672 -p 15673:15672 -e RABBITMQ_NODENAME=rabbit@rabbit2 -e RABBITMQ_ERLANG_COOKIE='unique_cookie_name' -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin rabbitmq:management

这个命令与启动第一个容器的命令类似,只是主机名、容器名和映射的端口不同,这里将容器内部端口映射到宿主机的 5673 和 15673 端口,以避免端口冲突。启动容器后,需要通过以下命令将其加入到集群中:


docker exec -it rabbit2 rabbitmqctl stop_app

docker exec -it rabbit2 rabbitmqctl reset

docker exec -it rabbit2 rabbitmqctl join_cluster rabbit@rabbit1

docker exec -it rabbit2 rabbitmqctl start_app

上述命令中:

  • docker exec -it rabbit2 rabbitmqctl stop_app:进入rabbit2容器并停止 RabbitMQ 应用,注意rabbitmqctl stop会将 Erlang 虚拟机关闭,而rabbitmqctl stop_app只关闭 RabbitMQ 服务,这样后续操作不会影响整个 Erlang 环境 。
  • docker exec -it rabbit2 rabbitmqctl reset:重置rabbit2节点,清除其当前的集群和节点状态信息,以便重新加入集群。
  • docker exec -it rabbit2 rabbitmqctl join_cluster rabbit@rabbit1:将rabbit2节点加入到以rabbit@rabbit1为节点名的集群中。
  • docker exec -it rabbit2 rabbitmqctl start_app:重新启动rabbit2节点上的 RabbitMQ 应用,使其在集群中正常工作。
  1. 启动第三个 RabbitMQ 容器并加入集群:同样的方法,启动第三个 RabbitMQ 容器:

docker run -d --hostname rabbit3 --name rabbit3 --network rabbitmq-cluster -p 5674:5672 -p 15674:15672 -e RABBITMQ_NODENAME=rabbit@rabbit3 -e RABBITMQ_ERLANG_COOKIE='unique_cookie_name' -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin rabbitmq:management

并将其加入集群:


docker exec -it rabbit3 rabbitmqctl stop_app

docker exec -it rabbit3 rabbitmqctl reset

docker exec -it rabbit3 rabbitmqctl join_cluster rabbit@rabbit1

docker exec -it rabbit3 rabbitmqctl start_app

通过以上步骤,我们成功启动了三个 RabbitMQ 容器,并将它们组成了一个集群。每个容器都有自己独立的运行环境,但通过 Docker 网络和集群配置,它们能够协同工作,提供高可用性的消息队列服务。

(三)配置与验证

  1. 基本配置:在 RabbitMQ 集群搭建完成后,我们可以对其进行一些基本配置。例如,设置用户名和密码是保障 RabbitMQ 服务安全的重要步骤。我们在启动容器时已经通过环境变量设置了默认的用户名admin和密码admin,但在实际生产环境中,应该设置更复杂、安全的用户名和密码。可以通过 RabbitMQ 的管理界面或命令行进行修改。通过管理界面修改时,先访问http://宿主机IP:15672,使用默认用户名和密码登录后,在管理界面中找到 “Users” 选项卡,点击用户名进入用户详情页面,即可修改密码 。使用命令行修改密码的方法如下,以修改admin用户的密码为例:

docker exec -it rabbit1 rabbitmqctl change_password admin new_password

这里rabbit1是容器名,new_password是要设置的新密码,需要将其替换为实际的强密码。

另外,还可以设置虚拟主机(Virtual Host)。虚拟主机是 RabbitMQ 中的一个逻辑概念,它可以将不同的应用或项目隔离开来,每个虚拟主机都有自己独立的队列、交换器和绑定关系 。创建虚拟主机的命令如下:


docker exec -it rabbit1 rabbitmqctl add_vhost /my_vhost

这将创建一个名为/my_vhost的虚拟主机,实际使用中可以根据项目需求命名。然后可以为虚拟主机添加用户并设置权限,例如为admin用户在/my_vhost虚拟主机上赋予所有权限:


docker exec -it rabbit1 rabbitmqctl set_permissions -p /my_vhost admin ".*" ".*" ".*"

其中-p参数指定虚拟主机,后面三个".*"分别表示配置权限、写权限和读权限,这里赋予了admin用户在该虚拟主机上的所有权限。

2. 验证集群:验证 RabbitMQ 集群是否搭建成功,可以通过以下几种方式:

  • 查看集群状态:在任意一个 RabbitMQ 容器中执行以下命令查看集群状态:

docker exec -it rabbit1 rabbitmqctl cluster_status

如果集群搭建成功,会输出类似以下的信息:


Cluster status of node rabbit@rabbit1 ...

[{nodes,[{disc,[rabbit@rabbit1]},{ram,[rabbit@rabbit2,rabbit@rabbit3]}]},

{running_nodes,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]},

{cluster_name,<<\"rabbit@rabbit1\">>},

{partitions,[]}]

...done.

其中nodes部分显示了集群中的节点信息,disc表示磁盘节点,ram表示内存节点;running_nodes列出了当前正在运行的节点;cluster_name显示了集群的名称;partitions表示集群中的分区情况,正常情况下应该为空。

  • 使用管理界面:访问http://宿主机IP:15672,使用设置的用户名和密码登录 RabbitMQ 管理界面。在管理界面的 “Overview” 页面中,可以看到集群中所有节点的状态信息,包括节点名称、运行状态、内存使用情况、磁盘空间等 。如果所有节点都显示为 “running”,则说明集群搭建成功。同时,还可以在管理界面中创建队列、交换器,发送和接收消息,进一步验证集群的功能是否正常。
  • 发送和接收消息测试:编写一个简单的消息发送和接收程序,连接到 RabbitMQ 集群进行测试。以 Python 为例,使用pika库来连接 RabbitMQ 并发送、接收消息。首先安装pika库:

pip install pika

然后编写发送消息的代码:


import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('宿主机IP', 5672, '/my_vhost', pika.PlainCredentials('admin', 'new_password')))

channel = connection.channel()

channel.queue_declare(queue='test_queue')

channel.basic_publish(exchange='', routing_key='test_queue', body='Hello, RabbitMQ Cluster!')

print("Message sent!")

connection.close()

再编写接收消息的代码:


import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('宿主机IP', 5672, '/my_vhost', pika.PlainCredentials('admin', 'new_password')))

channel = connection.channel()

channel.queue_declare(queue='test_queue')

def callback(ch, method, properties, body):

print("Received message:", body.decode())

channel.basic_consume(queue='test_queue', on_message_callback=callback, auto_ack=True)

print('Waiting for messages...')

channel.start_consuming()

运行发送消息的代码后,再运行接收消息的代码,如果能够正确接收到发送的消息,则说明 RabbitMQ 集群的消息收发功能正常,进一步验证了集群搭建的成功。


    网站公告

    今日签到

    点亮在社区的每一天
    去签到