AlmaLinux 9 × Docker Compose 部署指南

适用范围:AlmaLinux 9(x86_64 / aarch64)
教学示例:本文档以一份覆盖关系型数据库、文档库、消息中间件、分析型数据库的多服务 Compose 配置为示例,完整演示在 AlmaLinux 9 上从零编写 docker-compose.yml、启动并验证整套服务的全过程。读者既可直接复现示例服务组合,亦可根据实际需要选用其中部分服务
撰写日期:2026/06/30


📑 目录


1 文档定位

本文档是面向 AlmaLinux 9 的 Docker / Compose 部署教学,内容涵盖:

  • Docker Engine 与 docker compose 插件(v2)的安装与验证
  • docker-compose.yml 的写法与常见字段
  • 一份多服务数据栈(MongoDB / MySQL / Oracle / GBase / RabbitMQ / Kafka / Redis)的部署示例

学习路径建议:

本文档不与任何既有 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 timeSystem timeLast 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/fstabNo such file or directory,说明本机没有传统的 fstab 机制,跳过该行命令,只保留 swapoff -a 即可;重启后如需持久化,按镜像提供方文档配置(常见方式是通过 systemd-swapzram-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 -hdocker 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-managerCurl error (35) ... Connection reset by peer

该错误是 TCP/TLS 握手阶段被对端 RST,常见原因:

  1. 网络环境屏蔽了 download.docker.com(国内网络环境、公司/校园网防火墙、WSL2 默认网络栈无法访问部分海外域名)
  2. HTTP/HTTPS 代理未透传到 WSL2:Windows 端开启了代理软件,WSL 内 http_proxy / https_proxy 未同步
  3. 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-cecontainerd.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' locally
  • docker pull <image>net/http: TLS handshake timeoutconnection 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.io DaoCloud 公共代理,免登录,推荐首选
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.cn USTC / 南京大学镜像,高校背景可信度较高
https://<your-id>.mirror.aliyuncs.com 阿里云个人加速器(控制台「容器镜像服务」→「镜像加速器」获取) 需登录阿里云账号
https://mirror.ccs.tencentyun.com 腾讯云个人加速器 需登录腾讯云账号

注意事项

  1. registry-mirrors 只对 Docker Hub 的 docker.io/* 镜像生效。Oracle、Confluent、GBase 等第三方仓库(各自独立的 registry)仍走直连,如遇不通需在 yml 中改用 image: <mirror>/<original-image> 的方式绕过。
  2. 镜像源可用性会随时间变化,建议每季度curl -I <url> 逐源验证,剔除不可达节点,避免每次拉取都被多个超时拖累。
  3. 部分小镜像站缺少 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 yesmaxmemory-policy allkeys-lru 等)。

5.6 校验 yml

docker compose config -q        # 语法/引用检查,无输出表示通过

5.7 低内存场景调优建议(WSL2 / 16GB 以下物理机)

5.3 完整 docker-compose.ymlmem_limit 是按 16 GB 物理内存、生产可用为目标设计的。在 WSL2 11 GB、笔记本 8 GB、单核虚机 4 GB 等内存吃紧场景下,直接 docker compose up -d 会出现 OOM、Oracle 启动崩溃等问题。本节给出经过验证的调优方案。

5.7.1 内存分档建议

按宿主物理内存分三档,只调 mem_limitshm_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

调优原则

  1. oracle-xegbase8s 是单台机器上最大的两个内存消耗者,内存吃紧时优先压这两个
  2. 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 54321oracle 用户运行,宿主挂载目录必须匹配该属主,否则容器报 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:9092PLAINTEXT://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 自动把挂载卷 chown1000: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-xesudo rm -rf ~/docker-services/oracle-xe && mkdir -p ~/docker-services/oracle-xe/datasudo 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 的当前值或留作粗略估计。回到官方页面复核后,建议替换为带引用链接的精确值:

  1. 每个官方镜像(mongo:4.2mysql:8gvenzl/oracle-xe:21.3.0rabbitmq:3.12-managementconfluentinc/cp-kafka:7.5.3redis:7)在 Linux x86_64 + AlmaLinux 9 上的官方推荐 --memory / ulimit / --shm-size。候选页面:hub.docker.com/_/<name>github.com/docker-library/<name>
  2. liaosnet/gbase8s:v8.8_3503x1_x64 的官方 README(候选:hub.docker.com/r/liaosnet/gbase8sgithub.com/liaosnet/gbase8s-docker)。
  3. Kafka 7.5.3 在 KRaft 单节点下的推荐 KAFKA_HEAP_OPTS / KAFKA_NUM_PARTITIONS 等;候选:docs.confluent.io/platform/current/installation/docker/image-reference.html
  4. docker-compose-plugin 在 RHEL 9 仓库中的具体版本号随时间漂移,本文档示例命令未锁定具体版本。

如需把以上"待确认"替换为确定结论,可恢复对 docs.docker.comhub.docker.comdocs.confluent.io 等域的访问,回到对应页面复核。


11 参考来源


维护建议:本教程示例 yml 改动时,记得同步 2 教学示例 中的端口表。

posted @ 2026-08-18 11:10  RK5123153  阅读(7)  评论(0)    收藏  举报