架构总览
第一步:连接到服务器
1.1 确保虚拟机网络模式为桥接(Bridge)
重要:如果虚拟机使用 NAT 模式,宿主机无法直接访问虚拟机端口。
在 VMware/VirtualBox 中:
- VMware:虚拟机设置 → 网络适配器 → 选择"桥接模式(Bridged)"
- VirtualBox:虚拟机设置 → 网络 → 选择"桥接网卡"
桥接模式后,虚拟机和宿主机处于同一网络,宿主机可以直接通过 IP 访问。
1.2 查看虚拟机 IP
ip addr show
找到类似 192.168.1.x 的 IP 地址,这就是虚拟机在局域网中的 IP。
1.3 SSH 连接到虚拟机
ssh root@192.168.1.x
替换为你的虚拟机实际 IP。
第二步:安装 docker compose
2.1 检查是否已安装
docker compose --version
如果显示版本号,说明已安装,跳过此步。
2.2 安装 docker compose
# 下载 v2.24.0(支持 Linux AMD64)
curl -L "https://github.com/docker/compose/releases/download/v2.24.0/docker compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker compose# 添加执行权限
chmod +x /usr/local/bin/docker compose# 验证
docker compose --version
第三步:创建项目目录
mkdir -p /opt/docker-services && cd /opt/docker-services
第四步:创建 docker compose.yml
vi /opt/docker-services/docker-compose.yml
按 i 进入编辑模式,粘贴以下内容:
services:# =====================# MongoDB 4.4# 注意:6.0+ 需要 AVX 指令集,CentOS 7 不支持,需使用 4.4# =====================mongodb:image: mongo:4.4container_name: mongodbrestart: unless-stoppedports:- "27017:27017"environment:MONGO_INITDB_ROOT_USERNAME: adminMONGO_INITDB_ROOT_PASSWORD: "RootPass123!"volumes:- ./mongodb/data:/data/dbnetworks:- services-networkcommand: --wiredTigerCacheSizeGB 1# =====================# MySQL 8# =====================mysql8:image: mysql:8container_name: mysql8restart: unless-stoppedports:- "3306:3306"volumes:- ./mysql/data:/var/lib/mysql- ./mysql/conf:/etc/mysql/conf.denvironment:MYSQL_ROOT_PASSWORD: "RootPass123!"MYSQL_DATABASE: appdbTZ: Asia/Shanghainetworks:- services-networkcommand: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci# =====================# RabbitMQ 3.12# =====================rabbitmq:image: rabbitmq:3.12-managementcontainer_name: rabbitmqrestart: unless-stoppedports:- "5672:5672"- "15672:15672"volumes:- ./rabbitmq/data:/var/lib/rabbitmqenvironment:RABBITMQ_DEFAULT_USER: adminRABBITMQ_DEFAULT_PASS: "Admin123!"networks:- services-network# =====================# Kafka(KRaft 模式,无需 Zookeeper)# Kafka 3.6+ 内置 KRaft 共识协议,替代 Zookeeper# =====================kafka:image: confluentinc/cp-kafka:7.5.0container_name: kafkarestart: unless-stoppedports:- "9092:9092"volumes:- ./kafka/data:/var/lib/kafka/dataenvironment:CLUSTER_ID: "dbDx_GCIYsCsD23V00G1FA"KAFKA_NODE_ID: 1KAFKA_PROCESS_ROLES: "broker,controller"KAFKA_CONTROLLER_QUORUM_VOTERS: "1@kafka:9093"# 关键修改:使用 PLAINTEXT 协议,并公布虚拟机 IPKAFKA_LISTENERS: "PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093"KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://192.168.56.101:9092"KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT"KAFKA_INTER_BROKER_LISTENER_NAME: "PLAINTEXT"KAFKA_CONTROLLER_LISTENER_NAMES: "CONTROLLER"KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0KAFKA_LOG_RETENTION_HOURS: 168KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"networks:- services-network# =====================# Redis 7# =====================redis:image: redis:7container_name: redisrestart: unless-stoppedports:- "6379:6379"volumes:- ./redis/data:/data- ./redis/conf/redis.conf:/usr/local/etc/redis/redis.confnetworks:- services-networkcommand: redis-server /usr/local/etc/redis/redis.conf# =====================
# 网络定义
# =====================
networks:services-network:driver: bridge
保存退出(Esc → :wq)。
第五步:创建配置文件
MySQL 配置文件
mkdir -p /opt/docker-services/mysql/conf
vi /opt/docker-services/mysql/conf/custom.cnf
粘贴以下内容:
[mysqld]
max_connections=200
innodb_buffer_pool_size=512M[client]
default-character-set=utf8mb4
保存退出。
Redis 配置文件
mkdir -p /opt/docker-services/redis/conf
vi /opt/docker-services/redis/conf/redis.conf
粘贴以下内容:
maxmemory 512mb
maxmemory-policy allkeys-lru
appendonly yes
保存退出。
第六步:开放端口(防火墙配置)
服务启动后,宿主机远程访问需要开放对应端口。CentOS 7 使用 firewalld。
6.1 检查防火墙状态
systemctl status firewalld
如果显示 active (running),需要开放端口。如果 inactive,跳过此步骤。
6.2 开放所有服务端口
# 开放端口(permanent 表示永久生效,reload 使配置立即生效)
firewall-cmd --permanent --add-port=27017/tcp # MongoDB
firewall-cmd --permanent --add-port=3306/tcp # MySQL
firewall-cmd --permanent --add-port=5672/tcp # RabbitMQ
firewall-cmd --permanent --add-port=15672/tcp # RabbitMQ 管理界面
firewall-cmd --permanent --add-port=6379/tcp # Redis
firewall-cmd --permanent --add-port=9092/tcp # Kafka# 生效配置
firewall-cmd --reload
6.3 验证端口已开放
firewall-cmd --list-ports
应该看到:
15672/tcp 3306/tcp 5672/tcp 6379/tcp 9092/tcp 27017/tcp
6.4 可选:限制 IP 访问(增强安全)
如果只允许特定 IP 访问(比如你平时工作的电脑 IP 是 192.168.1.100):
# 只允许指定 IP 访问 MongoDB
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.100/32" port port="27017" protocol="tcp" accept'# 只允许指定 IP 访问 MySQL
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.100/32" port port="3306" protocol="tcp" accept'# 生效
firewall-cmd --reload
6.5 关闭所有端口(测试用)
如果部署初期想先在虚拟机内部测试,可以暂时清空防火墙规则:
firewall-cmd --flush
firewall-cmd --reload
生产环境建议:恢复第 6.2 步的规则,不要长时间关闭防火墙。
第七步:启动所有服务
6.1 拉取所有镜像(首次需要)
cd /opt/docker-services
docker compose pull
这一步需要下载所有镜像,视网络情况可能需要 5-10 分钟。
6.2 启动所有服务
docker compose up -d
参数说明:
-d:后台运行- docker compose 会自动按照 depends_on 的依赖顺序启动
6.3 查看启动状态
docker compose ps
应该看到 5 个服务都是 Up 状态(MongoDB、MySQL、RabbitMQ、Kafka、Redis)。
第八步:验证所有服务
# 查看所有容器日志(确认没有 Error)
docker compose logs --tail=20# 逐个验证
docker exec mongodb mongosh --eval "db.version()" # MongoDB 4.4
docker exec mysql8 mysql -u root -p'RootPass123!' -e "SELECT 1;"
docker exec rabbitmq rabbitmqctl status
docker exec kafka kafka-topics --bootstrap-server localhost:9092 --list
docker exec redis redis-cli ping
第九步:常用命令速查
# 进入项目目录
cd /opt/docker-services# 启动全部服务
docker compose up -d# 停止全部服务(保留数据)
docker compose stop# 查看状态
docker compose ps# 查看日志
docker compose logs -f# 查看某个服务日志
docker compose logs -f kafka# 重启某个服务
docker compose restart kafka# 重新构建(修改了镜像或配置后)
docker compose up -d --force-recreate# 删除所有容器和数据(慎用!)
docker compose down -v# 只删除容器,保留数据
docker compose down# 注意:KRaft 模式下 Kafka 单节点不需要也无法用 --scale 扩容
# 如需多节点,请配置 KAFKA_CONTROLLER_QUORUM_VOTERS 为多节点
服务器重启后恢复服务
自动恢复,服务器重启后运行:
cd /opt/docker-services
docker compose up -d
docker compose 会自动重启所有服务。
生产环境增强配置
上面的是基础配置,下面是生产级别增强版本,直接替换 docker compose.yml 即可:
version: '3.8'services:# =====================# MongoDB 4.4(无 AVX 支持)# =====================mongodb:image: mongo:4.4container_name: mongodbrestart: alwaysports:- "27017:27017"environment:MONGO_INITDB_ROOT_USERNAME: adminMONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD}volumes:- ./mongodb/data:/data/db- ./mongodb/backup:/backupnetworks:- services-networkhealthcheck:test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]interval: 10stimeout: 5sretries: 5command: --wiredTigerCacheSizeGB 1 --journalresources:limits:memory: 2Greservations:memory: 512M# =====================# MySQL 8# =====================mysql8:image: mysql:8container_name: mysql8restart: alwaysports:- "3306:3306"volumes:- ./mysql/data:/var/lib/mysql- ./mysql/conf:/etc/mysql/conf.d- ./mysql/backup:/backupenvironment:MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}MYSQL_DATABASE: appdbTZ: Asia/Shanghainetworks:- services-networkhealthcheck:test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]interval: 10stimeout: 5sretries: 5command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci# =====================# RabbitMQ 3.12# =====================rabbitmq:image: rabbitmq:3.12-managementcontainer_name: rabbitmqrestart: alwaysports:- "5672:5672"- "15672:15672"volumes:- ./rabbitmq/data:/var/lib/rabbitmq- ./rabbitmq/definitions.json:/etc/rabbitmq/definitions.jsonenvironment:RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER}RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASS}RABBITMQ_DEFAULT_VHOST: /networks:- services-networkhealthcheck:test: ["CMD", "rabbitmq-diagnostics", "ping"]interval: 30stimeout: 10sretries: 5# =====================# Kafka(KRaft 模式,无需 Zookeeper)# =====================kafka:image: confluentinc/cp-kafka:7.5.0container_name: kafkarestart: alwaysports:- "9092:9092"volumes:- ./kafka/data:/var/lib/kafka/dataenvironment:# KRaft 模式关键配置KAFKA_NODE_ID: 1KAFKA_PROCESS_ROLES: broker,controllerKAFKA_CLUSTER_ID: dbDx_GCIYsCsD23V00G1FA# 监听器配置KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXTKAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXTKAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLERKAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093# 副本配置KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0# 日志配置KAFKA_LOG_RETENTION_HOURS: 168KAFKA_LOG_SEGMENT_BYTES: 1073741824KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"networks:- services-network# =====================# Redis 7# =====================redis:image: redis:7container_name: redisrestart: alwaysports:- "6379:6379"volumes:- ./redis/data:/data- ./redis/conf/redis.conf:/usr/local/etc/redis/redis.confnetworks:- services-networkcommand: redis-server /usr/local/etc/redis/redis.confhealthcheck:test: ["CMD", "redis-cli", "ping"]interval: 10stimeout: 5sretries: 5networks:services-network:driver: bridge
生产环境 .env 文件
创建 .env 文件存储敏感信息(不要提交到 git):
vi /opt/docker-services/.env
MYSQL_ROOT_PASSWORD=你的强密码
RABBITMQ_USER=admin
RABBITMQ_PASS=你的强密码
服务访问信息
从虚拟机内部连接(localhost)
| 服务 | 连接地址 | 备注 |
|---|---|---|
| MongoDB | localhost:27017 |
用户名 admin,密码 RootPass123! |
| MySQL | localhost:3306 |
用户名 root,密码 RootPass123! |
| RabbitMQ | localhost:5672 |
管理界面:admin / Admin123! |
| Kafka | localhost:9092 |
KRaft 模式(无需 Zookeeper) |
| Redis | localhost:6379 |
从宿主机(物理机)连接
假设虚拟机 IP 为 192.168.1.100(查看方法:ip addr),则:
| 服务 | 连接地址 | 备注 |
|---|---|---|
| MongoDB | 192.168.1.100:27017 |
推荐用 MongoDB Compass 客户端 |
| MySQL | 192.168.1.100:3306 |
推荐用 Navicat 或 MySQL Workbench |
| RabbitMQ | 192.168.1.100:5672 |
AMQP 连接 |
| RabbitMQ 管理界面 | http://192.168.1.100:15672 | 浏览器直接访问 |
| Kafka | 192.168.1.100:9092 |
使用 KRaft 模式(无需 Zookeeper) |
| Redis | 192.168.1.100:6379 |
推荐用 RedisInsight |
RabbitMQ 管理界面:
admin/Admin123!(请修改为自己的密码,密码中有!记得加英文引号包裹)
快速故障排查
# 查看所有容器状态
docker compose ps# 查看所有日志
docker compose logs# 查看某个服务详细日志
docker compose logs kafka# 某个服务起不来,交互式调试
docker compose up mongodb# 检查容器内部网络连通性
docker exec mongodb ping kafka
docker exec kafka ping mongodb# 重建某个服务
docker compose up -d --force-recreate kafka# Kafka KRaft 模式检查
docker exec kafka kafka-metadata --snapshot /var/lib/kafka/data/__cluster_metadata-0/00000000000000000000.log
Kafka KRaft 模式常见问题与解决
问题一:CLUSTER_ID 格式错误
错误信息:
ERROR Kafka during zkInit expired, startup failed:
Cluster ID mismatch, found cmLe5gYfR9, stored in meta.properties: null
原因:
CLUSTER_ID 格式不正确。必须是 Base64 编码的 22 字符字符串。
错误示例:
# ❌ 错误格式 - 普通字符串不是有效的 CLUSTER_ID
CLUSTER_ID: "TESTCLUSTERID00001"
正确格式示例:
# ✅ 正确格式 - 22 字符 Base64 编码
CLUSTER_ID: "dbDx_GCIYsCsD23V00G1FA"
解决方案
方法一:使用预生成的 CLUSTER_ID
直接使用有效的 CLUSTER_ID:
environment:CLUSTER_ID: "dbDx_GCIYsCsD23V00G1FA"
方法二:生成新的 CLUSTER_ID
# 1. 生成新的 CLUSTER_ID
docker run --rm confluentinc/cp-kafka:7.5.0 kafka-storage random-uuid# 输出类似:YIx3LJJJikYQ2KC5SJXU6O4A
方法三:从现有集群迁移
如果已有运行中的 Kafka,使用以下命令获取:
# 查看现有 CLUSTER_ID
cat /opt/docker-services/kafka/data/__cluster_metadata-0/log/meta.properties# 输出示例:
# cluster.id=YIx3LJJJikYQ2KC5SJXU6O4A
问题二:KRaft 未初始化
错误信息:
Kafka cluster ID not found in meta.properties.
KRaft mode startup requires initialization.
原因:
数据目录已存在但 KRaft 未初始化。
解决方案:
# 1. 停止并删除 Kafka 容器
docker stop kafka
docker rm kafka# 2. 删除 Kafka 数据目录(重要:必须清空)
rm -rf /opt/docker-services/kafka/data/*
# 修改完配置后,删除所有当前没有被任何容器使用的卷,以此来释放磁盘空间
docker volume prune# 3. 重新启动 Kafka(会自动初始化 KRaft)
docker compose up -d kafka# 4. 验证初始化成功
docker exec kafka kafka-metadata --status /var/lib/kafka/data/__cluster_metadata-0/00000000000000000000.log
完整修复步骤
如果遇到 CLUSTER_ID 相关错误,执行以下完整步骤:
# 1. 进入目录
cd /opt/docker-services# 2. 停止所有服务
docker compose stop# 3. 删除旧容器
docker compose rm -f# 4. 清理 Kafka 数据(必须)
rm -rf ./kafka/data/*# 5. 生成新的 CLUSTER_ID
docker run --rm confluentinc/cp-kafka:7.5.0 kafka-storage random-uuid
# 假设输出为:YIx3LJJJikYQ2KC5SJXU6O4A# 6. 更新 docker-compose.yml 中的 CLUSTER_ID
# 将 CLUSTER_ID: "dbDx_GCIYsCsD23V00G1FA" 替换为新生成的 ID# 7. 重新启动所有服务
docker compose up -d# 8. 验证 Kafka 运行状态
docker compose logs kafka --tail=20
docker exec kafka kafka-topics --list --bootstrap-server localhost:9092
CLUSTER_ID 格式说明
| 格式要求 | 说明 |
|---|---|
| 长度 | 必须是 22 个字符 |
| 编码 | Base64 编码 |
| 示例 | YIx3LJJJikYQ2KC5SJXU6O4A |
生成有效 CLUSTER_ID 的方法:
# 方法1:使用 Kafka 工具生成(推荐)
docker run --rm confluentinc/cp-kafka:7.5.0 kafka-storage random-uuid# 方法2:手动生成 Base64 UUID
echo -n "$(cat /proc/sys/kernel/random/uuid)" | base64 | cut -c1-22# 方法3:Python 生成
python3 -c "import base64,uuid; print(base64.b64encode(uuid.uuid4().bytes).decode()[:22])"
