Docker Compose 常见报错排查笔记(含 nginx+mysql+redis 可运行模板)

发布时间:2026/9/25 21:08:30

Docker Compose 常见报错排查笔记(含 nginx+mysql+redis 可运行模板) 前段时间帮同事看一个 docker compose up -d 起不来的项目前后折腾了大半天最后发现是 YAML 里混了一个 Tab。借这个机会把这两年用 Compose 踩过的坑整理一下顺便把常用的模板存个档下次直接抄。先说一个前提本文用的是Compose v2命令是 docker compose中间空格v1 的 docker-compose连字符已经在 2023 年停止维护网上老教程里那种 version: 3.8 开头的写法在新版会打警告不再推荐。环境项目版本Docker Engine24.0.7Docker Composev2.24.5OSUbuntu 22.04 / Windows 11 Docker Desktop 4.29查看自己版本docker compose version # Docker Compose version v2.24.5先讲一个万能习惯写完先 config不管写多短的 compose.yml启动前先跑一次docker compose config这条命令做两件事校验 YAML 语法缩进、Tab、引号错乱都会在这里报出来还会指出具体行号展开所有变量插值和 extends 继承输出的是 Compose 实际会使用的最终配置。比如你写了 ${DB_PASSWORD}config 会告诉你这个变量最终被替换成了什么或者是不是空字符串——很多配置看着没问题但服务起不来的情况跑一次 config 就露馅了。如果报错输出大概长这样yaml: line 12: found character that cannot start any tokenline 12 就是出错行号直接跳过去改。坑 1YAML 缩进这是新手 90% 的第一个坑。YAML 有几个硬规则只能用空格不能用 Tab——用 Tab 直接 found character that cannot start any token同一层级缩进数必须一致惯例是2 空格冒号后面必须有空格image: nginx ✅image:nginx ❌列表项 - 后面也要空格错误示范services: nginx: image: nginx ports: # ← 这一行是 Tab - 8080:80正确写法services: nginx: image: nginx ports: - 8080:80VSCode 装个 YAML 插件Red Hat 出的那个底部状态栏能切空格/Tab缩进错误也会实时高亮比事后 config 校验省事。坑 2字段名拼错Compose 对未知字段是静默忽略的除非开了严格模式拼错了不报错但配置不生效。最常见的错写正确volumevolumesenviromentenvironmentportportscontainer_namecontainer_name这个反而经常被写成 containernamedepend_ondepends_on排查方法还是 docker compose config展开后的输出会告诉你 Compose实际读到了什么。如果你写了 enviroment展开的输出里就找不到对应的环境变量一眼看穿。坑 3挂载路径绑定挂载bind mount路径踩坑分平台Linux / Mac相对路径以 compose.yml所在目录为基准不是你执行命令的目录。services: nginx: volumes: - ./conf/nginx.conf:/etc/nginx/nginx.conf:ro - ./logs:/var/log/nginx:ro 表示只读挂载配置文件建议都加防止容器内进程误改宿主机文件。Windows路径分隔符推荐用正斜杠 /反斜杠在 YAML 里要转义很麻烦。volumes: - D:/projects/myapp/conf:/etc/nginx/conf:ro # ✅ # - D:\projects\myapp\conf:/etc/nginx/conf # ❌ 反斜杠容易被吃掉Docker Desktop 还要注意挂载的盘符必须在 Settings → Resources → File Sharing 里共享过否则报 Mount denied。Windows 11 上 C 盘默认共享D/E 盘要手动加。排查挂载失败docker compose logs nginx # 或者进容器看 docker compose exec nginx ls -la /etc/nginx/如果宿主机文件没挂进来容器里看到的是镜像自带的默认文件不是你的配置。坑 4depends_on 只等启动不等就绪这个是原文提到但没给解法的坑展开讲。depends_on 的默认行为是等被依赖的容器进入 running 状态就返回不管服务本身有没有准备好接受连接。MySQL 容器启动到能接受 TCP 连接中间有 10~30 秒的初始化时间这段时间 Web 服务连过去就是 Connection refused。错误示范services: web: image: myapp depends_on: - mysql mysql: image: mysql:8启动顺序是 mysql 先起但 web 起来的时候 mysql 还没监听 3306应用初始化连接池直接崩。正确写法推荐用 healthcheck condition: service_healthyservices: mysql: image: mysql:8 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: app healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -uroot, -p${MYSQL_ROOT_PASSWORD}] interval: 5s timeout: 3s retries: 20 start_period: 30s volumes: - mysql_data:/var/lib/mysql web: image: myapp depends_on: mysql: condition: service_healthystart_period: 30s 是给 MySQL 初始化的宽限期这段时间内 healthcheck 失败不算 unhealthy。retries: 20 interval: 5s 意味着最多等 100 秒够用。注意condition: service_healthy 是 Compose v2.1 支持的语法v1 的 docker-compose 老版本可能不认报 depends_on contains an invalid type。坑 5环境变量 ${VAR} 与 shell 变量混淆Compose 里 ${VAR} 有两层解析很多人分不清第一层Compose 插值——发生在读取 yml 时Compose 从宿主机 shell 环境或同目录下的 .env 文件里取值替换到 yml 里。services: mysql: environment: MYSQL_ROOT_PASSWORD: ${DB_ROOT_PW} # ← Compose 会从宿主机/.env 取 DB_ROOT_PW第二层容器内 shell 解析——如果值里带了 $$两个美元符Compose 会转义成单个 $最终传给容器里的 shell 去解析。services: app: command: bash -c echo $$HOSTNAME # ← 打印容器内的 HOSTNAME不是宿主机的写一个 $ 就是 Compose 插值写两个 $$ 就是留给容器内 shell——这个区别踩过一次就记一辈子。推荐做法敏感配置全放 .env同目录Compose 自动读并且 .env 加进 .gitignore# .env DB_ROOT_PWs3cret DB_USERapp DB_NAMEapp # .gitignore .env提交一个 .env.example 到仓库写明需要哪些变量别人 clone 下来复制成 .env 自己填。坑 6匿名卷导致重启丢数据services: mysql: image: mysql:8 volumes: - /var/lib/mysql # ← 匿名卷这种写法叫匿名卷Compose 会创建一个随机名字的 Docker volume。问题在于docker compose down不会删匿名卷默认行为docker compose down -v会删所有卷包括匿名的你换台机器部署或者 down -v 一次数据全没正确做法显式声明命名卷services: mysql: image: mysql:8 volumes: - mysql_data:/var/lib/mysql # ← 命名卷 volumes: mysql_data: # ← 顶层声明命名卷有固定名字down 不会删docker volume ls 能看到方便备份和迁移。完整可运行模板nginx mysql redis下面这份是我常用的模板直接 docker compose up -d 就能起包含了上面所有的最佳实践# compose.yml Compose v2不需要 version 字段 services: nginx: image: nginx:1.25-alpine container_name: nginx ports: - 80:80 - 443:443 volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/logs:/var/log/nginx - ./nginx/html:/usr/share/nginx/html:ro depends_on: - app restart: unless-stopped networks: - frontend - backend app: image: myapp:latest container_name: app environment: DB_HOST: mysql DB_PORT: 3306 DB_USER: ${DB_USER} DB_PASSWORD: ${DB_PASSWORD} DB_NAME: ${DB_NAME} REDIS_HOST: redis REDIS_PORT: 6379 depends_on: mysql: condition: service_healthy redis: condition: service_healthy restart: unless-stopped networks: - backend mysql: image: mysql:8.0 container_name: mysql environment: MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} MYSQL_DATABASE: ${DB_NAME} MYSQL_USER: ${DB_USER} MYSQL_PASSWORD: ${DB_PASSWORD} TZ: Asia/Shanghai command: - --character-set-serverutf8mb4 - --collation-serverutf8mb4_unicode_ci - --default-authentication-pluginmysql_native_password volumes: - mysql_data:/var/lib/mysql - ./mysql/init:/docker-entrypoint-initdb.d:ro # 首次启动执行的 SQL healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -uroot, -p${DB_ROOT_PASSWORD}] interval: 5s timeout: 3s retries: 20 start_period: 30s restart: unless-stopped networks: - backend redis: image: redis:7-alpine container_name: redis command: [redis-server, --requirepass, ${REDIS_PASSWORD}, --appendonly, yes] volumes: - redis_data:/data healthcheck: test: [CMD, redis-cli, -a, ${REDIS_PASSWORD}, ping] interval: 5s timeout: 3s retries: 10 restart: unless-stopped networks: - backend volumes: mysql_data: redis_data: networks: frontend: driver: bridge backend: driver: bridge internal: true # backend 网络不通外网只有 nginx 能进出配套的 .envDB_ROOT_PASSWORDChangeMe_Root_2026 DB_USERapp DB_PASSWORDChangeMe_App_2026 DB_NAMEapp REDIS_PASSWORDChangeMe_Redis_2026这份模板里几个细节值得单独说两个网络分离frontend 让 nginx 对外backend 设了 internal: truemysql/redis 只在内部网络里宿主机端口不暴露安全性直接提升一档restart: unless-stopped宿主机重启后容器自动拉起除非你手动 stop 过。比 always 更符合直觉TZ: Asia/Shanghai不设时区容器内默认 UTC日志时间戳会差 8 小时查问题很痛苦MySQL 初始化脚本目录/docker-entrypoint-initdb.d 里的 .sql、.sh 会在数据卷为空时首次启动执行用来建表灌初始数据很方便。注意如果 mysql_data 卷已经有数据脚本不会重跑排查工具速查现象命令看某个服务日志docker compose logs -f --tail200 mysql进容器排查docker compose exec mysql bash看容器状态docker compose ps看容器详细信息docker inspect container_id重启单个服务docker compose restart app重建单个服务docker compose up -d --build app停止并清理保留数据卷docker compose down停止并清理含数据卷docker compose down -v ⚠️ 慎用down -v 那个 -v 一按命名卷全删生产环境执行前三思。小结Compose 出问题基本就三类YAML 语法缩进、拼写、路径/变量解析挂载、${}、.env、服务时序depends_on 不等于就绪。养成三个习惯能规避 80% 的坑写完先 docker compose config 校验敏感值放 .env 不入库有状态服务必写 healthcheck依赖方用 condition: service_healthy上面那份 nginxmysqlredis 模板我自己用了两年改改环境变量和挂载路径就能套到新项目上需要的直接抄。
延伸阅读

更多相关文章

2026/9/25 21:08:30

商用燃气报警器怎么评测——不排榜只看五个维度

一个商业综合体的安全负责人最近卡在一件事上:美食广场要统一换报警器,他手上攒了六个品牌的样机,参数表摊了一桌,越比越糊涂。一家标 3%FS,另一家标 3%读数,第三家写着续航二十四小时却没说对应什么工况&a…

2026/9/25 22:18:34

后端人别再焦虑了!核心能力其实就这些

打开技术社区,满屏都是“Spring Cloud Alibaba实战”“Service Mesh落地”“云原生架构演进”,再刷刷招聘要求,分布式、高并发、微服务、容器化、DDD……仿佛少学一样就会被时代抛弃。于是很多后端人陷入焦虑:新技术层出不穷&…

2026/9/25 22:18:34

2025 AI出海实战:算力选型、大模型部署与Agent落地关键节点

1. 算力格局变了,出海的起跑线也跟着变了2025年做AI出海,如果还拿2023年那套“国内训模型、海外套个壳”的思路来打,基本等于开局就落后半个身位。我过去一年跟几个做多模态和Agent方向的团队聊下来,最直观的感受是:算…

2026/9/25 22:18:34

从自研RAG到WeKnora:企业知识库落地全记录

去年年初我们团队接了一个内部知识库的项目,要求把几十万份产品文档、故障工单和技术规范变成可检索、可问答的资产。一开始我们天真地以为“接个大模型API就完事了”,结果两个月下来,最耗精力的根本不是模型本身,而是围绕知识接入…

2026/9/25 22:18:34

Atlas 300V 24G推理加速卡跑YOLO:从环境搭建到模型转换全攻略

看到“atlas 300v 24g 是运算加速卡吗”这个问题,我第一反应是,又有人要入坑 AI 推理这条线了。先给结论:Atlas 300V 24G 确实是一张运算加速卡,但它不是普通显卡,更不是用来打游戏的,它是一张专门为神经网…

2026/9/25 22:13:33

邹平省心的新房装修设计公司实力与用户口碑

淄博业之峰家园装饰有限公司是淄博本土深耕家装行业的正规服务商,成立24年来始终立足淄博本地需求,为各类家装业主提供全流程的品质装修服务,其核心定位是做淄博人值得托付的良心家装品牌,主营别墅装修、新房装修、老房改造、大平…

2026/9/25 21:00:17

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/25 20:59:52

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/25 0:02:35

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:02:35

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:02:35

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 20:55:38

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/25 18:41:36

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/25 18:34:56

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑