云计算百科
云计算领域专业知识百科平台

第四阶段:数据与网络 (进阶篇)

欢迎来到第四阶段:进阶篇。

如果说前三个阶段是让你学会“把程序跑起来”,那这个阶段就是要解决两个最核心的生产问题:“数据存哪?” 和 “怎么联网?”

先看 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 就不再是镜像里那个旧的了,而是直接读取你电脑上的这个文件!
第三步:见证奇迹
  • 打开浏览器访问 http://localhost:8080,你应该看到之前的 Hello 内容。
  • 不要关浏览器,也不要动 Docker。
  • 打开你的代码编辑器,修改电脑上的 app.py。 把: return '<h1>Hello! 这是我用 Docker 构建的第一个网站!🎉</h1>' 改成: return '<h1>哇!我修改了代码,网页直接变了!🔥</h1>'
  • 保存文件。
  • 回到浏览器,刷新页面。
  • 你会发现:网页内容变了! 不需要 docker build,也不需要 docker restart。这就是开发神器 Bind Mount。


    小结

  • 容器里的数据是临时的,删了就没了。
  • Bind Mount (-v 路径:路径):适合代码开发,宿主机和容器实时同步。
  • Volume:适合存数据库文件,交给 Docker 管理(后面讲数据库实战会用到)。

  • 如果你的网页成功实现了“热更新”,我们来解密那个神秘的 -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

  • 对外交流:用 -p 宿主机端口:容器端口。
  • 对内交流:不要用 localhost!要创建一个网络 (docker network create),然后用容器名字互相访问。

  • 这个时候,你可能会觉得: “这也太麻烦了吧?每次都要手动创建网络,启动容器还要敲那么长一串命令:-d -p … -v … –network … –name …,万一我有 10 个容器怎么办?”

    你说得对! 这种繁琐的手工操作只适合学习原理。 在实际工作中,我们有更优雅的全自动解决方案。

    准备好进入 Docker 最舒服的章节了吗? 我们要介绍大杀器 —— Docker Compose!


    第五阶段:多容器编排 (Docker Compose 篇)

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 第四阶段:数据与网络 (进阶篇)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!