在说明Service是什么之前先了解下Service的使用场景:
针对上面三种需求,K8S提出了Service的概念,意在解决上述三个问题和场景,下面来看看Service的定义:
Service 可以理解为逻辑上的一组Pod,一种可以访问Pod的策略,而且其他Pod可以通过这个Service访问到这个代理的Pod。相对于Pod而言,它会有一个固定的名称,一旦创建就固定不变。
服务之间的调用,要使用Service名称调用,不要使用IP地址调用,因为PodIP是随机的,每次重启会重新生成IP地址,可以新建一个Service,通过Service名称来访问应用。如果跨namespaces,可以使用Service名“.”namespaces,来访问
Service的YAML格式的定义文件的完整内容如下:
apiVersion: v1
kind: Service #定义一个Service
metadata:
name: string #Service的名称
namespaces: string #命名空间
labels: #自定义标签属性列表
- name: string
annotations: #自定义注解列表
- name: string
spec: #详细描述
selector: [] #将选择具有指定Label标签的Pod作为管理范围
type: string #类型,指定Service的访问方式,默认是ClusterIP,详细信息如下
clusterIP: string #虚拟服务的IP地址,当type=clusterIP时,如果不指定,系统会自动分配,也可以手工指定,当type=LoadBalancer时,需要指定
sessionAffinity: string #是否支持session,可选值为ClientIP,默认值为None,ClientIP:表示将一个客户端的访问请求都转发到同一个后端服务。
ports: #service端口列表
- name: string #端口名称
protocol: string #端口协议,支持TCP、udp默认是TCP
port: int #服务监听的端口号
targetPort: int #需要转发到后端Pod的端口号
nodePort: int #当type=NodePort时,需要指定端口号,不指定就随机
status: #当type=LoadBalancer时,设置外部负载均衡的地址,用于公有云环境
loadBalancer: #外部负载均衡器
ingress: #外部负载均衡器
ip: string #外部负载均衡器的IP
hostname: string #外部负载均衡器主机名
示例:这是一个详细的svc信息
[root@k8s-master-1 test]# kubectl describe svc service-label
Name: service-label
Namespace: default
Labels:
Annotations:
Selector: env=prod
Type: ClusterIP
IP: 10.0.0.254
Port: 80-80 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.0.170:80,10.244.0.171:80
Session Affinity: None
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedToUpdateEndpointSlices 12d (x2 over 12d) endpoint-slice-controller Error updating Endpoint Slices for Service default/service-label: failed to update service-label-ct7r2 EndpointSlice for Service default/service-label: Operation cannot be fulfilled on endpointslices.discovery.k8s.io "service-label-ct7r2": the object has been modified; please apply your changes to the latest version and try again
[root@k8s-master-1 test]#
对应关系
1、svc和ep的映射关系
service-label==》10.244.0.170:80,10.244.0.171:80。这些数据会存储到iptables和ipvs中
2、service的域名信息与IP的对应关系:service-label => 10.0.0.254,这些数据会存储到kubedns/coredns中
3、负载均衡,反向代理
10.244.0.170:80,10.244.0.171:80 这两个pod就是需要进行负载均衡和反向代理的pod,iptables和ipvs会根据自身的负载均衡算法来完成此过程
4、整体数据访问流程:
service-label => 10.0.0.254 => 10.244.0.170:80
它是由每个node上的kube-proxy来负责实现负载均衡的,
1、kube-proxy的代理模式
2、会话保持机制
service支持通过设置 sessionAffinity 实现基于客户端IP的会话保持机制,就是首次将某个客户端来源的IP发起的请求装发到后端的某个Pod上,之后相同的客户端IP发起的请求都将被转发到相同的后端Pod上,配置service.spec.sessionAffinity,
[root@k8s-master-1 svc-test]# cat nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
sessionAffinity: ClientIP
ports: #定义service的关键字段
- name: http #Service 端口名称
port: 80 #Service自己的端口
protocol: TCP
targetPort: 80 #后端应用的端口,这两个端口可以不同
同时,用户还可以设置会话保持的最长时间,在此时间之后重置客户端来源IP的保持规则,配置参数为service.spec.sessionAffinityConfig.clientip.timeoutSeconds。例如下边:10800为秒
[root@k8s-master-1 svc-test]# cat nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
ports: #定义service的关键字段
- name: http #Service 端口名称
port: 80 #Service自己的端口
protocol: TCP
targetPort: 80 #后端应用的端口,这两个端口可以不同
一个容器应用可以设置多个端口服务
1、设置两个端口提供不同的服务
ports: #定义service的关键字段
- name: http #Service 端口名称
port: 80 #Service自己的端口
protocol: TCP
targetPort: 80 #后端应用的端口,这两个端口可以不同
- name: http2 #Service 端口名称
port: 81 #Service自己的端口
protocol: TCP
targetPort: 81 #后端应用的端口,这两个端口可以不同
2、设置不同的协议
ports: #定义service的关键字段
- name: http #Service 端口名称
port: 80 #Service自己的端口
protocol: TCP
targetPort: 80 #后端应用的端口,这两个端口可以不同
- name: http2 #Service 端口名称
port: 81 #Service自己的端口
protocol: UDP
targetPort: 81 #后端应用的端口,这两个端口可以不同
K8S中service分为4类,分别是ClusterIP、NodePort|、LoadBalancer、以及ExternaName.
其中,绿色代表从外向内的访问模式,蓝色代表从内向外的访问模式,黄色代表集群内部的访问模式。可以看到,除了ExtermaName类型之外,其余三个都是逐层封装而来的。
K8S默认的服务类型,只能在集群中进行服务通信,在ClusterIP中,K8S会在Service创建完毕之后提供一个内部的IP作为ClusterIP属性,K8S内部服务可以通过ClusterIP或者ServiceName来访问该服务。
NodePort:是在所有安装了kube-proxy的节点上打开一个端口,此端口可以代理至后端Pod,然后集群外部可以使用节点的IP地址和NodePort的端口访问到集群Pod的服务,NodePort端口范围默认是30000-32767 这个值可以修改,在/usr/lib/systemd/system kube-apiserver.service
[root@k8s-master-1 svc-test]# kubectl edit svc nginx-svc
spec:
clusterIP: 10.0.0.60
externalTrafficPolicy: Cluster
ports:
- name: http
nodePort: 30007 #自己设定端口
type: NodePort #类型改为NodePort
#使用浏览器访问
192.168.134.137:30007 如下图
即设置Service的type为ExternalName。这样做的好处就是内部服务访问外部服务的时候是通过别名来访问的,屏蔽了外部服务的真实信息,外部服务对内部服务透明,外部服务的修改基本上不会影响到内部服务的访问,做到了内部服务和外部服务解耦合。
#使用Service反向代理外部域名
[root@k8s-master-1 svc-test]# cp nginx-svc-external.yaml nginx-externalName.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-externalname
labels:
app: nginx-externalname
spec:
type: ExternalName
externalName: www.baidu.com
[root@k8s-master-1 svc-test]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-externalname ExternalName www.baidu.com 15s
可以看到是没有ClusterIP的,反向代理到www.baidu.com
访问:
[root@k8s-master-1 svc-test]#
[root@k8s-master-1 svc-test]# kubectl exec -it busybox -- sh
/ # wget nginx-externalname
Connecting to nginx-externalname (124.237.176.3:80)
wget: server returned error: HTTP/1.1 403 Forbidden
#返回403报错 我们来解析下
/ # nslookup nginx-externalname
Server: 10.0.0.2
Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local
Name: nginx-externalname
Address 1: 124.237.176.3
Address 2: 124.237.176.4
/ #
直接返回给我们百度的IP地址,可以直接请求IP地址
/ # wget 124.237.176.4
在NodePort的基础上,借助cloud provider(云提供商)创建一个外部负载均衡器并将请求转发到NodePort,再次不做演示
它的概念就是没有入口访问地址(无ClusterIP 地址),无头Services 不会获得 Cluster IP,kube-proxy 不会处理这类服务, 而且平台也不会为它们提供负载均衡或路由。 DNS 如何实现自动配置,依赖于 Service 是否定义了Label Selector。
1、如果设置了Label Selector, kubernetes则会根据Label Selector查询后端的Pod列表,自动创建 ep ,将服务名的解析设置为:当客户端访问该服务时,得到的是全部ep列表
示例详情请查看:五、K8S-StatefulSet(STS有状态服务)-CSDN博客
[root@k8s-master mainfests]# vim stateful-demo.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
2、如果没有 设置了Label Selector,控制平面不会创建 EndpointSlice 对象。
定义一个service
创建一个deploy
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
env: prod
replicas: 2
template:
metadata:
labels:
app: nginx
env: prod
spec:
containers:
- image: nginx:1.15.3
name: nginx
ports:
- containerPort: 80
name: nginx
#定义一个Service
[root@k8s-master-1 svc-test]# kubectl expose deployment nginx
或者yaml文件方式定义
[root@k8s-master-1 svc-test]# cat nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx-svc
spec:
ports: #定义service的关键字段
- name: http #Service 端口名称
port: 80 #Service自己的端口
protocol: TCP
targetPort: 80 #后端应用的端口,这两个端口可以不同
- name: https
port: 443
protocol: TCP
targetPort: 443
selector: #定义service的关键字段
env: prod
#查看svc
[root@k8s-master-1 svc-test]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 443/TCP 35d
nginx-svc ClusterIP 10.0.0.60 80/TCP,443/TCP 23s
[root@k8s-master-1 svc-test]#
#创建了Service会创建一个同名的EndPoint,他是通过pod的IP地址来刷新ep的。
[root@k8s-master-1 svc-test]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 192.168.134.135:6443 35d
nginx-svc 10.244.0.180:443,10.244.1.139:443,10.244.0.180:80 + 1 more... 29s
services的域名表示方法为:..svc.
servicename: 服务的名称
namespaces:命名空间
clusterdomain: 为kubernetes集群设置的域名的后缀
[root@k8s-master-1 svc-test]# kubectl exec -it busybox -- sh
/ # wget http://nginx-svc
/ # cat index.html
使用Service反向代理k8s集群之外的应用场景:
示例:
apiVersion: v1
kind: Service
metadata:
name: nginx-svc-external
labels:
app: nginx-svc-external
spec:
ports:
- name: http #Service 端口名称
port: 80 #Service自己的端口
protocol: TCP
targetPort: 80 #后端应用的端口,这两个端口可以不同
# selector:
# env: prod
sessionAffinity: None
type: ClusterIP
#创建Service
[root@k8s-master-1 svc-test]# kubectl apply -f nginx-svc-external.yaml
service/nginx-svc-external created
[root@k8s-master-1 svc-test]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 443/TCP 35d
nginx-svc ClusterIP 10.0.0.60 80/TCP,443/TCP 52m
nginx-svc-external ClusterIP 10.0.0.235 80/TCP 5s
[root@k8s-master-1 svc-test]#
不同之处:
就是没有selector ,不去匹配pod
之前创建完会创建一个同名的EndPoint,但是创建一个没有Selector的Service,就不会创建EP,那怎么连接到外部服务呢?只需要我们自己创建一个EndPoint,
导出之前的ep
[root@k8s-master-1 svc-test]# kubectl get ep nginx-svc -oyaml > nginx-ep-external.yaml
apiVersion: v1
kind: Endpoints
metadata:
labels:
app: nginx-svc-external #名称和协议都要与Servicce的名称一致,才能建立连接
name: nginx-svc-external
namespace: default
subsets:
- addresses:
- ip: 10.244.0.180 #写外部服务的IP地址
ports:
- name: http
port: 80 #外部服务的端口号
protocol: TCP
测试:
把外部的IP地址写成百度的地址:- ip: 39.156.66.10
创建ep,查看ep,
访问百度
[root@k8s-master-1 svc-test]# curl baidu.com -I
HTTP/1.1 200 OK
Date: Wed, 24 May 2023 08:40:34 GMT
Server: Apache
如果外部地址变更,可以使用replace来变更IP地址
[root@k8s-master-1 svc-test]# kubectl replace -f nginx-ep-external.yaml