Kubernetes Service 详解:服务暴露与流量管理全指南
Kubernetes Service 详解:服务暴露与流量管理
一、Service 核心概述
1.1 什么是 Service?
在 Kubernetes 中,Pod 是应用的载体,但 Pod IP 具有动态性(重建后会变更),直接通过 Pod IP 访问服务不稳定。Service 作为 Pod 的“统一访问入口”,通过 Label 关联一组提供相同服务的 Pod,实现负载均衡和固定访问地址,屏蔽 Pod 动态变化的细节。
1.2 核心原理
Service 的功能依赖 kube-proxy(每个 Node 节点上运行的代理进程):
- 创建 Service 时,API Server 将配置写入 Etcd;
- kube-proxy 监听 Etcd 中 Service 和 Pod 的变化,生成对应的访问规则(如 iptables/ipvs 规则);
- 外部请求访问 Service 地址时,kube-proxy 按规则将请求转发到后端 Pod。
1.3 kube-proxy 工作模式
kube-proxy 支持三种工作模式,决定请求转发的效率和策略:
| 模式 | 核心原理 | 优缺点 |
|---|---|---|
| userspace | kube-proxy 监听端口,请求先转发到 proxy 再转发到 Pod(用户空间转发) | 稳定但效率低(内核/用户空间数据拷贝),已逐步淘汰 |
| iptables | kube-proxy 生成 iptables 规则,请求直接通过内核转发到 Pod | 效率高,无中间转发;但不支持灵活负载均衡策略(如会话保持),Pod 故障无重试 |
| ipvs | 基于 Linux IPVS 模块,生成虚拟服务规则,直接转发请求 | 效率最高,支持更多负载均衡算法(轮询、加权轮询等),推荐生产环境使用 |
开启 ipvs 模式(推荐)
# 1. 编辑 kube-proxy 配置(修改模式为 ipvs)
kubectl edit cm kube-proxy -n kube-system
# 将 mode: "" 改为 mode: "ipvs"# 2. 重启 kube-proxy Pod(使配置生效)
kubectl delete pod -l k8s-app=kube-proxy -n kube-system# 3. 验证 ipvs 规则(需安装 ipvsadm 工具)
ipvsadm -Ln
# 输出示例(可见 Service 对应的后端 Pod 地址):
# TCP 10.97.97.97:80 rr
# -> 10.244.1.39:80 Masq 1 0 0
# -> 10.244.1.40:80 Masq 1 0 0
二、Service 资源清单与类型
2.1 通用资源清单格式
apiVersion: v1 # 固定版本
kind: Service # 资源类型
metadata:name: <service-name> # Service 名称namespace: <namespace> # 所属命名空间
spec:selector: # 关联 Pod 的 Label(核心)app: nginx-podtype: <service-type> # Service 类型(如 ClusterIP/NodePort)clusterIP: <virtual-ip> # 虚拟 IP(ClusterIP 模式下可选,默认自动分配)sessionAffinity: <affinity-type> # 会话亲和性(ClientIP/None)ports: # 端口映射规则- protocol: TCP # 协议(默认 TCP)port: 80 # Service 暴露的端口(集群内访问端口)targetPort: 80 # Pod 内部的端口(需与容器端口一致)nodePort: 30002 # NodePort 模式下的节点端口(30000-32767 范围内)
2.2 四种 Service 类型
Service 支持四种类型,适配不同访问场景(从集群内到集群外):
| 类型 | 核心作用 | 适用场景 |
|---|---|---|
| ClusterIP | 集群内访问,自动分配虚拟 IP(仅集群内可见) | 集群内部服务间通信(如后端 API 服务) |
| NodePort | 暴露 Service 到节点端口(NodeIP:NodePort),集群外可访问 | 开发/测试环境,快速暴露服务 |
| LoadBalancer | 结合外部负载均衡器(如阿里云 SLB、AWS ELB),对外提供固定访问地址 | 生产环境,需要高可用外部访问 |
| ExternalName | 将集群外部服务映射为集群内 Service(通过域名关联) | 访问集群外服务(如数据库、第三方 API) |
三、Service 实战使用
3.0 实验环境准备
先通过 Deployment 创建 3 个 Nginx Pod(带 app=nginx-pod 标签),作为 Service 的后端:
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: pc-deploymentnamespace: dev
spec:replicas: 3selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80
# 创建 Deployment
kubectl create -f deployment.yaml# 验证 Pod(确认 Label 为 app=nginx-pod)
kubectl get pods -n dev -o wide --show-labels
3.1 ClusterIP 类型(集群内访问)
默认类型,自动分配集群内唯一虚拟 IP,仅集群内 Pod/节点可访问。
配置文件(service-clusterip.yaml)
apiVersion: v1
kind: Service
metadata:name: service-clusteripnamespace: dev
spec:selector:app: nginx-pod # 关联 Label 为 app=nginx-pod 的 Podtype: ClusterIPclusterIP: 10.97.97.97 # 可选,固定虚拟 IP(不指定则自动分配)ports:- port: 80 # Service 端口targetPort: 80 # Pod 端口
操作命令
# 1. 创建 Service
kubectl create -f service-clusterip.yaml# 2. 查看 Service(CLUSTER-IP 为 10.97.97.97)
kubectl get svc -n dev -o wide
# 输出示例:
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# service-clusterip ClusterIP 10.97.97.97 <none> 80/TCP 13s# 3. 查看后端 Pod(Endpoints 列表为关联的 Pod 地址)
kubectl describe svc service-clusterip -n dev
# 输出关键信息:
# Endpoints: 10.244.1.39:80,10.244.1.40:80,10.244.2.33:80# 4. 集群内访问测试(轮询转发到后端 Pod)
curl 10.97.97.97
负载均衡策略
- 默认策略:轮询(rr),通过 ipvs/iptables 实现;
- 会话保持:设置
sessionAffinity: ClientIP,同一客户端请求固定转发到同一 Pod:spec:sessionAffinity: ClientIP # 新增会话亲和性配置
3.2 Headless Service(无 ClusterIP)
不分配虚拟 IP,通过 Service 域名直接解析到后端 Pod IP,适用于自定义负载均衡场景(如客户端自行控制转发)。
配置文件(service-headliness.yaml)
apiVersion: v1
kind: Service
metadata:name: service-headlinessnamespace: dev
spec:selector:app: nginx-podclusterIP: None # 关键:不分配 ClusterIPtype: ClusterIPports:- port: 80targetPort: 80
操作命令
# 1. 创建 Service
kubectl create -f service-headliness.yaml# 2. 查看 Service(CLUSTER-IP 为 None)
kubectl get svc service-headliness -n dev -o wide# 3. 域名解析测试(集群内 Pod 中执行)
# 先创建测试 Pod
kubectl run busybox11 --image=busybox -n dev --sleep 6000
# 进入 Pod 解析 Service 域名
kubectl exec -it busybox11 -n dev -- /bin/sh
/ # nslookup service-headliness.dev.svc.cluster.local
# 输出示例(直接返回后端 Pod IP):
# Address 1: 10.244.1.39
# Address 2: 10.244.1.40
# Address 3: 10.244.2.33
3.3 NodePort 类型(集群外访问)
在每个 Node 节点上暴露一个固定端口(30000-32767),外部通过 NodeIP:NodePort 访问 Service,适用于开发/测试环境。
配置文件(service-nodeport.yaml)
apiVersion: v1
kind: Service
metadata:name: service-nodeportnamespace: dev
spec:selector:app: nginx-podtype: NodePort # 类型为 NodePortports:- port: 80 # Service 端口targetPort: 80 # Pod 端口nodePort: 30002 # 自定义节点端口(可选,默认自动分配)
操作命令
# 1. 创建 Service
kubectl create -f service-nodeport.yaml# 2. 查看 Service(PORT(S) 显示 80:30002/TCP)
kubectl get svc -n dev -o wide
# 输出示例:
# NAME TYPE CLUSTER-IP PORT(S) AGE
# service-nodeport NodePort 10.105.64.191 80:30002/TCP 10s# 3. 外部访问测试(浏览器/ curl 访问任意 Node IP:30002)
curl http://192.168.100.100:30002 # 192.168.100.100 为 Node IP
3.4 ExternalName 类型(映射外部服务)
将集群外部服务(如百度、自建数据库)映射为集群内 Service,通过 Service 域名即可访问外部服务,无需暴露外部地址。
配置文件(service-externalname.yaml)
apiVersion: v1
kind: Service
metadata:name: service-externalnamenamespace: dev
spec:type: ExternalName # 类型为 ExternalNameexternalName: www.baidu.com # 外部服务域名(或 IP)
操作命令
# 1. 创建 Service
kubectl create -f service-externalname.yaml# 2. 域名解析测试(集群内 Pod 中执行)
kubectl exec -it busybox11 -n dev -- /bin/sh
/ # nslookup service-externalname.dev.svc.cluster.local
# 输出示例(解析到百度 IP):
# Address 1: 39.156.66.18
# Address 2: 39.156.66.14
四、Ingress:统一入口管理(7层负载均衡)
4.1 为什么需要 Ingress?
NodePort 和 LoadBalancer 存在明显缺点:
- NodePort:占用大量节点端口,服务增多时难以管理;
- LoadBalancer:每个 Service 需一个外部 LB,成本高、配置繁琐。
Ingress 作为 7层负载均衡器,通过一个入口(NodePort/LB)暴露多个 Service,支持域名路由、HTTPS 终止、路径匹配等高级功能,本质是对 Nginx/HAProxy 等反向代理的抽象。
4.2 核心组件
- Ingress 资源:定义路由规则(如“nginx.chenyu.com → nginx-service”);
- Ingress Controller:实现反向代理的程序(如 Nginx-Ingress),监听 Ingress 规则变化,生成对应的代理配置。
4.3 Ingress 实战
4.3.1 部署 Ingress Controller(Nginx 版本)
# 1. 创建目录并下载部署文件
mkdir ingress-controller && cd ingress-controller
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/baremetal/deploy.yaml# 2. 部署 Ingress Controller(默认命名空间 ingress-nginx)
kubectl apply -f deploy.yaml# 3. 验证部署(Pod 状态为 Running)
kubectl get pod -n ingress-nginx
# 输出示例:
# NAME READY STATUS AGE
# nginx-ingress-controller-fbf967dd5-4qpbp 1/1 Running 12h# 4. 查看 Ingress 暴露的 NodePort(80 对应 HTTP,443 对应 HTTPS)
kubectl get svc -n ingress-nginx
# 输出示例:
# NAME TYPE PORT(S) AGE
# ingress-nginx NodePort 80:32240/TCP,443:31335/TCP 11h
4.3.2 准备后端 Service(Nginx + Tomcat)
创建两个 Service 作为 Ingress 路由目标:
# tomcat-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deploymentnamespace: dev
spec:replicas: 3selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:name: tomcat-deploymentnamespace: dev
spec:replicas: 3selector:matchLabels:app: tomcat-podtemplate:metadata:labels:app: tomcat-podspec:containers:- name: tomcatimage: tomcat:8.5-jre10-slimports:- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:name: nginx-servicenamespace: dev
spec:selector:app: nginx-podtype: ClusterIPports:- port: 80targetPort: 80
---
apiVersion: v1
kind: Service
metadata:name: tomcat-servicenamespace: dev
spec:selector:app: tomcat-podtype: ClusterIPports:- port: 8080targetPort: 8080
# 创建资源
kubectl create -f tomcat-nginx.yaml
4.3.3 HTTP 路由配置
通过域名路由到不同 Service(如 nginx.chenyu.com → nginx-service):
# ingress-http.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: ingress-httpnamespace: dev
spec:ingressClassName: nginx # 指定 Ingress Controller 类型rules:- host: nginx.chenyu.com # 自定义域名(需配置本地 Hosts 解析)http:paths:- path: /pathType: Prefixbackend:service:name: nginx-serviceport:number: 80- host: tomcat.chenyu.com # 另一个域名http:paths:- path: /pathType: Prefixbackend:service:name: tomcat-serviceport:number: 8080
# 1. 创建 Ingress 规则
kubectl create -f ingress-http.yaml# 2. 验证规则(可见 Hosts 与 Service 的映射)
kubectl describe ingress ingress-http -n dev# 3. 本地配置 Hosts(将域名解析到任意 Node IP)
echo "192.168.100.100 nginx.chenyu.com tomcat.chenyu.com" >> /etc/hosts# 4. 访问测试(通过 NodePort 32240 访问)
curl http://nginx.chenyu.com:32240 # 访问 Nginx
curl http://tomcat.chenyu.com:32240 # 访问 Tomcat
4.3.4 HTTPS 路由配置(带证书)
为域名配置 HTTPS,需先生成证书并创建 Secret:
# 1. 生成自签名证书(有效期 365 天)
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 \-keyout tls.key -out tls.crt \-subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=chenyu.com"# 2. 创建 Kubernetes Secret(存储证书)
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
# ingress-https.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: ingress-httpsnamespace: dev
spec:tls: # HTTPS 配置- hosts:- nginx.chenyu.com- tomcat.chenyu.comsecretName: tls-secret # 关联证书 SecretingressClassName: nginxrules:- host: nginx.chenyu.comhttp:paths:- path: /pathType: Prefixbackend:service:name: nginx-serviceport:number: 80- host: tomcat.chenyu.comhttp:paths:- path: /pathType: Prefixbackend:service:name: tomcat-serviceport:number: 8080
# 1. 创建 HTTPS Ingress 规则(先删除 HTTP 规则)
kubectl delete ingress ingress-http -n dev
kubectl create -f ingress-https.yaml# 2. 访问测试(通过 HTTPS 端口 31335)
curl -k https://nginx.chenyu.com:31335 # -k 忽略证书校验
五、总结
Service 是 Kubernetes 服务暴露的核心组件,通过 Label 关联 Pod 实现负载均衡和固定访问;Ingress 则解决了多 Service 统一入口的问题,支持 7 层路由和 HTTPS 等高级功能。
| 组件 | 核心场景 | 推荐用法 |
|---|---|---|
| Service | 集群内服务通信、简单外部访问(NodePort) | 内部服务用 ClusterIP,测试环境用 NodePort |
| Ingress | 生产环境多服务统一入口、HTTPS 终止、域名路由 | 搭配 Nginx-Ingress,配置域名和路径路由 |
通过合理组合 Service 和 Ingress,可实现从集群内到集群外的灵活、安全、高效的服务访问架构。
