首页 » K8s » 第5章 网络与服务

第5章 网络与服务

 

本章目录(点击跳转):

本章目标:理解 K8s 网络模型(Pod 网络、集群 DNS),掌握 Service 的四种类型、Ingress 入口,以及 NetworkPolicy 网络隔离。

5.1 Kubernetes 网络模型(先建立全局观)

K8s 网络有一条黄金假设:

集群里每个 Pod 都有一个独立的 IP,所有 Pod 之间不经过 NAT 直接互通。

也就是说,Pod A 在任何节点上,都能直接用 Pod B 的 IP 访问它。这背后是 CNI 网络插件(flannel、calico 等)在干活,小白不用深究实现,记住结论即可。

在这个基础上还有两层:

外部用户
   │
   ▼
[Ingress](域名/路径 路由,通常是 Nginx Ingress 控制器)
   │
   ▼
[Service](稳定虚拟 IP,把流量分给一组 Pod)
   │
   ▼
[Pod](IP 会变,用标签被 Service 选中)
  • Pod 间:用 IP 或 DNS 名直接访问。
  • Service:给"一组 Pod"的稳定入口,Pod 换 IP 它不变。
  • Ingress:给 Service 加"域名 + 路径"路由,通常终结 HTTPS。

5.2 集群内 DNS

每个 Pod 自动获得 DNS 配置,可以直接用名字访问 Service:

<service名>.<命名空间>.svc.cluster.local

例如 default 命名空间的 hello-web,全名是 hello-web.default.svc.cluster.local,同命名空间里直接写 hello-web 就行。

动手验证:

# 用 2 章/4 章的 hello-web 或 web service
kubectl run dns-test --image=busybox:1.36 --rm -it -- sh
# 进入 shell 后:
nslookup web              # 解析 Service 名
nslookup web.default.svc.cluster.local
wget -qO- web            # 直接按名字访问!
exit

Pod 之间访问也一样:<pod名>.<命名空间>.svc.cluster.local(需要 headless service 或 StatefulSet 才有稳定 Pod DNS)。

5.3 Service 详解

5.3.1 基础结构

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web            # 选中哪些 Pod(靠标签)
  ports:
    - name: http
      port: 80          # Service 对外端口
      targetPort: 8080  # 转发到容器的哪个端口
      protocol: TCP
  type: ClusterIP       # 类型

核心就两个字段:selector(选谁)和 ports(怎么映射)。

5.3.2 四种类型对比

类型 作用 谁能访问 典型场景
ClusterIP(默认) 集群内虚拟 IP 仅集群内 微服务互相调用
NodePort 每个节点开一个端口 集群外(走节点IP:端口) 测试/简单暴露
LoadBalancer 云厂商自动建负载均衡 集群外(LB IP) 云生产环境
ExternalName 返回一个 CNAME 集群内 把外部域名引入集群

minikube 里 LoadBalancer 会显示 <pending> 或一个 a.b.c.d:port,可用 minikube service <name> --url 访问。

5.3.3 完整示例:把 Web 服务暴露出去

# web-svc.yaml(ClusterIP,集群内访问)
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80
# web-svc-nodeport.yaml(NodePort,外部可访问)
apiVersion: v1
kind: Service
metadata:
  name: web-external
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080    # 可指定 30000-32767 内的固定端口
kubectl apply -f web-svc.yaml
kubectl apply -f web-svc-nodeport.yaml
kubectl get svc

# 集群内访问(起个测试 Pod)
kubectl run curler --rm -it --image=curlimages/curl:8.5.0 -- sh -c "curl http://web; exit"

# 集群外访问(minikube)
minikube service web-external --url
# 或任意节点:curl http://<节点IP>:30080

5.3.4 Headless Service(无头服务)

clusterIP: None,不分配虚拟 IP、不做负载均衡,DNS 直接解析出所有 Pod 的 IP。

用途:

  • StatefulSet 的标配(第 4 章已见):让每个 Pod 有独立 DNS 名;
  • 客户端自己挑实例(如数据库主从切换)。
apiVersion: v1
kind: Service
metadata:
  name: web-headless
spec:
  clusterIP: None
  selector:
    app: web
  ports:
    - port: 80
kubectl apply -f /dev/stdin <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: headless-test
spec:
  containers:
    - name: c
      image: busybox:1.36
      command: ["sh","-c","nslookup web-headless; sleep 300"]
EOF
kubectl logs headless-test
# 输出:解析出多个 A 记录(每个 Pod 一个 IP)
kubectl delete pod headless-test

5.3.5 Service 排障

# 1) Service 的 endpoints 是空的?→ selector 没匹配到任何 Pod
kubectl get endpoints web
kubectl get pods --show-labels            # 对比标签

# 2) 通了但 502/超时?→ targetPort 与容器实际监听端口不一致
kubectl exec -it <pod> -- netstat -tlnp   # 或 ss -tlnp

# 3) 看 Service 详情
kubectl describe svc web

5.4 Ingress:域名入口与路径路由

没有 Ingress 时,一个域名/一个 Service 对应一个 NodePort,很原始。Ingress 让你用一个入口做"域名 + 路径"路由,还管 HTTPS。

5.4.1 安装 Ingress 控制器(minikube)

Ingress 本身只是一份"路由规则"(资源),真正执行路由的是 Ingress 控制器(一个运行在集群里的组件,最常见的是 ingress-nginx)。

# minikube 一键启用
minikube addons enable ingress
kubectl get pods -n ingress
# 看到 ingress-nginx-controller-* 的 Pod 就 OK

# 查看入口 IP
kubectl get svc -n ingress | grep ingress
# minikube 下通常有 LoadBalancer 地址
minikube ip

5.4.2 Ingress 示例:一个域名两条路径

假设两个 Service:web(80)和 api(80)。

# ingress-demo.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: demo-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
    - host: myapp.local          # 域名(演示用,需配 hosts)
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api
                port:
                  number: 80
# 准备两个后端(演示用两个不同 nginx)
kubectl create deployment web --image=nginx:1.27
kubectl create deployment api --image=nginx:1.27
kubectl expose deployment web --name=web --port=80
kubectl expose deployment api --name=api --port=80

kubectl apply -f ingress-demo.yaml
kubectl get ingress

# 测试(把 myapp.local 指到集群入口 IP):
# Windows: 编辑 C:\Windows\System32\drivers\etc\hosts 加一行
#   <入口IP>  myapp.local
curl http://myapp.local/        # → web
curl http://myapp.local/api/    # → api

5.4.3 HTTPS 终结

spec:
  tls:
    - hosts:
        - myapp.local
      secretName: myapp-tls      # 存放证书的 Secret(kubernetes.io/tls 类型)
  rules:
    - host: myapp.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

证书 Secret 创建方式:

# 自签证书(演示)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout tls.key -out tls.crt -subj "/CN=myapp.local"

kubectl create secret tls myapp-tls --cert=tls.crt --key=tls.key

生产环境用 Let's Encrypt(cert-manager 自动续期),这里不展开。

5.5 NetworkPolicy:Pod 间的"防火墙"

默认情况下集群内所有 Pod 互通。NetworkPolicy 声明"只允许谁访问谁",一旦对某 Pod 施加了策略,未声明的流量默认全部拒绝(白名单模式)。

# netpol-demo.yaml
# 只允许 default 命名空间中带 label "role=frontend" 的 Pod
# 访问带 label "app=web" 的 Pod 的 80 端口
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-allow-frontend
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              role: frontend
      ports:
        - protocol: TCP
          port: 80
kubectl apply -f netpol-demo.yaml

# 用一个普通 Pod 访问 web → 被拒(超时)
kubectl run attacker --rm -it --image=busybox:1.36 -- sh -c "wget -qO- web; echo rc=\$?"

# 加 frontend 标签的 Pod 访问 → 成功
kubectl run frontend-test --rm -it --labels=role=frontend --image=busybox:1.36 \
  -- sh -c "wget -qO- web; echo rc=\$?"

kubectl delete -f netpol-demo.yaml

注意:NetworkPolicy 需要 CNI 支持(calico/cilium 支持;flannel 默认不支持策略,仅支持连通性)。minikube 默认 flannel,策略测试可能不生效,生产用 calico/cilium 才完整。

5.6 网络排查套路

# 1) Service 有没有 endpoints?
kubectl get endpoints <svc>

# 2) DNS 解析对不对?
kubectl run tmp --rm -it --image=busybox:1.36 -- nslookup <svc>

# 3) 从消费方 Pod 里 curl/nc 目标
kubectl run tmp --rm -it --image=curlimages/curl:8.5.0 -- curl -v http://<svc>

# 4) 是不是 NetworkPolicy 拦了?
kubectl get networkpolicy -A

# 5) NodePort 从外部不通?
#    - 安全组/防火墙放通 30000-32767
#    - 确认 svc 的 SELECTORS 匹配到 Pod

5.7 本章小结

  • K8s 网络铁律:每个 Pod 有独立 IP,全网互通(除非 NetworkPolicy 拦)。
  • Service = selector + ports;四种类型 ClusterIP / NodePort / LoadBalancer / ExternalName;headless 给 StatefulSet 用。
  • 集群内访问一律用 DNS 名,不写 IP。
  • Ingress = 域名+路径路由+HTTPS,需要 Ingress 控制器。
  • NetworkPolicy 是白名单模式,用了就"默认拒绝"。

下一章:存储——让数据活得比 Pod 久。

原文链接:第5章 网络与服务,转载请注明来源!

赞 0