Kubernetes Service 深度实践:从 ClusterIP 到 Ingress 七层代理

实验环境:1 Master + 2 Worker + Harbor 私有仓库,Kubernetes 1.28,Flannel 网络


一、理解标签与 Service 中的 Endpoint

1. 建立一个 Service

bash 复制代码
kubectl create service clusterip --tcp 80:80 testservice --dry-run=client -o yaml >> testservice.yaml

编辑 testservice.yaml,指定 selector 为 app=webserver

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  labels:
    app: testservice
  name: testservice
spec:
  ports:
  - name: webport
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: webserver
  type: ClusterIP

应用并监控:

bash 复制代码
kubectl apply -f testservice.yaml
watch -n 1 "kubectl describe svc testservice ; kubectl get pods --show-labels"

此时 Service 已创建,但 Endpoints 为空------因为还没有匹配 selector 的 Pod。


图 1:建立 Service 后 Endpoints 为空

2. 建立 Pod 并测试暴露

标签不匹配时,Pod 不会被暴露:

bash 复制代码
kubectl run webserver --image myapp:v1 --dry-run=client -o yaml > webserver.yml

Pod 标签为 run=webserver,与 Service 的 app=webserver 不匹配,Endpoints 仍为空。

修改标签后,Endpoint 自动绑定:

bash 复制代码
kubectl label pods webserver app=webserver
curl 10.100.62.227

输出 Hello MyApp | Version: v1,说明流量已转发到 Pod。此时 Endpoints 显示为 10.244.2.7:80


二、利用 Service 暴露控制器

1. 生成 Service + Deployment 组合 YAML

bash 复制代码
kubectl create service clusterip --tcp 80:80 webservice --dry-run=client -o yaml >> webservice.yaml
kubectl create deployment webcluster --image myapp:v1 --dry-run=client -o yaml >> webservice.yaml

编辑组合文件,确保 Deployment 的 matchLabels 与 Service 的 selector 一致:

yaml 复制代码
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: webservice
  name: webservice
spec:
  ports:
  - name: webport
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: webcluster
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: webcluster
  name: webcluster
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webcluster
  template:
    metadata:
      labels:
        app: webcluster
    spec:
      containers:
      - image: myapp:v1
        name: myapp

2. 应用与验证负载均衡

bash 复制代码
kubectl apply -f webservice.yaml
curl 10.98.31.55/hostname.html

多次请求返回不同 Pod 名称,证明 Service 已自动实现后端轮询调度。


图 2:Service 暴露 Deployment 并实现轮询调度


三、Service 的 IPVS 模式

1. 安装 ipvsadm

bash 复制代码
for name in 100 10 20 200; do
  ssh -l root 172.25.254.$name "dnf install ipvsadm -y"
done

2. 切换 kube-proxy 为 IPVS 模式

bash 复制代码
kubectl -n kube-system edit cm kube-proxy
# 修改 mode: "ipvs"

kubectl -n kube-system delete pods -l k8s-app=kube-proxy

3. 验证 IPVS 规则

bash 复制代码
ipvsadm -Ln

输出中可见 10.98.31.55:80 对应两条后端 Pod 记录,调度算法为 rr(轮询)。


图 3:IPVS 模式下的 Service 负载均衡规则


四、Service 的 ClusterIP 类型

1. 标准 ClusterIP

默认类型,分配集群内部 VIP,仅集群内可访问:

bash 复制代码
kubectl describe svc webservice
# IP: 10.109.112.7

2. 集群内部 DNS 解析

bash 复制代码
dig webservice.default.svc.cluster.local @10.96.0.10

返回 A 记录指向 ClusterIP。集群内部通信应使用 Service 名称而非 IP,因为 IP 可能变化。


图 4:ClusterIP Service 通过 CoreDNS 解析到 VIP

3. Headless 模式

设置 clusterIP: None,Service 不再分配 VIP,DNS 直接返回所有后端 Pod IP:

yaml 复制代码
spec:
  clusterIP: None
bash 复制代码
dig webservice.default.svc.cluster.local @10.96.0.10

返回两个 A 记录,分别为 10.244.5.9910.244.2.15。适用于需要直接访问 Pod 的场景(如 StatefulSet)。


图 5:Headless Service 直接返回后端 Pod IP


五、Service 的 NodePort 类型

1. 生成 NodePort

yaml 复制代码
spec:
  ports:
  - name: webport
    port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30951
  selector:
    app: webcluster
  type: NodePort

不指定 nodePort 时,系统从 30000-32767 随机分配。

2. 查看 IPVS 策略

bash 复制代码
ipvsadm -Ln

可见 172.25.254.100:30951172.17.0.1:30951 均映射到后端 Pod。

3. 访问验证

bash 复制代码
curl 172.25.254.100:30951/hostname.html


图 6:NodePort 通过节点端口暴露服务

4. 端口范围破限

如需使用 32767 以上端口,修改 kube-apiserver:

bash 复制代码
vim /etc/kubernetes/manifests/kube-apiserver.yaml
# 添加 --service-node-port-range=30000-50000

重建 kube-apiserver Pod 后,即可分配 nodePort: 44444


图 7:修改 apiserver 端口范围后分配 44444 端口


六、LoadBalancer 类型

1. 建立 Service

yaml 复制代码
type: LoadBalancer
bash 复制代码
kubectl get svc
# webservice   LoadBalancer   10.111.5.103   <pending>   80:31426/TCP

EXTERNAL-IP<pending>,需要外部负载均衡器支持。裸金属集群使用 MetalLB 实现。

2. 部署 MetalLB

上传镜像到 Harbor:

bash 复制代码
docker load -i metallb-v0.16.1.tar
docker tag quay.io/metallb/controller:v0.16.1 reg.timinglee.org/metallb/controller:v0.16.1
docker tag quay.io/metallb/speaker:v0.16.1 reg.timinglee.org/metallb/speaker:v0.16.1
docker push reg.timinglee.org/metallb/controller:v0.16.1
docker push reg.timinglee.org/metallb/speaker:v0.16.1

配置 kube-proxy:

bash 复制代码
kubectl edit configmap -n kube-system kube-proxy
# mode: "ipvs"
# ipvs:
#   strictARP: true
kubectl -n kube-system delete pods -l k8s-app=kube-proxy

配置 MetalLB IP 池:

yaml 复制代码
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 172.25.254.50-172.25.254.99
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system
spec:
  ipAddressPools:
  - first-pool

3. 验证

bash 复制代码
kubectl get svc
# webservice   LoadBalancer   10.111.5.103   172.25.254.50   80:31426/TCP

EXTERNAL-IP 已分配为 172.25.254.50


图 8:MetalLB 为 LoadBalancer Service 分配外部 IP


七、ExternalName

将 Service 映射到外部域名:

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: extrnalname
spec:
  type: ExternalName
  externalName: www.timinglee.org
bash 复制代码
kubectl get svc extrnalname
# extrnalname   ExternalName   <none>   www.timinglee.org   <none>

无 ClusterIP,DNS 查询直接返回 CNAME 记录。


图 9:ExternalName Service 映射到外部域名


八、Ingress 七层代理

1. 部署 Ingress-Nginx

下载官方部署文件:

bash 复制代码
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/baremetal/deploy.yaml

准备镜像并推送到 Harbor:

bash 复制代码
docker load -i ingress-v1.15.1.tar
docker tag registry.k8s.io/ingress-nginx/controller:v1.15.1 reg.timinglee.org/ingress-nginx/controller:v1.15.1
docker tag registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.6.9 reg.timinglee.org/ingress-nginx/kube-webhook-certgen:v1.6.9
docker push reg.timinglee.org/ingress-nginx/controller:v1.15.1
docker push reg.timinglee.org/ingress-nginx/kube-webhook-certgen:v1.6.9

修改 deploy.yaml:

  • 替换镜像地址为 Harbor 私有仓库
  • 将 Service 类型改为 LoadBalancer(配合 MetalLB)
bash 复制代码
kubectl apply -f deploy.yaml
kubectl -n ingress-nginx get svc

ingress-nginx-controller 获得 EXTERNAL-IP 172.25.254.50


图 10:Ingress-Nginx Controller 部署完成并获得外部 IP

2. 建立 Ingress 代理的微服务

部署两个版本的业务:

bash 复制代码
# myapp:v1
kubectl create deployment myappv1 --image myapp:v1 --replicas 2 --dry-run=client -o yaml > myappv1.yaml
kubectl create service clusterip servicev1 --tcp 80:80 --dry-run=client -o yaml >> myappv1.yaml

# myapp:v2
kubectl create deployment myappv2 --image myapp:v2 --replicas 2 --dry-run=client -o yaml > myappv2.yaml
kubectl create service clusterip servicev2 --tcp 80:80 --dry-run=client -o yaml >> myappv2.yaml

验证两个 Service 可正常访问:

bash 复制代码
curl 10.97.35.20    # Hello MyApp | Version: v1
curl 10.100.116.247 # Hello MyApp | Version: v2


图 11:部署 v1/v2 两个版本的微服务

3. 基于路径的访问

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress1
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: www.timinglee.org
    http:
      paths:
      - backend:
          service:
            name: servicev1
            port:
              number: 80
        path: /v1
        pathType: Prefix
      - backend:
          service:
            name: servicev2
            port:
              number: 80
        path: /v2
        pathType: Prefix

客户端配置 /etc/hosts 后验证:

bash 复制代码
curl www.timinglee.org/v1  # v1
curl www.timinglee.org/v2  # v2

4. 基于域名的访问

yaml 复制代码
rules:
- host: myapp1.timinglee.org
  http:
    paths:
    - backend:
        service:
          name: servicev1
          port:
            number: 80
      path: /
      pathType: Prefix
- host: myapp2.timinglee.org
  http:
    paths:
    - backend:
        service:
          name: servicev2
          port:
            number: 80
      path: /
      pathType: Prefix
bash 复制代码
curl myapp1.timinglee.org  # v1
curl myapp2.timinglee.org  # v2


图 12:不同域名路由到不同后端服务

5. 动静分离

利用正则表达式实现路径重写:

yaml 复制代码
annotations:
  nginx.ingress.kubernetes.io/use-regex: "true"
  nginx.ingress.kubernetes.io/rewrite-target: /index.html
spec:
  rules:
  - host: www.timinglee.org
    http:
      paths:
      - backend:
          service:
            name: servicev1
            port:
              number: 80
        path: /php
        pathType: ImplementationSpecific
      - backend:
          service:
            name: servicev2
            port:
              number: 80
        path: /static
        pathType: ImplementationSpecific
bash 复制代码
curl www.timinglee.org/php/     # v1
curl www.timinglee.org/static/  # v2


图 13:基于正则的路径重写实现动静分离

6. TLS 加密访问

生成自签证书:

bash 复制代码
openssl req -newkey rsa:2048 -nodes -keyout tls.key -x509 -days 365 \
  -subj "/CN=nginxsvc/O=nginxsvc" -out tls.crt
kubectl create secret tls web-tls-secret --key tls.key --cert tls.crt

配置 Ingress:

yaml 复制代码
annotations:
  nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - myapp-tls.timinglee.org
    secretName: web-tls-secret
  rules:
  - host: myapp-tls.timinglee.org
    http:
      paths:
      - backend:
          service:
            name: servicev1
            port:
              number: 80
        path: /
        pathType: Prefix
bash 复制代码
curl -k https://myapp-tls.timinglee.org


图 14:Ingress 配置 TLS 加密访问

7. Auth 认证

生成认证文件:

bash 复制代码
dnf install httpd-tools -y
htpasswd -cm auth admin
kubectl create secret generic web-auth --from-file auth

配置 Ingress:

yaml 复制代码
annotations:
  nginx.ingress.kubernetes.io/auth-type: basic
  nginx.ingress.kubernetes.io/auth-secret: web-auth
  nginx.ingress.kubernetes.io/auth-realm: "Please input username and password"

验证:

bash 复制代码
curl www.timinglee.org/v1              # 401 Authorization Required
curl www.timinglee.org/v1 -u admin:lee # v1


图 15:Ingress Basic Auth 认证拦截与放行

8. 网页重写

默认发布文件:

yaml 复制代码
annotations:
  nginx.ingress.kubernetes.io/app-root: /hostname.html

访问根路径自动 302 跳转到 /hostname.html

正则表达式重定向:

yaml 复制代码
annotations:
  nginx.ingress.kubernetes.io/rewrite-target: /$2
  nginx.ingress.kubernetes.io/use-regex: "true"
spec:
  rules:
  - host: www.timinglee.org
    http:
      paths:
      - backend:
          service:
            name: servicev1
            port:
              number: 80
        path: /
        pathType: Prefix
      - backend:
          service:
            name: servicev2
            port:
              number: 80
        path: /lee(/*.*/*|$)(.*)
        pathType: ImplementationSpecific
bash 复制代码
curl -L www.timinglee.org        # v1
curl -L www.timinglee.org/lee    # v2
curl -L www.timinglee.org/lee/a  # v2


图 16:Ingress 路径重写与 302 跳转


九、金丝雀发布(Canary)

1. 基于 Header 的灰度发布

yaml 复制代码
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress1
spec:
  ingressClassName: nginx
  rules:
  - host: www.timinglee.org
    http:
      paths:
      - backend:
          service:
            name: servicev1
            port:
              number: 80
        path: /
        pathType: Prefix
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress2
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-header: "version"
    nginx.ingress.kubernetes.io/canary-by-header-value: "2"
spec:
  ingressClassName: nginx
  rules:
  - host: www.timinglee.org
    http:
      paths:
      - backend:
          service:
            name: servicev2
            port:
              number: 80
        path: /
        pathType: Prefix
bash 复制代码
curl www.timinglee.org              # v1
curl -H "version:2" www.timinglee.org  # v2

2. 基于权重的灰度发布

yaml 复制代码
annotations:
  nginx.ingress.kubernetes.io/canary: "true"
  nginx.ingress.kubernetes.io/canary-weight: "10"
  nginx.ingress.kubernetes.io/canary-weight-total: "100"

100 次请求统计:v1 约 95 次,v2 约 5 次,符合 10% 权重分配。


图 17:基于权重的金丝雀发布流量分配


总结

类型 适用场景 特点
ClusterIP 集群内部通信 默认类型,分配 VIP
Headless 直接访问 Pod DNS 返回 Pod IP 列表
NodePort 开发测试/简单暴露 每个节点开放端口,范围 30000-32767
LoadBalancer 生产环境公网暴露 需云厂商 LB 或 MetalLB
ExternalName 外部服务映射 DNS CNAME 转发
Ingress 七层路由/域名/HTTPS 基于 Nginx,支持路径、域名、TLS、认证、灰度

Service 是 Kubernetes 网络的核心抽象,理解其底层实现(iptables/IPVS)和各种类型的工作机制,是排查集群网络问题和设计高可用架构的基础。

相关推荐
IT大白鼠1 小时前
Kubernetes 持久化存储:PV、PVC、StorageClass 存储体系
云原生·容器·kubernetes
青禾8372 小时前
Docker 从入门到实践:镜像、容器、数据卷与 Docker Compose
运维·docker·容器
GGG7662 小时前
K8s1.28 全栈落地|Jenkins+GitLab+Harbor DevOps 流水线封神实战
kubernetes·gitlab·jenkins
wdfk_prog2 小时前
Docker 开发环境到底要交付什么:镜像、源码与运行脚本
运维·docker·容器
一技安身3 小时前
【Docker】Docker 和 docker‑compose 的核心区别 + 为什么要单独装 compose
docker·容器·eureka
java_logo3 小时前
Docker 部署 SRS:轻松搭建实时音视频流媒体平台
docker·容器·webrtc·实时音视频·rtmp·http-flv·轩辕镜像
名字还没想好☜12 小时前
用 systemd 托管后台服务:Restart 策略、日志接管、资源限制与开机自启全踩一遍
运维·docker·kubernetes
动力 continue13 小时前
docker容器大舞台,创建镜像,构造类,基础速通
docker·容器
野熊佩骑17 小时前
Kubernetes实战系列文章(三) 之 K8S运维常用命令
linux·运维·docker·微服务·云原生·容器·kubernetes