Kubernetes Service 完全指南:从原理到生产实战
本文深入剖析 Kubernetes Service 的核心技术,涵盖 Service 架构设计、四种服务类型、服务发现机制、负载均衡原理、YAML 字段详解,以及生产环境最佳实践。掌握 Service 是打通 K8s 网络访问的关键一步。
目录
- [为什么需要 Service?](#为什么需要 Service?)
- [Service 核心架构](#Service 核心架构)
- [四种 Service 类型详解](#四种 Service 类型详解)
- 服务发现机制
- [YAML 字段详解](#YAML 字段详解)
- 负载均衡原理
- [实战:生产级 Service 配置](#实战:生产级 Service 配置)
- 常用命令速查
- 最佳实践与避坑指南
一、为什么需要 Service?
1.1 Pod 直接访问的问题
| 问题 | 说明 |
|---|---|
| IP 不固定 | Pod 重建后 IP 会改变 |
| 负载均衡 | 无法在多个 Pod 间分配流量 |
| 服务发现 | 客户端无法自动感知 Pod 变化 |
| 扩缩容 | 副本数变化时需手动更新 |
1.2 Service 的解决方案
#mermaid-svg-xTpL2cDhZO59VRQX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xTpL2cDhZO59VRQX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xTpL2cDhZO59VRQX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xTpL2cDhZO59VRQX .error-icon{fill:#552222;}#mermaid-svg-xTpL2cDhZO59VRQX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xTpL2cDhZO59VRQX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xTpL2cDhZO59VRQX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xTpL2cDhZO59VRQX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xTpL2cDhZO59VRQX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xTpL2cDhZO59VRQX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xTpL2cDhZO59VRQX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xTpL2cDhZO59VRQX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xTpL2cDhZO59VRQX .marker.cross{stroke:#333333;}#mermaid-svg-xTpL2cDhZO59VRQX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xTpL2cDhZO59VRQX p{margin:0;}#mermaid-svg-xTpL2cDhZO59VRQX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xTpL2cDhZO59VRQX .cluster-label text{fill:#333;}#mermaid-svg-xTpL2cDhZO59VRQX .cluster-label span{color:#333;}#mermaid-svg-xTpL2cDhZO59VRQX .cluster-label span p{background-color:transparent;}#mermaid-svg-xTpL2cDhZO59VRQX .label text,#mermaid-svg-xTpL2cDhZO59VRQX span{fill:#333;color:#333;}#mermaid-svg-xTpL2cDhZO59VRQX .node rect,#mermaid-svg-xTpL2cDhZO59VRQX .node circle,#mermaid-svg-xTpL2cDhZO59VRQX .node ellipse,#mermaid-svg-xTpL2cDhZO59VRQX .node polygon,#mermaid-svg-xTpL2cDhZO59VRQX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xTpL2cDhZO59VRQX .rough-node .label text,#mermaid-svg-xTpL2cDhZO59VRQX .node .label text,#mermaid-svg-xTpL2cDhZO59VRQX .image-shape .label,#mermaid-svg-xTpL2cDhZO59VRQX .icon-shape .label{text-anchor:middle;}#mermaid-svg-xTpL2cDhZO59VRQX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xTpL2cDhZO59VRQX .rough-node .label,#mermaid-svg-xTpL2cDhZO59VRQX .node .label,#mermaid-svg-xTpL2cDhZO59VRQX .image-shape .label,#mermaid-svg-xTpL2cDhZO59VRQX .icon-shape .label{text-align:center;}#mermaid-svg-xTpL2cDhZO59VRQX .node.clickable{cursor:pointer;}#mermaid-svg-xTpL2cDhZO59VRQX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xTpL2cDhZO59VRQX .arrowheadPath{fill:#333333;}#mermaid-svg-xTpL2cDhZO59VRQX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xTpL2cDhZO59VRQX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xTpL2cDhZO59VRQX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xTpL2cDhZO59VRQX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xTpL2cDhZO59VRQX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xTpL2cDhZO59VRQX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xTpL2cDhZO59VRQX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xTpL2cDhZO59VRQX .cluster text{fill:#333;}#mermaid-svg-xTpL2cDhZO59VRQX .cluster span{color:#333;}#mermaid-svg-xTpL2cDhZO59VRQX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xTpL2cDhZO59VRQX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xTpL2cDhZO59VRQX rect.text{fill:none;stroke-width:0;}#mermaid-svg-xTpL2cDhZO59VRQX .icon-shape,#mermaid-svg-xTpL2cDhZO59VRQX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xTpL2cDhZO59VRQX .icon-shape p,#mermaid-svg-xTpL2cDhZO59VRQX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xTpL2cDhZO59VRQX .icon-shape .label rect,#mermaid-svg-xTpL2cDhZO59VRQX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xTpL2cDhZO59VRQX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xTpL2cDhZO59VRQX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xTpL2cDhZO59VRQX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 自动发现
客户端
Service
Pod 1
Pod 2
Pod 3
新 Pod
核心能力:
- 稳定 IP:Service 有固定的 ClusterIP
- 负载均衡:自动在多个 Pod 间分发流量
- 服务发现:通过 DNS 名称访问
- 自动更新:Pod 变化时自动更新端点
二、Service 核心架构
2.1 工作原理
#mermaid-svg-vYyXMw9rqnAodi5j{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vYyXMw9rqnAodi5j .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vYyXMw9rqnAodi5j .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vYyXMw9rqnAodi5j .error-icon{fill:#552222;}#mermaid-svg-vYyXMw9rqnAodi5j .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vYyXMw9rqnAodi5j .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vYyXMw9rqnAodi5j .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vYyXMw9rqnAodi5j .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vYyXMw9rqnAodi5j .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vYyXMw9rqnAodi5j .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vYyXMw9rqnAodi5j .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vYyXMw9rqnAodi5j .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vYyXMw9rqnAodi5j .marker.cross{stroke:#333333;}#mermaid-svg-vYyXMw9rqnAodi5j svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vYyXMw9rqnAodi5j p{margin:0;}#mermaid-svg-vYyXMw9rqnAodi5j .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vYyXMw9rqnAodi5j .cluster-label text{fill:#333;}#mermaid-svg-vYyXMw9rqnAodi5j .cluster-label span{color:#333;}#mermaid-svg-vYyXMw9rqnAodi5j .cluster-label span p{background-color:transparent;}#mermaid-svg-vYyXMw9rqnAodi5j .label text,#mermaid-svg-vYyXMw9rqnAodi5j span{fill:#333;color:#333;}#mermaid-svg-vYyXMw9rqnAodi5j .node rect,#mermaid-svg-vYyXMw9rqnAodi5j .node circle,#mermaid-svg-vYyXMw9rqnAodi5j .node ellipse,#mermaid-svg-vYyXMw9rqnAodi5j .node polygon,#mermaid-svg-vYyXMw9rqnAodi5j .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vYyXMw9rqnAodi5j .rough-node .label text,#mermaid-svg-vYyXMw9rqnAodi5j .node .label text,#mermaid-svg-vYyXMw9rqnAodi5j .image-shape .label,#mermaid-svg-vYyXMw9rqnAodi5j .icon-shape .label{text-anchor:middle;}#mermaid-svg-vYyXMw9rqnAodi5j .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vYyXMw9rqnAodi5j .rough-node .label,#mermaid-svg-vYyXMw9rqnAodi5j .node .label,#mermaid-svg-vYyXMw9rqnAodi5j .image-shape .label,#mermaid-svg-vYyXMw9rqnAodi5j .icon-shape .label{text-align:center;}#mermaid-svg-vYyXMw9rqnAodi5j .node.clickable{cursor:pointer;}#mermaid-svg-vYyXMw9rqnAodi5j .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vYyXMw9rqnAodi5j .arrowheadPath{fill:#333333;}#mermaid-svg-vYyXMw9rqnAodi5j .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vYyXMw9rqnAodi5j .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vYyXMw9rqnAodi5j .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vYyXMw9rqnAodi5j .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vYyXMw9rqnAodi5j .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vYyXMw9rqnAodi5j .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vYyXMw9rqnAodi5j .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vYyXMw9rqnAodi5j .cluster text{fill:#333;}#mermaid-svg-vYyXMw9rqnAodi5j .cluster span{color:#333;}#mermaid-svg-vYyXMw9rqnAodi5j div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vYyXMw9rqnAodi5j .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vYyXMw9rqnAodi5j rect.text{fill:none;stroke-width:0;}#mermaid-svg-vYyXMw9rqnAodi5j .icon-shape,#mermaid-svg-vYyXMw9rqnAodi5j .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vYyXMw9rqnAodi5j .icon-shape p,#mermaid-svg-vYyXMw9rqnAodi5j .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vYyXMw9rqnAodi5j .icon-shape .label rect,#mermaid-svg-vYyXMw9rqnAodi5j .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vYyXMw9rqnAodi5j .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vYyXMw9rqnAodi5j .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vYyXMw9rqnAodi5j :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Service
Endpoints Controller
监听 Pod 变化
更新 Endpoints
kube-proxy
更新 iptables/IPVS 规则
流量转发到 Pod
关键组件:
| 组件 | 作用 |
|---|---|
| Service | 定义服务访问策略 |
| Endpoints | 记录后端 Pod 的 IP 和端口 |
| kube-proxy | 实现流量转发规则 |
| CoreDNS | 提供 DNS 服务发现 |
2.2 Label Selector 机制
yaml
# Service 通过 selector 关联 Pod
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web # 选择所有 app=web 的 Pod
tier: frontend # 可以多个条件
ports:
- port: 80
targetPort: 8080
三、四种 Service 类型详解
3.1 ClusterIP(默认)
用途:集群内部访问
yaml
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
type: ClusterIP # 默认类型
selector:
app: web
ports:
- port: 80 # Service 端口
targetPort: 8080 # Pod 端口
protocol: TCP
特点:
- 分配虚拟 IP(ClusterIP)
- 只能在集群内部访问
- 适合内部服务间通信
3.2 NodePort
用途:外部通过节点 IP 访问
yaml
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
type: NodePort
selector:
app: web
ports:
- port: 80 # Service 端口
targetPort: 8080 # Pod 端口
nodePort: 30080 # 节点端口(30000-32767)
特点:
- 在每个节点上开放端口
- 通过
节点IP:NodePort访问 - 端口范围:30000-32767
3.3 LoadBalancer
用途:云平台负载均衡器
yaml
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
type: LoadBalancer
selector:
app: web
ports:
- port: 80
targetPort: 8080
特点:
- 需要云平台支持(AWS ELB、GCP LB 等)
- 自动创建外部负载均衡器
- 分配外部 IP
3.4 ExternalName
用途:外部服务映射
yaml
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: db.example.com
特点:
- 无 selector、无端口定义
- 返回 CNAME 记录
- 用于访问集群外部服务
3.5 四种类型对比
| 类型 | 访问范围 | 适用场景 | 端口 |
|---|---|---|---|
| ClusterIP | 集群内部 | 微服务间通信 | 任意 |
| NodePort | 集群外部 | 开发测试 | 30000-32767 |
| LoadBalancer | 集群外部 | 生产环境 | 任意 |
| ExternalName | 集群内→外部 | 外部服务映射 | 无 |
四、服务发现机制
4.1 DNS 服务发现
bash
# Service 创建后自动生成 DNS 记录
<service-name>.<namespace>.svc.cluster.local
# 示例
web-service.default.svc.cluster.local
Pod 内访问示例:
bash
# 同一命名空间
curl http://web-service
# 不同命名空间
curl http://web-service.production
# 完整域名
curl http://web-service.production.svc.cluster.local
4.2 环境变量发现
yaml
# Pod 自动注入环境变量
WEB_SERVICE_SERVICE_HOST=10.96.0.1
WEB_SERVICE_SERVICE_PORT=80
注意:环境变量方式有限制:
- 只能发现同一命名空间的 Service
- Service 必须先于 Pod 创建
4.3 Headless Service
yaml
apiVersion: v1
kind: Service
metadata:
name: web-headless
spec:
clusterIP: None # Headless Service
selector:
app: web
ports:
- port: 80
用途:
- 直接返回 Pod IP 列表
- 适合有状态应用(StatefulSet)
- 客户端自定义负载均衡
五、YAML 字段详解
5.1 完整结构
yaml
apiVersion: v1
kind: Service
metadata:
name: web-service # Service 名称
namespace: default # 命名空间
labels:
app: web
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: nlb
spec:
type: ClusterIP # 服务类型
selector: # Pod 选择器
app: web
ports: # 端口配置
- name: http # 端口名称
port: 80 # Service 端口
targetPort: 8080 # Pod 端口
protocol: TCP # 协议
nodePort: 30080 # NodePort 类型时的节点端口
sessionAffinity: None # 会话亲和性
externalTrafficPolicy: Cluster # 外部流量策略
5.2 核心字段说明
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
spec.type |
String | ❌ | 服务类型(默认 ClusterIP) |
spec.selector |
Map | ❌ | Pod 选择器(ExternalName 类型不需要) |
spec.ports |
List | ✅ | 端口配置列表 |
spec.sessionAffinity |
String | ❌ | 会话亲和性(None/ClientIP) |
spec.externalTrafficPolicy |
String | ❌ | 外部流量策略(Cluster/Local) |
5.3 端口配置详解
yaml
ports:
- name: http # 端口名称(必须符合 DNS 命名规范)
port: 80 # Service 对外暴露的端口
targetPort: 8080 # 后端 Pod 的端口(可以是端口号或名称)
protocol: TCP # TCP/UDP/SCTP
nodePort: 30080 # 节点端口(仅 NodePort 类型)
targetPort 的三种写法:
yaml
targetPort: 8080 # 数字端口
targetPort: http # 命名的 Pod 端口
targetPort: web-port # 引用 Pod spec 中定义的端口名称
六、负载均衡原理
6.1 kube-proxy 三种模式
| 模式 | 原理 | 性能 | 推荐 |
|---|---|---|---|
| userspace | 用户态转发 | 最低 | ❌ 已废弃 |
| iptables | iptables 规则 | 中等 | ✅ 默认 |
| IPVS | IPVS 内核模块 | 最高 | ✅ 推荐 |
6.2 负载均衡算法
iptables 模式:
- 随机选择(默认)
- 基于概率的负载均衡
IPVS 模式:
- 支持多种算法:
rr:轮询(默认)lc:最少连接dh:目标哈希sh:源哈希sed:最短期望延迟nq:永不排队
bash
# 查看 kube-proxy 模式
kubectl logs -n kube-system kube-proxy-xxx | grep "Using"
# 配置 IPVS 模式
# 编辑 kube-proxy ConfigMap
kubectl edit configmap kube-proxy -n kube-system
# 修改 mode: "ipvs"
6.3 会话亲和性
yaml
spec:
sessionAffinity: ClientIP # 基于客户端 IP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 会话保持时间(秒)
适用场景:
- 有状态应用
- 需要会话保持的服务
七、实战:生产级 Service 配置
7.1 完整示例:微服务架构
yaml
# 1. 后端服务(ClusterIP)
apiVersion: v1
kind: Service
metadata:
name: backend-service
namespace: production
labels:
app: backend
spec:
type: ClusterIP
selector:
app: backend
tier: api
ports:
- name: http
port: 8080
targetPort: 8080
protocol: TCP
sessionAffinity: None
---
# 2. 前端服务(NodePort)
apiVersion: v1
kind: Service
metadata:
name: frontend-service
namespace: production
labels:
app: frontend
spec:
type: NodePort
selector:
app: frontend
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30080
protocol: TCP
---
# 3. 数据库服务(Headless)
apiVersion: v1
kind: Service
metadata:
name: db-headless
namespace: production
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
---
# 4. 外部服务映射(ExternalName)
apiVersion: v1
kind: Service
metadata:
name: external-api
namespace: production
spec:
type: ExternalName
externalName: api.external.com
7.2 无 Selector Service
yaml
# 手动管理 Endpoints
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
ports:
- port: 3306
targetPort: 3306
---
# 手动创建 Endpoints
apiVersion: v1
kind: Endpoints
metadata:
name: external-db
subsets:
- addresses:
- ip: 192.168.1.100 # 外部数据库 IP
ports:
- port: 3306
用途:
- 访问集群外部服务
- 迁移过程中的临时方案
7.3 多端口 Service
yaml
apiVersion: v1
kind: Service
metadata:
name: multi-port-service
spec:
selector:
app: web
ports:
- name: http
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
- name: metrics
port: 9090
targetPort: 9090
八、常用命令速查
8.1 创建与查看
bash
# 创建 Service
kubectl apply -f service.yaml
# 查看所有 Service
kubectl get services
# 查看详细信息
kubectl get service <name> -o wide
# 查看 Service 详情
kubectl describe service <name>
# 查看 YAML 定义
kubectl get service <name> -o yaml
8.2 调试与测试
bash
# 查看 Endpoints
kubectl get endpoints <service-name>
# 查看 EndpointSlices
kubectl get endpointslice
# 测试 Service 连通性
kubectl run test-pod --image=busybox --rm -it -- wget -O- http://<service-name>
# 端口转发(本地测试)
kubectl port-forward service/<service-name> 8080:80
# 查看 Service 关联的 Pod
kubectl get pods -l app=web
8.3 更新与删除
bash
# 更新 Service
kubectl apply -f service.yaml
# 编辑 Service
kubectl edit service <name>
# 删除 Service
kubectl delete service <name>
# 删除多个 Service
kubectl delete service svc1 svc2 svc3
8.4 排查问题
bash
# 1. 检查 Service 是否存在
kubectl get service <name>
# 2. 检查 Endpoints 是否正常
kubectl get endpoints <name>
# 如果 ENDPOINTS 为空,说明 selector 没有匹配到 Pod
# 3. 检查 Pod 标签
kubectl get pods --show-labels
# 4. 检查 Pod 是否就绪
kubectl get pods -o wide
# READY 列应为 1/1
# 5. 查看 kube-proxy 日志
kubectl logs -n kube-system kube-proxy-xxx
九、最佳实践与避坑指南
9.1 必须遵守的原则
| 原则 | 说明 | 示例 |
|---|---|---|
| 使用命名端口 | 便于管理和理解 | name: http |
| 配置就绪探针 | 确保流量只到就绪 Pod | readinessProbe |
| 避免 NodePort 生产 | 使用 LoadBalancer 或 Ingress | type: LoadBalancer |
| 合理设置标签 | selector 精确匹配 | app+version |
| 监控 Endpoints | 及时发现服务异常 | 定期检查 |
9.2 常见问题与解决
| 问题 | 原因 | 解决方案 |
|---|---|---|
| Endpoints 为空 | selector 不匹配 | 检查 Pod 标签 |
| 无法访问 Service | 网络策略阻止 | 检查 NetworkPolicy |
| 负载不均衡 | iptables 随机性 | 升级到 IPVS |
| 会话丢失 | 未配置会话亲和 | sessionAffinity: ClientIP |
| 外部无法访问 | 类型错误 | 使用 NodePort/LoadBalancer |
9.3 性能优化建议
yaml
# 使用 IPVS 模式
# kube-proxy ConfigMap
mode: ipvs
ipvs:
scheduler: "lc" # 最少连接算法
# 配置 externalTrafficPolicy
spec:
externalTrafficPolicy: Local # 保留客户端源 IP
# 注意:Local 模式可能造成负载不均
9.4 安全最佳实践
yaml
# 使用 NetworkPolicy 限制访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: web-policy
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 80
十、总结
10.1 核心要点回顾
- Service 提供稳定访问:解决 Pod IP 不固定的问题
- 四种类型适用不同场景:ClusterIP 内部、NodePort 测试、LoadBalancer 生产、ExternalName 外部映射
- 服务发现靠 DNS :
<service>.<namespace>.svc.cluster.local - 负载均衡由 kube-proxy 实现:IPVS 模式性能最优
- Endpoints 是桥梁:连接 Service 和 Pod
10.2 学习路线
Service → Ingress → NetworkPolicy → Service Mesh
↓
ConfigMap/Secret → PersistentVolume → StatefulSet
10.3 下一步学习
- Ingress:统一管理 HTTP/HTTPS 路由
- NetworkPolicy:网络隔离与安全
- Service Mesh:Istio、Linkerd 等高级流量管理
- CoreDNS:深入理解服务发现
- Ingress Controller:Nginx、Traefik、Kong
附录:Service 完整示例汇总
完整的 Web 应用 Service 配置
yaml
# 1. Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.25.3
ports:
- name: http
containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
livenessProbe:
httpGet:
path: /
port: 80
---
# 2. ClusterIP Service(内部访问)
apiVersion: v1
kind: Service
metadata:
name: web-internal
spec:
type: ClusterIP
selector:
app: web
ports:
- name: http
port: 80
targetPort: http
---
# 3. LoadBalancer Service(外部访问)
apiVersion: v1
kind: Service
metadata:
name: web-external
spec:
type: LoadBalancer
selector:
app: web
ports:
- name: http
port: 80
targetPort: http
sessionAffinity: None
验证测试
bash
# 1. 创建资源
kubectl apply -f web-app.yaml
# 2. 查看状态
kubectl get all
# 3. 测试内部访问
kubectl run test --image=busybox --rm -it -- wget -O- http://web-internal
# 4. 测试负载均衡(多次请求观察不同 Pod)
for i in {1..10}; do
kubectl exec <pod-name> -- curl -s http://web-internal
echo
done
# 5. 查看外部 IP
kubectl get service web-external