🌐 第04课:Service与网络

📌 课程阶段:K8s基础(4/5)|预计时间:60分钟|难度:⭐⭐⭐☆☆

一、Service——K8s的服务发现机制

Pod是临时的,IP会变。Service为一组Pod提供稳定的访问入口(固定IP + DNS名),并通过selector自动负载均衡到后端Pod。

Service工作原理

                    ┌─────────────────────┐
                    │    Client (Pod A)    │
                    │  curl httpd-svc:80   │
                    └──────────┬──────────┘
                               │
                    ┌──────────▼──────────┐
                    │      Service        │
                    │  ClusterIP: 10.96.x │
                    │  DNS: httpd-svc     │
                    │  Port: 80 → 8080    │
                    └──────────┬──────────┘
                               │ 负载均衡(round-robin)
              ┌────────────────┼────────────────┐
              │                │                │
        ┌─────▼─────┐   ┌─────▼─────┐   ┌─────▼─────┐
        │  Pod-1    │   │  Pod-2    │   │  Pod-3    │
        │ 10.244.1.x│   │ 10.244.2.x│   │ 10.244.1.y│
        │ Ready ✓   │   │ Ready ✓   │   │ Ready ✓   │
        └───────────┘   └───────────┘   └───────────┘

  kube-proxy在每个节点维护iptables/IPVS规则:
  ClusterIP → DNAT → PodIP (round-robin)

二、Service类型详解

类型访问范围外部IP典型场景
ClusterIP集群内部微服务间通信
NodePort集群内外NodeIP:30000-32767开发测试、临时暴露
LoadBalancer公网云厂商LB IP生产环境对外服务
ExternalNameDNS CNAME引用外部服务

2.1 ClusterIP(默认)

# clusterip-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: web-svc
  namespace: default
spec:
  type: ClusterIP            # 默认值
  selector:
    app: web-app             # 匹配Pod标签
  ports:
  - name: http
    port: 80                 # Service端口
    targetPort: 8080         # Pod容器端口
    protocol: TCP
  - name: https
    port: 443
    targetPort: 8443
  sessionAffinity: ClientIP  # 会话亲和性(同一客户端→同一Pod)
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 3600

# ✅ 验证通过
kubectl apply -f clusterip-svc.yaml
kubectl get svc web-svc
# NAME      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
# web-svc   ClusterIP   10.96.142.58    <none>        80/TCP,443/TCP   5s

# DNS解析测试
kubectl run test --image=busybox:1.36 --rm -it --restart=Never -- \
  nslookup web-svc.default.svc.cluster.local
# Server:    10.96.0.10
# Address:   10.96.0.10
# Name:      web-svc.default.svc.cluster.local
# Address:   10.96.142.58

2.2 NodePort

# nodeport-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: web-nodeport
spec:
  type: NodePort
  selector:
    app: web-app
  ports:
  - name: http
    port: 80                 # ClusterIP端口
    targetPort: 8080         # Pod端口
    nodePort: 30080          # 节点端口(30000-32767)

# ✅ 验证通过
kubectl apply -f nodeport-svc.yaml
kubectl get svc web-nodeport
# NAME            TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
# web-nodeport    NodePort   10.96.200.1    <none>        80:30080/TCP     5s

# 从任意节点访问
curl http://<NodeIP>:30080

2.3 LoadBalancer

# loadbalancer-svc.yaml(云环境专用)
apiVersion: v1
kind: Service
metadata:
  name: web-lb
  annotations:
    # AWS示例
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
  type: LoadBalancer
  selector:
    app: web-app
  ports:
  - port: 80
    targetPort: 8080

# ✅ 验证通过(云环境)
kubectl get svc web-lb
# NAME     TYPE           CLUSTER-IP     EXTERNAL-IP                    PORT(S)
# web-lb   LoadBalancer   10.96.50.1     a1b2c3.elb.amazonaws.com      80:31234/TCP

2.4 ExternalName

# external-svc.yaml - 引用集群外部服务
apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: mysql.prod.example.com

# 集群内访问:external-db.default.svc.cluster.local
# 实际CNAME到 mysql.prod.example.com

2.5 无Selector Service

# 手动管理Endpoints(对接外部服务)
apiVersion: v1
kind: Service
metadata:
  name: external-api
spec:
  ports:
  - port: 443
    targetPort: 443
  # 注意:没有selector!

---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-api     # 必须与Service同名
subsets:
- addresses:
  - ip: 192.168.1.100    # 外部服务IP
  - ip: 192.168.1.101
  ports:
  - port: 443

三、K8s网络模型

K8s网络四大原则

┌──────────────────────────────────────────────────┐
│            K8s网络四大黄金法则                      │
├──────────────────────────────────────────────────┤
│  1️⃣  所有Pod间可以直接通信(无需NAT)              │
│  2️⃣  所有Node与Pod间可以直接通信(无需NAT)         │
│  3️⃣  Pod看到的自己的IP和别人看到它的IP一致           │
│  4️⃣  同Pod内容器共享Network Namespace(localhost)  │
└──────────────────────────────────────────────────┘

网络层级:
  ┌────────────────── CNI ──────────────────┐
  │  Flannel | Calico | Cilium | Weave Net  │
  └──────────────────────────────────────────┘
       │              │              │
  ┌────▼────┐   ┌────▼────┐   ┌────▼────┐
  │ Pod网络  │   │Service  │   │ 外部流量 │
  │ Pod↔Pod │   │ ClusterIP│  │ Ingress │
  └─────────┘   └─────────┘   └─────────┘

3.1 DNS服务发现

# K8s DNS命名规范
# <service-name>.<namespace>.svc.<cluster-domain>

# 同命名空间
curl http://web-svc:80

# 跨命名空间
curl http://web-svc.production:80

# 完整FQDN
curl http://web-svc.default.svc.cluster.local:80

# Headless Service(无ClusterIP,直接返回Pod IP)
apiVersion: v1
kind: Service
metadata:
  name: web-headless
spec:
  clusterIP: None           # Headless!
  selector:
    app: web-app
  ports:
  - port: 80
    targetPort: 8080

# ✅ 验证通过 - DNS返回所有Pod IP
kubectl run test --image=busybox:1.36 --rm -it --restart=Never -- \
  nslookup web-headless.default.svc.cluster.local
# Name:    web-headless.default.svc.cluster.local
# Address: 10.244.1.5
# Address: 10.244.2.8
# Address: 10.244.1.12

四、kube-proxy与代理模式

# 查看kube-proxy模式
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode
# mode: "iptables"  或 "ipvs"

# iptables模式(默认)
# 规则链:KUBE-SERVICES → KUBE-SVC-xxx → KUBE-SEP-xxx
# 随机选择后端Pod
sudo iptables -t nat -L KUBE-SERVICES | head -10

# IPVS模式(高性能)
# 支持更多负载均衡算法:rr|lc|wrr|wlc|sh|dh
# 性能更好:O(1) vs O(n)
kubectl get configmap kube-proxy -n kube-system -o yaml | sed 's/mode: "".*/mode: "ipvs"/' | \
  kubectl apply -f -
kubectl rollout restart daemonset kube-proxy -n kube-system

五、网络策略(NetworkPolicy基础)

# networkpolicy-basics.yaml - 默认拒绝入站
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: default
spec:
  podSelector: {}            # 匹配所有Pod
  policyTypes:
  - Ingress                  # 拒绝所有入站

---
# 允许特定标签Pod访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend
spec:
  podSelector:
    matchLabels:
      app: backend           # 应用到backend Pod
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend      # 只允许frontend Pod访问
    ports:
    - port: 8080
      protocol: TCP
💡 NetworkPolicy需要CNI插件支持(Calico/Cilium支持,Flannel部分支持)。

六、故障排查实战

6.1 Service没有Endpoints

# 现象:访问Service无响应
kubectl get endpoints web-svc
# NAME      ENDPOINTS          AGE
# web-svc   <none>            5m   ← 没有后端!

# 排查:检查selector是否匹配
kubectl get pods --show-labels | grep web-app
# web-app-xxx   1/1   Running   app=web,version=v1   ← 标签不匹配!

# Service selector: app=web-app
# Pod labels: app=web  ← 名称不一致!

# 修复:统一标签
kubectl edit svc web-svc
# 将selector改为 app=web

6.2 DNS解析失败

# 检查CoreDNS
kubectl get pods -n kube-system -l k8s-app=kube-dns
# NAME                       READY   STATUS    RESTARTS   AGE
# coredns-5dd5756b68-xxx     1/1     Running   0          1h

# 测试DNS
kubectl run test --image=busybox:1.36 --rm -it --restart=Never -- \
  nslookup kubernetes.default.svc.cluster.local

# 如果失败:
# 1. 检查CoreDNS日志
kubectl logs -n kube-system coredns-5dd5756b68-xxx
# 2. 检查resolv.conf
kubectl run test --image=busybox:1.36 --rm -it --restart=Never -- \
  cat /etc/resolv.conf
# 3. 检查kubelet的cluster-dns配置

6.3 跨节点Pod无法通信

# 排查CNI
kubectl get pods -n kube-flannel
# 如果Flannel Pod异常,跨节点通信会失败

# 检查节点路由
ip route | grep flannel
# 10.244.1.0/24 via ...  ← 对端节点的Pod网段路由

# 检查节点间网络连通性
ping <other-node-ip>
traceroute <other-node-pod-ip>

七、练习

  1. 创建一个3副本Deployment + ClusterIP Service,验证服务发现和负载均衡
  2. 创建NodePort Service,从集群外部访问应用
  3. 创建Headless Service,对比DNS解析结果与普通Service的区别
  4. 创建ExternalName Service,将集群内请求代理到外部服务
  5. 部署NetworkPolicy,实现frontend→backend的网络隔离

🏆 第04课成就解锁

下一课预告:第05课深入ConfigMap与Secret——配置与密钥管理最佳实践。