
前言FDE系列内容总纲【大纲】FDE 前沿部署工程师学习系列教程-CSDN博客前置课程列表见文档结尾附录。阶段2·Day 41Docker 入门 — 把环境装进盒子FDE 学习系列教程 · 第二阶段 · 第 5 周 · Day 1预计时长2.5 小时 | 难度★★☆☆☆ | 前置知识WSL2 环境、Linux 基础命令、FastAPI一句话目标理解容器和虚拟机、镜像和容器的区别装好 Docker掌握容器的增删查改、端口映射和数据卷一条命令跑起第一个容器。 开场在我电脑上能跑这个千古难题上周你在 WSL 里部署 FastAPI回忆一下流程装 Python → 建 venv → pip 装依赖 → 配 MySQL → 启动服务……前前后后折腾了小半天。现在客户给了你一台全新的服务器你上去一看Python 是 3.6 的你代码用了 3.10 的语法没装 pip网络还受限MySQL 客户端版本对不上系统是 CentOS你熟的是 Ubuntu于是经典名场面上演在我电脑上明明能跑啊问题的根源就一句话代码和运行环境是分离的。代码拷过去了环境没拷过去。Docker 就是来终结这个问题的。它把你的代码 Python 运行时 所有依赖库 系统配置一起打包成一个标准盒子。这个盒子搬到任何装了 Docker 的机器上——你的笔记本、客户的服务器、云上的主机——敲同一条命令跑出来的东西一模一样。没有 Docker 有 Docker ─────────── ───────── 拷代码 → 装 Python → 装依赖 拷镜像盒子→ docker run → 装 MySQL → 配环境变量 → 祈祷 完事。盒子里啥都有开箱即跑 半天还可能翻车 30 秒环境零差异今天先把 Docker 跑起来理解几个核心概念学会操作容器这个盒子。 一、容器 vs 虚拟机轻量的秘密你可能听过虚拟机VMware、VirtualBox。容器和它都能做环境隔离但路子完全不同打个比方虚拟机像租了一整套独立的房子——自己的厨房、水电、大门什么都独立但又重又占地方容器像同一栋公寓楼里的独立房间——楼的地基和水电内核共享但每个房间各自独立、互不打扰轻得多对比项虚拟机容器启动时间几十秒~几分钟要启动整个系统秒级就是起一个进程资源占用GB 级每个带完整系统MB 级只带应用和依赖隔离级别硬件级强隔离进程级隔离共享内核一台机器能跑几个几个~十几个几十~上百个典型用途跑不同操作系统、强隔离应用部署、微服务FDE 主战场 一句话容器就是一种轻量级的虚拟机它不虚拟硬件只把应用和它需要的环境打包隔离起来。快、小、可移植是它统治现代部署的原因。 二、镜像 vs 容器类和对象的关系这是 Docker 最核心的一对概念用你学过的 Python 一类比就通镜像Image ≈ 类class —— 只读的模板/图纸 容器Container≈ 对象instance —— 镜像跑起来后的活实例 class Dog: ──────────► docker pull python:3.12 def bark() 下载镜像拿到图纸 dog1 Dog() ──────────► docker run python:3.12 dog2 Dog() 从镜像创建容器按图纸造实例 同一个镜像可以同时跑很多容器注docker commit是 Docker 的一个命令用于将一个容器的当前状态保存为一个新的镜像。记住这个对应关系后面所有命令都围绕这两样东西操作镜像Image容器Container查看列表docker imagesdocker ps下载/创建docker pulldocker run/docker create删除docker rmidocker rm生命周期不动它就一直在run → start → stop → rm️ 三、安装 Docker DesktopWindows WSL2上周你已经装好了 WSL2这是 Docker Desktop 在 Windows 上的最佳后端。安装步骤打开官网下载页Docker Desktop: The #1 Containerization Tool for Developers | Docker点Download for Windows拿到Docker Desktop Installer.exe双击运行安装选项里保持勾选 Use WSL 2 instead of Hyper-V默认就是勾的一路 OK装完会提示注销/重启电脑照做重启后启动Docker Desktop桌面或开始菜单第一次启动需要等 1 分钟左右初始化看到左下角或顶部小鲸鱼图标变成绿色稳定状态就说明引擎就绪了验证安装打开WSL 终端Ubuntu或 PowerShell输入docker --version # Docker version 26.x.x, build xxxxxxx docker compose version # Docker Compose version v2.x.xdocker compose是 Docker 官方提供的多容器应用编排工具Compose V2作为 Docker CLI 插件使用命令用空格形式docker compose。它通过一个 YAML 文件通常叫compose.yaml或docker-compose.yml声明式地定义和运行多个容器一条命令即可启动整套服务。跑官方的Hello World验证整条链路docker run hello-worldDocker 会先下载hello-world镜像然后运行它。看到下面这段恭喜安装完全成功Unable to find image hello-world:latest locally latest: Pulling from library/hello-world ... Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. ...⚠️常见启动报错排查提示WSL 2 installation is incomplete管理员 PowerShell 跑wsl --update然后重启提示虚拟化没开重启进 BIOS开启Intel VT-x/AMD-V (SVM)小鲸鱼一直转圈Settings → Resources → WSL Integration确认给你的 Ubuntu 发行版打开了开关拉镜像特别慢Settings → Docker Engine配置国内镜像加速registry-mirrors或挂网络代理️ 四、容器增删查改全流程光跑 hello-world 不过瘾来玩一个真实的镜像——Nginx一个 Web 服务器明天/周四会细讲今天先用它练容器操作。第 1 步拉取镜像# pull 从镜像仓库默认 Docker Hub下载镜像到本地 docker pull nginx:alpine # alpine 是 Alpine Linux 精简版的标签整个镜像只有几 MB # 不加标签默认拉 :latest最新版生产环境建议固定版本号镜像名的完整格式 nginx : alpine └─┬─┘ └─┬─┘ 镜像名仓库名 标签 tag通常是版本号 省略用户/官方源时 latest 最新不稳定别在生产用 默认从官方 library 拉查看本地已有镜像docker images # REPOSITORY TAG IMAGE ID CREATED SIZE # nginx alpine 605c9e0c4f92 2 weeks ago 42.6MB # hello-world latest d2c94e258dcb 6 months ago 13.3kB第 2 步运行容器# --name 给容器起名字不写会随机分配一个像 sharp_einstein 的名字 # -d 后台运行detached不占用当前终端 # -p 8080:80 端口映射下一节细讲 docker run -d --name my-nginx -p 8080:80 nginx:alpine # 输出一长串容器 ID容器跑起来后浏览器打开 **http://localhost:8080**会看到 Nginx 的欢迎页——一个 Web 服务就这么起来了你本机根本没装 Nginx。这就是容器的魔力。第 3 步查看容器docker ps # 查看【正在运行】的容器 # CONTAINER ID IMAGE ... STATUS PORTS NAMES # a1b2c3d4e5f6 nginx:alpine ... Up 5 seconds 0.0.0.0:8080-80/tcp my-nginx docker ps -a # 查看【所有】容器包括已经停止的docker ps各列含义CONTAINER ID 容器ID简写前12位 IMAGE 用的哪个镜像 COMMAND 容器启动执行的命令 CREATED 何时创建 STATUS 状态Up 运行/Exited 退出/Paused 暂停 PORTS 端口映射 NAMES 容器名查看容器日志docker logs my-nginx # 看历史日志 docker logs -f my-nginx # follow 实时追踪和 tail -f 一样CtrlC 退出查看 docker logs --tail 10 my-nginx # 只看最后 10 行查看容器内部细节配置、网络、挂载等元信息docker inspect my-nginx # 输出一大段 JSON docker inspect --format {{.NetworkSettings.IPAddress}} my-nginx # 只取容器内网IP第 4 步进入容器内部容器本质是一个隔离的小 Linux你可以钻进去看看# -i 保持输入打开interactive -t 分配一个终端tty # bash 是要在容器里执行的 shellalpine 精简版可能只有 sh docker exec -it my-nginx sh进去后你会发现自己身处一个迷你 Linux 里/ # hostname # 显示的是容器ID / # ls /usr/share/nginx/html/ # Nginx 欢迎页就在这 / # exit # 退出容器容器继续在后台运行不会停docker exec是排障神器服务在容器里行为异常时exec进去看文件、看进程、手动执行命令。注意它要求容器正在运行。第 5 步停止 / 启动 / 删除# 停止优雅发停止信号 docker stop my-nginx # 已停止的容器可以重新启动配置都还在 docker start my-nginx # 重启 docker restart my-nginx # 删除容器必须先停止或加 -f 强制删运行中的 docker stop my-nginx docker rm my-nginx # 一键清理所有【已停止】的容器大扫除常用 docker container prune删除镜像docker rmi nginx:alpine # ⚠️ 如果还有容器哪怕是已停止的基于这个镜像会删不掉 # 报错image is being used by stopped container xxx # 先 docker rm 那个容器再 rmi 镜像生命周期回顾 pull 镜像 ──► run 创建并启动 ──► ps 运行中 │ │ │ stop ▼ │ 已停止Exited │ │ │ start┘ 回到运行 │ │ └────────────── rm ▼ 容器删除没了️ 五、端口映射 -p让外面能访问容器里的服务刚才的命令里有个-p 8080:80这是初学者最容易迷糊的地方单独讲透。容器是网络隔离的——容器里 Nginx 监听 80 端口但这个 80 是容器房间内部的端口你的 Windows、局域网其他机器默认都摸不到。-p就是在宿主机和容器之间开一扇门docker run -d -p 8080:80 --name my-nginx nginx:alpine └─┬─┘ │ 宿主机端口 : 容器内端口 你访问的 服务实际监听的 你的浏览器 Docker 容器内部 http://localhost:8080 ──────────────► Nginx :80 ▲ -p 映射 ▲ └── 你敲这个 └── 服务长在这# 把宿主机 8080 → 容器 80 docker run -d -p 8080:80 --name web1 nginx:alpine # 浏览器访问 http://localhost:8080 # 换一个宿主机端口再起一个同样的容器实例一个镜像多个容器 docker run -d -p 8081:80 --name web2 nginx:alpine # 浏览器访问 http://localhost:8081两个容器互不干扰 # 直接用宿主机 80 端口需要管理员权限且 80 没被占用 docker run -d -p 80:80 --name web80 nginx:alpine⚠️左右别搞反-p 宿主机端口:容器内端口。容器内端口由镜像里的程序决定Nginx 默认 80你的 FastAPI 是 8000不能瞎写宿主机端口是你自己选的对外门面只要没被占用随便挑1024 以上常见报错Bind for 0.0.0.0:8080 failed: port is already allocated宿主机 8080 被占了docker ps找出来停掉或者换个端口。️ 六、数据卷 -v容器删了数据还在容器有个致命特性容器内部的文件随容器删除一起消失。docker exec web1 sh -c echo 我的修改 /usr/share/nginx/html/test.txt docker rm -f web1 # 容器一删 docker run -d -p 8080:80 --name web1 nginx:alpine # 重建一个 # test.txt 没了容器回到镜像的初始状态这对无状态的应用无所谓但数据库容器这么玩就完了——容器一删客户数据全没。数据卷Volume就是把容器内的某个目录映射到宿主机上让数据活在容器之外# 语法-v 宿主机路径:容器内路径 mkdir -p ~/docker-data/web echo h1这是我自己的页面/h1 ~/docker-data/web/index.html docker run -d --name web-volume \ -p 8082:80 \ -v ~/docker-data/web:/usr/share/nginx/html \ nginx:alpine参数列表参数作用-d后台运行detach--name web-volume给容器命名方便后续stop/rm/logs引用-p 8082:80端口映射宿主机8082→ 容器80-v ~/docker-data/web:/usr/share/nginx/html绑定挂载宿主机~/docker-data/web挂到容器内 Nginx 默认站点根目录浏览器打开 http://localhost:8082看到的是你自己写的页面而不是 Nginx 默认欢迎页。宿主机文件系统 容器内部 ~/docker-data/web/ /usr/share/nginx/html/ ├── index.html ◄同步► ├── index.html └── 你在这边改 └── 容器里看到的 改宿主机文件 → 容器立刻可见双向同步 docker rm 删掉容器 → 宿主机目录纹丝不动数据还在验证数据持久性docker rm -f web-volume # 宿主机上的 ~/docker-data/web/index.html 依然在 ls ~/docker-data/web/ # index.html # 用同样的挂载再建一个新容器数据复活 docker run -d -p 8082:80 -v ~/docker-data/web:/usr/share/nginx/html --name web-volume2 nginx:alpine两种挂载方式知道区别即可方式写法特点用途绑定挂载bind mount-v /宿主机/绝对路径:/容器路径直接对应宿主机真实目录你能直接编辑开发时挂代码、挂配置今天用的数据卷named volume-v 卷名:/容器路径由 Docker 统一管理存在 Docker 专用区域数据库数据持久化明天/后天用# 命名卷Docker 帮你保管不用关心物理位置 docker volume create mydata docker run -d -v mydata:/var/lib/mysql mysql:8.0 docker volume ls # 查看所有卷 docker volume inspect mydata # 看物理位置FDE 铁律凡是需要持久化的数据数据库、上传的文件、配置必须挂卷。容器本身随时可以删了重建牛的地方正在于此数据则必须活在容器外面。 七、docker run 常用参数速查今天命令不少整理一张表以后docker run照着挑参数参数全称/含义例子-d后台运行docker run -d nginx--name容器命名--name my-api-p端口映射-p 8000:8000-v数据卷/目录挂载-v ./data:/app/data-it交互模式 终端docker run -it python:3.12 bash-e设置环境变量-e MYSQL_ROOT_PASSWORD123--rm容器退出后自动删除一次性容器docker run --rm python:3.12 python -c print(1)--network指定网络后天 Compose 会自动处理--restart重启策略--restart unless-stopped挂了自动拉起--restart unless-stopped强烈建议给常驻服务加上容器异常退出会自动重启服务器重启后也会自动拉起。它和你上周手写的健康检查脚本目标一致但这是 Docker 内置的能力。 本课小结知识点一句话记住容器解决什么代码环境打包消除在我电脑上能跑容器 vs 虚拟机容器共享内核、秒级启动、MB 级体积镜像 vs 容器镜像是只读模板类容器是运行实例对象docker pull下载镜像docker run从镜像创建并启动容器docker ps / ps -a运行中的 / 所有容器docker logs -f实时看容器日志docker exec -it进入运行中的容器docker stop/start/rm停 / 启 / 删容器docker images / rmi看镜像 / 删镜像-p 宿主:容器端口映射方向别搞反-v 宿主:容器挂载数据容器删了数据在--restart容器挂了自动重启 核心认知记住两个分离——镜像和容器分离一个镜像可以跑出任意多个容器容器是一次性的、随时可重建的容器和数据分离容器本身无状态、随便删重要数据必须通过-v放到容器外面理解了这两点你就抓住了 Docker 思维的精髓容器是牲口不是宠物——病了不要治直接杀掉重建一个。 课后练习练习 1跑通 Python 容器约 20 分钟docker pull python:3.12-slim用docker images确认用一条命令在容器里执行 Python 代码docker run --rm python:3.12-slim python -c print(你好容器里的 Python)观察--rm让容器执行完自动消失docker ps -a里找不到它用-it进入容器的 Python 交互环境docker run -it python:3.12-slim python算几道题后exit()练习 2Nginx 容器五连操作约 25 分钟后台启动 Nginx映射到宿主机 8088 端口命名lab-nginx浏览器访问 http://localhost:8088 看到欢迎页docker exec进容器找到欢迎页文件路径并cat出来用docker logs查看访问日志然后刷新几次浏览器再看停止、重启、最终删除该容器练习 3数据卷持久化验证约 20 分钟在~/docker-data/site放一个自己的index.html用-v挂载启动 Nginx浏览器确认显示你的页面docker rm -f强删容器检查宿主机文件还在不在再用相同挂载启动一个新容器换名字确认页面复活用一句话回答为什么数据库容器必须挂卷而 Nginx 这种 Web 服务器可以不挂 下节预告今天你跑的都是别人做好的镜像nginx、python相当于住精装房。但 FDE 在客户现场交付的是自己的应用——明天你要学会自己盖房子写Dockerfile把你第 2 周的 FastAPI 项目连同 Python 环境和所有依赖打包成一个专属镜像。明天还会搞懂一个让构建速度快 10 倍的关键机制——层缓存以及为什么 Dockerfile 里先拷 requirements.txt 再拷代码是个专业细节。明天见附录前置课程列表阶段一认知启蒙AI 认知与 FDE 角色AI 认知【FDE系列】阶段1Day 1AI 层级关系 — 四个嵌套的圈-CSDN博客【FDE系列】阶段1Day 2AI 三阶段发展史 — 会认 → 会判断 → 会创造-CSDN博客【FDE系列】阶段1Day 3符号 AI vs 机器学习 — 两条路线的本质区别-CSDN博客【FDE系列】阶段1Day 4Transformer 的历史意义 — 2017 年的分水岭-CSDN博客【FDE系列】阶段1Day 5本周复习与自测 — 检验你的 AI 认知地基-CSDN博客【FDE系列】阶段1Day 6Transformer 架构 — 一张图纸盖出千千万万栋楼-CSDN博客【FDE系列】阶段1Day 7LLM 本质 — 文字接龙机器-CSDN博客【FDE系列】阶段1Day 8Token — 模型眼中的最小单位-CSDN博客【FDE系列】阶段1Day 9AI 幻觉 — 为什么会一本正经地胡说八道-CSDN博客【FDE系列】阶段1Day 10上下文窗口 — 模型的记忆力上限 本周复习-CSDN博客【FDE系列】阶段1Day 11Prompt — 给模型立规矩-CSDN博客【FDE系列】阶段1Day 12Memory — 让模型记住上下文【FDE系列】阶段1Day 13RAG — 给模型配图书管理员-CSDN博客【FDE系列】阶段1Day 14Tool Use — 让模型动手操作-CSDN博客【FDE系列】阶段1Day 15MCP — 统一的工具接口标准 第三周复习-CSDN博客FDE 基础概念【FDE系列】阶段1Day 16什么是 FDE — 把 AI 变成客户结果的人-CSDN博客【FDE系列】阶段1Day 17FDE vs 传统实施 — 三大本质区别-CSDN博客【FDE系列】阶段1Day 18FDE 三重身份 C6 胜任力模型-CSDN博客【FDE系列】阶段1Day 19七阶段行动路径 行业经验的价值-CSDN博客【FDE系列】阶段1Day 20阶段总结与产出物 — 第一阶段收官-CSDN博客阶段二技术地基Python FastAPI SQL Docker API 集成Python基础【FDE系列】阶段2Day 21Python 环境搭建 — 写出你的第一行代码-CSDN博客【FDE系列】阶段2Day 22变量、数据类型、条件判断 — Python 的“记忆“和“判断“-CSDN博客【FDE系列】阶段2Day 23循环与函数 — 让代码跑 100 遍、把逻辑打包复用-CSDN博客【FDE系列】阶段2Day 24数据结构 — 列表、字典、集合、元组-CSDN博客【FDE系列】阶段2Day 25文件读写与 JSON — 让程序连通外部数据第一周收官-CSDN博客【FDE系列】阶段2Day 26模块化编程 — 把代码拆成“抽屉柜“-CSDN博客【FDE系列】阶段2Day 27异常处理与日志 — 让程序“摔不烂、查得到“-CSDN博客FastAPI入门到进阶【FDE系列】阶段2Day 28FastAPI 入门 — 把你的函数变成 API 服务-CSDN博客【FDE系列】阶段2Day 29FastAPI 进阶 — Pydantic 模型与完整 CRUD 实战-CSDN博客【FDE系列】阶段2Day 30生产代码规范 — 测试、类型注解、配置管理第二周收官-CSDN博客SQL基础【FDE系列】阶段2Day 31SQL 基础 — 增删改查一把梭-CSDN博客【FDE系列】阶段2Day 32多表查询 — JOIN 与聚合-CSDN博客【FDE系列】阶段2Day 33进阶查询 — 窗口函数与 CTE-CSDN博客【FDE系列】阶段2Day 34数据清洗 — 把脏数据捋干净-CSDN博客【FDE系列】阶段2Day 35Python SQL — 工单接入 MySQL 本周收官-CSDN博客Linux基础【FDE系列】阶段2Day 36Linux 入门与文件操作 — 扔掉鼠标的第一天-CSDN博客【FDE系列】阶段2Day 37权限、进程与文本三剑客-CSDN博客【FDE系列】阶段2Day 38Shell 脚本 — 把命令串起来自动跑-CSDN博客【FDE系列】阶段2Day 39Linux 综合实战 — 让服务无人值守-CSDN博客【FDE系列】阶段2Day 40Shell 进阶 — 生产级脚本与本周收官-CSDN博客