03-Service 与网络
Service 与网络
Part 1:Service —— 稳定的访问入口
概念引入
还记得港口的比喻吗?Pod 是货船,但货船会来来去去——有的卸完货走了,有的坏了被替换。如果客户每次都要找到具体的某艘船,那就太麻烦了。
Service 就是港口的"接待处"。 不管后面的船怎么换,接待处的地址永远不变。客户只需要找接待处,接待处自动把请求转发给可用的船。
原理讲解
为什么需要 Service?
Pod 有两个问题:
- IP 会变:Pod 重启后 IP 地址变化
- 数量会变:扩缩容时 Pod 数量不固定
Service 解决了这两个问题:提供一个稳定的访问入口,自动管理后端 Pod 的流量分发。
Service 的类型
| 类型 | 谁能访问 | 用途 |
|---|---|---|
| ClusterIP | 仅集群内部 | 微服务之间互相调用 |
| NodePort | 集群外部 | 通过节点端口暴露服务 |
| LoadBalancer | 公网 | 云厂商提供的负载均衡器 |
| ExternalName | 映射外部服务 | 把集群内名称映射到外部域名 |
标签选择器
Service 怎么知道哪些 Pod 是它的后端?和 ReplicaSet 一样,通过标签选择器:
selector:
app: nginx # 匹配所有 labels 里有 app: nginx 的 Pod
DNS 服务发现
K8s 内置了 DNS(CoreDNS),每个 Service 自动获得一个域名:
<service-name>.<namespace>.svc.cluster.local
比如名为 nginx-svc 的 Service 在同 namespace 下可以直接用 nginx-svc 访问。
Service YAML 结构
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
type: ClusterIP # Service 类型
selector:
app: nginx # 匹配哪些 Pod
ports:
- port: 80 # Service 暴露的端口
targetPort: 80 # Pod 上的目标端口
动手实验
步骤 1:创建 Deployment
kubectl create deployment nginx --image=nginx:1.27 --replicas=3
步骤 2:创建 ClusterIP Service
cat > nginx-svc.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
type: ClusterIP
selector:
app: nginx
ports:
- port: 80
targetPort: 80
EOF
kubectl apply -f nginx-svc.yaml
步骤 3:查看 Service
kubectl get services
预期输出:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 (none) 443/TCP 10m
nginx-svc ClusterIP 10.96.45.123 (none) 80/TCP 5s
步骤 4:测试集群内访问
启动一个临时 Pod 来访问 Service:
kubectl run curl-test --image=curlimages/curl --rm -it -- curl nginx-svc
预期输出:Nginx 的 HTML 页面内容。
步骤 5:创建 NodePort Service
cat > nginx-nodeport.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
targetPort: 80
nodePort: 30080
EOF
kubectl apply -f nginx-nodeport.yaml
步骤 6:查看 NodePort
kubectl get service nginx-nodeport
预期输出:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-nodeport NodePort 10.96.78.45 (none) 80:30080/TCP 5s
步骤 7:从外部访问
如果你在创建 Kind 集群时使用了包含 extraPortMappings 的配置,NodePort 30080 已映射到宿主机,可以直接访问:
curl http://localhost:30080
💡 如果没有配置 extraPortMappings,Kind 节点的 IP 是 Docker 内部网络,宿主机无法直接访问。此时可以用
kubectl port-forward service/nginx-nodeport 8080:80临时转发,但这不是 NodePort 的工作方式——port-forward 是 kubectl 的独立机制,与 NodePort 无关。
步骤 8:清理
kubectl delete -f nginx-svc.yaml -f nginx-nodeport.yaml
kubectl delete deployment nginx
rm nginx-svc.yaml nginx-nodeport.yaml
Part 2:网络基础 —— 从 Pod 到 Ingress
概念引入
你的 K8s 集群里跑着好几个微服务:前端、后端、数据库。它们之间怎么通信?外部用户怎么访问你的网站?
K8s 提供了三层网络能力:
- Pod 网络:每个 Pod 有独立 IP,Pod 之间可以直接通信
- Service:提供稳定的访问入口(前面讲过)
- Ingress:HTTP 层的智能路由(根据域名/路径转发到不同 Service)
原理讲解
Ingress 是什么?
Service 的 NodePort 方式虽然能从外部访问,但有缺点:端口范围有限(30000-32767)、不支持域名路由。
Ingress 就是 K8s 的"HTTP 路由器",可以根据域名和路径把请求转发到不同的 Service:
Ingress Controller
Ingress 资源本身只是一组规则,你需要一个 Ingress Controller 来执行这些规则:
| Controller | 特点 |
|---|---|
| Nginx Ingress | 最流行,功能丰富 |
| Traefik | 自动发现,配置简单 |
| Kong | API 网关,插件丰富 |
Kind 集群默认不带 Ingress Controller,需要手动安装。
DNS 服务发现
K8s 集群内置 CoreDNS,每个 Service 自动获得域名:
# 同 namespace 访问
nginx-svc
# 跨 namespace 访问
nginx-svc.production
# 完整域名
nginx-svc.production.svc.cluster.local
Pod 之间的网络
K8s 保证:
- 每个 Pod 有唯一 IP
- 任意两个 Pod 可以直接通信(不需要 NAT)
- 同一节点的 Pod 和不同节点的 Pod 都能互通
这个"扁平网络"由 CNI 插件实现(Kind 默认用 kindnet)。
动手实验
⚠️ 前置条件:确保你的 Kind 集群使用了包含 80/443 端口映射的配置。否则
curl localhost将无法连通。
步骤 1:安装 Ingress Controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
# 等待就绪
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/component=controller \
-n ingress-nginx --timeout=90s
⚠️ 重要:Kind 集群只在 control-plane 节点上做了 80/443 端口映射。必须确保 Ingress Controller 运行在 control-plane 节点上,否则
curl localhost无法连通。
# 将 Ingress Controller 固定到 control-plane 节点
kubectl label node k8s-guide-control-plane ingress-ready=true
kubectl patch deployment -n ingress-nginx ingress-nginx-controller \
-p '{"spec":{"template":{"spec":{"nodeSelector":{"ingress-ready":"true"}}}}}'
# 等待 Pod 重新调度完成
kubectl rollout status deployment -n ingress-nginx ingress-nginx-controller --timeout=120s
步骤 2:创建两个应用
# 应用 A(web)
kubectl create deployment web --image=nginx:1.27
kubectl expose deployment web --port=80
# 应用 B(api)
kubectl create deployment api --image=hashicorp/http-echo -- /http-echo -text="Hello from API"
kubectl expose deployment api --port=5678
步骤 3:测试 DNS 解析
kubectl run dns-test --image=busybox --rm -it -- nslookup web
预期输出(包含 web Service 的 ClusterIP):
Name: web.default.svc.cluster.local
Address 1: 10.96.xx.xx
步骤 4:创建 Ingress
cat > my-ingress.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 5678
EOF
kubectl apply -f my-ingress.yaml
步骤 5:通过 Ingress 访问
# 访问根路径 -> web(Nginx 欢迎页)
curl localhost
# 访问 /api -> api
curl localhost/api
预期输出:/api 返回 “Hello from API”
步骤 6:清理
kubectl delete ingress my-ingress
kubectl delete deployment web api
kubectl delete service web api
rm my-ingress.yaml
自检问题
- ClusterIP 和 NodePort 的区别是什么?
- Service 和 Ingress 的区别是什么?
- 为什么需要 Ingress Controller?
- Pod A 怎么通过名字找到 Service B?
下一步
应用能访问了,但配置信息(数据库地址、API 密钥等)怎么管理?扩缩容怎么自动化?
📚 本文来自 K8s Guide —— 开源免费的 Kubernetes 中文学习指南
- 🗺️ 初学者轨道 + 面试轨道,从零基础到拿 Offer 一站式覆盖
- 🧪 每篇文章配套 Kind 实验脚本,本地一键运行
- 🔗 本文源码:docs/beginner/06-service.md / docs/beginner/09-networking-basics.md
⭐ 如果对你有帮助,欢迎 Star! github.com/callmebg/k8s-guide
更多推荐



所有评论(0)