ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Docker Compose 命令实战指南:从入门到生产环境踩坑总结

Docker Compose 命令实战指南:从入门到生产环境踩坑总结 Docker-Compose 这套命令我没记错的话前前后后用了快五年。从最早拿它编排一个 MySQL Redis Nginx 的小项目到后来在生产环境里维护 Prometheus Grafana 监控全家桶再到帮同事排查 Chat2DB 这类工具的容器化部署问题几乎每天都要跟 docker-compose 打交道。很多人觉得 compose 不就是 up 和 down 吗真上手才会发现这里面的门道远比想象中多比如 docker-compose up 怎么限制容器配置离线环境怎么装 docker-composecompose 跑起来之后怎么排查网络和服务状态。这篇就把我用过的、踩过的、总结出来的命令细节一次性讲透适合刚接触容器编排的开发者也适合已经用了很久但一直靠复制粘贴的老手。1. 为什么是 docker-compose而不是一串 docker run1.1 多容器应用的编排痛点先聊聊最基础的问题为什么非要 docker-compose我自己刚接触容器的时候也这么想过——要起一个 MySQLdocker run一条命令就够了再起一个 Redis再来一条重复几次不就行了真去做了才发现痛点在“环境一致性”和“服务关联”上。比如一个典型的 Web 项目后端服务依赖数据库数据库初始化需要挂载数据卷Redis 需要网络别名才能让后端通过固定主机名访问。你用 docker run 一条条执行顺序、网络、数据卷全靠人脑记换一台机器部署又要重新敲一遍。万一某个服务启动时依赖另一个服务已经就绪你还得自己写循环等待脚本这活儿我干过一次就不想干第二遍。docker-compose 解决的就是这个问题把服务定义、网络、数据卷、依赖关系全部写进一个docker-compose.yml然后用一组统一命令来管理整个生命周期。说白了它就是多容器场景下的“项目说明书 遥控器”一条up全部拉起一条down全部清理依赖关系由 compose 在启动时自动处理不用你操心顺序。1.2 compose 文件与命令的关系很多新手容易犯一个误区把 docker-compose 当成一个独立软件却忽略它和 Docker Engine 的关系。实际上 docker-compose 本身不做容器运行时的工作它更像是一个“编排客户端”把 YAML 文件里定义的服务翻译成 Docker 原生的 API 调用再由 Docker Engine 真正去创建、启动、销毁容器。这就解释了为什么你执行docker-compose up看到的日志格式和docker run很像但对象却是一整组服务。所以我一直建议学命令之前先把docker-compose.yml里的三个核心字段搞清楚services定义有哪些服务、networks定义服务间如何通信、volumes定义数据如何持久化。命令是对这三个维度做增删改查文件才是灵魂。另外提醒一句docker-compose 默认会在当前目录下找docker-compose.yml或docker-compose.yaml如果你的文件名不一样比如叫stack.yml那后面所有命令都得加-f stack.yml否则它会抱怨找不到配置文件。这个点不复杂但我在实际工作中见过不少人栽在这里。2. docker-compose 命令全景详解2.1 生命周期类命令up、down、restart、stop、start生命周期命令是使用频率最高的先讲up。docker-compose up的作用是创建并启动所有服务如果镜像不存在会自动拉取如果有配置变更会重建容器。我第一次用的时候以为它等同于 start实际上差别很大start 只是启动已经存在的容器up 会检查配置并决定是否需要重建。日常最常用的几个参数我列一下docker-compose up -d后台启动不加 -d 会前台挂住日志直接刷屏适合调试但不适合日常使用。docker-compose up --build启动前强制重新构建镜像改了 Dockerfile 后必须加这个参数否则它还用缓存里的旧镜像。docker-compose up --no-deps只启动指定服务不启动它依赖的服务排查单个服务问题时很有用。再说down它和 up 是反操作会停止容器并删除网络。注意默认情况下它不会删除数据卷所以数据库数据还在。如果你确定要连数据卷一起清掉用docker-compose down -v这个 -v 是“连窝端”执行前一定要确认数据不需要保留我见过同事手滑把整个测试库给清了。restart、stop、start这三个就比较直白。restart 重启所有服务或指定服务stop 优雅停止start 重新启动已停止的容器不会重新创建。有个小细节如果你改了 YAML 文件里的环境变量或端口映射然后执行docker-compose restart改动不会生效必须up -d重建。这个问题坑过很多人包括我。2.2 状态与调试类命令ps、logs、top、exec、run服务跑起来之后怎么观察状态首推docker-compose ps它会列出当前目录对应项目下的所有容器显示状态、端口映射、启动命令。加-a可以显示已经停止的容器排查崩溃原因的时候很有用。看日志用docker-compose logs不加参数默认输出所有服务的日志服务多了会非常乱。我一般这么用docker-compose logs -f --tail100 服务名追踪某个服务最后 100 行日志。docker-compose logs --no-color关闭颜色输出重定向到文件时不会有一堆转义字符看起来清爽很多。top命令可以查看容器内运行的进程和 Linux 上的 top 类似但作用对象是某个服务比如docker-compose top web可以确认容器里到底跑了哪些进程排查进程异常退出时很管用。exec和run是最容易混淆的一对。docker-compose exec 服务名 命令是在已经运行的容器里执行命令适合进去看文件、调试环境变量比如docker-compose exec db mysql -uroot -p可以直接进数据库。docker-compose run --rm 服务名 命令则是临时启动一个新容器来执行命令执行完销毁--rm保证不留垃圾容器。两者的核心区别exec 用现有的容器run 新起一个。2.3 构建与镜像管理类命令build、pull、push、images如果你的服务镜像需要本地构建比如自定义了 Dockerfile就不能只靠 up 去拉远程镜像了。docker-compose build专门负责构建镜像构建规则写在 YAML 的build字段里包括构建上下文、Dockerfile 路径、构建参数。常用参数是--no-cache当你怀疑构建缓存导致代码没更新时强制全量重新构建。docker-compose pull负责拉取镜像在部署前提前把所有镜像拉下来避免启动时网络超时。push则是把本地构建的镜像推送到镜像仓库用于 CI/CD 流水线单人项目用得少但团队协作时很关键。docker-compose images列出项目当前使用的镜像和标签我习惯在升级版本前后各执行一次对比镜像版本变化操作记录一目了然。2.4 配置与校验类命令config、versiondocker-compose config是我认为被低估最严重的命令。它会读取当前的 YAML 文件把环境变量替换、默认值补充、多个文件合并后的最终配置完整打印出来。修改配置拿不准时先跑一下这个命令看解析结果对不对比直接 up 然后报错再改要高效得多。举个例子YAML 里用了${MYSQL_ROOT_PASSWORD}这样的环境变量占位如果不确定 .env 文件有没有正确加载执行docker-compose config就能看到最终字符串是什么密码有没有变为空值一目了然。还有--services参数只列出所有服务名写脚本遍历服务时很实用。docker-compose version没啥好说的查看 compose 版本号但有个细节如果你用的是 docker-compose v1命令是带横杠的独立二进制如果是 v2 插件可能要用docker compose无横杠。两者命令参数基本兼容但有些细微差异遇到报错先确认版本再排查。3. 实战用 docker-compose 部署 Prometheus Grafana 监控站3.1 准备 compose 文件理论说再多不如跑一个实际例子。这里我以最经典的 Prometheus Grafana 监控组合来演示这套方案我在内网环境里部署过多次整个流程可以直接抄作业。先创建目录和 compose 文件mkdir -p /opt/monitor/{prometheus,grafana} cd /opt/monitor vim docker-compose.yml在docker-compose.yml里写入下面内容version: 3.8 services: prometheus: image: prom/prometheus:v2.45.0 container_name: prometheus ports: - 9090:9090 volumes: - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - prometheus-data:/prometheus command: - --config.file/etc/prometheus/prometheus.yml - --storage.tsdb.path/prometheus restart: unless-stopped grafana: image: grafana/grafana:10.1.0 container_name: grafana ports: - 3000:3000 environment: - GF_SECURITY_ADMIN_PASSWORDadmin123 - GF_INSTALL_PLUGINSgrafana-clock-panel volumes: - grafana-data:/var/lib/grafana depends_on: - prometheus restart: unless-stopped volumes: prometheus-data: grafana-data:这里面有几个值得注意的点。depends_on只是控制启动顺序不保证服务内部真正就绪也就是说 Grafana 容器起来了不代表它已经能连上 Prometheus这只是启动顺序的“软依赖”。如果你有初始化等待需求得自己写脚本或者用健康检查可以后面再加。3.2 启动与验证配置写好后先做配置校验docker-compose config -q-q表示安静模式配置合法就不输出任何内容有错会直接报错。没问题后再启动cd /opt/monitor docker-compose up -d这里我实测下来最担心的是镜像拉取速度如果你的网络环境一般可以先执行docker-compose pull单独拉镜像或者干脆配置镜像加速器。启动完成后用docker-compose ps查看状态两个服务都应该是 Up 状态。然后打开浏览器访问http://服务器IP:3000默认用户名 admin密码就是我在环境变量里设置的admin123登录后选择数据源类型 Prometheus地址填http://prometheus:9090——注意这里填的是服务名不是 IP因为 compose 会在项目内部创建一个默认网络服务名就是容器之间的 DNS 域名这个机制省去了自己维护 IP 的麻烦。3.3 滚动升级与配置热加载监控服务跑久了总要升级版本。我的做法是先修改 YAML 文件里的镜像标签比如从v2.45.0改成v2.47.0然后执行docker-compose up -d --no-deps prometheus--no-deps告诉 compose 只处理 prometheus 这个服务不要碰 Grafana避免误重建。实测这个操作很快新容器起来后旧容器会被自动删除数据卷没有丢失历史监控数据都在。还有一个细节Prometheus 修改告警规则后不需要重启容器发送 SIGHUP 信号即可加载新配置docker-compose exec prometheus kill -HUP 1PID 为 1 是因为容器内 prometheus 进程就是主进程。这个小技巧能避免不必要的容器重建长期运行的服务尤其推荐。4. 进阶用法与生产环境必知项4.1 docker-compose up 限制容器配置生产环境最怕的就是资源争抢。你不可能让监控容器和业务容器抢 CPU 抢内存docker-compose 也支持在 YAML 里直接限制资源。在服务定义下面加deploy字段services: prometheus: image: prom/prometheus:v2.45.0 deploy: resources: limits: cpus: 1.5 memory: 2G reservations: cpus: 0.5 memory: 512M有人会问docker-compose 不是单机编排工具吗deploy不是 swarm 才有的吗这里要特别注意compose v2 已经支持deploy.resources字段因为 Docker Engine 底层会把解析结果转成容器运行时资源限制参数所以即便不用 swarmlimits依然生效。如果不放心可以用docker inspect 容器名查看HostConfig.Memory值是否非零。内存限制生效后有个坑如果进程实际占用超过限制容器会被 OOM Killer 杀掉重启策略如果是unless-stopped会反复重启看起来像抖动。我的经验是内存限制一定要给足余量Prometheus 这类时序数据库对内存很敏感至少按平常峰值的 1.5 倍来配。4.2 离线安装 docker-compose内网环境不能联网装 docker-compose 是很多运维头疼的事。常规在线安装是直接下 GitHub release 的二进制文件但内网机器访问不了外网就必须走离线通道。我常用的办法是找一台能联网的机器下载对应版本的二进制文件然后拷贝到内网。这里关键在于版本对应比如 Docker Engine 是 20.10.x建议用 docker-compose 2.12.x 以上的版本兼容性更好。下载命令参考wget https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64把文件拷到内网机器后重命名并赋予执行权限然后移动到系统路径mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose chmod x /usr/local/bin/docker-compose docker-compose versionchmod x这步漏掉执行时会报Permission denied这是我见过最多的离线安装失败原因。另外如果你的内网机器是 ARM 架构比如飞腾、鲲鹏这类国产芯片需要下载docker-compose-linux-aarch64版本x86 的二进制在 ARM 上跑不起来会报exec format error。还有一种离线方式是直接打包 Docker 镜像docker-compose本身不依赖镜像但这个思路适用于内网部署整套应用在有网的机器上docker-compose pull拉好镜像然后docker save打包成 tar 文件拷到内网用docker load导入再docker-compose up -d。这套流程我在没有外网的隔离环境里验证过多次非常稳。4.3 集成 PostgreSQL 与 Chat2DB 的实战组合除了监控数据库容器化也是高频场景。我最近帮团队搭了一套本地开发环境就是用 docker-compose 跑 PostgreSQL 加 Chat2DB 的容器版。Chat2DB 是一个数据库客户端工具支持 AI 辅助 SQL 生成官方提供了 docker-compose 部署方式。核心的 compose 片段大概是services: postgres: image: postgres:15 environment: POSTGRES_USER: dev POSTGRES_PASSWORD: dev123 POSTGRES_DB: myapp ports: - 5432:5432 volumes: - pg-data:/var/lib/postgresql/data chat2db: image: chat2db/chat2db:latest ports: - 10824:10824 depends_on: - postgres这里要提醒的是 PostgreSQL 的数据卷挂载路径不同大版本有差异。比如 PostgreSQL 14 及以下版本数据目录是/var/lib/postgresql/dataPostgreSQL 15 开始官方镜像改成了/var/lib/postgresql/data下的子版本号目录如果你直接挂载主机目录启动时可能会提示权限问题。解决办法是挂到一个空目录让容器自己初始化不要先往里面放文件否则会触发“data directory has wrong ownership”的错误。5. 常见问题与排查技巧实录5.1 端口冲突这是 docker-compose up 最常见的报错之一Error starting userland proxy: listen tcp4 0.0.0.0:3306: bind: address already in use。说明宿主机上某个进程已经占用了 3306 端口。排查方式sudo ss -lntp | grep 3306找到占用进程后要么停掉它要么修改 YAML 文件里的端口映射把3306:3306改成33061:3306不影响容器内部端口。我一般更喜欢改宿主机端口因为数据库容器内部端口变了会导致连接串全部要改麻烦。5.2 网络连接不上两个服务在同一个 compose 项目里理论上可以通过服务名互通。如果出现连接不上先检查两者是否在同一个网络里。执行docker network ls docker-compose ps再查看服务容器的网络归属docker inspect 容器名 | grep -A 20 Networks最常见的原因是配置文件里手动指定了network_mode: host导致容器不再使用 compose 默认网络服务名无法解析。解决办法是去掉network_mode改用ports端口映射。5.3 容器启动顺序与依赖即使写了depends_on也不能保证服务完全就绪。比如数据库容器虽然起来了但 PostgreSQL 还在初始化此时依赖它的应用去连数据库必然失败。解决方案是在应用启动命令里加入等待脚本或者使用healthcheck加condition: service_healthy的组合。 提示compose v2 支持 depends_on 的 condition 字段前提是依赖的服务定义了 healthcheck。比如 PostgreSQL 的健康检查可以是 pg_isready 命令这样应用容器会在数据库真正可连接后才启动。services: postgres: image: postgres:15 healthcheck: test: [CMD-SHELL, pg_isready -U dev] interval: 5s timeout: 3s retries: 10 app: build: . depends_on: postgres: condition: service_healthy### 5.4 常见错误速查表 我在日常维护中整理了一个高频错误对照表遇到问题先对照排查大多数情况能省下大把时间。 | 报错信息 | 原因 | 解决方案 | | --- | --- | --- | | No such service: xxx | 服务名写错或文件不对 | 用 docker-compose config --services 列出所有服务名 | | Cannot create container for service | 容器名冲突 | docker rm -f 删除同名的旧容器 | | Image ... not found | 镜像不存在或拉取失败 | 先 docker-compose pull检查镜像标签是否打错 | | version is obsolete | compose 版本过高 | 删除 version 字段或升级 docker-compose | | Service ... failed to build | Dockerfile 构建失败 | docker-compose build --no-cache 服务名 查看完整日志 | | volume is in use | 数据卷被某个容器占用 | 找到占用容器并停止再 docker-compose down -v 清理 | | Permission denied | 数据卷权限不足 | 检查挂载目录属主chown 或 chmod 调整 | 这张表里的问题我几乎每个都踩过。尤其是 version is obsoletedocker-compose v2 已经可以直接忽略 version 字段写了反而在个别新版本上报提示删掉就好。 ## 写在最后的经验 这几年用下来我对 docker-compose 最大的体会是命令本身不难难的是理解它背后的对象模型。你不需要背每一个参数但一定要清楚自己面对的是“一组服务”up、down、ps、logs 都是在操作这组服务这和 docker run 操作单个容器的思维模式完全不同。 如果再让我给新手一个建议那就是遇到问题先从 docker-compose config 开始排查而不是直接 up。配置解析对了后面百分之八十的问题都能避免。还有一个小技巧写完 YAML 文件之后用 vim 打开时注意缩进YAML 对空格极度敏感一个错误缩进就能让整个文件白忙活。我在终端里编辑配置时习惯先 :set list 显示空格和制表符防止混用 Tab 导致解析错误。 最后分享一下我的日常工作流部署前 config 校验启动时 up -d观察状态用 ps出问题看日志 logs --tail进容器排查用 exec退出清理用 down。这套流程用顺了不管面对什么项目心里都有底。
返回列表