K8s 学习笔记(四):服务发现与网络

写在前面

本文是 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 变化时,Service 自动更新后端列表(Endpoints)

二、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

将 Service 映射到外部 DNS 名称,不创建 Endpoints。

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

三、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 主从)。


四、Endpoints

Endpoints 是 Service 和 Pod 之间的桥梁。

1
2
3
4
5
6
7
# 查看 Service 对应的 Endpoints
kubectl get endpoints web-app
# NAME      ENDPOINTS                                      AGE
# web-app   10.244.1.5:8080,10.244.2.3:8080,10.244.3.7:8080

# Service 没有 selector 时,需要手动创建 Endpoints
# 用于代理集群外部的服务

手动 Endpoints 示例(代理外部服务):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Service(不指定 selector)
apiVersion: v1
kind: Service
metadata:
  name: external-api
spec:
  ports:
  - port: 80
    targetPort: 80
---
# 手动指定 Endpoints
apiVersion: v1
kind: Endpoints
metadata:
  name: external-api            # 和 Service 同名
subsets:
- addresses:
  - ip: 192.168.1.100           # 外部服务 IP
  ports:
  - port: 80

五、Ingress

Ingress 是 K8s 的 HTTP 路由层,类似 Nginx 反向代理,根据域名和路径转发到不同 Service。

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
29
30
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx       # 指定 Ingress Controller
  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

六、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 需要网络插件支持(Calico、Cilium 等),minikube 默认不支持。


八、开发者视角:服务接入

8.1 微服务间的调用方式

1
2
3
// 不要硬编码 IP,用 Service 名称
resp, err := http.Get("http://user-service:8080/api/users")
resp, err := http.Get("http://order-service.prod.svc.cluster.local:8080/api/orders")

8.2 服务接入检查清单

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
1. Service selector 是否匹配 Pod 的 labels?
   → kubectl get endpoints <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 的配合
  • Endpoints 和外部服务代理
  • Ingress HTTP 路由和 TLS
  • DNS 命名规则和跨命名空间访问
  • NetworkPolicy 网络策略
  • 开发者视角的服务接入

下一篇将学习配置与存储:ConfigMap、Secret、PV/PVC。