k8s集群网络层碎碎念
比起君子讷于言而敏于行我更喜欢君子善于言且敏于行。目录前言一、访问流程抓重点1. “对象”的本质2. Service 对象的存在形式是什么3. 如何查看 Service1命令行2Rancher UI 查看二、详细拆解——hostNetwork1. hostNetwork 是什么2. 典型应用场景3. hostNetwork 落地操作方法 1在 Pod YAML 里指定方法 2Deployment/DaemonSet4. hostNetwork的注意事项二、service1. ClusterIP应用场景落地方式2. NodePort原理应用场景落地方式3. LoadBalancer原理应用场景落地方式4. ExternalName原理应用场景方案落地5. Headless / ClusterIP: None原理应用场景方案落地总结前言众所周知最近在新增上海节点的master到k8s集群中并把之前的北京master丝滑移除。在此期间又会涉及到一些网络问题包括但不限于域名解析、pod访问、ingress pod移除等等。总而言之一锅粥趁热打铁彻底捋清楚k8s集群所有的网络访问内容清晰明了的应对工作。Kubernetes 的网络Pod、Service、NodePort、hostNetwork属于网络层/传输层一、访问流程抓重点化繁为简我们最简单明了的去看访问的流程可以发现实际上k8s的网络访问核心内容就是service。觉得很杂乱只是因为service有不同的类型用在不同的场景下。抓住了本质就很好往下梳理了。1. “对象”的本质在 Kubernetes 里一切都是对象 (Object)。可以把它想成系统里的条目或记录有点像 Excel 里的行也像数据库里的记录。对象不是程序也不会自己跑它只是告诉 Kubernetes我要什么、怎么部署、需要哪些资源Kubernetes 会根据对象去调度 Pod、分配 IP、配置网络、管理副本2. Service 对象的存在形式是什么在 Kubernetes 里Service 是一种API 对象像 Pod、Deployment、ConfigMap 都是对象它在etcd里存储而不会自己跑成 Pod。当你创建 Service 时它会生成一个对象对象 系统记录 期望状态包含名字metadata.name比如my-service类型spec.typeClusterIP / NodePort / LoadBalancer / …选择器spec.selector选择哪些 Pod端口映射spec.portsService port → Pod targetPortClusterIPKubernetes 自动分配 VIP它本身不占用 Pod也不直接运行程序实际转发流量靠kube-proxyiptables 或 IPVS完成你创建对象 你告诉 Kubernetes我要这样一个东西系统去实现它YAML 是对象的“身份证”里面有所有属性把 Kubernetes 想成一个公司Pod 员工在办公室工作运行程序Deployment HR 发的招聘单我要 3 个前端开发员创建 PodService 前台接待员谁来找你应该把客户引到哪个员工PodClusterIP 内部 VIP大家公司内部都能找前台接待NodePort / LoadBalancer 外部访问通道客户从外部通过前台找到员工hostNetwork罗子君的妈妈 —— 公司大门Pod 直接绑定节点端口 ——林凌Pod注意Service 这个“前台”自己不做工作只是负责指引流量。3. 如何查看 Service1命令行可以用kubectl查看 Service# 查看某个命名空间下所有 Service kubectl get svc -n namespace # 查看详细信息 kubectl describe svc my-service -n namespace # 查看 YAML 原始定义 kubectl get svc my-service -n namespace -o yaml2Rancher UI 查看在 Rancher 页面里1.进入集群→ 选择命名空间比如cattle-system——菜单选择Workloads / Services——找到对应 Service比如rancher可以看到TypeClusterIP / NodePort / LoadBalancerPortsSelectors对应 Pod 标签EndpointsPod 列表 IPRancher 的 Service 界面就相当于kubectl get svc -n namespace的可视化版而且还能直接看到 Service 关联的 Pod。二、详细拆解——hostNetwork我们先来把和service并列的hostNetwork搞清楚1. hostNetwork 是什么pod直接使用宿主机的网络栈也就是pod网络接口直接挂在节点上pod的容器IP节点IPPod 内的端口就是节点上的端口会绕过CNIpod和pod直接访问会通过CNI插件calico、flannel等和 Service/kube-proxy网络访问更直接。用途类似端口直通适合对延迟、端口固定、外部访问敏感的场景。2. 典型应用场景核心思想任何必须直接占用节点端口或低延迟直通的服务都可以考虑 hostNetwork场景说明为什么用 hostNetworkIngress Controllernginx-ingress, traefik对外服务入口需要固定端口 80/443避免 NodePort 或 LB 映射复杂Pod 直接监听节点端口高性能网络服务低延迟需求如某些金融/视频流服务绕过 kube-proxy 和 iptables减少延迟系统级服务DNS、CNI 插件、网络监控代理必须直接访问宿主机网络不能被 Service 隔离调试/运维工具Prometheus Node Exporter、日志收集 Agent直接访问宿主机端口和接口方便省事3. hostNetwork 落地操作方法 1hostNetworkhostNetwork本身只是改变 Pod 的网络模式让 Pod 直接使用节点 IP 和端口。apiVersion: v1 kind: Pod metadata: name: hostnetwork-demo spec: hostNetwork: true # 开启 hostNetwork containers: - name: nginx image: nginx ports: - containerPort: 80 # 容器端口直接占用节点端口方法 2Deployment/DaemonSetDaemonSet本身只是保证 Pod 在每个节点上都有一个副本。很多 ingress-controller 和监控 Agent 通常更推荐用 DaemonSet 部署到每个节点apiVersion: apps/v1 kind: DaemonSet metadata: name: ingress-nginx namespace: ingress-nginx spec: template: spec: hostNetwork: true containers: - name: controller image: ingress-nginx/controller ports: - containerPort: 80 - containerPort: 443我们来拆解一下方法2的原理DeploymentDaemonSet概括集群级副本控制节点级副本控制用途管理一组相同的 Pod保证副本数replicas在集群每个节点上都运行一份 PodPod 分布Pod 可以分布在任意节点由 scheduler 决定每个节点都有一个 Pod新节点加入时自动调度 Pod大白话你想建 3 个咖啡馆不管在哪个街区只要总数是 3 就行每个街区都必须建一个咖啡馆保证每个街区都有覆盖典型场景Web 应用、后端服务、不要求每个节点都有副本ingress-controller、日志收集 agentfluentd、监控 agentnode-exporter、网络插件calico、cilium、flannelhostNetwork 单 Pod 模式下pod直接绑定在节点端口上会出现一个问题那就是如果 Pod 只部署在一个节点上其他节点访问同端口就访问不到 Pod。类似单机服务端口固定但不高可用。所以我理解为DaemonSet hostNetwork相当于是hostnetwork的高可用模式每个节点都部署一份 Pod每个 Pod 都用 hostNetwork 直接绑定节点端口。从而实现访问任何节点的端口都能命中 Pod方法3hostNetwork DaemonSethostNetwork DaemonSet 高可用 hostNetwork应用场景rancher.test.com访问。实际上访问任意节点ip:32443都能访问到rancher。我需要让用户去访问域名实际上是访问url:443来访问rancher页面。基础方式我可以把rancher pod的端口号使用hostNetwork映射到节点端口把url只挂给这一个节点可以实现。缺点一旦这个节点挂了访问就会失败。事实上任意节点都可以访问的。单节点会不稳定中阶方式可以使用nodeport的方式NodePort 把节点上的高位32443端口映射到Pod的443端口。此时用户访问url:32443就会访问到pod:443访问通过。可是我们要实现的是用户访问url:443。也就意味着需要在每一个节点上做一次网络转发让443端口访问来的都去找32443端口。这么做也很麻烦依旧需要手动执行每一台设备。当然如果是云厂商服务器也可以直接使用LB让外部通过标准端口访问LB 转发到 NodePort再到 Pod。相对来说比手动执行iptable或者firewalld或者nginx要省事儿。可惜我这个是手搓的k8s集群不能直接用LB。备注本文记录Kubernetes 的网络Pod、Service、NodePort、hostNetwork属于网络层/传输层。Ingress 本质上不是 Pod 网络本身它是一层 HTTP/HTTPS 层面的路由器。所以这里不考虑使用Ingress解决“HTTP 请求路由到哪个 Pod/Service”的问题。后续会单独再出一篇关于ingress的内容。进阶方式组合起来hostNetwork → Pod 直接用节点 IP 和端口DaemonSet → 每个节点都有这个 Pod结果无论访问哪台节点的 443都会命中一个 Pod不会因为单节点挂掉而访问失败类比就像每栋都配了前台客户到哪栋楼都有人接待核心意思就是hostNetwork 保证 Pod 端口直接暴露DaemonSet 保证每个节点都有 Pod →从而实现高可用。——————————————————————————————————————————4. hostNetwork的注意事项1.端口冲突hostNetwork Pod 端口是节点端口要保证不2.安全性Pod 有节点网络权限安全隔离比普通 Pod 弱。3.不适合大规模 PodhostNetwork 不能动态负载均衡到多个节点需要配合 DNS 或外部 LB。——————————————————————————————————————————二、service我们按照原理 → 应用场景 → 方案落地的方式一个个的来梳理service的不同类型。Kubernetes官方 Service 类型本质上只有这五种ClusterIP默认NodePortLoadBalancerExternalNameHeadless / ClusterIP: None所以严格来说这就是“官方 Service 类型”的全集。但是在实际运维或文档里大家经常会把hostNetwork DaemonSet Ingress/Service 联合方式也算作“Service 类型”所以会出现感觉“好像不止五种”的情况。简单理解如果只看 Kubernetes API → 就是上面五种。如果看实际生产使用 → 会有组合方式比如 hostNetwork pod NodePort service Ingress来实现不同的访问策略。换句话说官方文档中只有这五种你平时看到的各种外网访问场景其实都是这五种基础类型在组合使用。1. ClusterIP内部访问Pod A --- ClusterIP Service VIP --- kube-proxy --- Pod B targetPort我把ClusterIP理解为实现了Pod 高可用。ClusterIP 是集群内部的虚拟 IP (VIP)如果某个 Pod 挂掉了VIP 会自动不再把流量发给它 → Pod 层面的高可用原理给一组Pod 分配一个虚拟 IPVIP集群内部流量访问该 VIP 时kube-proxy会把流量负载到所有匹配的 Pod上Pod 的 IP 是动态的VIP 屏蔽了 Pod IP 的变化应用场景集群内部服务发现、后端微服务之间调用、不需要直接暴露给外部落地方式Pod 访问 创建的my-service即可ClusterIP 自动负载均衡apiVersion: v1 kind: Service metadata: name: my-service spec: type: ClusterIP selector: app: my-app ports: - port: 80 targetPort: 80802. NodePort我把NodePort理解为实现了节点高可用 外部访问外部请求 → 节点IP:nodePort → kube-proxy → Service → Pod其中一个节点挂掉流量需要打到其它节点的 NodePort → 实现节点 HANodePort 实际上是外部访问集群的统一入口而Pod HA仍然靠 ClusterIP 分发原理给 Service 分配一个固定端口NodePort通常在 30000~32767NodePort 是 Node 级别端口映射不关心 Pod 的具体 IPNodePort 端口是固定在节点上的只要 Pod 存在节点任意 IP 都能通过这个端口访问对应 Service应用场景小型集群或者本地测试、暴露服务给外部不依赖云 LB、临时访问或者调试落地方式访问方式http://NodeIP:30080这个ip可以自己去随意调整创建了一个my-service的对象并注册到kube-apiserver——NodePort 会在所有节点开放这个端口30080——流量经过kube-proxy再转发到 Pod 的targetPort8080apiVersion: v1 kind: Service metadata: name: my-service spec: type: NodePort selector: app: my-app ports: - port: 80 # Service 端口 targetPort: 8080 # Pod 端口 nodePort: 30080 # 外部访问端口3. LoadBalancer我把LoadBalancer理解为实现了外网的访问LoadBalancer → 云厂商 LB 前置背后其实还是 NodePort注意只有云厂商是可以实现的如果是自建的集群不能自动生成外网IP需要手搓下面提供了两种常用方式。开源 LB 控制器在裸机集群中实现LoadBalancer类型可以分配内网或公网 IP支持 ARP/BGP 模式NodePort 外部 LB自己在机房的 LBF5、Nginx、HAProxy 等上做转发指向集群所有节点的 NodePort原理云厂商提供 LBLoad Balancer服务直接用type: LoadBalancer外部请求 → 云 LB → NodePort → Service → Pod对外暴露固定 IPLB IP隐藏节点和 Pod 的实际 IP应用场景公网访问服务、高可用集群负载均衡流量、云环境AWS ELB、阿里云 SLB、Azure LB落地方式创建后云 LB 自动生成外网 IPapiVersion: v1 kind: Service metadata: name: my-service spec: type: LoadBalancer selector: app: my-app ports: - port: 80 targetPort: 80804. ExternalName我把ExternalName理解为实现了DNS解析原理不做流量转发不经过kube-proxy规则直接返回DNS的名称Pod 内访问my-service时DNS 解析到指定外部域名应用场景集群内需要访问外部服务、想给外部服务起一个集群内别名方案落地Pod 访问external-api→ DNS 解析到api.example.comapiVersion: v1 kind: Service metadata: name: external-api spec: type: ExternalName externalName: api.example.com5. Headless / ClusterIP: None我把Headless Service 理解为把 k8s的负载均衡功能关掉自己拿 Pod IP 来做流量分配。介于本人暂时没有遇到过这种使用场景所以感觉很鸡肋个人观点勿喷原理暴露 Pod 的真实 IP客户端自己选择要访问哪个 Pod。常用于有状态服务StatefulSet不分配 VIP 给podService只做 DNS 解析返回 Pod 的真实 IP 列表网络需求Pod 间的 CNI 网络必须可达否则无法访问 Pod。应用场景数据库集群MySQL、Cassandra、Kafka、StatefulSet需要客户端知道每个 Pod IP方案落地Pod 获取 Service DNS → 得到多个 Pod IP → 自己负载均衡apiVersion: v1 kind: Service metadata: name: headless-service spec: clusterIP: None selector: app: my-app ports: - port: 80 targetPort: 8080其实网络内容并不复杂关键是要彻底的区分清楚它们的本质。如果不确定自己是否彻底区分清楚那么用下面几个问题来考考自己叭~~~Headless和hostNetwork的区别是什么呢五种service方式哪些会使用到kube-proxy哪些会使用到CNI呢聪明的你一定有答案了叭总结ClusterIP 是基础NodePort/LoadBalancer 是对外暴露方式ExternalName 是别名Headless 是自管理 Pod IP 列表