k8s–基础–16–Service–理论
1、什么是Service
1.1、问题
Kubernetes中,每个Pod都会有IP地址,这些IP地址并不是一直处于稳定的状态,可能随时改变,这会导致一个问题:在Kubernetes集群中,如果一组Pod(称为 backend)为其它Pod(称为 frontend)提供服务,那么那些fronten该如何发现这些backend?
1.2、什么是Service
- service是一个固定接入层,客户端可以通过访问service来访问到service关联的后端pod。
- service工作依赖于dns服务,service的名称解析是依赖于dns服务,因此在部署完k8s之后需要在部署dns服务
- 1.11之前的版本使用的是:kubeDNs
- 较新的版本使用的是:coredns
- kubernetes要想给客户端提供网络功能,需要依赖第三方的网络插件(flannel,calico等)
1.2.1、简单理解
- 可以将Service理解为微服务名称,比如consul的oa-server,这个oa-server有个列表,列表包含所有注册到consul的OA服务。我们通过访问微服务名称oa-server,就可以访问OA服务,且自动负载均衡。
- 也可以将Service理解为一个4层的负载均衡。比如haproxy等
1.3、Service定义的一种抽象
逻辑上的一组Pod,访问这组POD的策略(通常称为微服务),这一组Pod能够被Service访问到,通常是通过Label Selector实现的。
1.3.1、举例
- 一组Pod(称为 backend),它运行了3个副本,frontend需要访问backend服务。
- frontend 不需要关心它们调用了哪个 backend副本,只要能访问就行。
- frontend 不需要关心 backend副本的IP是否发生变化,只要能访问就行。
- frontend 不需要关心 backend的状态,只要能访问就行。
- backend的pod的ip可能会随时变化,那么我们就需要在 backend前面 加上一个固定接入层service(backend_service),frontend只需要访问backend_service地址,就会代理到backend的pod,那么pod就算ip怎么变化,通过service都可以找到
- 对Kubernetes集群中的应用
- Kubernetes提供了简单的Endpoints API,只要Service中的一组Pod发生变更,应用程序就会被更新。
- 对非Kubernetes集群中的应用
- Kubernetes提供了基于 VIP 的网桥的方式访问Service,再由Service重定向到backend的Pod。
2、kubernetes集群中的三类ip地址
2.1、node network(节点网络)
- 需要实实在在配置的
- 节点网络地址是配置在节点接口之上
- 因此这些地址都是配置在某些设备之上的,这些设备可能是硬件,也可能是软件模拟的
2.2、Pod network(Pod网络)
- 需要实实在在配置的
- pod网络地址是配置在pod资源之上
- 因此这些地址都是配置在某些设备之上的,这些设备可能是硬件,也可能是软件模拟的
2.3、cluster network(集群网络)
- 称为集群地址,也称为service network
- 是虚拟的地址(virtual ip),没有配置在某个接口上,只是出现在service的规则当中。
3、kube-proxy 组件
- 每个K8s节点上 都有 kube-proxy组件
- kube-proxy组件将始终监视着apiserver中有关service资源的变动信息,需要跟master之上的apiserver交互,随时连接到apiserver上获取任何一个与service资源相关的资源变动状态,这种是通过kubernetes中固有的一种请求方法watch(监视)来实现的,一旦有service资源的内容发生变动(如创建,删除),kube-proxy都会将它转化成当前节点之上的能够实现service资源调度,把我们请求调度到后端特定的pod资源之上的规则,这个规则可能是iptables,也可能是ipvs,取决于service的实现方式
- 如果某个服务背后的pod资源发生改变,比如service的标签选择器适用的pod又多了一个,这个pod使用的信息会立即反应在apiserver上,kube-proxy能监听到这个service的变化,将其立即转为service规则(如iptables规则)
4、service实现方式
4.1、iptables
- 客户端ip请求时直接请求service的ip,这个请求报文被本地内核空间中的service规则所截取,进而直接调度给相关的pod
- 这个方式是直接工作在内核空间,由iptables规则直接实现
- 作用版本:Kubernetes 1.11之前的版本使用
4.2、ipvs
- 客户端请求到达内核空间之后直接由ipvs规则来调度到相关的pod资源
- 作用版本:1.11+版本使用的是ipvs,ipvs如果没有被激活就会自动降级为iptables
5、创建service
5.1、创建pod
- 具体看 k8s–基础–14–deployment
- 该pod是为了给service代理使用
5.1.1、配置文件
apiVersion: apps/v1
kind: Deployment
metadata:
# Deployment 的名称
name: deployment-nginx
# Deployment 的标签
labels:
k1: k1_la
k2: k2_la
spec:
# 副本数目
replicas: 3
# 选择器
selector:
# 选择器匹配的标签
matchLabels:
nginx_pod: nginx_pod_la
# Pod 模板
template:
metadata:
# Pod的标签
labels:
nginx_pod: nginx_pod_la
spec:
containers:
# 容器名称
- name: nginx
# 镜像
image: nginx
# 镜像策略
imagePullPolicy: IfNotPresent
# 容器端口
ports:
- containerPort: 80
5.1.2、验证:使用curl 也能调通所有IP地址
kubectl get pods -l nginx_pod=nginx_pod_la -o wide
curl 10.244.1.89:80
- 注意:容器不会使用该节点上的80端口,也不会使用任何特定的NAT规则去路由流量到Pod上。
- 这意味着:可以在同一个节点上运行多个Pod,且使用相同的容器端口
- 这意味着:可以从集群中任何其他的Pod或节点上使用IP的方式访问到它们。
- 像Docker一样,端口能够被发布到主机节点的接口上,但是出于网络模型的原因应该从根本上减少这种用法。
5.2、创建service
- Service从逻辑上定义了运行在集群中的一组Pod,这些Pod提供了相同的功能,当每个Service创建时,会被分配一个唯一的IP地址,也称为clusterIP。
- clusterIP与Service的生命周期绑定在一起,当Service存在的时候它不会改变。
- 可以配置Pod使它与Service进行通信,通过Service通信,可以做到对Pod的负载均衡。
5.2.1、脚本
vim /root/test/service_nginx.yaml
内容
apiVersion: v1
kind: Service
metadata:
# Service 的名称
name: service-nginx
labels:
# Service 的标签
service_k1: k1_la
spec:
ports:
# 端口
- port: 80
# 协议
protocol: TCP
# 标签选择器,选择 pod 的标签
selector:
nginx_pod: nginx_pod_la
- 上面创建一个Service,对应具有标签 nginx_pod: nginx_pod_la 的Pod
- 目标TCP端口80,在一个抽象的Service端口上暴露
- targetPort:容器接收流量的端口
- port:抽象的Service端口,可以使任何其它Pod访问该Service的端口。
5.2.2、启动
kubectl apply -f /root/test/service_nginx.yaml
5.2.3、验证
5.2.3.1、查看service
kubectl get svc service-nginx
5.2.3.2、查看service 的明细
kubectl describe svc service-nginx
正如前面所提到的,一个Service由一组backend Pod组成。这些Pod通过endpoints暴露出来。
10.107.212.90 的请求,会被POST到一个名称为 nginx_pod_la 的 Endpoint对象上。
- 当Pod终止后:自动从Endpoint中移除
- 当Pod新增后:自动被添加到Endpoint中。
5.2.3.3、查看service的Endpoint
kubectl get ep service-nginx
5.2.3.4、验证负载均衡
从集群中任意节点上使用 curl 命令请求Nginx
语法:Service <CLUSTER-IP>:<PORT>
# CLUSTER-IP:service的ip
# PORT:service的端口
# 通过访问service ip:port可以路由到后端的pod,具体哪个pod看负载均衡策略
curl 10.107.212.90:80
6、没有selector的Service
6.1、使用场景:案例
- 希望 Service服务 使用外部的数据库集群
- 希望Service服务 指向另一个Namespace中或其它集群中的服务。
- 将某个服务 负载到Kubernetes集群中
6.2、案例
创建一个没有selector的Service(mysql-service),mysql-service服务能使用外部的数据库集群。
6.2.1、创建Service(mysql-service)服务
6.2.1.1、mysql-service脚本
vi /root/test/service_mysql.yaml
内容
kind: Service
apiVersion: v1
metadata:
# Service 的名称
name: mysql-service
spec:
ports:
# 端口
- port: 80
# 协议
protocol: TCP
# 目标端口
targetPort: 3306
# 访问类型
type:
# 可以被k8s之外的主机或者浏览器访问
NodePort
6.2.1.2、mysql-service启动
kubectl apply -f /root/test/service_mysql.yaml
kubectl get svc mysql-service
6.2.2、创建相关的Endpoints对象
由于mysql-service没有selector,就不会创建相关的Endpoints对象。可以手动将Service映射到指定的Endpoints。
6.2.2.1、mysql-service脚本
vi /root/test/endpoints_mysql.yaml
内容
kind: Endpoints
apiVersion: v1
metadata:
# 这里要写service的名称
name: mysql-service
subsets:
- addresses:
# 这个地址可以是集群外部的地址,如mysql等,当访问serveice的时候,会被代理到192.168.187.171:3306
# 有几个mysql服务,就写几个IP,这样就可以起到负载均衡了。
- ip: 192.168.187.171
ports:
- port: 3306
6.2.2.2、mysql-service启动
kubectl apply -f /root/test/endpoints_mysql.yaml
kubectl describe svc mysql-service
kubectl get ep mysql-service
注意:Endpoint IP 地址不能是下面的地址
- loopback(127.0.0.0/8)
- link-local(169.254.0.0/16)
- link-local多播(224.0.0.0/24)。