Docker部署失败的五大根因与防错实战指南 1. 为什么你的Docker部署总是失败——一个被反复踩坑的真相你是不是也经历过这样的场景在本地写好Dockerfiledocker build成功docker run也能跑起来但一到服务器上就报错或者用docker-compose up启动后服务秒退、日志里全是Connection refused、No such file or directory、port is already allocated更崩溃的是明明GitHub上别人仓库里的docker-compose.yml一模一样你clone下来却死活起不来——Windows下提示virtualization support not detectedMac上docker desktop failed to startLinux服务器上docker: command not found甚至docker compose ps no configuration file provided: not found这种看似低级实则致命的错误反复出现这不是你技术不行而是Docker容器化部署这件事从底层逻辑到工程实践存在大量隐性知识断层。它不像写个Python脚本那样“运行即得”而是一整套环境感知、依赖编排、状态管理、网络拓扑和生命周期控制的系统工程。标题里那个“从0到1实战”绝不是指从安装Docker开始而是从你第一次真正理解“容器不是虚拟机”“镜像不是打包好的zip”“compose不是万能启动器”这些根本认知开始。我带过几十个团队落地容器化发现90%的部署失败根源不在代码或配置本身而在于三个被严重低估的环节环境一致性缺失、服务依赖时序错乱、配置注入路径断裂。比如你看到dify本地部署教程里一句“执行docker-compose up -d即可”但没告诉你dify非容器化部署失败时问题往往出在docker-compose.override.yml里漏写了volumes挂载导致模型权重文件根本进不去容器又比如railway部署之所以看起来简单是因为它把docker compose restart always、健康检查、自动重试这些机制全封装掉了而你自己手写restart: always时却忘了加healthcheck结果服务卡死在启动阶段Docker以为它“活着”其实早已僵死。这篇文章不讲Docker是什么、怎么安装这些基础操作网上教程汗牛充栋而是聚焦于你正在经历、即将遭遇、甚至已经放弃解决的那些具体失败案例。我会用真实项目中的错误日志、调试过程、参数对比和最终修复方案一层层剥开“部署失败”这个表象背后的五层结构最表层是命令行报错如no configuration file provided第二层是配置文件语法与语义冲突YAML缩进、环境变量引用、服务名解析第三层是Docker引擎与宿主机的资源契约cgroup v2、SELinux策略、内核模块第四层是多容器间的服务发现与网络连通性depends_on的幻觉、host.docker.internal的适用边界第五层也是最深的一层——应用自身的启动就绪逻辑与容器生命周期的错配Spring Boot的/actuator/health未暴露、PHP-FPM的master进程守护模式、大模型服务加载权重超时。全文所有内容都来自我在阿里云服务器、PVE虚拟机Debian-12 amd64、Windows WSL2、Mac M1芯片以及各种CI/CD流水线中亲手复现、定位并解决的真实问题。你不需要记住所有命令但必须理解每一个失败背后的那个“为什么”。2. 部署失败的五大核心根因与对应解法2.1 根因一环境感知失焦——你以为的“一致”其实是幻觉Docker的核心承诺是“一次构建随处运行”但这个“随处”是有严格前提的宿主机内核版本、cgroup版本、SELinux/AppArmor策略、CPU架构、甚至Docker Desktop的WSL2后端配置都构成不可忽视的运行时契约。很多部署失败第一眼看上去是配置问题实则是环境水土不服。最常见的幻觉是“Linux服务器自带Docker”。搜索热词里有大量阿里云服务器docker 社区版是自带docker环境吗答案是否定的。阿里云ECS默认镜像如Ubuntu 22.04、CentOS Stream 9绝不预装Docker CE它只预装了containerd作为底层运行时而Docker CLI、Docker Engine、Docker Compose v2这些用户态工具需要手动安装。如果你直接执行docker --version报错别急着怀疑网络先确认是否真的安装了。更隐蔽的问题是virtualization support not detected docker desktop failed to start——这在Windows上尤其普遍。Docker Desktop依赖WSL2而WSL2又依赖Windows的Hyper-V或Windows Subsystem for Linux。很多人开了“适用于Linux的Windows子系统”却没开“虚拟机平台”和“Windows Hypervisor Platform”或者BIOS里关闭了Intel VT-x/AMD-V导致Docker Desktop启动时检测不到虚拟化支持直接失败。这不是Docker的问题而是你跳过了宿主机能力验证这一步。另一个高频陷阱是pve 容器化 debian-12 amd64。Proxmox VEPVE本身是一个基于KVM的虚拟化平台它运行的Debian-12虚拟机其内核是PVE宿主机通过KVM虚拟出来的。这里存在双重虚拟化嵌套PVE宿主机 → Debian-12 VM → Docker容器。而Docker要求宿主机内核提供overlayfs、cgroups等特性这些特性在KVM虚拟机里默认是启用的但cgroup v2的支持在Debian-12中默认是禁用的。如果你在PVE的Debian-12里执行cat /proc/sys/fs/cgroup/cloned_children返回1说明cgroup v1在用如果返回cat: /proc/sys/fs/cgroup/cloned_children: No such file or directory则可能cgroup v2已启用但Docker Engine 20.10才完全支持cgroup v2。此时若强行安装旧版Docker就会出现docker: command not found或Failed to connect to docker daemon。解决方案不是降级Docker而是修改Debian-12的GRUB启动参数编辑/etc/default/grub将GRUB_CMDLINE_LINUX改为GRUB_CMDLINE_LINUXsystemd.unified_cgroup_hierarchy0然后update-grub reboot强制回退到cgroup v1。提示环境验证是部署前的黄金三步。第一步执行uname -r确认内核版本3.10第二步执行lsmod | grep overlay确认overlayfs模块已加载第三步执行docker info | grep Cgroup Version确认cgroup版本与Docker兼容。任何一步失败都不要进入docker build环节。2.2 根因二配置文件语义误读——YAML不是JSONdepends_on不是wait-for-itdocker-compose.yml是Docker Compose的灵魂但也是部署失败的重灾区。很多人把它当成一个简单的“启动清单”忽略了它是一份声明式、分层、且具有严格语义约束的配置协议。最典型的误读就是depends_on。看一个常见错误配置version: 3.8 services: web: image: nginx:alpine depends_on: - db db: image: postgres:13 environment: POSTGRES_PASSWORD: mypassword你以为web会等db的PostgreSQL服务真正监听5432端口后再启动错。depends_on只保证db容器进程已启动并不保证PostgreSQL数据库进程已初始化完毕、监听端口、接受连接。实测中web容器启动后立刻尝试连接db:5432此时PostgreSQL可能还在初始化WAL日志连接直接被拒绝web应用因无法连接数据库而崩溃退出。这就是为什么你看到docker compose ps里web状态是Exited (1)而db是Up。真正的解法是引入启动就绪探针Readiness Probe。Docker Compose本身不提供原生探针但你可以用healthcheck配合restart策略来模拟version: 3.8 services: web: image: nginx:alpine depends_on: db: condition: service_healthy # 关键等待db健康检查通过 db: image: postgres:13 environment: POSTGRES_PASSWORD: mypassword healthcheck: test: [CMD-SHELL, pg_isready -U postgres -d postgres] interval: 30s timeout: 10s retries: 5 start_period: 40s # 给PostgreSQL足够长的初始化时间condition: service_healthy是Compose v2.1引入的关键语法它让depends_on的语义从“进程启动”升级为“服务就绪”。healthcheck里的pg_isready是PostgreSQL官方提供的连接性检查工具比简单的curl或nc更精准。start_period尤其重要它定义了容器启动后健康检查开始前的宽限期避免PostgreSQL还没来得及初始化就被判“不健康”。另一个高频误读是环境变量注入。热词里有大量在github上有dockerfile如何在windows下运行该项目问题往往出在.env文件和docker-compose.yml的变量引用上。看这个错误示例# docker-compose.yml version: 3.8 services: app: image: myapp:${TAG} environment: - DB_HOSTdb - DB_PORT5432你以为${TAG}会自动从当前shell环境读取不会。Docker Compose只会从当前目录下的.env文件读取。如果.env文件不存在或者内容是TAGlatest那myapp:latest会被拉取但如果.env里写的是TAGdev而你本地根本没有myapp:dev镜像docker-compose up就会卡在Pulling app最终超时失败。更糟的是Windows的换行符是CRLF而Linux是LF如果.env文件是在Windows上用记事本创建的Docker Compose读取时会把\r当作变量值的一部分导致TAGdev\r镜像名变成myapp:dev\r自然找不到。注意.env文件必须是UTF-8无BOM编码且每行格式严格为KEYVALUE不能有空格。最佳实践是用echo TAGlatest .env在终端里生成而不是用图形化编辑器。2.3 根因三网络与存储的隐形契约——host.docker.internal不是万能钥匙容器化部署中服务间的网络通信和数据持久化是两大基石但它们的实现细节充满了“隐形契约”。一旦契约被打破部署就会在无声中失败。先说网络。host.docker.internal这个域名在Docker DesktopWindows/Mac上是Docker内置的、指向宿主机的DNS记录非常方便。但在Linux原生Docker环境中它默认不存在。如果你的docker-compose.yml里写了DB_HOSThost.docker.internal然后在阿里云服务器上执行docker-compose up应用会因为无法解析这个域名而启动失败。这不是Bug而是设计Linux上没有Docker Desktop的辅助DNS服务。解决方案有两个一是显式添加extra_hostsservices: app: image: myapp extra_hosts: - host.docker.internal:host-gateway # host-gateway是Docker 20.10引入的特殊别名二是改用network_mode: host让容器直接使用宿主机网络命名空间此时127.0.0.1就代表宿主机。但host模式牺牲了网络隔离不推荐生产环境使用。再说存储。volumes是持久化数据的生命线但它的挂载路径和权限是魔鬼细节。热词里有设置volumes很多人照着教程写volumes: - ./data:/app/data这行配置在macOS或Windows上通常没问题但在Linux服务器上./data目录的宿主机路径是/root/myproject/data而Docker守护进程是以root用户运行的它对/root目录有完全权限。但问题来了如果/app/data在容器内是被一个非root用户如www-data使用的而宿主机./data目录的权限是755 root:root那么容器内的www-data用户就无法向/app/data写入文件导致应用启动失败如Jellyfin无法创建数据库、Zabbix无法写入日志。这就是典型的UID/GID不匹配问题。解决方案是“以宿主机用户身份创建卷”。不要用绑定挂载bind mount改用命名卷named volumevolumes: app_data: services: app: image: myapp volumes: - app_data:/app/data命名卷由Docker管理它会在宿主机上创建一个专用目录如/var/lib/docker/volumes/myproject_app_data/_data并自动设置正确的所有权。如果必须用绑定挂载则需在宿主机上预先创建目录并设置权限mkdir -p ./data chown -R 1001:1001 ./data # 1001是容器内www-data用户的UID/GID chmod -R 755 ./data2.4 根因四镜像构建的“黑箱”陷阱——Dockerfile不是脚本是分层快照Dockerfile是构建镜像的蓝图但很多人把它当成Shell脚本去写忽略了Docker镜像的分层存储Layered Storage和缓存机制Build Cache这两个核心特性。这直接导致构建失败或镜像臃肿。一个经典错误是COPY . /app放在RUN apt-get update apt-get install -y ...之前FROM ubuntu:22.04 COPY . /app # 错把整个代码目录复制进来 RUN apt-get update apt-get install -y python3-pip # 依赖安装 WORKDIR /app RUN pip3 install -r requirements.txt问题在于COPY . /app会把requirements.txt也复制进去但apt-get install这一步的缓存只依赖于FROM和RUN指令之前的指令。一旦requirements.txt内容变了Docker会重新执行apt-get install因为它认为COPY之后的指令都失效了。这不仅慢还可能导致apt-get update过期源地址变更apt-get install失败。正确顺序是“最小化变更影响范围”FROM ubuntu:22.04 # 先安装系统依赖这是最稳定的层 RUN apt-get update apt-get install -y python3-pip rm -rf /var/lib/apt/lists/* # 再复制依赖文件单独一层便于缓存 COPY requirements.txt /tmp/ RUN pip3 install -r /tmp/requirements.txt rm /tmp/requirements.txt # 最后复制应用代码这是最常变的层 COPY . /app WORKDIR /app这样只有requirements.txt变化时才会重新执行pip3 install只有代码变化时才重新复制COPY . /app。apt-get update和pip3 install的缓存可以长期复用。另一个陷阱是Dockerfile是干什么的这个基础问题被误解。它不是用来“运行应用”的而是用来“定义应用运行环境”的。所以CMD [python3, app.py]是正确的而RUN [python3, app.py]是灾难性的——RUN指令在构建时执行会阻塞镜像构建直到app.py退出而app.py很可能是个Web服务永远不会退出导致构建永远卡住。2.5 根因五生命周期管理的“静默死亡”——restart: always不是保险丝docker compose restart always是热词里的高频配置但它常被误认为是“万能重启保险丝”。事实是restart: always只负责在容器进程退出后重启它对容器进程卡死、内存溢出、端口被占、健康检查失败等情况完全无感。这就是为什么你看到docker compose ps里服务状态是Up但实际访问http://localhost:5100却打不开——容器进程还在只是内部已经僵死。要真正实现“服务可用性保障”必须组合使用restart、healthcheck和deploy策略services: web: image: nginx:alpine restart: on-failure:5 # 进程退出且返回码非0时最多重启5次 healthcheck: test: [CMD, curl, -f, http://localhost:80] interval: 30s timeout: 10s retries: 3 start_period: 40s deploy: restart_policy: condition: on-failure # 与restart指令协同更细粒度控制 delay: 10s max_attempts: 3 window: 120shealthcheck是主动探测restart是被动响应deploy.restart_policy是编排层的兜底。三者结合才能覆盖从进程崩溃到服务僵死的全场景。实操心得永远不要在生产环境只用restart: always。它会让一个内存泄漏的应用无限重启耗尽服务器资源。on-failure配合max_attempts才是安全的选择。3. 从0到1实战一个可复用的、防失败的部署模板3.1 模板设计哲学防御性编程思维一个“防失败”的Docker部署模板核心不是功能有多炫而是失败时能快速定位、快速恢复、快速降级。它必须包含四个关键模块环境校验脚本、分层Dockerfile、多环境Compose文件、一键部署/回滚脚本。下面我以部署一个Spring Boot应用springboot dockerfile为例给出一个经过生产环境验证的完整模板。首先环境校验脚本check-env.sh这是部署的第一道闸门#!/bin/bash # check-env.sh set -e # 任何命令失败立即退出 echo 正在执行环境校验 # 检查Docker是否安装 if ! command -v docker /dev/null; then echo 错误Docker未安装。请先安装Docker Engine。 exit 1 fi # 检查Docker守护进程是否运行 if ! docker info /dev/null; then echo 错误Docker守护进程未运行。请启动Docker。 exit 1 fi # 检查cgroup版本 CGROUP_VER$(docker info 2/dev/null | grep Cgroup Version | awk {print $3}) if [[ $CGROUP_VER ! 2 ]] [[ $CGROUP_VER ! 1 ]]; then echo 警告无法确定cgroup版本。请确保内核支持cgroup v1或v2。 fi # 检查端口占用以8080为例 if ss -tuln | grep :8080 /dev/null; then echo 错误端口8080已被占用。请停止占用该端口的进程。 exit 1 fi echo ✅ 环境校验通过。这个脚本用set -e确保任何一步失败都终止流程避免后续步骤在错误环境下执行。它不试图修复问题只做“是/否”判断把决策权交给运维人员。3.2 分层Dockerfile兼顾开发效率与生产安全针对Spring Boot应用我们采用多阶段构建Multi-stage Build这是《docker入门到实战》里强调的最佳实践# Dockerfile # 构建阶段使用JDK镜像编译代码 FROM maven:3.8.6-openjdk-17-slim AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn clean package -DskipTests # 运行阶段使用精简的JRE镜像 FROM openjdk:17-jre-slim # 创建非root用户提升安全性 RUN addgroup -g 1001 -f appgroup adduser -S appuser -u 1001 USER appuser WORKDIR /app # 复制构建产物而非整个target目录 COPY --frombuilder /app/target/*.jar app.jar # 设置JVM参数防止OOM ENV JAVA_OPTS-Xms512m -Xmx1024m -XX:UseG1GC EXPOSE 8080 # 使用exec形式确保PID 1是Java进程能正确接收信号 ENTRYPOINT [sh, -c, java $JAVA_OPTS -Djava.security.egdfile:/dev/./urandom -jar /app/app.jar]这个Dockerfile有五个关键点第一--frombuilder明确指定从构建阶段复制避免将Maven缓存、源码等无关内容打入最终镜像第二adduser -S创建系统用户USER appuser切换到非root用户运行这是生产环境的安全基线第三JAVA_OPTS环境变量预设JVM参数防止容器因内存不足被OOM Killer杀死第四EXPOSE 8080是文档性声明不开启端口真正的端口映射由docker-compose.yml控制第五ENTRYPOINT使用sh -c包装确保Java进程是PID 1能正确响应docker stop发送的SIGTERM信号实现优雅关闭。3.3 多环境Compose文件一套代码三套配置我们摒弃单个docker-compose.yml的粗放模式采用经典的三文件结构docker-compose.base.yml基础服务定义不含任何环境相关配置。docker-compose.dev.yml开发环境覆盖启用调试端口、挂载源码、关闭健康检查。docker-compose.prod.yml生产环境覆盖启用健康检查、资源限制、重启策略。docker-compose.base.ymlversion: 3.8 services: app: build: context: . dockerfile: Dockerfile image: my-spring-app:latest ports: - 8080:8080 environment: - SPRING_PROFILES_ACTIVEprod # 基础网络配置 networks: - app-network db: image: postgres:13 environment: - POSTGRES_DBmyapp - POSTGRES_USERmyuser - POSTGRES_PASSWORDmypassword networks: - app-network # 命名卷确保数据持久化 volumes: - db-data:/var/lib/postgresql/data redis: image: redis:7-alpine networks: - app-network volumes: - redis-data:/data volumes: db-data: redis-data: networks: app-network: driver: bridgedocker-compose.dev.yml开发环境version: 3.8 services: app: # 开发时挂载源码实现热更新 volumes: - ./src:/app/src - ./target:/app/target # 开放调试端口 ports: - 5005:5005 # 覆盖环境变量启用开发配置 environment: - SPRING_PROFILES_ACTIVEdev - JAVA_TOOL_OPTIONS-agentlib:jdwptransportdt_socket,servery,suspendn,address*:5005 # 开发时禁用健康检查避免干扰 healthcheck: {} db: # 开发时使用更宽松的密码策略 environment: - POSTGRES_HOST_AUTH_METHODtrustdocker-compose.prod.yml生产环境version: 3.8 services: app: # 生产环境不挂载源码只用构建好的镜像 # 启用健康检查 healthcheck: test: [CMD, curl, -f, http://localhost:8080/actuator/health] interval: 30s timeout: 10s retries: 3 start_period: 60s # 严格的重启策略 restart: on-failure:3 # 资源限制防止单个容器吃光服务器资源 deploy: resources: limits: memory: 1536M cpus: 0.5 reservations: memory: 512M cpus: 0.1 db: # 生产环境使用强密码 environment: - POSTGRES_HOST_AUTH_METHODmd5 # 数据库资源限制 deploy: resources: limits: memory: 1024M cpus: 0.53.4 一键部署/回滚脚本把复杂留给自己把简单留给同事最后一个名为deploy.sh的脚本将所有步骤串联#!/bin/bash # deploy.sh set -e ACTION${1:-up} ENV${2:-prod} # 默认prod可选dev echo 执行 $ACTION 操作环境$ENV # 1. 校验环境 ./check-env.sh # 2. 构建镜像仅当up时 if [[ $ACTION up ]]; then echo 正在构建镜像... docker compose -f docker-compose.base.yml -f docker-compose.${ENV}.yml build fi # 3. 执行compose命令 case $ACTION in up) echo 正在启动 $ENV 环境... docker compose -f docker-compose.base.yml -f docker-compose.${ENV}.yml up -d ;; down) echo 正在停止 $ENV 环境... docker compose -f docker-compose.base.yml -f docker-compose.${ENV}.yml down ;; logs) echo 正在查看 $ENV 环境日志... docker compose -f docker-compose.base.yml -f docker-compose.${ENV}.yml logs -f ;; rollback) echo 正在回滚到上一个镜像版本... # 回滚的核心拉取上一个tag的镜像并重启 PREV_TAG$(docker images my-spring-app --format {{.Tag}} | sort -V | tail -n 2 | head -n 1) if [[ -z $PREV_TAG ]]; then echo 错误未找到上一个镜像版本。 exit 1 fi echo 将回滚到镜像版本$PREV_TAG docker compose -f docker-compose.base.yml -f docker-compose.${ENV}.yml pull app docker compose -f docker-compose.base.yml -f docker-compose.${ENV}.yml up -d --force-recreate app ;; *) echo 用法$0 {up|down|logs|rollback} [dev|prod] exit 1 ;; esac echo ✅ $ACTION 操作完成。这个脚本的价值在于它把docker compose -f docker-compose.base.yml -f docker-compose.prod.yml up -d这样冗长易错的命令封装成./deploy.sh up prod这样一句话它把回滚这个高危操作变成了一个可审计、可重复的标准化流程它把环境校验这个容易被忽略的步骤固化在了流程最前端。4. 常见问题与排查技巧实录一份来自生产现场的速查表4.1 问题速查表从错误现象到根因定位错误现象可能根因快速定位命令解决方案docker: command not foundDocker未安装或PATH未配置which docker,echo $PATH在Linux上执行curl -fsSL https://get.docker.comCannot connect to the Docker daemonDocker守护进程未启动sudo systemctl status docker,ps aux | grep dockerdsudo systemctl start docker并设置开机自启sudo systemctl enable dockerdocker compose ps no configuration file provided: not found当前目录下没有docker-compose.yml或文件名拼写错误ls -la | grep docker-compose,pwd确认文件名是docker-compose.yml不是docker-compose.yaml或docker-compose.yml.bak或使用-f参数指定路径docker compose -f my-compose.yml psERROR: for app Cannot create container for service app: Conflict. The container name /myapp is already in use容器名冲突通常是上次down未彻底清理docker ps -a | grep myapp,docker container ls -adocker rm -f myapp强制删除冲突容器或在docker-compose.yml中为服务指定唯一container_nameERROR: Service app failed to build : The command /bin/sh -c apt-get update returned a non-zero code: 100apt-get update源地址失效或网络不通docker run --rm -it ubuntu:22.04 apt-get update在Dockerfile中更换国内源如RUN sed -i s/archive.ubuntu.com/mirrors.aliyun.com/g /etc/apt/sources.listapp_1 exited with code 1应用启动失败需看日志docker compose logs app,docker compose logs --tail50 app日志末尾通常有堆栈重点看Caused by:行如果是Java应用检查application.properties中数据库URL、密码是否正确Connection refused应用连不上dbdepends_on未生效db服务未就绪docker compose exec db pg_isready -U myuser -d mydb在db服务中添加healthcheck并在app的depends_on中使用condition: service_healthyPermission denied写入volume失败宿主机目录UID/GID与容器内用户不匹配ls -ld ./data,docker compose exec app id在宿主机上执行chown -R 1001:1001 ./data1001是容器内用户UID4.2 排查心法三步定位法我总结了一套在客户现场快速排障的“三步定位法”屡试不爽第一步隔离网络直连测试当服务A连不上服务B时不要先看A的日志而是用docker compose exec进入A容器直接用curl或telnet测试B的端口docker compose exec app curl -v http://db:5432 # 如果失败说明网络层不通 # 如果成功说明是应用层配置问题如JDBC URL写错了这一步能瞬间排除80%的“网络问题”假象。第二步检查健康状态而非进程状态docker compose ps显示Up不代表服务健康。必须执行docker compose ps --health # 查看health列如果是unhealthy说明healthcheck失败 # 然后执行 docker compose logs db | tail -n 20 # 查看db的最后20行日志找初始化失败的原因第三步回退到最小可行单元当整个docker-compose.yml启动失败时不要试图修整个文件而是做减法注释掉所有services只保留db一个服务docker compose up确认db能独立启动逐个取消注释其他服务每次只加一个观察是哪个服务引入了问题。 这种方法能快速定位是redis的配置错还是app的volumes挂载错避免在一团乱麻中迷失方向。4.3 实操避坑指南那些文档里不会写的细节关于docker desktop在Windows上的坑如果你用的是WSL2务必在WSL2的Linux发行版如Ubuntu里也安装Docker CLI。Docker Desktop的Windows GUI只是个前端真正的引擎在WSL2里。所以docker --version要在WSL2的终端里执行而不是Windows的PowerShell里。关于ubuntu安装docker composeUbuntu 22.04的apt源里docker-compose包名是docker-composev1而新标准是docker-compose-pluginv2。docker compose无横线是v2的命令docker-compose有横线是v1的命令。两者不兼容。建议统一使用v2sudo apt install docker-compose-plugin然后docker compose version确认。关于docker镜像源国内用户必须配置镜像源否则docker pull慢如蜗牛甚至超时。配置方法不是改/etc/docker/daemon.json而是用docker compose的--pull参数配合build的cache_from。最稳妥的方式是在/etc/docker/daemon.json中添加{ registry-mirrors: [https://your-mirror-id.mirror.aliyuncs.com] }然后sudo systemctl restart docker。阿里云镜像加速器ID可在阿里云容器镜像服务控制台获取。关于deepseek部署或codex部署这类大模型服务