文章目录
一、Service四层代理概念、原理
1、Service四层代理概念
在K8S中Pod是有生命周期的,如果重启Pod如果Pod重启它的IP很有可能会发生变化,如果我们程序中指定Pod地址是写死的,Pod每升级一次,刚升级的Pod,集群中其他关联此Pod的服务就找不到升级的Pod。
为了解决上面问题,在K8S集群中定义了Service资源对象(简写svc),Service的IP地址是不会随便改动的,相当于定义了一个服务的入口,客户端或者集群中其他组件通过连接Service,Service代理到后端相应的服务实例,Service是一组Pod的集合,这组Pod能够被Service访问到,通常是通过标签关联实现的。
小结:
1、Pod IP地址经常发生变化,Service代理Pod,我们客户端访问,只需要访问Service,Service代理到后端Pod。
2、通过Pod IP在K8S集群之外是无法访问到的,所以需要创建Service,这个Service可以在K8s集群外部访问。
2、Service工作原理
-
k8s在创建Service时,会根据标签选择器selector(lable selector)来查找Pod,据此创建与Service同名的endpoint对象。
-
当Pod 地址发生变化时,endpoint也会随之发生变化,service接收前端client请求的时候,就会通过endpoint,找到转发到哪个Pod进行访问的地址。(至于转发到哪个节点的Pod,由负载均衡kube-proxy决定)
小结:
具体来说,当一个Pod被创建并加入到Service中时,Kubernetes会为该Pod创建一个Endpoint对象,Endpoint对象包含了Pod的IP地址和端口号。当请求到达Service的Cluster IP地址时,ipvs(iptables)规则会将请求转发到对应的Endpoint上,从而实现了四层代理的功能。
3、Service原理解读
-
Service是一个固定接入层,客户端通过访问ServiceIP+端口,从而访问到后端关联的Pod资源,Service的工作需要依赖于K8S集群中的DNS服务,不同版本的K8S集群,使用的DNS服务也是不同的,1.11之前版本使用kubeDNS,较新版本使用coreDNS。
-
Service的域名解析需要依赖于DNS服务,而DNS服务需要依赖于网络插件(flannel、calico)等,因此在部署K8S集群后也需要部署网络插件。
-
K8s节点上kube-proxy这个组件将始终监视着apiserver中有关service资源的变动信息,需要跟master之上的apiserver交互,随时连接到apiserver上获取任何一个与service资源相关的资源变动状态,这种是通过kubernetes中固有的一种请求方法watch(监视)来实现,一旦有service资源的内容发生变动(如创建,删除),kube-proxy都会将它转化成当前节点之上的能够实现service资源调度,把我们请求调度到后端特定的pod资源之上的规则,这个规则可能是iptables,也可能是ipvs,取决于service的实现方式。
4、Service四种类型
- ClusterIP:默认类型,只能在集群内部访问,通过ClusterIP暴露服务,可以在集群内部的其他Pod中访问该服务。
- NodePort:在ClusterIP的基础上,通过NodePort将服务暴露到集群外部,可以通过NodeIP:NodePort的方式访问服务。
- ExternalName:将服务映射到集群外部的DNS记录,可以通过服务名称直接访问外部服务,应用在夸名称空间访问。
二、Service四层代理三种类型案例
1、创建ClusterIP类型Service
默认类型,只能在集群内部访问,通过ClusterIP暴露服务,可以在集群内部的其他Pod中访问该服务。
cat clusterip-deploy.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-cluster
namespace: default
labels:
type: web-cluster
env: uat
spec:
selector:
matchLabels:
type: web-cluster
env: uat
replicas: 3
template:
metadata:
namespace: default
labels:
type: web-cluster
env: uat
spec:
containers:
- name: web-cluster
image: nginx
imagePullPolicy: IfNotPresent
startupProbe:
tcpSocket:
port: 80
readinessProbe:
httpGet:
port: 80
path: "index.html"
livenessProbe:
httpGet:
port: 80
path: "index.html"
service资源清单如下:
cat clusterip-svc.yaml
---
apiVersion: v1
kind: Service
metadata:
name: web-clusterip
namespace: default
labels:
env: uat
spec:
type: ClusterIP # ClusterIP类型
ports:
- port: 80 # SVC端口
protocol: TCP # TCP协议
targetPort: 80 # Pod暴露端口
selector: # 关联具有以下标签的Pod
env: uat
type: web-cluster
执行YAML文件 && 查看资源
kubectl apply -f clusterip-deploy.yaml -f clusterip-svc.yaml
kubectl get pods,svc
查看Service Endpoints
是否关联上Pod
kubectl describe svc web-clusterip
获取Service IP地址,通过Service代理访问后端Pod
kubectl get svc web-clusterip|awk NR==2|awk '{print $3}'
10.103.211.187
curl 10.103.211.187
2、创建NodePort类型Service
在ClusterIP的基础上,通过NodePort将服务暴露到集群外部,可以通过NodeIP:NodePort的方式访问服务。
创建NodePort类型svc关联标签具有 app=demo-nginx
Deployment资源清单如下:
cat nodeport-deploy.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-nodeport
namespace: default
labels:
app: web-nodeport
env: uat
spec:
selector:
matchLabels:
app: web-nodeport
env: uat
replicas: 3
template:
metadata:
namespace: default
labels:
app: web-nodeport
env: uat
spec:
containers:
- name: uat-nginx
image: nginx
imagePullPolicy: IfNotPresent
startupProbe:
tcpSocket:
port: 80
readinessProbe:
httpGet:
port: 80
path: "index.html"
livenessProbe:
httpGet:
port: 80
path: "index.html"
Service资源清单如下:
cat nodeport-svc.yaml
---
apiVersion: v1
kind: Service
metadata:
name: nodeport-svc
namespace: default
labels:
env: uat
spec:
type: NodePort
ports:
- port: 80 # svc的端口,这个是k8s集群内部服务可访问的端口
protocol: TCP # 协议
targetPort: 80 # Pod上端口
nodePort: 30303 # Node节点暴露端口
selector: # 关联具有app=web-nginx && env=uat 的Pod
app: web-nodeport
env: uat
执行YAML清单 && 查看状态
kubectl apply -f nodeport-deploy.yaml
kubectl apply -f nodeport-svc.yaml
kubectl get pods,svc
查看Service的Endpoints是否关联上Pod
kubectl describe svc nodeport-svc|grep Endpoints
通过浏览器访问NodeIP:30303
3、创建ExternalName类型Service
将服务映射到集群外部的DNS记录,可以通过服务名称直接访问外部服务。
应用场景:跨名称空间关联,ExternalName可以理解成软链接SVC资源
需求:default名称空间下的client服务要可以访问nginx-ns名称空间下的nginx-svc服务,实验图如下:
创建 服务端相关资源,并放在 nginx-ns
命名空间下
cat nginx-server.yaml
---
apiVersion: v1
kind: Namespace
metadata:
name: nginx-ns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-server
namespace: nginx-ns
labels:
app: nginx-server
env: uat
spec:
selector:
matchLabels:
app: nginx-server
env: uat
replicas: 3
template:
metadata:
labels:
app: nginx-server
env: uat
spec:
containers:
- name: nginx-server
image: nginx
imagePullPolicy: IfNotPresent
startupProbe:
tcpSocket:
port: 80
readinessProbe:
httpGet:
port: 80
path: "index.html"
livenessProbe:
httpGet:
port: 80
path: "index.html"
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: nginx-ns
spec:
ports:
- name: http
port: 80
protocol: TCP
selector:
app: nginx-server
env: uat
kubectl apply -f nginx-server.yaml
创建 客户端 Pod资源:
cat nginx-client.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-client
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx-client
template:
metadata:
labels:
app: nginx-client
spec:
containers:
- name: nginx-client
image: busybox:1.28
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c", "sleep 3600000"]
kubectl apply -f nginx-client.yaml
创建 ExternalName 类型 Service,链接到 nginx-ns
名称空间下的nginx-svc
Service
cat nginx-client-svc.yaml
---
apiVersion: v1
kind: Service
metadata:
name: client-nginx
spec:
type: ExternalName
externalName: nginx-svc.nginx-ns.svc.cluster.local # 访问此SVC链接到nginx-svc.nginx-ns.svc.cluster.local SVC
ports:
- name: http
port: 80
targetPort: 80
kubectl apply -f nginx-client-svc.yaml
查看Service详细信息:
kubectl describe svc client-nginx
测试:进入client端访问nginx-service网站
kubectl exec -it nginx-client-784fd7bfc7-2d892 -- /bin/sh
wget -q -O - client-nginx
三、拓展
1、Service域名解析
Service只要创建成功,我们就可以直接通过他的域名来访问(在Pod内可以使用域名访问,在Node节点是不可以使用域名访问的),每个服务创建后都会在集群DNS中动态添加一个资源记录,添加完成后就可以解析了,资源记录的格式是:
SVC_NAME.NS_NAME.DOMAIN.LTD.
服务名.命名空间.域名后缀
集群默认的域名后缀是svc.cluster.local.
用上面的 创建ClusterIP类型svc
来做实验,在Pod中使用域名访问。
kubectl exec -it web-cluster-5db6bc847b-226k9 -- /bin/bash
curl web-clusterip.default.svc.cluster.local
2、自定义Endpoints资源
场景:创建Service资源,代理本机的3306端口
首先在宿主机安装、启动MySQL服务
yum install mariadb-server.x86_64 -y
systemctl start mariadb
systemctl enable mariadb
创建Service代理3306端口 YAML如下:
cat 3306-svc.yaml
---
apiVersion: v1
kind: Service
metadata:
name: svc-3306
spec:
type: ClusterIP
ports:
- port: 3306
kubectl apply -f 3306-svc.yaml
我们svc中没有定义关联的Pod标签,所以svc中并Endpoint为none
kubectl describe svc svc-3306
创建 Endpoint
资源 并关联上面的SVC
cat 3306-ep.yaml
---
apiVersion: v1
kind: Endpoints
metadata:
name: svc-3306 # ep的名称必须和svc一致,通过名称相关联
subsets:
- addresses:
- ip: 172.21.0.13
ports:
- port: 3306
OK,此时我们查看SVC的 Endpoint 会关联上面创建的Endpoint,后续K8S集群连接MySQL服务直接可以写SVC的IP+端口进行访问了。
kubectl describe svc svc-3306