欢迎光临
我们一直在努力

Docker(介绍、下载与基础使用)

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

传统开发运维中最令人崩溃的几个场景:

痛点场景传统做法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

物理硬件

对比维度虚拟机(VM)容器(Container)
隔离级别 硬件级(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-defaultversion 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

  • 打开官网下载页:https://www.docker.com/products/docker-desktop/
  • 点击 Download for Windows,得到 Docker Desktop Installer.exe
  • 双击运行,务必勾选 Use WSL 2 instead of Hyper-V (recommended)
  • 一路 Next → Install → 安装完成后 Restart 重启电脑
  • 启动 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。

    安装步骤:

  • 双击 .dmg,把 Docker(鲸鱼图标) 拖进 Applications 文件夹
  • 打开「应用程序」→ 双击 Docker
  • 首次启动提示「无法验证开发者」→ 系统设置 → 隐私与安全性 → 仍要打开
  • 输入密码安装特权助手(privileged helper tool)
  • 等待菜单栏鲸鱼图标停止动画,显示 Docker Desktop is running
  • 打开终端验证:docker –version
  • 🍎 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 的区别

    特性CMDENTRYPOINT
    数量限制 只有最后一条生效 只有最后一条生效
    被 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: myproject

    services:
    # ———- Web 应用 ———-
    web:
    build: # 从 Dockerfile 构建
    context: .
    dockerfile: Dockerfile
    image: myflask:1.0
    container_name: flaskweb
    restart: unlessstopped
    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:
    appnet
    healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:5000/"]
    interval: 30s
    timeout: 5s
    retries: 3

    # ———- MySQL ———-
    mysql:
    image: mysql:8.0
    container_name: projectmysql
    restart: always
    ports:
    "3306:3306"
    environment:
    MYSQL_ROOT_PASSWORD: Root@123456
    MYSQL_DATABASE: demo
    TZ: Asia/Shanghai
    volumes:
    mysqldata:/var/lib/mysql # 数据卷持久化
    ./conf/my.cnf:/etc/mysql/conf.d/my.cnf:ro # 配置文件
    command:
    charactersetserver=utf8mb4
    collationserver=utf8mb4_unicode_ci
    networks:
    appnet
    healthcheck:
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-pRoot@123456"]
    interval: 10s
    timeout: 5s
    retries: 10

    # ———- Redis ———-
    redis:
    image: redis:7alpine
    container_name: projectredis
    restart: always
    ports:
    "6379:6379"
    volumes:
    redisdata:/data
    command: ["redis-server", "–appendonly", "yes", "–requirepass", "Redis@123"]
    networks:
    appnet

    # ———- Nginx 反向代理 ———-
    nginx:
    image: nginx:1.27alpine
    container_name: projectnginx
    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:
    appnet

    # ———- 数据卷声明 ———-
    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: defaultauthenticationplugin=mysql_native_password
    restart: always
    environment:
    MYSQL_DATABASE: wordpress
    MYSQL_USER: wpuser
    MYSQL_PASSWORD: Wp@123456
    MYSQL_RANDOM_ROOT_PASSWORD: "1"
    volumes:
    dbdata:/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:
    wpdata:/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 是什么关系?该学哪个?

    DockerKubernetes(K8s)
    定位 单机容器引擎 集群容器编排平台
    解决什么 怎么把应用打包、跑起来 几十上百台机器上成百上千个容器怎么调度、扩缩容、自愈、灰度发布
    学习顺序 先学 后学

    结论: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 集群编排

    三条最重要的经验

  • 一切皆镜像:应用交付的标准单位是镜像,不是代码压缩包。Dockerfile 要进 Git 仓库,和环境一起版本化。
  • 容器是无状态的、可随时销毁的:数据一定要放到 Volume 或外部存储里,配置放到绑定挂载或环境变量里。做到「容器随时删了重建,业务毫无感知」。
  • 镜像越小越好,权限越低越好:用 alpine/slim/distroless 基础镜像 + 多阶段构建 + 非 root 用户运行,既省资源又更安全。

  • 📚 延伸学习

    • 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 环境,欢迎 点赞 👍 收藏 ⭐ 关注 🔔 三连支持一下!有问题评论区见,我会尽量回复~

    赞(0)
    未经允许不得转载:171主机测评 » Docker(介绍、下载与基础使用)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址