欢迎来到第四阶段:进阶篇。
如果说前三个阶段是让你学会“把程序跑起来”,那这个阶段就是要解决两个最核心的生产问题:“数据存哪?” 和 “怎么联网?”
先看 4.1,这是一个非常痛的领悟。
4.1 数据持久化 (Data Persistence)
1. 一个可怕的事实
还记得我们之前说的吗?容器是“胶囊公寓”。 当你删除一个容器 (docker rm),你在容器里产生的所有文件、修改的数据,都会瞬间灰飞烟灭!
场景模拟:你运行了一个 MySQL 数据库容器,辛辛苦苦存了一个月的用户数据。某天你误删了容器,或者容器崩了重装……砰!数据全没了。
为什么? 因为容器的设计初衷就是**“用完即扔”** (Disposable)。容器里的存储层是临时的。
2. 解决方案:挂载 (Mount)
为了保住数据,Docker 允许我们把宿主机(你的电脑)的一部分硬盘空间,“借”给容器用。 这样,数据其实是存在你电脑硬盘上的,容器只是通过一个“窗口”去读写它。即使容器炸了,数据还在你电脑里。
主要有两种方式,必须分清楚(面试必考,工作必用):
🅰️ 方式一:Bind Mounts (挂载目录)
- 通俗解释:“实时同步文件夹”。
- 原理:把你电脑桌面上某个具体的文件夹,直接映射到容器里的某个文件夹。
- 场景:开发代码时最常用!
- 你在电脑上改了代码 (app.py),容器里立刻同步更新,不需要每次改代码都重启容器。
🅱️ 方式二:Volumes (数据卷)
- 通俗解释:“Docker 托管的私有保险箱”。
- 原理:Docker 在硬盘深处划了一块地盘(你通常不用管在哪),专门用来存数据。
- 场景:数据库最常用!
- 运行 MySQL、Redis 时,把数据文件存在 Volume 里。安全、好管理、迁移方便。
实战演示:体验“热更新” (Bind Mount)
我们用刚才的 Python 网站来体验一下 Bind Mount 的威力。
目标:我们修改电脑上的 app.py,浏览器里的网页内容要立刻变,不需要重新构建镜像。
第一步:清理战场
先把之前跑的容器停掉删掉(如果还在跑的话):
docker rm -f my-website
(注意:这次我们用了 -f 强制删除,省去 stop 的步骤,方便吧?)
第二步:挂载启动
请确保你的终端还在 docker-demo 文件夹里(里面有 app.py)。
请根据你的系统选择命令:
💻 Windows (PowerShell):
docker run –d –p 8080:5000 –v ${PWD}:/app —name my-website my-web-app
💻 Windows (CMD) / 🍎 Mac / 🐧 Linux:
docker run -d -p 8080:5000 -v "$(pwd)":/app –name my-website my-web-app
(如果 Windows CMD 报错,把 $(pwd) 换成 %cd% 试试)
命令核心解析:-v
- -v <宿主机路径>:<容器路径>
- 这里的意思是:把我当前电脑的目录 (pwd),挂载到容器里的 /app 目录。
- 这样,容器里的 app.py 就不再是镜像里那个旧的了,而是直接读取你电脑上的这个文件!
第三步:见证奇迹
你会发现:网页内容变了! 不需要 docker build,也不需要 docker restart。这就是开发神器 Bind Mount。
小结
如果你的网页成功实现了“热更新”,我们来解密那个神秘的 -p 8080:5000 到底是在干什么(网络端口)。
我们进入 4.2。
这一节要揭开一直以来那个神秘参数 -p 的面纱,并且解答一个让无数新手撞墙的问题:“我的代码里写 localhost 为什么连不上数据库?”
4.2 容器网络 (Networking)
Docker 的网络就像一个巨大的酒店。
- 宿主机 (Host):是酒店大堂(公网/你的电脑)。
- 容器 (Container):是独立的客房。
1. 端口映射:打通“内线电话” (-p)
问题: 你在容器(客房 101)里启动了一个网页服务,监听端口 5000。 但是,如果你在电脑(大堂)打开浏览器访问 localhost:5000,是打不开的。
原因: 容器是隔离的。客房里的电话只能在客房里响,大堂听不到。
解决方案:端口映射 (-p) 你需要告诉前台:“如果有人打大堂的 8080 号电话,请转接到 101 房间的 5000 号 分机。”
命令解析:
docker run -p 8080:5000 ...
- 冒号左边 (8080):宿主机端口(你电脑对外开放的口)。
- 冒号右边 (5000):容器端口(程序里写死的口)。
常见误区:
- 如果你程序里写的是 app.run(port=3000),那你右边必须写 3000。
- 左边可以随便改(只要不冲突)。比如 -p 80:5000,那你访问 localhost 就能直接看网页,不用输端口号了(浏览器默认 80)。
2. 容器互连:最大的“坑” (Localhost Trap)
这是本节最核心的知识点,请仔细看。
场景: 你起了一个 Web 容器,又起了一个 MySQL 数据库容器。 你在 Web 代码里连接数据库,地址写的是:connect('localhost', 3306)。 结果:报错!连接失败!
为什么?
- 在 Web 容器里,localhost 指的是 Web 容器自己!
- 它会在自己这个小盒子里找 3306 端口,当然找不到(因为 MySQL 在另一个盒子里)。
正确做法:使用 Docker Network
Docker 允许你创建一个虚拟的局域网 (Bridge Network)。 只要两个容器都在同一个网络里,它们就可以通过“名字”互相访问。
原理图:
#mermaid-svg-6N54APHDqj8Zisa3{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-6N54APHDqj8Zisa3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6N54APHDqj8Zisa3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6N54APHDqj8Zisa3 .error-icon{fill:#552222;}#mermaid-svg-6N54APHDqj8Zisa3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6N54APHDqj8Zisa3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6N54APHDqj8Zisa3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6N54APHDqj8Zisa3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6N54APHDqj8Zisa3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6N54APHDqj8Zisa3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6N54APHDqj8Zisa3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6N54APHDqj8Zisa3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6N54APHDqj8Zisa3 .marker.cross{stroke:#333333;}#mermaid-svg-6N54APHDqj8Zisa3 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6N54APHDqj8Zisa3 p{margin:0;}#mermaid-svg-6N54APHDqj8Zisa3 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-6N54APHDqj8Zisa3 .cluster-label text{fill:#333;}#mermaid-svg-6N54APHDqj8Zisa3 .cluster-label span{color:#333;}#mermaid-svg-6N54APHDqj8Zisa3 .cluster-label span p{background-color:transparent;}#mermaid-svg-6N54APHDqj8Zisa3 .label text,#mermaid-svg-6N54APHDqj8Zisa3 span{fill:#333;color:#333;}#mermaid-svg-6N54APHDqj8Zisa3 .node rect,#mermaid-svg-6N54APHDqj8Zisa3 .node circle,#mermaid-svg-6N54APHDqj8Zisa3 .node ellipse,#mermaid-svg-6N54APHDqj8Zisa3 .node polygon,#mermaid-svg-6N54APHDqj8Zisa3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6N54APHDqj8Zisa3 .rough-node .label text,#mermaid-svg-6N54APHDqj8Zisa3 .node .label text,#mermaid-svg-6N54APHDqj8Zisa3 .image-shape .label,#mermaid-svg-6N54APHDqj8Zisa3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-6N54APHDqj8Zisa3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6N54APHDqj8Zisa3 .rough-node .label,#mermaid-svg-6N54APHDqj8Zisa3 .node .label,#mermaid-svg-6N54APHDqj8Zisa3 .image-shape .label,#mermaid-svg-6N54APHDqj8Zisa3 .icon-shape .label{text-align:center;}#mermaid-svg-6N54APHDqj8Zisa3 .node.clickable{cursor:pointer;}#mermaid-svg-6N54APHDqj8Zisa3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6N54APHDqj8Zisa3 .arrowheadPath{fill:#333333;}#mermaid-svg-6N54APHDqj8Zisa3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6N54APHDqj8Zisa3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6N54APHDqj8Zisa3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6N54APHDqj8Zisa3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6N54APHDqj8Zisa3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6N54APHDqj8Zisa3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6N54APHDqj8Zisa3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6N54APHDqj8Zisa3 .cluster text{fill:#333;}#mermaid-svg-6N54APHDqj8Zisa3 .cluster span{color:#333;}#mermaid-svg-6N54APHDqj8Zisa3 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-6N54APHDqj8Zisa3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6N54APHDqj8Zisa3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-6N54APHDqj8Zisa3 .icon-shape,#mermaid-svg-6N54APHDqj8Zisa3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6N54APHDqj8Zisa3 .icon-shape p,#mermaid-svg-6N54APHDqj8Zisa3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6N54APHDqj8Zisa3 .icon-shape rect,#mermaid-svg-6N54APHDqj8Zisa3 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6N54APHDqj8Zisa3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6N54APHDqj8Zisa3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6N54APHDqj8Zisa3 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
容器B
容器A
Docker网络
webapp
mysql
你只需要在代码里把 localhost 换成 mysql(对方容器的名字),Docker 就会自动通过内置的 DNS 帮你找到它的 IP 地址。
手动演示(了解原理即可)
虽然我们在下一章(Docker Compose)才会大量使用网络,但现在我们可以简单体验一下“通过名字 ping 通对方”。
1. 创建一个网络
我们要建一个专门的“聊天室”。
docker network create my-net
2. 启动两个容器加入这个网络
我们启动两个极简的 Linux (Alpine) 容器,分别叫 box1 和 box2,并把它们插到 my-net 上。
打开终端窗口 1:
docker run -it –name box1 –network my-net alpine sh
(你现在进入了 box1 内部)
打开终端窗口 2:
docker run -it –name box2 –network my-net alpine sh
(你现在进入了 box2 内部)
3. 互相 Ping (见证奇迹)
在 box1 (窗口 1) 里输入:
ping box2
你会看到类似这样的输出: PING box2 (172.18.0.3): 56 data bytes…
这意味着: 你并没有输入 IP 地址,只是喊了对方的名字 box2,Docker 就自动帮你解析到了它的 IP,并且连通了!
总结 4.2
这个时候,你可能会觉得: “这也太麻烦了吧?每次都要手动创建网络,启动容器还要敲那么长一串命令:-d -p … -v … –network … –name …,万一我有 10 个容器怎么办?”
你说得对! 这种繁琐的手工操作只适合学习原理。 在实际工作中,我们有更优雅的全自动解决方案。
准备好进入 Docker 最舒服的章节了吗? 我们要介绍大杀器 —— Docker Compose!
第五阶段:多容器编排 (Docker Compose 篇)
网硕互联帮助中心



评论前必须登录!
注册