一、VS Code 编写 K8s 资源文件1.1 安装与扩展VS Code 分 User Installer / System Installer功能无差别仅安装位置、自动更新权限不同。两个必备扩展YAML by Red Hat通用 YAML 支持负责语法校验、Schema 校验、字段补全、悬浮文档、格式化、锚点跳转。Kubernetes by Microsoftk8s 开发运维工作台依赖 Red Hat YAML 扩展提供 Cluster Explorer上下文 / 命名空间 / 工作负载 / Service/Pod/Node 实时树视图、apply/delete/describe/ 日志跟随 / 进 Pod shell / 端口转发、Helm 操作、CRD Schema 智能感知。1.2 连接集群下载kubectl.exe放到$HOME\.kube\并加入 PATH。将集群凭据config放到$HOME\.kube\config左侧 Cluster Explorer 即可看到集群。编写资源时输入p选择Kubernetes Pod自动生成模板补全小技巧关键字后加冒号:。二、Service 是什么2.1 背景问题两个前置实验结论Pod IP 仅集群内部可达集群外部无法通过 PodIP 访问 Pod。控制器动态创建 / 销毁 PodPod IP 不断变化hostPort占用节点端口同一节点无法跑多个同端口 Pod。前端应用无法跟踪后端 Pod 的 IP 变化也无法做负载均衡。2.2 Service 概念Service 把一组 Pod 的网络应用统一暴露成网络服务拥有固定不变的 ClusterIP 和端口为后端 Pod 提供负载均衡。Service 通过selector标签选择后端 Pod自动维护 Service ↔ PodEndpoints映射关系后端 Pod 增删变化对客户端透明。关键目标无需改造老应用即可接入服务发现。三、Service 基本管理3.1 命令创建# 创建 Deployment3 副本 httpd kubectl create deployment web --imagehub.laoma.cloud/library/httpd:2.4.58 --replicas3 # 创建 ClusterIP 类型 Service访问 ClusterIP 的 8080 转发到 Pod 的 80 kubectl create service clusterip web --tcp8080:80 kubectl get svc kubectl describe service web # 查看 Selector、Endpoints、Session Affinity kubectl describe svc web | grep -e Endpoints -e IP: curl 查看到的IP地址:8080 # 访问测试--tcp8080:80Service 端口 8080 → 后端 Pod 端口 80。Service 默认标签appsvc-nameEndpoints字段列出当前后端 PodIP:Port 列表。3.2 负载均衡验证# 把每个 Pod 主页改成自己的 Pod 名 for pod in $(kubectl get pods -o name | awk -F/ {print $2}); do kubectl exec -it $pod -- bash -c echo $pod htdocs/index.html done # 连续访问 100 次统计各 Pod 命中次数轮询负载均衡 for i in {1..100}; do curl -s 自己的IP地址:8080; done | sort | uniq -c新创建标签匹配appweb的 Pod 会自动被 Service 纳入后端rollout restart重建 Pod 后 Service 仍能动态发现新 Pod。Deployment 的 selector 与 Service 的 selector 可以是不同标签如 deploy 匹配app1web1svc 匹配app2web2。3.3 expose 与 yaml 创建# expose 方式--portService端口 --target-portPod端口 --selector标签 kubectl expose deployment web --port8080 --target-port80 --selectorapp2web2 # 导出 yaml 模板 kubectl create service clusterip web --tcp8080:80 -o yaml --dry-runclient svc-web.yml kubectl apply -f svc-web.yml注意Service IP 只做 TCP/UDP 端口转发ping 不通 ClusterIP但可以 ping 通 PodIP。Service 核心 yaml 字段apiVersion: v1 kind: Service metadata: name: web labels: app: web spec: type: ClusterIP selector: app: web ports: - name: 8080-80 port: 8080 # ClusterIP 监听端口 protocol: TCP targetPort: 80 # 后端 Pod 端口四、Service 发现集群内三种访问方式4.1 通过 IP 访问实验mysql Pod ClusterIP ServiceWordPress 通过 ServiceIP 连接数据库。# mysql Pod Service kubectl run mysql --imagehub.laoma.cloud/library/mysql --image-pull-policyIfNotPresent \ --envMYSQL_ROOT_PASSWORDredhat --envMYSQL_USERtom \ --envMYSQL_PASSWORDredhat --envMYSQL_DATABASEblog \ --dry-runclient -o yaml pod-mysql.yaml kubectl apply -f pod-mysql.yaml kubectl expose pod mysql --port3306 --target-port3306 # WordPress 直接用 ServiceIP 作为数据库地址 kubectl run wordpress --imagehub.laoma.cloud/library/wordpress --image-pull-policyIfNotPresent \ --envWORDPRESS_DB_USERtom --envWORDPRESS_DB_PASSWORDredhat \ --envWORDPRESS_DB_NAMEblog --envWORDPRESS_DB_HOST10.111.69.45 kubectl expose pod wordpress --port80 --target-port80 --type NodePort4.2 通过环境变量访问Pod 启动时kubelet 把已存在的 Service 注入环境变量SVC_SERVICE_HOST、SVC_SERVICE_PORTSVC_PORT_PORT_TCP_ADDR、SVC_PORT_PORT_TCP_PORT、SVC_PORT等。限制Service 必须先于 Pod 创建后创建的 Service 不会回填到已有 PodPod 只能访问同一命名空间的 Service。# 进入临时 Pod 查看注入变量 kubectl run test --rm -it --imagebusybox sh env | grep MYSQL # MYSQL_SERVICE_HOST10.111.69.45 MYSQL_SERVICE_PORT3306 # WordPress 用环境变量引用 --envWORDPRESS_DB_HOST$(MYSQL_SERVICE_HOST)4.3 通过 DNS 名称访问推荐kubeadm 默认安装CoreDNSService 名kube-dnsClusterIP10.96.0.10端口 53新 Service 创建后 CoreDNS 自动添加 DNS 记录。Pod 的/etc/resolv.confnameserver 10.96.0.10搜索域svc.cluster.local等。访问格式同命名空间service或service.namespace跨命名空间service.namespace.svc.cluster.localkubectl run busybox --rm -it --imagebusybox /bin/sh wget wordpress:80 # 同 ns省略命名空间 wget wordpress.services:80 # 显式指定 ns wget wordpress.services.svc.cluster.local:80 # WordPress 直接用 Service 名连库 --envWORDPRESS_DB_HOSTmysql五、Service 类型4 种 Headless表格类型作用范围说明ClusterIP默认仅集群内部从预留 IP 池分配内部 ClusterIP其他类型都基于它构建可在spec.clusterIP指定合法 IP置None即无头服务NodePort集群所有节点在每个节点上开放同一端口默认 30000‑32767外部通过节点IP:NodePort访问内部仍先经过 ClusterIPLoadBalancer集群外部需要云厂商 LB 或第三方 MetalLB 提供独立外部 IPK8s 本身不实现 LBExternalName集群内部把 Service 映射成外部域名的 CNAME 记录不创建代理、不分配 IP、不做负载均衡Headless集群内部clusterIP: None不分配 ClusterIPkube‑proxy 不处理DNS 直接返回后端 Pod 的 A/AAAA 记录5.1 ClusterIP默认类型可手动指定spec.clusterIP必须落在service-cluster-ip-rangeCIDR 内非法值 API 返回 422。5.2 NodePortkubectl expose deployment web --type NodePort --port8080 --target-port80 \ -o yaml --dry-runclient service-NodePort.yaml kubectl apply -f service-NodePort.yaml kubectl get service # 8080:31917/TCP # 访问任一节点 curl http://10.1.8.30:31917 curl http://10.1.8.31:31917 curl http://10.1.8.32:31917三个端口概念nodePort节点上监听的端口30000‑32767可手动指定固定端口如30080portClusterIP 上监听端口targetPortPod 监听端口。iptables 规则验证每个节点额外增加 KUBE‑NODEPORTS 链规则iptables-save | grep 31917 # -A KUBE-NODEPORTS --dport 31917 -j KUBE-SVC-WE5D4GWSX3MMPCHH iptables-save | grep KUBE-SVC-WE5D4GWSX3MMPCHH # 规则链按概率分流到各 KUBE-SEP-xxx 后端端点链5.3 LoadBalancerMetalLB 实现裸金属集群K8s 自身不实现 LoadBalancer裸金属环境用MetalLB提供外部 IP。部署步骤# 下载并修改镜像仓库地址 wget https://laoma.cloud/.../metallb-0.14.8.tar.gz tar -xf metallb-0.14.8.tar.gz sed -i s/quay.io/hub.laoma.cloud/g metallb-0.14.8/config/manifests/metallb-native.yaml kubectl apply -f metallb-0.14.8/config/manifests/metallb-native.yaml kubectl get all -n metallb-system # controller 1 副本 speaker DaemonSet 每节点一个配置地址池 L2 宣告# ippool.yaml apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: first-pool namespace: metallb-system spec: addresses: - 10.1.8.40-10.1.8.80 --- # L2.yaml apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: example namespace: metallb-systemkubectl apply -f ippool.yaml kubectl apply -f L2.yaml创建 LoadBalancer Service 并测试kubectl expose deployment web --type LoadBalancer --port80 --target-port80 \ -o yaml --dry-runclient service-LoadBalancer.yaml kubectl apply -f service-LoadBalancer.yaml kubectl get service # web LoadBalancer 10.109.101.67 10.1.8.40 80:32101/TCP curl http://10.1.8.40:80 # 注意用 Service 端口 80不是 NodePort 32101流量路径客户端 → MetalLB VIPL2 模式 ARP 宣告流量进入集群任意节点→ 节点内核拦截 Service IP → kube‑proxy 匹配 iptables/IPVS 规则 → Service 选择健康 Pod → CNI 跨节点转发 → Pod 网络命名空间 → Pod 响应原路返回。MetalLB 只负责 ARP 宣告 占用 IP转发仍由 kube‑proxy Service 完成。5.4 ExternalNameapiVersion: v1 kind: Service metadata: name: www spec: type: ExternalName externalName: www.laoma.cloud查询www.ns.svc.cluster.local时 DNS 返回 CNAME 记录www.laoma.cloud重定向发生在 DNS 层不经过代理转发。5.5 Headless ServiceclusterIP: None不分配 ClusterIPkube‑proxy 不处理平台不提供负载均衡。带 selector控制平面创建 EndpointSliceDNS 直接返回后端 Pod 的 A/AAAA 记录用于 StatefulSet、自定义负载均衡、直连 Pod。不带 selector不创建 EndpointSliceExternalName 走 CNAME其他类型对就绪端点配置 A/AAAA 记录此时port必须与targetPort匹配。六、Service 会话保持Session Affinity6.1 原理spec.sessionAffinityNone默认/ClientIP基于客户端 IP 亲和。sessionAffinityConfig.clientIP.timeoutSeconds粘性时长默认10800 秒3 小时。首次访问记录「客户端 IP ↔ Pod」映射后续请求转发同一 Pod适合有状态服务Java Web 登录态、购物车、WebSocket、游戏长连接。6.2 实验kubectl create deployment web --imagehub.laoma.cloud/library/httpd --replicas2 kubectl expose deployment web --port 80 # 改各 Pod 主页为 Pod 名 for pod in $(kubectl get pods -o name | awk -F/ {print $2}); do kubectl exec -it $pod -- bash -c echo $pod htdocs/index.html done # 默认无亲和20 次请求两个 Pod 各 10 次 for i in {1..20}; do curl -s 10.111.90.183; done | sort | uniq -c # 开启 ClientIP 亲和 kubectl patch svc web -p {spec:{sessionAffinity:ClientIP}} # 同一客户端 20 次请求全部落到同一个 Pod for i in {1..20}; do curl -s 10.111.90.183; done | sort | uniq -c七、金丝雀发布Canary7.1 思路同时保留旧版本stable与新版本canary两套 Deployment共用同一个 Service用track标签区分通过调整两套副本比例控制新版本流量占比验证无误后逐步全量。7.2 实验步骤# 1. 准备两个版本页面 ConfigMap mkdir web echo hello nginx 1.28 web/index28.html echo hello nginx 1.29 web/index29.html kubectl create configmap web --from-file./web旧版本 Deploymenttrack: stable10 副本apiVersion: apps/v1 kind: Deployment metadata: labels: {app: web} name: web-28 spec: replicas: 10 selector: matchLabels: {app: web, tier: frontend, track: stable} template: metadata: labels: {app: web, tier: frontend, track: stable} spec: containers: - image: hub.laoma.cloud/library/nginx:1.28 name: nginx imagePullPolicy: IfNotPresent ports: - containerPort: 80 volumeMounts: - name: webcontent mountPath: /usr/share/nginx/html volumes: - name: webcontent configMap: name: web items: - key: index28.html path: index.htmlServiceselector 只匹配 app tier不区分 trackapiVersion: v1 kind: Service metadata: labels: {app: web} name: web spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: web tier: frontend新版本 Deploymenttrack: canary1 副本镜像nginx:1.29ConfigMap 取index29.html标签track: canary。7.3 流量比例验证kubectl apply -f webapp-1.28.yaml kubectl apply -f webapp-svc.yaml kubectl apply -f webapp-1.29.yaml # 8 旧 2 新 → 约 80% / 20% 流量 kubectl scale deployment web-28 --replicas 8 kubectl scale deployment web-29 --replicas 2 for i in {1..50}; do curl -s 10.98.235.36; done | sort -n | uniq -c # 39 hello nginx 1.28 / 11 hello nginx 1.29 # 逐步扩大新版本6 旧 4 新 kubectl scale deployment web-28 --replicas 6 kubectl scale deployment web-29 --replicas 4八、kube‑proxy8.1 作用kube‑proxy 是节点上运行的核心网络组件DaemonSet监听 Service 与 Endpoint 变化负责Service → 后端 Pod 的流量转发与负载均衡。8.2 三种工作模式对比表格模式地位性能适用场景特点iptables默认模式中服务数 1000小规模、环境稳定依赖内核 netfilter规则链随服务 / 端点增多膨胀匹配逐条遍历规则多时 CPU 高、延迟大IPVS生产推荐极高十万级服务中大规模生产环境基于内核 IPVS 哈希表O (1) 查找绕过复杂规则链支持多种调度算法Userspace已废弃低仅旧版兼容全用户态转发性能最差K8s 1.25 已移除生产建议IPVS 模式 Calico 网络插件。8.3 查看与切换模式# 查看 ConfigMap 中的 mode空字符串即默认 iptables kubectl get cm -n kube-system kube-proxy -o yaml | grep mode # 查看当前运行模式日志中 Using iptables proxy / Using ipvs Proxier kubectl get pods -n kube-system -l k8s-appkube-proxy -o name kubectl logs -n kube-system kube-proxy-9xfwh | grep Using # 切换为 IPVS编辑 ConfigMapmode: ipvs kubectl edit configmap -n kube-system kube-proxy # 重启 DaemonSet 生效 kubectl rollout restart daemonset -n kube-system kube-proxy # 再次验证日志 kubectl logs -n kube-system new-pod | grep Using8.4 IPVS 模式工作原理kube‑proxy 在宿主机内核创建 IPVS 虚拟服务器把后端 Pod 注册为 Real Server规则存于哈希表O (1)流量到达后按调度算法直接转发到 Pod绕过 iptables 规则链。支持的调度算法表格算法含义适用场景rr轮询节点配置一致的简单均衡wrr加权轮询最推荐通用 Web/API/ 网关权重正比于 Podresources.requests.cpu/memorylc最少连接长连接、WebSocket、游戏服务dh目标地址哈希会话保持sh源地址哈希配合sessionAffinity: ClientIPsed最短预期延迟‑nq最少队列‑实验验证# 1. 创建 3 副本 nginx Deployment主页写 Pod 名创建 Service kubectl create deployment web --imagenginx --replicas3 for pod in $(kubectl get pods -o custom-columnsNAME:.metadata.name --no-headers); do kubectl exec $pod -- bash -c echo $pod /usr/share/nginx/html/index.html done kubectl expose deployment web --port80 # 2. 查看 IPVS 虚拟服务器与后端 Real Server ipvsadm -Lnt 10.103.143.120:80 # TCP 10.103.143.120:80 rr # - 10.224.19.38:80 Masq 1 ... # - 10.224.113.164:80 Masq 1 ... # - 10.224.113.165:80 Masq 1 ... # 3. 验证轮询60 次访问各 Pod 命中 20 次 for i in {1..60}; do curl -s 10.103.143.120; done | sort | uniq -c切换调度算法kubectl edit configmap kube-proxy -n kube-system # ipvs: # scheduler: wrr # mode: ipvs kubectl rollout restart ds kube-proxy -n kube-system生产最佳配置KubeProxyConfiguration 节选mode: ipvs ipvs: scheduler: rr # rr / wrr / lc 三选一 tcpTimeout: 900 tcpFinTimeout: 120 udpTimeout: 300 excludeCIDRs: - 127.0.0.1/32 strictARP: true oomScoreAdj: -999 iptables: minSyncPeriod: 5s syncPeriod: 30s logging: format: text verbosity: 2会话保持优先级Service 配置sessionAffinity: ClientIP→ kube‑proxy 自动使用 IPVS 的SH源地址哈希保证同一 IP → 同一 Pod优先级最高。Service 不配置亲和性 → 使用 IPVSscheduler中设置的算法rr /lc/wrr。8.5 iptables 模式工作原理kube‑proxy 监听 Service/Endpoint 变化在宿主机内核动态生成 iptables 规则链流量经 netfilter 逐条匹配规则完成 DNAT 与负载均衡。规则链分层全链路KUBE-SERVICES服务总入口匹配目的 ClusterIP 端口跳转到该 Service 专属调度链。KUBE-SVC-XXXService 专属调度链非 Pod 网段流量先做KUBE-MARK-MASQSNAT 标记再用statistic --mode random按概率分流到各端点链。KUBE-SEP-XXX后端端点链执行DNAT --to-destination PodIP:Port。实验验证# 保存规则并按 ServiceIP 追踪链路 iptables-save iptables.list grep 10.103.143.120 iptables.list grep KUBE-SVC-7D76YWGERGEPC4GC iptables.list grep -e KUBE-SEP-HYZM2VM7RCC7M2HX -e KUBE-SEP-CHPZVFCOFL4YLCYM -e KUBE-SEP-HM7RLUR2RXSQ3D6F iptables.list | grep DNAT3 个后端 Pod 时概率分配为 1/3、1/2剩余、无条件兜底合计各 1/3。缺点每新增 Service/Endpoint 都要增改规则服务超过 1000 后规则链膨胀CPU 占用飙升、延迟显著增加。8.6 Userspace 模式kube‑proxy 在用户态监听 Service 端口接收流量后本地代理转发到后端 Pod全用户态处理性能极低K8s 1.25 已彻底移除。九、环境清理kubectl delete ns services思维导图