写在前面
本文是 K8s 学习笔记系列的第四篇,介绍 K8s 的服务发现和网络机制:Service、Ingress 和 DNS。前置知识:工作负载管理(第三篇)。
一、Service 概述
Pod 的 IP 是不固定的(重启后会变),Service 提供稳定的访问入口。
1
| 客户端 → Service(固定 IP + 固定 DNS)→ Pod(通过标签选择器自动转发)
|
1.1 Service 工作原理
1
2
3
4
| 1. Service 通过 selector 匹配 Pod 的标签
2. kube-proxy 在每个节点上配置转发规则
3. 客户端访问 Service IP,流量被转发到后端 Pod
4. Pod 变化时,控制器自动更新后端列表(EndpointSlice)
|
二、Service 类型
2.1 ClusterIP(默认)
集群内部访问,外部不可达。
1
2
3
4
5
6
7
8
9
10
11
| apiVersion: v1
kind: Service
metadata:
name: web-app
spec:
type: ClusterIP # 默认值,可省略
selector:
app: web-app
ports:
- port: 80 # Service 端口
targetPort: 8080 # Pod 端口
|
1
2
3
4
| 集群内访问方式:
- Service 名称:http://web-app:80
- Service IP:http://10.100.200.50:80
- 完整 DNS:http://web-app.default.svc.cluster.local:80
|
2.2 NodePort
通过节点端口暴露服务,外部可以通过 节点IP:端口 访问。
1
2
3
4
5
6
7
8
9
10
11
12
| apiVersion: v1
kind: Service
metadata:
name: web-app
spec:
type: NodePort
selector:
app: web-app
ports:
- port: 80 # Service 端口
targetPort: 8080 # Pod 端口
nodePort: 30080 # 节点端口(30000-32767,可省略自动分配)
|
1
2
| 外部访问方式:
- http://<任意节点IP>:30080
|
NodePort 适合测试和小规模使用,生产环境一般用 Ingress 或 LoadBalancer。
2.3 LoadBalancer
云厂商提供的负载均衡器(AWS ELB、GCP LB 等)。
1
2
3
4
5
6
7
8
9
10
11
| apiVersion: v1
kind: Service
metadata:
name: web-app
spec:
type: LoadBalancer
selector:
app: web-app
ports:
- port: 80
targetPort: 8080
|
本地环境(minikube)没有真正的 LoadBalancer。可以用 minikube tunnel 模拟。
2.4 ExternalName
通过 DNS CNAME 把 Service 名称映射到外部 DNS 名称,不创建 EndpointSlice,也不提供代理或端口转换。客户端协议还要能正确处理别名后的主机名,例如 TLS 证书校验。
1
2
3
4
5
6
7
| apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: db.example.com # 外部数据库地址
|
1
2
| 集群内访问:mysql://external-db:3306
实际解析到:mysql://db.example.com:3306
|
2.5 选型对比
四种 Service 类型怎么选?一张表 + 决策顺序:
| 类型 | 访问范围 | 典型场景 | 生产? |
|---|
| ClusterIP | 集群内 | 微服务互调(绝大多数 Service) | ✓ 默认 |
| NodePort | 节点 IP:端口 | 测试、临时暴露、自建集群 | △ 一般不直接 |
| LoadBalancer | 集群外部 | 由云平台或负载均衡器实现提供入口,是否公网取决于实现与配置 | ✓ 视环境而定 |
| ExternalName | 集群内 → 外部 DNS | 把外部服务(数据库/API)包成本地 Service | ✓ |
1
2
3
4
5
6
7
8
| 决策顺序(从简到繁):
① 集群内调用 → ClusterIP(95% 的 Service)
② 把外部服务当本地用 → ExternalName(统一入口,便于迁移)
③ 单服务直接对公网 → LoadBalancer(云上)
④ 测试 / 自建集群暴露 → NodePort
公网入口要"一个 IP 对多服务 + 域名路由 + TLS"
→ 别堆多个 LoadBalancer(贵),用 Ingress(见第五章)+ 一个 LB
|
1
2
3
4
| 一句口诀:
内部调用 ClusterIP,外部映射 ExternalName
云上直暴 LoadBalancer,测试调试 NodePort
公网多服务,Ingress 来收口
|
三、Headless Service
不分配 ClusterIP,直接返回后端 Pod 的 IP。常配合 StatefulSet 使用。
1
2
3
4
5
6
7
8
9
10
| apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
clusterIP: None # Headless
selector:
app: mysql
ports:
- port: 3306
|
1
2
3
4
| DNS 解析结果:
mysql-0.mysql.default.svc.cluster.local → Pod-0 的 IP
mysql-1.mysql.default.svc.cluster.local → Pod-1 的 IP
mysql-2.mysql.default.svc.cluster.local → Pod-2 的 IP
|
StatefulSet 的每个 Pod 有固定的 DNS 名称,适合需要知道具体连接哪个实例的场景(如 MySQL 主从)。
四、EndpointSlice
EndpointSlice 记录 Service 的后端地址,是当前 Service 发现和转发使用的主要 API。旧的 Endpoints API 从 Kubernetes 1.33 起已弃用,不应再作为新配置的首选。
1
2
3
4
5
| # 查看 Service 对应的 EndpointSlice
kubectl get endpointslice \
-l kubernetes.io/service-name=web-app
# Service 没有 selector 时,控制器不会自动创建 EndpointSlice
|
手动 EndpointSlice 示例(为无 selector 的 Service 声明外部后端):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| # Service(不指定 selector)
apiVersion: v1
kind: Service
metadata:
name: external-api
spec:
ports:
- port: 80
targetPort: 80
---
# 手动指定 EndpointSlice
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: external-api-1
labels:
kubernetes.io/service-name: external-api
endpointslice.kubernetes.io/managed-by: example.com/manual
addressType: IPv4
ports:
- name: http
protocol: TCP
port: 80
endpoints:
- addresses:
- "192.168.1.100"
|
五、Ingress
Ingress 是 Kubernetes 中声明 HTTP/HTTPS 路由的稳定 API,实际转发必须由另行安装的 Ingress Controller 实现。Ingress API 已冻结,Kubernetes 项目建议新设计优先评估仍在演进的 Gateway API;现有 Ingress 不会因此立即失效。
5.1 为什么需要 Ingress
1
2
3
4
5
6
7
8
| 没有 Ingress:
每个 Service 都需要 NodePort/LoadBalancer
端口管理混乱,无法按域名路由
有 Ingress:
只需要一个入口(LoadBalancer 或 NodePort)
按域名和路径路由到不同 Service
支持 TLS 证书
|
5.2 Ingress 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
spec:
ingressClassName: example # 替换为集群实际安装的 IngressClass
rules:
- host: app.example.com # 域名
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app # 转发到这个 Service
port:
number: 80
- host: api.example.com # 另一个域名
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
|
5.3 路径路由
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /static
pathType: Prefix
backend:
service:
name: static-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-app
port:
number: 80
|
5.4 TLS 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| spec:
tls:
- hosts:
- app.example.com
secretName: tls-secret # 包含 TLS 证书的 Secret
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app
port:
number: 80
|
5.5 安装 Ingress Controller
1
2
3
4
5
| # minikube 启用 Ingress 插件
minikube addons enable ingress
# 验证 Ingress Controller
kubectl get pods -n ingress-nginx
|
上述命令只用于 minikube 自带插件的学习环境。社区 ingress-nginx 项目已于 2026 年 3 月退役,不再获得错误修复或安全更新;生产环境应选择仍受维护的 Gateway API 实现或其他 Ingress Controller,并按其文档配置 ingressClassName。
六、DNS
K8s 集群内置 DNS 服务(CoreDNS),Pod 自动配置 DNS 解析。
6.1 DNS 命名规则
1
2
3
4
| <service-name> # 同命名空间
<service-name>.<namespace> # 跨命名空间
<service-name>.<namespace>.svc # 完整写法
<service-name>.<namespace>.svc.cluster.local # 完整 FQDN
|
6.2 跨命名空间访问
1
2
3
4
5
| # 在 dev 命名空间访问 prod 命名空间的 Service
mysql://mysql.prod.svc.cluster.local:3306
# 简写
mysql://mysql.prod:3306
|
6.3 DNS 调试
1
2
3
4
5
6
7
8
9
10
| # 启动调试 Pod
kubectl run debug --image=busybox --rm -it --restart=Never -- sh
# 在调试 Pod 中
nslookup web-app # 同命名空间
nslookup web-app.default.svc.cluster.local # 完整域名
nslookup web-app.prod # 跨命名空间
# 查看Pod 的 DNS 配置
cat /etc/resolv.conf
|
七、网络策略
NetworkPolicy 控制 Pod 之间的网络访问(默认全部互通)。
7.1 网络策略示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-policy
namespace: prod
spec:
podSelector:
matchLabels:
app: api-server # 策略应用到这些 Pod
policyTypes:
- Ingress # 入站规则
ingress:
- from:
- podSelector: # 只允许这些 Pod 访问
matchLabels:
app: web-app
ports:
- port: 8080
protocol: TCP
|
7.2 常见策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # 拒绝所有入站(默认拒绝)
spec:
podSelector: {}
policyTypes:
- Ingress
# 允许同命名空间访问
spec:
podSelector: {}
ingress:
- from:
- podSelector: {}
# 允许指定命名空间访问
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
env: dev
|
NetworkPolicy 只有在集群网络实现支持并启用相应策略能力时才会生效。minikube 是否支持取决于所选网络插件和启动配置,不能仅凭 API 对象创建成功判断策略已经执行。
八、开发者视角:服务接入
8.1 微服务间的调用方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| package example
import "net/http"
func checkServices(client *http.Client) error {
// 不要硬编码 Pod IP;通过 Service DNS 名称访问。
userResp, err := client.Get("http://user-service:8080/api/users")
if err != nil {
return err
}
userResp.Body.Close()
orderResp, err := client.Get("http://order-service.prod.svc.cluster.local:8080/api/orders")
if err != nil {
return err
}
orderResp.Body.Close()
return nil
}
|
示例由调用方传入配置了超时的 http.Client;生产代码还应检查 HTTP 状态码,并按幂等性设计重试。
8.2 服务接入检查清单
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| 1. Service selector 是否匹配 Pod 的 labels?
→ kubectl get endpointslice -l kubernetes.io/service-name=<service> 检查是否有后端
2. 端口是否正确?
→ Service `port` 要转到应用实际监听的 `targetPort`;`containerPort` 主要用于声明和命名,本身不会让进程监听端口
3. Pod 是否 Ready?
→ readinessProbe 没通过的 Pod 不会接收流量
4. 跨命名空间是否用了完整 DNS?
→ 用 <service>.<namespace> 格式
5. 外部访问是否配置了 Ingress?
→ 检查 Ingress 规则和域名解析
|
九、小结
本文学习了 K8s 的服务发现与网络:
- Service 四种类型(ClusterIP、NodePort、LoadBalancer、ExternalName)
- Headless Service 和 StatefulSet 的配合
- EndpointSlice 和外部服务后端
- Ingress HTTP 路由和 TLS
- DNS 命名规则和跨命名空间访问
- NetworkPolicy 网络策略
- 开发者视角的服务接入
下一篇将学习配置与存储:ConfigMap、Secret、PV/PVC。
参考资料