Service 与网络


Part 1:Service —— 稳定的访问入口

概念引入

还记得港口的比喻吗?Pod 是货船,但货船会来来去去——有的卸完货走了,有的坏了被替换。如果客户每次都要找到具体的某艘船,那就太麻烦了。

Service 就是港口的"接待处"。 不管后面的船怎么换,接待处的地址永远不变。客户只需要找接待处,接待处自动把请求转发给可用的船。

固定地址

Pod 挂了

客户端

Service
接待处

Pod 1

Pod 2

Pod 3

新 Pod 4

Service 自动
把流量切到
存活的 Pod

原理讲解

为什么需要 Service?

Pod 有两个问题:

  1. IP 会变:Pod 重启后 IP 地址变化
  2. 数量会变:扩缩容时 Pod 数量不固定

Service 解决了这两个问题:提供一个稳定的访问入口,自动管理后端 Pod 的流量分发。

Service 的类型
类型 谁能访问 用途
ClusterIP 仅集群内部 微服务之间互相调用
NodePort 集群外部 通过节点端口暴露服务
LoadBalancer 公网 云厂商提供的负载均衡器
ExternalName 映射外部服务 把集群内名称映射到外部域名

外部访问

节点端口 30080

公网 IP

NodePort Service

Pod

LoadBalancer

集群内部

ClusterIP Service

Pod

Pod

标签选择器

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 网络

第二层:Service

第三层:Ingress

Pod IP 直连

Ingress - HTTP 路由

Service - 稳定的访问入口

Pod A

Pod B

Pod C

外部用户

  • Pod 网络:每个 Pod 有独立 IP,Pod 之间可以直接通信
  • Service:提供稳定的访问入口(前面讲过)
  • Ingress:HTTP 层的智能路由(根据域名/路径转发到不同 Service)

原理讲解

Ingress 是什么?

Service 的 NodePort 方式虽然能从外部访问,但有缺点:端口范围有限(30000-32767)、不支持域名路由。

Ingress 就是 K8s 的"HTTP 路由器",可以根据域名和路径把请求转发到不同的 Service:

api.example.com

www.example.com

/admin

用户请求

Ingress

api-svc:80

web-svc:80

admin-svc:80

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

自检问题

  1. ClusterIP 和 NodePort 的区别是什么?
查看答案 ClusterIP 只能在集群内部访问,NodePort 通过在每个节点上开一个端口(30000-32767)允许外部访问。
  1. Service 和 Ingress 的区别是什么?
查看答案 Service 是 L4(TCP/UDP)层的负载均衡,提供稳定的 IP/端口。Ingress 是 L7(HTTP)层的路由器,可以根据域名和 URL 路径把请求转发到不同的 Service。
  1. 为什么需要 Ingress Controller?
查看答案 Ingress 资源只是一组路由规则,本身不执行任何操作。Ingress Controller(如 Nginx Ingress)是实际处理流量的组件,它读取 Ingress 规则并配置自己的转发逻辑。
  1. Pod A 怎么通过名字找到 Service B?
查看答案 通过 K8s 内置的 CoreDNS。每个 Service 自动注册域名(如 `my-svc.default.svc.cluster.local`),同 namespace 下可直接用 Service 名字访问。

下一步

应用能访问了,但配置信息(数据库地址、API 密钥等)怎么管理?扩缩容怎么自动化?

04. 配置与扩缩


📚 本文来自 K8s Guide —— 开源免费的 Kubernetes 中文学习指南

如果对你有帮助,欢迎 Star! github.com/callmebg/k8s-guide

Logo

AtomGit AI 社区提供模型库、数据集、Agent、Token等资源

更多推荐