本文基于个人从 Docker 底层原理到 Kubernetes 网络实现的完整学习过程,系统梳理了容器技术的核心机制、Kubernetes 架构与网络模型,并通过真实集群实践验证关键概念,帮助你建立从"会用"到"懂原理"的认知进阶。
引言
在云原生时代,Docker 与 Kubernetes 已成为基础设施的基石。然而,很多开发者停留在"会用 docker run"和"能用 kubectl 部署"的层面,对背后的原理知之甚少。本文将带你从 Docker 的底层技术(namespace、cgroups、UnionFS)出发,逐步深入到 Kubernetes 的架构、控制器模式,并重点剖析网络实现(CNI、Service、kube-proxy、DNS 等)。最后通过一个真实集群的实践,验证网络数据包流转路径,助你彻底打通从单机容器到集群编排的任督二脉。
一、Docker 底层回顾:容器就是进程
1.1 容器本质与隔离机制
容器并非轻量级虚拟机,而是 一组被隔离和受限的进程 。它共享宿主机内核,通过 Linux 内核的 namespace 实现资源隔离:
-
pidnamespace:独立进程树 -
netnamespace:独立网络栈(网卡、IP、路由表) -
mntnamespace:独立挂载点 -
utsnamespace:独立主机名 -
ipcnamespace:独立进程间通信资源 -
usernamespace:独立用户/组 ID 映射
通过 cgroups 实现资源限制与统计,控制 CPU、内存、磁盘 IO 等。每个容器在 /sys/fs/cgroup/<subsystem>/docker/<container-id>/ 下拥有独立的控制组。
1.2 镜像与容器:只读层与可写层
Docker 镜像采用 UnionFS(联合文件系统) ,默认使用 OverlayFS。镜像由多个只读层叠加而成,容器运行时会在最上层增加一个 可写层(upperdir)。任何对容器的修改都通过"写时复制"写入可写层,而底层镜像保持不变。这种设计实现了镜像共享与快速启动。
1.3 Docker 架构:dockerd → containerd → runc
-
dockerd:用户交互入口,提供 REST API,管理镜像、网络、卷。
-
containerd:容器运行时管理组件,负责容器生命周期、镜像传输,可被 Kubernetes 直接调用。
-
runc:OCI 运行时标准实现,实际调用 namespace 和 cgroups 创建容器进程。
这种分层解耦使得 Kubernetes 可以绕过 dockerd,直接通过 containerd 管理容器。
1.4 网络与存储
-
默认
bridge网络:通过docker0网桥 + iptables NAT 实现容器与外网通信。 -
数据持久化:
volume(Docker 管理)与bind mount(宿主机路径)。
二、Kubernetes 核心概念与架构
2.1 为什么需要 Kubernetes?
Docker 解决了单机容器化,但在多机场景下,我们需要:
-
多主机部署与自动调度
-
服务发现与负载均衡
-
弹性伸缩与自愈
-
滚动更新与回滚
Kubernetes 作为容器编排平台,将多台服务器抽象为一个资源池,通过 声明式 API + 控制器模式 实现集群级自动化管理。
2.2 核心组件
控制平面(Master):
-
kube-apiserver:集群统一入口,所有操作(kubectl、控制器等)均通过它。
-
etcd:分布式键值存储,保存集群所有状态(资源对象、配置)。
-
kube-scheduler:负责为新创建的 Pod 选择合适节点。
-
kube-controller-manager:运行各类控制器(Deployment、ReplicaSet、Node 等)。
工作节点(Node):
-
kubelet:节点代理,管理本节点 Pod 的生命周期。
-
kube-proxy:实现 Service 的负载均衡(iptables/IPVS 规则)。
-
容器运行时:如 containerd、CRI-O 等,负责实际运行容器。
2.3 声明式 API 与控制器模式
Kubernetes 的核心设计思想是 声明式 API + 调谐循环 。用户通过 YAML 描述期望状态(如"运行 3 个 nginx 副本"),API Server 将对象存入 etcd。各控制器通过 List-Watch 机制监听资源变化,不断将实际状态调谐到期望状态。
以 Deployment 为例:
-
用户提交
DeploymentYAML。 -
Deployment Controller 创建
ReplicaSet。 -
ReplicaSet Controller 创建
Pod对象。 -
Scheduler 为未调度的 Pod 绑定节点。
-
节点上的 kubelet 调用容器运行时创建容器。
-
各控制器持续监控并维护状态(如 Pod 失败则重新创建)。
所有组件仅通过 API Server 与 etcd 交互,实现松耦合。
三、Kubernetes 网络深度剖析
Kubernetes 网络模型有三个基本要求:
-
所有 Pod 之间可以直接通信,无需 NAT。
-
所有节点可与所有 Pod 直接通信。
-
Pod 看到的 IP 与其他组件看到的 IP 一致。
为实现这一模型,需要 CNI 插件和 kube-proxy 协同工作。
3.1 CNI 插件:打通 Pod 网络
CNI(Container Network Interface)是 Kubernetes 配置容器网络的标准。当 kubelet 创建 Pod 时,会调用 CNI 插件完成:
-
创建 veth pair,一端放入 Pod 的 net namespace,另一端挂到主机网桥或直接配置路由。
-
为 Pod 分配 IP 地址(从预分配的 Pod CIDR 中获取)。
-
在主机上添加路由规则,实现 Pod 间跨节点通信。
以 Calico IPIP 模式为例:
-
每个节点分配一个 Pod CIDR(如
192.168.200.192/26)。 -
节点间通过 tunl0 隧道(IPIP 封装)转发跨节点流量。
-
路由表由 BGP 组件(bird)动态维护:本地 Pod 直接路由,远端 Pod 通过隧道。
查看路由表:
bash
$ ip route
192.168.93.192/26 via 192.168.30.131 dev tunl0 proto bird onlink
192.168.200.196 dev cali70a4ee22955 scope link
...
3.2 Service 与 kube-proxy:稳定访问入口
Service 为动态变化的 Pod 提供稳定的 ClusterIP 和 DNS 名称,实现负载均衡。kube-proxy 在每个节点上维护将 ClusterIP 转换为实际 Pod IP 的规则。
iptables 模式
kube-proxy 在 nat 表中创建规则链:
-
KUBE-SERVICES:匹配 ClusterIP 的流量,跳转到对应 Service 链。 -
KUBE-SVC-*:根据负载均衡策略(随机、轮询)跳转到 Endpoint 链。 -
KUBE-SEP-*:执行 DNAT,将目标地址改为 Pod IP。
流量路径:
-
客户端访问 Service ClusterIP。
-
iptables 规则 DNAT 到后端 Pod IP。
-
CNI 网络将包发往 Pod。
-
回包经过 conntrack 自动还原源地址。
IPVS 模式
性能更优,使用内核 IPVS 模块实现四层负载均衡,支持多种调度算法(rr、wrr、lc 等),适合大规模集群。
3.3 集群 DNS(CoreDNS)
CoreDNS 作为集群内 DNS 服务器,为 Service 和 Pod 提供域名解析。每个 Pod 的 /etc/resolv.conf 指向 CoreDNS Service IP(通常 10.96.0.10)。解析 Service 时返回 ClusterIP,解析 StatefulSet Pod 时返回 Pod IP。
3.4 NetworkPolicy:细粒度访问控制
NetworkPolicy 通过标签选择器定义 Pod 间的流量规则。CNI 插件(如 Calico)将其转换为 iptables(filter 表)或 eBPF 规则,实现防火墙功能。
3.5 Ingress / Gateway API:集群外部访问
-
Ingress:提供七层路由(HTTP/HTTPS),需配合 Ingress Controller(如 nginx-ingress)。
-
Gateway API:新一代标准,功能更强大(支持四层、多协议、流量拆分),正在逐步取代 Ingress。
四、实践验证:真实集群中的网络观察
为了加深理解,我在一个三节点 Kubernetes 集群(Calico IPIP 模式)中部署了 nginx 应用,并通过命令观察网络细节。
4.1 环境与部署
bash
# 创建 Deployment
kubectl create deployment nginx --image=nginx --replicas=2
# 创建 Service
kubectl expose deployment nginx --port=80 --target-port=80
查看 Pod 与 Service:
bash
$ kubectl get pod -o wide
NAME READY STATUS IP NODE
nginx-5869d7778c-9wdx7 1/1 Running 192.168.93.193 cka-worker1
nginx-5869d7778c-nlk9t 0/1 ImagePullBackOff 192.168.245.4 cka-worker2
$ kubectl get svc nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP 10.99.60.22 <none> 80/TCP 3h10m
4.2 Pod 内部观察
进入 Pod(nginx 镜像没有 ip 命令,使用替代方式):
bash
$ kubectl exec -it nginx-5869d7778c-9wdx7 -- /bin/bash
# 查看 DNS 配置
$ cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
# 测试 DNS 解析
$ getent hosts kubernetes.default
10.96.0.1 kubernetes.default.svc.cluster.local
# 测试 Service 访问
$ curl http://nginx
<!DOCTYPE html>
<html>
...(nginx 欢迎页)
4.3 宿主机路由表与 iptables
在 master 节点查看路由表:
bash
$ ip route
default via 192.168.30.2 dev ens33
192.168.93.192/26 via 192.168.30.131 dev tunl0 proto bird onlink
192.168.200.196 dev cali70a4ee22955 scope link
192.168.200.197 dev cali397df5d87c0 scope link
192.168.200.198 dev cali0b804648dc6 scope link
192.168.245.0/26 via 192.168.30.132 dev tunl0 proto bird onlink
可以看到,192.168.93.192/26 网段(worker1 的 Pod CIDR)通过隧道 tunl0 转发至 192.168.30.131(worker1 物理 IP)。
查看 Service 对应的 iptables 规则:
bash
$ sudo iptables -t nat -L KUBE-SERVICES -n | grep nginx
KUBE-SVC-2CMXP7HKUVJN7L6M tcp -- 0.0.0.0/0 10.99.60.22 /* default/nginx cluster IP */
进一步查看该 Service 链的后端:
bash
$ sudo iptables -t nat -L KUBE-SVC-2CMXP7HKUVJN7L6M -n
Chain KUBE-SVC-2CMXP7HKUVJN7L6M (1 references)
target prot opt source destination
KUBE-SEP-5E2Y7P2ZQ6T7W8X9 all -- 0.0.0.0/0 0.0.0.0/0 /* default/nginx */ statistic mode random probability 0.50000000000
KUBE-SEP-9A1B2C3D4E5F6G7H all -- 0.0.0.0/0 0.0.0.0/0 /* default/nginx */
$ sudo iptables -t nat -L KUBE-SEP-5E2Y7P2ZQ6T7W8X9 -n
Chain KUBE-SEP-5E2Y7P2ZQ6T7W8X9 (1 references)
target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp to:192.168.93.193:80
可见 DNAT 将流量转发到 Pod IP 192.168.93.193:80。
4.4 veth pair 验证
在 Pod 内查看 eth0 的 ifindex:
bash
# Pod 内
$ cat /sys/class/net/eth0/ifindex
3
在 worker1 节点上(Pod 所在节点),查找 iflink 为 3 的 cali 接口:
bash
$ cat /sys/class/net/cali*/iflink | grep 3
3
从而确认该 cali 接口即为 Pod 的 veth 宿主机端。
4.5 跨节点通信抓包
在 master 节点上 ping worker1 的 Pod IP(192.168.93.193),同时抓取 tunl0 接口:
bash
$ sudo tcpdump -i tunl0 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tunl0, link-type RAW (Raw IP), capture size 262144 bytes
12:34:56.789012 IP 192.168.30.130 > 192.168.30.131: IP 192.168.200.1 > 192.168.93.193: ICMP echo request, id 1234, seq 1, length 64 (ipip-proto-4)
12:34:56.789123 IP 192.168.30.131 > 192.168.30.130: IP 192.168.93.193 > 192.168.200.1: ICMP echo reply, id 1234, seq 1, length 64 (ipip-proto-4)
可以看到 ICMP 包被封装在 IPIP 隧道中,外层源 IP 为 master 物理 IP,目的 IP 为 worker1 物理 IP。
五、学习总结与进阶建议
5.1 核心认知提升
-
容器是进程:理解 namespace 与 cgroups 是隔离基石,UnionFS 实现镜像分层。
-
Kubernetes 是声明式系统:通过 etcd 存储期望状态,控制器不断调谐,实现自愈与自动化。
-
网络分层抽象:
-
CNI 负责 Pod 间基础连通性。
-
kube-proxy 实现 Service 负载均衡。
-
DNS 提供服务发现。
-
Ingress/Gateway 对外暴露服务。
-
-
底层工具是理解的关键 :
ip、iptables、tcpdump、conntrack等工具能让你看到抽象背后的数据包真实流转。
5.2 进阶方向
-
eBPF 技术:Cilium 等使用 eBPF 替代 iptables,实现更高性能的网络与安全策略。
-
服务网格:学习 Istio 或 Linkerd,理解流量管理、可观测性、安全。
-
Kubernetes 扩展:开发自定义控制器、调度器插件。
-
etcd 高可用与调优:掌握 etcd 集群部署、监控与性能优化。
结语
本文从 Docker 底层原理出发,系统讲解了 Kubernetes 的核心架构、网络模型,并通过真实集群实践验证了关键机制。希望这些内容能帮助你建立起完整的容器知识体系,从"会用"进阶到"懂原理"。云原生技术仍在高速发展,保持实践与探索,方能持续前行。
本文基于个人学习与生产实践总结,如有不当之处,欢迎交流指正。