AlmaLinux 9 × Docker Compose 部署指南
适用范围:AlmaLinux 9(x86_64 / aarch64)
教学示例:本文档以一份覆盖关系型数据库、文档库、消息中间件、分析型数据库的多服务 Compose 配置为示例,完整演示在 AlmaLinux 9 上从零编写docker-compose.yml、启动并验证整套服务的全过程。读者既可直接复现示例服务组合,亦可根据实际需要选用其中部分服务
撰写日期:2026/06/30
📑 目录
- 1. 文档定位
- 2. 教学示例:服务全景
- 3. 环境准备
- 4. 安装 Docker Engine 与 docker compose 插件
- 5. 从零编写 docker-compose.yml
- 6. 启动与验证
- 7. 常用运维命令
- 8. 防火墙与 SELinux
- 9. 常见问题排查
- 10. 待确认事项
- 11. 参考来源
1 文档定位
本文档是面向 AlmaLinux 9 的 Docker / Compose 部署教学,内容涵盖:
- Docker Engine 与
docker compose插件(v2)的安装与验证 docker-compose.yml的写法与常见字段- 一份多服务数据栈(MongoDB / MySQL / Oracle / GBase / RabbitMQ / Kafka / Redis)的部署示例
学习路径建议:
- 如果你只想把 Docker 装起来 → 看 4 安装 Docker 就够
- 如果你要部署一套多服务的数据/消息中间件 → 通读 2 教学示例 到 6 启动与验证
- 如果只关心某个具体服务能否跑起来 → 直接跳到 5 从零编写 docker-compose.yml 对应小节,抄走对应
services:块即可
本文档不与任何既有 compose 文件绑定。所有 YAML 示例均面向教学编写,读者可根据生产环境实际需要增删服务、调整镜像版本、修改端口与内存等参数。
2 教学示例:服务全景
为使后续"配置 → 启动 → 验证"的讲解具备可操作性,本文档全程围绕下面这套示例服务展开:
| 类别 | 服务 | 镜像 | 主机端口 |
|---|---|---|---|
| 关系型 | MySQL 8 | mysql:8 |
3316 |
| 关系型 | Oracle XE 21c | gvenzl/oracle-xe:21.3.0 |
1521, 5500 |
| 国产数据库 | GBase 8s | liaosnet/gbase8s:v8.8_3503x1_x64 |
9088, 27018 |
| 文档型 | MongoDB 4.2 | mongo:4.2 |
27027 |
| 缓存 | Redis 7 | redis:7 |
6379 |
| 消息 | RabbitMQ 3.12 | rabbitmq:3.12-management |
5672, 15672 |
| 消息 | Kafka 7.5.3(KRaft) | confluentinc/cp-kafka:7.5.3 |
9092 |
整套服务对宿主机物理内存的粗略规划(按 5 从零编写 docker-compose.yml 示例中 mem_limit 之和 + 余量):
| 场景 | 推荐内存 |
|---|---|
| 仅启动 MongoDB + MySQL + Redis + RabbitMQ | ≥ 4 GB |
| 加入 Kafka | ≥ 8 GB |
| 全部 8 个服务同时跑 | ≥ 12 GB |
上述推荐值仅作容量规划起点,待确认 是否覆盖各镜像的真实工作集需求。详见 10 待确认事项。
3 环境准备
3.1 系统要求
- AlmaLinux 9.x(最小化安装即可,后续按需补包)
- 当前用户具备
sudo权限 - 宿主机已启用网络并能访问 Docker 官方仓库
cat /etc/almalinux-release
uname -r
3.2 同步系统时间
时区不一致会让 Kafka、Oracle 等组件出现奇怪错误,先校准:
sudo timedatectl set-timezone Asia/Shanghai
sudo timedatectl status
# chronyc tracking 输出字段(Reference time / System time / Last offset 等)以 UTC 表示,
# 看起来会比北京时间晚 8 小时,属于正常现象,不必再次调整
注:
chronyc tracking的输出字段(Reference time、System time、Last offset等)统一以 UTC 表示,直接打印会显示为比北京时间晚 8 小时,这是预期行为,并非时间未校准。判断校准是否成功,改用timedatectl status即可。
3.3 更新系统
sudo dnf -y update
sudo dnf -y install dnf-plugins-core \
ca-certificates curl \
tar bash-completion
3.4 关闭 swap(强烈建议)
Oracle、Kafka 等组件在启用 swap 后性能会明显下降,推荐关闭:
sudo swapoff -a
sudo sed -i '/\sswap\s/s/^/#/' /etc/fstab
环境差异说明
- 物理机 / 虚拟机(标准 AlmaLinux 9 最小化安装):直接执行上述命令即可,重启后 swap 仍为关闭状态。
- WSL2 子系统:不存在
/etc/fstab,lsblk/systemctl也不可用,且 swap 由 Windows 端的wslconfig管理(默认使用C:\pagefile.sys)。这种情况下 不推荐关闭 swap,让 WSL2 继续借用 Windows pagefile 即可,否则在内存吃紧时 WSL 内的进程会直接被 OOM Killer 终止,反而更容易出问题。- 公有云精简镜像 / 容器化宿主(如部分 AWS、Azure、阿里云定制镜像):可能不生成
/etc/fstab。若sudo sed -i '/\sswap\s/s/^/#/' /etc/fstab报No such file or directory,说明本机没有传统的 fstab 机制,跳过该行命令,只保留swapoff -a即可;重启后如需持久化,按镜像提供方文档配置(常见方式是通过systemd-swap或zram-generator的 unit 文件)。
关闭 swap 的影响
关闭 swap 在 Docker / 中间件场景下整体上是利大于弊的,主要影响如下:
-
内存不足时的"兜底"消失
启用 swap 时,内存耗尽后系统会把不活跃的页换出到磁盘,虽然慢但仍能继续运行;关闭后,一旦物理内存耗尽,内核会直接触发 OOM Killer,优先按oom_score_adj终止得分最高的进程。在本指南的 8 服务同机部署场景下,常见后果是某个内存最大的容器(如 oracle-xe)被整体杀掉。 -
可能出现的连锁反应
- 容器异常退出:
docker inspect <container>中State.OOMKilled: true - 宿主机日志:
journalctl -k | grep -i "out of memory" - MySQL/Redis 写入中突然断连,可能产生未提交事务或 AOF 重放
- 容器异常退出:
-
缓解措施
- 按 2 教学示例 中的内存规划表预留充足物理内存
- 关键服务在 yml 中通过
mem_limit显式约束,避免一个容器吃光全部内存 - 通过
/proc/sys/vm/overcommit_memory控制内存超额分配策略(默认即可,生产可评估改为1) - 监控
free -h、docker stats,提前扩容而非等 OOM
如果确实需要保留 swap
K8s 节点或开发试验机(主机内存 < 16 GB)可以保留 swap,但应:
- 调低
vm.swappiness(例如sysctl -w vm.swappiness=10),减少匿名页换出 - 关键服务仍设
mem_limit,让 cgroup 提前触发 OOM,避免整个宿主机卡死 - 确认数据卷使用 SSD,否则 swap 落盘反而拉低整体性能
经验值:本文档示例的 9 服务同机部署,建议宿主物理内存 ≥ 16 GB 且关闭 swap;若主机内存不足,优先按需裁剪服务数量,其次再考虑保留 swap 并调低
swappiness。
4 安装 Docker Engine 与 docker compose 插件
安装方式以 Docker 官方 RHEL 安装页为准。Docker 没有 AlmaLinux 专属子页,AlmaLinux 9 与 RHEL 9 二进制兼容,可直接套用
docker-ce的 el9 包。
4.1 卸载可能冲突的旧包
sudo dnf remove -y \
docker docker-client docker-client-latest \
docker-common docker-latest docker-latest-logrotate \
docker-logrotate docker-engine \
podman runc
干净系统上 dnf 会提示"未安装"(
No match for argument: ...与No packages marked for removal),属正常。卸载命令不会清空/var/lib/docker/下的镜像、容器、卷与网络。
4.2 添加 Docker 官方 dnf 仓库
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
若
dnf config-manager报Curl error (35) ... Connection reset by peer该错误是 TCP/TLS 握手阶段被对端 RST,常见原因:
- 网络环境屏蔽了
download.docker.com(国内网络环境、公司/校园网防火墙、WSL2 默认网络栈无法访问部分海外域名)- HTTP/HTTPS 代理未透传到 WSL2:Windows 端开启了代理软件,WSL 内
http_proxy/https_proxy未同步- MTU 不匹配:部分 VPN/隧道下默认 MTU 1500 偏大,导致大 TLS 握手包分片后被丢包
解决方案按推荐顺序:
# 1) 清理未写入完成的 repo 文件 sudo rm -f /etc/yum.repos.d/docker-ce.repo # 2) 切换为国内镜像源(AlmaLinux 9 与 RHEL 9 el9 体系兼容,使用 centos/ 路径) # 阿里云: sudo dnf config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 腾讯云(备选): # sudo dnf config-manager --add-repo https://mirrors.cloud.tencent.com/docker-ce/linux/centos/docker-ce.repo # 3) 验证源是否生效 dnf repolist | grep docker切换为镜像源后,后续
dnf install会从镜像站点拉取docker-ce、containerd.io等包,版本与官方一致。若仍报错,排查
env | grep -i proxy是否残留代理变量、curl -vI https://mirrors.aliyun.com能否拿到 200 状态码,再决定是否需要配置 HTTP 代理或调整 MTU(sudo ip link set eth0 mtu 1400)。
4.3 安装 Engine 与 Compose 插件
sudo dnf install -y \
docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
GPG 提示出现时,核对指纹是否为 060A 61C5 1B55 8A7F 742B 77AA C52F EB6B 621E 9F35(Docker 官方公布值),确认后接受。
如需固定版本:
dnf list docker-ce --showduplicates | sort -r
sudo dnf install -y \
docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> \
containerd.io docker-buildx-plugin docker-compose-plugin
4.4 启动与开机自启
sudo systemctl enable --now docker
sudo systemctl status docker --no-pager
4.4.1 配置 Docker 镜像加速器
默认情况下,Docker 守护进程从 registry-1.docker.io 拉取镜像。在国内网络或公司防火墙环境下,该域名常被 RST,表现为:
docker run hello-world长时间卡在Unable to find image 'hello-world:latest' locallydocker pull <image>报net/http: TLS handshake timeout或connection reset by peer
解决方案是为 Docker 守护进程配置镜像加速器(registry-mirrors) + 显式 DNS + 提升并发数。Docker 仍按原镜像名拉取,守护进程在内部把请求重定向到镜像源,首个源不可达时自动 fallback 至下一个。
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://hub.rat.dev",
"https://docker-0.unsee.tech",
"https://docker.1ms.run",
"https://docker.hlmirror.com",
"https://docker.mirrors.ustc.edu.cn",
"https://docker.nju.edu.cn"
],
"dns": ["8.8.8.8", "114.114.114.114", "223.5.5.5"],
"max-concurrent-downloads": 10,
"max-concurrent-uploads": 5
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
各字段含义:
| 字段 | 作用 | 取值建议 |
|---|---|---|
registry-mirrors |
镜像源列表,Docker 按顺序尝试,首个失败后自动 fallback 至下一个 | 至少 3 个,优先放可达且同步延迟低的源(可 curl -I <url> 验证) |
dns |
Docker 守护进程使用的 DNS 服务器 | 显式指定可避免 WSL2 / 容器化宿主里 /etc/resolv.conf 漂移导致的 no such host |
max-concurrent-downloads |
同时拉取的镜像层数 | 默认 3,部署多服务时建议 8-10,显著缩短 docker compose pull 时间 |
max-concurrent-uploads |
同时推送的镜像层数(docker push) |
推送场景才会用,默认 5 已够 |
镜像源选型参考(若需替换为其他源,可按需选择):
镜像源 适用场景 是否需要登录 https://docker.m.daocloud.ioDaoCloud 公共代理,免登录,推荐首选 否 https://hub.rat.dev/https://docker-0.unsee.tech/https://docker.1ms.run社区公共镜像,适合多源 fallback 否 https://docker.hlmirror.com个人维护的公共镜像,适合作为冗余源 否 https://docker.mirrors.ustc.edu.cn/https://docker.nju.edu.cnUSTC / 南京大学镜像,高校背景可信度较高 否 https://<your-id>.mirror.aliyuncs.com阿里云个人加速器(控制台「容器镜像服务」→「镜像加速器」获取) 需登录阿里云账号 https://mirror.ccs.tencentyun.com腾讯云个人加速器 需登录腾讯云账号
注意事项
registry-mirrors只对 Docker Hub 的docker.io/*镜像生效。Oracle、Confluent、GBase 等第三方仓库(各自独立的registry)仍走直连,如遇不通需在 yml 中改用image: <mirror>/<original-image>的方式绕过。- 镜像源可用性会随时间变化,建议每季度用
curl -I <url>逐源验证,剔除不可达节点,避免每次拉取都被多个超时拖累。- 部分小镜像站缺少 Docker Content Trust 签名,如有严格的镜像来源审计需求,优先选择 DaoCloud、阿里云、腾讯云、USTC 等可信源。
配置完成后,使用 docker info | grep -A 9 "Registry Mirrors" 确认加速器已生效。
4.5 验证
sudo docker run hello-world
docker compose version # 注意:docker compose 中间有空格
hello-world容器执行完echo后即正常退出,因此docker ps默认看不到它;查看全部用docker ps -a,STATUS显示Exited (0)即代表完整链路已跑通。
4.6(可选)让非 root 用户免 sudo
sudo usermod -aG docker $USER
newgrp docker
退出当前 SSH 会话重新登录后,docker 命令不再需要 sudo。
5 从零编写 docker-compose.yml
本节从零开始编写一份 docker-compose.yml,将 2 教学示例 列出的服务组装为可启动的应用栈。所有字段均可按实际需要调整——读者可删除不需要的服务、修改端口与镜像版本,或基于本示例扩展为更复杂的部署。
5.1 创建工作目录
mkdir -p ~/docker-services && cd ~/docker-services
后续所有路径都假设你在 ~/docker-services/ 下执行。
5.2 创建数据子目录
yml 中使用了大量相对路径挂载,启动前需要把空目录准备好:
mkdir -p \
mongodb/data \
mysql/data mysql/conf \
oracle-xe/data \
gbase8s/data gbase8s/logs \
rabbitmq/data \
kafka/data \
redis/data redis/conf
5.2.1 部署前 IP 自查与替换
直接复用 §5.3 的 yml 有 3 处 IP 必须根据本机实际网络调整,否则会出现容器能起、客户端连不上的情况。在保存 yml 之前,先查本机 eth0 真实 IP:
ip -4 addr show eth0 | grep inet
# 典型输出:
# inet 172.24.186.43/20 brd 172.24.191.255 scope global eth0
# ^^^^^^^^^^^^^^
# 这就是本机对外可达的 IP
随后按需替换 yml 中以下三处:
| # | 位置 | 默认值 | 替换为 | 不改的后果 |
|---|---|---|---|---|
| 1 | kafka 服务的 KAFKA_ADVERTISED_LISTENERS |
REPLACE_WITH_ETH0_IP(yml 中已写为占位符) |
上面 ip 查到的 eth0 IP |
外部客户端连不上 Kafka,报 Connection to node -1 |
下面 §5.3 的完整 yml 中 KAFKA_ADVERTISED_LISTENERS 故意写成了占位符 REPLACE_WITH_ETH0_IP,docker compose up 之前必须先执行 ip -4 addr show eth0 | grep inet 拿到本机 IP,然后在 yml 里全局替换该占位符。否则 Kafka 容器能正常起,但所有外部客户端都会报 Connection to node -1(见 5.7.2)。
5.3 完整 docker-compose.yml
把以下内容保存为 ~/docker-services/docker-compose.yml:
services:
# =====================
# MongoDB 4.2
# =====================
mongodb:
image: mongo:4.2
container_name: mongodb
restart: always
mem_limit: 1073741824
ports:
- "27027:27017"
volumes:
- ./mongodb/data:/data/db
networks:
- services-network
command: --wiredTigerCacheSizeGB 1
# =====================
# MySQL 8
# =====================
mysql8:
image: mysql:8
container_name: mysql8
restart: always
mem_limit: 1073741824
ports:
- "3316:3306"
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/conf:/etc/mysql/conf.d
environment:
MYSQL_ROOT_PASSWORD: "RootPass123!"
MYSQL_DATABASE: appdb
networks:
- services-network
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
# =====================
# Oracle Database Express Edition 21c
# =====================
oracle-xe:
image: gvenzl/oracle-xe:21.3.0
container_name: oracle-xe
restart: always
mem_limit: 4294967296
shm_size: 2gb
ports:
- "1521:1521"
- "5500:5500"
volumes:
- ./oracle-xe/data:/opt/oracle/oradata
environment:
ORACLE_PASSWORD: "OracleXE123!"
ORACLE_CHARACTERSET: "AL32UTF8"
networks:
- services-network
# =====================
# GBase 8s (国产事务型数据库)
# =====================
gbase8s:
image: liaosnet/gbase8s:v8.8_3503x1_x64
container_name: gbase8s
restart: no
mem_limit: 2147483648
shm_size: 1gb
ports:
- "9088:9088"
- "27018:27017"
volumes:
- ./gbase8s/data:/opt/gbase8s/data
- ./gbase8s/logs:/opt/gbase8s/logs
environment:
SERVERNAME: "gbaseserver"
USERPASS: "GBase123!"
CPUS: "1"
MEMS: "2048"
networks:
- services-network
# =====================
# RabbitMQ 3.12
# =====================
rabbitmq:
image: rabbitmq:3.12-management
container_name: rabbitmq
restart: no
mem_limit: 536870912
ports:
- "5672:5672"
- "15672:15672"
volumes:
- ./rabbitmq/data:/var/lib/rabbitmq
environment:
RABBITMQ_DEFAULT_USER: admin
RABBITMQ_DEFAULT_PASS: "Admin123!"
networks:
- services-network
# =====================
# Kafka(KRaft 模式,无需 Zookeeper)
# =====================
kafka:
image: confluentinc/cp-kafka:7.5.3
container_name: kafka
restart: no
mem_limit: 1073741824
ports:
- "9092:9092"
volumes:
- ./kafka/data:/var/lib/kafka/data
environment:
CLUSTER_ID: "dbDx_GCIYsCsD23V00G1FA"
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: "broker,controller"
KAFKA_CONTROLLER_QUORUM_VOTERS: "1@kafka:9093"
KAFKA_LISTENERS: "PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093"
# !!! 必改 !!! 改成宿主机 eth0 实际 IP(执行 `ip -4 addr show eth0` 查看)
# 占位符故意写成 "REPLACE_WITH_ETH0_IP",不改 Kafka 也能启动,但外部客户端会报
# "Connection to node -1",因为 broker 把所有客户端引向了不存在的 IP
# 详细排查见 [5.7.2](#572-kafka-advertised_listeners-自查)
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://REPLACE_WITH_ETH0_IP: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: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
KAFKA_LOG_RETENTION_HOURS: 168
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
networks:
- services-network
# =====================
# Redis 7
# =====================
redis:
image: redis:7
container_name: redis
restart: no
mem_limit: 268435456
ports:
- "6379:6379"
volumes:
- ./redis/data:/data
- ./redis/conf/redis.conf:/usr/local/etc/redis/redis.conf
networks:
- services-network
command: redis-server /usr/local/etc/redis/redis.conf
# =====================
# 网络定义
# =====================
networks:
services-network:
driver: bridge
5.4 关键字段拆解
如果你想改成自己的版本,只要读懂下面几组字段就行:
image / container_name / restart
image: mysql:8 # 镜像名:标签
container_name: mysql8 # 容器名(唯一,删除后重建)
restart: always # 退出后是否自动重启:no | always | on-failure | unless-stopped
mem_limit / shm_size
mem_limit: 1073741824 # 内存硬上限(字节),也支持 "1g"、"512m"
shm_size: "2gb" # /dev/shm 大小,默认 64m;Oracle、PostgreSQL 等需要调大
ports / volumes / environment
ports:
- "3316:3306" # 主机:容器
volumes:
- ./mysql/data:/var/lib/mysql # 主机相对路径:容器绝对路径
environment:
MYSQL_ROOT_PASSWORD: "xxx" # 注入到容器的环境变量
networks / depends_on
networks:
- services-network # 加入到下面定义的网络中
5.5 Redis 自定义配置(可选)
上面的 Redis 挂载了 ./redis/conf/redis.conf。如果你暂时没有这个文件,可以:
docker run --rm redis:7 cat /usr/local/etc/redis/redis.conf > redis/conf/redis.conf
之后再根据实际场景修改(例如 appendonly yes、maxmemory-policy allkeys-lru 等)。
5.6 校验 yml
docker compose config -q # 语法/引用检查,无输出表示通过
5.7 低内存场景调优建议(WSL2 / 16GB 以下物理机)
5.3 完整 docker-compose.yml 的 mem_limit 是按 16 GB 物理内存、生产可用为目标设计的。在 WSL2 11 GB、笔记本 8 GB、单核虚机 4 GB 等内存吃紧场景下,直接 docker compose up -d 会出现 OOM、Oracle 启动崩溃等问题。本节给出经过验证的调优方案。
5.7.1 内存分档建议
按宿主物理内存分三档,只调 mem_limit 和 shm_size,其他字段保持原 yml:
| 服务 | 标准版(16 GB+) | 低配版(11 GB WSL2) | 极简版(8 GB) |
|---|---|---|---|
oracle-xe |
4g / shm 2gb |
2g / shm 1g |
暂不启动 |
gbase8s |
2g / shm 1gb |
1g / shm 512m |
512m / shm 256m |
kafka |
1g |
1g |
512m |
rabbitmq |
512m |
512m |
256m |
mongodb |
1g |
1g |
512m |
mysql8 |
1g |
1g |
512m |
redis |
256m |
256m |
128m |
| 合计 | 11.75 GB | 7.75 GB | 3.4 GB |
调优原则
oracle-xe和gbase8s是单台机器上最大的两个内存消耗者,内存吃紧时优先压这两个redis永远不用大于256m,单实例 Redis 工作集 100 MB 已经很充裕
5.7.2 Kafka ADVERTISED_LISTENERS 自查
如果使用低配版,KAFKA_ADVERTISED_LISTENERS 必须改为宿主 eth0 实际 IP,否则外部客户端连不上:
ip -4 addr show eth0 | grep inet
# 典型输出:172.24.186.43/20 brd 172.24.191.255
# ^^^^^^^^^^^^^^
# 这就是要填进 yml 的 IP
随后把 yml 中:
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://REPLACE_WITH_ETH0_IP:9092"
改为(以 172.24.186.43 为例):
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://172.24.186.43:9092"
5.7.3 RabbitMQ 版本升级
rabbitmq:3.12-management 已于 2024 年 6 月 EOL,虽然短期内(2026 年)Docker Hub 镜像仍可使用,但社区已不再提供安全更新。新部署建议直接采用 4.0 系列,环境变量 RABBITMQ_DEFAULT_USER / RABBITMQ_DEFAULT_PASS 兼容:
rabbitmq:
image: rabbitmq:4.0-management # 替代 3.12-management
# 其余字段保持不变
环境变量 RABBITMQ_DEFAULT_USER / RABBITMQ_DEFAULT_PASS 在 4.0 仍然支持,无需修改。
5.7.4 分批启动脚本(WSL2 / 低内存场景)
低内存宿主一次性 up -d 容易 OOM,推荐分 3 批启动,每批启动后看 docker compose ps 确认 Running 再继续:
cd ~/docker-services
# 第 1 批:4 个轻量服务(总内存 ≈ 2.7 GB)
docker compose up -d mongodb mysql8 redis rabbitmq
docker compose ps
docker stats --no-stream # 确认内存余量 ≥ 6 GB 再继续
# 第 2 批:消息/国产数据库(+ 2 GB,合计 ≈ 4.7 GB)
docker compose up -d kafka gbase8s
docker compose ps
docker stats --no-stream # 确认内存余量 ≥ 4 GB 再继续
# 第 3 批:Oracle(+ 2 GB,合计 ≈ 6.7 GB,初始化 1-2 分钟)
docker compose up -d oracle-xe
docker logs -f oracle-xe # 等 DATABASE IS READY TO USE! 后 Ctrl+C
如果中途 OOM,临时关掉非关键服务腾内存:
docker compose stop rabbitmq # 释放 512m docker compose stop gbase8s # 释放 1g docker compose stop mongodb # 释放 1g(谨慎,客户端连接会全部断开)永久方案:在 Windows 端编辑
%UserProfile%\.wslconfig:[wsl2] memory=16GB swap=0然后在 PowerShell(管理员)执行
wsl --shutdown后重启 WSL,新配置生效。
5.7.5 Oracle 数据目录属主(WSL2 / Linux 宿主机必做)
gvenzl/oracle-xe 容器内以 uid 54321 的 oracle 用户运行,宿主挂载目录必须匹配该属主,否则容器报 Permission denied: /opt/oracle/oradata/XE:
# 1) 首次启动前,建好数据目录
mkdir -p ~/docker-services/oracle-xe/data
# 2) 改属主
sudo chown -R 54321:54321 ~/docker-services/oracle-xe
# 3) 之后再 docker compose up -d oracle-xe
不要在 yml 里写
user: "54321:54321"——Oracle 镜像的 entrypoint 设计为"启动时 root 初始化,运行期切到 oracle",在 yml 里写死用户会破坏初始化流程。
6 启动与验证
6.1 前台调试启动
docker compose up
按 Ctrl+C 停止,容器会保留。
6.2 后台启动
docker compose up -d
6.3 查看运行状态
docker compose ps
docker compose logs -f --tail=200
6.4 逐项验证
# MongoDB
docker compose exec mongodb mongosh --eval 'db.runCommand({ping:1})'
# MySQL
docker compose exec mysql8 mysql -uroot -p'RootPass123!' -e 'SHOW DATABASES;'
# Oracle XE(首次启动较慢,可能 1-2 分钟)
docker compose exec oracle-xe bash -c 'echo "SELECT status FROM v\\$instance;" | sqlplus -S system/OracleXE123!@//localhost:1521/XEPDB1'
# GBase 8s
docker compose logs -f gbase8s | head -40
# RabbitMQ
curl -u admin:Admin123! http://localhost:15672/api/overview | head -200
# Kafka(在容器内)
docker compose exec kafka kafka-topics --bootstrap-server localhost:9092 --list
# Redis
docker compose exec redis redis-cli ping
6.5 停止与销毁
docker compose stop # 停止
docker compose down # 停止并删除容器(保留卷)
docker compose down -v # 停止并删除容器与卷(数据丢失)
7 常用运维命令
# 查看资源占用
docker stats
# 进入容器
docker compose exec mongodb bash
# 查看具体服务日志
docker compose logs -f kafka --tail=500
# 重启单个服务
docker compose restart redis
# 拉取最新镜像(需先改 yml)
docker compose pull
docker compose up -d
# 清理无用镜像
docker image prune -f
8 防火墙与 SELinux
AlmaLinux 9 默认开启 firewalld 与 SELinux。Docker 守护进程会把规则加进 nftables,但仍需要在 firewalld 里把信任的 docker0 / br-xxx 接口或服务端口放行,否则外部主机访问会失败。
8.1 放行常用端口(按需)
sudo firewall-cmd --permanent --add-port=3316/tcp # MySQL
sudo firewall-cmd --permanent --add-port=27027/tcp # MongoDB
sudo firewall-cmd --permanent --add-port=1521/tcp # Oracle
sudo firewall-cmd --permanent --add-port=5500/tcp # Oracle EM Express
sudo firewall-cmd --permanent --add-port=5672/tcp # RabbitMQ AMQP
sudo firewall-cmd --permanent --add-port=15672/tcp # RabbitMQ 管理
sudo firewall-cmd --permanent --add-port=6379/tcp # Redis
sudo firewall-cmd --permanent --add-port=9092/tcp # Kafka
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports
仅本机访问也可省略这一步。
8.2 SELinux
AlmaLinux 9 默认 enforcing,大多数官方镜像能正常工作;如果出现"Permission denied"或挂载失败,可在 yml 中给对应服务加 privileged: true 或临时 sudo setenforce 0 验证是否为 SELinux 触发。
永久方案(推荐):给挂载目录打 SELinux 标签:
sudo chcon -Rt svirt_sandbox_file_t /path/to/your/data-dir
9 常见问题排查
WSL2 / 低内存宿主(≤ 16 GB)请先看 2 教学示例 容量规划,按"轻量先上,重量后置"分批启动,避免一次性
up触发 OOM。Oracle 专属问题(目录属主、软链环修复)的完整命令见 5.7.5。
| 现象 | 排查方向 |
|---|---|
docker compose up 后某个服务一直 Restarting |
docker compose logs <service> 看启动报错;常见原因:端口冲突、shm_size 不够、Oracle 还在初始化(需 1-2 分钟) |
| 容器立即被内核杀死,日志里看不到异常 | 极可能是 OOM:docker inspect --format '{{.State.OOMKilled}}' <container>,返回 true 即内存被 cgroup 触发 OOM Killer;同时 journalctl -k | grep -i "out of memory" 查宿主机日志 |
| Kafka 客户端连接失败 | yml 中 KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://REPLACE_WITH_ETH0_IP:9092 或 PLAINTEXT://192.168.1.100:9092(旧版默认值),需改为宿主机 eth0 实际 IP(见 5.2.1) |
Kafka 报 /var/lib/kafka/data writable 失败 |
第三方 AI 有时会建议 chown -R 1000:1000 修数据目录,但 confluentinc/cp-kafka:7.5.3 启动时 Docker 自动把挂载卷 chown 到 1000:1000,实际不会报这个错;若真报,先 docker logs --tail=50 kafka 看完整日志,90% 是其他原因(内存不够、KRaft 配置错、端口冲突) |
Kafka 启动报 UnboundLocalError: local variable 'lo' 或 dub 段错误(exit 139) |
cp-kafka:7.5.0 镜像内 urllib3 2.x 与 Python 3.9 _sre C 扩展的已知 bug,与宿主系统无关(CentOS/Ubuntu 同样会复现);升到 7.5.3+ 即可;若必须保留 7.5.0,用自定义 Dockerfile 在镜像内 pip install 'urllib3<2' |
Oracle 报 ORA-00845: MEMORY_TARGET not supported |
宿主机 /dev/shm 不足;确认 shm_size: 2gb 已写入 yml 且宿主 df -h /dev/shm 有 ≥ 2 GB |
Oracle 报 Cannot create folder : errno=13 : Permission denied : /opt/oracle/oradata/XE |
挂载目录的所有者与容器内 oracle 用户(uid 54321)不一致;执行 sudo chown -R 54321:54321 ~/docker-services/oracle-xe/(若 SELinux enforcing 加上 sudo chcon -Rt svirt_sandbox_file_t ~/docker-services/oracle-xe/) |
Oracle 启动末期报 ORA-27037: Too many levels of symbolic links |
容器内 spfileXE.ora 形成了软链死循环(通常由"上次启动失败时只 rm -rf data/* 没清空整个卷"导致);处理:docker compose stop oracle-xe → sudo rm -rf ~/docker-services/oracle-xe && mkdir -p ~/docker-services/oracle-xe/data → sudo chown -R 54321:54321 ~/docker-services/oracle-xe → 重新 up -d |
| RabbitMQ 启动后立刻退出 | 通常是 ulimit 或内存不足;docker inspect <id> 看 OOM 标志 |
permission denied 挂载目录 |
SELinux 标签问题,见 8 防火墙与 SELinux;Oracle 场景下用 chown 54321:54321 同时改宿主目录属主 |
10 待确认事项
下列信息未拿到官方原文,在文档里都采用了示例 yml 的当前值或留作粗略估计。回到官方页面复核后,建议替换为带引用链接的精确值:
- 每个官方镜像(
mongo:4.2、mysql:8、gvenzl/oracle-xe:21.3.0、rabbitmq:3.12-management、confluentinc/cp-kafka:7.5.3、redis:7)在 Linux x86_64 + AlmaLinux 9 上的官方推荐--memory/ulimit/--shm-size。候选页面:hub.docker.com/_/<name>与github.com/docker-library/<name>。 liaosnet/gbase8s:v8.8_3503x1_x64的官方 README(候选:hub.docker.com/r/liaosnet/gbase8s与github.com/liaosnet/gbase8s-docker)。- Kafka 7.5.3 在 KRaft 单节点下的推荐
KAFKA_HEAP_OPTS/KAFKA_NUM_PARTITIONS等;候选:docs.confluent.io/platform/current/installation/docker/image-reference.html。 - docker-compose-plugin 在 RHEL 9 仓库中的具体版本号随时间漂移,本文档示例命令未锁定具体版本。
如需把以上"待确认"替换为确定结论,可恢复对
docs.docker.com、hub.docker.com、docs.confluent.io等域的访问,回到对应页面复核。
11 参考来源
- Install Docker Engine on RHEL — Docker 官方 ✅ 已逐字引用
- Install Docker Engine — 索引页 ✅ 已确认(无 AlmaLinux 专属子页)
- Docker run reference —
mem_limit/shm_size参数语义 - Docker Engine — resource constraints — 资源约束语义
- AlmaLinux 9.0 Release Notes — 确认 AlmaLinux 9 与 RHEL 9 el9 通用
- AlmaLinux Wiki — Containers — Docker 子页当前 404,故未引用
维护建议:本教程示例 yml 改动时,记得同步 2 教学示例 中的端口表。

浙公网安备 33010602011771号