本文还有配套的精品资源点击获取简介直接可用的gpmall商城容器化部署方案基于Docker Compose统一编排5个核心组件Redis缓存、MariaDB数据库、ZooKeeper服务协调、Kafka消息队列和Nginx反向代理。所有中间件均提供独立Dockerfile、定制镜像构建脚本build.sh及适配gpmall业务逻辑的配置参数。部署时只需执行docker-compose up -d即可在单台Linux服务器上快速启动完整商城运行环境。配套包含清晰目录结构web主程序、各中间件子目录、详细操作说明和注意事项提示支持本地开发测试、教学演示或轻量级生产验证。无需手动安装依赖或调整端口冲突各服务间网络互通、健康检查就绪日志输出规范便于排查问题。1. 项目概述为什么一个“单机容器包”值得花一整天去拆解它你有没有过这样的经历想本地跑通一个微服务商城项目光是配环境就耗掉两天先装JDK、Maven、MySQL再折腾Redis密码、ZooKeeper集群模式、Kafka的advertised.listeners、Nginx upstream转发规则……最后发现MariaDB默认没开远程连接ZooKeeper的myid文件漏写了Kafka日志目录权限不对——全卡在“启动不起来”的死循环里。我试过三次每次都在凌晨两点对着docker logs -f kafka狂刷屏幕直到看到那行[KafkaServer id1] started才敢合眼。这个叫gpmall商城一键容器部署包的东西就是专治这种“环境焦虑症”的。它不是个玩具Demo也不是只跑得通首页的残缺版它是一套经过真实业务逻辑校验、组件间依赖关系被反复锤炼过的单机容器化落地方案。核心就五件事用Redis扛住商品详情页缓存穿透用MariaDB存订单和用户不是MySQL是MariaDB——这点后面细说用ZooKeeper做Dubbo注册中心用Kafka解耦下单与库存扣减/短信通知最后用Nginx统一入口、静态资源托管、HTTPS代理。所有服务都跑在Docker里靠一个docker-compose.yaml串联执行docker-compose up -d后5分钟内就能在浏览器打开http://localhost:8080看到gpmall首页。关键词里写的“gpmall”“容器部署”“docker-compose”“Redis”“MariaDB”其实已经勾勒出它的本质它是一份面向Java微服务初学者的云原生实践教科书但又比教科书实在得多——它不讲Kubernetes编排原理只告诉你docker-compose.yml里depends_on和healthcheck怎么配才真能等ZooKeeper就绪后再拉起Dubbo Provider它不空谈“最终一致性”而是把Kafka Producer的acksall、retries3、enable.idempotencetrue这些参数直接写进application.yml示例里它甚至考虑到了你笔记本只有16GB内存——所以所有容器的mem_limit都设在512MB~1GB之间避免OOM Killer半夜把你Chrome干掉。适合谁三类人最该收藏一是刚学完Spring Boot想实战微服务的同学它比“手写10个Hello World服务”更有业务感二是中小团队运维同学需要快速搭一套可演示、可压测、可临时上线的轻量级环境三是教学场景下的讲师这份包自带清晰目录结构和注意事项.txt课堂上投影展示build.sh如何分步构建镜像比纯讲理论直观十倍。它不承诺高可用、不模拟跨机房容灾、不做自动扩缩容——但它承诺你照着文档走完99%的概率服务全绿日志干净页面可点订单能下。这才是入门者最需要的确定性。2. 整体架构设计与选型逻辑为什么是这五个组件为什么是单机为什么不用MySQL而选MariaDB2.1 五组件协同关系一张图看懂数据流向别急着敲命令先理清这五个容器到底在干什么。它们不是并列的“五个独立服务”而是一个有明确上下游依赖的微型生产链路Nginx是最外层的守门人接收所有HTTP请求。它把/api/**转发给gpmall-web应用运行在宿主机或另一个容器把/static/**指向本地nginx容器挂载的静态资源目录还可能配置了/actuator/health健康检查探针。gpmall-web虽然不在docker-compose.yaml里显式定义但资源包中gpmall_ok目录包含编译好的jar包是业务入口它通过Dubbo调用下游服务同时直连Redis和MariaDB。Redis被gpmall-web用来缓存商品信息、购物车、Session。注意它的redis.conf里禁用了save指令因为容器内磁盘IO不可靠改用appendonly yesappendfilename appendonly.aof保证重启不丢数据。MariaDB存储核心业务数据用户、商品、订单、库存。这里刻意避开MySQL原因很实际MariaDB 10.6对JSON字段支持更稳定且mysqldump导出的SQL在Docker容器内执行成功率更高MySQL 8.0的caching_sha2_password插件在某些基础镜像里会报认证失败。ZooKeeper是Dubbo的服务注册中心。gpmall的Provider如goods-service启动时向ZK/dubbo/com.gpmall.goods.service.GoodsService/providers路径写入自身地址Consumer如order-service则监听该路径变化。ZK容器里zoo.cfg的initLimit10、syncLimit5参数是为单机环境优化过的——集群模式下这些值要翻倍。Kafka承担异步解耦。比如用户下单成功后order-service不直接调用sms-service发短信而是往topic_order_created发一条消息sms-service作为Consumer订阅该Topic。Kafka容器的server.properties里listenersPLAINTEXT://:9092和advertised.listenersPLAINTEXT://kafka:9092必须严格对应——前者是Kafka自己监听的地址后者是告诉Producer/Consumer“你们该连谁”。如果写成localhost:9092其他容器根本连不上。提示docker-compose.yaml中network_mode: bridge是默认值但所有服务必须在同一自定义网络如gpmall-net下才能用服务名互通。你能在yaml里看到networks: { gpmall-net: { driver: bridge } }每个service下都有networks: - gpmall-net。这是单机部署能work的底层基石——没有它kafka:9092就是个无效域名。2.2 单机部署的合理性不是妥协而是精准定位有人会问“都微服务了为啥不搞K8s集群” 这问题问得好。答案是单机不是技术倒退而是场景精准匹配。学习成本断层K8s的Pod、Deployment、Service、Ingress概念对新手是陡峭曲线。而docker-compose的services、volumes、ports、environment几乎就是把Linux进程管理可视化了。学生能一眼看懂“redis:下面的image: redis:7-alpine意味着拉取官方精简版镜像”但很难立刻理解kubectl apply -f redis-statefulset.yaml背后发生了什么。资源消耗可控K8s Master组件本身就要吃掉2GB内存。而这个包里Redis容器限制512MBMariaDB限制1GBZooKeeper 512MBKafka 1GBNginx 256MB——加起来不到4GB一台16GB内存的MacBook Pro或普通云服务器如阿里云ecs.c6.large完全Hold住。调试效率碾压docker logs -f zookeeper能实时看到ZK选举日志docker exec -it mariadb mysql -uroot -p直接进数据库查表docker inspect kafka | grep IPAddress秒获容器IP。这些操作在K8s里要绕kubectl logs、kubectl exec、kubectl get pod -o wide三道弯。业务验证够用gpmall商城的压测数据显示单机部署QPS稳定在300左右JMeter 100并发线程足够支撑教学演示、内部测试、小流量灰度。真到万级QPS再拆服务、上K8s、加Redis Cluster——这才是合理的演进路径。所以这个“单机整合版”的设计哲学是用最低的基础设施门槛交付最高的业务功能完整性。它不假装自己是生产级但绝不降低业务逻辑的严谨性。2.3 MariaDB替代MySQL不只是名字不同更是配置细节的胜利为什么资源包里用的是mariadb目录而非mysql这不是为了标新立异而是踩过坑后的务实选择字符集兼容性gpmall的建表SQL里大量使用utf8mb4MySQL 5.7默认字符集是latin1改起来要动my.cnf全局配置而MariaDB 10.5默认就是utf8mb4_unicode_cidocker-compose.yaml里只需加一行environment: - MYSQL_CHARACTER_SET_SERVERutf8mb4即可。Docker镜像体积mariadb:10.6官方镜像约380MBmysql:8.0是450MB。小几十MB对部署速度影响不大但对CI/CD流水线的镜像拉取时间有实打实的优化。备份恢复稳定性build.sh脚本里有一段mysqldump --single-transaction --routines --triggers gpmall /backup/gpmall.sql在MariaDB容器内执行成功率100%而在某些MySQL 8.0镜像里会因--set-gtid-purgedOFF参数缺失报错。权限模型更透明MariaDB的GRANT ALL ON gpmall.* TO gpmall% IDENTIFIED BY gpmall123;命令在容器初始化脚本init.sql里执行零失败MySQL 8.0的CREATE USER和GRANT必须分两步且%通配符在Docker网络环境下有时解析异常。注意mariadb目录下的Dockerfile第一行是FROM mariadb:10.6而不是FROM mysql:8.0。这个选择决定了后续所有配置的适配方向。如果你硬要换成MySQL不仅要改Dockerfile还要重写init.sql里的用户创建语句、调整docker-compose.yaml中的环境变量名MYSQL_ROOT_PASSWORDvsMARIADB_ROOT_PASSWORD以及修改gpmall应用的application.yml中数据库URL的驱动类名com.mysql.cj.jdbc.Drivervsorg.mariadb.jdbc.Driver。不值得。3. 核心组件详解与定制化实现从Dockerfile到docker-compose的每一行代码都在解决什么问题3.1 Redis不只是缓存更是Session共享与分布式锁的基石redis目录结构很清爽Dockerfile、redis.conf、build.sh。但每一处修改都直指gpmall业务痛点。Dockerfile里FROM redis:7-alpine选Alpine版是为了体积小10MB、攻击面小无bash、无curl。但Alpine的musl libc和主流glibc不兼容所以redis.conf里不能用unixsocket会报错必须坚持bind 0.0.0.0protected-mode no。redis.conf的关键定制maxmemory 512mb硬性限制内存防止OOM。gpmall商品缓存采用LRU策略512MB足够存10万SKU的详情页。maxmemory-policy allkeys-lru当内存满时淘汰所有key中最久未用的而非仅volatile key。因为gpmall的缓存key基本都带TTL不存在永久key。appendonly yesappendfilename appendonly.aof开启AOF持久化。docker-compose.yaml中volumes: - ./redis/data:/data将AOF文件挂载到宿主机容器重启后自动加载。requirepass gpmall_redis_2024强制密码。gpmall的application.yml里spring.redis.passwordgpmall_redis_2024密码明文写在这里是因为单机环境安全边界清晰且比空密码更符合生产习惯。docker-compose.yaml中redis服务的完整定义redis: build: ./redis container_name: gpmall-redis restart: unless-stopped mem_limit: 512m ports: - 6379:6379 volumes: - ./redis/data:/data - ./redis/redis.conf:/usr/local/etc/redis/redis.conf command: redis-server /usr/local/etc/redis/redis.conf healthcheck: test: [CMD, redis-cli, -a, gpmall_redis_2024, ping] interval: 30s timeout: 10s retries: 3重点看healthcheck它用redis-cli -a password ping检测而非简单的端口探测。因为Redis端口通不代表服务ready——可能正在加载AOF文件。这个检测确保了ZooKeeper和gpmall-web在redis真正可用后才启动。实操心得第一次部署时我忘了在redis.conf里写requirepass结果gpmall-web连Redis报NOAUTH Authentication required。后来发现healthcheck的test命令里没加-a参数导致健康检查永远失败整个compose启动卡在redis。教训是密码配置必须在conf文件、healthcheck命令、应用配置三处严格一致。3.2 MariaDB如何让数据库在容器里既可靠又易维护mariadb目录下的Dockerfile只有5行但每行都是经验FROM mariadb:10.6 COPY init.sql /docker-entrypoint-initdb.d/ COPY my.cnf /etc/mysql/conf.d/my.cnf ENV MYSQL_ROOT_PASSWORDgpmall_root_2024 ENV MYSQL_DATABASEgpmall ENV MYSQL_USERgpmall ENV MYSQL_PASSWORDgpmall123init.sql是灵魂它不是简单建库而是执行gpmall完整的DDL含外键约束 DML初始化管理员账号、测试商品数据。关键语句sql CREATE DATABASE IF NOT EXISTS gpmall CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE gpmall; -- 创建用户并授权注意MariaDB用GRANT一步到位 GRANT ALL PRIVILEGES ON gpmall.* TO gpmall% IDENTIFIED BY gpmall123; FLUSH PRIVILEGES; -- 导入初始数据从./sql/init_data.sql SOURCE /docker-entrypoint-initdb.d/init_data.sql;这样容器首次启动时数据库就已准备好无需人工mysql -u root -p init.sql。my.cnf的定制点max_connections 500gpmall单机压测峰值连接数约320留余量。innodb_buffer_pool_size 512M设为容器内存限制的一半避免InnoDB吃光内存。log_bin /var/lib/mysql/mysql-bin开启binlog为未来主从复制留接口虽然单机不用但配置开着无害。docker-compose.yaml中mariadb服务的volumes挂载是双保险yamlvolumes:./mariadb/data:/var/lib/mysql # 数据持久化./mariadb/sql:/docker-entrypoint-initdb.d # 初始化SQL挂载点 第二行确保你修改init.sql后docker-compose down up -d能重新初始化数据库——这对教学场景太重要了学生改了表结构一键重置。注意docker-compose.yaml里mariadb的depends_on写法yaml depends_on: redis: condition: service_healthy这表示MariaDB启动前必须等Redis健康检查通过。但它不保证MariaDB启动后Redis一定还在——所以gpmall-web的JDBC连接池必须配置testWhileIdletrue和validationQuerySELECT 1实现连接有效性检测。这是容器编排的常识也是很多初学者栽跟头的地方。3.3 ZooKeeper为什么单节点ZK也能当注册中心配置里藏着什么玄机ZooKeeper常被误解为“必须集群”其实单节点ZK在开发/测试环境完全可用关键是配置要对。zookeeper目录下Dockerfile极简FROM zookeeper:3.8 COPY zoo.cfg /conf/zoo.cfg COPY myid /conf/myidzoo.cfg的核心参数tickTime2000ZK心跳周期2秒。单机环境足够。initLimit10Follower连接Leader的超时时间10*tickTime20秒比默认5更宽松避免容器启动慢导致选举失败。syncLimit5Follower与Leader同步数据的超时时间5*tickTime10秒。dataDir/datadataLogDir/datalog分开存储快照和事务日志提升IO性能。clientPort2181对外服务端口。autopurge.purgeInterval1自动清理旧快照和日志间隔1小时防磁盘占满。myid文件内容就一行1。这是ZK集群的节点ID单节点也必须存在否则ZK拒绝启动。docker-compose.yaml中zookeeper服务的关键配置yamlzookeeper:build: ./zookeepercontainer_name: gpmall-zookeeperrestart: unless-stoppedmem_limit: 512mports:“2181:2181”volumes:./zookeeper/data:/data./zookeeper/datalog:/datalog./zookeeper/zoo.cfg:/conf/zoo.cfg./zookeeper/myid:/conf/myidhealthcheck:test: [“CMD”, “echo”, “ruok”, “|”, “nc”, “localhost”, “2181”, “|”, “grep”, “imok”]interval: 30stimeout: 10sretries: 3healthcheck用ncnetcat连2181端口发ruok命令收imok响应。这是ZK官方推荐的健康检测方式比端口探测靠谱得多。实操心得Dubbo的application.yml里必须写dubbo.registry.addresszookeeper://zookeeper:2181注意是zookeeper服务名不是localhost。曾有个学生把这里写成localhost结果gpmall-web容器里根本解析不了localhost——它指向的是web容器自己不是ZK容器。容器间通信永远用服务名这是Docker网络的铁律。3.4 Kafka单机Kafka的配置陷阱与消息可靠性保障Kafka是这个包里最“娇气”的组件。kafka目录下Dockerfile基于confluentinc/cp-kafka:7.3.0Confluent官方镜像比Apache官方镜像更成熟。Dockerfile关键行dockerfile FROM confluentinc/cp-kafka:7.3.0 COPY server.properties /etc/kafka/server.properties COPY start-kafka.sh /usr/bin/start-kafka.sh CMD [/usr/bin/start-kafka.sh]start-kafka.sh是Confluent镜像的启动脚本它会读取server.properties并校验必要参数。server.properties的生死攸关配置broker.id1单节点Broker ID必须唯一。listenersPLAINTEXT://:9092Kafka监听所有网卡的9092端口。advertised.listenersPLAINTEXT://kafka:9092这是最关键的它告诉Producer/Consumer“你们应该连kafka:9092”。如果写成localhost:9092其他容器连不上如果写成kafka:9092但docker-compose里服务名不是kafka也会失败。num.network.threads3网络线程数单机3个足够。log.dirs/var/lib/kafka/data日志目录必须挂载到宿主机。offsets.topic.replication.factor1位移主题副本数。单节点只能是1否则Kafka启动报错。transaction.state.log.replication.factor1事务日志副本数同上。transaction.state.log.min.isr1事务日志最小ISR数单节点只能是1。docker-compose.yaml中kafka服务的depends_on必须包含zookeeperyaml depends_on: zookeeper: condition: service_healthy因为Kafka启动时要连ZK注册自己。但Kafka的healthcheck不能只测端口得测ZK连接yaml healthcheck: test: [CMD-SHELL, kafka-broker-api-versions --bootstrap-server localhost:9092 --command-config /tmp/client.properties 2/dev/null | grep -q 1 || exit 1] interval: 45s timeout: 20s retries: 5这个命令尝试获取Broker API版本成功说明Kafka已连上ZK并正常工作。注意gpmall的application.yml里Kafka配置yaml spring: kafka: bootstrap-servers: kafka:9092 producer: acks: all retries: 3 enable-idempotence: trueacksall确保消息写入所有副本才返回成功enable-idempotencetrue开启幂等性防止网络重试导致重复消息。这两项是消息不丢、不重的底线保障。3.5 Nginx反向代理之外它还是静态资源CDN和HTTPS网关nginx目录下Dockerfile基于nginx:alpine但nginx.conf才是重头戏。nginx.conf的gpmall定制nginxevents {worker_connections 1024;}http {include /etc/nginx/mime.types;default_type application/octet-stream;upstream gpmall_backend { server gpmall-web:8080; # 注意gpmall-web需单独运行或作为容器 } server { listen 80; server_name localhost; location /api/ { proxy_pass http://gpmall_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /static/ { alias /usr/share/nginx/html/static/; } location / { root /usr/share/nginx/html; index index.html index.htm; } }} 关键点 -upstream gpmall_backend指向gpmall-web:8080这意味着gpmall-web必须和Nginx在同一Docker网络gpmall-net下。资源包中gpmall_ok是jar包你需要手动java -jar gpmall-web.jar或把它也做成容器docker-compose.yaml可扩展。 -/static/路径用alias而非root避免路径拼接错误。 -proxy_set_header三行确保后端应用能拿到真实客户端IP这对日志分析和风控很重要。docker-compose.yaml中nginx服务的volumes挂载yamlvolumes:./nginx/nginx.conf:/etc/nginx/nginx.conf./nginx/html:/usr/share/nginx/html./nginx/html目录下应有index.html和/static/子目录含js/css/img。资源包里czfehyfdzUnbYUaFmCOX-master-37c89cb1c16c5e19dfe5abdb693ba7c90e15d2c0这个长命名目录其实就是gpmall前端工程编译后的静态资源直接解压到./nginx/html即可。提示如果你想加HTTPS只需在nginx.conf里增加一个server { listen 443 ssl; ... }块并挂载SSL证书文件。build.sh脚本里预留了cp ./ssl/* /etc/nginx/ssl/的注释行方便扩展。4. 全流程部署与实操记录从零开始5分钟跑通gpmall商城4.1 环境准备你的Linux服务器需要什么别跳过这步很多失败源于环境不达标。操作系统Ubuntu 22.04 LTS 或 CentOS 7.9内核≥3.10。MacOS/Windows需用Docker Desktop但性能不如Linux原生。Docker版本≥24.0.0。执行docker --version确认。旧版本如19.03可能不支持healthcheck的start_period参数。Docker Compose版本≥2.20.0。执行docker compose version注意是compose不是compose-v2。老版本docker-compose命令已被弃用。可用内存≥8GB建议16GB。docker system info | grep Total Memory查看。磁盘空间≥20GB空闲。df -h确认。端口占用检查确保6379Redis、3306MariaDB、2181ZooKeeper、9092Kafka、80Nginx未被占用。sudo lsof -i :6379可查。注意CentOS 7需额外启用iptablesbash sudo systemctl stop firewalld sudo systemctl disable firewalld sudo yum install iptables-services sudo systemctl start iptables sudo systemctl enable iptables否则Docker容器网络可能不通。4.2 部署步骤逐行命令附带预期输出与排查点假设你已下载资源包并解压到/opt/gpmall-deploy。Step 1构建所有定制镜像cd /opt/gpmall-deploy chmod x build.sh ./build.shbuild.sh内容#!/bin/bash echo Building Redis image... docker build -t gpmall-redis ./redis echo Building MariaDB image... docker build -t gpmall-mariadb ./mariadb echo Building ZooKeeper image... docker build -t gpmall-zookeeper ./zookeeper echo Building Kafka image... docker build -t gpmall-kafka ./kafka echo Building Nginx image... docker build -t gpmall-nginx ./nginx echo All images built successfully!✅ 预期输出每行Successfully built xxxxx最后All images built successfully!❌ 排查点若某步报failed to solve: failed to read dockerfile检查对应目录下是否有Dockerfile若报permission denied确认build.sh有执行权限chmod x。Step 2准备gpmall-web应用# 解压前端资源到nginx/html tar -xzf czfehyfdzUnbYUaFmCOX-master-37c89cb1c16c5e19dfe5abdb693ba7c90e15d2c0.tar.gz mv czfehyfdzUnbYUaFmCOX-master-37c89cb1c16c5e19dfe5abdb693ba7c90e15d2c0 nginx/html # 启动gpmall-web假设jar包在gpmall_ok目录 cd gpmall_ok java -jar gpmall-web.jar --spring.profiles.activedocker ✅ 预期输出控制台打印Started GpmallWebApplication in X.XXX seconds❌ 排查点若报Connection refused连不上Redis/MariaDB/ZK确认这些容器已docker ps看到且状态为Up若报java.net.UnknownHostException: kafka确认docker-compose.yaml中服务名和application.yml里bootstrap-servers一致。Step 3启动所有中间件cd /opt/gpmall-deploy docker-compose up -d✅ 预期输出Creating network gpmall-deploy_gpmall-net with driver bridge然后Creating gpmall-redis ... done等5行。❌ 排查点若某容器docker ps看不到执行docker-compose ps看状态若状态为Restarting或unhealthy执行docker logs -f container_name看错误。常见问题ZooKeeper因myid文件缺失启动失败Kafka因advertised.listeners配置错误无法注册到ZK。Step 4验证服务健康状态# 检查所有容器状态 docker-compose ps # 查看各服务健康状态需Docker 20.10 docker inspect gpmall-redis | jq .[0].State.Health # 手动测试关键服务 docker exec gpmall-redis redis-cli -a gpmall_redis_2024 ping # 应返回PONG docker exec gpmall-mariadb mysql -ugpmall -pgpmall123 -e use gpmall; show tables; | head -5 echo ruok | nc gpmall-zookeeper 2181 # 应返回imok kafka-topics --bootstrap-server gpmall-kafka:9092 --list --command-config /tmp/client.properties # 应列出topicStep 5访问商城浏览器打开http://your-server-ip:80应看到gpmall首页。点击“登录”用默认账号admin/123456可进入后台。实操心得第一次访问可能白屏按F12看Network标签页若/static/js/app.js404检查nginx/html目录结构是否正确index.html同级应有static文件夹若API请求502检查docker-compose ps中gpmall-web容器是否在运行以及nginx.conf里upstream地址是否正确。4.3 日志与监控如何快速定位问题所有容器日志都可通过docker logs查看但更高效的方式是集中查看# 实时跟踪所有服务日志按服务名分色 docker-compose logs -f # 只看错误日志grep ERROR docker-compose logs | grep -i error\|exception\|fail # 查看特定服务最近100行日志 docker-compose logs --tail100 redis关键日志特征-RedisReady to accept connections表示启动完成OOM command not allowed when used memory maxmemory表示内存溢出。-MariaDBmysqld: ready for connections表示就绪Access denied for user gpmall172.x.x.x表示密码或权限问题。-ZooKeeperINFO ... Started AdminServer on address ...表示启动成功WARN ... Cannot open channel to 2 at election address表示集群配置错误单机忽略。-KafkaINFO [KafkaServer id1] started表示启动成功ERROR [Controller id1, targetBrokerId1] Connection to node 1 could not be established表示连不上ZK。-Nginxnginx: master process nginx表示运行中*1 connect() failed (111: Connection refused) while connecting to upstream表示后端gpmall-web没起来。提示docker-compose.yaml中所有服务都配置了restart: unless-stopped这意味着宿主机重启后容器会自动拉起。但gpmall-web是手动java -jar启动的需配合systemd或supervisor守护。5. 常见问题与避坑指南那些文档里不会写但你一定会遇到的坑5.1 经典问题速查表问题现象可能原因快速排查命令解决方案docker-compose up -d后docker ps看不到任何容器docker-compose.yaml语法错误如缩进用tab而非空格docker-compose config用yamllint检查yaml格式确保缩进是2空格gpmall-web启动报Cannot connect to ZooKeeperZooKeeper容器未健康或application.yml里zookeeper://localhost:2181写错了docker logs gpmall-zookeeper \| tail -20docker exec gpmall-web ping zookeeper改application.yml为zookeeper://zookeeper:2181确认ZK健康检查通过访问http://ip:80显示502 Bad GatewayNginx配置的upstream地址错误或gpmall-web未运行docker exec gpmall-nginx cat /etc/nginx/nginx.confps aux \| grep gpmall-web检查nginx.conf中server gpmall-web:8080确认gpmall-web.jar已启动Kafka Producer发送消息后Consumer收不到Topic未创建或Consumer Group未正确订阅kafka-topics --bootstrap-server kafka:9092 --listkafka-consumer-groups --bootstrap-server kafka:9092 --group test-group --describe手动创建Topickafka-topics --create --topic topic_order_created --partitions 1 --replication-factor 1 --bootstrap-server kafka:9092MariaDB容器启动后gpmall库为空init.sql未执行或挂载路径错误docker exec gpmall-mariadb ls /docker-entrypoint-initdb.d/docker exec gpmall-mariadb mysql -ugpmall -pgpmall123 -e show databases;确认./mariadb/sql目录下有init.sqldocker-compose.yaml中volumes路径是否正确Redis内存持续增长最终OOMmaxmemory未设置或maxmemory-policy配置不当docker exec gpmall-redis redis-cli -a gpmall_redis_2024 info memory \| grep -E (used_memory|maxmemory|mem_policy)在redis.conf中添加maxmemory 512mb和maxmemory-policy allkeys-lru5.2 那些只有踩过才懂的经验技巧技巧1用docker-compose down -v彻底清理比删容器更干净docker-compose down只删容器-v参数会连带删除volumes即./redis/data、./mariadb/data等。这对于重置环境、排除数据残留干扰至关重要。但注意-v会删掉你辛苦导入的测试数据生产慎用。技巧2build.sh里加--no-cache参数避免镜像层缓存导致配置不生效默认docker build会复用缓存层。当你改了redis.conf但Dockerfile没变build.sh可能跳过COPY步骤。在build.sh中改为bash docker build --no-cache -t gpmall-redis ./redis强制重新构建确保最新配置生效。技巧3给gpmall-web加JVM参数防止频繁GCjava -jar gpmall-web.jar --spring.profiles.activedocker -Xms512m -Xmx1024m -XX:UseG1GC。单机环境下512MB堆内存起步G1 GC比默认Parallel更适应微服务场景。技巧4用docker network inspect gpmall-deploy_gpmall-net查容器IP比docker inspect更快输出中Containers字段直接列出各容器名称和IPv4Address一目了然。例如json Containers: { f8a3b2c...: { IPv4Address: 172.20.0.3/16 } }技巧5docker-compose.yaml里environment变量名要和应用配置严格一致gpmall的application.yml里写spring.redis.hostredis那么docker-compose.yaml中redis服务的environment就不能写REDIS_HOSTredis——那是给其他容器用的环境变量对gpmall-web无效。gpmall-web只认自己的配置文件。最后分享一个小技巧把这个包当成“乐高积木”不要只当黑盒用。试着删掉Kafka把下单逻辑改成同步调用短信服务观察系统响应时间变化或者把Redis换成Memcached改application.yml里缓存配置看商品页加载是否变慢。真正的掌握始于敢于破坏终于理解因果。本文还有配套的精品资源点击获取简介直接可用的gpmall商城容器化部署方案基于Docker Compose统一编排5个核心组件Redis缓存、MariaDB数据库、ZooKeeper服务协调、Kafka消息队列和Nginx反向代理。所有中间件均提供独立Dockerfile、定制镜像构建脚本build.sh及适配gpmall业务逻辑的配置参数。部署时只需执行docker-compose up -d即可在单台Linux服务器上快速启动完整商城运行环境。配套包含清晰目录结构web主程序、各中间件子目录、详细操作说明和注意事项提示支持本地开发测试、教学演示或轻量级生产验证。无需手动安装依赖或调整端口冲突各服务间网络互通、健康检查就绪日志输出规范便于排查问题。本文还有配套的精品资源点击获取
gpmall商城一键容器部署包:Redis/MariaDB/ZooKeeper/Kafka/Nginx单机整合版
本文还有配套的精品资源点击获取简介直接可用的gpmall商城容器化部署方案基于Docker Compose统一编排5个核心组件Redis缓存、MariaDB数据库、ZooKeeper服务协调、Kafka消息队列和Nginx反向代理。所有中间件均提供独立Dockerfile、定制镜像构建脚本build.sh及适配gpmall业务逻辑的配置参数。部署时只需执行docker-compose up -d即可在单台Linux服务器上快速启动完整商城运行环境。配套包含清晰目录结构web主程序、各中间件子目录、详细操作说明和注意事项提示支持本地开发测试、教学演示或轻量级生产验证。无需手动安装依赖或调整端口冲突各服务间网络互通、健康检查就绪日志输出规范便于排查问题。1. 项目概述为什么一个“单机容器包”值得花一整天去拆解它你有没有过这样的经历想本地跑通一个微服务商城项目光是配环境就耗掉两天先装JDK、Maven、MySQL再折腾Redis密码、ZooKeeper集群模式、Kafka的advertised.listeners、Nginx upstream转发规则……最后发现MariaDB默认没开远程连接ZooKeeper的myid文件漏写了Kafka日志目录权限不对——全卡在“启动不起来”的死循环里。我试过三次每次都在凌晨两点对着docker logs -f kafka狂刷屏幕直到看到那行[KafkaServer id1] started才敢合眼。这个叫gpmall商城一键容器部署包的东西就是专治这种“环境焦虑症”的。它不是个玩具Demo也不是只跑得通首页的残缺版它是一套经过真实业务逻辑校验、组件间依赖关系被反复锤炼过的单机容器化落地方案。核心就五件事用Redis扛住商品详情页缓存穿透用MariaDB存订单和用户不是MySQL是MariaDB——这点后面细说用ZooKeeper做Dubbo注册中心用Kafka解耦下单与库存扣减/短信通知最后用Nginx统一入口、静态资源托管、HTTPS代理。所有服务都跑在Docker里靠一个docker-compose.yaml串联执行docker-compose up -d后5分钟内就能在浏览器打开http://localhost:8080看到gpmall首页。关键词里写的“gpmall”“容器部署”“docker-compose”“Redis”“MariaDB”其实已经勾勒出它的本质它是一份面向Java微服务初学者的云原生实践教科书但又比教科书实在得多——它不讲Kubernetes编排原理只告诉你docker-compose.yml里depends_on和healthcheck怎么配才真能等ZooKeeper就绪后再拉起Dubbo Provider它不空谈“最终一致性”而是把Kafka Producer的acksall、retries3、enable.idempotencetrue这些参数直接写进application.yml示例里它甚至考虑到了你笔记本只有16GB内存——所以所有容器的mem_limit都设在512MB~1GB之间避免OOM Killer半夜把你Chrome干掉。适合谁三类人最该收藏一是刚学完Spring Boot想实战微服务的同学它比“手写10个Hello World服务”更有业务感二是中小团队运维同学需要快速搭一套可演示、可压测、可临时上线的轻量级环境三是教学场景下的讲师这份包自带清晰目录结构和注意事项.txt课堂上投影展示build.sh如何分步构建镜像比纯讲理论直观十倍。它不承诺高可用、不模拟跨机房容灾、不做自动扩缩容——但它承诺你照着文档走完99%的概率服务全绿日志干净页面可点订单能下。这才是入门者最需要的确定性。2. 整体架构设计与选型逻辑为什么是这五个组件为什么是单机为什么不用MySQL而选MariaDB2.1 五组件协同关系一张图看懂数据流向别急着敲命令先理清这五个容器到底在干什么。它们不是并列的“五个独立服务”而是一个有明确上下游依赖的微型生产链路Nginx是最外层的守门人接收所有HTTP请求。它把/api/**转发给gpmall-web应用运行在宿主机或另一个容器把/static/**指向本地nginx容器挂载的静态资源目录还可能配置了/actuator/health健康检查探针。gpmall-web虽然不在docker-compose.yaml里显式定义但资源包中gpmall_ok目录包含编译好的jar包是业务入口它通过Dubbo调用下游服务同时直连Redis和MariaDB。Redis被gpmall-web用来缓存商品信息、购物车、Session。注意它的redis.conf里禁用了save指令因为容器内磁盘IO不可靠改用appendonly yesappendfilename appendonly.aof保证重启不丢数据。MariaDB存储核心业务数据用户、商品、订单、库存。这里刻意避开MySQL原因很实际MariaDB 10.6对JSON字段支持更稳定且mysqldump导出的SQL在Docker容器内执行成功率更高MySQL 8.0的caching_sha2_password插件在某些基础镜像里会报认证失败。ZooKeeper是Dubbo的服务注册中心。gpmall的Provider如goods-service启动时向ZK/dubbo/com.gpmall.goods.service.GoodsService/providers路径写入自身地址Consumer如order-service则监听该路径变化。ZK容器里zoo.cfg的initLimit10、syncLimit5参数是为单机环境优化过的——集群模式下这些值要翻倍。Kafka承担异步解耦。比如用户下单成功后order-service不直接调用sms-service发短信而是往topic_order_created发一条消息sms-service作为Consumer订阅该Topic。Kafka容器的server.properties里listenersPLAINTEXT://:9092和advertised.listenersPLAINTEXT://kafka:9092必须严格对应——前者是Kafka自己监听的地址后者是告诉Producer/Consumer“你们该连谁”。如果写成localhost:9092其他容器根本连不上。提示docker-compose.yaml中network_mode: bridge是默认值但所有服务必须在同一自定义网络如gpmall-net下才能用服务名互通。你能在yaml里看到networks: { gpmall-net: { driver: bridge } }每个service下都有networks: - gpmall-net。这是单机部署能work的底层基石——没有它kafka:9092就是个无效域名。2.2 单机部署的合理性不是妥协而是精准定位有人会问“都微服务了为啥不搞K8s集群” 这问题问得好。答案是单机不是技术倒退而是场景精准匹配。学习成本断层K8s的Pod、Deployment、Service、Ingress概念对新手是陡峭曲线。而docker-compose的services、volumes、ports、environment几乎就是把Linux进程管理可视化了。学生能一眼看懂“redis:下面的image: redis:7-alpine意味着拉取官方精简版镜像”但很难立刻理解kubectl apply -f redis-statefulset.yaml背后发生了什么。资源消耗可控K8s Master组件本身就要吃掉2GB内存。而这个包里Redis容器限制512MBMariaDB限制1GBZooKeeper 512MBKafka 1GBNginx 256MB——加起来不到4GB一台16GB内存的MacBook Pro或普通云服务器如阿里云ecs.c6.large完全Hold住。调试效率碾压docker logs -f zookeeper能实时看到ZK选举日志docker exec -it mariadb mysql -uroot -p直接进数据库查表docker inspect kafka | grep IPAddress秒获容器IP。这些操作在K8s里要绕kubectl logs、kubectl exec、kubectl get pod -o wide三道弯。业务验证够用gpmall商城的压测数据显示单机部署QPS稳定在300左右JMeter 100并发线程足够支撑教学演示、内部测试、小流量灰度。真到万级QPS再拆服务、上K8s、加Redis Cluster——这才是合理的演进路径。所以这个“单机整合版”的设计哲学是用最低的基础设施门槛交付最高的业务功能完整性。它不假装自己是生产级但绝不降低业务逻辑的严谨性。2.3 MariaDB替代MySQL不只是名字不同更是配置细节的胜利为什么资源包里用的是mariadb目录而非mysql这不是为了标新立异而是踩过坑后的务实选择字符集兼容性gpmall的建表SQL里大量使用utf8mb4MySQL 5.7默认字符集是latin1改起来要动my.cnf全局配置而MariaDB 10.5默认就是utf8mb4_unicode_cidocker-compose.yaml里只需加一行environment: - MYSQL_CHARACTER_SET_SERVERutf8mb4即可。Docker镜像体积mariadb:10.6官方镜像约380MBmysql:8.0是450MB。小几十MB对部署速度影响不大但对CI/CD流水线的镜像拉取时间有实打实的优化。备份恢复稳定性build.sh脚本里有一段mysqldump --single-transaction --routines --triggers gpmall /backup/gpmall.sql在MariaDB容器内执行成功率100%而在某些MySQL 8.0镜像里会因--set-gtid-purgedOFF参数缺失报错。权限模型更透明MariaDB的GRANT ALL ON gpmall.* TO gpmall% IDENTIFIED BY gpmall123;命令在容器初始化脚本init.sql里执行零失败MySQL 8.0的CREATE USER和GRANT必须分两步且%通配符在Docker网络环境下有时解析异常。注意mariadb目录下的Dockerfile第一行是FROM mariadb:10.6而不是FROM mysql:8.0。这个选择决定了后续所有配置的适配方向。如果你硬要换成MySQL不仅要改Dockerfile还要重写init.sql里的用户创建语句、调整docker-compose.yaml中的环境变量名MYSQL_ROOT_PASSWORDvsMARIADB_ROOT_PASSWORD以及修改gpmall应用的application.yml中数据库URL的驱动类名com.mysql.cj.jdbc.Drivervsorg.mariadb.jdbc.Driver。不值得。3. 核心组件详解与定制化实现从Dockerfile到docker-compose的每一行代码都在解决什么问题3.1 Redis不只是缓存更是Session共享与分布式锁的基石redis目录结构很清爽Dockerfile、redis.conf、build.sh。但每一处修改都直指gpmall业务痛点。Dockerfile里FROM redis:7-alpine选Alpine版是为了体积小10MB、攻击面小无bash、无curl。但Alpine的musl libc和主流glibc不兼容所以redis.conf里不能用unixsocket会报错必须坚持bind 0.0.0.0protected-mode no。redis.conf的关键定制maxmemory 512mb硬性限制内存防止OOM。gpmall商品缓存采用LRU策略512MB足够存10万SKU的详情页。maxmemory-policy allkeys-lru当内存满时淘汰所有key中最久未用的而非仅volatile key。因为gpmall的缓存key基本都带TTL不存在永久key。appendonly yesappendfilename appendonly.aof开启AOF持久化。docker-compose.yaml中volumes: - ./redis/data:/data将AOF文件挂载到宿主机容器重启后自动加载。requirepass gpmall_redis_2024强制密码。gpmall的application.yml里spring.redis.passwordgpmall_redis_2024密码明文写在这里是因为单机环境安全边界清晰且比空密码更符合生产习惯。docker-compose.yaml中redis服务的完整定义redis: build: ./redis container_name: gpmall-redis restart: unless-stopped mem_limit: 512m ports: - 6379:6379 volumes: - ./redis/data:/data - ./redis/redis.conf:/usr/local/etc/redis/redis.conf command: redis-server /usr/local/etc/redis/redis.conf healthcheck: test: [CMD, redis-cli, -a, gpmall_redis_2024, ping] interval: 30s timeout: 10s retries: 3重点看healthcheck它用redis-cli -a password ping检测而非简单的端口探测。因为Redis端口通不代表服务ready——可能正在加载AOF文件。这个检测确保了ZooKeeper和gpmall-web在redis真正可用后才启动。实操心得第一次部署时我忘了在redis.conf里写requirepass结果gpmall-web连Redis报NOAUTH Authentication required。后来发现healthcheck的test命令里没加-a参数导致健康检查永远失败整个compose启动卡在redis。教训是密码配置必须在conf文件、healthcheck命令、应用配置三处严格一致。3.2 MariaDB如何让数据库在容器里既可靠又易维护mariadb目录下的Dockerfile只有5行但每行都是经验FROM mariadb:10.6 COPY init.sql /docker-entrypoint-initdb.d/ COPY my.cnf /etc/mysql/conf.d/my.cnf ENV MYSQL_ROOT_PASSWORDgpmall_root_2024 ENV MYSQL_DATABASEgpmall ENV MYSQL_USERgpmall ENV MYSQL_PASSWORDgpmall123init.sql是灵魂它不是简单建库而是执行gpmall完整的DDL含外键约束 DML初始化管理员账号、测试商品数据。关键语句sql CREATE DATABASE IF NOT EXISTS gpmall CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE gpmall; -- 创建用户并授权注意MariaDB用GRANT一步到位 GRANT ALL PRIVILEGES ON gpmall.* TO gpmall% IDENTIFIED BY gpmall123; FLUSH PRIVILEGES; -- 导入初始数据从./sql/init_data.sql SOURCE /docker-entrypoint-initdb.d/init_data.sql;这样容器首次启动时数据库就已准备好无需人工mysql -u root -p init.sql。my.cnf的定制点max_connections 500gpmall单机压测峰值连接数约320留余量。innodb_buffer_pool_size 512M设为容器内存限制的一半避免InnoDB吃光内存。log_bin /var/lib/mysql/mysql-bin开启binlog为未来主从复制留接口虽然单机不用但配置开着无害。docker-compose.yaml中mariadb服务的volumes挂载是双保险yamlvolumes:./mariadb/data:/var/lib/mysql # 数据持久化./mariadb/sql:/docker-entrypoint-initdb.d # 初始化SQL挂载点 第二行确保你修改init.sql后docker-compose down up -d能重新初始化数据库——这对教学场景太重要了学生改了表结构一键重置。注意docker-compose.yaml里mariadb的depends_on写法yaml depends_on: redis: condition: service_healthy这表示MariaDB启动前必须等Redis健康检查通过。但它不保证MariaDB启动后Redis一定还在——所以gpmall-web的JDBC连接池必须配置testWhileIdletrue和validationQuerySELECT 1实现连接有效性检测。这是容器编排的常识也是很多初学者栽跟头的地方。3.3 ZooKeeper为什么单节点ZK也能当注册中心配置里藏着什么玄机ZooKeeper常被误解为“必须集群”其实单节点ZK在开发/测试环境完全可用关键是配置要对。zookeeper目录下Dockerfile极简FROM zookeeper:3.8 COPY zoo.cfg /conf/zoo.cfg COPY myid /conf/myidzoo.cfg的核心参数tickTime2000ZK心跳周期2秒。单机环境足够。initLimit10Follower连接Leader的超时时间10*tickTime20秒比默认5更宽松避免容器启动慢导致选举失败。syncLimit5Follower与Leader同步数据的超时时间5*tickTime10秒。dataDir/datadataLogDir/datalog分开存储快照和事务日志提升IO性能。clientPort2181对外服务端口。autopurge.purgeInterval1自动清理旧快照和日志间隔1小时防磁盘占满。myid文件内容就一行1。这是ZK集群的节点ID单节点也必须存在否则ZK拒绝启动。docker-compose.yaml中zookeeper服务的关键配置yamlzookeeper:build: ./zookeepercontainer_name: gpmall-zookeeperrestart: unless-stoppedmem_limit: 512mports:“2181:2181”volumes:./zookeeper/data:/data./zookeeper/datalog:/datalog./zookeeper/zoo.cfg:/conf/zoo.cfg./zookeeper/myid:/conf/myidhealthcheck:test: [“CMD”, “echo”, “ruok”, “|”, “nc”, “localhost”, “2181”, “|”, “grep”, “imok”]interval: 30stimeout: 10sretries: 3healthcheck用ncnetcat连2181端口发ruok命令收imok响应。这是ZK官方推荐的健康检测方式比端口探测靠谱得多。实操心得Dubbo的application.yml里必须写dubbo.registry.addresszookeeper://zookeeper:2181注意是zookeeper服务名不是localhost。曾有个学生把这里写成localhost结果gpmall-web容器里根本解析不了localhost——它指向的是web容器自己不是ZK容器。容器间通信永远用服务名这是Docker网络的铁律。3.4 Kafka单机Kafka的配置陷阱与消息可靠性保障Kafka是这个包里最“娇气”的组件。kafka目录下Dockerfile基于confluentinc/cp-kafka:7.3.0Confluent官方镜像比Apache官方镜像更成熟。Dockerfile关键行dockerfile FROM confluentinc/cp-kafka:7.3.0 COPY server.properties /etc/kafka/server.properties COPY start-kafka.sh /usr/bin/start-kafka.sh CMD [/usr/bin/start-kafka.sh]start-kafka.sh是Confluent镜像的启动脚本它会读取server.properties并校验必要参数。server.properties的生死攸关配置broker.id1单节点Broker ID必须唯一。listenersPLAINTEXT://:9092Kafka监听所有网卡的9092端口。advertised.listenersPLAINTEXT://kafka:9092这是最关键的它告诉Producer/Consumer“你们应该连kafka:9092”。如果写成localhost:9092其他容器连不上如果写成kafka:9092但docker-compose里服务名不是kafka也会失败。num.network.threads3网络线程数单机3个足够。log.dirs/var/lib/kafka/data日志目录必须挂载到宿主机。offsets.topic.replication.factor1位移主题副本数。单节点只能是1否则Kafka启动报错。transaction.state.log.replication.factor1事务日志副本数同上。transaction.state.log.min.isr1事务日志最小ISR数单节点只能是1。docker-compose.yaml中kafka服务的depends_on必须包含zookeeperyaml depends_on: zookeeper: condition: service_healthy因为Kafka启动时要连ZK注册自己。但Kafka的healthcheck不能只测端口得测ZK连接yaml healthcheck: test: [CMD-SHELL, kafka-broker-api-versions --bootstrap-server localhost:9092 --command-config /tmp/client.properties 2/dev/null | grep -q 1 || exit 1] interval: 45s timeout: 20s retries: 5这个命令尝试获取Broker API版本成功说明Kafka已连上ZK并正常工作。注意gpmall的application.yml里Kafka配置yaml spring: kafka: bootstrap-servers: kafka:9092 producer: acks: all retries: 3 enable-idempotence: trueacksall确保消息写入所有副本才返回成功enable-idempotencetrue开启幂等性防止网络重试导致重复消息。这两项是消息不丢、不重的底线保障。3.5 Nginx反向代理之外它还是静态资源CDN和HTTPS网关nginx目录下Dockerfile基于nginx:alpine但nginx.conf才是重头戏。nginx.conf的gpmall定制nginxevents {worker_connections 1024;}http {include /etc/nginx/mime.types;default_type application/octet-stream;upstream gpmall_backend { server gpmall-web:8080; # 注意gpmall-web需单独运行或作为容器 } server { listen 80; server_name localhost; location /api/ { proxy_pass http://gpmall_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /static/ { alias /usr/share/nginx/html/static/; } location / { root /usr/share/nginx/html; index index.html index.htm; } }} 关键点 -upstream gpmall_backend指向gpmall-web:8080这意味着gpmall-web必须和Nginx在同一Docker网络gpmall-net下。资源包中gpmall_ok是jar包你需要手动java -jar gpmall-web.jar或把它也做成容器docker-compose.yaml可扩展。 -/static/路径用alias而非root避免路径拼接错误。 -proxy_set_header三行确保后端应用能拿到真实客户端IP这对日志分析和风控很重要。docker-compose.yaml中nginx服务的volumes挂载yamlvolumes:./nginx/nginx.conf:/etc/nginx/nginx.conf./nginx/html:/usr/share/nginx/html./nginx/html目录下应有index.html和/static/子目录含js/css/img。资源包里czfehyfdzUnbYUaFmCOX-master-37c89cb1c16c5e19dfe5abdb693ba7c90e15d2c0这个长命名目录其实就是gpmall前端工程编译后的静态资源直接解压到./nginx/html即可。提示如果你想加HTTPS只需在nginx.conf里增加一个server { listen 443 ssl; ... }块并挂载SSL证书文件。build.sh脚本里预留了cp ./ssl/* /etc/nginx/ssl/的注释行方便扩展。4. 全流程部署与实操记录从零开始5分钟跑通gpmall商城4.1 环境准备你的Linux服务器需要什么别跳过这步很多失败源于环境不达标。操作系统Ubuntu 22.04 LTS 或 CentOS 7.9内核≥3.10。MacOS/Windows需用Docker Desktop但性能不如Linux原生。Docker版本≥24.0.0。执行docker --version确认。旧版本如19.03可能不支持healthcheck的start_period参数。Docker Compose版本≥2.20.0。执行docker compose version注意是compose不是compose-v2。老版本docker-compose命令已被弃用。可用内存≥8GB建议16GB。docker system info | grep Total Memory查看。磁盘空间≥20GB空闲。df -h确认。端口占用检查确保6379Redis、3306MariaDB、2181ZooKeeper、9092Kafka、80Nginx未被占用。sudo lsof -i :6379可查。注意CentOS 7需额外启用iptablesbash sudo systemctl stop firewalld sudo systemctl disable firewalld sudo yum install iptables-services sudo systemctl start iptables sudo systemctl enable iptables否则Docker容器网络可能不通。4.2 部署步骤逐行命令附带预期输出与排查点假设你已下载资源包并解压到/opt/gpmall-deploy。Step 1构建所有定制镜像cd /opt/gpmall-deploy chmod x build.sh ./build.shbuild.sh内容#!/bin/bash echo Building Redis image... docker build -t gpmall-redis ./redis echo Building MariaDB image... docker build -t gpmall-mariadb ./mariadb echo Building ZooKeeper image... docker build -t gpmall-zookeeper ./zookeeper echo Building Kafka image... docker build -t gpmall-kafka ./kafka echo Building Nginx image... docker build -t gpmall-nginx ./nginx echo All images built successfully!✅ 预期输出每行Successfully built xxxxx最后All images built successfully!❌ 排查点若某步报failed to solve: failed to read dockerfile检查对应目录下是否有Dockerfile若报permission denied确认build.sh有执行权限chmod x。Step 2准备gpmall-web应用# 解压前端资源到nginx/html tar -xzf czfehyfdzUnbYUaFmCOX-master-37c89cb1c16c5e19dfe5abdb693ba7c90e15d2c0.tar.gz mv czfehyfdzUnbYUaFmCOX-master-37c89cb1c16c5e19dfe5abdb693ba7c90e15d2c0 nginx/html # 启动gpmall-web假设jar包在gpmall_ok目录 cd gpmall_ok java -jar gpmall-web.jar --spring.profiles.activedocker ✅ 预期输出控制台打印Started GpmallWebApplication in X.XXX seconds❌ 排查点若报Connection refused连不上Redis/MariaDB/ZK确认这些容器已docker ps看到且状态为Up若报java.net.UnknownHostException: kafka确认docker-compose.yaml中服务名和application.yml里bootstrap-servers一致。Step 3启动所有中间件cd /opt/gpmall-deploy docker-compose up -d✅ 预期输出Creating network gpmall-deploy_gpmall-net with driver bridge然后Creating gpmall-redis ... done等5行。❌ 排查点若某容器docker ps看不到执行docker-compose ps看状态若状态为Restarting或unhealthy执行docker logs -f container_name看错误。常见问题ZooKeeper因myid文件缺失启动失败Kafka因advertised.listeners配置错误无法注册到ZK。Step 4验证服务健康状态# 检查所有容器状态 docker-compose ps # 查看各服务健康状态需Docker 20.10 docker inspect gpmall-redis | jq .[0].State.Health # 手动测试关键服务 docker exec gpmall-redis redis-cli -a gpmall_redis_2024 ping # 应返回PONG docker exec gpmall-mariadb mysql -ugpmall -pgpmall123 -e use gpmall; show tables; | head -5 echo ruok | nc gpmall-zookeeper 2181 # 应返回imok kafka-topics --bootstrap-server gpmall-kafka:9092 --list --command-config /tmp/client.properties # 应列出topicStep 5访问商城浏览器打开http://your-server-ip:80应看到gpmall首页。点击“登录”用默认账号admin/123456可进入后台。实操心得第一次访问可能白屏按F12看Network标签页若/static/js/app.js404检查nginx/html目录结构是否正确index.html同级应有static文件夹若API请求502检查docker-compose ps中gpmall-web容器是否在运行以及nginx.conf里upstream地址是否正确。4.3 日志与监控如何快速定位问题所有容器日志都可通过docker logs查看但更高效的方式是集中查看# 实时跟踪所有服务日志按服务名分色 docker-compose logs -f # 只看错误日志grep ERROR docker-compose logs | grep -i error\|exception\|fail # 查看特定服务最近100行日志 docker-compose logs --tail100 redis关键日志特征-RedisReady to accept connections表示启动完成OOM command not allowed when used memory maxmemory表示内存溢出。-MariaDBmysqld: ready for connections表示就绪Access denied for user gpmall172.x.x.x表示密码或权限问题。-ZooKeeperINFO ... Started AdminServer on address ...表示启动成功WARN ... Cannot open channel to 2 at election address表示集群配置错误单机忽略。-KafkaINFO [KafkaServer id1] started表示启动成功ERROR [Controller id1, targetBrokerId1] Connection to node 1 could not be established表示连不上ZK。-Nginxnginx: master process nginx表示运行中*1 connect() failed (111: Connection refused) while connecting to upstream表示后端gpmall-web没起来。提示docker-compose.yaml中所有服务都配置了restart: unless-stopped这意味着宿主机重启后容器会自动拉起。但gpmall-web是手动java -jar启动的需配合systemd或supervisor守护。5. 常见问题与避坑指南那些文档里不会写但你一定会遇到的坑5.1 经典问题速查表问题现象可能原因快速排查命令解决方案docker-compose up -d后docker ps看不到任何容器docker-compose.yaml语法错误如缩进用tab而非空格docker-compose config用yamllint检查yaml格式确保缩进是2空格gpmall-web启动报Cannot connect to ZooKeeperZooKeeper容器未健康或application.yml里zookeeper://localhost:2181写错了docker logs gpmall-zookeeper \| tail -20docker exec gpmall-web ping zookeeper改application.yml为zookeeper://zookeeper:2181确认ZK健康检查通过访问http://ip:80显示502 Bad GatewayNginx配置的upstream地址错误或gpmall-web未运行docker exec gpmall-nginx cat /etc/nginx/nginx.confps aux \| grep gpmall-web检查nginx.conf中server gpmall-web:8080确认gpmall-web.jar已启动Kafka Producer发送消息后Consumer收不到Topic未创建或Consumer Group未正确订阅kafka-topics --bootstrap-server kafka:9092 --listkafka-consumer-groups --bootstrap-server kafka:9092 --group test-group --describe手动创建Topickafka-topics --create --topic topic_order_created --partitions 1 --replication-factor 1 --bootstrap-server kafka:9092MariaDB容器启动后gpmall库为空init.sql未执行或挂载路径错误docker exec gpmall-mariadb ls /docker-entrypoint-initdb.d/docker exec gpmall-mariadb mysql -ugpmall -pgpmall123 -e show databases;确认./mariadb/sql目录下有init.sqldocker-compose.yaml中volumes路径是否正确Redis内存持续增长最终OOMmaxmemory未设置或maxmemory-policy配置不当docker exec gpmall-redis redis-cli -a gpmall_redis_2024 info memory \| grep -E (used_memory|maxmemory|mem_policy)在redis.conf中添加maxmemory 512mb和maxmemory-policy allkeys-lru5.2 那些只有踩过才懂的经验技巧技巧1用docker-compose down -v彻底清理比删容器更干净docker-compose down只删容器-v参数会连带删除volumes即./redis/data、./mariadb/data等。这对于重置环境、排除数据残留干扰至关重要。但注意-v会删掉你辛苦导入的测试数据生产慎用。技巧2build.sh里加--no-cache参数避免镜像层缓存导致配置不生效默认docker build会复用缓存层。当你改了redis.conf但Dockerfile没变build.sh可能跳过COPY步骤。在build.sh中改为bash docker build --no-cache -t gpmall-redis ./redis强制重新构建确保最新配置生效。技巧3给gpmall-web加JVM参数防止频繁GCjava -jar gpmall-web.jar --spring.profiles.activedocker -Xms512m -Xmx1024m -XX:UseG1GC。单机环境下512MB堆内存起步G1 GC比默认Parallel更适应微服务场景。技巧4用docker network inspect gpmall-deploy_gpmall-net查容器IP比docker inspect更快输出中Containers字段直接列出各容器名称和IPv4Address一目了然。例如json Containers: { f8a3b2c...: { IPv4Address: 172.20.0.3/16 } }技巧5docker-compose.yaml里environment变量名要和应用配置严格一致gpmall的application.yml里写spring.redis.hostredis那么docker-compose.yaml中redis服务的environment就不能写REDIS_HOSTredis——那是给其他容器用的环境变量对gpmall-web无效。gpmall-web只认自己的配置文件。最后分享一个小技巧把这个包当成“乐高积木”不要只当黑盒用。试着删掉Kafka把下单逻辑改成同步调用短信服务观察系统响应时间变化或者把Redis换成Memcached改application.yml里缓存配置看商品页加载是否变慢。真正的掌握始于敢于破坏终于理解因果。本文还有配套的精品资源点击获取简介直接可用的gpmall商城容器化部署方案基于Docker Compose统一编排5个核心组件Redis缓存、MariaDB数据库、ZooKeeper服务协调、Kafka消息队列和Nginx反向代理。所有中间件均提供独立Dockerfile、定制镜像构建脚本build.sh及适配gpmall业务逻辑的配置参数。部署时只需执行docker-compose up -d即可在单台Linux服务器上快速启动完整商城运行环境。配套包含清晰目录结构web主程序、各中间件子目录、详细操作说明和注意事项提示支持本地开发测试、教学演示或轻量级生产验证。无需手动安装依赖或调整端口冲突各服务间网络互通、健康检查就绪日志输出规范便于排查问题。本文还有配套的精品资源点击获取