Docker 从入门到上手:介绍、下载与基础使用(全平台超详细教程)
一句话总结:Docker 是一个开源的容器化平台,它把「应用 + 运行环境 + 依赖库 + 配置」打包成一个标准化的镜像(Image),在任何装了 Docker 的机器上都能以秒级启动跑出完全一致的容器(Container)——彻底告别「在我电脑上明明能跑」的经典事故。
📋 目录
- 一、Docker 是什么
- 1.1 官方定义与发展历程
- 1.2 为什么需要 Docker
- 1.3 容器 VS 虚拟机
- 1.4 三大核心概念
- 1.5 Docker 架构原理
- 二、下载与安装
- 2.1 版本选择
- 2.2 Windows 安装
- 2.3 macOS 安装
- 2.4 Linux 安装
- 2.5 验证安装
- 2.6 配置国内镜像加速器
- 三、镜像(Image)基础操作
- 四、容器(Container)基础操作
- 4.1 docker run 参数详解
- 4.2 容器生命周期
- 4.3 容器管理命令
- 五、数据持久化(Volume)
- 六、Dockerfile:构建自己的镜像
- 6.1 常用指令
- 6.2 实战案例
- 6.3 CMD 与 ENTRYPOINT 的区别
- 6.4 镜像瘦身技巧
- 七、网络模式
- 八、Docker Compose:多容器编排
- 九、实战:常见服务一键部署
- 十、常用命令速查表
- 十一、常见问题 FAQ
- 十二、总结
一、Docker 是什么
1.1 官方定义与发展历程
Docker 是一个开源的应用容器引擎,基于 Go 语言开发,遵循 Apache 2.0 协议。它让开发者可以把应用及其全部依赖打包到一个可移植的容器中,然后发布到任何流行的 Linux / Windows / macOS 机器上运行。
官网: https://www.docker.com/
文档: https://docs.docker.com/
镜像仓库: https://hub.docker.com/ (Docker Hub,官方公共仓库)
GitHub: https://github.com/moby/moby (Docker 引擎开源实现)
| 2013 | dotCloud 公司(后改名 Docker Inc.)开源 Docker,基于 LXC |
| 2014 | Docker 1.0 发布;引入 libcontainer 自研容器运行时 |
| 2015 | OCI(Open Container Initiative) 开放容器标准成立 |
| 2017 | Docker 拆分为 CE(社区版) 与 EE(企业版);多阶段构建、Compose 普及 |
| 2019 | Docker 把容器运行时下沉为 containerd + runc,可被 Kubernetes 直接调用 |
| 2021 | Docker Desktop 对大型企业(>250 人或年收入 >$10M)开始收费;Compose V2 用 Go 重写 |
| 至今 | 成为云原生(Cloud Native)事实标准,Kubernetes、Service Mesh 全部构建在容器之上 |
💡 一个形象的比喻: 集装箱(Container)出现之前,货物装卸靠人力,码头效率极低、货物损耗大。集装箱统一了尺寸规格后,同一批货可以用轮船、火车、卡车无缝转运。 Docker 就是软件界的集装箱:不管里面装的是 Java、Python、MySQL 还是 Nginx,对外都是统一的标准接口,一次打包,处处运行。
1.2 为什么需要 Docker
传统开发运维中最令人崩溃的几个场景:
| 环境不一致 | 「我本地是好的」→ 排查 Python/Node/JDK 版本、环境变量、动态库差异,耗时数小时 | 镜像自带完整环境,开发/测试/生产完全一致 |
| 部署慢 | 手动装系统依赖、拉代码、编译、改配置,一台机器半小时起 | docker run 一条命令,秒级启动 |
| 资源浪费 | 一台服务器只敢跑一个应用,怕互相污染 | 容器隔离,单机跑几十个应用互不干扰 |
| 迁移困难 | 换云厂商 / 换操作系统 = 环境重装一遍 | 镜像直接搬运,docker save / pull 即可 |
| 扩容弹性差 | 加机器 → 装环境 → 部署,小时级 | 镜像已在仓库,扩容只需拉镜像启动,秒级 |
| 依赖冲突 | 项目 A 要 JDK 8,项目 B 要 JDK 17,装在同一台机器上打架 | 各跑各的容器,互不影响 |
1.3 容器 VS 虚拟机
这是面试与实战中最常被问到的对比。核心区别在于隔离级别和是否携带 Guest OS:
#mermaid-svg-hRRQ4Wn541WJhZYt{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hRRQ4Wn541WJhZYt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hRRQ4Wn541WJhZYt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hRRQ4Wn541WJhZYt .error-icon{fill:#552222;}#mermaid-svg-hRRQ4Wn541WJhZYt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hRRQ4Wn541WJhZYt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hRRQ4Wn541WJhZYt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hRRQ4Wn541WJhZYt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hRRQ4Wn541WJhZYt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hRRQ4Wn541WJhZYt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hRRQ4Wn541WJhZYt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hRRQ4Wn541WJhZYt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hRRQ4Wn541WJhZYt .marker.cross{stroke:#333333;}#mermaid-svg-hRRQ4Wn541WJhZYt svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hRRQ4Wn541WJhZYt p{margin:0;}#mermaid-svg-hRRQ4Wn541WJhZYt .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-hRRQ4Wn541WJhZYt .cluster-label text{fill:#333;}#mermaid-svg-hRRQ4Wn541WJhZYt .cluster-label span{color:#333;}#mermaid-svg-hRRQ4Wn541WJhZYt .cluster-label span p{background-color:transparent;}#mermaid-svg-hRRQ4Wn541WJhZYt .label text,#mermaid-svg-hRRQ4Wn541WJhZYt span{fill:#333;color:#333;}#mermaid-svg-hRRQ4Wn541WJhZYt .node rect,#mermaid-svg-hRRQ4Wn541WJhZYt .node circle,#mermaid-svg-hRRQ4Wn541WJhZYt .node ellipse,#mermaid-svg-hRRQ4Wn541WJhZYt .node polygon,#mermaid-svg-hRRQ4Wn541WJhZYt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hRRQ4Wn541WJhZYt .rough-node .label text,#mermaid-svg-hRRQ4Wn541WJhZYt .node .label text,#mermaid-svg-hRRQ4Wn541WJhZYt .image-shape .label,#mermaid-svg-hRRQ4Wn541WJhZYt .icon-shape .label{text-anchor:middle;}#mermaid-svg-hRRQ4Wn541WJhZYt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hRRQ4Wn541WJhZYt .rough-node .label,#mermaid-svg-hRRQ4Wn541WJhZYt .node .label,#mermaid-svg-hRRQ4Wn541WJhZYt .image-shape .label,#mermaid-svg-hRRQ4Wn541WJhZYt .icon-shape .label{text-align:center;}#mermaid-svg-hRRQ4Wn541WJhZYt .node.clickable{cursor:pointer;}#mermaid-svg-hRRQ4Wn541WJhZYt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hRRQ4Wn541WJhZYt .arrowheadPath{fill:#333333;}#mermaid-svg-hRRQ4Wn541WJhZYt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hRRQ4Wn541WJhZYt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hRRQ4Wn541WJhZYt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hRRQ4Wn541WJhZYt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hRRQ4Wn541WJhZYt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hRRQ4Wn541WJhZYt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hRRQ4Wn541WJhZYt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hRRQ4Wn541WJhZYt .cluster text{fill:#333;}#mermaid-svg-hRRQ4Wn541WJhZYt .cluster span{color:#333;}#mermaid-svg-hRRQ4Wn541WJhZYt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hRRQ4Wn541WJhZYt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hRRQ4Wn541WJhZYt rect.text{fill:none;stroke-width:0;}#mermaid-svg-hRRQ4Wn541WJhZYt .icon-shape,#mermaid-svg-hRRQ4Wn541WJhZYt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hRRQ4Wn541WJhZYt .icon-shape p,#mermaid-svg-hRRQ4Wn541WJhZYt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hRRQ4Wn541WJhZYt .icon-shape .label rect,#mermaid-svg-hRRQ4Wn541WJhZYt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hRRQ4Wn541WJhZYt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hRRQ4Wn541WJhZYt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hRRQ4Wn541WJhZYt :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
容器架构(轻)
App A
Bins/Libs
App B
Bins/Libs
Docker Engine 容器引擎
Host OS 共用宿主内核
物理硬件
虚拟机架构(重)
App A
Bins/Libs
Guest OS
App B
Bins/Libs
Guest OS
Hypervisor 虚拟化层
Host OS
物理硬件
| 隔离级别 | 硬件级(Hypervisor 虚拟出完整硬件) | 进程级(Namespace + Cgroups) |
| 是否含操作系统 | ✅ 每个 VM 都有完整 Guest OS | ❌ 共用宿主机内核,只带应用与依赖 |
| 镜像体积 | GB 级(几 GB ~ 几十 GB) | MB 级(几 MB ~ 几百 MB) |
| 启动速度 | 分钟级(要引导 OS) | 秒级 / 毫秒级 |
| 资源占用 | 高(每个 VM 独占内存) | 低(共享内核,按需占用) |
| 单机数量 | 几个 ~ 十几个 | 几十 ~ 上千个 |
| 性能损耗 | 较大(虚拟化开销) | 接近原生(≈5%) |
| 安全性 | 强(硬件级隔离) | 相对弱(共享内核,需配合安全策略) |
| 典型产品 | VMware、VirtualBox、KVM、Hyper-V | Docker、containerd、Podman、LXC |
🎯 结论:两者不是替代关系,而是互补关系。
- 需要跑不同操作系统、需要强安全隔离 → 用虚拟机
- 应用交付、微服务部署、CI/CD、快速扩缩容 → 用容器
- 生产环境常见组合:在虚拟机 / 云主机里跑 Docker 容器
1.4 三大核心概念:镜像 / 容器 / 仓库
#mermaid-svg-i44VRNIX9xcFkUwu{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-i44VRNIX9xcFkUwu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-i44VRNIX9xcFkUwu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-i44VRNIX9xcFkUwu .error-icon{fill:#552222;}#mermaid-svg-i44VRNIX9xcFkUwu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-i44VRNIX9xcFkUwu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-i44VRNIX9xcFkUwu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-i44VRNIX9xcFkUwu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-i44VRNIX9xcFkUwu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-i44VRNIX9xcFkUwu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-i44VRNIX9xcFkUwu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-i44VRNIX9xcFkUwu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-i44VRNIX9xcFkUwu .marker.cross{stroke:#333333;}#mermaid-svg-i44VRNIX9xcFkUwu svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-i44VRNIX9xcFkUwu p{margin:0;}#mermaid-svg-i44VRNIX9xcFkUwu .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-i44VRNIX9xcFkUwu .cluster-label text{fill:#333;}#mermaid-svg-i44VRNIX9xcFkUwu .cluster-label span{color:#333;}#mermaid-svg-i44VRNIX9xcFkUwu .cluster-label span p{background-color:transparent;}#mermaid-svg-i44VRNIX9xcFkUwu .label text,#mermaid-svg-i44VRNIX9xcFkUwu span{fill:#333;color:#333;}#mermaid-svg-i44VRNIX9xcFkUwu .node rect,#mermaid-svg-i44VRNIX9xcFkUwu .node circle,#mermaid-svg-i44VRNIX9xcFkUwu .node ellipse,#mermaid-svg-i44VRNIX9xcFkUwu .node polygon,#mermaid-svg-i44VRNIX9xcFkUwu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-i44VRNIX9xcFkUwu .rough-node .label text,#mermaid-svg-i44VRNIX9xcFkUwu .node .label text,#mermaid-svg-i44VRNIX9xcFkUwu .image-shape .label,#mermaid-svg-i44VRNIX9xcFkUwu .icon-shape .label{text-anchor:middle;}#mermaid-svg-i44VRNIX9xcFkUwu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-i44VRNIX9xcFkUwu .rough-node .label,#mermaid-svg-i44VRNIX9xcFkUwu .node .label,#mermaid-svg-i44VRNIX9xcFkUwu .image-shape .label,#mermaid-svg-i44VRNIX9xcFkUwu .icon-shape .label{text-align:center;}#mermaid-svg-i44VRNIX9xcFkUwu .node.clickable{cursor:pointer;}#mermaid-svg-i44VRNIX9xcFkUwu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-i44VRNIX9xcFkUwu .arrowheadPath{fill:#333333;}#mermaid-svg-i44VRNIX9xcFkUwu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-i44VRNIX9xcFkUwu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-i44VRNIX9xcFkUwu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-i44VRNIX9xcFkUwu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-i44VRNIX9xcFkUwu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-i44VRNIX9xcFkUwu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-i44VRNIX9xcFkUwu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-i44VRNIX9xcFkUwu .cluster text{fill:#333;}#mermaid-svg-i44VRNIX9xcFkUwu .cluster span{color:#333;}#mermaid-svg-i44VRNIX9xcFkUwu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-i44VRNIX9xcFkUwu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-i44VRNIX9xcFkUwu rect.text{fill:none;stroke-width:0;}#mermaid-svg-i44VRNIX9xcFkUwu .icon-shape,#mermaid-svg-i44VRNIX9xcFkUwu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-i44VRNIX9xcFkUwu .icon-shape p,#mermaid-svg-i44VRNIX9xcFkUwu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-i44VRNIX9xcFkUwu .icon-shape .label rect,#mermaid-svg-i44VRNIX9xcFkUwu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-i44VRNIX9xcFkUwu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-i44VRNIX9xcFkUwu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-i44VRNIX9xcFkUwu :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
docker pull
docker run
docker run
docker run
docker commit / build
Registry 仓库Docker Hub / 私有仓库
Image 镜像只读模板 = 应用 + 依赖 + 配置
Container 容器 1可读可写运行实例
Container 容器 2
Container 容器 N
| 镜像 | Image | 面向对象中的类(Class) / 安装光盘 | 只读的分层模板,包含运行应用所需的一切。同一镜像可启动无数个容器 |
| 容器 | Container | 类的实例(Object) / 运行中的程序 | 镜像的运行实例,在镜像顶层加了一个可写层。容器删除后可写层数据消失 |
| 仓库 | Registry | 代码托管平台(GitHub / Gitee) | 集中存放镜像的地方。Docker Hub 是官方公共仓库,企业可搭 Harbor 私有仓库 |
镜像的分层存储(UnionFS 联合文件系统)——理解 Docker 高效的关键:
FROM ubuntu:22.04 # 第 1 层:基础系统层
RUN apt-get install -y nginx # 第 2 层:软件安装层
COPY ./html /usr/share/nginx/html # 第 3 层:配置/代码层
EXPOSE 80 # 第 4 层:元数据层
CMD ["nginx", "-g", "daemon off;"] # 容器启动命令
- 每条指令生成一个只读层,层与层之间可被多个镜像共享(本机只存一份)
- docker run 时在只读层之上叠加一个容器可写层
- 容器内修改文件采用 写时复制(Copy-on-Write):先把文件从只读层复制到可写层,再修改
- 👉 所以镜像构建有缓存:修改一条指令只会重建该层及其之后的层,前面的层直接复用缓存
1.5 Docker 架构原理
Docker 采用 C/S(客户端-服务端)架构,客户端与守护进程可以在同一台机器,也可以远程通信:
#mermaid-svg-a4xYIQJhamHMWCPZ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-a4xYIQJhamHMWCPZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-a4xYIQJhamHMWCPZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-a4xYIQJhamHMWCPZ .error-icon{fill:#552222;}#mermaid-svg-a4xYIQJhamHMWCPZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-a4xYIQJhamHMWCPZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-a4xYIQJhamHMWCPZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-a4xYIQJhamHMWCPZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-a4xYIQJhamHMWCPZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-a4xYIQJhamHMWCPZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-a4xYIQJhamHMWCPZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-a4xYIQJhamHMWCPZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-a4xYIQJhamHMWCPZ .marker.cross{stroke:#333333;}#mermaid-svg-a4xYIQJhamHMWCPZ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-a4xYIQJhamHMWCPZ p{margin:0;}#mermaid-svg-a4xYIQJhamHMWCPZ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-a4xYIQJhamHMWCPZ .cluster-label text{fill:#333;}#mermaid-svg-a4xYIQJhamHMWCPZ .cluster-label span{color:#333;}#mermaid-svg-a4xYIQJhamHMWCPZ .cluster-label span p{background-color:transparent;}#mermaid-svg-a4xYIQJhamHMWCPZ .label text,#mermaid-svg-a4xYIQJhamHMWCPZ span{fill:#333;color:#333;}#mermaid-svg-a4xYIQJhamHMWCPZ .node rect,#mermaid-svg-a4xYIQJhamHMWCPZ .node circle,#mermaid-svg-a4xYIQJhamHMWCPZ .node ellipse,#mermaid-svg-a4xYIQJhamHMWCPZ .node polygon,#mermaid-svg-a4xYIQJhamHMWCPZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-a4xYIQJhamHMWCPZ .rough-node .label text,#mermaid-svg-a4xYIQJhamHMWCPZ .node .label text,#mermaid-svg-a4xYIQJhamHMWCPZ .image-shape .label,#mermaid-svg-a4xYIQJhamHMWCPZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-a4xYIQJhamHMWCPZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-a4xYIQJhamHMWCPZ .rough-node .label,#mermaid-svg-a4xYIQJhamHMWCPZ .node .label,#mermaid-svg-a4xYIQJhamHMWCPZ .image-shape .label,#mermaid-svg-a4xYIQJhamHMWCPZ .icon-shape .label{text-align:center;}#mermaid-svg-a4xYIQJhamHMWCPZ .node.clickable{cursor:pointer;}#mermaid-svg-a4xYIQJhamHMWCPZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-a4xYIQJhamHMWCPZ .arrowheadPath{fill:#333333;}#mermaid-svg-a4xYIQJhamHMWCPZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-a4xYIQJhamHMWCPZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-a4xYIQJhamHMWCPZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a4xYIQJhamHMWCPZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-a4xYIQJhamHMWCPZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a4xYIQJhamHMWCPZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-a4xYIQJhamHMWCPZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-a4xYIQJhamHMWCPZ .cluster text{fill:#333;}#mermaid-svg-a4xYIQJhamHMWCPZ .cluster span{color:#333;}#mermaid-svg-a4xYIQJhamHMWCPZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-a4xYIQJhamHMWCPZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-a4xYIQJhamHMWCPZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-a4xYIQJhamHMWCPZ .icon-shape,#mermaid-svg-a4xYIQJhamHMWCPZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a4xYIQJhamHMWCPZ .icon-shape p,#mermaid-svg-a4xYIQJhamHMWCPZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-a4xYIQJhamHMWCPZ .icon-shape .label rect,#mermaid-svg-a4xYIQJhamHMWCPZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a4xYIQJhamHMWCPZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-a4xYIQJhamHMWCPZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-a4xYIQJhamHMWCPZ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Docker 宿主机(Docker Host)
Docker 客户端(CLI / GUI)
REST API/var/run/docker.sock
docker pull / push
docker build / pull / run …
dockerdDocker 守护进程
containerd容器生命周期管理
containerd-shim
runcOCI 运行时,创建完即退出
容器 1
容器 2
Images 镜像存储/var/lib/docker
RegistryDocker Hub / Harbor
| Docker Client | 用户交互入口,docker 命令、Docker Desktop GUI、API 调用 |
| dockerd | 守护进程,接收 REST API 请求,管理镜像、网络、卷 |
| containerd | 高级容器运行时,负责容器生命周期、镜像拉取与存储 |
| runc | 低级容器运行时(OCI 标准实现),真正调用内核创建容器,创建完就退出 |
| Namespace | 内核隔离机制:PID / NET / IPC / MNT / UTS / USER,让容器「看不见」别人 |
| Cgroups | 内核资源限制机制:限制容器能用的 CPU、内存、IO 上限 |
| UnionFS | 联合文件系统(overlay2),实现镜像分层与写时复制 |
💡 一句话记忆:Namespace 管隔离(看不见),Cgroups 管限制(用不多),UnionFS 管分层(存得省)。
二、下载与安装
2.1 版本选择(先搞清楚该装哪个)
| Docker Desktop | Docker 桌面版 | 带 GUI 图形界面,内含 Docker Engine + CLI + Compose + Kubernetes。Win/Mac 必装它 | 个人开发者、初学者 |
| Docker Engine | Docker 引擎 | 纯命令行,Linux 原生安装 | Linux 服务器、生产环境 |
| Docker CE | Community Edition 社区版 | 免费开源,功能完整 | 绝大多数场景 ✅ |
| Docker EE / Mirantis | Enterprise Edition 企业版 | 收费,带安全加固、镜像扫描、RBAC、商业支持 | 大型企业 |
⚠️ Docker Desktop 商业授权提醒(重要): 自 2021 年 8 月起,Docker Desktop 对员工数 > 250 人 或 年收入 > 1000 万美元的企业需付费订阅(Pro / Team / Business)。 个人用户、学生、教育科研、非商业开源项目、小型企业仍然完全免费。 Linux 上的 Docker Engine 永久免费开源,不受此限制;也可用完全免费的替代品 Podman、Rancher Desktop、OrbStack(macOS)。
2.2 Windows 安装(Docker Desktop + WSL2)
Windows 上必须依赖虚拟化技术,官方推荐使用 WSL 2(Windows Subsystem for Linux 2) 作为后端。
前置条件检查
| 操作系统 | Windows 10 64 位(版本 2004 / Build 19041 及以上)或 Windows 11 |
| 内存 | 至少 4 GB |
| CPU 虚拟化 | BIOS 中已开启 VT-x(Intel)/ SVM(AMD) |
| WSL 2 | 推荐安装并设为默认版本 |
第一步:检查虚拟化是否已开启
按 Ctrl + Shift + Esc 打开任务管理器 → 性能 选项卡 → CPU → 右下角查看「虚拟化:已启用」。
或用命令查询:
systeminfo
在输出末尾找到:
Hyper-V 要求: 已检测到虚拟机监控程序。将不显示 Hyper-V 所需的功能。
或:
Hyper-V 要求: 固件中已启用虚拟化: 是
⚠️ 如果显示「固件中已启用虚拟化:否」:需要重启进 BIOS/UEFI 手动开启。
- Intel 平台:Advanced → CPU Configuration → Intel Virtualization Technology (VT-x) → Enabled
- AMD 平台:Advanced → CPU Configuration → SVM Mode → Enabled
- 品牌机快捷键通常是 Del / F2 / F10 / F12,开机时狂按即可进入。
第二步:安装 WSL 2(PowerShell 管理员身份运行)
# 一键安装 WSL(Windows 10 2004+ / Windows 11 推荐方式,会自动开启所需功能并重启)
wsl —install
# 若上面的命令不可用(旧版本 Windows),手动开启两个功能:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
# 重启电脑后,设置 WSL 默认版本为 2
wsl —set-default–version 2
# 安装一个 Ubuntu 发行版(可选,Docker 自己也会装)
wsl —install –d Ubuntu
# 查看 WSL 状态
wsl —list —verbose
期望输出:
NAME STATE VERSION
* Ubuntu Running 2
docker-desktop Running 2
💡 VERSION 必须是 2。如果显示 1,用 wsl –set-version Ubuntu 2 升级。
第三步:下载并安装 Docker Desktop
- 同意服务协议(Accept)
- 可以跳过登录(Skip / Continue without signing in)
- 完成引导(Finish),等待左下角鲸鱼图标变成稳定的绿色 Engine running
第四步:验证
在 PowerShell 或 CMD 中执行:
docker —version
docker compose version
docker run hello-world
⚠️ 常见坑:安装后提示 WSL 2 installation is incomplete 解决:下载安装 WSL2 Linux 内核更新包 → 重启 → wsl –update 下载地址:https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi
2.3 macOS 安装
方式一:Docker Desktop(推荐,带 GUI)
前往 https://www.docker.com/products/docker-desktop/ 下载,注意区分芯片架构:
| Docker Desktop for Mac with Apple Chip | M1 / M2 / M3 / M4 芯片 | Docker-arm64.dmg |
| Docker Desktop for Mac with Intel Chip | Intel 芯片的 Mac | Docker-x86_64.dmg |
💡 查看自己的芯片:点击左上角 → 关于本机,看「芯片」一栏显示 Apple M* 还是 Intel。
安装步骤:
🍎 Apple Silicon 用户注意:部分老旧镜像只有 amd64 架构,拉取时会警告 image with reference … was found but does not match the specified platform。 解决:显式指定架构 docker pull –platform linux/amd64 镜像名(会走 Rosetta/QEMU 模拟,性能略降)。 也可用更轻快的第三方替代品 OrbStack(个人免费,启动快、省电)。
方式二:Homebrew 命令行安装
brew install –cask docker # 安装 Docker Desktop
open /Applications/Docker.app # 首次需手动启动完成初始化
2.4 Linux 安装
Linux 上直接装 Docker Engine(CE),无需 Desktop,纯命令行、免费、性能最好。
方式一:官方一键脚本(最简单,适合个人测试)
# 下载并执行官方安装脚本
curl -fsSL https://get.docker.com | bash -s docker
# 国内网络慢,可指定阿里云镜像源
curl -fsSL https://get.docker.com | bash -s docker –mirror Aliyun
方式二:Ubuntu / Debian 手动安装(生产推荐)
# 1. 卸载可能冲突的旧版本
sudo apt-get remove -y docker docker-engine docker.io containerd runc
# 2. 安装依赖工具
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# 3. 添加 Docker 官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg –dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# 4. 添加软件源(国内可把 download.docker.com 换成 mirrors.aliyun.com/docker-ce)
echo \\
"deb [arch=$(dpkg –print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \\
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \\
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 5. 安装 Docker Engine + CLI + containerd + Buildx + Compose 插件
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 6. 启动并设置开机自启
sudo systemctl enable –now docker
方式三:CentOS / RHEL / Rocky Linux 安装
# 1. 卸载旧版本
sudo yum remove -y docker docker-client docker-client-latest docker-common \\
docker-latest docker-engine
# 2. 安装 yum-utils 并添加官方源
sudo yum install -y yum-utils
sudo yum-config-manager –add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 3. 安装 Docker
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 4. 启动并开机自启
sudo systemctl enable –now docker
免 sudo 使用 docker(强烈推荐)
安装完成后,普通用户执行 docker 会报权限错误:
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
解决办法——把当前用户加入 docker 用户组:
sudo groupadd docker # 若组已存在会提示 already exists,忽略即可
sudo usermod -aG docker $USER
newgrp docker # 立即生效(或直接注销重新登录)
docker run hello-world # 验证不再需要 sudo
⚠️ 安全提示:docker 组等同于 root 权限(可挂载宿主机根目录),生产服务器请谨慎授权。
2.5 验证安装是否成功
① 查看客户端与服务端版本
docker version
Client: Docker Engine – Community
Version: 27.3.1
API version: 1.56
Go version: go1.22.7
OS/Arch: linux/amd64
Context: default
Server: Docker Engine – Community
Engine:
Version: 27.3.1
API version: 1.56 (minimum version 1.24)
Go version: go1.22.7
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.7.22
runc:
Version: 1.1.14
💡 Client 和 Server 都要显示才算正常。只有 Client 说明守护进程没启动(Linux 执行 sudo systemctl start docker)。
② 查看引擎详细信息
docker info
重点关注 Storage Driver(应为 overlay2)、Cgroup Driver、Registry Mirrors、Docker Root Dir。
③ 跑第一个容器(经典 Hello World)
docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:d000bc569937abbe195e20322a0bde6b2922d805332fd6d8a68b19f524b7d21d
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
看到这串输出,恭喜——你的 Docker 环境已经完全跑通了 ✅
2.6 配置国内镜像加速器(必做)
国内直连 Docker Hub 经常超时:
Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
① 获取专属加速地址(最稳定)
登录阿里云容器镜像服务控制台 → 镜像工具 → 镜像加速器,获得形如 https://xxxxxxxx.mirror.aliyuncs.com 的个人专属地址。 地址:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
② Linux 配置
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://xxxxxxxx.mirror.aliyuncs.com",
"https://docker.m.daocloud.io",
"https://dockerproxy.net"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
③ Windows / macOS(Docker Desktop)配置
Docker Desktop → 右上角 ⚙️ Settings → 左侧 Docker Engine → 在右侧 JSON 中加入 registry-mirrors → 点击 Apply & restart。
④ 验证是否生效
docker info | grep -A 5 "Registry Mirrors"
Registry Mirrors:
https://xxxxxxxx.mirror.aliyuncs.com/
https://docker.m.daocloud.io/
Live Restore Enabled: false
⚠️ 注意:公共加速站点会不定期失效或限流(2024 年后大量公共镜像站已关闭),优先使用阿里云/腾讯云的个人专属地址,或自建 Harbor / Nexus 私有仓库代理。企业内网还可配置 proxies 走 HTTP 代理。
其他实用 daemon.json 配置项
| registry-mirrors | 镜像加速器地址列表 |
| insecure-registries | 允许访问自签名的私有仓库,如 ["192.168.1.10:5000"] |
| data-root | 修改镜像/容器存储目录(默认 /var/lib/docker,C 盘爆满时改到 D 盘) |
| log-opts.max-size | 单个容器日志上限,防止日志撑爆磁盘 |
| default-address-pools | 自定义容器网段,避免和公司内网冲突 |
三、镜像(Image)基础操作
| docker search | 在 Docker Hub 搜索镜像 | docker search nginx |
| docker pull | 从仓库下载镜像 | docker pull nginx:1.27 |
| docker images / image ls | 列出本地所有镜像 | docker images -a |
| docker rmi / image rm | 删除镜像 | docker rmi nginx:1.27 |
| docker tag | 给镜像打新标签 | docker tag nginx my-nginx:v1 |
| docker inspect | 查看镜像详细元数据(JSON) | docker inspect nginx |
| docker history | 查看镜像各层构建历史 | docker history nginx |
| docker build | 用 Dockerfile 构建镜像 | docker build -t myapp:1.0 . |
| docker save | 导出镜像为 tar 包(离线传输) | docker save -o nginx.tar nginx:1.27 |
| docker load | 从 tar 包导入镜像 | docker load -i nginx.tar |
| docker export / import | 导出/导入容器文件系统(丢弃分层) | docker export c1 > c1.tar |
| docker commit | 把容器当前状态保存为新镜像 | docker commit c1 myimg:v1 |
| docker system prune | 一键清理无用镜像/容器/网络/缓存 | docker system prune -a |
| docker login / logout | 登录/登出镜像仓库 | docker login |
| docker push | 上传镜像到仓库 | docker push 用户名/myimg:v1 |
3.1 镜像命名规则
[仓库地址[:端口]/][命名空间/]镜像名[:标签]
示例:
nginx → docker.io/library/nginx:latest
registry.cn-hangzhou.aliyuncs.com/ns/app:v1 → 阿里云杭州仓库 / ns 命名空间 / app 镜像 / v1 标签
192.168.1.10:5000/myapp:dev → 私有仓库
⚠️ 强烈建议永远显式指定 tag,不要用默认的 latest。 latest 并不代表「最新版本」,它只是一个普通标签名,容易导致生产环境拉到不可预期的版本,无法回滚。
3.2 实操演示
# 搜索官方 nginx 镜像(OFFICIAL 为 [OK] 表示官方镜像)
docker search nginx –limit 5
NAME DESCRIPTION STARS OFFICIAL
nginx Official build of Nginx. 21899 [OK]
linuxserver/nginx An Nginx container, brought to you by LinuxS… 271
bitnami/nginx Bitnami nginx Docker Image 203
# 拉取指定版本镜像
docker pull nginx:1.27.3
docker pull mysql:8.0
docker pull redis:7-alpine # alpine 版本体积只有几 MB,强烈推荐
# 查看本地镜像
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx 1.27.3 d90f753b8a9c 3 weeks ago 193MB
mysql 8.0 532b8f3ab0d8 4 weeks ago 573MB
redis 7-alpine 0e1c9c9f8b1a 2 weeks ago 43.2MB
# 只看镜像 ID(方便脚本处理)
docker images -q
# 按名称过滤
docker images nginx
# 删除镜像(有容器正在使用时会失败,需先删容器)
docker rmi nginx:1.27.3
# 强制删除(即使有容器引用)
docker rmi -f nginx:1.27.3
# 批量删除所有 <none> 悬空镜像(构建失败残留)
docker image prune -f
# 离线搬运镜像(无网络的生产服务器)
docker save -o nginx.tar nginx:1.27.3
docker load -i nginx.tar
💡 docker commit 不推荐用于制作正式镜像:它产生的镜像是黑盒,无法追溯、不可复现、体积臃肿。生产环境请一律使用 Dockerfile(见第六章)。
四、容器(Container)基础操作
4.1 docker run 参数详解(最常用)
docker run [选项] 镜像名[:标签] [容器内要执行的命令]
| -d | detach,后台守护运行(不占用当前终端) | docker run -d nginx |
| -it | -i 保持标准输入打开 + -t 分配伪终端,交互式 | docker run -it ubuntu /bin/bash |
| –name | 给容器起个名字(否则随机生成如 nostalgic_curie) | –name my-nginx |
| -p | 端口映射 宿主机端口:容器端口 | -p 8080:80 |
| -P | 随机映射镜像 EXPOSE 声明的所有端口 | -P |
| -v | 数据挂载 宿主机路径:容器路径[:权限] | -v /data/html:/usr/share/nginx/html:ro |
| –rm | 容器退出后自动删除(适合临时测试) | docker run –rm -it alpine sh |
| -e | 设置环境变量 | -e MYSQL_ROOT_PASSWORD=123456 |
| –env-file | 从文件批量加载环境变量 | –env-file ./app.env |
| –network | 指定网络 | –network my-net |
| –restart | 重启策略:no/always/on-failure/unless-stopped | –restart=always |
| -m | 限制最大内存 | -m 512m |
| –cpus | 限制可用 CPU 核数 | –cpus=1.5 |
| -w | 指定容器内工作目录 | -w /app |
| -u | 指定运行用户 | -u 1000:1000 |
| –hostname | 设置容器主机名 | –hostname web01 |
| -l | 打标签(方便批量管理) | -l app=web |
端口映射的四种写法:
-p 8080:80 # 宿主机 8080 → 容器 80(最常用)
-p 127.0.0.1:8080:80 # 只允许本机访问,安全
-p 80:80 -p 443:443 # 多个端口分别映射
-p 8080:80/udp # 指定 UDP 协议
完整示例:
# 后台运行 nginx,宿主机 8080 映射到容器 80,起名 my-nginx,异常自动重启
docker run -d \\
–name my-nginx \\
-p 8080:80 \\
-v /opt/html:/usr/share/nginx/html:ro \\
–restart=unless-stopped \\
nginx:1.27.3
浏览器访问 http://localhost:8080 即可看到 Nginx 欢迎页 ✅
4.2 容器生命周期
#mermaid-svg-TSiIH6iQ4DZQRRVt{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TSiIH6iQ4DZQRRVt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TSiIH6iQ4DZQRRVt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TSiIH6iQ4DZQRRVt .error-icon{fill:#552222;}#mermaid-svg-TSiIH6iQ4DZQRRVt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TSiIH6iQ4DZQRRVt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TSiIH6iQ4DZQRRVt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TSiIH6iQ4DZQRRVt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TSiIH6iQ4DZQRRVt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TSiIH6iQ4DZQRRVt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TSiIH6iQ4DZQRRVt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TSiIH6iQ4DZQRRVt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TSiIH6iQ4DZQRRVt .marker.cross{stroke:#333333;}#mermaid-svg-TSiIH6iQ4DZQRRVt svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TSiIH6iQ4DZQRRVt p{margin:0;}#mermaid-svg-TSiIH6iQ4DZQRRVt defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-TSiIH6iQ4DZQRRVt g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-TSiIH6iQ4DZQRRVt g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-TSiIH6iQ4DZQRRVt g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-TSiIH6iQ4DZQRRVt g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-TSiIH6iQ4DZQRRVt g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-TSiIH6iQ4DZQRRVt .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-TSiIH6iQ4DZQRRVt .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-TSiIH6iQ4DZQRRVt .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-TSiIH6iQ4DZQRRVt .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-TSiIH6iQ4DZQRRVt .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-TSiIH6iQ4DZQRRVt .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-TSiIH6iQ4DZQRRVt .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-TSiIH6iQ4DZQRRVt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TSiIH6iQ4DZQRRVt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TSiIH6iQ4DZQRRVt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TSiIH6iQ4DZQRRVt .edgeLabel .label text{fill:#333;}#mermaid-svg-TSiIH6iQ4DZQRRVt .label div .edgeLabel{color:#333;}#mermaid-svg-TSiIH6iQ4DZQRRVt .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-TSiIH6iQ4DZQRRVt .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-TSiIH6iQ4DZQRRVt .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-TSiIH6iQ4DZQRRVt .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-TSiIH6iQ4DZQRRVt .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-TSiIH6iQ4DZQRRVt .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TSiIH6iQ4DZQRRVt .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TSiIH6iQ4DZQRRVt #statediagram-barbEnd{fill:#333333;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TSiIH6iQ4DZQRRVt .cluster-label,#mermaid-svg-TSiIH6iQ4DZQRRVt .nodeLabel{color:#131300;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-TSiIH6iQ4DZQRRVt .note-edge{stroke-dasharray:5;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-note text{fill:black;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram-note .nodeLabel{color:black;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagram .edgeLabel{color:red;}#mermaid-svg-TSiIH6iQ4DZQRRVt #dependencyStart,#mermaid-svg-TSiIH6iQ4DZQRRVt #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-TSiIH6iQ4DZQRRVt .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TSiIH6iQ4DZQRRVt :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
docker create
docker start
docker run
docker pause
docker unpause
docker stop / kill
主进程执行完毕 / 崩溃
docker start / restart
docker start
docker rm
docker rm
docker rm -f
Created
Running
Paused
Stopped
Exited
🔑 核心认知:容器的生命 = 主进程(PID 1)的生命。主进程退出,容器就停止。 这就是为什么 docker run -it ubuntu 退出 shell 后容器就没了;也是为什么 Nginx 镜像的 CMD 要写 daemon off;——必须让主进程前台常驻。
4.3 容器管理命令
| docker ps | 查看运行中的容器 | docker ps |
| docker ps -a | 查看所有容器(含已退出) | docker ps -a |
| docker ps -q | 只输出容器 ID | docker stop $(docker ps -q) |
| docker start | 启动已停止的容器 | docker start my-nginx |
| docker stop | 优雅停止(发 SIGTERM,默认等 10s) | docker stop my-nginx |
| docker restart | 重启容器 | docker restart my-nginx |
| docker kill | 强制杀死(发 SIGKILL) | docker kill -s SIGKILL c1 |
| docker rm | 删除已停止的容器 | docker rm my-nginx |
| docker rm -f | 强制删除运行中的容器 | docker rm -f $(docker ps -aq) |
| docker logs | 查看容器日志 | docker logs -f –tail 100 my-nginx |
| docker exec | 在运行中的容器里执行命令(最常用) | docker exec -it my-nginx bash |
| docker attach | 连接到容器主进程终端(退出会导致容器停止,不推荐) | docker attach c1 |
| docker inspect | 查看容器详细信息(IP、挂载、配置) | docker inspect my-nginx |
| docker stats | 实时查看容器 CPU/内存/网络占用 | docker stats |
| docker top | 查看容器内进程 | docker top my-nginx |
| docker port | 查看端口映射关系 | docker port my-nginx |
| docker cp | 宿主机 ↔ 容器 互相拷贝文件 | docker cp c1:/app/log.txt . |
| docker diff | 查看容器对文件系统的改动 | docker diff c1 |
| docker pause / unpause | 暂停 / 恢复容器 | docker pause c1 |
| docker rename | 重命名容器 | docker rename c1 new-name |
| docker wait | 阻塞等待容器退出并返回退出码 | docker wait c1 |
高频操作演示:
# 进入正在运行的容器(排查问题的第一反应)
docker exec -it my-nginx /bin/bash
# alpine 系镜像没有 bash,用 sh:
docker exec -it my-nginx /bin/sh
# 不进入容器,直接执行单条命令
docker exec my-nginx cat /etc/nginx/nginx.conf
docker exec my-db mysql -uroot -p123456 -e "show databases;"
# 实时追踪日志(Ctrl + C 退出)
docker logs -f –tail 200 my-nginx
# 查看容器 IP 地址
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-nginx
# 把容器里的文件拷到宿主机
docker cp my-nginx:/etc/nginx/conf.d/default.conf ./default.conf
# 把宿主机文件拷进容器
docker cp ./index.html my-nginx:/usr/share/nginx/html/
# 一键停止 / 删除所有容器
docker stop $(docker ps -q)
docker rm $(docker ps -aq)
# 一键清理:停止的容器 + 未被使用的网络 + 悬空镜像 + 构建缓存
docker system prune
# 加 -a:连未被任何容器使用的镜像也一起删(谨慎!)
docker system prune -a –volumes
⚠️ docker system prune -a –volumes 会删除所有未被使用的卷(数据)!生产环境执行前务必确认,数据无价。
五、数据持久化(Volume)
为什么需要数据持久化?
容器的可写层是临时的:容器一旦被 docker rm 删除,里面产生的所有数据(数据库文件、上传的图片、日志)全部永久丢失。而且可写层性能差、无法在容器间共享。
Docker 提供三种数据管理方式:
| Volume(数据卷) | 由 Docker 管理,存放在 /var/lib/docker/volumes/ | ✅ 首选,数据库数据、需要备份的数据 | -v mysql-data:/var/lib/mysql |
| Bind Mount(绑定挂载) | 直接挂载宿主机任意目录/文件 | 开发调试(改代码即时生效)、挂配置文件 | -v /opt/html:/usr/share/nginx/html |
| tmpfs Mount | 只存在内存中,容器停止即消失 | 临时敏感数据(密钥、缓存) | –tmpfs /run:size=100m |
#mermaid-svg-TIjQXbLwHF3MBbFa{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TIjQXbLwHF3MBbFa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TIjQXbLwHF3MBbFa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TIjQXbLwHF3MBbFa .error-icon{fill:#552222;}#mermaid-svg-TIjQXbLwHF3MBbFa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TIjQXbLwHF3MBbFa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TIjQXbLwHF3MBbFa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TIjQXbLwHF3MBbFa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TIjQXbLwHF3MBbFa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TIjQXbLwHF3MBbFa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TIjQXbLwHF3MBbFa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TIjQXbLwHF3MBbFa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TIjQXbLwHF3MBbFa .marker.cross{stroke:#333333;}#mermaid-svg-TIjQXbLwHF3MBbFa svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TIjQXbLwHF3MBbFa p{margin:0;}#mermaid-svg-TIjQXbLwHF3MBbFa .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-TIjQXbLwHF3MBbFa .cluster-label text{fill:#333;}#mermaid-svg-TIjQXbLwHF3MBbFa .cluster-label span{color:#333;}#mermaid-svg-TIjQXbLwHF3MBbFa .cluster-label span p{background-color:transparent;}#mermaid-svg-TIjQXbLwHF3MBbFa .label text,#mermaid-svg-TIjQXbLwHF3MBbFa span{fill:#333;color:#333;}#mermaid-svg-TIjQXbLwHF3MBbFa .node rect,#mermaid-svg-TIjQXbLwHF3MBbFa .node circle,#mermaid-svg-TIjQXbLwHF3MBbFa .node ellipse,#mermaid-svg-TIjQXbLwHF3MBbFa .node polygon,#mermaid-svg-TIjQXbLwHF3MBbFa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TIjQXbLwHF3MBbFa .rough-node .label text,#mermaid-svg-TIjQXbLwHF3MBbFa .node .label text,#mermaid-svg-TIjQXbLwHF3MBbFa .image-shape .label,#mermaid-svg-TIjQXbLwHF3MBbFa .icon-shape .label{text-anchor:middle;}#mermaid-svg-TIjQXbLwHF3MBbFa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TIjQXbLwHF3MBbFa .rough-node .label,#mermaid-svg-TIjQXbLwHF3MBbFa .node .label,#mermaid-svg-TIjQXbLwHF3MBbFa .image-shape .label,#mermaid-svg-TIjQXbLwHF3MBbFa .icon-shape .label{text-align:center;}#mermaid-svg-TIjQXbLwHF3MBbFa .node.clickable{cursor:pointer;}#mermaid-svg-TIjQXbLwHF3MBbFa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TIjQXbLwHF3MBbFa .arrowheadPath{fill:#333333;}#mermaid-svg-TIjQXbLwHF3MBbFa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TIjQXbLwHF3MBbFa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TIjQXbLwHF3MBbFa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TIjQXbLwHF3MBbFa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TIjQXbLwHF3MBbFa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TIjQXbLwHF3MBbFa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TIjQXbLwHF3MBbFa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TIjQXbLwHF3MBbFa .cluster text{fill:#333;}#mermaid-svg-TIjQXbLwHF3MBbFa .cluster span{color:#333;}#mermaid-svg-TIjQXbLwHF3MBbFa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TIjQXbLwHF3MBbFa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TIjQXbLwHF3MBbFa rect.text{fill:none;stroke-width:0;}#mermaid-svg-TIjQXbLwHF3MBbFa .icon-shape,#mermaid-svg-TIjQXbLwHF3MBbFa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TIjQXbLwHF3MBbFa .icon-shape p,#mermaid-svg-TIjQXbLwHF3MBbFa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TIjQXbLwHF3MBbFa .icon-shape .label rect,#mermaid-svg-TIjQXbLwHF3MBbFa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TIjQXbLwHF3MBbFa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TIjQXbLwHF3MBbFa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TIjQXbLwHF3MBbFa :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Nginx 容器
MySQL 容器
宿主机文件系统
Volume 数据卷Docker 托管
Bind Mount 绑定挂载直接映射宿主机目录
/var/lib/docker/volumes/mysql-data/_data
/opt/html
/var/lib/mysql
/usr/share/nginx/html
5.1 数据卷常用命令
docker volume create mysql-data # 创建一个具名卷
docker volume ls # 列出所有卷
docker volume inspect mysql-data # 查看卷的详细信息(含宿主机真实路径)
docker volume rm mysql-data # 删除卷
docker volume prune # 清理所有未被使用的卷(⚠️ 数据会丢)
inspect 输出示例:
[
{
"CreatedAt": "2025-01-15T10:23:41+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/mysql-data/_data",
"Name": "mysql-data",
"Options": {},
"Scope": "local"
}
]
5.2 三种挂载写法对比
# ① 具名卷(推荐用于数据库):冒号前是"卷名"(不以 / 开头)
docker run -d –name db -v mysql-data:/var/lib/mysql mysql:8.0
# ② 匿名卷:只写容器内路径,Docker 自动生成一长串哈希作为卷名(难管理,不推荐)
docker run -d –name db -v /var/lib/mysql mysql:8.0
# ③ 绑定挂载:冒号前是"宿主机绝对路径"(以 / 或盘符开头)
docker run -d –name web -v /opt/html:/usr/share/nginx/html:ro nginx
# Windows 写法(PowerShell):
docker run -d –name web -v D:\\html:/usr/share/nginx/html nginx
# 权限后缀:ro = read-only 只读;rw = 读写(默认)
5.3 实战:MySQL 数据持久化验证
# 1. 启动 MySQL,数据存到具名卷,配置文件绑定挂载
docker run -d \\
–name mysql-test \\
-p 3306:3306 \\
-e MYSQL_ROOT_PASSWORD=Root@123456 \\
-e MYSQL_DATABASE=demo \\
-v mysql-data:/var/lib/mysql \\
-v /opt/mysql/conf/my.cnf:/etc/mysql/my.cnf:ro \\
–restart=unless-stopped \\
mysql:8.0
# 2. 写入一条数据
docker exec -it mysql-test mysql -uroot -pRoot@123456 \\
-e "CREATE DATABASE IF NOT EXISTS demo; USE demo; CREATE TABLE t(id INT); INSERT INTO t VALUES(1);"
# 3. 删除容器(注意:卷没删!)
docker rm -f mysql-test
# 4. 用同一个卷重新启动
docker run -d –name mysql-test2 -p 3306:3306 \\
-e MYSQL_ROOT_PASSWORD=Root@123456 -v mysql-data:/var/lib/mysql mysql:8.0
# 5. 验证数据还在 ✅
docker exec -it mysql-test2 mysql -uroot -pRoot@123456 -e "USE demo; SELECT * FROM t;"
+——+
| id |
+——+
| 1 |
+——+
🎉 容器删了,数据还在——这就是数据持久化的意义。 生产环境的正确姿势:配置文件用绑定挂载(方便修改)+ 数据目录用数据卷(Docker 托管、易备份)。
六、Dockerfile:构建自己的镜像
Dockerfile 是一个纯文本文件,里面按顺序写着一条条构建指令,Docker 读取它自动构建出镜像。这是镜像交付的标准方式,可版本控制、可复现、可审查。
6.1 常用指令速查
| FROM | 必须的第一条指令,指定基础镜像 | FROM python:3.12-slim |
| WORKDIR | 设置工作目录(不存在会自动创建,替代 cd) | WORKDIR /app |
| COPY | 从构建上下文复制文件到镜像 | COPY . /app |
| ADD | 同 COPY,但额外支持自动解压 tar 和 URL 下载(一般用 COPY 更安全) | ADD app.tar.gz /app |
| RUN | 构建时执行命令,生成一个新的镜像层 | RUN pip install -r requirements.txt |
| ENV | 设置环境变量(构建时和运行时都生效) | ENV PYTHONUNBUFFERED=1 |
| ARG | 构建时变量(只在 build 阶段生效) | ARG VERSION=1.0 |
| EXPOSE | 声明容器监听端口(仅文档作用,实际映射还要靠 -p) | EXPOSE 8000 |
| VOLUME | 声明匿名挂载点 | VOLUME /data |
| USER | 切换执行用户(安全最佳实践,别用 root) | USER appuser |
| CMD | 容器启动时的默认命令,可被 docker run 后的参数覆盖 | CMD ["python", "app.py"] |
| ENTRYPOINT | 容器启动时的入口命令,不易被覆盖 | ENTRYPOINT ["java","-jar","app.jar"] |
| LABEL | 添加元数据标签 | LABEL maintainer="me@example.com" |
| HEALTHCHECK | 定义容器健康检查 | HEALTHCHECK CMD curl -f http://localhost/ |
| ONBUILD | 触发器,被继承时才执行 | ONBUILD COPY . /app |
6.2 实战案例
案例一:Python Flask Web 应用
目录结构:
myapp/
├── Dockerfile
├── requirements.txt
├── app.py
└── .dockerignore
app.py:
from flask import Flask
import os, socket
app = Flask(__name__)
@app.route("/")
def hello():
return f"Hello Docker! 运行在 {socket.gethostname()},环境={os.getenv('ENV','dev')}"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
requirements.txt:
flask==3.0.3
gunicorn==22.0.0
.dockerignore(极其重要,减小上下文体积、避免敏感文件泄漏):
.git
.venv
__pycache__
*.pyc
.idea
.vscode
.env
*.log
node_modules
Dockerfile:
# 1. 基础镜像:slim 版比完整版小 800MB+
FROM python:3.12-slim
# 2. 元数据
LABEL maintainer="dev@example.com"
# 3. 工作目录
WORKDIR /app
# 4. 设置环境变量:不缓冲输出(日志实时可见)+ pip 使用国内源
ENV PYTHONUNBUFFERED=1 \\
PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple
# 5. 先只拷贝依赖文件 → 充分利用构建缓存!
# 只要 requirements.txt 不变,这一层就不会重新执行 pip install
COPY requirements.txt .
RUN pip install –no-cache-dir -r requirements.txt
# 6. 再拷贝业务代码(代码频繁变动,放在后面)
COPY . .
# 7. 创建非 root 用户并切换
RUN useradd -m appuser && chown -R appuser /app
USER appuser
# 8. 声明端口
EXPOSE 5000
# 9. 健康检查
HEALTHCHECK –interval=30s –timeout=3s –retries=3 \\
CMD python -c "import urllib.request;urllib.request.urlopen('http://localhost:5000/')" || exit 1
# 10. 启动命令:生产用 gunicorn,多 worker
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"]
构建并运行:
# -t 指定镜像名:标签,最后的 . 表示构建上下文为当前目录
docker build -t myflask:1.0 .
# 查看构建过程(带进度条)
docker build –progress=plain -t myflask:1.0 .
# 不使用缓存完全重建
docker build –no-cache -t myflask:1.0 .
# 运行
docker run -d –name flask-app -p 5000:5000 -e ENV=prod myflask:1.0
[+] Building 42.7s (11/11) FINISHED
=> [internal] load build definition from Dockerfile
=> [internal] load .dockerignore
=> [1/6] FROM docker.io/library/python:3.12-slim
=> [2/6] WORKDIR /app
=> [3/6] COPY requirements.txt .
=> [4/6] RUN pip install –no-cache-dir -r requirements.txt
=> [5/6] COPY . .
=> [6/6] RUN useradd -m appuser && chown -R appuser /app
=> exporting to image
=> => naming to docker.io/library/myflask:1.0
访问 http://localhost:5000 → Hello Docker! 运行在 3f9a2b1c8e7d,环境=prod ✅
案例二:Java Spring Boot 应用
# 构建阶段:用 Maven 镜像编译
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline # 先下依赖,利用缓存
COPY src ./src
RUN mvn clean package -DskipTests
# 运行阶段:只拷贝 jar,镜像极小
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY –from=builder /build/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "-Xms256m", "-Xmx512m", "app.jar"]
docker build -t myjava:1.0 .
docker run -d –name java-app -p 8080:8080 myjava:1.0
案例三:Nginx 部署静态网站
FROM nginx:1.27-alpine
# 删除默认配置,换成自己的
RUN rm -rf /usr/share/nginx/html/*
COPY ./dist /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
docker build -t myweb:1.0 .
docker run -d –name web -p 80:80 myweb:1.0
案例四:多阶段构建(Node 前端 + Nginx 部署)
# —- 阶段 1:构建 —-
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci –registry=https://registry.npmmirror.com
COPY . .
RUN npm run build
# —- 阶段 2:运行 —-
FROM nginx:1.27-alpine
COPY –from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
💡 多阶段构建(Multi-stage Build) 是镜像瘦身的杀手锏: 上面这个例子最终镜像里完全不包含 node_modules 和源码,只有编译产物 + Nginx,体积从 1GB+ 降到 20MB 左右。
6.3 CMD 与 ENTRYPOINT 的区别
| 数量限制 | 只有最后一条生效 | 只有最后一条生效 |
| 被 docker run xxx 追加参数 | 整体被覆盖 | 作为参数追加到 ENTRYPOINT 后 |
| 被 docker run –entrypoint | 不影响 | 可覆盖 |
| 典型用途 | 提供默认命令 / 默认参数 | 把镜像当成一个「可执行程序」 |
直观对比:
# 写法 A:只用 CMD
FROM ubuntu
CMD ["echo", "hello"]
docker run A # 输出:hello
docker run A echo "world" # 输出:world ← CMD 被完全覆盖
# 写法 B:只用 ENTRYPOINT
FROM ubuntu
ENTRYPOINT ["echo", "hello"]
docker run B # 输出:hello
docker run B "world" # 输出:hello world ← 参数被追加
# 写法 C:最佳实践组合 —— ENTRYPOINT 定命令,CMD 定默认参数
FROM nginx
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
docker run C # 实际执行:nginx -g "daemon off;"
docker run C -v # 实际执行:nginx -v ← 只替换默认参数
6.4 镜像瘦身技巧
| 选小基础镜像 | alpine(5MB)> slim > distroless(无 shell 最安全)> 完整版 |
| 多阶段构建 | 编译环境与运行环境分离,只把产物拷进最终镜像 |
| 合并 RUN 指令 | 用 && 串联并在同一层清理缓存,减少层数 |
| 清理包缓存 | apt-get clean && rm -rf /var/lib/apt/lists/*;pip –no-cache-dir;npm ci && npm cache clean –force |
| 善用 .dockerignore | 排除 .git、node_modules、日志、密钥 |
| 精确 COPY | 别动不动 COPY . .,只拷需要的文件 |
| 固定 tag + digest | FROM node:20.11.1-alpine3.19,避免 latest 漂移 |
# ❌ 反面教材:产生 3 层,且缓存全部残留在镜像里
RUN apt-get update
RUN apt-get install -y curl vim
RUN apt-get install -y git
# ✅ 正确写法:1 层搞定,装完立刻清理
RUN apt-get update && apt-get install -y –no-install-recommends \\
curl \\
vim \\
git \\
&& apt-get clean \\
&& rm -rf /var/lib/apt/lists/*
⚠️ alpine 的坑:它用的是 musl libc 而非 glibc,某些依赖 glibc 的程序(部分 Python C 扩展、旧版 JDK)会报错。 解决:apk add –no-cache libc6-compat,或改用 debian-slim 系列。
七、网络模式
容器之间、容器与外界如何通信?Docker 提供 5 种网络模式:
#mermaid-svg-sDLvOZuBv6zk7L7n{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sDLvOZuBv6zk7L7n .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sDLvOZuBv6zk7L7n .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sDLvOZuBv6zk7L7n .error-icon{fill:#552222;}#mermaid-svg-sDLvOZuBv6zk7L7n .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sDLvOZuBv6zk7L7n .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sDLvOZuBv6zk7L7n .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sDLvOZuBv6zk7L7n .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sDLvOZuBv6zk7L7n .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sDLvOZuBv6zk7L7n .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sDLvOZuBv6zk7L7n .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sDLvOZuBv6zk7L7n .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sDLvOZuBv6zk7L7n .marker.cross{stroke:#333333;}#mermaid-svg-sDLvOZuBv6zk7L7n svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sDLvOZuBv6zk7L7n p{margin:0;}#mermaid-svg-sDLvOZuBv6zk7L7n .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-sDLvOZuBv6zk7L7n .cluster-label text{fill:#333;}#mermaid-svg-sDLvOZuBv6zk7L7n .cluster-label span{color:#333;}#mermaid-svg-sDLvOZuBv6zk7L7n .cluster-label span p{background-color:transparent;}#mermaid-svg-sDLvOZuBv6zk7L7n .label text,#mermaid-svg-sDLvOZuBv6zk7L7n span{fill:#333;color:#333;}#mermaid-svg-sDLvOZuBv6zk7L7n .node rect,#mermaid-svg-sDLvOZuBv6zk7L7n .node circle,#mermaid-svg-sDLvOZuBv6zk7L7n .node ellipse,#mermaid-svg-sDLvOZuBv6zk7L7n .node polygon,#mermaid-svg-sDLvOZuBv6zk7L7n .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sDLvOZuBv6zk7L7n .rough-node .label text,#mermaid-svg-sDLvOZuBv6zk7L7n .node .label text,#mermaid-svg-sDLvOZuBv6zk7L7n .image-shape .label,#mermaid-svg-sDLvOZuBv6zk7L7n .icon-shape .label{text-anchor:middle;}#mermaid-svg-sDLvOZuBv6zk7L7n .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sDLvOZuBv6zk7L7n .rough-node .label,#mermaid-svg-sDLvOZuBv6zk7L7n .node .label,#mermaid-svg-sDLvOZuBv6zk7L7n .image-shape .label,#mermaid-svg-sDLvOZuBv6zk7L7n .icon-shape .label{text-align:center;}#mermaid-svg-sDLvOZuBv6zk7L7n .node.clickable{cursor:pointer;}#mermaid-svg-sDLvOZuBv6zk7L7n .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sDLvOZuBv6zk7L7n .arrowheadPath{fill:#333333;}#mermaid-svg-sDLvOZuBv6zk7L7n .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sDLvOZuBv6zk7L7n .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sDLvOZuBv6zk7L7n .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sDLvOZuBv6zk7L7n .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sDLvOZuBv6zk7L7n .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sDLvOZuBv6zk7L7n .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sDLvOZuBv6zk7L7n .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sDLvOZuBv6zk7L7n .cluster text{fill:#333;}#mermaid-svg-sDLvOZuBv6zk7L7n .cluster span{color:#333;}#mermaid-svg-sDLvOZuBv6zk7L7n div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sDLvOZuBv6zk7L7n .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sDLvOZuBv6zk7L7n rect.text{fill:none;stroke-width:0;}#mermaid-svg-sDLvOZuBv6zk7L7n .icon-shape,#mermaid-svg-sDLvOZuBv6zk7L7n .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sDLvOZuBv6zk7L7n .icon-shape p,#mermaid-svg-sDLvOZuBv6zk7L7n .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sDLvOZuBv6zk7L7n .icon-shape .label rect,#mermaid-svg-sDLvOZuBv6zk7L7n .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sDLvOZuBv6zk7L7n .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sDLvOZuBv6zk7L7n .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sDLvOZuBv6zk7L7n :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
bridge 桥接模式(默认)
宿主机 8080
外网访问者
docker0 网桥172.17.0.1
容器A172.17.0.2:80
容器B172.17.0.3:3306
| bridge(默认) | –network bridge | 容器接入虚拟网桥 docker0,拥有独立 IP,通过端口映射对外暴露 | 绝大多数单机场景 ✅ |
| host | –network host | 容器直接使用宿主机网络栈,无隔离,-p 参数失效 | 追求极致网络性能(如高并发代理) |
| none | –network none | 只有 lo 回环网卡,完全无网络 | 只跑离线计算任务,安全隔离 |
| container | –network container:名称 | 与另一个容器共享网络命名空间(共用 IP 和端口) | Pod 类场景(K8s 就是这么做的) |
| 自定义 bridge | –network my-net | 用户创建的桥接网络,支持容器名 DNS 自动解析 | 多容器互联(强烈推荐) |
7.1 网络常用命令
docker network ls # 查看所有网络
docker network inspect bridge # 查看网络详情(含所有连接容器的 IP)
docker network create my-net # 创建自定义网络(默认 bridge 驱动)
docker network create –driver bridge –subnet 172.20.0.0/16 my-net
docker network connect my-net c1 # 把已运行的容器接入网络
docker network disconnect my-net c1 # 断开
docker network rm my-net # 删除网络
docker network prune # 清理未使用的网络
7.2 实战:容器互联(自定义网络的 DNS 魔法)
# 1. 创建自定义网络
docker network create app-net
# 2. 启动 MySQL,接入 app-net
docker run -d –name mysql-db –network app-net \\
-e MYSQL_ROOT_PASSWORD=123456 mysql:8.0
# 3. 启动应用,同样接入 app-net
docker run -d –name web-app –network app-net -p 8080:8080 myflask:1.0
关键区别:
# ❌ 默认 bridge 网络:只能用 IP 通信,容器重启 IP 会变
docker exec web-app ping 172.17.0.2
# ✅ 自定义网络:可以直接用"容器名"当域名,Docker 内置 DNS 自动解析
docker exec web-app ping mysql-db
PING mysql-db (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.087 ms
💡 所以应用配置里的数据库地址直接写 mysql-db(容器名)即可,不用写 IP,也不用 –link(–link 已被官方废弃)。
八、Docker Compose:多容器编排
问题:一个真实项目往往有 Web + MySQL + Redis + Nginx 四五个容器,每个都手敲一条长长的 docker run,参数还容易写错,怎么办?
答案:Docker Compose —— 用一个 YAML 文件声明式描述所有服务,一条命令全部启停。
| Compose V1 | docker-compose | Python | ⛔ 已停止维护(2023 年 6 月 EOL),需单独安装 |
| Compose V2 | docker compose(中间是空格) | Go,作为 Docker CLI 插件 | ✅ 官方推荐,Docker Desktop / 新版 Engine 自带 |
# 检查版本
docker compose version
Docker Compose version v2.29.7
8.1 docker-compose.yml 完整示例
# 项目名(决定容器名前缀和网络名),不写则用目录名
name: my–project
services:
# ———- Web 应用 ———-
web:
build: # 从 Dockerfile 构建
context: .
dockerfile: Dockerfile
image: myflask:1.0
container_name: flask–web
restart: unless–stopped
ports:
– "5000:5000" # 端口映射(加引号避免 YAML 解析成数字)
environment: # 环境变量
– ENV=prod
– DB_HOST=mysql # 直接用服务名当域名!
– DB_PORT=3306
– REDIS_HOST=redis
env_file:
– .env
volumes:
– ./logs:/app/logs # 绑定挂载
depends_on: # 启动顺序(仅顺序,不等就绪)
mysql:
condition: service_healthy
redis:
condition: service_started
networks:
– app–net
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/"]
interval: 30s
timeout: 5s
retries: 3
# ———- MySQL ———-
mysql:
image: mysql:8.0
container_name: project–mysql
restart: always
ports:
– "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: Root@123456
MYSQL_DATABASE: demo
TZ: Asia/Shanghai
volumes:
– mysql–data:/var/lib/mysql # 数据卷持久化
– ./conf/my.cnf:/etc/mysql/conf.d/my.cnf:ro # 配置文件
command:
– ––character–set–server=utf8mb4
– ––collation–server=utf8mb4_unicode_ci
networks:
– app–net
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-pRoot@123456"]
interval: 10s
timeout: 5s
retries: 10
# ———- Redis ———-
redis:
image: redis:7–alpine
container_name: project–redis
restart: always
ports:
– "6379:6379"
volumes:
– redis–data:/data
command: ["redis-server", "–appendonly", "yes", "–requirepass", "Redis@123"]
networks:
– app–net
# ———- Nginx 反向代理 ———-
nginx:
image: nginx:1.27–alpine
container_name: project–nginx
restart: always
ports:
– "80:80"
volumes:
– ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
– ./nginx/conf.d:/etc/nginx/conf.d:ro
depends_on:
– web
networks:
– app–net
# ———- 数据卷声明 ———-
volumes:
mysql-data:
redis-data:
# ———- 网络声明 ———-
networks:
app-net:
driver: bridge
配套的 nginx/conf.d/default.conf:
upstream backend {
server web:5000; # 用 compose 服务名,Docker DNS 自动解析
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
8.2 Compose 拓扑图
#mermaid-svg-TO3w5IqEdPS2mwTO{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TO3w5IqEdPS2mwTO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TO3w5IqEdPS2mwTO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TO3w5IqEdPS2mwTO .error-icon{fill:#552222;}#mermaid-svg-TO3w5IqEdPS2mwTO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TO3w5IqEdPS2mwTO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TO3w5IqEdPS2mwTO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TO3w5IqEdPS2mwTO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TO3w5IqEdPS2mwTO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TO3w5IqEdPS2mwTO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TO3w5IqEdPS2mwTO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TO3w5IqEdPS2mwTO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TO3w5IqEdPS2mwTO .marker.cross{stroke:#333333;}#mermaid-svg-TO3w5IqEdPS2mwTO svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TO3w5IqEdPS2mwTO p{margin:0;}#mermaid-svg-TO3w5IqEdPS2mwTO .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-TO3w5IqEdPS2mwTO .cluster-label text{fill:#333;}#mermaid-svg-TO3w5IqEdPS2mwTO .cluster-label span{color:#333;}#mermaid-svg-TO3w5IqEdPS2mwTO .cluster-label span p{background-color:transparent;}#mermaid-svg-TO3w5IqEdPS2mwTO .label text,#mermaid-svg-TO3w5IqEdPS2mwTO span{fill:#333;color:#333;}#mermaid-svg-TO3w5IqEdPS2mwTO .node rect,#mermaid-svg-TO3w5IqEdPS2mwTO .node circle,#mermaid-svg-TO3w5IqEdPS2mwTO .node ellipse,#mermaid-svg-TO3w5IqEdPS2mwTO .node polygon,#mermaid-svg-TO3w5IqEdPS2mwTO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TO3w5IqEdPS2mwTO .rough-node .label text,#mermaid-svg-TO3w5IqEdPS2mwTO .node .label text,#mermaid-svg-TO3w5IqEdPS2mwTO .image-shape .label,#mermaid-svg-TO3w5IqEdPS2mwTO .icon-shape .label{text-anchor:middle;}#mermaid-svg-TO3w5IqEdPS2mwTO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TO3w5IqEdPS2mwTO .rough-node .label,#mermaid-svg-TO3w5IqEdPS2mwTO .node .label,#mermaid-svg-TO3w5IqEdPS2mwTO .image-shape .label,#mermaid-svg-TO3w5IqEdPS2mwTO .icon-shape .label{text-align:center;}#mermaid-svg-TO3w5IqEdPS2mwTO .node.clickable{cursor:pointer;}#mermaid-svg-TO3w5IqEdPS2mwTO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TO3w5IqEdPS2mwTO .arrowheadPath{fill:#333333;}#mermaid-svg-TO3w5IqEdPS2mwTO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TO3w5IqEdPS2mwTO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TO3w5IqEdPS2mwTO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TO3w5IqEdPS2mwTO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TO3w5IqEdPS2mwTO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TO3w5IqEdPS2mwTO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TO3w5IqEdPS2mwTO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TO3w5IqEdPS2mwTO .cluster text{fill:#333;}#mermaid-svg-TO3w5IqEdPS2mwTO .cluster span{color:#333;}#mermaid-svg-TO3w5IqEdPS2mwTO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TO3w5IqEdPS2mwTO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TO3w5IqEdPS2mwTO rect.text{fill:none;stroke-width:0;}#mermaid-svg-TO3w5IqEdPS2mwTO .icon-shape,#mermaid-svg-TO3w5IqEdPS2mwTO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TO3w5IqEdPS2mwTO .icon-shape p,#mermaid-svg-TO3w5IqEdPS2mwTO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TO3w5IqEdPS2mwTO .icon-shape .label rect,#mermaid-svg-TO3w5IqEdPS2mwTO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TO3w5IqEdPS2mwTO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TO3w5IqEdPS2mwTO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TO3w5IqEdPS2mwTO :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
app-net 自定义网络
:80
proxy_pass web:5000
DB_HOST=mysql:3306
REDIS_HOST=redis:6379
用户浏览器
nginx 服务反向代理
web 服务Flask 应用
mysql 服务8.0
redis 服务7-alpine
mysql-data 卷
redis-data 卷
8.3 Compose 常用命令
# 启动(-d 后台运行,首次会自动 build + 创建网络与卷)
docker compose up -d
# 只构建不启动
docker compose build
# 代码改动后重新构建并滚动更新
docker compose up -d –build
# 查看服务状态
docker compose ps
# 查看所有服务日志 / 单个服务日志
docker compose logs -f
docker compose logs -f web
# 进入某个服务的容器
docker compose exec web sh
docker compose exec mysql mysql -uroot -pRoot@123456
# 停止 / 重启 / 启动
docker compose stop
docker compose restart web
docker compose start
# 停止并删除容器 + 网络(保留数据卷)
docker compose down
# ⚠️ 连数据卷一起删除(数据全丢,慎用!)
docker compose down -v
# 扩容某个服务到 3 个实例
docker compose up -d –scale web=3
# 在 compose 环境里执行一次性命令
docker compose run –rm web python manage.py migrate
# 查看最终渲染的配置(排查 YAML 缩进错误神器)
docker compose config
# 查看资源占用
docker compose top
docker stats
8.4 Compose 常用字段速查
| image | 使用现成镜像 |
| build | 从 Dockerfile 构建(context 上下文 + dockerfile 路径 + args 构建参数) |
| container_name | 指定容器名(指定后就无法 scale 多实例) |
| ports | 端口映射,格式 "宿主:容器" |
| expose | 仅对同网络内容器开放端口,不映射到宿主机 |
| volumes | 数据挂载,-v 的 YAML 版 |
| environment / env_file | 环境变量 |
| command | 覆盖镜像的 CMD |
| entrypoint | 覆盖镜像的 ENTRYPOINT |
| depends_on | 启动依赖顺序(配合 condition: service_healthy 才真正等就绪) |
| restart | 重启策略:no / always / on-failure / unless-stopped |
| networks | 加入的网络 |
| healthcheck | 健康检查 |
| deploy.resources.limits | 资源限制(cpus、memory) |
九、实战:常见服务一键部署
9.1 部署 MySQL 8.0
docker run -d \\
–name mysql \\
-p 3306:3306 \\
-e MYSQL_ROOT_PASSWORD=Root@123456 \\
-e MYSQL_DATABASE=demo \\
-e TZ=Asia/Shanghai \\
-v mysql-data:/var/lib/mysql \\
–restart=always \\
mysql:8.0 \\
–character-set-server=utf8mb4 \\
–collation-server=utf8mb4_unicode_ci
# 连接测试
docker exec -it mysql mysql -uroot -pRoot@123456 -e "SELECT VERSION();"
+———–+
| VERSION() |
+———–+
| 8.0.39 |
+———–+
⚠️ 安全提醒:生产环境不要把 3306 端口直接暴露到公网!改用 -p 127.0.0.1:3306:3306 或干脆不映射端口,只让同网络的应用容器访问。
9.2 部署 Redis
docker run -d \\
–name redis \\
-p 6379:6379 \\
-v redis-data:/data \\
–restart=always \\
redis:7-alpine \\
redis-server –appendonly yes –requirepass Redis@123456
# 进入 redis-cli
docker exec -it redis redis-cli -a Redis@123456
127.0.0.1:6379> set name "Docker"
OK
127.0.0.1:6379> get name
"Docker"
9.3 部署 Nginx + 静态网站
# 准备目录
mkdir -p /opt/nginx/html /opt/nginx/conf.d /opt/nginx/logs
echo "<h1>Hello from Docker Nginx!</h1>" > /opt/nginx/html/index.html
docker run -d \\
–name nginx \\
-p 80:80 -p 443:443 \\
-v /opt/nginx/html:/usr/share/nginx/html:ro \\
-v /opt/nginx/conf.d:/etc/nginx/conf.d:ro \\
-v /opt/nginx/logs:/var/log/nginx \\
–restart=always \\
nginx:1.27-alpine
# 修改配置后热加载(无需重启容器)
docker exec nginx nginx -s reload
# 校验配置文件语法
docker exec nginx nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
9.4 部署可视化管理面板 Portainer
不想天天敲命令?装个 Web 管理界面:
docker run -d \\
–name portainer \\
-p 9443:9443 -p 8000:8000 \\
-v /var/run/docker.sock:/var/run/docker.sock \\
-v portainer-data:/data \\
–restart=always \\
portainer/portainer-ce:latest
浏览器访问 https://localhost:9443,首次进入设置管理员密码,即可用图形界面管理镜像、容器、卷、网络、Compose 栈。
9.5 一键部署 WordPress + MySQL(官方经典示例)
docker-compose.yml:
services:
db:
image: mysql:8.0
command: ––default–authentication–plugin=mysql_native_password
restart: always
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: Wp@123456
MYSQL_RANDOM_ROOT_PASSWORD: "1"
volumes:
– db–data:/var/lib/mysql
wordpress:
image: wordpress:latest
restart: always
ports:
– "8000:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: Wp@123456
WORDPRESS_DB_NAME: wordpress
volumes:
– wp–data:/var/www/html
depends_on:
– db
volumes:
db-data:
wp-data:
docker compose up -d
访问 http://localhost:8000,一个完整的 WordPress 站点就跑起来了 🎉
十、常用命令速查表
镜像相关
docker search nginx # 搜索镜像
docker pull nginx:1.27 # 拉取镜像
docker images # 列出镜像
docker rmi nginx:1.27 # 删除镜像
docker tag nginx:1.27 myrepo/nginx:v1 # 打标签
docker build -t myapp:1.0 . # 构建镜像
docker history myapp:1.0 # 查看层历史
docker save -o myapp.tar myapp:1.0 # 导出
docker load -i myapp.tar # 导入
docker push myrepo/myapp:v1 # 推送
docker image prune -a # 清理无用镜像
容器相关
docker run -d -p 80:80 –name web nginx # 后台运行
docker ps -a # 全部容器
docker stop web / start web / restart web # 停/启/重启
docker rm -f web # 强制删除
docker logs -f –tail 100 web # 看日志
docker exec -it web sh # 进容器
docker stats # 资源监控
docker inspect web # 详细信息
docker cp web:/etc/nginx/nginx.conf . # 拷文件
docker system prune -a # 大扫除
卷与网络
docker volume ls / create / inspect / rm / prune
docker network ls / create / inspect / connect / rm / prune
Compose
docker compose up -d # 启动
docker compose down # 停止并删除
docker compose ps # 状态
docker compose logs -f # 日志
docker compose exec web sh # 进容器
docker compose build # 构建
docker compose pull # 拉取所有服务镜像
docker compose config # 校验配置
系统信息
docker version # 版本
docker info # 引擎信息
docker system df # 磁盘占用统计
docker events # 实时事件流
十一、常见问题 FAQ
Q1:docker run 报错 Cannot connect to the Docker daemon at unix:///var/run/docker.sock?
Docker 守护进程没启动。
# Linux
sudo systemctl status docker
sudo systemctl start docker
sudo systemctl enable docker
# Windows / macOS
打开 Docker Desktop,等待鲸鱼图标变成稳定的 Engine running
Q2:拉取镜像超慢或直接超时 Client.Timeout exceeded?
配置国内镜像加速器(见 2.6 节),改完 daemon.json 必须重启 Docker:
sudo systemctl daemon-reload && sudo systemctl restart docker
docker info | grep -A3 "Registry Mirrors" # 确认已生效
Q3:容器一启动就退出(Exited (0) / Exited (1)),docker ps 看不到?
容器生命 = 主进程生命。常见原因:
| 主进程执行完就结束了(如 docker run ubuntu echo hi) | 用 -it 保持前台,或让主进程常驻 |
| 程序是后台守护模式(如 nginx 默认 daemon on) | 加 daemon off; 让进程前台运行 |
| 配置错误 / 缺少环境变量导致启动崩溃 | docker logs 容器名 看报错 |
| 端口被占用 | docker logs 会提示 address already in use |
docker ps -a # 找到 Exited 的容器
docker logs 容器名 # 看具体报错原因
Q4:-p 80:80 映射了,但浏览器访问不了?
按顺序排查:
docker ps # ① 容器是否 Up 状态
docker port 容器名 # ② 端口映射是否正确
curl http://localhost # ③ 本机能否访问
sudo ss -tlnp | grep 80 # ④ 宿主机是否真的在监听
sudo iptables -L -n # ⑤ 防火墙是否放行
云服务器还要去控制台安全组放行对应端口(这是最常被忽略的一步)。
Q5:C 盘 / 磁盘被 Docker 占满了怎么办?
docker system df # 先看谁占的空间
docker system prune -a # 清理未使用的镜像、容器、网络、构建缓存
# 大杀器:把数据目录搬到其他盘
# Linux:修改 /etc/docker/daemon.json
{
"data-root": "/data/docker"
}
sudo systemctl restart docker
Windows Docker Desktop:Settings → Resources → Advanced → Disk image location 改到 D 盘。
Q6:容器里的数据怎么备份?
# 方式一:直接备份数据卷(推荐)
docker run –rm -v mysql-data:/data -v $(pwd):/backup alpine \\
tar czf /backup/mysql-data-$(date +%F).tar.gz -C /data .
# 方式二:数据库用官方逻辑备份工具
docker exec mysql mysqldump -uroot -pRoot@123456 –all-databases > backup.sql
docker exec -i mysql mysql -uroot -pRoot@123456 < backup.sql # 恢复
Q7:docker-compose 命令找不到?
你装的是 Compose V2,命令是 docker compose(中间是空格,不是横杠):
docker compose version # 验证
# 如果确实需要老的横杠命令,做个别名:
echo 'alias docker-compose="docker compose"' >> ~/.bashrc && source ~/.bashrc
Q8:构建镜像时报错 failed to solve: failed to read dockerfile?
十有八九是构建上下文里找不到 Dockerfile。检查:
ls -la Dockerfile # ① 文件名大小写必须完全正确(Linux 区分大小写)
docker build -t myapp . # ② 最后那个 . 不能漏!它指定构建上下文
docker build -f docker/Dockerfile -t myapp . # ③ Dockerfile 不在当前目录时用 -f 指定
Q9:修改了代码,为什么容器里没生效?
镜像是构建时打包的快照,不是实时同步。改完代码必须重新构建:
docker compose up -d –build # Compose 项目
# 或手动:
docker build -t myapp:1.0 . && docker rm -f app && docker run -d –name app myapp:1.0
💡 开发阶段想改代码即时生效,用绑定挂载把源码目录挂进容器(-v $(pwd):/app),配合热重载工具(如 nodemon、flask –debug、uvicorn –reload)。
Q10:Apple Silicon(M1/M2/M3)拉镜像报 no matching manifest for linux/arm64?
该镜像没有 ARM 版本:
# 显式拉 amd64 版本,走 Rosetta/QEMU 模拟
docker pull –platform linux/amd64 镜像名:标签
docker run –platform linux/amd64 -d 镜像名:标签
Q11:容器内时区不对,日志时间差了 8 小时?
# 方式一:环境变量
docker run -d -e TZ=Asia/Shanghai 镜像名
# 方式二:挂载宿主机时区文件
docker run -d -v /etc/localtime:/etc/localtime:ro 镜像名
# 方式三:写进 Dockerfile
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
Q12:Docker 和 Kubernetes 是什么关系?该学哪个?
| 定位 | 单机容器引擎 | 集群容器编排平台 |
| 解决什么 | 怎么把应用打包、跑起来 | 几十上百台机器上成百上千个容器怎么调度、扩缩容、自愈、灰度发布 |
| 学习顺序 | 先学 | 后学 |
结论:Docker 是地基,K8s 是高楼。 单机部署、开发测试、小项目 → Docker + Compose 完全够用;大规模微服务集群 → 才需要上 K8s。先把 Docker 玩熟,再学 K8s 会非常轻松。
十二、总结
核心知识脉络
#mermaid-svg-OxNSWcAg5qLB6cMU{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OxNSWcAg5qLB6cMU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OxNSWcAg5qLB6cMU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OxNSWcAg5qLB6cMU .error-icon{fill:#552222;}#mermaid-svg-OxNSWcAg5qLB6cMU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OxNSWcAg5qLB6cMU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OxNSWcAg5qLB6cMU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OxNSWcAg5qLB6cMU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OxNSWcAg5qLB6cMU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OxNSWcAg5qLB6cMU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OxNSWcAg5qLB6cMU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OxNSWcAg5qLB6cMU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OxNSWcAg5qLB6cMU .marker.cross{stroke:#333333;}#mermaid-svg-OxNSWcAg5qLB6cMU svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OxNSWcAg5qLB6cMU p{margin:0;}#mermaid-svg-OxNSWcAg5qLB6cMU .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-OxNSWcAg5qLB6cMU .cluster-label text{fill:#333;}#mermaid-svg-OxNSWcAg5qLB6cMU .cluster-label span{color:#333;}#mermaid-svg-OxNSWcAg5qLB6cMU .cluster-label span p{background-color:transparent;}#mermaid-svg-OxNSWcAg5qLB6cMU .label text,#mermaid-svg-OxNSWcAg5qLB6cMU span{fill:#333;color:#333;}#mermaid-svg-OxNSWcAg5qLB6cMU .node rect,#mermaid-svg-OxNSWcAg5qLB6cMU .node circle,#mermaid-svg-OxNSWcAg5qLB6cMU .node ellipse,#mermaid-svg-OxNSWcAg5qLB6cMU .node polygon,#mermaid-svg-OxNSWcAg5qLB6cMU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OxNSWcAg5qLB6cMU .rough-node .label text,#mermaid-svg-OxNSWcAg5qLB6cMU .node .label text,#mermaid-svg-OxNSWcAg5qLB6cMU .image-shape .label,#mermaid-svg-OxNSWcAg5qLB6cMU .icon-shape .label{text-anchor:middle;}#mermaid-svg-OxNSWcAg5qLB6cMU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OxNSWcAg5qLB6cMU .rough-node .label,#mermaid-svg-OxNSWcAg5qLB6cMU .node .label,#mermaid-svg-OxNSWcAg5qLB6cMU .image-shape .label,#mermaid-svg-OxNSWcAg5qLB6cMU .icon-shape .label{text-align:center;}#mermaid-svg-OxNSWcAg5qLB6cMU .node.clickable{cursor:pointer;}#mermaid-svg-OxNSWcAg5qLB6cMU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OxNSWcAg5qLB6cMU .arrowheadPath{fill:#333333;}#mermaid-svg-OxNSWcAg5qLB6cMU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OxNSWcAg5qLB6cMU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OxNSWcAg5qLB6cMU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OxNSWcAg5qLB6cMU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OxNSWcAg5qLB6cMU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OxNSWcAg5qLB6cMU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OxNSWcAg5qLB6cMU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OxNSWcAg5qLB6cMU .cluster text{fill:#333;}#mermaid-svg-OxNSWcAg5qLB6cMU .cluster span{color:#333;}#mermaid-svg-OxNSWcAg5qLB6cMU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OxNSWcAg5qLB6cMU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OxNSWcAg5qLB6cMU rect.text{fill:none;stroke-width:0;}#mermaid-svg-OxNSWcAg5qLB6cMU .icon-shape,#mermaid-svg-OxNSWcAg5qLB6cMU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OxNSWcAg5qLB6cMU .icon-shape p,#mermaid-svg-OxNSWcAg5qLB6cMU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OxNSWcAg5qLB6cMU .icon-shape .label rect,#mermaid-svg-OxNSWcAg5qLB6cMU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OxNSWcAg5qLB6cMU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OxNSWcAg5qLB6cMU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OxNSWcAg5qLB6cMU :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Docker
基本概念
镜像 Image:只读分层模板
容器 Container:运行实例
仓库 Registry:镜像存储
Namespace 隔离 / Cgroups 限制 / UnionFS 分层
安装部署
Windows:Docker Desktop + WSL2
macOS:dmg / Homebrew
Linux:Docker Engine CE
配置镜像加速器 daemon.json
基础操作
镜像:pull / images / rmi / build / save
容器:run / ps / logs / exec / rm
参数:-d -it -p -v –name –rm -e
进阶能力
Volume 数据持久化
Dockerfile 镜像构建
多阶段构建瘦身
Network 容器互联
Compose 多服务编排
生态延伸
Harbor 私有仓库
Portainer 可视化管理
Kubernetes 集群编排
CI/CD 流水线集成
学习路线建议
| 入门 | 会用现成镜像 | 装好 Docker → 配好加速器 → pull / run / ps / logs / exec / stop / rm 练熟 |
| 上手 | 会做数据持久化和网络 | 理解 -v 三种挂载 → 会建自定义网络 → 容器名互联 |
| 进阶 | 会打包自己的应用 | 写 Dockerfile → 掌握 COPY/RUN/CMD/ENTRYPOINT → 多阶段构建瘦身 |
| 熟练 | 会编排完整项目 | 写 docker-compose.yml → 一键起 Web+DB+Cache+Nginx → 会看日志排障 |
| 高阶 | 走向云原生 | 私有仓库 Harbor → CI/CD 自动构建推送 → Kubernetes 集群编排 |
三条最重要的经验
📚 延伸学习
- Docker 官方文档:https://docs.docker.com/
- Docker Hub 镜像仓库:https://hub.docker.com/
- Dockerfile 最佳实践:https://docs.docker.com/build/building/best-practices/
- Compose 规范:https://docs.docker.com/compose/compose-file/
如果这篇文章帮你搞定了 Docker 环境,欢迎 点赞 👍 收藏 ⭐ 关注 🔔 三连支持一下!有问题评论区见,我会尽量回复~





