Docker 学习篇五| docker 与 docker compose一、docker 与 docker compose 的关系1.1 一句话概括1.2 它们读什么文件1.3 它们都能做「构建」和「启动」二、构建阶段源码 → 镜像2.1 docker build —— 单模块构建2.2 docker compose build —— 批量构建2.3 构建阶段对比总结三、启动阶段镜像 → 容器3.1 docker run —— 单容器启动3.2 docker compose up —— 批量启动3.3 启动阶段对比总结四、最佳实践各阶段用什么工具个人项目推荐路线什么时候该用 build:什么时候坚持 image:一句话总结五、完整示例blog 项目走一遍第一步写 Dockerfile第二步写 docker-compose.yml全部用 image:第三步本地构建 启动第四步部署到服务器速查总表一、docker 与 docker compose 的关系1.1 一句话概括docker 是单兵作战工具管单个对象一个镜像、一个容器。docker compose 是批量指挥工具管整个项目一组容器 网络 依赖。docker ← 管单个docker run 一个容器、docker build 一个镜像 docker compose ← 管整个项目docker compose up 启动一组容器1.2 它们读什么文件工具读哪个文件能不能读另一个文件dockerDockerfile不认识docker-compose.yml。敲docker build它只看当前目录有没有 Dockerfileyml 放在旁边它也不理docker composedocker-compose.yml可以通过build:认识 Dockerfile。yml 里写了build: ./my-appcompose 就会去那个目录找 Dockerfile 并调用docker build关键区别docker build -t blog-server . ← 只读 Dockerfile不认识 yml docker compose up -d ← 读 docker-compose.yml docker compose build ← 读 yml → 找 build: → 读 Dockerfile docker compose up -d --build ← 读 yml → 找 build: → 读 Dockerfile → 构建 → 启动docker compose能通过 yml 里的build:路径找到 Dockerfile但 Dockerfile 反过来完全不知道 yml 的存在——这个认知是单向的。1.3 它们都能做「构建」和「启动」两个工具的本质能力重叠在两个阶段docker docker compose │ │ ┌───────┴───────┐ ┌───────┴───────┐ │ │ │ │ 构建 启动 构建 启动 (docker build) (docker run) (compose build) (compose up)后续章节就从这两个阶段入手逐个对比。二、构建阶段源码 → 镜像2.1 docker build —— 单模块构建构建是什么意思把源码 运行环境JDK、依赖库等打包成一个只读的镜像文件。一次构建到处运行。读取的文件Dockerfile。这是唯一输入docker-compose.yml 跟它没有任何关系。命令dockerbuild-t镜像名:标签.# ↑ ↑ ↑# 镜像名:标签 构建上下文存在的不足一个项目如果有多个模块blog-server、blog-ui需要敲多次dockerbuild-tblog-server:latest ./blog-serverdockerbuild-tblog-ui:latest ./blog-ui手动逐个构建每个都要敲一遍。项目模块越多越麻烦而且没人帮你判断构建顺序。2.2 docker compose build —— 批量构建构建是什么意思通过 docker-compose.yml 批量调用docker build一次完成所有镜像的构建。本质上是对docker build的批量包装。读取的文件docker-compose.yml。但 compose 自己不会凭空构建——它必须先在 yml 里看到build:指令才知道要去哪里找 Dockerfile。# yml 里写了 build: —— compose 认得它会去读 Dockerfileservices:blog-server:build:./blog-server# → compose 找这里/Dockerfile → 调用 docker build如果 yml 里用的是image:compose 在构建阶段就无事可做services:blog-server:image:blog-server:latest# → compose 不构建直接等启动时用已有镜像命令dockercompose build# 构建 yml 中所有写了 build: 的服务dockercompose build blog-server# 只构建指定服务dockercompose up-d--build# 构建 启动一步到位存在的不足build: 模式下compose 帮你省了一个命令但绑死了构建和源码位置。问题出在部署时服务器上 ✅ 有 docker-compose.yml ✅ 有 .tar 镜像文件 ❌ 没有源码 ❌ 没有 Dockerfile docker compose up -d ← 报错找不到 ./blog-server/Dockerfile服务器没有源码和 Dockerfilebuild:指向的路径不存在compose 直接报错。build: 在服务器上是死路一条。2.3 构建阶段对比总结docker builddocker compose build构建是什么意思源码环境 → 一个镜像批量调用 docker build读什么文件Dockerfile先读 yml 找build:再读 Dockerfile命令docker build -t 镜像名 .docker compose build能同时构建多个吗❌ 一次一个✅ 批量需要源码位置信息需要.指定上下文需要build:指定路径服务器上能用吗✅ 不需要服务器只需docker load❌ 服务器没源码build:报错三、启动阶段镜像 → 容器3.1 docker run —— 单容器启动启动是什么意思从已有镜像创建一个容器实例并运行起来。镜像停在文件状态容器是真正干活的进程。命令dockerrun-d\--nameblog-server\-p8081:8080\-vblog-upload:/app/upload\-eDB_HOSTmysql\--networkblog-net\--restartunless-stopped\blog-server:latest存在的不足服务一多问题就暴露了命令又长又重复— 四个服务要敲四段类似的参数端口、挂载、环境变量每个都要手写没有统一管理入口— 启停得逐个操作docker start mysql、docker start redis、docker start blog-server……网络得手动建— 容器间要互访必须先docker network create blog-net每个容器加--network。漏一个就连不上没有启动顺序控制— blog-server 依赖 MySQL但docker run不会等你MySQL 没就绪它就挂了3.2 docker compose up —— 批量启动启动是什么意思根据 yml 声明的一次性配置把一组镜像启动为一组相互连通的容器。相当于把 N 条docker run写成一份配置文件一条命令执行。命令dockercompose up-d# 启动所有服务dockercompose up-dblog-server# 只启动 blog-servercompose 自动做了三件事自动建网络— 名叫目录名_default所有容器都在这个网络里服务名就是 DNS 域名自动命名容器—目录名-服务名-1不用手动--name自动管理依赖顺序—depends_on保证依赖先启动services:blog-server:depends_on:mysql:condition:service_healthy# 等 MySQL 健康检查通过才启动存在的不足yml 变了才增量更新— 改 yml 后重跑up -d没变的服务不动。但如果docker run起了一个同名容器compose 会报错纯 docker run 的场景不适用— 只跑一个 hello-world 测试写 yml 太隆重了容易让人忽略底层— 刚上手时用 compose 很爽但容器网络、数据卷、镜像分层这些概念不通过docker run亲手敲一遍很难真正理解3.3 启动阶段对比总结docker rundocker compose up启动是什么意思镜像 → 一个容器镜像 → 一组容器读什么文件无配置文件全靠命令行参数docker-compose.yml命令docker run -d --name ... 镜像名docker compose up -d网络配置手动docker network create--network自动创建服务名 DNS容器命名手动--name自动目录名-服务名-1依赖顺序不处理depends_on 控制适合场景单个容器、临时测试两个及以上服务的项目四、最佳实践各阶段用什么工具个人项目推荐路线构建阶段docker build本地打镜像 ↓ 镜像传输docker save → .tar → 传到服务器 → docker load ↓ 启动阶段docker compose up同一份 yml不改一行为什么构建不用 compose build启动却用 compose up阶段推荐工具原因构建docker build服务器不需要构建compose build在服务器上无用还报错启动docker compose up项目管理需要批量能力手动 docker run 不可维护什么时候该用 build:什么时候坚持 image:image: 模式推荐 build: 模式 ───────────────── ───────────── 个人项目、生产部署 大团队分文件场景 本地服务器同一份 yml 本地开发省一个命令image:模式下compose不参与构建只负责启动职责清晰dockerbuild-tblog-server.# 构建归 docker 管dockercompose up-d# 启动归 compose 管一句话总结docker build docker compose up各司其职互不越界。— 构建阶段用docker build因为服务器只需要镜像不需要源码。— 启动阶段用docker compose up因为项目管理需要批量能力。—build:是 compose 认识 Dockerfile 的桥梁但只在本地开发时用部署到服务器上还是image:最稳。五、完整示例blog 项目走一遍第一步写 Dockerfile后端blog-server/Dockerfile多阶段构建FROM maven:3.9-eclipse-temurin-21 AS builder COPY . . RUN mvn clean package -DskipTests FROM eclipse-temurin:21-jre-alpine COPY --frombuilder target/*.jar app.jar CMD [java, -jar, app.jar]前端blog-ui/Dockerfile多阶段构建FROM node:20-alpine AS builder COPY package*.json . RUN npm install COPY . . RUN npm run build FROM nginx:alpine COPY --frombuilder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf第二步写 docker-compose.yml全部用 image:services:mysql:image:mysql:8.0container_name:blog-mysqlports:[3307:3306]volumes:[mysql-data:/var/lib/mysql]environment:MYSQL_ROOT_PASSWORD:rootMYSQL_DATABASE:bloghealthcheck:test:[CMD,mysqladmin,ping,-h,localhost]restart:unless-stoppednetworks:[blog-net]redis:image:redis:7-alpinecontainer_name:blog-redisports:[6380:6379]restart:unless-stoppednetworks:[blog-net]blog-server:image:blog-server:latestcontainer_name:blog-serverports:[8081:8080]environment:DB_HOST:mysqlDB_PORT:3306REDIS_HOST:redisREDIS_PORT:6379depends_on:mysql:condition:service_healthyrestart:unless-stoppednetworks:[blog-net]blog-ui:image:blog-ui:latestcontainer_name:blog-uiports:[80:80]depends_on:[blog-server]restart:unless-stoppednetworks:[blog-net]volumes:mysql-data:networks:blog-net:driver:bridge第三步本地构建 启动# 构建阶段用 docker build dockerbuild-tblog-server:latest ./blog-serverdockerbuild-tblog-ui:latest ./blog-ui# 启动阶段用 docker compose up dockercompose up-d# 浏览器访问 http://localhost ← 前端 Nginx# Nginx 代理 /api/* → blog-server:8080# blog-server 连 mysql:3306、redis:6379第四步部署到服务器# 本地导出镜像dockersave-oblog-server.tar blog-server:latestdockersave-oblog-ui.tar blog-ui:latest# 传到服务器后dockerload-iblog-server.tardockerload-iblog-ui.tardockercompose up-d# 同一份 yml不改一行部署时建议删掉 yml 中 MySQL 和 Redis 的ports映射——生产环境只暴露前端 80/443 就够了。速查总表阶段工具读什么文件命令存在不足构建docker buildDockerfiledocker build -t 名 .一次构建一个多个模块敲多次构建docker compose buildyml →build:→ Dockerfiledocker compose build部署到服务器没源码就废启动docker run无配置文件全参数docker run -d --name ...服务一多不可维护启动docker compose updocker-compose.ymldocker compose up -d单容器场景写 yml 太隆重