Docker 和 K8s:镜像、Compose、代码进不进镜像
Docker 是一款可以把程序和环境打包并运行的工具。K8s 是应用服务和服务器之间的中间层,通过 API 简化部署、扩容等运维。

Dockerfile:从操作系统到应用服务启动要做哪些事,列清楚的一份清单。


关系可以记成:
- 镜像:做容器的模具
- Dockerfile:做模具的图纸,详细列出镜像怎么造出来
Docker 容器和镜像的关系 + 挂载
- 镜像:只读模板(代码、运行时、配置),不能运行;
- 容器:镜像启动后的运行实例,镜像之上加一层可写层;多个容器共用同一个镜像。
- 挂载(volume / bind mount):把宿主机目录 / 存储挂载到容器内,实现数据持久化。容器删除,挂载卷数据还在。三种:bind 挂载(宿主机指定目录)、volume(Docker 托管)、tmpfs(内存挂载)。
挂载的三种方式:Volume / Bind Mount / tmpfs
为什么需要挂载?容器本身是临时的,删掉容器,容器内部所有写入的数据全部丢失。挂载把数据放到容器外部,用来持久化数据、共享文件、实时同步代码、存放临时内存数据。
1. Volume 数据卷(生产首选)
Docker 自己帮你在宿主机上管理一块目录(/var/lib/docker/volumes/),你不用关心它的真实路径。
Docker 托管的持久存储,生产存数据库首选,容器删除数据不丢,方便迁移备份。
2. Bind Mount 绑定挂载(开发最常用)
直接把宿主机你指定的某个目录 / 文件,映射进容器里。宿主机改文件,容器立刻看见;容器内修改,宿主机文件同步变。
宿主机指定目录直接映射,开发调试、挂载配置文件用,两边文件实时同步。
3. tmpfs 内存挂载(临时内存盘)
不写磁盘,数据放在宿主机内存里。容器停止 / 宿主机重启,数据直接全部消失;也因此读写速度极快。
临时敏感数据、高速缓存适用,容器停数据就清空,不落地磁盘。
一句话对照
| 方式 | 数据在哪 | 容器删了还在吗 | 典型场景 |
|---|---|---|---|
| Volume | Docker 托管目录 | 在 | 生产存数据库 |
| Bind Mount | 宿主机指定目录 | 看宿主机文件 | 开发调试、挂配置 |
| tmpfs | 宿主机内存 | 不在 | 临时敏感数据、高速缓存 |
docker-compose
- 单个
docker run= 开一台单独的小电脑 docker-compose= 一键启动一整套(Web + 数据库 + Redis 等),并自动把它们连好网- 一个配置文件,一条启动命令,管理一整套 Docker 服务
容器之间可以建子网。Compose 用 yml 管理多个容器:怎么创建、怎么协同。Docker 会为每个 compose 文件自动建一个子网,还可以自定义启动顺序。

常用命令
| 命令 | 作用 |
|---|---|
docker pull |
从仓库下载镜像 |
docker image |
查看已下载镜像 |
docker rmi |
删除镜像 |
docker rm |
删除容器 |
docker run 镜像名 |
用镜像创建并启动容器,每个容器有独立 ID |
docker run -d |
后台运行,不阻塞当前窗口 |
docker run -p 80:80 |
端口映射:冒号前是宿主机端口,后是容器端口。容器网络默认与宿主机隔离,不映射就访问不到容器内服务 |
docker run -v 宿主机目录:容器内目录 |
挂载卷,宿主机与容器目录绑定,用于持久化 |
docker ps |
查看进程状态(process status) |
docker start / docker stop |
启动 / 停止已有容器;之前的端口映射、挂载、环境变量不用重写 |
docker inspect |
查看端口映射、环境变量等配置 |
docker logs |
看容器日志 |
docker exec -it |
进入容器内命令行,与宿主机隔离 |
生产环境重启策略:
docker run -d --restart always:容器一停就重启(崩溃、宿主机断电等)docker run -d --restart unless-stopped:手动停过的容器不再自动重启
Docker 和 K8s 的关系
- Docker:容器引擎,负责打包镜像、本地创建 / 运行容器。
- K8s(Kubernetes):容器编排平台。
一句话:Docker 是造容器、跑容器的工具;K8s 用来管理一大堆容器,做调度、扩缩容、自愈、服务发现。
类比:Docker = 汽车;K8s = 交通调度中心
Docker 造出来的「车」,在 K8s 里不会裸跑:它们会被装进档口(Pod),再挂到稳定窗口(Service)后面。下面把这套层级和通信方式摊开。
Kubernetes

K8s 处在应用服务和服务器之间,暴露一系列 API,让部署、扩容等运维更省事。
先理清层级:Container → Pod → Service
从最小运行单元往上:
- 容器(Container):最底层的进程隔离单元,真正跑业务代码(比如一个 Java / Node 进程),共享宿主机内核。
- Pod:K8s 的最小调度单元。一个 Pod 里可以有 1 个或多个容器(业务上多数是 1 个)。每个 Pod 有自己的 Pod IP,但 Pod 是临时的——销毁、重启、扩缩容时,IP 都会变。
- Service:服务抽象。它不跑业务程序,也不是容器,角色是「稳定访问入口 + 集群内负载均衡」。
Service 和 Pod 怎么对上
Service 用**标签选择器(selector)**匹配一组 Pod,匹配到的都是它的后端实例:
- 1 个 Service 通常对应多个 Pod:生产最常见。例如用户服务 3 个副本,就是 3 个 Pod,一起挂在
user-service后面;请求打到 Service,再负载均衡到这 3 个 Pod。 - 后端 Pod 扩缩容、重启、故障替换时,Service 自动更新端点列表,对调用方透明。
- 特殊情况下,1 个 Pod 也可以同时被多个 Service 匹配。
通俗一点:
- 容器 = 后厨厨师,真正做菜(执行业务)
- Pod = 一个后厨档口(里面有一个或多个厨师)
- Service = 前台收银台 + 派单:有固定窗口地址(稳定 IP / 域名),顾客只到前台下单,前台再分给空闲档口;后厨换人,不影响前台怎么接待
服务怎么通信
集群内通信和集群外访问,都主要靠 Service + DNS。
集群内(主流):
- ClusterIP + 内部 DNS
每个 Service 有一个稳定的集群内虚拟 IP(ClusterIP);CoreDNS 还会生成域名,形如service-name.namespace.svc.cluster.local。服务之间用域名或 ClusterIP 调用,是最常用的方式。 - 按场景选 Service 类型
- Headless Service:无 ClusterIP,DNS 直接返回后端 Pod IP 列表,适合有状态服务(如数据库集群)直连
- NodePort / LoadBalancer:主要对外暴露,也可用于节点间访问
- Pod 间直连
Pod 之间可以用 Pod IP 互通,但 IP 会随重建变化,业务里不推荐直接写死。
澄清误区:镜像装的不止是依赖
Docker 镜像不止是依赖环境。镜像 = 基础操作系统 + 系统库 + 语言运行时 + 第三方依赖包 + 业务代码 / 配置,整包打在一起。
模式 A:业务代码打进镜像(COPY)
Dockerfile 里 COPY . .,代码进镜像。
- 镜像内:OS + Python + pip 依赖(如 vllm、langchain)+ RAG 业务代码
- 启动:
docker run,直接跑镜像里的代码
优点:
- 环境 + 代码一体。镜像到哪跑,就是哪一版代码,不会本地更新了线上还是旧的。
- 交付简单:只交镜像,不用另拷代码。K8s 生产多数是这种。
- 版本可控:镜像打 tag(v1.0、v1.1),本身就代表环境 + 代码版本。
缺点:
- 代码一改就要重新 build
- 镜像体积变大
模式 B:镜像只存依赖,代码用 Volume 挂进来
镜像只装 Python、依赖等,不 COPY 业务代码。启动时:
1 | docker run -v /home/myrag-code:/app my-base-env-image |
镜像里只有依赖;代码在宿主机,容器读宿主机文件。
优点:改代码不用 rebuild,重启容器就生效,适合本地调试。
生产慎用:
- 镜像和代码版本脱钩,不知道跑的是哪份代码;宿主机误改、误删,容器直接挂。
- 换机器要同步拷代码,容易漏文件。
- K8s 不推荐大规模用宿主机挂业务代码,运维麻烦。
模型权重一般用挂载,不打进镜像:几十 GB 打进镜像会爆、分发也慢。业务代码通常很小,适合打进镜像。
什么时候用哪种
- 本地开发调试:Volume 挂代码,镜像只存依赖。
- 测试、生产:业务代码打进镜像,环境 + 代码一体交付。
前后端可以拆成不同镜像:

Docker 部署常见坑
- SSE 长连接:常配 Nginx 反向代理;Nginx 默认缓冲、超时会打断 SSE。要单独改配置。本地没 Nginx,问题不易暴露。
- WeasyPrint PDF:依赖系统库和中文字体;轻量镜像常缺,Dockerfile 要额外装,否则中文乱码或直接报错。
- 持久化:容器销毁,内部数据没了。MySQL、报告、日志必须挂 Volume,漏挂就丢业务数据。
- 多副本扩展:若用进程内 EventBus、内存缓存,Docker 很容易起多副本,但内存状态不跨实例共享,硬扩容会异常,要先改架构。
共性:本地一切正常,一进 Docker 才踩坑。本地自带系统库、没有 Nginx、单进程跑,容易把这些问题盖住。




