一、Docker 到底解决了什么问题
场景:博客部署
没有 Docker 时,部署博客要这样做:
- 服务器上装 Python 3.11 + pip + 所有依赖(fastapi、uvicorn、sqlalchemy...)
- 服务器上装 Node.js + npm + 所有依赖(nuxt、tailwindcss、vue...)
- 配置 Nginx 反向代理
- 用 systemd 管理服务
- 写部署脚本把代码传上去
问题来了:
- 你换一台服务器,以上步骤全部重来一遍
- 服务器系统升级了,依赖可能不兼容
- 多个项目跑在同一台服务器上,Python 版本冲突怎么办?
- "我本地跑得好好的,服务器上怎么就挂了?"
有了 Docker 之后:
Dockerfile 里已经写死了:用什么 Python 版本、装什么依赖、怎么启动。换一台服务器?docker compose up -d,完事。
三个核心好处
| 好处 | 没有 Docker | 有 Docker |
|---|---|---|
| 环境隔离 | 所有项目共享服务器的 Python、Node.js,版本冲突 | 每个容器有自己的环境,互不影响 |
| 可移植 | 换服务器要重新装环境 | 换服务器只要 Docker + 镜像,一条命令启动 |
| 一致性 | "我本地能跑"是经典借口 | Dockerfile 写死环境,本地和服务器完全一样 |
可移植性的实际场景
只有一台服务器的情况下是感受不到的。但在实际工作里:
- 项目要部署到 3 台服务器(测试环境、预发布、生产环境)
- 没有 Docker:3 台服务器分别装环境,每台都可能出问题
- 有 Docker:把镜像推送到仓库,3 台服务器各拉一份跑起来,环境 100% 一样
二、容器和源码的关系
实验验证
在学习服务器上做了这个实验:
# 1. 确认博客正常运行
curl -I localhost:3000 # 返回 200 OK
curl localhost:8001/api/articles # 返回正常 JSON
# 2. 删除整个源码目录
rm -rf ~/博客项目目录
# 3. 再次访问——容器还在跑,博客正常
curl -I localhost:3000 # 依然 200 OK
curl localhost:8001/api/articles # 依然正常
结论:删了源码,容器不受影响。
为什么?
构建阶段:源码 + Dockerfile → docker build → 镜像(包含代码+依赖+环境)
运行阶段:镜像 → docker run → 容器(里面已经有代码了,不需要外面的源码)
镜像是一次性打包好的,容器运行时不依赖外面的文件。就像你把菜做好装进饭盒,饭盒里的菜不会因为你把厨房拆了就消失。
源码还是需要的
虽然删了源码容器还能跑,但不会真的删源码——源码在 Git 仓库里。源码的作用是:
- 代码有 bug 要改
- 功能要更新
- 重新构建镜像时需要
三、博客更新流程
场景:改了博客代码,怎么更新服务器?
没有 Docker 时:
# 本地改完代码
git push
# 服务器上
ssh 用户名@你的服务器地址
cd ~/blog
git pull
# 重启服务
systemctl restart blog-api
systemctl restart blog-web
有 Docker 时:
# 服务器上
cd ~/项目目录
git pull # 拉最新代码
docker compose up -d --build # 重新构建镜像并启动
两条命令。--build 会重新执行 Dockerfile,用新代码构建新镜像,然后自动替换旧容器。
更新流程图
改了代码 → git push → 服务器 git pull → docker compose up -d --build
↓
重新构建镜像(用新代码)
↓
停掉旧容器 → 启动新容器
四、镜像仓库(Image Registry)
是什么
镜像仓库就是存 Docker 镜像的网盘。你构建好的镜像推送到仓库,其他服务器从仓库拉取。
类比:
- GitHub 存代码
- Docker Hub(镜像仓库)存镜像
常见的镜像仓库
| 仓库 | 说明 |
|---|---|
| Docker Hub | 官方公共仓库,免费(公开仓库) |
| 阿里云容器镜像服务 | 国内快,免费 |
| 腾讯云容器镜像服务 | 国内快,免费 |
| GitHub Container Registry | GitHub 自带的 |
| 私有仓库 | 公司内部搭的(Harbor) |
什么时候需要
| 场景 | 需不需要镜像仓库 |
|---|---|
| 只有一台服务器 | 不需要,本地构建就行 |
| 多台服务器部署 | 需要,不然每台都要构建一次 |
| 团队协作 | 需要,大家共用一份镜像 |
| 生产环境 | 建议用,保证镜像一致性 |
五、镜像仓库工作流程
完整流程
1. 本地构建镜像
docker build -t blog-api .
2. 给镜像打标签(加上仓库地址)
docker tag blog-api:latest registry.cn-hangzhou.aliyuncs.com/你的命名空间/blog-api:v1
3. 推送到仓库
docker push registry.cn-hangzhou.aliyuncs.com/你的命名空间/blog-api:v1
4. 其他服务器拉取
docker pull registry.cn-hangzhou.aliyuncs.com/你的命名空间/blog-api:v1
5. 其他服务器运行
docker run -d -p 8001:8001 registry.cn-hangzhou.aliyuncs.com/你的命名空间/blog-api:v1
标签名的格式
registry地址/命名空间/镜像名:标签
例:registry.cn-hangzhou.aliyuncs.com/你的命名空间/blog-api:v1
registry.cn-hangzhou.aliyuncs.com:阿里云镜像仓库地址你的命名空间:你的命名空间(相当于文件夹)blog-api:镜像名v1:版本标签(不写默认 latest)
docker-compose 用仓库镜像
如果镜像已经推送到仓库,docker-compose.yml 可以改成:
services:
backend:
image: registry.cn-hangzhou.aliyuncs.com/你的命名空间/blog-api:v1 # 用仓库镜像,不用 build
ports:
- "8001:8001"
volumes:
- ./data:/app/data
restart: always
frontend:
image: registry.cn-hangzhou.aliyuncs.com/你的命名空间/blog-web:v1
ports:
- "3000:3000"
depends_on:
- backend
restart: always
image: 替代 build:,直接从仓库拉取镜像,不用本地构建。
六、部署方案演进
当前架构
学习服务器(测试环境) 生产服务器
Docker 容器化 直接部署(systemd)
用于学习和测试 线上运行
未来理想架构(生产也容器化)
你改代码 → git push
↓
镜像仓库(阿里云/腾讯云)
↓
┌───────┴───────┐
↓ ↓
学习服务器 生产服务器
git pull git pull
docker compose docker compose
up -d --build up -d --build
实际操作步骤
- 在学习服务器上构建镜像并推送到仓库
- 在生产服务器上拉取镜像
- 生产服务器也用 docker-compose 管理
七、镜像标签策略
为什么要打标签
latest标签:latest 不是固定版本,每次 push 会覆盖- 版本标签:
v1、v2、20260705,方便回滚
推荐的标签命名
| 标签 | 含义 |
|---|---|
v1 v2 v3 |
语义化版本 |
20260705 |
日期标签 |
commit hash |
git 提交哈希(如 a1b2c3d) |
回滚操作
如果新版本有 bug,想回到上一个版本:
# 用旧标签的镜像启动
docker run -d -p 8001:8001 registry.cn-hangzhou.aliyuncs.com/你的命名空间/blog-api:v1
八、常见问题
Q:Docker 容器删了数据还在吗?
看情况。如果用了卷挂载(-v),数据存在宿主机上,容器删了数据还在。如果没用卷,数据跟着容器一起没了。
Q:Docker 和虚拟机的区别?
Docker 容器共享宿主机内核,启动快(秒级)、占用资源少(MB级)。虚拟机有独立内核,启动慢(分钟级)、占用资源多(GB级)。Docker 适合微服务和快速部署,虚拟机适合需要强隔离的场景。
Q:Dockerfile 的分层缓存是什么?
Dockerfile 每条指令创建一层,Docker 会缓存每一层。如果某层没变化就用缓存,不重新构建。所以要把不常变的指令(如安装依赖)放前面,常变的(如复制代码)放后面。
Q:Docker 镜像怎么分发?
推送到镜像仓库(Docker Hub、阿里云等),其他服务器从仓库拉取。用 docker tag 给镜像打上仓库地址标签,然后 docker push。
Q:容器挂了怎么办?
docker-compose.yml 里配置 restart: always,容器挂了 Docker 自动重启。如果是 OOM(内存不足)被系统杀的,需要排查内存问题。
Q:Docker 数据持久化怎么做?
用 -v 参数挂载卷:docker run -v 宿主机路径:容器路径。数据存在宿主机上,容器删了数据还在。也可以用命名卷让 Docker 管理。
