Docker 学习文档(Mac + Docker Desktop 版)
适用人群:刚在 Mac 上装好 Docker Desktop、想系统学 Docker 的同学 工具环境:macOS + Docker Desktop(可视化桌面版) 作者视角:以「生产级」标准要求自己------镜像不是能跑就行,体积、安全、构建速度、可维护性一个都不能少。
0. 学习地图(先看这张图,心里有数)
scss
Docker 知识树
├── 理念层:容器 vs 虚拟机、为什么需要 Docker
├── Mac 专属:Docker Desktop 本质 = Mac 上跑了一台 Linux 虚拟机
├── 概念层:镜像(Image) / 容器(Container) / 仓库(Registry) / 卷(Volume) / 网络(Network)
├── 操作层:docker 命令 + Docker Desktop 图形界面
├── 构建层:Dockerfile / 多阶段构建 / .dockerignore
├── 编排层:docker-compose(一次管多个容器)
└── 实战层:从「跑起来」到「安全上线」
学习顺序建议:先理解概念 → 用 Desktop 点一点 → 用命令行敲一遍 → 自己写一个 Dockerfile → 用 compose 组一套服务。
1. Docker 是什么?为什么要用它?
1.1 一句话理解
Docker 把你的「应用 + 它运行所需的一切依赖(系统库、环境变量、配置文件)」打包成一个标准化、可移植的盒子(镜像) 。这个盒子在任何装了 Docker 的机器上都能以完全一致的方式跑起来。
1.2 经典痛点(你一定遇过)
| 场景 | 没有 Docker | 有 Docker |
|---|---|---|
| 「在我机器上能跑」 | 同事环境一跑就崩 | 镜像一样,跑出来就一样 |
| 环境搭建 | 装 MySQL/Redis/Python 半小时起 | docker run 一条命令秒起 |
| 版本冲突 | 项目 A 要 Python 3.8,项目 B 要 3.12,互相打架 | 每个项目各自隔离 |
| 上线交付 | 运维按文档手动配环境,容易漏 | 直接交付镜像,开箱即用 |
1.3 容器 ≠ 虚拟机(重要区别)
| 虚拟机(VM) | 容器(Container) | |
|---|---|---|
| 隔离级别 | 虚拟整台机器(含 Guest OS 内核) | 共享宿主机内核,隔离进程/文件系统 |
| 体积 | 几百 MB ~ GB | 通常几 MB ~ 几百 MB |
| 启动速度 | 秒级~分钟级 | 毫秒级~秒级 |
| 资源占用 | 重 | 轻 |
记忆口诀:VM 是「租整栋楼」,容器是「租一个带独立水电的房间」,楼(内核)大家共用。
2. Mac 上 Docker 的特殊性(新手最容易踩的坑)
2.1 核心事实:Mac 不能直接跑 Linux 容器
macOS 的内核是 Darwin(XNU),而 Docker 容器本质上是 Linux 进程。所以:
Docker Desktop 在你的 Mac 上悄悄启动了一台轻量 Linux 虚拟机(基于 HyperKit / Apple 虚拟机框架),所有容器其实都跑在这台 VM 里。
带来的直接影响:
- 你
docker run的容器,逻辑上在「那台 Linux VM」里,不是直接在你的 macOS 上。 - 容器访问宿主机(Mac)的服务时,不能直接用
localhost,要用特殊地址(见第 9 章)。
2.2 芯片架构:Intel(x86_64) vs Apple Silicon(arm64)
Mac 分两种芯片:
- Intel Mac:x86_64 架构
- Apple Silicon(M1/M2/M3...):arm64 架构
坑点 :很多老镜像只有 x86_64 版本。在 M 系列 Mac 上直接跑可能报错(如 exec format error)或靠 QEMU 模拟变慢。
解决:
- 优先拉取带
arm64标签或multi-arch(多架构)的官方镜像(官方镜像一般已支持)。 - 用
docker run --platform linux/amd64 镜像名强制用 x86 模拟跑(慢,仅应急)。
查看自己架构:
bash
uname -m
# arm64 = Apple Silicon;x86_64 = Intel
2.3 Docker Desktop 的资源设置
打开 Docker Desktop → 右上角齿轮 ⚙️ → Settings → Resources:
- CPUs / Memory / Disk:分配多少资源给那台 Linux VM。默认一般够用,跑重服务(如 Elasticsearch)可加内存。
- Disk image location:VM 磁盘文件位置,别手贱删。
- General 里可勾选「Start Docker Desktop when you log in」(开机自启)。
3. Docker Desktop 界面导览(点点看,建立直觉)
启动 Docker Desktop 后,左侧是一排导航,常用这几个:
| 模块 | 干什么 | 对应命令行 |
|---|---|---|
| Containers | 看正在跑/已停止的容器,点进去能看日志、终端、启停 | docker ps / docker logs |
| Images | 看本地已有的镜像,可删除、运行、查看层级 | docker images |
| Volumes | 看数据卷(容器里持久化数据的地盘) | docker volume ls |
| Builds(新版) | 看构建记录、BuildKit 缓存 | docker build |
| Docker Hub | 浏览/拉取官方镜像 | docker pull |
| Settings ⚙️ | 资源、镜像源、Kubernetes 开关等 | --- |
新手建议:先用命令行把概念敲熟,再用 Desktop 的图形界面「回看」------图形界面是命令的可视化,理解了命令,界面一看就懂。
4. 核心概念(必须刻进脑子里)
4.1 镜像(Image)------「模板 / 类」
只读的模板,定义了容器里有什么。比如 node:20-alpine 就是一个预装了 Node 20 的镜像。
4.2 容器(Container)------「运行中的实例 / 对象」
镜像跑起来后变成容器,是可读可写的运行实例。一个镜像可以同时跑出 N 个容器。
4.3 仓库 / 镜像源(Registry)------「应用商店」
- Docker Hub :默认公共仓库(
docker pull nginx就是从这拉)。 - 国内加速:在 Settings → Docker Engine 里配置镜像加速器(如中科大、阿里云镜像),拉取更快。
4.4 卷(Volume)------「容器的硬盘」
容器删了,里面数据默认全没了。想持久化(数据库文件、上传资料),必须用 Volume 或挂载宿主机目录。
bash
docker run -v 卷名:/容器里的路径 镜像
4.5 网络(Network)------「容器间的局域网」
默认容器间可通过自定义网络互访(用服务名当域名)。compose 会自动建网络。
4.6 Dockerfile------「镜像的配方」
一个文本文件,写清楚「基于什么基础镜像、装什么、拷什么、怎么启动」。这是你亲手造镜像的方式(第 7 章细讲)。
5. 必会命令(建议全部亲手敲一遍)
提示:Docker Desktop 装好后,命令行里
docker命令就可用了(它会连上那台 VM)。
5.1 镜像相关
bash
docker pull nginx:alpine # 拉取镜像(指定 tag,别用 latest!)
docker images # 看本地镜像列表
docker rmi 镜像ID # 删除镜像
docker image prune # 删掉所有没被用的悬空镜像
5.2 容器相关(最常用)
bash
docker run -d -p 8080:80 --name mynginx nginx:alpine
# -d 后台运行
# -p 8080:80 把宿主机 8080 映射到容器 80 端口
# --name 给容器起个名字(不然是一串随机串)
docker ps # 看正在跑的容器
docker ps -a # 看所有容器(含已退出的)
docker logs mynginx # 看日志
docker exec -it mynginx sh # 进入容器里的 shell(-it 交互)
docker stop mynginx # 停
docker start mynginx # 起
docker rm mynginx # 删(先 stop)
docker rm -f mynginx # 强制删(没 stop 也行)
5.3 清理(Mac 磁盘容易被 Docker 吃满)
bash
docker system prune # 删停止的容器 + 悬空镜像 + 网络(安全)
docker system prune -a # 更狠:连没被用的镜像也删(谨慎)
在 Mac 上,Docker Desktop 的 VM 磁盘只增不减 ,
prune删文件后还要去 Settings → Resources → 「Clean / Reset」 才能真正回收磁盘空间。
5.4 一键跑个 Nginx 验证环境
bash
docker run -d -p 8080:80 --name demo nginx:alpine
浏览器打开 http://localhost:8080,看到 Nginx 欢迎页 = 环境 OK ✅
6. 第一个实战:从零做一个自己的镜像
目标:做一个打印「Hello Docker」的 Python 小服务,并容器化。
6.1 项目结构
demo/
├── app.py
└── requirements.txt
app.py:
python
from http.server import BaseHTTPRequestHandler, HTTPServer
class H(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(b"Hello Docker from Mac!")
if __name__ == "__main__":
HTTPServer(("0.0.0.0", 8000)).serve_forever()
requirements.txt:(本例其实没第三方依赖,留空也行,演示用)
6.2 写 Dockerfile(基础版,先跑通)
dockerfile
FROM python:3.12-alpine
WORKDIR /app
COPY app.py .
CMD ["python", "app.py"]
6.3 构建并运行
bash
cd demo
docker build -t hello-docker .
docker run -d -p 8000:8000 --name hello hello-docker
# 打开 http://localhost:8000
跑通后你会发现镜像有几百 MB(因为带了整个 Python)。第 7 章教你压缩到几十 MB。
7. Dockerfile 写法与最佳实践(重点章节)
这是「生产级」和「玩具级」的分水岭。下面每条都是实战踩出来的。
7.1 基础镜像选型(体积/安全的第一步)
| 基础镜像 | 特点 | 适用 |
|---|---|---|
python:3.12 |
完整 Debian,体积大 | 调试、需要系统库 |
python:3.12-slim |
精简 Debian,中等 | 通用生产 |
python:3.12-alpine |
最小(musl libc) | 追求极小,但部分 C 库编译要装 build 依赖 |
gcr.io/distroless/... |
几乎只有运行时,最安全 | 对安全要求极高 |
原则 :版本别写 latest,要 pin 具体版本(如 python:3.12-alpine),否则今天构建和明天构建可能不一样,出事都查不到原因。
7.2 多阶段构建(把构建环境和运行环境分开)
以 Go 为例(Go 编译出静态二进制,最能体现多阶段价值):
dockerfile
# 阶段1:构建
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY . .
RUN go build -o app .
# 阶段2:运行(极小的纯净镜像)
FROM alpine:3.20
WORKDIR /app
COPY --from=builder /src/app .
USER 1000 # 不用 root 跑
CMD ["./app"]
好处:构建工具(编译器、源码)全部留在第一阶段,最终镜像只有二进制 + 极简系统,体积能小一个数量级,攻击面也大幅减少。
7.3 非 root 用户运行(安全底线)
dockerfile
RUN addgroup -S app && adduser -S app -G app
USER app
容器里默认是 root,一旦被攻破,攻击者就在容器里拥有最高权限。切到普通用户是基本素养。
7.4 合理利用层缓存(让构建飞快)
Docker 按行构建,改得越少、越靠后的指令,越要放后面:
dockerfile
# ✅ 好:先拷依赖清单装依赖(这层很少变),再拷代码(常变)
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
# ❌ 差:COPY . . 在最前,每次改一行代码都要重装所有依赖
原理:依赖不变 → 这一层命中缓存 → 跳过 npm install,几秒过;只重跑代码拷贝和启动。
7.5 .dockerignore(别把垃圾塞进镜像)
项目根目录建 .dockerignore,作用类似 .gitignore:
bash
node_modules
.git
.env
Dockerfile
*.log
dist(如果构建在宿主机做)
不忽略的话,COPY . . 会把 node_modules、.git 全打进镜像,体积爆炸、还有泄密风险。
7.6 HEALTHCHECK(让编排系统知道你「真活着」)
dockerfile
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:8000/ || exit 1
加了之后,docker ps 里状态会从 starting 变成 healthy,K8s/compose 也靠它判断要不要重启。
7.7 不把密钥写进镜像
dockerfile
# ❌ 绝对不要
ENV DB_PASSWORD=supersecret
# ✅ 运行时通过 -e 或 secrets 注入
docker run -e DB_PASSWORD=$DB_PASSWORD ...
镜像会被推到仓库、被别人 docker history 看到,硬编码密钥等于公开。
7.8 把第 6 章的镜像压到极小(Alpine 版)
dockerfile
FROM python:3.12-alpine
WORKDIR /app
COPY app.py .
# 用 python:3.12-alpine 已够小;若用多阶段可进一步压
USER 1000
EXPOSE 8000
CMD ["python", "app.py"]
相比完整 python:3.12(~1GB),alpine 版约 ~50MB,差距 20 倍。
8. docker-compose 入门(一次管多个容器)
单条 docker run 管一两个容器还行,服务一多就乱了。compose 用一个 docker-compose.yml 描述整套服务。
8.1 例子:Web + Redis
yaml
version: "3.9"
services:
web:
build: . # 用当前目录 Dockerfile 构建
ports:
- "8000:8000"
depends_on:
- redis
redis:
image: redis:7-alpine
volumes:
- redis-data:/data # 数据持久化
volumes:
redis-data:
8.2 常用命令
bash
docker compose up -d # 后台启动整套
docker compose ps # 看状态
docker compose logs web # 看某个服务日志
docker compose down # 停并删容器(数据卷保留)
docker compose down -v # 连数据卷一起删(慎用)
Docker Desktop 里也能直接看到 compose 项目,点点就能启停,和命令行一一对应。
9. Mac 常见坑 & 排查清单
| 现象 | 原因 | 解决 |
|---|---|---|
exec format error |
镜像架构不匹配(M 芯片跑 x86 镜像) | 换 multi-arch 镜像,或 --platform linux/amd64 |
| 容器里访问宿主机服务连不上 | 容器在 Linux VM 里,localhost 是 VM 自己 |
用 host.docker.internal 代替 localhost |
| 文件改了容器内没变 | 没挂载卷 | 加 -v $(pwd):/app 挂载 |
| Mac 磁盘被吃满 | VM 磁盘不自动收缩 | prune 后到 Settings → Resources 清理 |
| 端口被占用 | 宿主机端口已被占 | 换 -p 8081:80 映射端口 |
| 拉镜像慢 | 默认连 Docker Hub 跨国 | 配国内镜像加速器 |
访问宿主机(Mac)的经典写法:
bash
# 容器里的应用要连 Mac 上跑的 MySQL(端口 3306)
docker run myapp # 代码里填 host.docker.internal:3306
10. 学习路径 & 进阶建议
10.1 30 天上手路线
- 第 1 周 :概念 + Desktop 界面 + 必会命令(第 1~5 章),把
nginx/redis/mysql跑熟。 - 第 2 周:自己写 Dockerfile,练多阶段、.dockerignore、非 root(第 6~7 章)。
- 第 3 周:docker-compose 组一套「Web + DB + 缓存」(第 8 章)。
- 第 4 周 :镜像优化(体积/安全扫描)、
.dockerignore/secret 管理进阶。
10.2 推荐进阶主题
- 镜像安全扫描 :
docker scout cves 镜像名(Desktop 内置),或 Trivy。 - BuildKit 新特性 :
--mount=type=cache缓存依赖、--mount=type=secret安全传密钥。 - Kubernetes:Docker Desktop 里 Settings 勾选 Kubernetes 即可本地单节点练手。
- CI 集成 :把
docker build放进 GitHub Actions / GitLab CI,做到提交即构建。
10.3 给自己的三条铁律(记住就超过大多数人)
- 基础镜像版本要 pin 死,绝不写
latest。 - 最终运行镜像能小则小、能非 root 则非 root。
- 密钥永远不进镜像,运行时注入。
附:速查卡片(贴墙上)
bash
# 拉起一个服务
docker run -d -p 宿主机端口:容器端口 --name 名字 镜像:tag
# 进容器
docker exec -it 名字 sh
# 构建
docker build -t 名字:版本 .
# 清理
docker system prune
# 编排
docker compose up -d
docker compose down
文档完。建议边看边敲,遇到报错先对照第 9 章排查清单。Docker 是「手感型」技能,敲得越多越熟。