本篇内容着重说明日常使用docker常用的部分,其他非常用操作这里不再说明
第一部分:Docker 基础 1. Docker 简介 什么是 Docker? Docker 是一个开源的应用容器引擎,让开发者可以打包应用及其依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 或 Windows 操作系统上。
Docker vs 虚拟机
特性
Docker 容器
虚拟机
启动速度
秒级
分钟级
资源占用
MB 级
GB 级
性能
接近原生
有损耗
隔离性
进程级
系统级
镜像大小
通常 MB 级
通常 GB 级
Docker 的优势
快速部署 :容器启动时间极短
资源高效 :共享宿主机内核,资源占用少
一致性 :开发、测试、生产环境完全一致
可移植性 :一次构建,随处运行
版本控制 :镜像版本管理简单
2. Docker 安装 Linux 安装(Ubuntu/Debian) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 sudo apt-get updatesudo apt-get install -y \ apt-transport-https \ ca-certificates \ curl \ gnupg \ lsb-release curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullsudo apt-get updatesudo apt-get install -y docker-ce docker-ce-cli containerd.iosudo usermod -aG docker $USER docker --version
CentOS/RHEL 安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine sudo yum install -y yum-utilssudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.reposudo yum install -y docker-ce docker-ce-cli containerd.iosudo systemctl start dockersudo systemctl enable dockerdocker --version
Windows/macOS 安装
下载 Docker Desktop
运行安装程序
按照向导完成安装
启动 Docker Desktop
验证安装 1 2 3 4 5 6 7 8 docker --version docker run hello-world docker info
3. Docker 核心概念 镜像(Image) 镜像是一个只读模板,包含运行应用所需的代码、运行时、库、环境变量和配置文件。
容器(Container) 容器是镜像的运行实例。可以创建、启动、停止、删除容器。
仓库(Registry) 仓库是存储和分发镜像的服务。Docker Hub 是最大的公共仓库。
Docker 架构 graph TB
subgraph "Docker Host"
subgraph "Container 1"
C1App["App"]
C1Libs["Bins/Libs"]
end
subgraph "Container 2"
C2App["App"]
C2Libs["Bins/Libs"]
end
Engine["Docker Engine"]
end
C1App --> C1Libs
C2App --> C2Libs
Engine --> C1Libs
Engine --> C2Libs
4. Docker 镜像管理 搜索镜像 1 2 3 4 5 6 7 8 docker search nginx docker search --filter=is-official=true nginx docker search --filter=stars=1000 nginx
拉取镜像 1 2 3 4 5 6 7 8 docker pull nginx docker pull nginx:1.20 docker pull --platform linux/amd64 nginx:latest
查看镜像 1 2 3 4 5 6 7 8 9 10 11 12 docker images docker inspect nginx:latest docker history nginx:latest docker images --filter "dangling=true" docker images --filter "reference=nginx:*"
镜像操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 docker tag nginx:latest my-nginx:v1 docker push my-nginx:v1 docker rmi nginx:latest docker image prune -a docker save -o nginx.tar nginx:latest docker load -i nginx.tar
构建镜像 1 2 3 4 5 6 7 8 9 10 11 docker build -t my-app:v1 . docker build -t my-app:v1 -f Dockerfile.prod . docker build --no-cache -t my-app:v1 . docker build --build-arg NODE_ENV=production -t my-app:v1 .
5. Docker 容器管理 创建和启动容器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 docker run nginx docker run -d nginx docker run -it ubuntu bash docker run --rm nginx docker run --name my-nginx -d nginx
端口映射 1 2 3 4 5 6 7 8 docker run -d -p 8080:80 nginx docker run -d -p 8080:80 -p 8443:443 nginx docker run -d -p 127.0.0.1:8080:80 nginx
环境变量 1 2 3 4 5 6 7 8 docker run -d -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0 docker run -d --env-file .env mysql:8.0 docker run -d -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=mydb mysql:8.0
卷挂载 1 2 3 4 5 6 7 8 9 10 11 docker run -d -v mysql-data:/var/lib/mysql mysql:8.0 docker run -d -v /host/path:/container/path nginx docker run -d -v /host/config:/etc/nginx/conf.d:ro nginx docker run -d -v mysql-data:/var/lib/mysql -v ./config:/etc/mysql/conf.d mysql:8.0
容器管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 docker ps docker ps -a docker logs my-nginx docker logs -f my-nginx docker inspect my-nginx docker stats my-nginx docker top my-nginx
容器操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 docker stop my-nginx docker start my-nginx docker restart my-nginx docker pause my-nginx docker unpause my-nginx docker rm my-nginx docker rm -f my-nginx docker container prune
执行命令 1 2 3 4 5 6 7 8 9 10 11 docker exec my-nginx ls -la docker exec -it my-nginx bash docker exec -it my-nginx sh docker exec -it --user root my-nginx bash
6. Docker 网络管理 先搞懂:为什么需要网络? 想象一下你家里有几台电脑:
电脑A要访问电脑B上的服务
它们需要在同一个”局域网”里才能互相找到
Docker容器也是一样:
容器之间要互相通信
容器要被外部访问
这些都需要通过”网络”来实现
Docker 网络模式一览 graph TB
subgraph "Docker 网络模式"
Bridge["bridge 桥接网络<br/>(默认模式)"]
Host["host 主机网络"]
None["none 无网络"]
Overlay["overlay 覆盖网络"]
Macvlan["macvlan MAC地址网络"]
end
Bridge --- BridgeUse["容器间通信"]
Host --- HostUse["高性能场景"]
None --- NoneUse["完全隔离"]
Overlay --- OverlayUse["跨主机通信"]
Macvlan --- MacvlanUse["特殊硬件设备"]
style Bridge fill:#4CAF50
style Host fill:#2196F3
style None fill:#9E9E9E
style Overlay fill:#FF9800
style Macvlan fill:#9C27B0
style BridgeUse fill:#81C784
style HostUse fill:#64B5F6
style NoneUse fill:#BDBDBD
style OverlayUse fill:#FFB74D
style MacvlanUse fill:#BA68C8
模式一:bridge 桥接网络(最常用,必学!) 通俗解释 把Docker的bridge网络想象成你家里的路由器 :
所有设备(容器)都连接到这个路由器上
路由器会给每个设备分配一个内网IP
设备之间可以通过内网IP互相访问
外面的人要访问你家设备,需要通过路由器做”端口映射”
工作原理图 graph TB
subgraph "宿主机"
subgraph "bridge 网络 172.17.0.0/16"
C1["容器A<br/>172.17.0.2"]
C2["容器B<br/>172.17.0.3"]
C3["容器C<br/>172.17.0.4"]
end
Docker0["docker0 虚拟网桥"]
end
Internet["互联网"]
C1 --> Docker0
C2 --> Docker0
C3 --> Docker0
Docker0 --> Internet
style Docker0 fill:#4CAF50
style C1 fill:#2196F3
style C2 fill:#2196F3
style C3 fill:#2196F3
实际演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 docker run -d --name web1 nginx docker run -d --name web2 nginx docker inspect web1 | grep IPAddress docker inspect web2 | grep IPAddress docker exec web1 ping 172.17.0.3 docker exec web1 ping web2
自定义bridge网络(推荐!) 1 2 3 4 5 6 7 8 9 10 docker network create my-network docker run -d --name web1 --network my-network nginx docker run -d --name web2 --network my-network nginx docker exec web1 ping web2
为什么推荐自定义bridge网络?
对比项
默认bridge
自定义bridge
容器名访问
❌ 不支持
✅ 支持
DNS解析
❌ 没有
✅ 有
隔离性
所有容器都在同一网络
可以创建多个隔离网络
灵活性
低
高
模式二:host 主机网络(高性能场景) 通俗解释 把host模式想象成直接把电脑连到公网 :
容器不再有自己独立的IP
容器直接使用宿主机的IP和端口
性能最高,因为没有NAT转换
但端口会冲突,因为所有容器共享宿主机端口
工作原理图 graph TB
subgraph "宿主机"
C1["容器A<br/>直接使用宿主机IP"]
C2["容器B<br/>直接使用宿主机IP"]
end
Internet["互联网"]
C1 --> Internet
C2 --> Internet
style C1 fill:#FF9800
style C2 fill:#FF9800
实际演示 1 2 3 4 5 6 7 8 9 docker run -d --name web-host --network host nginx docker run -d --name web-host2 --network host nginx
适用场景 ✅ 适合 :
对网络性能要求极高的应用
需要处理大量网络连接的服务
单容器应用(不用担心端口冲突)
❌ 不适合 :
多个容器都需要用相同端口
需要容器间隔离的场景
需要端口映射的场景
模式三:none 无网络(完全隔离) 通俗解释 把none模式想象成把电脑网线拔掉 :
容器没有任何网络连接
完全隔离,不能访问外部网络
其他容器也无法访问它
用于完全不需要网络的场景
实际演示 1 2 3 4 5 6 7 8 9 10 docker run -d --name web-none --network none nginx docker exec web-none ping baidu.com docker exec web-none ip addr
适用场景 ✅ 适合 :
数据处理任务(不需要网络)
安全敏感的应用(完全隔离)
批处理作业
❌ 不适合 :
需要访问外部网络的应用
需要与其他容器通信的应用
需要被外部访问的服务
模式四:overlay 覆盖网络(跨主机通信) 通俗解释 把overlay网络想象成VPN :
多台物理服务器上的容器
通过overlay网络可以像在同一个局域网一样通信
用于Docker Swarm集群或多主机部署
普通单机环境用不到
工作原理图 graph TB
subgraph "主机A"
C1["容器A<br/>10.0.0.2"]
end
subgraph "主机B"
C2["容器B<br/>10.0.0.3"]
end
subgraph "overlay 网络"
VPN["虚拟网络隧道"]
end
C1 --> VPN
C2 --> VPN
VPN --> C1
VPN --> C2
style C1 fill:#2196F3
style C2 fill:#2196F3
style VPN fill:#FF9800
实际演示(需要Swarm模式) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 docker swarm init docker network create --driver overlay my-overlay docker service create --name web --network my-overlay nginx docker service create --name db --network my-overlay mysql
模式五:macvlan MAC地址网络(特殊硬件设备) 通俗解释 把macvlan模式想象成给容器办一张独立的网卡 :
容器有自己独立的MAC地址
可以直接获得物理网络的IP
容器看起来就像一台独立的物理机
用于需要容器直接接入物理网络的场景
实际演示 1 2 3 4 5 6 7 8 9 10 11 docker network create -d macvlan \ --subnet=192.168.1.0/24 \ --gateway=192.168.1.1 \ -o parent=eth0 \ my-macvlan docker run -d --name web --network my-macvlan nginx
网络操作常用命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 docker network create my-network docker network create --subnet=172.18.0.0/16 my-network docker network ls docker network inspect my-network docker network connect my-network my-nginx docker network disconnect my-network my-nginx docker network rm my-network docker network prune
容器间通信实战 1 2 3 4 5 6 7 8 9 10 11 docker network create app-network docker run -d --name mysql --network app-network -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0 docker run -d --name app --network app-network -e DB_HOST=mysql my-app:v1 docker exec app ping mysql
网络模式选择指南 graph TB
Start["选择网络模式"]
Q1{"需要跨主机通信?"}
Q2{"需要完全隔离?"}
Q3{"需要最高性能?"}
Q4{"需要容器间通信?"}
Overlay["overlay 模式"]
None["none 模式"]
Host["host 模式"]
Bridge["bridge 模式"]
Start --> Q1
Q1 -->|"是"| Overlay
Q1 -->|"否"| Q2
Q2 -->|"是"| None
Q2 -->|"否"| Q3
Q3 -->|"是"| Host
Q3 -->|"否"| Q4
Q4 -->|"是"| Bridge
Q4 -->|"否"| None
style Overlay fill:#FF9800
style None fill:#9E9E9E
style Host fill:#2196F3
style Bridge fill:#4CAF50
模式
适用场景
性能
隔离性
复杂度
bridge
多容器应用(默认)
中
高
简单
host
高性能单容器
高
低
简单
none
完全隔离任务
低
最高
简单
overlay
跨主机集群
中
高
复杂
macvlan
直接接入物理网络
高
中
复杂
Docker Compose 中的网络配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 version: '3.9' services: web: image: nginx networks: - frontend - backend database: image: mysql networks: - backend networks: frontend: driver: bridge backend: driver: bridge ipam: config: - subnet: 172.18 .0 .0 /16
1 2 3 4 5 docker network ls | grep myproject docker network inspect myproject_backend
7. Docker 卷管理 卷类型
命名卷 :由 Docker 管理,可重用
绑定卷 :挂载主机目录
tmpfs 卷 :临时文件系统
卷操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 docker volume create my-volume docker volume ls docker volume inspect my-volume docker volume rm my-volume docker volume prune docker run -d -v my-volume:/data my-app:v1 docker run -d -v /host/data:/container/data my-app:v1
数据备份和恢复 1 2 3 4 5 docker run --rm -v my-volume:/source -v $(pwd ):/backup alpine tar czf /backup/backup.tar.gz -C /source . docker run --rm -v my-volume:/target -v $(pwd ):/backup alpine tar xzf /backup/backup.tar.gz -C /target
8. Dockerfile 编写指南 RUN、CMD、ENTRYPOINT 区别详解 可以把 Docker 镜像想象成一个做菜的菜谱 ,而容器就是最终做出来的一道菜 。
graph LR
subgraph "构建阶段 docker build"
A["FROM 基础镜像"]
B["RUN 执行命令<br/>安装软件/配置环境"]
C["COPY/ADD 复制文件"]
D["构建完成"]
end
subgraph "运行阶段 docker run"
E["ENTRYPOINT 入口程序"]
F["CMD 默认参数"]
G["容器运行"]
end
A --> B --> C --> D
D --> E
E --> F --> G
style A fill:#4CAF50
style B fill:#FF9800
style C fill:#2196F3
style D fill:#9C27B0
style E fill:#F44336
style F fill:#FF5722
style G fill:#795548
RUN:准备食材的阶段(构建镜像时执行) RUN 是在**制作菜谱(构建镜像)**的过程中执行的命令。
做什么 :安装软件、创建目录、下载依赖等。这些都是为最终运行容器做准备。
什么时候执行 :当你执行 docker build 时。
结果 :会生成一个新的镜像层。比如你 RUN apt-get install python,这就会把 Python 打包进镜像里。
打个比方 :就像你在写菜谱时,先把土豆削皮、切块(RUN),这些步骤做完之后,土豆块就成了菜谱的一部分。以后每次照着这个菜谱做菜,土豆块都是现成的。
1 2 3 FROM ubuntuRUN apt-get update && apt-get install -y nginx RUN mkdir /app
CMD:默认的烹饪方式(容器启动时执行) CMD 是容器启动时默认要执行的命令 。它就像一个菜谱上写的”推荐做法”。
做什么 :指定容器启动后默认运行的程序,比如 nginx -g "daemon off;"。
什么时候执行 :当你执行 docker run 且没有附加其他命令 时。
特点 :可以被覆盖。如果你在 docker run 后面加了别的命令,CMD 就会被替换掉。
打个比方 :菜谱上写着”建议清蒸”(CMD)。但你今天想红烧,就可以在 docker run 时改成红烧。菜谱的建议被覆盖了。
1 2 FROM ubuntuCMD ["echo" , "Hello, World!" ]
运行时:
1 2 docker run myimage docker run myimage echo Hi
ENTRYPOINT:固定的烹饪方式(容器启动时执行) ENTRYPOINT 也是容器启动时执行的命令 ,但它更固定 。
做什么 :定义容器的主要入口程序。通常配合 CMD 一起使用。
什么时候执行 :同样在 docker run 时执行。
特点 :不容易被覆盖 。如果你在 docker run 后面加了参数,这些参数会作为追加参数 传给 ENTRYPOINT,而不是替换它。
打个比方 :菜谱规定”必须用蒸的”(ENTRYPOINT)。你可以选择蒸5分钟还是10分钟(通过 CMD 或命令行传参),但你不能改成炒的。
1 2 3 FROM ubuntuENTRYPOINT ["echo" ] CMD ["Hello, World!" ]
运行时:
1 2 3 docker run myimage docker run myimage Hi docker run myimage --help
核心区别总结表
指令
执行时机
能否被覆盖
典型用途
RUN
docker build 构建时
不可覆盖(已固化到镜像)
安装软件、配置环境
CMD
docker run 启动时
可以 被 docker run 后的命令完全替换
提供默认启动命令和参数
ENTRYPOINT
docker run 启动时
不能 被替换,只能追加参数
定义容器的主程序入口
实战中的黄金搭档:ENTRYPOINT + CMD 这是最常见的用法,组合起来非常灵活:
1 2 3 4 5 6 7 FROM python:3.9 WORKDIR /app COPY . . RUN pip install -r requirements.txt ENTRYPOINT ["python" ] CMD ["app.py" ]
默认启动:docker run myapp → 执行 python app.py
运行其他脚本:docker run myapp test.py → 执行 python test.py(CMD被覆盖,但ENTRYPOINT不变)
加参数:docker run myapp app.py --debug → 执行 python app.py --debug
这样既保证了容器的主功能(Python解释器)不被篡改,又保留了灵活性(可以运行不同的脚本)。
EXPOSE 使用详解 EXPOSE 可以理解为在容器的墙上开了一扇窗 ,告诉别人:”我这个容器里面有个服务在用这个端口号”。
但是注意 :这扇窗只是声明 ,并没有真正打开!真正的开门(端口映射)需要在 docker run 时用 -p 参数来做。
基本语法 1 EXPOSE <端口号> [<端口号>/<协议>]
端口号 :容器内部服务的端口
协议 :可选,默认是 TCP,也可以指定 UDP
1 2 3 4 EXPOSE 80 EXPOSE 53 /udp EXPOSE 8080 /tcp EXPOSE 3000
实际作用 1. 文档作用 :告诉使用者这个容器提供什么服务
1 2 3 4 FROM nginxEXPOSE 80 EXPOSE 443
2. 配合 -P 参数自动映射
1 2 3 4 5 docker run -P mynginx
3. 不写 EXPOSE 也能映射端口
1 2 docker run -p 8080:80 mynginx
常见误区澄清
误区
真相
写了EXPOSE就能从外部访问
❌ 还需要 -p 或 -P 做端口映射
多个EXPOSE会互相影响
✅ 每个EXPOSE独立声明,互不影响
EXPOSE必须写在最后
❌ 可以写在Dockerfile任意位置,习惯放末尾
不写EXPOSE就不能映射端口
❌ 照样可以用 -p 手动映射
完整示例:从 Dockerfile 到运行 1 2 3 4 5 6 7 8 9 10 FROM node:16 -alpineWORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 CMD ["node" , "app.js" ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 docker build -t my-node-app . docker run -p 8080:3000 my-node-app docker run -P my-node-app docker port my-node-app
结合 docker-compose 使用 1 2 3 4 5 6 7 8 services: web: build: . ports: - "8080:3000" expose: - "3000"
ADD 与 COPY 区别详解 ADD 和 COPY 是 Dockerfile 里两个看起来很像的指令,很多新手容易混淆。
核心区别一句话
COPY :纯粹的”复制文件”,只做一件事。
ADD :COPY 的增强版,除了复制还能自动解压 和支持URL下载 。
COPY:老实本分的搬运工 只做一件事 :把文件从宿主机(构建上下文)复制到镜像里。
1 2 3 4 COPY ./app /app COPY package.json /app/ COPY *.txt /tmp/
特点 :
简单、透明、可预期
不会做任何额外操作
推荐优先使用
ADD:多才多艺的多面手 除了 COPY 的功能,还有两个特殊能力:
能力1:自动解压压缩包
1 2 3 ADD app.tar.gz /app/ ADD source.zip /data/
对比 COPY 的行为:
能力2:支持URL下载
1 2 ADD https://example.com/file.tar.gz /tmp/
注意 :这个功能在实际生产中很少用 ,因为:
无法利用缓存(每次构建都会重新下载)
不好控制版本
推荐用 curl 或 wget 替代
实战对比表
场景
用 COPY
用 ADD
复制本地代码文件
✅ 首选
❌ 没必要
复制配置文件
✅ 首选
❌ 没必要
复制并自动解压 tar.gz
❌ 不解压
✅ 自动解压
复制并自动解压 zip
❌ 不解压
✅ 自动解压
从URL下载文件
❌ 不支持
✅ 支持(但不推荐)
复制目录结构
✅ 首选
❌ 没必要
最佳实践:90%的情况用 COPY 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 FROM python:3.9 -slimWORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . ADD model_weights.tar.gz /app/models/ CMD ["python" , "app.py" ]
为什么推荐优先用 COPY? 1. 行为更清晰
1 2 COPY app.tar.gz /tmp/ ADD app.tar.gz /tmp/
2. 安全性更好
ADD 的 URL 下载功能可能带来安全隐患
自动解压可能产生意想不到的文件结构
3. Docker 官方也推荐
Docker 官方文档明确建议:除非你需要自动解压功能,否则始终使用 COPY。
特殊场景:什么时候非用 ADD 不可? 场景1:需要自动解压
1 2 3 4 5 6 ADD model.tar.gz /models/ COPY model.tar.gz /tmp/ RUN tar -xzf /tmp/model.tar.gz -C /models/ && rm /tmp/model.tar.gz
常见的坑 坑1:ADD 解压行为不一致
1 2 3 4 5 ADD ./files.tar.gz /dest/ ADD https://example.com/files.tar.gz /dest/
坑2:ADD 的 URL 下载没有缓存
1 2 3 4 5 ADD https://example.com/data.txt /data/ RUN curl -o /data.txt https://example.com/data.txt
COPY 多文件语法与 –from 使用 COPY 可以一次复制多个文件 1 2 3 4 5 6 7 COPY package.json package-lock.json /app/ COPY package*.json /app/ COPY *.txt /app/ COPY src/**/*.js /app/dist/
注意事项 1 2 3 4 5 6 7 8 COPY file1.txt file2.txt config.json /app/config/ COPY file1.txt file2.txt /app/output.txt COPY *.json /app/config/
–from 主要用于多阶段构建 目前支持的指令是:
1. COPY –from(最常用)
1 2 3 4 5 6 7 8 FROM node:16 AS builderWORKDIR /app COPY . . RUN npm run build FROM nginx:alpineCOPY --from=builder /app/dist /usr/share/nginx/html
2. COPY –from 还可以从外部镜像复制
1 2 COPY --from=nginx:latest /etc/nginx/nginx.conf /etc/nginx/
完整的多阶段构建示例 graph TB
subgraph "阶段1:Go后端构建"
G1["FROM golang:1.19 AS go-builder"]
G2["COPY go.mod go.sum ./"]
G3["RUN go mod download"]
G4["COPY . ."]
G5["RUN go build -o server"]
end
subgraph "阶段2:前端构建"
N1["FROM node:16 AS frontend-builder"]
N2["COPY frontend/package*.json ./"]
N3["RUN npm ci"]
N4["COPY frontend/ ."]
N5["RUN npm run build"]
end
subgraph "阶段3:最终镜像"
A1["FROM alpine:latest"]
A2["COPY --from=go-builder 二进制文件"]
A3["COPY --from=frontend-builder 静态文件"]
A4["EXPOSE 8080"]
end
G1 --> G2 --> G3 --> G4 --> G5
N1 --> N2 --> N3 --> N4 --> N5
A1 --> A2
A1 --> A3
A2 --> A4
A3 --> A4
G5 -.-> A2
N5 -.-> A3
常见用法总结表
指令
是否支持 --from
用途
COPY
✅ 支持
从其他阶段或外部镜像复制文件
ADD
❌ 不支持
只能用常规的源路径
RUN
❌ 不支持
不能直接用 --from
分层构建与缓存机制 可以把 Docker 镜像想象成一个千层蛋糕 :
每一层 :对应 Dockerfile 里的一条指令(FROM、RUN、COPY、ADD 等)
每一层都是只读的 :一旦创建就不会改变
层叠起来 :形成最终的镜像
graph TB
subgraph "Docker 镜像分层结构"
L5["第5层:CMD 启动命令(元数据层)"]
L4["第4层:COPY index.html 复制文件"]
L3["第3层:RUN apt-get install 安装nginx"]
L2["第2层:RUN apt-get update 更新包列表"]
L1["第1层:FROM ubuntu:20.04 基础层"]
end
L5 --> L4
L4 --> L3
L3 --> L2
L2 --> L1
style L1 fill:#4CAF50
style L2 fill:#8BC34A
style L3 fill:#CDDC39
style L4 fill:#FFEB3B
style L5 fill:#FFC107
缓存机制:像 Git 一样聪明 Docker 构建时会逐条执行 Dockerfile 中的指令,并对每条指令的结果进行缓存 。
缓存命中条件(缺一不可) :
父层缓存命中 :上一层的缓存必须存在且有效
指令完全相同 :RUN apt-get install -y nginx 和 RUN apt-get install -y nginx 一样
上下文文件未变 :对于 COPY 和 ADD,会校验文件的 checksum
示例 :
1 2 3 4 5 FROM node:16 -alpine RUN apk add --no-cache git COPY package.json /app/ RUN npm install COPY . /app
第一次构建耗时90秒,第二次构建时如果 package.json 没变,只需要重新执行最后一步,构建时间从90秒降到几秒!
缓存失效的连锁反应 一旦某层缓存失效,后续所有层都会重建 :
graph TB
subgraph "缓存失效连锁反应"
L1["FROM node:16-alpine ✅ 缓存命中"]
L2["COPY package.json /app/ ✅ 缓存命中"]
L3["RUN npm install ✅ 缓存命中"]
L4["COPY src/ /app/src ❌ 缓存失效"]
L5["RUN npm run build ❌ 被迫重新构建"]
L6["COPY dist/ /app/dist ❌ 被迫重新构建"]
end
L1 --> L2
L2 --> L3
L3 --> L4
L4 --> L5
L5 --> L6
style L1 fill:#90EE90
style L2 fill:#90EE90
style L3 fill:#90EE90
style L4 fill:#FFB6C1
style L5 fill:#FFB6C1
style L6 fill:#FFB6C1
这就是为什么把不常变的指令放在前面 如此重要!
分层构建的最佳实践 1. 分离依赖和源码(最重要!)
1 2 3 4 5 6 7 8 9 COPY . /app RUN npm install COPY package.json package-lock.json /app/ RUN npm install COPY . /app
2. 合理排序指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 FROM python:3.9 -slimRUN apt-get update && \ apt-get install -y --no-install-recommends \ build-essential \ libpq-dev && \ rm -rf /var/lib/apt/lists/* COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["python" , "app.py" ]
3. 利用 .dockerignore 减少无效缓存失效
1 2 3 4 5 6 7 8 # .dockerignore node_modules .git *.log .env dist .cache __pycache__
这样 COPY . 时就不会因为这些无关文件的变化导致缓存失效。
查看和清理缓存的命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 docker system df docker builder ls docker builder prune docker build --no-cache -t myapp . docker history myapp
一个完整的优化案例 原始 Dockerfile(低效):
1 2 3 4 5 6 7 FROM node:16 COPY . /app WORKDIR /app RUN npm install RUN npm run build EXPOSE 3000 CMD ["npm" , "start" ]
优化后(高效):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 FROM node:16 -alpine AS builderWORKDIR /app COPY package.json package-lock.json ./ RUN npm ci --only=production COPY . . RUN npm run build FROM node:16 -alpineWORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY package.json . EXPOSE 3000 CMD ["node" , "dist/server.js" ]
优化效果 :
首次构建:约120秒
仅改代码:约5秒(只重建最后几层)
改依赖:约30秒(重建依赖层和后续层)
镜像大小:从500MB降到150MB
Dockerfile 指令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 FROM ubuntu:20.04 LABEL maintainer="your@email.com" WORKDIR /app COPY . . COPY requirements.txt . COPY . . RUN apt-get update && apt-get install -y python3 python3-pip RUN pip3 install -r requirements.txt ENV PYTHONUNBUFFERED=1 EXPOSE 8000 CMD ["python3" , "app.py" ]
多阶段构建 1 2 3 4 5 6 7 8 9 10 11 12 13 FROM node:18 AS builderWORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM nginx:alpineCOPY --from=builder /app/dist /usr/share/nginx/html EXPOSE 80 CMD ["nginx" , "-g" , "daemon off;" ]
最佳实践示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 FROM node:18 -alpineWORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . RUN addgroup -g 1001 -S nodejs RUN adduser -S nodejs -u 1001 USER nodejsEXPOSE 3000 CMD ["node" , "server.js" ]
常见 Dockerfile 示例 Python 应用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 FROM python:3.9 -slimWORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 CMD ["python" , "app.py" ]
Java 应用 1 2 3 4 5 6 7 8 9 10 11 12 FROM maven:3.8 -openjdk-11 AS builderWORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests FROM openjdk:11 -jre-slimWORKDIR /app COPY --from=builder /app/target/*.jar app.jar EXPOSE 8080 CMD ["java" , "-jar" , "app.jar" ]
9. Docker 常见问题和坑 问题 1:权限问题 现象 :容器内文件权限错误解决方案 :
1 2 3 4 5 sudo chown -R 1000:1000 /host/pathRUN chown -R nodejs:nodejs /app
问题 2:网络问题 现象 :容器无法访问外网解决方案 :
1 2 3 4 5 docker exec container cat /etc/resolv.conf docker run --dns=8.8.8.8 my-image
问题 3:磁盘空间 现象 :Docker 占用大量磁盘空间解决方案 :
1 2 3 4 5 6 7 8 9 10 docker system df docker system prune -a docker image prune -a docker container prune docker volume prune
问题 4:镜像拉取失败 现象 :拉取镜像超时或失败解决方案 :
1 2 3 4 5 6 7 8 9 sudo mkdir -p /etc/dockersudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors" : ["https://mirror.aliyuncs.com" ] } EOF sudo systemctl daemon-reloadsudo systemctl restart docker
问题 5:容器日志过大 现象 :容器日志占用大量空间解决方案 :
1 2 3 4 5 docker run --log-opt max-size=10m --log-opt max-file=3 my-image sudo truncate -s 0 /var/lib/docker/containers/*/*-json.log
问题 6:容器时间同步 现象 :容器内时间与主机不一致解决方案 :
1 2 3 4 5 docker run -v /etc/localtime:/etc/localtime:ro my-image docker run -e TZ=Asia/Shanghai my-image
问题 7:容器资源限制 现象 :容器占用过多资源解决方案 :
1 2 3 4 docker run -d --cpus=1 --memory=512m my-image
问题 8:容器无法删除 现象 :容器删除失败解决方案 :
1 2 3 4 5 6 7 8 docker rm -f container_name docker container prune sudo systemctl restart docker
第二部分:Docker Compose 10. Docker Compose 简介 什么是 Docker Compose? Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。通过一个 YAML 文件,你可以配置应用程序的所有服务,然后使用一个命令创建并启动所有服务。
为什么使用 Docker Compose?
多容器管理 :轻松管理 Web 服务器、数据库、缓存等多个服务
环境一致性 :开发、测试、生产环境使用相同的配置
快速搭建开发环境 :一条命令启动整个技术栈
配置版本控制 :YAML 文件可以纳入版本控制
易于扩展 :可以轻松添加新服务或修改现有配置
11. Docker Compose 安装 前提条件 确保已安装 Docker Engine:
Linux 安装 方法一:使用包管理器(推荐) 1 2 3 4 5 6 7 8 sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s) -$(uname -m) " -o /usr/local/bin/docker-composesudo chmod +x /usr/local/bin/docker-composedocker-compose --version
方法二:使用 pip 安装 1 sudo pip install docker-compose
Windows/macOS 安装 Docker Desktop 用户 Docker Desktop 已内置 Docker Compose,无需单独安装。
验证安装 1 2 3 docker-compose --version docker compose version
版本说明
V1 :docker-compose(Python 版本,已停止维护)
V2 :docker compose(Go 版本,推荐使用)
12. Docker Compose 文件详解 基本结构 1 2 3 4 5 6 7 8 9 10 11 12 13 version: '3.9' services: web: database: networks: frontend: volumes: data:
服务配置项详解 镜像配置 1 2 3 4 5 6 7 8 9 services: web: image: nginx:latest build: ./app build: context: ./app dockerfile: Dockerfile args: NODE_ENV: production
build.context 参数详解 context 是 build 配置中的核心参数,用于指定**构建上下文(Build Context)**的路径。
什么是构建上下文?
构建上下文是 Docker 守护进程在构建镜像时可以访问的文件和目录集合。Docker 会将这个目录的内容发送给 Docker 守护进程,用于构建镜像。
语法格式 :
1 2 3 4 services: web: build: context: ./path/to/context
context 的三种形式 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 services: web: build: ./app services: web: build: context: ./app dockerfile: Dockerfile services: web: build: context: /absolute/path/to/app
context 的实际作用 :
1 2 3 4 5 6 7 8 9 10 11 myapp/ ├── docker-compose.yml ├── frontend/ │ ├── Dockerfile │ └── src/ ├── backend/ │ ├── Dockerfile │ └── src/ └── shared/ └── config/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 services: frontend: build: context: ./frontend dockerfile: Dockerfile backend: build: context: ./backend dockerfile: Dockerfile app: build: context: . dockerfile: ./app/Dockerfile
context 与 COPY/ADD 的关系 :
1 2 3 4 5 COPY . . COPY package.json . COPY ../shared/config .
多阶段构建中的 context :
1 2 3 4 5 services: app: build: context: . dockerfile: Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 FROM node:16 AS frontendWORKDIR /app COPY frontend/package*.json ./ RUN npm ci COPY frontend/ . RUN npm run build FROM python:3.9 AS backendWORKDIR /app COPY backend/requirements.txt . RUN pip install -r requirements.txt COPY backend/ . FROM alpine:latestCOPY --from=frontend /app/dist /var/www/html COPY --from=backend /app /opt/app
常见使用场景 :
场景
context 配置
说明
单服务项目
context: .
根目录作为上下文
多服务项目
context: ./service-name
每个服务独立上下文
共享配置
context: .
可以访问所有子目录
monorepo
context: ./packages/app
指向具体包目录
性能优化建议 :
1 2 3 4 5 6 7 8 9 10 11 12 services: web: build: context: . services: web: build: context: ./app
1 2 3 4 5 6 # .dockerignore(放在 context 目录下) node_modules .git *.log .env dist
容器配置 1 2 3 4 5 6 7 services: web: container_name: my-web hostname: web-server restart: always user: "1000:1000" working_dir: /app
端口映射 1 2 3 4 5 6 7 services: web: ports: - "80:80" - "443:443" - "127.0.0.1:8080:8080" - "8080-8090:8080-8090"
环境变量 1 2 3 4 5 6 7 8 services: database: environment: - MYSQL_ROOT_PASSWORD=secret - MYSQL_DATABASE=mydb env_file: - .env - ./config/database.env
卷挂载 1 2 3 4 5 6 services: database: volumes: - mysql-data:/var/lib/mysql - ./init-scripts:/docker-entrypoint-initdb.d - ./config/my.cnf:/etc/mysql/conf.d/my.cnf:ro
网络配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 services: web: networks: - frontend ports: - "80:80" database: networks: - backend networks: frontend: backend: driver: bridge
依赖关系 1 2 3 4 5 6 7 8 9 10 11 12 services: web: depends_on: - database - redis database: healthcheck: test: ["CMD" , "mysqladmin" , "ping" , "-h" , "localhost" ] interval: 10s timeout: 5s retries: 3
资源限制 1 2 3 4 5 6 7 8 9 10 services: web: deploy: resources: limits: cpus: '0.5' memory: 512M reservations: cpus: '0.25' memory: 256M
日志配置 1 2 3 4 5 6 7 services: web: logging: driver: json-file options: max-size: "10m" max-file: "3"
13. Docker Compose 常用命令 基础命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 docker compose up -d docker compose up -d --build docker compose down docker compose down -v docker compose ps docker compose ps -a
服务管理 1 2 3 4 5 6 7 8 9 10 11 docker compose start web docker compose stop web docker compose restart web docker compose up -d --force-recreate web
日志和调试 1 2 3 4 5 6 7 8 9 10 11 docker compose logs docker compose logs web docker compose logs -f web docker compose logs --tail =100 web
执行命令 1 2 3 4 5 6 7 8 docker compose exec web bash docker compose exec database mysql -u root -p docker compose run web python manage.py migrate
镜像管理 1 2 3 4 5 6 7 8 9 10 11 docker compose build docker compose build web docker compose pull docker image prune -a
网络和卷管理 1 2 3 4 5 6 7 8 docker network ls docker volume ls docker volume prune
配置验证 1 2 3 4 5 docker compose config docker compose config --services
14. Docker Compose 实用示例 示例 1:Nginx 反向代理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 version: '3.9' services: nginx-proxy: image: nginx:latest container_name: nginx-proxy restart: always ports: - "80:80" - "443:443" volumes: - ./nginx/conf.d:/etc/nginx/conf.d - ./nginx/ssl:/etc/nginx/ssl - ./html:/usr/share/nginx/html - ./logs:/var/log/nginx extra_hosts: - "host.docker.internal:host-gateway" networks: - web-network networks: web-network: driver: bridge
示例 2:MySQL 数据库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 version: '3.9' services: mysql: image: mysql:8.0 container_name: mysql-db restart: always environment: MYSQL_ROOT_PASSWORD: StrongRootPass123 MYSQL_DATABASE: myapp MYSQL_USER: admin MYSQL_PASSWORD: StrongPassword123 ports: - "3306:3306" volumes: - mysql-data:/var/lib/mysql - ./mysql/conf.d:/etc/mysql/conf.d - ./mysql/initdb:/docker-entrypoint-initdb.d command: --default-authentication-plugin=mysql_native_password healthcheck: test: ["CMD" , "mysqladmin" , "ping" , "-h" , "localhost" ] interval: 10s timeout: 5s retries: 3 networks: - backend-network volumes: mysql-data: networks: backend-network: driver: bridge
示例 3:Redis 缓存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 version: '3.9' services: redis: image: redis:7-alpine container_name: redis-cache restart: always ports: - "6379:6379" volumes: - redis-data:/data command: redis-server --appendonly yes --requirepass StrongRedisPass123 healthcheck: test: ["CMD" , "redis-cli" , "-a" , "StrongRedisPass123" , "ping" ] interval: 10s timeout: 5s retries: 3 networks: - backend-network volumes: redis-data: networks: backend-network: driver: bridge
示例 4:完整的 Web 应用栈 网络拓扑图 :
graph TB
subgraph "frontend-network"
Nginx["Nginx 反向代理<br/>:80/:443"]
Frontend["前端服务<br/>:3000"]
Backend["后端服务<br/>:8000"]
end
subgraph "backend-network"
Backend2["后端服务"]
MySQL["MySQL 数据库<br/>:3306"]
Redis["Redis 缓存<br/>:6379"]
end
Nginx --> Frontend
Frontend --> Backend
Backend2 --> MySQL
Backend2 --> Redis
style Nginx fill:#4CAF50
style Frontend fill:#2196F3
style Backend fill:#FF9800
style Backend2 fill:#FF9800
style MySQL fill:#00BCD4
style Redis fill:#F44336
docker-compose.yml :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 services: frontend: build: context: ./frontend dockerfile: Dockerfile container_name: frontend restart: always ports: - "3000:3000" environment: - NODE_ENV=production - API_URL=http://backend:8000 depends_on: backend: condition: service_healthy networks: - frontend-network backend: build: context: ./backend dockerfile: Dockerfile container_name: backend restart: always ports: - "8000:8000" environment: - DATABASE_URL=mysql://admin:StrongPassword123@database:3306/myapp - REDIS_URL=redis://redis:6379/0 - SECRET_KEY=your-secret-key-here depends_on: database: condition: service_healthy redis: condition: service_healthy networks: - frontend-network - backend-network database: image: mysql:8.0 container_name: mysql-db restart: always environment: MYSQL_ROOT_PASSWORD: StrongRootPass123 MYSQL_DATABASE: myapp MYSQL_USER: admin MYSQL_PASSWORD: StrongPassword123 ports: - "3306:3306" volumes: - mysql-data:/var/lib/mysql healthcheck: test: ["CMD" , "mysqladmin" , "ping" , "-h" , "localhost" ] interval: 10s timeout: 5s retries: 3 networks: - backend-network redis: image: redis:7-alpine container_name: redis-cache restart: always ports: - "6379:6379" volumes: - redis-data:/data command: redis-server --appendonly yes healthcheck: test: ["CMD" , "redis-cli" , "ping" ] interval: 10s timeout: 5s retries: 3 networks: - backend-network nginx: image: nginx:latest container_name: nginx-proxy restart: always ports: - "80:80" - "443:443" volumes: - ./nginx/conf.d:/etc/nginx/conf.d - ./nginx/ssl:/etc/nginx/ssl depends_on: - frontend - backend networks: - frontend-network volumes: mysql-data: redis-data: networks: frontend-network: backend-network:
示例 5:Python Django 应用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 version: '3.9' services: web: build: . command: python manage.py runserver 0.0 .0 .0 :8000 volumes: - .:/app ports: - "8000:8000" environment: - DEBUG=1 - DATABASE_URL=postgres://postgres:password@db:5432/myapp depends_on: - db networks: - app-network db: image: postgres:14 environment: POSTGRES_DB: myapp POSTGRES_USER: postgres POSTGRES_PASSWORD: password volumes: - postgres-data:/var/lib/postgresql/data ports: - "5432:5432" networks: - app-network redis: image: redis:7 ports: - "6379:6379" networks: - app-network volumes: postgres-data: networks: app-network:
示例 6:微服务架构 网络拓扑图 :
graph TB
subgraph "microservices-network"
Gateway["API 网关 Kong<br/>:8000/:8443/:8001"]
UserService["用户服务<br/>:3001"]
OrderService["订单服务<br/>:3002"]
MongoDB["MongoDB<br/>:27017"]
PostgreSQL["PostgreSQL<br/>:5432"]
Redis["Redis<br/>:6379"]
end
Gateway --> UserService
Gateway --> OrderService
UserService --> MongoDB
OrderService --> PostgreSQL
OrderService --> Redis
style Gateway fill:#4CAF50
style UserService fill:#2196F3
style OrderService fill:#FF9800
style MongoDB fill:#00BCD4
style PostgreSQL fill:#3F51B5
style Redis fill:#F44336
docker-compose.yml :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 services: api-gateway: image: kong:latest container_name: api-gateway restart: always ports: - "8000:8000" - "8443:8443" - "8001:8001" environment: KONG_DATABASE: "off" KONG_DECLARATIVE_CONFIG: /etc/kong/kong.yml KONG_PROXY_ACCESS_LOG: /dev/stdout KONG_ADMIN_ACCESS_LOG: /dev/stdout KONG_PROXY_ERROR_LOG: /dev/stderr KONG_ADMIN_ERROR_LOG: /dev/stderr volumes: - ./kong:/etc/kong networks: - microservices-network user-service: build: ./services/user container_name: user-service restart: always ports: - "3001:3000" environment: - NODE_ENV=production - DB_HOST=mongodb - DB_PORT=27017 - DB_NAME=userdb depends_on: - mongodb networks: - microservices-network order-service: build: ./services/order container_name: order-service restart: always ports: - "3002:3000" environment: - NODE_ENV=production - DB_HOST=postgresql - DB_PORT=5432 - DB_NAME=orderdb - REDIS_HOST=redis - REDIS_PORT=6379 depends_on: - postgresql - redis networks: - microservices-network mongodb: image: mongo:6 container_name: mongodb restart: always ports: - "27017:27017" volumes: - mongo-data:/data/db networks: - microservices-network postgresql: image: postgres:14 container_name: postgresql restart: always environment: POSTGRES_DB: orderdb POSTGRES_USER: postgres POSTGRES_PASSWORD: password ports: - "5432:5432" volumes: - postgres-data:/var/lib/postgresql/data networks: - microservices-network redis: image: redis:7-alpine container_name: redis restart: always ports: - "6379:6379" volumes: - redis-data:/data networks: - microservices-network volumes: mongo-data: postgres-data: redis-data: networks: microservices-network: driver: bridge
15. Docker Compose 常见问题和坑 问题 1:容器启动顺序问题 现象 :应用启动时数据库还没准备好,导致连接失败。
解决方案 :
1 2 3 4 5 6 7 8 9 10 11 12 services: web: depends_on: database: condition: service_healthy database: healthcheck: test: ["CMD" , "mysqladmin" , "ping" , "-h" , "localhost" ] interval: 10s timeout: 5s retries: 3
问题 2:网络通信问题 现象 :容器之间无法通信。
解决方案 :
1 2 3 4 5 6 7 8 9 10 11 services: web: networks: - app-network database: networks: - app-network networks: app-network: driver: bridge
问题 3:数据持久化问题 现象 :容器重启后数据丢失。
graph TB
subgraph "不使用数据卷(数据丢失)"
A1["容器停止"]
A2["数据消失"]
A3["新容器启动"]
A4["数据丢失"]
end
subgraph "使用数据卷(数据持久化)"
B1["容器停止"]
B2["数据保存在卷中"]
B3["新容器启动"]
B4["数据恢复"]
end
A1 --> A2 --> A3 --> A4
B1 --> B2 --> B3 --> B4
style A2 fill:#FFB6C1
style A4 fill:#FFB6C1
style B2 fill:#90EE90
style B4 fill:#90EE90
解决方案 :
1 2 3 4 5 6 7 services: database: volumes: - mysql-data:/var/lib/mysql volumes: mysql-data:
问题 4:端口冲突 现象 :端口被占用,容器无法启动。
解决方案 :
1 2 3 4 5 6 netstat -tulpn | grep :80 ports: - "8080:80"
问题 5:环境变量不生效 现象 :配置的环境变量没有生效。
解决方案 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 services: web: environment: - NODE_ENV=production - API_KEY=12345 environment: NODE_ENV: production API_KEY: 12345 env_file: - .env
问题 6:构建缓存问题 现象 :修改代码后没有生效。
解决方案 :
1 2 3 4 5 docker compose build --no-cache docker compose up -d --force-recreate
问题 7:权限问题 现象 :容器内文件权限错误。
解决方案 :
1 2 3 4 5 services: web: user: "1000:1000" volumes: - ./app:/app
问题 8:容器无法访问宿主机服务 现象 :容器内无法访问宿主机的数据库等服务。
解决方案 :
1 2 3 4 services: web: extra_hosts: - "host.docker.internal:host-gateway"
问题 9:日志过大 现象 :容器日志占用大量磁盘空间。
解决方案 :
1 2 3 4 5 6 7 services: web: logging: driver: json-file options: max-size: "10m" max-file: "3"
问题 10:生产环境安全 现象 :敏感信息泄露。
解决方案 :
1 2 3 4 5 6 7 8 9 10 services: database: environment: - MYSQL_ROOT_PASSWORD=/run/secrets/db_root_password secrets: - db_root_password secrets: db_root_password: file: ./secrets/db_root_password.txt
问题 11:.env 文件自动读取机制 现象 :不清楚 .env 文件如何被自动读取和注入。
详解 :
当你运行 docker-compose up 时,Compose 会自动查找当前目录下的 .env 文件 ,并将其中的变量注入到 docker-compose.yml 中。
自动读取规则 :
graph TB
subgraph "项目目录结构 myapp/"
A["docker-compose.yml"]
B[".env ✅ 会被自动读取"]
C[".env.prod ❌ 不会被自动读取"]
subgraph "config/"
D[".env ❌ 不在当前目录,不会自动读取"]
end
E["app/"]
end
style B fill:#90EE90
style C fill:#FFB6C1
style D fill:#FFB6C1
变量优先级(高到低) :
1 2 3 4 1. shell 环境变量 2. --env-file 指定的文件 3. 当前目录的 .env 文件 4. Dockerfile 中的 ENV
示例 :
1 2 3 4 APP_PORT=8080 DB_PASSWORD=MySecretPass123 IMAGE_TAG=v2.0.0
1 2 3 4 5 6 7 8 9 10 services: web: image: myapp:${IMAGE_TAG} ports: - "${APP_PORT}:5000" db: image: postgres:13 environment: POSTGRES_PASSWORD: ${DB_PASSWORD}
注意 :.env 是给 docker-compose.yml 本身 用的,如果想给容器内部 注入环境变量,需要用 env_file:
1 2 3 4 5 6 7 services: web: image: myapp env_file: - ./app.env environment: - EXTRA_VAR=value
问题 12:docker-compose up -d 与 up -d –build 的区别 现象 :不清楚这两个命令的区别。
详解 :
命令
行为
适用场景
up -d
如果镜像已存在,直接启动;如果不存在,则构建
日常开发,代码没改
up -d --build
总是重新构建 镜像,然后再启动
代码改了,需要重新构建
实际演示 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 docker-compose up -d docker-compose up -d --build
三种构建方式的对比 :
1 2 3 4 5 6 7 8 9 docker-compose build web docker-compose up -d docker-compose up -d --build docker-compose up -d
日常工作流 :
1 2 3 4 5 6 7 8 9 10 11 docker-compose up -d --build docker-compose up -d docker-compose up -d --build web docker-compose restart
第三部分:最佳实践 16. Docker 最佳实践 镜像构建最佳实践
使用多阶段构建 :减小镜像体积
使用 .dockerignore :排除不需要的文件
合并 RUN 指令 :减少镜像层数
使用官方基础镜像 :确保安全性
设置非 root 用户 :提高安全性
容器运行最佳实践
使用 –rm 参数 :自动删除停止的容器
设置资源限制 :避免资源耗尽
使用健康检查 :确保服务可用
使用命名卷 :数据持久化
使用日志限制 :避免磁盘占满
安全最佳实践
不使用 root 用户 :容器内使用非 root 用户
扫描镜像漏洞 :使用 docker scan
使用 secrets 管理敏感信息 :不硬编码密码
限制网络访问 :使用自定义网络
定期更新镜像 :获取安全补丁
17. Docker Compose 最佳实践 配置最佳实践
使用版本控制 :将配置文件纳入 Git
使用环境变量 :敏感信息不硬编码
使用多文件配置 :开发、生产环境分离
使用健康检查 :确保服务依赖正确
使用资源限制 :避免资源竞争
开发环境最佳实践
使用绑定卷 :代码修改实时生效
使用 watch 模式 :自动重建容器
使用 override 文件 :覆盖默认配置
使用 .env 文件 :管理环境变量
使用 profiles :按需启动服务
生产环境最佳实践
使用固定版本标签 :避免意外更新
使用资源限制 :确保稳定性
使用日志管理 :集中收集日志
使用 secrets :安全存储密码
使用 healthcheck :确保服务可用
18. 总结 Docker 核心要点
镜像 :应用的只读模板
容器 :镜像的运行实例
网络 :容器间通信
卷 :数据持久化
Dockerfile :构建镜像的脚本
Docker Compose 核心要点
服务 :容器的配置模板
网络 :服务间通信
卷 :数据持久化
依赖管理 :服务启动顺序
健康检查 :服务状态监控
常用命令速查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 docker run -d --name my-nginx -p 80:80 nginx docker ps -a docker logs -f my-nginx docker exec -it my-nginx bash docker stop my-nginx docker rm my-nginx docker compose up -d docker compose down docker compose ps docker compose logs -f docker compose exec web bash docker compose build --no-cache
学习资源
Docker 官方文档
Docker Compose 官方文档
Docker Hub
Docker 官方示例
最后更新:2026年8月24日