K8S架构与组件完全解析
目录
K8S-组件介绍
一、概述
K8S的由来
K8S的功能
K8S解决的问题
K8S的特性
二、K8S架构与组件
K8S架构
K8S组件
master 节点组件
Kube-apiserver
Kube-controller-manager
Kube-scheduler
etcd
node节点组件
Kubelet
Kube-Proxy
Controller Runtime
Pod
三、K8S核心概念
四、Kubetnetes 涉及的端口
Calico
Flannel
Weave Net
Cilium
选择网络解决方案
覆盖网络
工作原理
常用技术
优点
缺点
应用场景
Flannel
工作原理
核心组件
特点
使用场景
Calico
工作原理
特点
使用场景
K8s网络原理
一、K8s网络模型
二、Docker的网络模型
三、网络的命名空间
1.网络命名空间的实现
2.网络命名空间的操作
3.网络命名空间的一些技巧
四、Veth设备对
1.Veth设备对的操作命令
2.Veth设备对如何查看对端
五、网桥
1.Linux网桥的实现
2.网桥的常用操作命令
六、Iptables/Netfilter
七、路由
1.路由表的创建
2.路由表的查看
八、Docker的网络实现
1.查看Docker启动后的系统情况
2.查看容器启动后的情况(容器无端口映射)
3.查看容器启动后的情况(容器端口映射)
九、K8s的网络实现
1.容器到容器的通信
2.Pod之间的通信(背会)
十、Pod和Service网络实战
K8s四层网络
前言
K8s网络的实现原理
默认网络模式
常见网络组件
1. Flannel
2. Calico
3. Weave Net
Ingress的使用
Ingress的实现原理
Ingress的YAML示例
使用场景
查看所用网络组件
不单独配置网络组件的影响
配置网络组件的好处
类似组件
1. Service
2. API Gateway
选择合适的网络插件和Ingress配置
总结
K8S-组件介绍
一、概述
Kubernetes 简称 k8s,是支持云原生部署的一个平台,起源于谷歌。谷歌早在十几年之前就对其应用,通过容器方式进行部署。
k8s 本质上就是用来简化微服务的开发和部署的,关注点包括自愈和自动伸缩、调度和发布、调用链监控、配置管理、Metrics 监控、日志监控、弹性和容错、API 管理、服务安全等,k8s 将这些微服务的公共关注点以组件形式封装打包到 k8s 这个大平台中,让开发人员在开发微服务时专注于业务逻辑的实现,而不需要去特别关心微服务底层的这些公共关注点,大大简化了微服务应用的开发和部署,提高了开发效率。
-
官网:Kubernetes
-
GitHub:https://github.com/kubernetes/kubernetes
K8S的由来
-
K8S由google的Borg系统(博格系统,google内部使用的大规模容器编排工具)作为原型,后经GO语言延用Borg的思路重写并捐献给CNCF基金会开源。
-
云原生基金会(CNCF)于2015年12月成立,隶属于Linux基金会。CNCF孵化的第一个项目就是Kubernetes,随着容器的广泛使用,Kubernetes已经成为容器编排工具的事实标准。
K8S的功能
-
跨主机编排容器。
-
更充分地利用硬件资源来最大化地满足企业应用的需求。
-
控制与自动化应用的部署与升级。
-
为有状态的应用程序挂载和添加存储器。
-
线上扩展或缩减容器化应用程序与它们的资源。
-
声明式的容器管理,保证所部署的应用按照我们部署的方式运作。
-
通过自动布局、自动重启、自动复制、自动伸缩实现应用的状态检查与自我修复。
-
为多个容器提供服务发现和负载均衡,使得用户无需考虑容器IP问题。
K8S解决的问题
K8S 解决了裸跑 Docker 的若干痛点:
-
单机使用,无法有效集群
-
随着容器数量的上升,管理成本攀升
-
没有有效的容灾、自愈机制
-
没有预设编排模板,无法实现快速、大规模容器调度
-
没有统一的配置管理中心工具
-
没有容器生命周期的管理工具
-
没有图形化运维管理工具
K8S 提供了容器编排,资源调度,弹性伸缩,部署管理,服务发现等一系列功能。
K8S的特性
-
弹性伸缩
使用命令、UI 或者基于 CPU 使用情况自动快速扩容和缩容应用程序实例,保证应用业务高峰并发时的高可用性;业务低峰时回收资源,以最小成本运行服务。
-
自我修复
在节点故障时重新启动失败的容器,替换和重新部署,保证预期的副本数量;杀死健康检查失败的容器,并且在未准备好之前不会处理客户端请求,确保线上服务不中断。
-
自动发布(默认滚动发布模式)和回滚
K8S 采用滚动更新策略更新应用,一次更新一个 Pod,而不是同时删除所有 Pod,如果更新过程中出现问题,将回滚更改,确保升级不影响业务。
-
集中化配置管理和密钥管理
管理机密数据和应用程序配置,而不需要把敏感数据暴露在镜像里,提高敏感数据安全性。并可以将一些常用的配置存储在 K8S 中,方便应用程序使用。
-
存储编排,支持外挂存储并对外挂存储资源进行编排
挂载外部存储系统,无论是来自本地存储,公有云(如 AWS),还是网络存储(如 NFS、GlusterFS、Ceph)都作为集群资源的一部分使用,极大提高存储使用灵活性。
-
任务批处理运行
提供一次性任务,定时任务;满足批量数据处理和分析的场景。
二、K8S架构与组件
K8S架构
k8s 总体架构采用了经典的 maste/slave 架构模式,分 master 节点和 worker 节点,节点可以是虚拟机也可以是物理机。
K8S组件
master 节点组件
Kube-apiserver
-
用于暴露 Kubernetes API,任何资源请求或调用操作都是通过 kube-apiserver 提供的接口进行。以 HTTPRestful API 提供接口服务,所有对象资源的增删改查和监听操作都交给 API Server 处理后再提交给 Etcd 存储。
-
可以理解成 API Server 是 K8S 的请求入口服务。API Server 负责接收 K8S 所有请求(来自 UI 界面或者 CLI命令行工具), 然后根据用户的具体请求,去通知其他组件干活。可以说 API Server 是 K8S 集群架构的大脑。
Kube-controller-manager
-
运行管理控制器,是 K8S 集群中处理常规任务的后台线程,是 K8S 集群里所有资源对象的自动化控制中心。在 K8S 集群中,一个资源对应一个控制器,而 Controller manager 就是负责管理这些控制器的。
-
由一系列控制器组成,通过 API Server 监控整个集群的状态,并确保集群处于预期的工作状态,比如当某个 Node意外宕机时,Controller Manager 会及时发现并执行自动化修复流程,确保集群始终处于预期的工作状态。
控制器类型 作用 Node Controller(节点控制器) 负责在节点出现故障时发现和响应。 Replication Controller(副本控制器) 负责保证集群中一个 RC(资源对象 Replication Controller)所关联的 Pod 副本数始终保持预设值。可以理解成确保集群中有且仅有 N 个 Pod 实例,N 是 RC 中定义的 Pod 副本数量。 EndpointsController(端点控制器) 填充端点对象(即连接 Services 和 Pods),负责监听 Service 和对应的 Pod副本的变化。 可以理解端点是一个服务暴露出来的访问点,如果需要访问一个服务,则必须知道它的 endpoint。 Service Account & Token Controllers(服务帐户和令牌控制器) 为新的命名空间创建默认帐户和 API 访问令牌。 ResourceQuota Controller(资源配额控制器) 确保指定的资源对象在任何时候都不会超量占用系统物理资源。 Namespace Controller(命名空间控制器) 管理 namespace 的生命周期。 Service Controller(服务控制器) 属于 K8S 集群与外部的云平台之间的一个接口控制器。
Kube-scheduler
-
是负责资源调度的进程,根据调度算法为新创建的 Pod 选择一个合适的 Node 节点。
-
可以理解成 K8S 所有 Node 节点的调度器。当用户要部署服务时,Scheduler 会根据调度算法选择最合适的 Node 节点来部署 Pod。
-
预选策略(predicate)
-
优选策略(priorities)
etcd
-
K8S 的存储服务。etcd 是分布式键值存储系统,存储了 K8S 的关键配置和用户配置,K8S 中仅 API Server 才具备读写权限,其他组件必须通过 API Server 的接口才能读写数据。
node节点组件
在 Kubernetes 集群中,在每个 Node(又称 Worker Node)上都会启动一个 kubelet 服务进程。该进程用于处理 Master 下发到本节点的任务,管理 Pod 及 Pod 中的容器。每个 kubelet 进程都会在 API Server 上注册节点自身的信息,定期向 Master 汇报节点资源的使用情况,并通过 cAdvisor 监控容器和节点资源。
Kubelet
-
Node 节点的监视器,以及与 Master 节点的通讯器。Kubelet 是 Master 节点安插在 Node 节点上的“眼线”,它会定时向 API Server 汇报自己 Node 节点上运行的服务的状态,并接受来自 Master 节点的指示采取调整措施。
-
从 Master 节点获取自己节点上 Pod 的期望状态(比如运行什么容器、运行的副本数量、网络或者存储如何配置等), 直接跟容器引擎交互实现容器的生命周期管理,如果自己节点上 Pod 的状态与期望状态不一致,则调用对应的容器平台接口(即 docker 的接口)达到这个状态。
-
管理镜像和容器的清理工作,保证节点上镜像不会占满磁盘空间,退出的容器不会占用太多资源。
Kube-Proxy
-
在每个 Node 节点上实现 Pod 网络代理,是 Kubernetes Service 资源的载体,负责维护网络规则和四层负载均衡工作。 负责写入规则至iptables、ipvs实现服务映射访问的。
-
Kube-Proxy 本身不是直接给 Pod 提供网络,Pod 的网络是由 Kubelet 提供的,Kube-Proxy 实际上维护的是虚拟的 Pod 集群网络。
-
Kube-apiserver 通过监控 Kube-Proxy 进行对 Kubernetes Service 的更新和端点的维护。
-
在 K8S 集群中微服务的负载均衡是由 Kube-proxy 实现的。Kube-proxy 是 K8S 集群内部的负载均衡器。它是一个分布式代理服务器,在 K8S 的每个节点上都会运行一个 Kube-proxy 组件。
Controller Runtime
-
容器引擎,如:docker、containerd,运行容器,负责本机的容器创建和管理工作。
-
当 kubernetes 把 pod 调度到节点上,节点上的 kubelet会指示 docker 启动特定的容器。接着,kubelet 会通过 docker 持续地收集容器的信息, 然后提交到主节点上。docker 会如往常一样拉取容器镜像、启动或停止容器。不同点仅仅在于这是由自动化系统控制而非管理员在每个节点上手动操作的。
Pod
k8s 中特有的一个概念,可以理解为对容器的包装,是 k8s 的基本调度单位,一个 Pod 代表集群上正在运行的一个进程,实际的容器是运行在 Pod 中的, 可以把 Pod 理解成豌豆荚,而同一 Pod 内的每个容器是一颗颗豌豆。一个节点可以启动一个或多个 Pod。生产环境中一般都是单个容器或者具有强关联互补的多个容器组成一个 Pod。
三、K8S核心概念
概念 | 作用 |
---|---|
cluster | 集群,Cluster指的是由Kubernetes管理的一组节点和资源池。 由Master和多个Worker节点组成,Master用于集群管理和控制,而Worker节点用于运行应用程序和Pod。提供了一种可扩展的、弹性的平台,用于分布式应用程序和容器的部署和管理。 |
node | 节点,是集群中的工作节点,用于运行Pod和容器。 是物理或虚拟机器,具有足够的资源(CPU、内存、存储)来运行容器化的应用。Kubernetes会自动将Pod调度到可用的节点上,以实现负载均衡和高可用性。 |
Container | 容器, 容器是一种轻量级的虚拟化技术,用于隔离和运行应用程序及其依赖项。将应用程序与其运行时环境进行隔离,并提供了一个独立的运行空间,使得应用程序可以在不同的环境中移植和部署。 |
Namespace | 命名空间, Namespace用于在Kubernetes集群中划分虚拟的资源隔离区域。 可用于组织和管理资源,以及为不同的团队、项目或环境提供逻辑分离。 在一个集群中,可以有多个命名空间,每个命名空间都有自己的一组资源和访问策略。 |
Pod | 容器组,Kubernetes基本调度单位,可以包含一个或多个容器。这些容器共享相同的网络和存储资源,并运行在同一主机上。 提供了一个隔离的运行环境,为容器提供了共享的IP地址和端口空间。 通常,Pod用于运行关联的容器,例如共享相同的资源和上下文的应用程序。 |
Service | 服务, Service定义了一组Pod的稳定网络终点,通过标签选择器与这些Pod进行关联。充当入口和负载均衡器,封装了一组Pod,并提供一个持久的访问地址(Cluster IP或Node IP)。 还支持内部服务发现和跨集群通信。 |
ReplicaSet | 副本集,ReplicaSet用于定义Pod的副本数量和在集群中的运行策略,确保指定的Pod副本数始终运行,并根据需要进行自动扩展或收缩。当Pod由于故障或节点故障而终止时,ReplicaSet会自动重新启动新的Pod副本。 |
Deployment | 部署,Deployment是一种用于声明化管理Pod和ReplicaSet的资源对象。可以定义Pod的期望状态,自动创建和更新ReplicaSet,并实现滚动升级和回滚策略,以确保无缝的应用程序更新。 |
ConfigMap | 配置映射,ConfigMap是一种存储配置数据的资源对象,用于将配置参数传递给应用程序。可以存储环境变量、配置文件、命令行参数等。 可以与Pod或容器相关联,并在容器启动时注入相关的配置信息。 |
Secrets | 密钥用于安全地存储和管理敏感信息,如密码、凭证等。 -Kuberentes中的Secret是一种资源对象,用于存储和传递加密的数据。 -密钥可以被挂载到容器中,用于应用程序的配置文件或认证凭证。 |
DaemonSet | DaemonSet是Kubernetes中的一个重要概念,提供了一种简单且可靠的方式来在集群的所有节点上运行相同的Pod,适用于许多系统级任务和后台进程的部署场景。 1.每个节点一个Pod副本:DaemonSet会在集群的每个节点上启动一个Pod副本,以确保每个节点都有该Pod的运行实例。 2. 系统级任务和守护进程:DaemonSet通常用于运行系统级任务和守护进程,例如日志收集器、监控代理和网络代理等。这些任务通常需要在每个节点上运行,并且要与节点的生命周期同步。 3. 自动扩缩容:当有新节点加入集群或某个节点从集群中移除时,DaemonSet会自动检测节点的状态变化,并相应地启动或终止Pod副本。 4. 节点亲和性规则:可以通过节点亲和性规则来选择在哪些节点上运行DaemonSet的Pod。这允许你将特定的任务或服务与特定类型的节点关联起来。 5. 资源限制和调度约束:可为DaemonSet中的Pod指定资源限制和调度约束,以确保它们在节点上均匀分布并满足资源需求。 |
四、Kubetnetes 涉及的端口
角色 | 协议 | 方向 | 端口范围 | 组件 | 使用者 |
---|---|---|---|---|---|
master | TCP | 入站 | 6443 | kube-apiserver | 所有 |
TCP | 入站 | 2379~2380 | etcd | kube-apiserver | |
TCP | 入站 | 10250 | kubelet | kube-apiserver,自身 | |
TCP | 入站 | 10259 | kube-scheduler | 自身 | |
TCP | 入站 | 10257 | kube-controller-manager | 自身 | |
node | TCP | 入站 | 10250 | kubelet | kube-apiserver |
TCP | 入站 | 30000~32767 | Service NodePort | 自身 |
Kubernetes (K8s) 网络解决方案是指在 Kubernetes 集群中实现容器网络通信的各种技术和工具。这些解决方案的设计目的是为了满足 Kubernetes 网络模型的要求,即:
-
所有 Pod 都必须能够无需 NAT 就能互相通信。
-
所有节点必须能够无需 NAT 就能与所有 Pod 通信。
-
Pod 在重新调度时保持相同的 IP 地址。
这些网络解决方案遵循 CNI(Container Network Interface)规范,提供插件以集成不同的网络技术。下面是一些主要的 Kubernetes 网络解决方案及其底层原理、优缺点:
Calico
-
原理:Calico 使用纯三层网络来提供 Pod 间通信,通过使用 IP 路由而不是覆盖网络,以减少网络复杂性和性能开销。它使用 BGP(边界网关协议)来广播和学习路由,支持网络策略来控制 Pod 间的流量。
-
优点:高性能,支持大规模集群;丰富的网络策略;直接使用物理网络,不需要封包解包的开销。
-
缺点:配置和管理相对复杂,对网络知识要求较高;在某些环境下(如较老的数据中心),对 BGP 的支持可能受限。
Flannel
-
原理:Flannel 是一个简单的覆盖网络解决方案,为每个 Pod 提供一个唯一的 IP。它使用 etcd 存储网络配置信息,支持多种后端,如 VXLAN、IPsec 和 AWS VPC。
-
优点:部署和配置简单,适合小型到中型集群;对新手友好。
-
缺点:性能不如基于路由的解决方案;覆盖网络可能会增加一些网络延迟。
Weave Net
-
原理:Weave Net 创建一个虚拟网络,连接不同 Docker 容器。它自动发现和管理集群中的容器,无需额外配置,支持网络策略,并提供服务发现机制。
-
优点:安装配置简单;自动处理网络分区和故障恢复;不依赖于集群底层的网络基础设施。
-
缺点:相较于其他解决方案,性能可能较低;在大规模集群中可能面临管理挑战。
Cilium
-
原理:Cilium 基于最新的 Linux 内核技术 BPF(Berkeley Packet Filter),提供网络安全、可观测性和负载均衡。它能够理解应用层(Layer 7)的协议,并在此基础上实施安全策略。
-
优点:提供应用层的网络策略和安全;支持多种网络模式,包括透明的服务网格;高性能和可扩展性;丰富的可观测性和监控功能。
-
缺点:较新的项目,社区相对较小;需要较新的 Linux 内核支持 BPF。
选择网络解决方案
选择合适的 Kubernetes 网络解决方案时,需要考虑以下因素:
-
集群规模:大型集群可能更适合使用 Calico 或 Cilium 这样的基于路由的解决方案。
-
性能需求:对于对网络性能有高要求的应用,基于路由的解决方案通常比覆盖网络性能更好。
-
网络策略:如果需要丰富的网络策略支持,Calico 和 Cilium 是较好的选择。
-
环境兼容性:需要考虑解决方案是否支持当前的基础设施和云环境。
-
易用性和管理:对于小型或测试环境,Flannel 或 Weave Net 可能因其简单性而更受青睐。
综合考虑这些因素后,你可以根据具体需求和偏好选择最合适的 Kubernetes 网络解决方案。
覆盖网络
覆盖网络(Overlay Network)是一种网络虚拟化技术,它在现有的网络基础设施之上创建了一个虚拟的网络层。这个虚拟网络使得网络上的设备(如服务器、容器或虚拟机)能够彼此通信,就像它们连接在同一个物理网络一样,即使实际上它们可能分布在不同的物理网络中。
工作原理
-
数据封装:覆盖网络通过封装原始数据包来工作。当数据从一个设备发送到另一个设备时,原始数据包会被封装在另一个数据包中。这个外层的数据包有自己的头部信息,指定了虚拟网络内的源地址和目的地址。
-
隧道技术:封装后的数据包通过隧道在物理网络中传输。这些隧道可以跨越不同的网络和互联网,允许分布在不同位置的设备安全地进行通信。
-
数据解封装:当封装的数据包到达目的地后,外层的数据包会被去除(解封装),恢复原始数据包,然后将其传递给接收设备。
常用技术
-
VXLAN (Virtual Extensible LAN):一种广泛使用的覆盖网络技术,可以支持大规模的虚拟网络。
-
NVGRE (Network Virtualization using Generic Routing Encapsulation):另一种覆盖网络技术,使用 GRE 封装来虚拟化网络层。
-
STT (Stateless Transport Tunneling):专为虚拟化环境设计的覆盖网络协议,优化了数据中心内的通信。
优点
-
灵活性:覆盖网络允许你在不改变底层物理网络基础设施的情况下创建复杂的网络拓扑。
-
可扩展性:可以轻松跨越不同的物理网络和数据中心创建虚拟网络,支持大规模部署。
-
安全性:封装技术和隧道技术提供了数据传输的隔离和安全保护。
缺点
-
性能开销:数据封装和解封装过程增加了额外的计算开销,可能会影响网络性能。
-
复杂性:管理覆盖网络的复杂性随着网络规模和使用的技术而增加。
应用场景
覆盖网络在多种环境和用例中非常有用,尤其是在需要高度灵活性和跨网络通信能力的场景,例如:
-
云计算和数据中心:在不同物理位置的虚拟机或容器之间提供灵活的网络通信。
-
多租户环境:在共享的物理网络基础设施上创建隔离的网络环境,为每个租户提供独立的网络空间。
-
容器编排:在 Kubernetes 等容器编排系统中,覆盖网络使得跨主机的容器可以无缝通信。
覆盖网络通过提供额外的虚拟化网络层,解决了现代数据中心和云环境中网络通信和隔离的挑战,使得网络设计和管理更加灵活和动态。
Flannel
Flannel 是一个简单的 Kubernetes 网络解决方案,用于为集群中的 Pod 提供一个覆盖网络。它让 Pod 能够无视底层网络基础设施,相互之间进行通信,就好像它们在同一个以太网交换机上一样。Flannel 是由 CoreOS 开发的,现在是 CNCF(云原生计算基金会)的一部分。
工作原理
Flannel 在 Kubernetes 集群的每个节点上运行一个代理进程,这些代理负责维护 Pod 网络的配置和状态。Flannel 使用一种中央数据存储(如 etcd)来保持集群的网络配置信息。
-
网络分配:当 Flannel 启动时,它会从中央数据存储中获取一个全局网络配置,包括整个 Pod 网络的 CIDR。然后,Flannel 为每个加入集群的节点分配一个子网,确保节点间的子网不会重叠。
-
数据封装:Flannel 使用数据包封装技术(如 VXLAN)在物理网络之上创建一个虚拟网络层。每当 Pod 间通信时,它的数据包会被封装在一个外部数据包中,然后通过物理网络路由到目的地节点,最后被解封装并传递给目标 Pod。
-
路由配置:Flannel 会配置节点的路由表,以便封装后的数据包可以通过物理网络正确路由到目标节点。每个节点知道如何将封装的数据包路由到集群中的任何其他节点。
核心组件
-
flanneld:Flannel 的主要代理,运行在 Kubernetes 集群的每个节点上。它负责分配子网、封装和解封装数据包以及更新路由表。
-
etcd:用作 Flannel 的中央数据存储,存储网络配置和每个节点的子网分配信息。
特点
-
简单性:Flannel 旨在提供简单而又可靠的覆盖网络,易于安装和配置。
-
灵活性:支持多种后端,包括 VXLAN(默认)、IPSec、Direct routing 等,允许用户根据需要选择最适合他们环境的数据封装技术。
-
可扩展性:虽然 Flannel 更适合小到中等规模的集群,但它也可以通过适当的调整和优化支持较大的部署。
使用场景
Flannel 适用于需要简单、易于设置的 Kubernetes 网络解决方案的场景。它特别适用于:
-
小到中等规模的 Kubernetes 部署。
-
开发和测试环境,其中网络性能和高级网络特性不是首要考虑因素。
-
对网络解决方案的定制要求不高的环境。
总的来说,Flannel 提供了一种简单且有效的方式来实现 Kubernetes Pod 网络,使得 Pod 能够跨越不同节点进行通信,而无需考虑底层的网络基础设施细节。
Calico
Calico 是一个广泛使用的、开源的网络和网络安全解决方案,专为容器、虚拟机和原生主机环境设计,非常适用于大规模 Kubernetes 集群。它提供了高性能的网络通信以及高级的网络策略管理,使得它成为构建和维护大型、复杂 Kubernetes 集群网络的流行选择。
工作原理
Calico 的核心原理包括:
-
纯三层网络:Calico 使用标准的 IP 路由而不是覆盖网络(如 VXLAN)来处理 Pod 间的通信,这意味着每个 Pod 都分配有一个唯一的 IP 地址。这种方法简化了路由过程,减少了封装和解封装的开销,提高了网络性能。
-
BGP(边界网关协议):Calico 使用 BGP 来分发路由信息。在一个 Calico 网络中,每个节点都可以作为一个 BGP 对等体,它会向集群中的其他节点宣告自己所负责的 Pod IP 范围。这使得集群内的每个节点都能了解如何将流量路由到任何特定的 Pod,无论这个 Pod 位于哪个节点上。
-
网络策略:Calico 提供了强大的网络策略管理功能,允许细粒度控制 Pod 间的通信。这些策略可以基于多种标准进行定义,包括命名空间、Pod 标签和选择器等。策略可以实施白名单或黑名单规则,以确保网络的安全性和合规性。
-
IPAM(IP 地址管理):Calico 提供了灵活的 IPAM 解决方案,支持静态和动态 IP 分配。Calico 可以与 Kubernetes 的 CNI 插件集成,自动为每个 Pod 分配 IP 地址,并确保这些地址在集群中的唯一性。
-
IPIP 封装(可选):尽管 Calico 通常使用无封装的纯三层路由,但它也支持 IPIP 封装,以允许在不支持直接路由的环境中跨越不同子网的 Pod 通信。
特点
-
高性能和可扩展性:Calico 提供了接近原生网络性能的通信能力,非常适合需要高吞吐量和低延迟的应用,且能够轻松扩展到数千个节点。
-
细粒度的网络安全策略:Calico 允许你定义详细的网络安全规则,控制 Pod 之间的流量流动,从而增强集群的安全性。
-
跨平台兼容性:Calico 不仅支持 Kubernetes,还可以用于其他容器编排工具(如 Docker Swarm)和传统的 VM 或裸机环境,使其成为多环境中的理想选择。
使用场景
-
大规模 Kubernetes 集群:Calico 特别适合于大型或高密度的 Kubernetes 集群,需要高性能网络通信和复杂的网络策略管理。
-
多云和混合云环境:Calico 的灵活性使其适用于多云和混合云环境,能够跨越不同云平台和数据中心实现网络通信和策略一致性。
-
需要高级网络策略管理的应用:对于需要细粒度网络控制和安全隔离的应用,Calico 提供了强大的工具集。
总的来说,Calico 以其高性能、可扩展性和强大的网络策略功能,在 Kubernetes 环境中提供了一种高效的网络解决方案。
K8s网络原理
一、K8s网络模型
K8s网络模型设计的一个基础原则是:
-
每个Pod都拥有一个独立的IP地址,而且假定所有Pod都在一个可以直接连通的、扁平的网络空间中。
-
同一个Pod内的不同容器将会共享一个网络命名空间,也就是说同一个Linux网络协议栈。意味着同一个Pod内的容器可以通过localhost来连接对方的端口。
二、Docker的网络模型
Docker使用到的与Linux网络有关的主要技术:
-
Network Namespace(网络命名空间)
-
Veth设备对
-
Iptables/Netfilter
-
网桥
-
路由
三、网络的命名空间
为了支持网络协议栈的多个实例,Linux中网络栈中引入了网络命名空间(Network Namespace),这些独立的协议栈被隔离到不同的命名空间中,处于不同命名空间的网络栈是完全隔离的,彼此之间无法通信。通过这种对网络资源的隔离,就能在一个宿主机上虚拟多个不同的网络环境。
Docker正是利用了网络的命名空间特性,实现了不同容器之间网络的隔离。
Linux的网络命名空间内可以有自己独立的路由表及独立的Iptables/Netfilter设置来提供包转发、NAT及IP包过滤等功能。
为了隔离出独立的协议栈,需要纳入命名空间的元素有进程、套接字、网络设备等。进程创建的套接字必须属于某个命名空间,套接字的操作也必须在命名空间内进行。同样,网络设备也必须属于某个命名空间。因为网络设备属于公共资源,所以可以通过修改属性实现这命名空间之间移动。当然是否允许移动和设备的特征有关。
1.网络命名空间的实现
Linux实现网络命名空间的核心:Linux的网络协议十分复杂,为了支持独立的协议栈,相关的全局变量都必须修改为协议栈私有。最好的办法就是让全局变量成为一个Net Namespace变量的成员,然后为协议栈私有。最好的办法就是让这些全局变量成为一个Net Namespace变量的成员,然后为协议栈的函数调用加入一个Namespace参数。
所有的网络设备(物理的或虚拟接口、桥等着内核里都叫作Net Device)都只能属于一个命名空间。通常物理的设备只能关联到root这个命名空间中,虚拟的网络设备(虚拟的以太网接口或者虚拟网口对)则可以被创建并关联到一个给定的命名空间中,而且可以在命名空间之间移动。
网络命名空间代表的是一个独立的协议栈,它们之间是相互隔离的,彼此无法通信,在协议栈内部都看不到对方。打破这种限制,让处于不同命名空间的网络相互通信,甚至和外部的网络进行通信的方法就是:Veth设备对。
Veth设备对重要作用就是:打破相互看不到的协议栈之间的壁垒,就像一个管子,一端连着这个网络命名空间的协议栈,一端连着另一个网络命名空间的协议栈。
所以如果想在两个命名空间之间进行通信,就必须有一个Veth设备对。
2.网络命名空间的操作
命令需要root用户运行
创建一个命名空间:
ip nets add <name>
在命名空间内执行命令:
ip netns exec <name> <command>
如果想执行多个命令,可以先进入内部sh,然后执行:
ip netns exec <name> bash
之后就是在新的命名空间内进行操作了,退出到外面的命名空间,输入exit
3.网络命名空间的一些技巧
可以在不同的网络命名空间之间转移设备,例如Veth设备对的转移。设备里面有一个重要属性:NETIF_F_ETNS_LOCAL,这个属性为"on",则不能转移到其他命名空间内。Veth设备属于可以转移的设备,很多其他设备如lo设备、vxlan设备、ppp设备、bridge设备等都是不可以转移的。
使用ethtool工具可以查看:
ethtool -k br0
netns-local: on [fixed]
netns-local的值是on,就说明不可以转移,否则可以
四、Veth设备对
引入Veth设备对是为了在不同的网络命名空间之间进行通信,利用它可以直接将两个网络命名空间连接起来。由于要连接两个网络命名空间,所以veth设备都是成对出现的,很像一对以太网卡,并且中间有一个直连的网线。既然是一对网卡,将其中一端称为另一端的peer,在Veth设备的一端发送数据时,会将数据直接发送到另一端,并触发另一端的接收操作。
1.Veth设备对的操作命令
创建Veth设备对,连接到不同的命名空间,并设置它们的地址,让它们通信。
创建Veth设备对:
ip link add veth0 type veth peer name veth1
创建后查看Veth设备对的信息,使用ip link show命令查看所有网络接口:
ip link show
会生成两个设备,一个是veth0,peer是veth1 两个设备都在同一个命名空间,将Veth看作是有两个头的网线,将另一个头甩给另一个命名空间
ip link set veth1 netns netns1
再次查看命名空间,只剩下一个veth0:
ip link show
在netns1命名空间可以看到veth1设备,符合预期。
现在看到的结果是两个不同的命名空间各自有一个Veth的网线头,各显示为一个Device。(Docker的实现里面,除了将Veth放入容器内)
下一步给两个设备veth0、veth1分配IP地址:
ip netns exec netns1 ip addr add 10.1.1.1/24 dev veth1
ip addr add 10.1.1.2/24 dev veth0
现在两个网络命名空间可以互相通信了:
ping 10.1.1.1
ip netns exec netns1 ping 10.1.1.2
至此两个网络命名空间之间就完全相通了。至此就能够理解Veth设备对的原理和用法了。在Docker内部,Veth设备对也是联系容器到外面的重要设备,离开它是不行的。
2.Veth设备对如何查看对端
一旦将Veth设备对的peer端放入另一个命名空间,在本命名空间就看不到了,想查看peer端连接的命名空间,可以使用ethtool工具来查看。
首先在一个命名空间中查询Veth设备对端接口在设备列表中的序列号:
ip nets exec netns1 ethtool -S veth1
NIC statistics:peer_ifindex: 5
得知另一端的接口设备的序列号是5,再到命名空间中查看序列号5代表的设备:
ip netns exec netns2 ip link | grep 5
veth0
现在就找到下标为5的设备了,是veth0,另一端自然就是另一个命名空间中的veth1了,互为peer。
五、网桥
Linux可以支持多个不同的网络,网络之间能够相互通信,网桥将这些网络连接起来并实现网络中主机的相互通信。
网桥是一个二层的虚拟网络设备,把若干个网络接口"连接起来",以使得网口之间的报文能够互相转发。网桥能够解析收发的报文,读取目标MAC地址的信息,和自己记录的MAC表结合,来决策报文的转发目标网口。为了实现这些功能,网桥会学习源MAC地址(二层网桥转发的依据就是MAC地址)。在转发报文时,网桥只需要向特定的网口进行转发,避免不必要的网络交互。网桥如果遇到一个未学习到的地址,就无法知道这个报文应该向哪个网口转发,就将报文广播给所有的网口(报文来源的网口除外)。
在实际网络中,网络拓扑不可能永久不变。网桥需要对学习到的MAC地址表加上超时时间(默认为5分钟),如果网桥收到了对应端口MAC地址回发的包,则重制超时时间,否则过了超时时间后,就认为设备已经不在那个端口上了,就会重新广播发送。
Linux内核支持网口的桥接,与单纯的交换机不同,交换机只是一个二层设备,对于接收到的报文,要么转发,要么丢弃。运行着linux内核的机器本身就是一台主机,有可能是网络报文的目的地,收到的报文除了转发和丢弃,还可能被送到网络层协议栈的上层(网络层),从而被主机本身的协议栈消化,既可以把网桥看作一个二层设备,也可以看作一个三层设备。
1.Linux网桥的实现
Linux内核是通过一个虚拟的网桥设备(Net Device)来实现桥接的。虚拟设备可以绑定若干个以太网接口设备,从而将它们桥接起来。
虚拟的网桥设备和普通设备的不同,最明显的一个特性是可以有一个IP地址:
网桥br0绑定了eth0和eth1。对于网络协议栈的上层来说,只看的到br0。桥接是在数据链路层实现的,上层不需要关心桥接的细节,协议栈上层需要发送的报文被送到br0,网桥设备的处理代码判断报文该被转发到eth0还是eth1,或者两者皆转发。协议栈的上层需要发送的报文被提交给网桥的处理代码,在这里会判断报文应该被转发,丢弃还是提交到协议栈上层。
有时eth0、eth1也可能会作为报文的源地址或目的地址,直接参与报文的发送与接收,从而绕过网桥。
2.网桥的常用操作命令
Docker自动完成了对网桥的创建和维护。
新增一个网桥设备:
brctl addbr xxxxx
为网桥增加网口,在Linux中,一个网口其实就是一个物理网卡,将物理网卡和网桥连接起来。
brctl addif xxxxx ethx
网桥的物理网卡作为一个网口,由于在链路层工作,就不再需要IP地址了,这样上面的IP地址自然失效。
ifconfig ethx 0.0.0.0
给网桥配置一个IP地址:
Ifconfig brxxx xxx.xxx.xxx.xxx
这样网桥就有一个IP地址,连接到上面的网卡就是一个纯链路层设备了。
六、Iptables/Netfilter
Linux提供了一套机制来为用户实现自定义的数据包处理过程。
在linux网络协议中有一组回调函数挂接点,通过这些挂接点挂接的钩子函数可以在linux网络栈处理数据包的过程中对数据进行一些操作,例如过滤、修改、丢弃等。整个挂接点技术叫作Netfilter和Iptables。
Netfilter负责在内核中执行各种挂接的规则,运行在内核模式中。而Iptables是在用户模式下运行的进程,负责协助维护内核中Netfilter的各种规则表。通过二者的配合来实现整个Linux网络协议栈中灵活的数据包处理机制。
四表:raw、mangle、nat、filter
Netfilter可以挂接的规则点有5个(链):
-
INPUT
-
OUTPUT
-
FORWARD
-
PREROUTING
-
POSTROUTING
-
流入:PREROUTING -> INPUT
-
流出:OUTPUT -> POSTROUTING
-
转发:PREROUTING -> FORWARD -> POSTROUTING
七、路由
Linux系统包含一个完整的路由功能。当IP在处理数据发送或者抓发时,会使用路由表来决定发往哪里。通常情况下,如果主机与目的主机直接相连,那么主机可以直接发送IP报文到目的主机,这个过程比较简单。例如,通过点对点的链接或通过网络共享,如果主机与目的主机没有直接相连,那么主机会将IP报文发送给默认的路由器,然后由路由器来决定往哪发送IP报文。
路由功能由IP层维护的一张路由表来实现。当主机收到数据报文时,用此表来决策接下来应该做什么操作。当从网络侧接收到数据报文时,IP层首先会检查报文的IP地址是否与主机自身的地址相同。如果数据报文中的IP地址是主机自身的地址,那么报文将被发送到传输层相应的协议中去。如果报文中的IP地址不是主机自身的地址,并且配置了路由功能,那么报文将被转发,否则,报文将被丢弃。
路由表中的数据一般是以条目形式存在的。一个典型的路由表条目通常包含以下主要的条目项。
-
目的IP地址:此字段表示目标的IP地址。这个IP地址可以是某台主机的地址,也可以是一个网络地址。如果这个条目包含的是一个主机地址,那么主机ID将被标记为非零。如果这个条目包含的是一个网络地址,主机ID将被标记为零。
-
下一个路由器的IP地址:下一个路由器并不总是最终的目的路由器,很可能是一个中间路由器。条目给出下一个路由器的地址用来转发从相应接口收到的IP数据报文。
-
标志:这个字段提供了另一组重要信息,例如目的IP地址是一个主机地址还是一个网络地址。从标志中可以得知下一路由器是一个真实的路由器还是一个直接相连的接口。
-
网络接口规范:数据报文的网络接口规范,该规范将与该报文一起被转发。
1.路由表的创建
Linux的路由表至少包括两个表:一个是LOCAL,另一个是MAIN。在LOCAL表中会包含所有的本地设备地址。LOCAL路由表是在配置网络设备地址时自动创建的。LOCAL表用于供Linux协议栈识别本地地址,以及进行本地各个不同网络接口之间的数据转发。
可以通过下面的命令查看LOCAL表的内容:
ip route show table local type local
MAIN表用于各类网络IP地址的转发。MAIN表的建立可以使用静态配置生存,也可以使用动态路由发现协议生成。
2.路由表的查看
使用ip route list命令查看当前的路由表。
ip route list
另一个查看路由表的工具:
netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 10.128.232.1 0.0.0.0 UG 0 0 0 ens5
10.128.232.0 0.0.0.0 255.255.252.0 U 0 0 0 ens5
标志是U,说明是可达路由,标志是G,说明这个网络接口连接的是网关,否则说明是直连主机。
八、Docker的网络实现
Docker支持以下4类网络模式:
-
host模式:使用–net=host指定
-
containe模式:使用–net=container:NAME_or_ID指定
-
none模式:使用–net=none指定
-
bridge模式:使用–net=bridge指定,为默认设置
在K8s管理模式下,通常只会使用bridge模式,在bridge模式下,Docker Daemon第一次启动时会创建一个虚拟的网桥,默认的名字是docker0,在私有网络空间中给这个网桥分配一个子网。
针对由Docker创建出来的每一个容器,都会创建一个虚拟的以太网设备(Veth设备对),其中一端关联到网桥上,另一端使用Linux的网络命名空间技术,映射到容器内的eth0设备,然后从网桥的地址段内给eth0接口分配一个IP地址。
-
ip1是网桥的IP地址,Docker Daemon会在几个备选地址段里选一个,通常是172开头的一个地址。ip2是Docker在启动容器时,在这个地址段随机选择的一个没有使用的IP地址。相应的MAC地址也根据这个IP地址,在02:42:ac:11:00:00和02:42:ac:11:ff:ff的范围内生存,可以确保不会有ARP的冲突。
-
启动后,Docker还将Veth对的名字映射到eth0网络接口。ip3就是主机的网卡地址。
-
ip1、ip2和ip3是不同的IP段,外部是看不到ip1和ip2的。同一台机器内的容器之间可以相互通信。不同主机上的容器不能够相互通信。
-
为了跨节点互相通信,必须在主机的地址上分配端口,然后通过这个端口路由代理到容器上,这种做法显然意味着一定要在容器之间小心谨慎地协调好端口的分配,或者使用动态端口分配技术。
1.查看Docker启动后的系统情况
Docker网络中bridge模式下Docker Daemon启动时创建docker0网桥,并在网桥使用的网段为容器分配IP。
ip addr
iptables-save
Docker创建了docker0网桥,并添加了Iptables规则。
2.查看容器启动后的情况(容器无端口映射)
docker run --name register -d registry
ip addr
iptables-save
ip route
可以看到如下情况。
-
宿主机器上的Netfilter和路由表都没有变化,说明在不进行端口映射时,Docker的默认网络上没有特殊处理的。相关的NAT和FILTER两个Netfilter链还是空的。
-
宿主机上的Veth对已经建立,并连接到了容器内。
进入容器,查看网络栈,容器内部的IP地址和路由如下:
ip route
ip addr
可以看到,默认停止的回环设备lo已经被启动,外面宿主机连接进来的Veth设备也被命名成了eth0,并且已经配置了地址172.17.0.10。
3.查看容器启动后的情况(容器端口映射)
用带端口映射的命令启动registry
docker run --name register -d -p 1180:5000 registry
启动后查看Iptables的变化
iptables-save
-
Docker服务在NAT和FILTER两个表内添加的两个Docker子链都是给端口映射用的。例如需要把宿主机的1180端口映射到容器的5000端口。
-
无论是宿主机接收还是宿主机本地协议栈发出的,目标地址是本地IP地址的包都会经过NAT表中的DOCKER子链。Docker为每一个端口映射都在这个链上增加了到实际容器目标地址和目标端口的转换。
-
经过这个DNAT的规则修改后的IP包,会重新经过路由模块的判断进行转发。由于目标地址和端口已经是容器的地址和端口,所以数据自然就送到docker0上,从而送到对应的容器内部。
-
在Forward时,需要在Docker子链中添加一条规则,如果目标端口和地址是指定容器的数据,则允许通过。
-
在Docker按照端口映射的方式启动容器时,主要的不同就是上述Iptables部分。从而容器内部的路由和网络设备,都和不做端口映射时一样,没有任何变化。
九、K8s的网络实现
K8s网络的设计主要致力于解决以下场景:
-
容器到容器之间的直接通信
-
抽象的pod到pod之间的通信
-
Pod到Service之间的通信
-
集群外部与内部组件之间的通信
1.容器到容器的通信
在同一个Pod内的容器共享同一个网络命名空间,共享同一个linux协议栈。对于网络的各类操作,就和它们在同一台机器上一样,设置可以用localhost地址访问彼此的端口。
容器1和容器2共享网络命名空间,打开的端口不会有冲突,可以直接使用linux的本地IPC进行通信(例如消息队列或者管道),互相访问只需要使用localhost就可以了。
2.Pod之间的通信(背会)
每一个Pod都有一个真实的全局IP地址,同一个Node内的不同Pod之间可以直接采用对方Pod的IP地址通信,而且不需要使用其他发现机制,例如DNS或者etcd。
Pod容器既有可能在同一个Node上运行,也有可能在不同的Node上运行,通信分为两类:
-
同一个Node内的Pod之间的通信和不同Node上的Pod之间的通信
1) 同一个Node内的Pod之间的通信
-
Pod1和Pod2都是通过Veth连接在同一个docker0网桥上的,IP地址IP1、IP2都是从docker0到网段上动态获取的,和网桥本身的IP3是同一个网段的。
-
在Pod1、Pod2的Linux协议上,默认路由都是docker0的地址,也就是说所有非本地地址的网络数据,都会被默认发送到dockero网桥上,由docker0网桥直接中转。
-
综上所述,Pod1和Pod2关联在同一个docker0网桥上,地址段相同,所以Pod1和Pod2之间是能直接通信的。
2)不同Node上的Pod之间的通信
Pod的地址是与docker0在同一个网段内的,docker0网段与宿主机网卡是两个完全不同的IP网段,并且不同Node之间通信只能通过宿主机的物理网卡进行,因此要实现位于不同Node上的Pod容器之间通信,就必须想办法通过主机的这个IP地址来进行寻址和通信。
K8s会记录所有正在运行Pod的IP分配信息,并将这些信息保存在etcd中(作为Service的Endpoint)。要想支持不同Node上的Pod之间的通信,就要达到两个条件:
-
在整个K8s集群中对Pod的IP分配进行规划,不能有冲突
-
将Pod的IP和所在Node的IP关联起来,通过这个关联让Pod可以互相访问
根据条件1,需要在部署k8s时,对docker0的IP地址进行规划,保证每一个Node上的docker0地址没有冲突。可以在规划后手工配置到每个Node上,或者做一个分配规则,由安装的程序自己去分配占用。例如K8s的网络增强开源软件Flannel就能够管理资源池的分配。
根据条件2:Pod中的数据中发出时,需要有一个机制能够知道对方Pod的IP地址,挂在哪个具体的Node上。也就是先要找到Node对应宿主机的IP地址,将数据发送到宿主机的网卡上,然后在宿主机上将相应的数据转到具体的docker0上,一旦数据到达宿主机Node,则那个Node内部的docker0便知道如何将数据发送到Pod。
十、Pod和Service网络实战
K8s网络模型要求每个Node上的容器都可以相互访问。一个Pod内的所有容器都需要共用同一个IP地址,意味着一定要使用网络的容器映射模式。Pod会启动一个基础容器,然后将Pod内所有容器都连接到上面,只需要为基础的这个Google_containers/pause容器执行端口映射规则。
1.部署一个RC/Pod
apiVersion: v1
kind: ReplicationController
metadata:name: frontendlabels:name: frontend
spec:replicas: 1selector:name: frontendtemplate:metadata:labels:name: frontendspec:containers:- name: php-redisimage: kubeguide/guestbook-php-frontendenv: - name: GET_HOSTS_FROMvalue: envports:- containerPort: 80hostPort: 80
假设在一个空的K8s集群上运行,检查下此时Node上网络接口有哪些?
ifconfig
可以看出有一个docker0网桥和一个本地地址的网络端口。
部署RC/Pod配置文件
kubectl create -f frontend-controller.yaml
kubectl get pods
docker ps
运行了两个容器,其中一个是google_containers/pause:latest的镜像。
实际Pod的IP数据流的网络目标都是这个google_containers/pause容器,而google_containers/pause容器实际上只负责接管这个Pod的Endpoint。
通过docker port命令来检验下
docker port
2.发布一个服务
Service允许在多个Pod之间抽象一些服务,服务可以通过提供在同一个Service的多个Pod之间的负载均衡机制来支持水平扩展。K8s集群会为服务分配一个虚拟IP地址,这个IP地址是在K8s的Portal Network中分配的,Portal Network的地址范围则是在Kubmaster上启动API服务进程时,使用–service-cluster-ip-range=xx命令行参数指定的。这个IP段可以是任何段,只要不和docker0或者物理网络的子网冲突就可以。目标为Service IP地址和端口的任何流量都将被重新定向到本地端口。 frontend-service.yaml
apiVersion: v1
kind: Service
metadata:name: frontendlabels:name: frontend
spec:ports:- port: 80selector:name: frontend
然后在K8s集群中定义这个服务
kubectl create -f frontend-service.yaml
kubectl get services
K8s集群已经为这个服务分配了一个虚拟IP地址,这个IP地址是在K8s的Portal Network中分配的。Portal Network的地址范围则是在K8s上启动API服务进程时,使用–service-cluster-ip-range=xx命令行参数指定的。
kube-proxy服务给每一个新创建的服务关联了一个随机的端口号,并且监听特定端口,为服务创建相关的负载均衡对象。K8s的kube-proxy作为一个全功能的代理服务器管理了两个独立的TCP连接:一个是从容器到kube-proxy,另一个是kube-proxy到负载均衡的目标pod。
登陆服务器检查Iptables/Netfilter的规则
iptables-save
所有流量都被导入kube-proxy中,需要kube-proxy完成一些负载均衡工作,创建Replication Controller并观察结果。
apiVersion: v1
kind: ReplicationController
metadata:name: frontendlabels:name: frontend
spec:replicas: 3selector: name: frontendtemplate:metadata:labels:name: frontendspec:containers:- name: php-redisimage: kubeguide/example-guestbook-php-redisenv:- name: GET_HOSTS_FROMvalue: envports:- containerPort: 80hostPort: 80
在集群上发布配置文件,等待并观察,确保所有Pod都运行起来了
kubectl create -f frontend-controller.yamlkubectl get pods -o wide
所有Pod都运行起来后,Service会将匹配到的标签为name=frontend的所有Pod都进行负载分发。因为Service的选择匹配所有这些Pod,负载均衡将会对这3个Pod进行分发。
K8s四层网络
抽象成四层网络
Node节点网络
底层基础设施支持节点主机之间网络的互通
Pod网络
能够相互做IP寻址、相互通讯
同一节点上的Pod网络
大多数场景下一个pod下面有1个容器 也有一些场景下一个pod下有多个容器
这些容器共享pod中的网络栈
eth0
是主机上的网卡 也是流量出入的设备 也支持k8s集群节点之间做网络寻址和互通的设备
docker0
是一个虚拟网桥 简单理解为是一个虚拟交换机
支持节点上面或节点之间进行IP寻址和互通的设备
veth0
是pod1上的虚拟网卡 支持pod内容器互通的设备
内部的容器可以通过localhost相互访问
pause
pause是一个特殊的容器 为pod建立veth0网络共享的接口
通过docker ps就可以看到这个容器
pod
pod的ip是docker0网桥分配的
不同节点上的Pod网络
pod1和pod2在同一个网桥dokcer0里 所以可以互联互通
节点内的pod网络在172.17.0.0/24这个地址空间内的
节点主机网络在10.100.0.0/24这个地址空间内的
也就是说 pod网络和节点网络不在同一个网络空间内
不同节点上的pod之间如何互通?
路由方案
-
对于目标段172.17.1.0/24这个网段的pod转发到10.100.0.3这个主机
-
对于目标段172.17.0.0/24这个网段的pod转发到10.100.0.2这个主机
-
当主机eth0接受到pod网络的包就会向内部的pod网桥转发
-
这样不同节点的pod网络就可以IP寻址和通讯
-
这种方案引入了底层的网络设备 但是额外的网络开销比较少
覆盖网络方案(Overlay)
-
如果是公有云网络 就不支持路由方案,所以就可以用覆盖网络方案
-
所谓的覆盖网络方案就是在现有的网络基础上建立一个虚拟网络
-
所支持的技术很多 比如Flannel/Weavenet
-
大多采用隧道封包的技术
-
pod网络的数据包在出节点之前先被封装成节点网络的数据包
-
经过底层网络到达目标节点
-
就会被解封出来再转发给内部的pod网络
这种方案对底层网络没有太多依赖
但封包解包对性能有影响
考虑到pod网络实现技术众多
k8s为了简化集成
引入了CNI技术
-
CNI是一个Pod网络集成标准 简化K8s和不同Pod网络实现技术的集成
-
kubelet通过CNI插件去操作pod网络 而不需要关注底层的具体实现
Service
一个service背后往往是由多个pod组成的集群
由此引入了服务发现以及负载均衡等问题
服务发现与负载均衡
如何发现并且定位pod ip?
account-app pod集合中pod ip可能会变化 重新发布或自己挂了 k8s重新调度部署 也会变化
要解决服务发现问题 需要了解下技术演进的过程
DNS
最早是通过DNS来解决这个问题
K8s也支持DNS组件
运行的时候 K8s会把Pod集群中的Pod信息(ip和端口)注册到DNS上去
client应用通过查询DNS就可以发现目标Pod 就可以发起调用
对client无侵入
劣势:
1、有些客户端每次都会查询DNS服务 造成不必要的开销
2、有些客户端会缓存DNS信息 默认超时时间比较长 当目标Pod ip发生了变化,就会存在缓存刷新不及时问题 就会导致访问pod失效
3、大多数负载均衡策略比较简单,有些还不支持负载均衡
所以K8s并没有采用这种方式做服务发现,但实际上K8s还是引入了DNS组件来实现通过域名访问服务的
Service Registry+Client方案
这种方案典型的代表: Eureka+Ribbon/Consul/Nacos
K8s 自带的分布式存储etcd 就可以用来实现Service Registry
1、k8s将pod集群中的pod信息自动注册到service registry
2、client应用可以通过servcie registry查询发现目标pod 然后可以发起调用
这种方案实现不复杂 client也可以实现灵活的负载均衡策略
但是对客户端应用有侵入性,所以k8s也没有直接采用这种方案
k8s service网络具体实现原理
Kube-Proxy、Kubelet、K8s Master 是K8s实现服务发现与注册到关键
"服务注册阶段"
1、服务Pod实例发布的时候 Kubelet会负责启动这些Pod实例
2、启动完之后 会把Pod实例列表注册到K8s Master上面去
3、在Servcie发布的时候 K8s会为Service分配ClusterIP,相关信息也会记录在Master节点上面,并且ClusterIP和PodIP有映射关系
"服务发现阶段"
Kube-Proxy会监听Master并且发现Service的ClusterIp和Pod实例列表,并且修改本地的Linux Iptables转发机制,在接受到目标IP为某个ClusterIp的时候进行负载均衡并且转发到对应Pod上面去
"服务实际调用阶段"
当有消费者Pod需要访问某个服务的时候 先调用dns获取服务名,就通过clusterip发起调用,会被本地的iptables机制所截获,然后通过负载均衡转发到pod实例上面。
dns组建也监听master节点并发现服务名称与clusterip之间的映射关系
k8s服务发现与eureka+ribbon方案对比
都是客户端代理的机制
负载均衡都是在客户端实现的
ribbon是以libary库的形式嵌入在客户端应用中的 对客户端应用是有侵入性的
而k8s的kube-proxy是独立的组件
每个docker节点上都有一个kube-proxy 对客户应用是无侵入的
该做法类似于ServiceMesh中的SideCar
Ribbon转发是穿透的
K8s中的转发是通过iptables转发
kube-proxy只负责服务发现 修改iptables规则 请求是不穿透kube-proxy的
早期的k8s是穿kube-proxy的 考虑单点问题和性能损耗 新的版本不穿kube-proxy了
ribbon实现服务名到服务实例ip地址的映射 它只有一层映射
在k8s中是有两层映射的
kube-proxy是clusterip到podip的映射
kube-dns实现服务名到clusterip的映射
通过clusterip统一屏蔽服务发现和负载均衡问题
前言
Kubernetes(K8s)是一个强大的容器编排平台,它的网络功能是其成功的关键之一。本文将带你了解K8s网络的实现原理、默认网络模式,以及常用的网络组件如Calico和Flannel,并展示如何使用Ingress,帮助你在不同场景下做出合适的选择。
K8s网络的实现原理
在Kubernetes中,网络的核心理念是“每个Pod都有自己的IP地址”,并且Pods可以彼此直接通信。为了实现这一点,K8s使用一种称为“容器网络接口”(CNI)的架构。CNI是一组标准,使得网络插件可以与K8s无缝集成。
默认网络模式
Kubernetes有几个默认的网络模式,其中最常用的是“flannel”模式。在这个模式下,K8s集群中的每个Pod都可以使用一个独立的IP地址,允许它们在不同节点之间进行通信,而无需进行NAT(网络地址转换)。
常见网络组件
1. Flannel
Flannel 是K8s的一个简单而常用的网络插件,主要用于提供覆盖网络。它通过在每个主机上创建一个虚拟网络,将各个Pod连接起来。
实现原理: Flannel使用VXLAN(虚拟扩展局域网)或其他后端(如host-gw)来创建一个虚拟网络。每个Pod都有一个独特的IP地址,Flannel负责在不同主机间转发流量。
YAML示例:
apiVersion: v1
kind: Pod
metadata:name: flannelnamespace: kube-system
spec:containers:- name: flannelimage: quay.io/coreos/flannel:v0.14.0args: [ "--ip-masq", "--kube-subnet-mgr=kube-subnet" ]
使用场景: Flannel适合用于小型集群和开发环境。
例子: 假设一个小型开发团队正在开发一个Web应用,使用Flannel可以快速配置一个K8s集群,使得开发人员能够专注于编码,而无需担心网络设置。
2. Calico
Calico 是一种更复杂的网络插件,它不仅提供覆盖网络功能,还实现了网络策略和安全功能。
实现原理: Calico使用BGP(边界网关协议)来动态路由流量。它允许每个Pod有一个唯一的IP地址,并可以根据用户定义的网络策略控制流量。
YAML示例:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:name: allow-access
spec:podSelector:matchLabels:role: frontendingress:- from:- podSelector:matchLabels:role: backend
使用场景: 适合需要严格网络安全控制的生产环境。
例子: 假设一家医院的应用程序,需要确保患者数据的安全。运维团队可以使用Calico设置网络策略,限制只有特定的服务能够访问敏感数据,确保数据的隐私和安全。配置示例:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:name: frontend-policynamespace: my-namespace
spec:podSelector:matchLabels:app: frontendpolicyTypes:- Ingressingress:- from:- podSelector:matchLabels:app: backend
在这个例子中,只有app=backend标签的Pod可以访问app=frontend标签的Pod。
3. Weave Net
Weave Net 是另一个流行的K8s网络插件,提供简单的安装和良好的可视化功能。
实现原理: Weave Net使用Overlay Network技术,通过在每个节点上创建一个虚拟网络,并通过加密和可视化工具来增强网络安全性和监控。
YAML示例:
apiVersion: v1
kind: Pod
metadata:name: weavenamespace: kube-system
spec:containers:- name: weaveimage: weaveworks/weave-kube:latest
使用场景: 适合需要高可用性和可扩展性的场景。
例子: 一家在线商店,随着用户的增加,流量不断增长。使用Weave Net,开发者可以轻松地扩展服务并保持网络连接稳定。可以通过Weave提供的命令行工具监控网络状态:
weave status
Ingress的使用
Ingress 是K8s中用于管理外部访问集群服务的一种API对象。它提供了HTTP和HTTPS路由到服务的能力。
Ingress的实现原理
Ingress通过一个或多个Ingress Controller(控制器)来工作,这些控制器负责接收外部请求并将其路由到相应的服务。Ingress Controller通常运行在集群内部,并使用负载均衡、SSL终止等技术来处理流量。
配套使用:Ingress通常与LoadBalancer服务类型和DNS服务一起使用。LoadBalancer服务为Ingress Controller提供一个外部IP地址,而DNS可以将域名解析到这个IP地址。
Ingress的YAML示例
以下是一个简单的Ingress配置示例:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: my-ingress
spec:rules:- host: myapp.example.comhttp:paths:- path: /pathType: Prefixbackend:service:name: my-serviceport:number: 80
使用场景
Ingress适合需要通过域名访问多个服务的情况。
例子:假设你有多个微服务,如用户服务和订单服务,你可以使用Ingress将所有流量路由到这些服务,而不需要在每个服务上暴露一个外部IP。用户可以通过myapp.example.com来访问这些服务,而Ingress Controller会根据配置将请求转发到相应的服务。
查看所用网络组件
要查看K8s集群中使用的网络组件,可以使用以下命令:
kubectl get pods -n kube-system
这将列出在kube-system命名空间中运行的所有Pod,你可以查找与网络相关的Pod(如Flannel、Calico等)以确认所用的网络插件。
不单独配置网络组件的影响
如果不单独配置网络组件,K8s集群仍然可以正常运行,但可能会面临一些影响和限制:
默认网络模式: 使用K8s默认的网络配置可能不支持高级功能,如网络策略、流量监控等。这可能会导致安全性降低,尤其是在生产环境中。
性能问题: 默认网络模式可能无法满足高负载或高可用性需求,影响应用的性能。
可扩展性: 在缺少合适网络插件的情况下,集群的可扩展性和灵活性可能受到限制。
配置网络组件的好处
安全性: 网络插件(如Calico)允许实现细粒度的网络策略,从而提高集群的安全性。
性能优化: 专业的网络插件提供了更高的性能和更低的延迟,适合处理高负载的应用。
可视化和监控: 一些网络组件提供流量监控和可视化工具,便于运维团队了解流量状况和网络健康。
功能丰富: 使用专用网络插件可以访问更丰富的功能,如负载均衡、故障恢复和流量控制。
类似组件
除了Ingress之外,还有一些其他组件也可以用于处理K8s中的外部访问:
1. Service
K8s的Service是一个基本的资源,用于定义如何访问一组Pod。Service可以创建不同类型的访问方式,包括ClusterIP(集群内部访问)、NodePort(节点IP访问)和LoadBalancer(负载均衡器)。
使用场景: Service通常用于需要直接访问某个Pod或服务的情况。
2. API Gateway
API Gateway是一个更高级的解决方案,通常用于微服务架构中。它可以提供更多的功能,如请求路由、负载均衡、安全认证和API版本管理。
使用场景: 适合大型微服务架构,尤其是需要多种功能集成的场景。
选择合适的网络插件和Ingress配置
在选择K8s网络插件和Ingress配置时,需要考虑几个因素:
集群规模: 小型集群可以选择Flannel,而大型或复杂的集群可能需要Calico或Weave Net。
安全需求: 如果你的应用对网络安全有较高要求,Calico是一个不错的选择。
外部访问方式: 根据应用的外部访问需求选择Ingress或API Gateway。
网络性能: 不同的网络插件在性能上有所不同,建议在正式环境前进行测试。
总结
理解 Kubernetes 的网络架构及其插件是成功使用K8s的关键。
Flannel 适合小型集群和开发环境;
Calico 适合需要严格网络安全控制的生产环境;
Weave Net 适合需要高可用性和可扩展性的场景。
Ingress 适合需要通过域名访问多个服务的情况。