第 5 章 · 03 网络存储与生产实践


文档摘要

第 5 章 · 03 网络存储与生产实践 单容器是"能跑的单元",多容器协作才是"能用的系统"。本节补齐容器化的最后三块拼图:网络(容器如何互联、如何对外暴露)、存储(数据如何不随容器消失)、编排(多容器应用如何一次拉起)。最后收束到生产实践:多阶段构建、安全铁律、Rootless 与 OCI 标准。 学习目标 理解 CNM 与 CNI 两大网络标准及 Docker 的 libnetwork 实现 掌握端口映射与容器间通信的基本手段 区分 volume、bind mount、tmpfs 三种挂载的适用场景 会用 Docker Compose 定义并运行多容器应用 掌握多阶段构建与容器安全五条铁律 了解 Rootless 容器与 OCI 标准的意义 一、网络标准:CNM 与 CNI

第 5 章 · 03 网络存储与生产实践

单容器是"能跑的单元",多容器协作才是"能用的系统"。本节补齐容器化的最后三块拼图:网络(容器如何互联、如何对外暴露)、存储(数据如何不随容器消失)、编排(多容器应用如何一次拉起)。最后收束到生产实践:多阶段构建、安全铁律、Rootless 与 OCI 标准。

学习目标

  • 理解 CNM 与 CNI 两大网络标准及 Docker 的 libnetwork 实现
  • 掌握端口映射与容器间通信的基本手段
  • 区分 volume、bind mount、tmpfs 三种挂载的适用场景
  • 会用 Docker Compose 定义并运行多容器应用
  • 掌握多阶段构建与容器安全五条铁律
  • 了解 Rootless 容器与 OCI 标准的意义

一、网络标准:CNM 与 CNI

容器网络有两个事实标准,各据一方:

CNM(Container Network Model):Docker 采用的模型,需要分布式键值存储(如 etcd)保存网络配置。Docker 的实现名为 libnetwork(Go 编写),围绕三个核心概念:Networks(交换机的软件实现,分组隔离一组端点)、Endpoints(虚拟网络接口,建立连接)、Sandboxes(隔离的网络栈:接口、路由表、端口)。关键规则:一个 endpoint 只能连接一个网络——容器要连多个网络就需要多个 endpoint。

CNI(Container Network Interface):Kubernetes 采用的模型,网络配置以 JSON 格式描述,由 CNI 插件(Calico、Cilium 等)实现。K8s 场景下 CNI 插件通常以 DaemonSet 形态部署在每个节点上——这是第 6 章要展开的话题,本节记住"Docker 用 CNM,K8s 用 CNI"即可。

二、端口映射与容器间通信

容器默认有自己的网络命名空间,宿主机访问不到容器 IP。对外暴露服务用端口映射:

docker container run -d --name apache1 -p 8080:8080 httpd curl 127.0.0.1:8080

-p 宿主机端口:容器端口。容器间通信则优先用自定义网络:创建自定义 bridge 网络后,容器间可通过服务名/容器名直接解析访问,无需暴露端口——这比"共享宿主网络"更安全、更清晰。生产容器应尽量"私有网络内互联,只把入口暴露出来"。

三、存储三兄弟:数据不随容器消失

容器文件系统是临时的(ephemeral):容器删除,可写层连同数据一起消失。持久化有三条路:

方式 机制 适用场景
volume(数据卷) 由引擎管理的目录(如 /var/lib/docker/volumes),docker volume create + 挂载 数据库等关键数据,首选
bind mount 直接映射宿主目录:-v /host/path:/container/path 开发时挂代码、共享宿主配置
tmpfs 内存中的临时文件系统 敏感数据/临时缓存,随容器消失

volume 与 bind mount 的取舍:volume 由引擎管理、可命名复用、跨机器迁移方便,是正式数据的默认选择;bind mount 依赖宿主路径,适合开发调试与宿主配置注入。无论哪种,应用都不应把持久数据写进容器可写层——既随容器消失,性能也不好。

四、Docker Compose:多容器应用的编排

Compose 用一份 YAML 定义多容器应用,一条命令全部拉起。典型例子是 ELK 栈:elasticsearch、logstash、kibana 各自独立容器,靠 Compose 作为一个整体部署。

services: web: build: . ports: - "8080:80" depends_on: - db db: image: postgres:16 volumes: - db_data:/var/lib/postgresql/data volumes: db_data:

常用命令:docker-compose up(创建并启动)、down(停止并清理)、ps、logs、exec。Compose 的价值不在"多",而在"可复现"——整组服务的拓扑写进代码,任何人一条命令拉起同一套环境。

五、多阶段构建:把小镜像还给你

镜像优化的黄金手段。核心思想:把构建过程拆成多个阶段,构建阶段(需要编译器、大量依赖)只负责产出,运行阶段只拿走最终产物:

FROM golang:1.21 AS builder WORKDIR /app COPY . . RUN go build -o myapp FROM alpine:3.19 COPY --from=builder /app/myapp /usr/local/bin/myapp ENTRYPOINT ["myapp"]

传统"单 Dockerfile 先构建再手动删多余内容"的做法有两个问题:需要精确知道删什么(很难)、删除动作本身产生不必要的层。多阶段构建用 COPY --from=builder 只复制产物,最终镜像不含编译器与源码,尺寸大幅缩小。镜像优化的其他手段:用 && 合并 RUN 减少层数、选更小的基础镜像(alpine/distroless)、安装后清理包管理器缓存、FROM 指定 tag。

六、安全五条铁律

容器安全最佳实践浓缩为五条,前两条直接对标"最小权限":

  1. 容器内只装必要包——攻击面最小化;
  2. 尽可能不以 root 运行容器(USER 指令指定普通用户);
  3. 不要挂载 Docker daemon 的 socket 进容器——拿到 socket 等于拿到宿主机 root;
  4. 文件系统与卷设为只读;
  5. 绝不用 --privileged 运行容器——它绕过了所有隔离。

补充两条:生产环境 client 与 daemon 间通信必须走 TLS(拒绝不安全 HTTP);容器也可能引发内核恐慌拖垮宿主——上述五条同时就是内核恐慌防护。

七、Rootless 与 OCI:两个方向的标准

Rootless 容器解决"运行容器需要 root"的历史问题:容器在 user namespace 中"看起来像 root",实际以普通用户权限执行——即使攻击者逃逸到宿主,因为不是真 root 也做不了太多。代价是:不能绑定 1024 以下端口、不能共享 rootful 用户的镜像、部分命令不可用(mount、podman stats 等)。网络由 slirp 管理(tap 设备 + 父命名空间转发),文件系统用 FUSE-OverlayFS 驱动。

OCI(Open Container Initiative):2015 年成立的开放治理组织,负责标准化容器——发布了两个规范:image-spec(镜像格式)与 runtime-spec(运行时规范)。OCI 容器必须支持的操作:Create、Kill、Delete、Start、Query State。runc 正是 runtime-spec 的参考实现。OCI 的意义在于:镜像和运行时不再属于任何一家公司,容器生态因此百花齐放(Docker、Podman、containerd、CRI-O 互操作)。

小结

本节完成了容器化的落地闭环:网络层面 CNM/CNI 二选一、端口映射与自定义网络各司其职;存储层面 volume/bind mount/tmpfs 三兄弟覆盖持久化全场景;Compose 让多容器应用可复现;多阶段构建与五条安全铁律让镜像既小又稳;Rootless 与 OCI 展现了容器技术开放演进的方向。容器解决的是"单机上的环境交付"——当容器数量从几个变成几百上千个,单靠手工 docker 命令已经难以为继,需要一台"自动分配机器、自动调度容器"的编排大脑。

下一节预告

第 6 章《Kubernetes 编排》将进入集群世界:控制平面四大组件、Pod 创建链路、Service 四种类型、调度与探针——回答"容器上生产"的最后一道大题。


发布者: 作者: 灏天文库 转发
评论区 (0)
U