本章目录(点击跳转):
- 5.1 Kubernetes 网络模型(先建立全局观)
- 5.2 集群内 DNS
- 5.3 Service 详解
- 5.3.1 基础结构
- 5.3.2 四种类型对比
- 5.3.3 完整示例:把 Web 服务暴露出去
- 5.3.4 Headless Service(无头服务)
- 5.3.5 Service 排障
- 5.4 Ingress:域名入口与路径路由
- 5.4.1 安装 Ingress 控制器(minikube)
- 5.4.2 Ingress 示例:一个域名两条路径
- 5.4.3 HTTPS 终结
- 5.5 NetworkPolicy:Pod 间的"防火墙"
- 5.6 网络排查套路
- 5.7 本章小结
本章目标:理解 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章 网络与服务,转载请注明来源!
