type
status
date
slug
summary
tags
category
icon
password
第一章:云原生技术概论
- 移动互联网时代,能够成长并发展起来的这些公司,他们的共同点是:
- 快速变更,不断创新,随时调整
- 提供持续可用的服务,应对各种可能的错误和中断
- 弹性可扩展的系统,应对用户规模的快速增长
- 提供新的用户体验,以移动为中心
软件开发的方式也不得不跟随时代而变化,首当其冲是:如何解决规模越来越大,变更越来越快的难题。
- 时代巨变掀起技术浪潮
- 软件正在改变世界。
- 移动互联网让这个变革影响到每一个人。
- 传统软件开发方式受到巨大的挑战。
- 因为云计算以及相关技术的普及,软件上云成为趋势。
- 云计算的形态持续在演进。
软件对各行各业的渗透和对世界的改变,以及移动互联网时代巨大的用户基数下快速变更和不断创新的需求对软件开发方式带来巨大的推动力,我们清晰地看到如此波澜壮阔的技术浪潮:
- 大时代下的个体
视角转回到个体,不管你是否接受,软件行业解决问题的技术一直在变化,并且这种变化并不是平缓的升级,而是剧烈的革新替代。譬如容器替代虚拟机、服务网格替代 SpringCloud、观测替代监控、Network Policy 替代 iptables 等等,这种替代导致软件开发中许多习以为常的假设全被打破。
- 思想先行,技术随后。基于“良好的架构设计思想”主题层层推进,深入讨论设计一套兼具稳定(高可用)、成本(少花钱)和效率(敏捷开发)的系统时,所面临的抉择和权衡。
- 回顾历史,重点不在于考古,而是借历史之名,理解每种技术出现的原因和淘汰的原因,更好地解决今天的现实问题,寻找出未来的技术演进之路。那么介绍云原生之前,让我们回顾一下过去几十年间云计算领域的演进历程。
- 虚拟化正是云计算基础架构的核心,是云计算发展的基础。
虚拟化技术是一种资源管理技术,在各种实体资源(CPU、内存、网络、存储等)之上构建一个逻辑层,从而摆脱物理限制的约束,提高物理资源的利用率。
- 云计算走向成熟
- IaaS(Infrastructure as a Service,基础设施即服务)的出现:通过按时计费的方式租借服务器(卖资源),将资本支出转变为运营支出,这使得云计算得以大规模兴起和普及。
- PaaS(Platform as a Service,平台即服务)的出现:使开发者不必费心考虑操作系统和开发工具更新或者硬件维护,云服务供应商由 IaaS 阶段的卖资源进阶为卖服务。
- 开源 IaaS 的出现:开源云计算平台 OpenStack 简化了云的部署过程并为其带来良好的可扩展性,这使普通的企业也具备了自建私有云的能力,云也发展出了多种服务模型:自建私有云、公共云、租赁私有云及混合云等。
- 开源 PaaS 的出现:开源应用平台 Cloud Foundry、OpenShift 能在混合云、多云乃至边缘的跨平台环境中一致地加快开发和交付应用。利用这些开源软件,企业内部良莠不齐的云架构系统,被“推着”成为行业“先进”水准。
- FaaS(Function as a Service,功能即服务)的出现:通过 FaaS,物理硬件、虚拟机操作系统和 Web 服务器软件管理等等全部由云服务供应商自动处理。无服务器(Serverless)的概念初现,开发者将无需再关注任何服务、资源等基础设施。
如图所示,在这期间出现了云计算领域多个重要里程碑:
- 容器技术无疑是过去十年间对软件开发行业影响最深远的技术之一
- Docker 最大的创新在于容器镜像,它包含了一个应用运行所需的完整环境(整个操作系统的文件系统),具有一致性、轻量级、可移植、编程语言无关等特性,实现 “一次构建,随处运行”。
- Kubernetes 将底层的计算、存储和网络资源抽象为标准化的 API 对象,应用不再依赖特定的基础设施,能够轻松在不同环境(如本地、云端、混合云)间迁移。
从虚拟机到容器,云计算市场经历了一次重大变革,甚至可以说是一次洗牌。在基于容器技术的容器编排市场中,Mesos、Swarm 和 Kubernetes 上演了一场史诗级“大战”。凭借先进的设计理念和高度开放的架构,Kubernetes 最终脱颖而出,成为容器编排领域的事实标准。
图容器技术兴起
- 云计算的演进总结
- 工作负载的变化:从早期的物理服务器,通过虚拟化技术演进为虚拟机,再通过容器化技术演进为目前的容器。
- 隔离单元:无论是启动时间还是单元大小,物理机、虚拟机、容器一路走来,实现了从重量级到轻量级的转变。
- 供应商:从闭源到开源,从 VMware 到 KVM,到 OpenStack,再到 Kubernetes。从单一供应商到跨越多个供应商,从公有云到自建云,再到混合云。
- 有了 IaaS,客户不用关注物理机器,只需关注基础架构及应用程序。
- 有了 PaaS,客户不用关注基础架构,只需关注应用程序。
- 有了 FaaS,客户只需关注功能和数据。
对以上云计算演进总结分析,可以发现以下规律:
图表示了从 IaaS 诞生到 PaaS、FaaS 一路演进,底层基础设施和平台越来越强大,以不同形态对上层应用提供强力支撑。
对于 XaaS 的一路演进,可以简单归纳为:
- 我们处于戏剧性和广泛的技术和经济转变的中间,软件公司准备接管大量的经济。
互联网冲击已经无所不在,部分软件已经变成水电煤一样的社会经济中的基础设施,感触更加深刻。思考这样的软件如果宕机,对社会产生什么影响?
- 过去二十年间,云的底层基础设施和平台越来越强大,软件架构的发展也逐渐和云匹配:
- 通过不可变基础设施(镜像)解决本地和远程一致性问题;
- 通过服务网格(ServiceMesh)将非业务逻辑从应用程序中剥离;
- 通过声明式 API 描述应用程序的状态,而不用管中间的处理过程;
- 通过 DevOps 方法论以及一系列工具来提升研发/运维效率...。
应用程序中的非业务逻辑不断被剥离,并下沉到云/基础设施层,代码越来越轻量。由此,工程师的开发工作回归本质(软件开发的本质是解决业务需求,各类“高深”、“复杂”的技术难题是业务需求的副产物,并不是软件开发的主题)。
- 最初,CNCF 对云原生的定义包含以下三个方面:
- 应用容器化:容器化是云原生的基础。
- 面向微服务架构:实施微服务是构建大规模系统的必备要素。
- 应用支持容器的编排调度:编排调度是指能够对容器应用的部署、扩展、运行和生命周期进行自动化管理。
由此可见,云原生并不是简单地使用云平台运行现有的应用程序,而是能充分利用云计算优势对应用程序进行设计、实现、部署、交付的理念。
- CNCF 云原生的定义 v1.0 版本
新定义的代表技术:不可变基础设施、容器、服务网格、微服务、声明式 API。
- 云原生的目标
- 可用(Available):通过各种机制来实现应用的高可用,以保证服务提供的连续性。
- 规模(Scale):要求云原生服务能够适应不同的规模(包括但不限于用户规模/部署规模/请求量),并能够在部署时动态分配资源,以便在不同的规模之间快速和平滑的伸缩。典型场景如:
- 初创公司或新产品线快速成长,用户规模和应用部署规模在短时间内十倍百倍增长。
- 促销、季节性、节假日带来的访问量波动。
- 高峰时间段的突发流量等。
- 敏捷(Agility):快速响应市场需求。
- 成本(Cost):充分有效的利用资源。
- 规模和敏捷之间的冲突:规模大而又要求敏捷,我们比喻为“巨人绣花”。
- 规模和可用性之间的冲突:规模大而要求可用性高,我们比喻为“大象起舞”。
- 敏捷和可用性之间的冲突:敏捷而要求高可用,我们比喻为“空中换发”。
这 4 个核心目标之间,存在彼此冲突的情况,如图 1-13 所示。
而云原生架构必须要在同时满足这 3 个彼此冲突目标的前提下,还要实现成本控制。
- Docker 的核心创新“容器镜像(container image)”:
- 容器镜像打包了整个容器运行依赖的环境,以避免依赖运行容器的服务器的操作系统,从而实现“build once,run anywhere”。
- 容器镜像一但构建完成,就变成只读状态,成为不可变基础设施的一份子。
- Docker 把与内部负责管理容器执行、分发、监控、网络、构建、日志等功能的模块重构为 containerd 项目 。如图 7-13 所示,containerd 的架构主要分为三个部分:生态系统(Ecosystem)、平台(Platform)和客户端(Client),每个部分在整个系统中扮演着不同的角色,协同工作以提供全面的容器管理功能。
- 只关注如 namespace、cgroups、镜像拆包等基础的容器运行时实现被称为“低层运行时”(low-level container runtime)。目前,应用最广泛的低层运行时是 runc;
- 支持更多高级功能,如镜像管理、容器应用的管理等,被称为“高层运行时”(high-level container runtime)。目前,应用最广泛高层运行时是 containerd。
图 7-13 Containerd 架构
2016 年,Docker 将 containerd 捐献给了 CNCF 管理,现在,containerd 已经成为最流行的容器运行时。
经过 runc、containerd 组件的拆分改造之后,Docker 就不再是一个简单的守护进程那么简单了,而是通过集成 containerd、containerd-shim、runc 等多个组件共同完成。
图 1-15 拆分后的 Docker 架构
根据拆分后的 Docker 架构图看 ,根据功能的不同,容器运行时被分成两类:
- 容器编排阶段:封装集群
Kubernetes 围绕容器抽象了一系列的“资源”概念能描述整个分布式集群的运行,还有可扩展的 API 接口、服务发现、容器网络及容器资源调度等关键特性,非常符合理想的分布式调度系统。
就像用 docker run 可以启动单个程序一样,现在用 kubectl apply -f 就能部署和运行一个分布式集群应用,而无需关心是在私有云还是公有云或者具体哪家云厂商上
- 微服务架构是一种面向服务的架构,由松耦合的具有有限上下文的元素组成。
松耦合(Loosely Coupled):意味着每个服务可以独立的更新,更新一个服务无需要求改变其他服务。
限界上下文(Bounded Contexts):意味着每个服务要有明确的边界性,你可以只关注自身软件的发布,而无需考虑谁在依赖你的发布版本。微服务和它的消费者严格通过 API 进行交互,不共享数据结构、数据库等。基于契约的微服务规范要求服务接口是稳定的,而且向下兼容。
- 微服务架构首先是一个分布式的架构,分布式意味着复杂性的挑战。
- 服务发现(Service Discovery)问题:解决“我想调用你,如何找到你”的问题。
- 服务熔断(Circuit Breaker)问题:缓解服务之间依赖的不可靠问题。
- 负载均衡(Load Balancing)问题:通过均匀分配流量,让请求处理更加及时。
- 安全通讯问题:包括协议加密(TLS)、身份认证(证书/签名)、访问鉴权(RBAC)等。
- Kubernetes 用 CoreDNS 替代 Spring Cloud 服务发现组件 Eureka。
- Kubernetes 用 Service/Load Balancer 替代 Spring Cloud 中的负载均衡组件 Ribbon。
- Kubernetes 用 ConfigMap 替代 Spring Cloud 的配置中心 Config。
- Kubernetes 用 Ingress 代替 Spring Cloud 的网关组件 Zuul。
软件架构从巨石应用向微服务架构转型的过程中带来了一系列的非功能性需求,例如:
Kubernetes 在基础设施层面,解决分布式系统问题的方案:
- 服务网格的出现
假设微服务 A 调用了微服务 B 的两个服务,即 B1 和 B2。若 B1 正常运行,而 B2 持续出现 500 错误,那么在达到一定阈值后,就应对 B2 进行熔断,以避免引发雪崩效应。如果仅在基础设施层面处理这个问题,那就会陷入两难境地“切断 A 到 B 的网络通路会影响到 B1 的正常运作,不切断则会持续受到 B2 错误的影响”。
图 1-21 是否要进行熔断?
上述问题在使用 Spring Cloud 等方案中比较容易处理,既然是使用程序代码来解决问题,只要合乎逻辑,想要实现什么功能就实现什么功能。但对于 Kubernetes,由于基础设施粒度更粗糙,通常只能管理到容器层面,对单个远程服务的有效管理就相对困难。类似的情况不仅仅在断路器上出现,服务的监控、认证、授权、安全、负载均衡等都有可能面临细化管理的需求。
为了解决这一类问题,微服务基础设施很快进行了第二次进化,引入了今天被称为“服务网格”(Service Mesh)的模式。
- 服务网格(ServiceMesh)是一个基础设施层,用于处理服务间通信。云原生应用有着复杂的服务拓扑,服务网格保证请求在这些拓扑中可靠地穿梭。在实际应用当中,服务网格通常是由一系列轻量级的网络代理组成的,它们与应用程序部署在一起,但对应用程序透明。
- 数据平面(Data plane):通常采用轻量级的网络代理(如 Envoy)作为 Sidecar,网络代理负责协调和控制服务之间的通信和流量处理,解决微服务之间服务熔断、负载均衡、安全通讯等问题。
- 控制平面(Control plane):包含多个控制组件,它们负责配置和管理 Sidecar ,并提供服务发现(Discovery)、配置管理(Configuration)、安全控制(Certificates)等功能。
业内绝大部分服务网格产品通常由“数据平面”和“控制平面”两部分组成,以服务网格的代表实现 Istio 架构为例
Istio 架构
值得注意的是,尽管服务网格的特点是 Sidecar 模式,但 Sidecar 模式并非服务网格专有。
- 服务网格本质是通过 iptables 劫持发送到应用容器的流量,将原本在业务层处理的分布式通信治理相关的技术问题,下沉到网络代理型边车中处理,实现业务与非业务逻辑解耦的目的。
- 不可变基础设施
- 重大故障时,难以快速重新构建服务:持续过多的手动操作并且缺乏记录,会导致很难由标准初始化的服务器来重新构建起等效的服务;
- 不一致风险:类似于程序变量因并发修改而带来的状态不一致风险。服务运行过程中,频繁的修改基础设施配置,同样会引入中间状态,导致出现无法预知的问题。
可变的基础设施从管理基础设施的层面看:“可变”的基础设施与传统运维操作相关。运维为了满足业务需求,进行了一次或多次变更,该 Linux 操作系统就是一个可变的基础设施。
可变的基础设施会导致以下问题:
从容器的角度看,镜像就是一个不可变基础设施。
- 声明式设计
- 命令式设计:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现;
- 声明式设计:告诉“机器”你想要的是什么(what),让机器想出如何去做(how),如sql查询。
声明式设计是指一种软件设计理念:“我们描述一个事物的目标状态,而非达成目标状态的流程”。至于目标状态如何达成,则由相应的工具在其内部实现。
和声明式设计相对的是命令式设计(又叫过程式设计),两者的区别是:
通过编写 YAML 文件表达我们的需求和意图,资源如何创建、服务如何关联,至于具体怎么实现,我们完全不需要关心,全部甩手给 Kubernetes。
- DevOps
瀑布模型
虽然敏捷开发提升了开发效率,但它的范围仅限于开发和测试环节,并没有覆盖到部署环节。显然,运维部门并没有收益。相反的,甚至可以说“敏捷”加重了运维的负担。运维追求的目标是稳定,频繁变更是破坏稳定的根源。
如图 1-30 所示的混乱之墙,就是我们常说的开发与运维之间的根因冲突。
甚至可以说“敏捷”加重了运维的负担。运维追求的目标是稳定,频繁变更是破坏稳定的根源。
2009 年 DevOps 概念引入之时,基于“Development“和“Operations”合成一个新词“DevOps”,强调开发(指交付前的广义上的研发活动,包括设计、测试等)与运维融合,目的是打破开发和运维之间的隔阂、加快软件交付流程、提高软件质量。
从存在的意义上说,DevOps 完善了敏捷开发存在的短板,实现了真正的闭环。如图 1-31 所示,开发和运维不再是“孤立”的团队,两者在软件的整个生命周期内相互协作,紧密配合。由此带来的效益,是软件的品质不仅高、交付的速度还快。
- 云原生架构的演进
- 为了解决单体架构“复杂度问题”,使用微服务架构。
- 为了解决微服务间“通讯异常问题”,使用治理框架 + 监控。
- 为了解决微服务架构下大量应用“部署问题”,使用容器。
- 为了解决容器的“编排和调度问题”,使用 Kubernetes。
- 为了解决微服务框架的“侵入性问题”,使用服务网格。
- 为了让服务网格有“更好的底层支撑”,将服务网格运行在 Kubernetes 上。
从研发应用的角度看,研发的复杂度降低了。在“强大底层系统”支撑的情况下,应用监控、通信治理、编排调度相关的逻辑从应用中剥离,并下沉到底层系统,已经符合云原生架构。但站在整个系统的角度看,复杂度并没有减少和消失,要实现“强大底层系统”付出的成本(人力成本、资源成本、技术试错成本)是非常昂贵的。为了降低成本,选择上云托管,将底层系统的复杂度交给云基础设施,让云提供保姆式服务,最终演变为无基础架构设计。
- 云原生代表技术栈
- 容器运行时:Docker、Containerd、CRI-O、Kata Containers。
- 镜像和仓库:Harbor、Dragonfly、Nydus。
- 应用封装:Kustomize、Helm。
- 持续集成:Gitlab、Tekton。
- 持续部署:ArgoCD、FluxCD。
- 容器编排:Kubernetes。
- 服务网格: Istio、Envoy、Linkerd。
- 网关:Ingress-Nginx、Kong、APISIX。
- 日志:Grafana Loki、Elastic Stack、ClickHouse。
- 监控:Prometheus、Grafana。
- 可观测:OpenTelemetry。
- 机器学习/离在线业务混合部署:Volcano、Koordinator...。
如果你有志投入云原生领域,希望构建一个高可用(高研发效率、低资源成本,且兼具稳定可靠)的云原生架构,对能力要求已提升到史无前例的程度。总结来说,除了掌握基础的 Docker 和 Kubernetes 知识外,熟知图 1-34 所示的几个领域也是必备要求。
- 云原生不是简单使用云计算平台运行现有的应用程序,也不是某个开源项目或者某种技术,而是一套指导软件和基础设施架构设计的思想。并不是因为云计算/容器技术的发展才出现云原生的概念,而是软件规模变得越来越大、越来越复杂的同时,对软件的可靠性、迭代效率、资源成本的要求也越来越高,这才有了云原生技术出现的契机和发展。
第二章: 构建“足够快”的网络服务
- 各类延迟指标
操作 | 延迟 |
CPU 从一级缓存中读取数据 | 1 ns |
CPU 分支预测错误(Branch mispredict) | 3 ns |
CPU 从二级缓存中读取数据 | 4 ns |
线程间,共享资源加锁/解锁 | 17 ns |
在 1Gbps 的网络上发送 2KB 数据 | 44 ns |
访问一次主存 | 100 ns |
使用 Zippy 压缩 1KB 数据 | 2,000 ns ≈ 2 μs |
从内存顺序读取 1 MB 数据 | 3,000 ns ≈ 3 μs |
一次 SSD 随机读 | 16,000 ns ≈ 16 μs |
从 SSD 顺序读取 1 MB 数据 | 49,000 ns ≈ 49 μs |
一个数据包在同一个数据中心往返 | 500,000 ns ≈ 0.5 ms |
从磁盘顺序读取 1 MB 数据 | 825,000 ns ≈ 0.8 ms |
一次磁盘寻址 | 2,000,000 ns ≈ 2 ms |
一次 DNS 解析查询 | 50,000,000 ns ≈ 50 ms |
把一个数据包从美国发送到欧洲 | 150,000,000 ns ≈ 150 ms |
在宿主机中冷启动一个常规容器 | 5,000 ms ≈ 5 s |
这些延迟数据与软件设计和性能调优息息相关。例如,由于物理距离的限制,无论如何优化,也无法将从上海到美国的 HTTPS 请求延迟降到 750ms 以下。
- HTTPS 请求优化分析
一个完整、未复用连接的 HTTPS 请求需要经过以下 5 个阶段:DNS 域名解析、TCP 握手、SSL 握手、服务器处理、内容传输。
如图 2-1 请求阶段分析所示,这些阶段共需要 5 个 RTT(Round-Trip Time,往返时间)2 = 1 RTT(DNS Lookup,域名解析)+ 1 RTT(TCP Handshake,TCP 握手)+ 2 RTT(SSL Handshake,SSL 握手)+ 1 RTT(Data Transfer,HTTP 内容请求传输)。
RTT 是评估本地主机与远程主机间网络延迟的重要指标之一。
举个例子,北京到美国洛杉矶的 RTT 延迟为 190 ms,那么从北京访问美国洛杉矶的服务延迟就是: 4 * 190(ms) + 后端业务处理时间(ms)。其中“4”代表 HTTPS 请求的 4 个 RTT。
由于 RTT 反映的是因物理距离带来的延迟,而 SSL 阶段又包含大量的加密/解密计算消耗。因此,优化工作应该集中在减少 RTT 和降低 SSL 计算量上。
- 各阶段耗时分析
HTTPS 请求的各个阶段可以使用 curl 命令进行详细的延迟分析。
curl 命令提供了 -w 参数,该参数支持 curl 按照指定的格式打印与请求相关的信息,部分信息可以用特定的变量表示,例如 status_code、size_download、time_namelookup 等等。
- HTTPS 的优化总结
- 域名解析优化:减少域名解析产生的延迟。例如,提前获取域名解析结果备用,那么后续的 HTTPS 连接就能减少一个 RTT。
- 对传输内容进行压缩:传输数据的大小与耗时成正比,压缩传输内容是降低请求耗时最有效的手段之一。
- SSL 层优化:升级 TLS 算法和 HTTPS 证书,例如升级 TLS 1.3 协议,可将 SSL 握手的 RTT 从 2 个减少到 1 个。
- 传输层优化:升级拥塞控制算法以提高网络吞吐量。将默认的 Cubic 升级为 BBR 对于大带宽、长链路的弱网环境尤其有效。
- 网络层优化:使用商业化的网络加速服务,通过路由优化数据包,实现动态服务加速。
- 使用更现代的 HTTP 协议:升级至 HTTP/2,进一步升级到基于 QUIC 协议的 HTTP/3。
- 域名解析
- 第 1 步,用户向“DNS 解析器”(Recursive resolver)发出解析 thebyte.con.cn 域名请求。“DNS 解析器”也称 LocalDNS,例如电信运营商的 114.114.114.114。
- “DNS 解析器” 判断是否存在解析缓存:
- 存在,返回缓存的结果,也就是直接执行第 8 步;
- 不存在,执行第 2 步,向就近的“根域名服务器”(Root nameserver)查询域名所属“TLD 域名服务器”(TLD nameserver,也就是顶级域名服务器),TLD 域名服务器维护着域名托管、权威域名服务器的信息。值得一提的是,有些文章说“根域名服务器”只有 13 台,实际上“根域名服务器”的数量远不止 13 台,截止 2024 年 7 月,全世界共有 1,845 台根域名服务器3。
- 获取 com.cn. 的“TLD 域名服务器”后,执行第 4 步,向该服务器查询 thebyte.com.cn. 的“权威域名服务器”(Authoritative nameserver)。
- 获取 thebyte.com.cn 的“权威域名服务器”后,执行第 6 步,向该服务器查询域名的具体解析记录。
- “DNS 解析器” 获取到解析记录后,再转发给客户端(第 8 步),整个解析过程结束。
- “DNS 解析器”是客户端与“权威域名服务器”的中间人,容易出现解析污染或者“DNS 解析器”宕机,这种情况会导致域名解析局部不可用;
- “权威域名服务器”出现故障,这种情况会导致域名解析全局不可用,但出现故障的概率极低。
通常情况下的域名解析过程,其实就是从“域名树”的根部到顶部,不断递归查询的过程。
域名解析过程
回顾整个解析过程,有 2 个环节容易出现问题:
- 排查域名解析故障
nslookup
thebyte.com.cn
命令可用于查询域名的解析结果 - Facebook 这次故障带给我们以下 DNS 系统的设计思考:
- 部署形式思考:可选择将“权威域名服务器”放在 SLB(Server Load Balancer,负载均衡)后方,或采用 OSPF Anycast 的部署形式避免单点问题。
- 部署位置思考:可选择自建集群 + 公有云服务混合部署,利用云增强 DNS 系统可靠性。
如果生产环境要进行很大风险性的操作,除了慎之又慎外,合理的方式应该使用一种二次确认的方式。例如,修改一个 iptables 规则,修改之后增加 10 分钟“观察期”。观察期结束后,系统自动恢复原来的配置,运维人员确认观察期内数据没有任何问题之后,再执行正式的操作。
- 使用 HTTPDNS 解决“中间商”问题
“域名解析器”是 DNS 查询中的第一站,作为一个“中间商”,域名解析经历了过多的中间环节,服务质量不可控。HTTPDNS 的工作原理如图 2-7 所示。客户端内部集成 HTTPDNS 模块,跳过“操作系统定义的解析服务”(图中的 LocalDNS,也就是默认基于 UDP 协议的域名解析系统),替换为使用 HTTPS 协议请求更可靠的“软件定义的解析服务”(图中的 6.6.6.6)。
- 对传输内容进行压缩
- 首先,HTTP 客户端发送 Accept-Encoding 首部,其中列出它支持的压缩算法及其优先级;
- 服务器则从中选择一种兼容的算法对响应主体进行压缩,并通过 Content-Encoding 首部告知客户端所选的压缩算法。 图 2-8 HTTP 压缩算法协商过程
对传输内容进行压缩是提升 HTTP 服务可用性的关键手段。如使用 Gzip 压缩后,一个 100KB 的文件通常会减少到 30KB,体积降低 70%。这不仅提高了网络传输效率,还能减少带宽成本。
所有现代浏览器、客户端和 HTTP 服务器软件都支持压缩技术。压缩算法的选择通过 HTTP 客户端和服务器之间的协商机制确定(如图 2-8 所示):
默认情况下,一般使用 Gzip 对内容进行压缩,但针对 HTTP 类型的文本内容还有一个更高压缩率的算法 Brotli。
Brotli 是 Google 推出的开源无损压缩算法,它内部有一个预定义的字典,涵盖了超过 1,300 个 HTTP 领域常用单词和短语。Brotli 在压缩过程中将这些常见的词汇和短语作为整体匹配,从而大幅提升文本型内容( HTML、CSS 和 JavaScript 文件)的压缩密度。
如图 2-9 所示,各类型压缩算法在不同压缩等级下的效果对比。可以看到,Brotli 压缩效果比常用的 Gzip 高出 17% 至 30%。
图 2-9 Brotli、Zopfli、gzip 不同压缩等级下的压缩率对比
在服务端安装了 Brotli 模块(如 ngx_brotli)后,可以与 gzip 一同启用,以最大化兼容性。以下是 Nginx 中启用 Brotli 的配置示例:
- HTTPS 加密原理
- 首先,客户端与服务端会进行协商,确定一个双方都支持的对称加密算法,例如 AES。
- 确认对称加密算法后,客户端会随机生成一个对称加密密钥 K。
- 客户端使用服务端的公钥加密密钥 K,并将密文传输给服务端。此时,只有服务端的私钥能够解密密钥 K。
加密和解密的过程可以使用一个密钥(Share key)作为参数,密钥必须保密,但加密和解密的过程可以公开。换句话说,只有知道密钥的人才能解密密文。常用的对称加密算法有 AES、ChaCha20、DES 等。因此,只要密钥不被中间人获取,两方通信的机密性就能得到保证。
对称加密的核心在于如何保证密钥不被暴露。但由于 HTTP 通信模型是 1 对 N,如果所有人都共享同一个密钥,其实就相当于没有加密。
如果由每个客户端随机生成一个密钥,并传输给服务端,那么就不存在共享密钥的问题了。但问题是客户端随机生成的密钥怎么传给服务端,同时不被别人知道。因为协商过程是明文的,密钥依然存在被中间人截获的可能性。
非对称机密:简单说就是有两把密钥,通常一把叫做公钥(Public Key),可以向公众开放。另一把叫私钥或密钥(Private Key),私钥是必须保密的。用公钥加密的内容必须用私钥才能解开,同样,私钥加密的内容只有公钥能解开。
非对称加密算法通常需要更多的计算资源,尤其在加密或解密大量数据时计算消耗更大。因此,我们使用计算效率更高的对称加密算法来加密 HTTP 内容,非对称加密算法来加密对称加密的密钥。请看下面的过程:
- 公钥仍有被劫持的可能性
- CA 机构拥有非对称加密的私钥和公钥。
- CA 机构对证书明文数据T进行hash。
- 对 hash 后的值用私钥加密,得到数字签名 S。
数字证书:其实所有证明的源头都是一条或多条不证自明的“公理”,由它推导出一切。 CA 机构(CA,Certificate Authority,证书认证机构),它是如今互联网世界正常运作的前提,而 CA 机构颁发的“身份证”就是数字证书。网站在使用 HTTPS 前,需要向 CA 机构申领一份数字证书,数字证书里含有证书持有者、域名、公钥、过期时间等信息。服务器把证书传输给浏览器,浏览器从证书里获取公钥就行了。
我们把证书原本的内容生成一份“签名”,比对证书内容和签名是否一致就能判别是否被篡改。这就是数字证书的“防伪技术”,这里的“签名”就叫数字签名。请看数字签名的制作过程:
实际上证书之间的认证也可以不止一层,可以 A 信任 B,B 信任 C。以此类推,我们把它叫做信任链或数字证书链,也就是一连串的数字证书。由根证书为起点,透过层层信任,使终端实体证书的持有者可以获得转授的信任,以证明身份。
- https加密通信逻辑
总结 HTTPS 的通信加密逻辑,如图 2-16 所示。服务端向 CA 机构申请证书,并在 TLS 握手阶段将证书发送给客户端,客户端校验数字证书合法性。
图 2-16 HTTPS 通信流程
接下来,服务器为每个客户端维护一个 session ID 。浏览器生成好密钥传给服务器后,服务器会把该密钥存到相应的 session ID 下,之后浏览器每次请求都会携带 session ID,服务器会根据 session ID 找到相应的密钥并进行解密加密操作,这样就不必要每次重新制作、传输密钥了!
- HTTPS 优化实践
- RSA 证书采用 RSA 算法生成公钥,具有良好的兼容性,但不支持完美前向保密(PFS)。PFS 能确保即使私钥泄露,也无法破解泄露之前的通信内容。
- ECC 证书则使用椭圆曲线加密算法(Elliptic Curve Cryptography)生成公钥,具有较快的计算速度和更高的安全性,且支持 PFS。ECC 能以更小的密钥长度提供相同或更高的安全性。例如,256 位的 ECC 密钥提供的安全性相当于 3072 位的 RSA 密钥。
- ssl_session_cache:设置 SSL/TLS 会话缓存的类型和大小。配置为 shared:SSL:10m 表示所有 Nginx 工作进程共享一个 10MB 的 SSL 会话缓存。根据官方说明,1MB 的缓存可以存储大约 4000 个会话。
- ssl_session_timeout:设置会话缓存中 SSL 参数的过期时间,决定客户端可以在多长时间内重用缓存的会话信息。在此例中,设定为 1 小时。
2018 年发布的 TLS 1.3 协议优化了 SSL 握手过程,将握手时间缩短至 1 次 RTT。如果复用之前的连接,甚至可以实现 0 RTT(通过使用 early_data 扩展)
HTTPS 数字证书分为 RSA 证书和 ECC 证书,二者的区别在于:
ECC 证书的唯一缺点是兼容性稍差。从 Nginx 1.11.0 开始,支持配置 RSA/ECC 双证书。双证书的实现原理是:在 TLS 握手过程期间,分析双方协商的密码套件(Cipher Suite),如果支持 ECDSA 算法则返回 ECC 证书,否则返回 RSA 证书。
需要注意的是,配置了 ECC 证书并不意味着它一定会生效。使用 openssl ciphers 命令来查看服务器中指定的 ssl_ciphers 配置所支持的密码套件及其优先级
调整 https 会话缓存
HTTPS 连接建立后,会生成一个会话(session),用于保存客户端和服务器之间的安全连接信息。如果会话未过期,后续连接可以复用之前的握手结果,从而提高连接效率。
与会话相关的配置如下:
上述配置说明如下:
原本客户端本地的 OCSP 查询工作转交给后端服务器处理。后端服务器会预先获取并缓存 OCSP 响应。
- 整个 HTTPS 优化手段(TLS1.3、ECC 证书、OCSP Stapling结束后可以使用 https://myssl.com/ 服务确认配置是否生效
接着,对不同证书(ECC 和 RSA),不同 TLS 协议(TLS1.2 和 TLS1.3)进行压测,测试结果如表 2-2 所示。
表 2-2 HTTPS 性能基准测试
证书、TLS 版本配置 | QPS | 单次发出请求数 | 延迟表现 |
RSA 证书 + TLS1.2 | 316.20 | 100 | 316.254ms |
RSA 证书 + TLS1.2 + QAT | 530.48 | 100 | 188.507ms |
RSA 证书 + TLS1.3 | 303.01 | 100 | 330.017ms |
RSA 证书 + TLS1.3 + QAT | 499.29 | 100 | 200.285ms |
ECC 证书 + TLS1.2 | 639.39 | 100 | 203.319ms |
ECC 证书 + TLS1.3 | 627.39 | 100 | 159.390ms |
表格中的 QAT 指的是 Quick Assist Technology。这是 Intel 公司推出的一种专用硬件加速技术
从测试结果上看,使用 ECC 证书明显比 RSA 证书性能提升很多。即使 RSA 证书使用了 QAT 加速,比起 ECC 证书还是存在明显差距。此外,使用 QAT 加速要额外购买硬件,硬件成本以及维护成本都很高,不再推荐使用。
所以,HTTPS 配置推荐使用 TLS1.3 协议 + ECC 证书方式。
- 网络拥塞控制的核心
- RTT 越小,延迟越低;
- 带宽越高,单位时间能传输的数据越多。
- 调整发送速率,避免过多数据进入网络,既充分利用带宽,又避免丢包或长时间延迟。
- RTprop(最小时延):水管的长度,距离越远延迟越大。
- BtlBw(瓶颈带宽):水管最窄处的流量,决定最大数据速率。
- BDP(带宽-时延积):水管中能同时容纳的数据量,= 带宽 × 时延。
- inflight 数据:已发出但未被确认的数据包,相当于水管中的水流量。
- (0,BDP) 应用受限区: 发送数据量不足,未占满带宽,RTT 最小,速率最高。
- (BDP,BDP + 缓冲区) 带宽受限区: 数据量达到带宽容量,RTT 增大,速率达到瓶颈。
- (BDP + 缓冲区,∞) 缓冲受限区: 数据量超过网络容量,丢包增多,延迟和速率受影响。
- 逻辑:通过 "丢包" 判断拥塞,丢包后降低发送速率,恢复后再提高,循环往复。
- 问题:丢包未必总由拥塞导致(可能因链路长、缓存过大等),导致过于保守,难以充分利用现代网络的高带宽。
- 目标:找到网络的最优点,最大化效率。
- min RTT(最低延迟):无丢包,缓存未占满。
- max BW(最大带宽):完全利用瓶颈带宽,缓存被充分利用。
- 矛盾:
- 测量 max BW 时需占满缓存,导致延迟增加;
- 测量 min RTT 时需不占满缓存,无法得到 max BW。 因此,算法需要权衡两者,动态调整发送速率。
- 拥塞控制的核心是调整发送速度,让 inflight 数据量接近 BDP。
- 早期算法依赖丢包,现代算法(如 BBR)通过测量 RTT 和带宽,实现最大化网络效能。
网络传输效率(吞吐量)取决于RTT(往返时延)和带宽:
网络拥塞控制的目标是:
关键术语解释:
网络状态的三个区间:
理想状态:
inflight 数据量接近 BDP,既满载带宽,又不丢包。
传统拥塞控制(早期算法)局限性:
现代拥塞控制(BBR)的改进:
总结:
- BBR 的设计原理和性能表现
- 带宽探测:
- 周期性检测网络链路的最大带宽。
- 当带宽提升时,增加发送速率以充分利用网络资源。
- 延迟探测:
- 定期测量最小 RTT,防止数据在中间设备的缓存队列中堆积。
- 根据最小 RTT 动态调整发送速率,避免网络延迟过高。
- 拥塞控制状态机:
- BBR 的运行分为 4 个状态:
- 启动(STARTUP):快速提升发送速率,找到瓶颈带宽。
- 排空(DRAIN):降低发送速率,清空缓冲区中的多余数据。
- 带宽探测(PROBE_BW):主要运行状态,维持稳定速率并偶尔探测更大带宽或释放带宽。
- 时延探测(PROBE_RTT):测量最小 RTT,调整拥塞窗口以优化性能。
- 低延迟、无丢包环境(<1ms, 0% 丢包):
- Cubic 的吞吐量最高(2.35Gb/s)。
- BBR 表现良好,但未在此场景中测试。
- 中等延迟、无丢包环境(<140ms, 0% 丢包):
- Reno、Cubic、Westwood 的吞吐量分别为 195Mb/s、147Mb/s 和 344Mb/s。
- BBR 达到 340Mb/s,与 Westwood 相近,表现优异。
- 中等延迟、轻微丢包环境(<140ms, 1.5% 丢包):
- Reno 和 Cubic 的吞吐量分别下降到 1.13Mb/s 和 1.23Mb/s。
- BBR 的吞吐量为 160Mb/s,显著优于其他算法。
- 中等延迟、高丢包环境(<140ms, 3% 丢包):
- Reno 和 Cubic 的吞吐量进一步下降到 0.65Mb/s 和 0.78Mb/s。
- BBR 的吞吐量仍保持 132Mb/s,远超其他算法。
- 不依赖丢包判断拥塞,通过带宽和延迟的动态测量调整发送速率。
- 高效利用带宽,在轻微丢包环境中表现尤为突出。
- 适应现代网络特性,在高带宽、高延迟和轻微丢包条件下显著优于传统算法(如 Reno 和 Cubic)。
BBR 的设计原理
BBR 是一种现代拥塞控制算法,其核心思路是不以丢包作为拥塞判断条件,而是通过交替测量**最大带宽(max BW)和最小延迟(min RTT)**来动态调整发送速率,达到高效利用网络资源的目的。
关键设计点:
BBR 的性能表现
BBR 的性能在不同网络条件下表现优异,尤其是在轻微丢包的情况下,能够显著提升吞吐量。
测试结果总结:
总结:BBR 的优势
- 动态加速
- 静态内容加速:像图片、视频、文件等通过 CDN(内容分发网络) 缓存到多个服务器,让你从最近的服务器下载,所以更快。
- 动态加速:用于处理动态请求(比如购物网站查询价格、搜索数据等),这些数据不能直接缓存。动态加速通过优化网络传输路径,把动态数据从服务器快速传输到用户的设备。
- 配置你的域名:
- 你需要将自己的域名通过一种方式叫 CNAME,重定向到 CDN 服务商提供的加速网络,比如
www.yourwebsite.com
指向 Akamai 加速网络yourwebsite.akamai.com
。 - 监测网络质量:
- CDN 会选择在你源站附近的一些“转发节点”(Relay Node),这些节点会先测试网络(下载一个小文件,看丢包率、延迟 RTT 等)。
- 选择最佳传输路径:
- 转发节点就像帮你“探路”的信使,会找出从用户到源站之间最优的一条路径,避开拥堵的网络链路。
- 动态请求加速:
- 用户的请求被路由到最近的边缘节点,然后边缘节点通过最优路径转发请求到源站,返回数据。
- 在东南亚和香港之间进行测试,动态加速可以让请求延迟减少 30%-50%。
- 比如:
- 从泰国曼谷到香港,直接访问需要 0.58 秒,用了动态加速后只需要 0.44 秒,延迟减少了 31%。
- 新加坡到香港,直接访问需要 0.58 秒,用了动态加速后变成了 0.39 秒,优化了 48%。
- 不同国家和地区的网络质量参差不齐(比如东南亚网络不如香港,所以延迟可能特别大)。
- 网络路径可能绕路:默认的互联网路由可能会选择一些不那么高效的路径,而动态加速会优化这些路径,避开网络拥堵。
- 默认的互联网路由 就像地图 app 绘制的一条长途路线,可能有红绿灯和堵车。
- 动态加速 就像地图选择的最优路线,避开拥堵、快速到达。
1. 动态加速是干什么的?
简而言之,动态加速就是专门用来提升动态请求的传输速度。
2. 动态加速怎么实现的?
动态加速背后的工作由 CDN 服务商 完成,比如 Akamai、Amazon CloudFront。
具体过程可以理解为这样:
3. 动态加速的效果怎么样?
通过优化路径,动态加速显著降低了延迟。以下是一个实践结果(使用 Akamai):
4. 它为啥有用?
动态加速可以帮助解决这些问题:
一个形象的类比:
5. 核心总结
动态加速的本质是利用边缘节点+优化路径技术,帮助动态请求减少不必要的网络延迟,从而加速访问速度。在跨国网络访问中,效果尤其明显,延迟降低了 30%-50%。
- QUIC 设计原理
- 建立连接时延迟大:HTTPS 初次连接(TCP 握手 + TLS 握手)至少需要 3 个 RTT 才能建立。
- 队头阻塞问题:以 HTTP/2 为例,一个 TCP 连接上的所有 stream(流,HTTP/2 传输的数据单元)必须按顺序依次传输。如果一个 stream 丢失,后面的 stream 将被阻塞,直到丢失的数据重传。
- 协议僵化问题:作为一个运行了接近 40 多年的协议,许多中间设备(如防火墙和路由器)已经变得依赖某些隐式规则,打补丁或者说推动 TCP 协议更新脱离现实。
QUIC(Quick UDP Internet Connection,快速 UDP 网络连接)是一种基于 UDP 封装的安全可靠传输协议,旨在取代 TCP,成为新一代互联网的主流传输协议。
根据图所示的各版本 HTTP 协议区别,可以看出 HTTP/3 最大的特点是:底层基于 UDP 、默认集成了 TLS 安全协议。
QUIC 出现之前,HTTP 采用 TCP 作为底层协议来实现可靠的数据传输。
TCP 暴露出来的先天设计缺陷体现在以下三个方面:
QUIC 的特点:
支持连接迁移
当用户网络环境发生变化,这在移动端相当普遍,例如 WIFI 切换到 4G 时,TCP 基于四元组的方式无法保持连接的存活。而 QUIC 由于使用 Connection ID 标识连接,当源地址发生改变时,连接不受环境变化影响,因此 QUIC 可以实现网络变化的无缝切换,从而保证连接存活和数据正常收发。
低时延连接
以 HTTPS 请求为例,即使是最新的 TLS 1.3 协议,初次连接也至少需要 2-RTT 才能开启数据传输
QUIC 内部集成了 TLS 安全协议,无需像 TCP 先经过三次握手,再经过 TLS 握手才开启数据传输。QUIC 初次连接只需要 1- RTT 就能开启数据传输。
图不同协议开启数据传输时,需要的 RTT数
可插拔拥塞控制
大多数 QUIC 实现工作在用户空间,支持灵活“插拔”不同的拥塞控制算法,如 Cubic、BBR 和 PCC 等。
降低对丢包的影响
QUIC 为每个 Stream 设计了独立的控制机制,Stream 之间没有先后依赖。这意味着,如果一个属于 Stream2 的 UDP 数据包丢失,它只会影响 Stream2 的处理,不会阻塞 Stream1 和 Stream3 的传输。
这样的设计有效避免了 TCP 协议中的队头阻塞问题。
QPACK 通过更高效的头部压缩技术,减少了网络传输中的冗余数据量。这种压缩机制不仅提升了数据传输的效率,还能缓解前面提到的“队头阻塞”。
经过上述全方面的优化设计,QUIC 确保了在当今网络环
经过上述全方面的优化设计,QUIC 确保了在当今网络环境中比 TCP 更安全、更快速的连接以及更高的传输效率。
- QUIC 实践
- 某些网络环境下(如网络设备配置不当、防火墙限制),UDP 数据包更容易被丢弃或阻断。
- QUIC 作为较新的协议,在一些边缘场景(如复杂的企业网络、旧版网络设备等),兼容性还是不够完善。
- 服务端层面:不仅需要适配 QUIC 协议,还要确保与 TCP 协议兼容。此外,TCP 经过多年的深度优化,QUIC 实际的效能表现是否能够与 TCP 相媲美?
- 客户端层面:需要在适配、收益之间进行成本权衡。采用 QUIC 协议的客户端必须具备降级容错能力,并准备长时间同时维护新旧两种网络库。
2022 年,爱奇艺基础架构团队对 HTTP/1.1、HTTP/2 和 HTTP/3 在不同网络条件下的性能差异进行了测试。笔者将测试数据在此分享,供读者参考。
从请求耗时表现来看(图 2-30),相同的网络质量下,HTTP/3 的耗时比 HTTP/2 降低了近一半,证明了上述讨论不虚。
图 2-30 不同网络质量下,各协议耗时表现(耗时单位 ms)
不过,从测试数据中也发现一个值得注意的问题!根据图 2-31 所示的网络请求成功率来看,HTTP/3 的失败率明显高于 HTTP/2。笔者“猜测”有两方面的原因:
图 2-31 不同网络质量下,各协议失败率表现
综上所述,无论是服务端还是客户端,集成 QUIC 协议并非易事:
- 小结
网络优化是一个端到端的系统性工作,遵循二八原则。即 80% 的性能损耗在 20% 的处理流程上。所以优化的前提是分析出导致 80% 性能损耗的 20% 的处理流程到底在哪里。
虽然 HTTPS 让传输更加安全,但很多服务赶鸭子上架式使用 HTTPS 往往都会遇到:请求变慢、服务器负载过高、证书过期不及时更新问题。
构建高可用架构的第一步是保证从客户端到服务端的请求是通畅无阻的,也就是域名解析不能失败。如果域名解析失败了,那么后端无论是两地三中心,还是别的什么高可用设计,都无法发挥作用。
掌握构建足够快的网络服务,低延迟的(尽可能快)、可靠的(避免请求失败)、安全的(使用 TLS1.3 协议 + ECC 证书,即快又安全),还要充分利用带宽(弱网环境下,榨干带宽,提高网络吞吐)。
第三章:深入 Linux 内核网络技术
- 创造操作系统,就是去创造一个所有应用程序赖以运行的基础环境。从根本上来说,就是在制定规则:什么可以接受,什么可以做,什么不可以做。事实上,所有的程序都是在制定规则,只不过操作系统是在制定最根本的规则。
—— 摘自《Linus Torvalds 自传》
- OSI 网络七层模型
OSI 模型将网络通信划分为七个独立的层次,每一层负责特定的网络功能。
层级 | 名称 | 含义 |
7 | 应用层(Application Layer) | 应用层是 OSI 模型的顶层,直接与用户的应用程序交互,提供网络服务如电子邮件、文件传输、网页浏览等。常见的应用层协议有 HTTP、FTP、SMTP 等。 |
6 | 表示层(Presentation Layer) | 表示层负责数据的格式化和转换,确保发送方和接收方能够理解彼此传输的数据格式。它还处理数据的加密、解密和压缩等操作。 |
5 | 会话层(Session Layer) | 会话层管理应用程序之间的会话或连接,负责建立、维护和终止通信会话。它还提供会话恢复功能,使得通信中断后可以从上次中断的位置继续传输。 |
4 | 传输层(Transport Layer) | 传输层负责端到端的数据传输管理,提供可靠的传输服务,如数据分段、重组、流量控制和错误校正等。常见的传输层协议有 TCP 和 UDP。 |
3 | 网络层(Network Layer) | 网络层负责不同网络之间的数据路由和转发。它使用 IP 地址来确定数据包的传输路径,并确保数据能够从源设备传输到目的设备,即使它们位于不同的网络中。 |
2 | 数据链路层(Data Link Layer) | 数据链路层负责节点之间的数据传输,提供错误检测和纠正功能。它确保数据帧能够在同一局域网(LAN)内可靠传输。数据链路层使用 MAC(Media Access Control,介质访问控制)地址来标识网络上的设备。 |
1 | 物理层(Physical Layer) | 物理层是 OSI 模型的第一层,负责实际的硬件传输,如电缆、光纤、开关和集线器等。它处理二进制数据(即 bit)在物理介质上的传输,包括信号的电气/光学特性、传输速率和传输模式等。 |
一般来说,数据链路层的数据单元被称为“帧”(Frames),网络层的数据单元为称为“数据包”(Packets),传输层的数据单元被称为“数据段”(Segments),应用层数据单元被称为“数据”(Data)。为了简化表述,笔者并不严格区分这些术语,本书绝大部分内容中会用“数据包”泛指各个网络分层的数据单元。
- Linux 系统收包流程
- 网络层(L3):根据路由表判断数据发送到哪里,是物理设备还是虚拟设备。
- 传输层(L4):解包并完成 NAT 转换(如果有)、连接跟踪等操作。
- DMA:网卡直接访问内存,不占用 CPU。
- RingBuffer:缓冲数据,解决网卡快、CPU慢的处理问题。
- IRQ 和 SoftIRQ:硬件中断和内核线程协同处理,降低 CPU 的实时负担。
- NAPI:提取和封装数据的驱动接口。
- skb:数据包在协议栈中的核心数据结构。
- 多层解包/封包。
- 频繁的内核态-用户态切换。
- CPU 被中断机制和协议栈处理占用较多时间。
网卡接收数据包:网卡(例如 eth0)从外部网络接收到数据包后,需要把数据包传递到内核中。这里使用的是 DMA(Direct Memory Access,直接内存访问) 技术,跳过 CPU,直接把数据写入操作系统内核的 RingBuffer。
硬件中断通知:当数据包被放入 RingBuffer 后,网卡会触发一次 IRQ(中断请求),告诉内核有新数据到达。然后内核通过中断处理函数标记这件事,并唤醒内核线程来继续处理。
软中断处理(SoftIRQ):内核通过唤醒 ksoftirqd 内核线程,从 RingBuffer 将数据取出。调用网卡驱动提供的 NAPI(New API)接口,读取数据包并转化为 skb(Socket Buffer),用于管理数据包的信息(头部、数据、元数据等)。
协议栈分层处理:
应用层获取数据:最终,内核将数据送入对应的应用程序 socket 缓冲区。应用通过 Socket API 调用获取数据,完成整个数据包接收流程。
整体过程: 从网卡接收数据(DMA 传输到 RingBuffer 中),到硬件/软中断通知内核,再通过 ksoftirqd 和 NAPI 接口提取数据,用 skb 格式传递到内核协议栈处理,最终送到应用程序对应的 socket 缓冲区。
关键技术:
问题点:
- Netfilter 的 5 个钩子
- PREROUTING:位于 网络层入站之前,修改目标 IP(DNAT)。
- FORWARD:位于 网络层中转的路由后,处理非本机流量。
- INPUT:位于 本机接收数据包的路由后,确保数据到本地应用。
- OUTPUT:位于 本地主机生成数据的路由前,用于过滤/修改出口数据。
- POSTROUTING:位于 所有出栈数据的路由后,执行源地址转换(SNAT)。
钩子名称 | 触发时机 | 作用 | 所在网络协议栈层面 | 典型用途 |
PREROUTING | 数据包刚进入协议栈时 | 在路由前对数据包进行目标地址转换(如 DNAT)。 | 网络层(L3)路由之前 | 修改目标 IP 地址 |
FORWARD | 数据包需要转发且目标不是本机 | 处理转发的数据包(如设置路由、防火墙过滤)。 | 网络层(L3)路由之后 | 管理和保护转发的数据包 |
INPUT | 数据包的目标是本机 | 处理发往本地的数据包,允许或拒绝访问,保护本机安全。 | 网络层(L3)路由之后 | 数据过滤、数据包传递给本地进程 |
OUTPUT | 本机生成数据包 | 加工或过滤本地生成的数据包,在其通过路由之前对其进行修改(如限制访问目标等)。 | 网络层(L3)路由之前 | 丢弃、过滤,或修改本地主机流量 |
POSTROUTING | 数据包即将离开协议栈(转发或本地主机) | 在路由之后(离开协议栈前)进行源地址转换(如 SNAT)。 | 网络层(L3)路由之后(出口) | 修改源 IP 地址,以匹配 NAT 需求 |
这 5 个钩子在网络协议栈的位置如图所示
以下为明确 Netfilter 钩子在网络协议栈中的位置 的 Mermaid 图:
更直观的解释与增强理解
hook 设计模式在其他软件系统中随处可见,如本书后面介绍的 eBPF、Kubernetes 等等,Kubernetes 在编排调度、网络、资源定义等通过暴露接口的方式,允许用户根据自己的需求插入自定义代码或逻辑来扩展 Kubernetes 的功能。
- 数据包过滤工具 iptables
- iptables 的表与链:
- 链 是 Netfilter 钩子的抽象,对应 Netfilter 的 5 个钩子:
PREROUTING
、INPUT
、FORWARD
、OUTPUT
、POSTROUTING
。 - 数据包经过内核协议栈时,会根据链上的规则进行匹配和处理。
- ACCEPT:允许数据包通过,继续执行后续规则。
- DROP:丢弃数据包。
- RETURN:退出当前链,返回上一链的规则。
- DNAT:修改目标地址,实现目标网络地址转换。
- SNAT:修改源地址,实现源网络地址转换。
- REDIRECT:端口映射,将数据包重定向到本机其他端口。
- REJECT:丢弃数据包,并向发送端返回错误信息。
- MASQUERADE:动态 SNAT,常用于动态 IP 地址的 NAT。
- LOG:记录数据包的日志信息。
- 表 是链的抽象,用于分类管理数据包处理规则。iptables 提供了 5 张规则表:
- 数据包流动过程(结合表与链)
- raw 表:标记连接追踪(可禁用连接追踪)。
- mangle 表:修改数据包的报文头。
- nat 表:进行源地址(SNAT)或目标地址(DNAT)转换。
- filter 表:数据包过滤,决定是否放行、拒绝或丢弃。
- security 表:应用安全策略(如 SELinux)。
- PREROUTING:进入协议栈时,首先经过 raw 表、mangle 表和 nat 表。
- FORWARD:非本机目标的数据包,经过 mangle 和 filter 表。
- INPUT:目标是本机的数据包,经过 mangle 和 filter 表。
- OUTPUT:本机生成的数据包,经过 nat 表、mangle 表和 filter 表。
- POSTROUTING:所有数据包在离开协议栈前,经过 mangle 和 nat 表。
- iptables 使用示例
- A INPUT:在
INPUT
链上追加规则。 - p tcp:指定协议为 TCP。
- -dport 22:匹配目标端口为 22。
- j ACCEPT:执行 ACCEPT 动作,允许数据包通过。
- iptables 的核心概念:
- 链:对应 Netfilter 的钩子,决定数据包处理的阶段。
- 表:分类管理规则,提供不同功能(如过滤、NAT、修改报文头)。
- 动作:对数据包执行的具体操作(如 ACCEPT、DROP、SNAT、DNAT 等)。
- 数据包流动顺序:
- raw -> mangle -> nat -> filter -> security,不同链上的规则按顺序处理数据包。
- 灵活性:
- iptables 将链与规则表解耦,便于管理复杂的规则集,同时支持多种动作,覆盖从防火墙到 NAT 的广泛功能。
iptables 是基于 Netfilter 开发的 Linux 数据包过滤工具,能够通过命令行管理网络流量。它提供了更易用的方式,代替直接通过 Netfilter 钩子编程操作。iptables 被广泛用于防火墙配置、网络地址转换(NAT)等功能。
链(Chain)
常见动作
表(Table)
表名 | 功能 |
raw | 禁用连接追踪机制,加速数据包处理。 |
mangle | 修改数据包内容,常用于修改报文头(如 ToS、TTL 等)。 |
nat | 实现网络地址转换(NAT),修改源地址(SNAT)或目标地址(DNAT)。 |
filter | 数据包过滤,决定数据包是放行(ACCEPT)、拒绝(REJECT)还是丢弃(DROP)。 |
security | 应用安全策略(如 SELinux),一般不常用。 |
数据包在通过 Netfilter 时,会按照一定顺序经过链和表的规则处理,具体流程如下:
以下为数据包流动的具体路径(对应链):
例如,放行 TCP 22 端口的流量:
4. 数据包流动图解
以下是数据包通过 Netfilter 的流动过程(结合表与链):
总结
通过 iptables,系统管理员可以轻松管理网络流量。
数据包通过 Netfilter 时的流动过程
- iptables 自定义链
- 自定义链特点
- 扩展内置链:
- 自定义链是对内置链(PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING)的扩展,使得规则组织更加灵活。
- 数据包可以从内置链跳转到自定义链,处理完成后可以返回内置链或进一步跳转到其他自定义链。
- 动态跳转规则:
- 自定义链可以与内置链共同协作,实现链间跳转。如 Kubernetes 使用自定义链实现 Service 的负载均衡转发。
- 设计灵活:
- 自定义链的规则相对独立,便于管理和扩展,从而简化复杂操作。
- Kubernetes 中的自定义链应用
- Service VIP 流量引导:
- 当外部请求目标为 Service 的 VIP 地址时,iptables 会触发类似以下规则:
- 规则表示:目的为 10.0.1.175:80 的数据包跳转到自定义链
KUBE-SVC-NWV5X
。 - 负载均衡自定义链:
KUBE-SVC-NWV5X
是一个实现负载均衡的链集合,用来随机分发请求给后端 Pods。例如:- 数据包会被随机分发到自定义链
KUBE-SEP-*
,每个链对应一个具体后端 Pod。 - 后端目标地址转换:
KUBE-SEP-*
规则链使用 DNAT 将数据包的目标地址修改为后端 Pod 的 IP 和端口:- 作用:将目标地址修改为
10.244.3.6:9376
,即某个后端 Pod 的 IP 和端口。 - 小结:
- 数据包从 Service VIP 流量开始,通过自定义链多次跳转,最终根据负载均衡策略转发到具体 Pod 的 IP 和端口。
- iptables 模式的性能问题
- 规则数量暴增:随着 Kubernetes 集群规模增长,Service 数量增多,iptables 规则逐渐增多,导致性能下降。
- 非增量更新机制:iptables 的规则更新是重新写入整个规则集,存在一定的时延,大规模集群环境中的性能问题更加显著。
- IPVS(IP Virtual Server):
- 基于 Linux 内核的四层负载均衡模块。
- 提供高性能的请求转发,规则数量增加时性能稳定。
- Service 流量仍然依靠 iptables 完成初始捕获和过滤,但数量有限,性能不受规则数量影响。
- 性能对比:
- IPVS 模式性能优于 iptables 模式,尤其是当 Kubernetes 集群规模较大(如 1,000 个 Service 和 10,000 个 Pod)时,表现差距显著 (见图 3-4)。
- 使用 Cilium 和“内核旁路”技术,绕过 iptables,实现更高性能的容器网络。
- iptables 的灵活性:
- 支持自定义链,为服务负载均衡和请求管理提供简单易用的规则配置。
- Kubernetes 的 kube-proxy 模式(iptables 模式)利用其灵活设计完成 Service 请求转发。
- 性能优化方向:
- 对于小型集群,iptables 模式仍然可用,但对于大规模集群要尽量避免。
- 推荐使用 IPVS 模式或 Cilium 的无 kube-proxy 模式,解决规则数量增长与集群性能下降问题。
自定义链是 iptables 中的扩展能力,用于实现复杂的数据包管理逻辑,支持链的跳转、递归等特性。随着容器技术和 Kubernetes 的广泛应用,自定义链被充分利用来构建灵活的网络流量管理和负载均衡方案。
Kubernetes Service 的流量管控原理
性能瓶颈
替代方案:IPVS 模式
进一步优化:Cilium 与无 kube-proxy 模式
iptables 与 kube-proxy 总结
总结一句话:自定义链通过扩展逻辑增强了 iptables 的灵活性,广泛应用于 Kubernetes 网络负载均衡,但随着 Service 数量增长,iptables 模式性能会显著下降,应选用 IPVS 或其他优化方案应对规模化场景。
- 连接跟踪模块 conntrack
- 跟踪连接:记录连接信息,用于数据包状态判断(如 ESTABLISHED)。
- 支持 NAT:用于实现 DNAT(目标地址转换)和 SNAT(源地址转换),帮助完成服务请求转发和响应返回。
- Service 流量处理:
- 请求:DNAT 将 Service 的地址转换为具体 Pod 的地址。
- 响应:SNAT 将 Pod 的地址转换回 Service 地址。
- 问题:如果同机内通信跳过网络层 conntrack(如通过网桥),会导致连接记录不完整,通信异常。
- 解决:通过设置
bridge-nf-call-iptables=1
,强制让网桥触发 iptables,保证 NAT 和连接记录完整性。
conntrack 是 Linux 内核中的一个模块,用于跟踪网络连接状态(如 TCP、UDP、ICMP)。它会为每个连接创建一个记录,并更新连接的状态(如 NEW、ESTABLISHED)。
核心功能:
通过命令 cat /proc/net/nf_conntrack 查看连接记录,输出了一个连接类型为 TCP,连接状态为 ESTABLISHED 的连接记录。
在 Kubernetes 中的应用:
一句话总结:conntrack 是连接和 NAT 的核心模块,Kubernetes 依赖它处理流量,需配置网桥规则确保通信正常。
- 内核旁路技术
- DPDK(Data Plane Development Kit):完全绕过内核,直接在用户空间处理数据包。
- XDP(eXpress Data Path):基于内核旁路思想,在内核空间处理数据包。
- RDMA(Remote Direct Memory Access):用于高效主机间数据传输,减少内核干预。
- 简介:eBPF 是一种强大的内核扩展机制,允许在内核空间运行经过安全验证的用户定义代码,几乎可以访问所有内核资源。
- 发展:从传统 BPF 发展而来,eBPF 扩展了功能,支持更多用途,如网络、性能监控和安全。
- 位置:位于网卡驱动层内,在数据包进入网络协议栈之前触发。
- 优势:
- 高性能:类似 DPDK 的处理速度。
- 内核集成:无需第三方库或专用 CPU。
- 灵活性:通过 eBPF 程序定义处理逻辑。
- 工作机制:
- 数据包接收:数据包在网卡驱动中被捕获。
- eBPF 处理:挂载在 XDP 钩子的 eBPF 程序处理数据包。
- 返回码决定行动:根据 eBPF 程序返回的动作决定数据包处理方式。
- 编写与编译:
- 编写:开发者使用 C 等高级语言编写 eBPF 程序,定义具体的网络数据包处理逻辑。
- 编译:使用专用编译器(如 Clang)将 C 代码编译为 eBPF 字节码。
- 加载与验证:
- 加载:通过高级语言库(如 Golang、Python)或工具(如
ip
命令、bpftool
)将编译后的 eBPF 字节码加载到内核。 - 验证(Verification):
- 内核中的 eBPF 验证器对程序进行静态检查,确保其安全性和正确性。
- 验证内容包括:无死循环、无越界内存访问、有限的栈空间使用等。
- 只有通过验证的 eBPF 程序才能被加载,避免对系统稳定性和安全性的潜在威胁。
- 挂载与执行:
- 挂载:将验证通过的 eBPF 程序挂载到特定的钩子点(如 XDP、TC、Tracepoints)。
- 执行:
- 当数据包或事件触发相应的钩子时,内核会执行挂载在该钩子的 eBPF 程序。
- eBPF 程序在内核态执行,处理数据包并根据逻辑返回相应的动作(如
XDP_DROP
、XDP_PASS
)。 - 管理与卸载:
- 管理:通过工具和命令监控 eBPF 程序的运行状态、性能指标。
- 卸载:在不需要时,可以安全地从钩子点卸载 eBPF 程序,释放资源。
- 高级功能:
- L3/L4 负载均衡:高效处理网络流量分发。
- 网络策略:精细控制容器间的网络访问。
- 观测与安全认证:实时监控与安全事件检测。
- 独立于 Netfilter:
- 连接跟踪与 NAT:Cilium 实现独立于 Netfilter 的连接跟踪和 NAT,避免传统工具的性能瓶颈。
- 专用查询命令:使用 Cilium 提供的命令如
cilium bpf nat list
和cilium bpf ct list global
查看 NAT 规则和连接记录。 - 优势:
- 高性能:通过 eBPF 和 XDP 实现高效的数据包处理,不受 Netfilter 规则数量限制。
- 可扩展性:适用于大规模 Kubernetes 集群,避免 iptables 规则暴增带来的性能问题。
- 低延迟与高吞吐:RDMA 能够实现极低的网络延迟(小于 3 μs)和高吞吐量(超过 400Gb/s)。
- CPU 资源消耗低:由于数据传输过程中无需频繁的 CPU 介入,RDMA 极大地减少了 CPU 负载,提高了整体系统性能。
- 绕过传统协议栈:应用程序通过专用接口(RDMA Verbs API)绕过主机操作系统和 TCP/IP 协议栈,直接进行内存间数据交换。
- Infiniband(无限带宽)
- 简介:专为 RDMA 设计,由 IBTA(InfiniBand Trade Association)在 2000 年提出。
- 性能:能够实现小于 3 μs 的时延和超过 400Gb/s 的网络吞吐量。
- 应用:在高性能计算(HPC)领域中广受青睐,如人工智能应用 ChatGPT 背后的分布式机器学习系统。
- 限制:
- 需要配置全套专用设备(专用网卡、交换机和网线),限制了其普及性。
- 技术架构封闭,无法兼容现有以太网标准。
- iWARP(Internet Wide Area RDMA Protocol)
- 简介:将 RDMA 封装在 TCP/IP 协议内。
- 性能:由于依赖 TCP/IP 的可靠性机制(如三次握手、拥塞控制),在 RDMA 性能上受限,失去了大部分优势。
- 应用:因先天设计缺陷,逐渐被业界抛弃。
- RoCE(RDMA over Converged Ethernet)
- 简介:由 IBTA 在 2010 年推出,将 Infiniband 的数据标准(IB Payload)移植到以太网。
- 版本:
- RoCEv1:基于二层以太网,局限于同一子网,无法跨子网通信。
- RoCEv2:基于三层 IP 网络,支持跨子网通信。
- 优势:
- 降低了 RDMA 技术的使用成本。
- 兼容现有以太网基础设施(支持 RoCE 的专用网卡和标准以太网交换机)。
- RoCEv2 解决了 RoCEv1 的跨子网通信限制,广泛应用于分布式存储、并行计算等通用数据中心场景。
- 应用:云计算平台 Azure 公开信息显示,至 2023 年,Azure 数据中心 70% 的流量已为 RDMA 流量。
- Infiniband:依靠专用设备(如专用网卡和交换机)来确保网络可靠性。
- RoCE:基于标准以太网实现 RDMA,要求基础设施具备无损以太网功能,以避免丢包对性能造成严重影响。常见的可靠性保障算法包括:
- DCQCN(Data Center Quantized Congestion Notification):由微软和 Mellanox 提出。
- HPCC(High Performance Congestion Control):由阿里巴巴提出。
- 内核旁路技术通过绕过传统内核处理路径,显著提升网络密集型应用的性能。
- eBPF 和 XDP作为内核集成的高性能数据包处理机制,提供了灵活且高效的网络流量管理能力。eBPF 程序通过编写、编译、加载、验证和挂载的流程,确保安全高效地在内核中执行。
- RDMA技术通过允许主机之间直接内存访问,实现了极低的网络延迟和高吞吐量,广泛应用于高性能计算、分布式存储和人工智能领域。RoCEv2 由于其低成本和高兼容性,已成为通用数据中心的主流 RDMA 实现方案。
- Cilium 等项目利用 eBPF 和 XDP,实现了高级网络功能,适应大规模集群需求,克服传统 Netfilter 的性能瓶颈。
在网络密集型应用中,Linux 内核频繁在内核态与用户态之间切换,加之复杂的网络协议栈处理,往往成为性能瓶颈。为提升性能,内核旁路(Kernel Bypass)技术应运而生,通过绕过内核直接处理网络数据包,从而减少开销。主要代表技术包括:
eBPF 和 快速数据路径 XDP
eBPF(Extended Berkeley Packet Filter)
XDP(快速数据路径)
eBPF 程序的加载、验证与执行
为了安全高效地在内核中运行用户定义的 eBPF 程序,Linux 内核执行了一系列严格的步骤:
XDP 支持的五种返回动作
返回码 | 描述 |
XDP_ABORTED | 处理时发生错误或异常。 |
XDP_DROP | 直接在网卡驱动层丢弃数据包,常用于过滤无效或恶意数据包(如 DDoS 防护)。 |
XDP_PASS | 允许数据包继续进入内核的网络协议栈,结合传统处理方式。 |
XDP_TX | 重新发送数据包到入站网络接口,适用于快速转发和修改数据包。 |
XDP_REDIRECT | 将数据包重定向到其他网卡或 CPU,结合 AF_XDP 可将数据包直接送往用户空间。 |
eBPF 钩子类型及其用途
钩子名称 | 描述 |
TC(Traffic Control) | 位于网络流量控制层,处理流经内核的网络数据包,可在数据包进入或离开网络栈的各个阶段触发。 |
XDP | 位于网卡驱动层,处理收到的网络数据包,用于高性能操作如 DDoS 防护和负载均衡。 |
Tracepoints | 内核代码中的静态探测点,用于性能分析、故障排查和监控,分布在调度器、文件系统等子系统中。 |
LSM(Linux Security Modules) | 安全模块框架中的钩子,触发 eBPF 程序以实施安全策略和访问控制,如文件和网络访问。 |
内核旁路技术的应用实例:Cilium
Cilium 是基于 eBPF 和 XDP 技术构建的高级容器网络解决方案,具有以下特点:
远程直接内存访问 RDMA
近年来,随着人工智能、分布式训练和分布式数据存储的快速发展,对网络传输性能提出了更高的要求。传统以太网在网络延迟、吞吐量和 CPU 资源消耗方面存在先天不足。在此背景下,广泛应用于高性能计算(HPC)领域的 RDMA(Remote Direct Memory Access,远程直接内存访问)技术,因其卓越的性能,逐渐成为满足上述需求的首选解决方案。
RDMA 的基本原理
RDMA 的设计灵感来源于 DMA(Direct Memory Access,直接内存访问),是一种允许主机之间直接访问彼此内存的技术。其主要特点包括:
图 3-10 RDMA 技术栈
RDMA 的主要实现协议
RDMA 网络的协议实现主要分为三类:Infiniband、RoCE 和 iWARP,它们各具特点:
RDMA 网络的可靠性保障
RDMA 网络对丢包极为敏感,任何数据包的丢失都可能导致大量重传,降低传输性能。为了确保网络的可靠性:
这些算法通过底层技术手段优化以太网流量控制,确保 RoCE 网络在高负载下依然保持低延迟和高吞吐。
总结
一句话总结:内核旁路技术通过 eBPF、XDP 和 RDMA 等机制,在不牺牲内核集成性的前提下,实现了高效、灵活的网络数据包处理,广泛应用于现代容器化和大规模集群环境中,显著提升了网络性能和系统可扩展性。
- Linux 网络虚拟化
- 网络接口卡(NIC):一台物理服务器需要插入一块网卡(NIC),作为其与外界网络通信的硬件接口。
- 物理连接:通过网线将 NIC 连接到以太网交换机,加入到局域网(LAN)中。
- 网络交换:交换机负责在多个设备之间转发数据包,实现局域网内设备的互联互通。
- 物理机相当于一辆车。
- *网卡(NIC)**像车上的轮子,负责与道路(网络)连接。
- 交换机类似于交通枢纽(如交叉路口),负责车辆(数据包)的转发和分配。
- 网络命名空间(Network Namespace):允许在同一台物理机上创建多个独立的网络环境。每个命名空间拥有自己独立的网络接口、路由表、防火墙规则等,互不干扰。
- 定义与功能:
- Veth 对(Veth Pair):由一对虚拟以太网设备组成,类似于一根双头网线的两端。
- 数据传输:在两个端点之间传递以太网帧,实现不同命名空间或命名空间与桥接设备之间的通信。
- 工作流程:
- 创建 Veth 对:
- 分配到网络命名空间:
- 配置 IP 地址并激活接口:
- 测试连接性:
- 局限性:
- 扩展性差:每增加一个通信对象(如容器),都需要创建一对新的 Veth 设备,管理复杂。
- 定义与功能:
- Linux Bridge(br0):在 Linux 系统中模拟物理交换机的功能,用于连接多个网络接口,实现二层(数据链路层)通信。
- 工作流程:
- 创建桥接设备:
- 连接 Veth 设备到桥接设备:
- 配置命名空间内的接口并测试通信:
- 优势:
- 扩展性好:多个 Veth 设备可以同时连接到桥接设备,简化管理。
- 动态性强:可以随时添加或移除连接到桥接设备的网络接口。
- 定义与功能:
- TUN(Network TUNnel):工作在第三层(网络层),处理 IP 数据包。适用于需要基于 IP 层的应用,如 VPN 隧道。
- TAP(Network TAP):工作在第二层(数据链路层),处理以太网帧。适用于需要处理完整以太网层数据的应用,如虚拟机网络桥接。
- 工作机制:
- 创建 TUN/TAP 设备:
- 将 TUN/TAP 设备桥接到 Linux Bridge:
- 配置 IP 地址并进行数据传输。
- TUN 像是一个用于处理纯粹通信内容的管道(仅限 IP 层)。
- TAP 像是一个能够读取和转发包含封装信息(如 MAC 地址)的通信线路。
- 应用案例:
- VPN 隧道:利用 TUN 设备将数据包封装后通过物理网络传输,构建虚拟专用网络。
- 虚拟机网络桥接:通过 TAP 设备与 Linux Bridge 连接,实现虚拟机之间的以太网通信。
- 软件定义网络(SDN):通过将网络的控制平面与数据平面分离,利用软件进行网络配置和管理,实现高度可编程和灵活的网络架构。
- Underlay 网络:物理网络,负责基础的数据传输。
- Overlay 网络:虚拟网络,构建在 Underlay 之上,通过隧道技术(如 VXLAN)实现多租户隔离和灵活的网络拓扑。
- VXLAN(Virtual Extensible LAN):“将数据包封装在另一个数据包中,在现有物理网络之上创建一个虚拟网络”。,实现大规模、多租户的虚拟网络环境。
- 其他隧道技术:如 Geneve、STT,均用于在物理网络之上构建虚拟网络,提供隔离和灵活性。
- 灵活性:能够动态调整网络拓扑,适应云原生环境的快速变化。
- 可扩展性:支持大规模集群和多租户场景,简化物理网络配置。
- 跨主机容器通信:通过 Overlay 网络,实现不同物理主机上的容器之间的互联互通,无需依赖底层物理网络的复杂配置。
- 创建网络命名空间:
- 创建 Veth 对并分配到命名空间:
- 创建并配置 Linux Bridge:
- 配置命名空间内的接口:
- 测试网络连通性:
- 物理网络与虚拟网络的对应关系:
- 物理机 ⇨ 网络命名空间
- 网卡(NIC) ⇨ 虚拟网卡(Veth、TUN/TAP)
- 以太网交换机 ⇨ Linux Bridge(虚拟交换机)
- 虚拟网络设备的角色:
- Veth:连接不同网络命名空间,类似于内部网络电缆。
- TUN/TAP:处理不同层级的数据包,适用于特定应用如 VPN 或虚拟机网络。
- Linux Bridge:作为虚拟交换机,连接多个虚拟网卡,实现二层通信。
- 网络虚拟化的优势:
- 隔离性:每个网络命名空间拥有独立的网络环境,互不干扰。
- 灵活性与可扩展性:通过虚拟交换机和 Overlay 网络,轻松管理大规模、多租户的网络环境。
- 高效性:减少物理设备依赖,实现软件层面的网络管理与配置。
- 现代应用场景:
- 容器化平台(如 Docker、Kubernetes):利用网络命名空间和虚拟交换机,实现容器间的网络隔离与互联。
- 分布式系统:通过 Overlay 网络技术(如 VXLAN),实现跨数据中心的高效通信。
Linux 网络虚拟化的核心技术主要是网络命名空间和各种虚拟网络设备
网络命名空间
Linux 内核从 2.4.19 版本起,开始逐步集成多种命名空间技术,以实现对各类资源的隔离。
深入理解 Linux 网络虚拟化:从物理网络到虚拟网络设备
为了更清晰地理解 Linux 网络虚拟化及其相关的虚拟网络设备,让我们先从物理网络的操作入手,进而类比和解释虚拟网络在网络命名空间中的实现方式。
1. 物理网络的基本操作
物理机与网络通信的常规流程如下:
类比:
2. 虚拟网络环境的引入
在现代数据中心和云原生环境中,单一物理机往往需要运行多个隔离的应用或容器。为了实现这种隔离和灵活的网络配置,Linux 引入了网络命名空间和各种虚拟网络设备。
核心概念:
类比: 就像在一栋楼中分隔出多个独立的房间,每个房间都有自己的门、窗和内部布局,彼此之间互不影响。
3. 虚拟网络设备的构建与连接
为了让不同网络命名空间之间或与外界进行通信,需要引入各种虚拟网络设备。这些设备在功能上类似于物理网络设备,但运行在软件层面。
a. 虚拟网卡 Veth(Virtual Ethernet)
类比: Veth 对像是一根内部连接两台虚拟计算机的网线,数据通过网线在两端设备间传输。
b. 虚拟交换机 Linux Bridge
类比: Linux Bridge 像是一座内部网络交换机,连接多个虚拟网卡,让它们像连接到同一交换机的物理设备一样进行通信。
c. 虚拟网络设备 TUN 和 TAP
类比:
4. 虚拟网络通信技术:软件定义网络(SDN)与 Overlay 网络
背景需求:
在云原生环境下,容器的动态部署、扩展和跨数据中心迁移需要灵活且可编程的网络配置。而传统基于物理设备的网络拓扑难以满足这种高频率的调整需求。
核心理念:
SDN 思想的核心是,在现有的物理网络之上新增一层虚拟网络,将控制平面(操作系统和各类网络控制软件等)和数据平面(底层通信的物理设备,以及各类通信协议等)解耦,将网络服务从底层硬件设备中抽象出来,由代码直接编程控制。
模型:
主要技术:
类比: 类似于在公路网络(Underlay)上构建高速公路(Overlay),允许不同的虚拟车辆(数据包)在高速公路上独立行驶,而不干扰原有的道路交通。
优势:
应用案例:
5. 综合应用示例:从物理网络到虚拟交换机
实际操作示例:建立网络命名空间与虚拟交换机的通信
结果: 两个网络命名空间 ns1 和 ns2 通过 Veth 设备和 Linux Bridge 实现了二层网络的互联,可以相互通信,仿佛连接在同一个物理交换机上一样。
类比: 通过在两间独立房间(网络命名空间)内分别安装门(Veth 设备),并将这两扇门连接到同一个内部走廊(Linux Bridge),使得房间内的人员(数据包)可以自由往来,不需要通过外部公共区域。
6. 总结
一句话总结:通过网络命名空间和各类虚拟网络设备(如 Veth、TUN/TAP、Linux Bridge),Linux 实现了灵活、高效的网络虚拟化,支持现代云原生与容器化环境中复杂、多变的网络需求,极大提升了系统的可扩展性与管理便捷性。
- VLAN 与 VXLAN 简明总结
- 定义:
- VLAN(Virtual Local Area Network):通过逻辑分割,将一个物理网络划分为多个独立的广播域,减少广播流量,提升网络管理效率。
- 作用:
- 划分广播域:每个VLAN是一个独立的广播域,防止广播风暴。
- 逻辑隔离:将不同部门或功能的设备隔离在不同VLAN中,提高安全性。
- 实现方式:
- VLAN Tag:在以太帧中添加VLAN ID(12位),标识数据包所属的VLAN。
- 交换机识别:支持VLAN的交换机根据VLAN ID转发数据包,确保同VLAN内通信。
- 单臂路由(Router-on-a-Stick):
- 概念:使用一个物理接口,通过子接口(Sub-Interface)处理多个VLAN,实现不同VLAN间的通信。
- 配置步骤:
- 缺点:
- 扩展性有限:最多支持4096个VLAN,不满足大型数据中心需求。
- 跨数据中心复杂:二层VLAN难以跨多数据中心扩展,管理成本高。
- 定义:
- VXLAN(Virtual eXtensible Local Area Network):一种隧道封装技术,通过在三层网络上封装二层以太网帧,实现大规模、多租户的虚拟网络。
- 优势:
- 高扩展性:使用24位VNI(VXLAN Network Identifier),支持多达1677万的虚拟网络,远超VLAN。
- 跨数据中心通信简便:基于三层网络,便于跨物理网络扩展,适合容器化和虚拟化环境。
- 灵活管理:无需依赖复杂的物理网络配置,通过软件配置即可实现网络隔离与互联。
- 工作原理:
- 封装机制:将L2以太网帧封装在L4 UDP报文中,通过L3网络传输。
- VTEP(VXLAN Tunnel Endpoint):负责封装和解封VXLAN报文的设备,通常为虚拟网络接口。
- VXLAN 报文结构:
- 原始以太网帧:待传输的数据包。
- VXLAN Header:包含24位VNI,标识虚拟网络。
- UDP Header:目的端口固定为4789,源端口随机。
- 外层 IP Header:源和目的IP为宿主机IP。
- 外层 MAC Header:源和目的MAC为宿主机MAC。
- 配置示例:
- 通信流程:
- 封装:源宿主机的VTEP将以太网帧封装为VXLAN报文。
- 传输:通过物理网络传输至目标宿主机。
- 解封:目标宿主机的VTEP解封VXLAN报文,获取原始数据帧并转发至目标容器或虚拟机。
- 类比:
- VLAN:在同一物理道路上设置多个车道,每个车道限制特定车辆通行。
- VXLAN:在公路网络上架设高速公路(隧道),允许更多种类和数量的车辆独立通行。
- VLAN:
- 办公室网络隔离
- 不同部门间的网络分割
- 基础数据中心网络管理
- VXLAN:
- 云计算环境中的多租户隔离
- 容器编排平台(如Kubernetes)的网络方案
- 跨数据中心的虚拟网络互联
- VLAN适用于较小规模的网络环境,通过逻辑分割提升网络管理和安全性,但在扩展性和跨数据中心通信上存在限制。
- VXLAN作为VLAN的扩展,提供了更高的扩展性和灵活性,适应现代数据中心和云原生环境的需求,支持大规模、多租户的网络架构。
1. 虚拟局域网(VLAN)
2. 虚拟可扩展局域网(VXLAN)
3. VLAN 与 VXLAN 对比
特性 | VLAN | VXLAN |
扩展性 | 支持最多4096个VLAN | 支持约1677万个VNI |
架构层级 | 二层网络(Layer 2) | 三层网络(Layer 3)上的二层隧道 |
跨数据中心 | 复杂,需要三层路由器支持 | 简单,通过三层网络直接隧道 |
适用场景 | 小规模或中等规模网络 | 大规模数据中心、多租户环境、容器化网络 |
配置复杂度 | 相对简单,但扩展受限 | 需要设置VTEP,有一定的软件配置要求 |
4. 应用场景
5. 总结
通过理解VLAN和VXLAN的基本原理及其优缺点,可以更有效地设计和管理现代复杂的网络环境,满足不同规模和需求的网络隔离与互联要求。
Relate Posts