一、理解标签与 Service 中的 Endpoint
**核心概念:**Service 通过标签选择器(Selector)匹配后端 Pod,被匹配到的 Pod 会出现在 Service 的 Endpoints 列表中,从而被暴露。
1. 建立 Service
使用命令生成 Service 的 YAML 模板,并修改为指定选择器:
bash
kubectl create service clusterip --tcp 80:80 testservice --dry-run=client -o yaml >> testservice.yaml
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
2. 标签匹配决定 Pod 是否被暴露
**关键结论:**当 Pod 的标签与 Service 选择器上的标签不一致时,Pod 不会被暴露,Endpoints 为空。
bash
kubectl run webserver --image myapp:v1 --dry-run=client -o yaml > webserver.yml
yaml
kind: Pod
metadata:
labels:
run: webserver
name: webserver
spec:
containers:
- image: myapp:v1
name: webserver
此时 Service 的 Endpoints 为空,因为 Pod 标签是 run=webserver,而 Service 选择器是 app=webserver。
修改标签后即可被暴露:
bash
kubectl label pods webserver app=webserver
curl 10.100.62.227
# 输出:Hello MyApp | Version: v1 | Pod Name
此时 Endpoints 显示为 10.244.2.7:80。
二、利用 Service 暴露控制器(Deployment)
**核心概念:**Service 可以关联 Deployment 管理的 Pod,实现负载均衡和自动调度。
1. 生成控制器和 Service 的组合 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
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
Endpoints 显示两个后端:10.244.2.8:80,10.244.5.95:80,多次访问会轮询到不同 Pod。
三、Service 的 IPVS 模式
**核心概念:**kube-proxy 支持 IPVS 模式,相比 iptables 模式性能更高,支持更多调度算法。
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 kube-proxy-6kp78 kube-proxy-hvk7k kube-proxy-p7fdf kube-proxy-tb29j
3. 验证效果
bash
ipvsadm -Ln
# 可以看到 Service 的 VIP 和对应的后端 Pod
IPVS 模式下,每个 Service 的 VIP 会出现在 ipvsadm -Ln 输出中,并显示对应的后端 Pod 地址。
四、Service 的 ClusterIP 类型
1. ClusterIP 基本模式
**核心概念:**ClusterIP 是 Service 的默认类型,提供一个集群内部虚拟 IP(VIP),通过该 IP 可以访问后端 Pod。
bash
kubectl describe svc webservice
# IP: 10.109.112.7(VIP,访问此 IP 可进行调度)
2. 集群内部 DNS 解析
核心概念: 集群内部通过 CoreDNS 解析 Service 名称,格式为 <servicename>.<namespace>.svc.cluster.local。
bash
dig webservice.default.svc.cluster.local @10.96.0.10
# 返回 Service 的 VIP:10.109.112.7
**重要结论:**集群内部通常使用 Service 的解析名称进行通信,因为资源的 IP 会变但名字不会变。
3. ClusterIP 的 Headless 模式
核心概念: 设置 clusterIP: None 后,Service 不会被分配 VIP,而是直接把 Endpoints 的 IP 通过 DNS 暴露。
yaml
spec:
clusterIP: None
bash
kubectl describe svc webservice
# IP: None(没有 VIP)
dig webservice.default.svc.cluster.local @10.96.0.10
直接返回后端 Pod 的 IP:10.244.5.99 和 10.244.2.15
五、Service 的 NodePort 模式
**核心概念:**NodePort 模式在 ClusterIP 基础上,在每个节点上开放一个端口(30000-32767),实现集群外部访问。
1. 生成 NodePort Service
yaml
spec:
ports:
- name: webport
port: 80
protocol: TCP
targetPort: 80
nodePort: 30951
selector:
app: webcluster
type: NodePort
2. 查看 IPVS 策略
bash
ipvsadm -Ln
# 可以看到节点 IP:30951 映射到后端 Pod
3. 外部访问
bash
curl 172.25.254.100:30951/hostname.html
六、Ingress 概述
**核心概念:**Ingress 是 Kubernetes 的 API 对象,用于管理集群外部访问集群内服务的 HTTP 和 HTTPS 路由,提供基于域名、路径的转发规则。
1. 基于路径的访问
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
bash
curl www.timinglee.org/v1
# Hello MyApp | Version: v1
curl www.timinglee.org/v2
# Hello MyApp | Version: v2
2. 基于域名的访问
yaml
spec:
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
# Hello MyApp | Version: v1
curl myapp2.timinglee.org
# Hello MyApp | Version: v2
3. 基于动静分离的方式
yaml
metadata:
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/
# Hello MyApp | Version: v1
curl www.timinglee.org/static/
# Hello MyApp | Version: v2
4. 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
yaml
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
# Hello MyApp | Version: v1
5. Auth 认证
**步骤:**安装 httpd-tools 生成认证文件,创建 Secret,在 Ingress 中配置认证注解。
bash
dnf install httpd-tools -y
htpasswd -cm auth admin
kubectl create secret generic web-auth --from-file auth
yaml
metadata:
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 -uadmin:lee
# Hello MyApp | Version: v1
6. Ingress 中的网页重写
利用 app-root 定义默认发布文件:
yaml
metadata:
annotations:
nginx.ingress.kubernetes.io/app-root: /hostname.html
bash
curl -I www.timinglee.org
# 302 重定向到 /hostname.html
利用正则表达式重定向网页:
yaml
metadata:
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
# Hello MyApp | Version: v1
curl -L www.timinglee.org/lee
# Hello MyApp | Version: v2
七、金丝雀发布(Canary)
**核心概念:**金丝雀发布(灰度发布)允许将部分流量引导到新版本,验证稳定后再全量发布。
1. 基于 Header 的灰度发布
yaml
---
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
# Hello MyApp | Version: v1(默认走 v1)
curl -H "version:2" www.timinglee.org
# Hello MyApp | Version: v2(带指定 Header 走 v2)
2. 基于权重的灰度发布
yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress2
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
nginx.ingress.kubernetes.io/canary-weight-total: "100"
spec:
ingressClassName: nginx
rules:
- host: www.timinglee.org
http:
paths:
- backend:
service:
name: servicev2
port:
number: 80
path: /
pathType: Prefix
bash
# 验证脚本:100 次请求统计 v1/v2 比例
sh check_cannary.sh
# 输出示例:v1:95, v2:5(约 10% 流量到 v2)
八、总结
核心要点回顾:
- 标签与 Endpoint:Service 通过标签选择器匹配 Pod,标签不一致则 Pod 不会被暴露,Endpoints 为空;标签一致后 Pod 才会被纳入 Endpoints 并对外提供服务。
- Service 类型:ClusterIP(集群内部访问)、NodePort(节点端口外部访问)、Headless(无 VIP,DNS 直连 Pod)。
- IPVS 模式 :kube-proxy 切换为 IPVS 后性能更高,每个 Service 的 VIP 会出现在
ipvsadm -Ln中,并映射到对应后端 Pod。 - 集群内 DNS 解析 :格式为
<servicename>.<namespace>.svc.cluster.local,集群内部通信优先使用 Service 名称,因为 IP 会变而名字不会变。 - Ingress 路由:支持基于路径、基于域名、动静分离、TLS 加密、Auth 认证以及网页重写等多种转发方式,统一管理集群外部访问。
- 金丝雀发布:通过 Header 或权重将部分流量引导到新版本,验证稳定后再全量发布,降低上线风险。
**实践建议:**生产环境中建议优先使用 Deployment 管理 Pod 并结合 Service 暴露,外部流量统一通过 Ingress 按域名或路径转发;灰度发布时先以 Header 方式小范围验证,再逐步调整权重直至全量切换。