前言:
Kubernetes中的Service是一种用于定义一组Pod如何被访问的抽象:它提供了一种稳定的网络端点,以便其他应用程序可以与 Pod 交互,而无需了解底层的Pod IP地址或其他细节。
Service
Service:Service是一个固定接入层,客户端可以通过访问service来访问到service关联的后端Pod,这个service工作依赖于在kubernetes集群之上部署的一个dns附件,就是kubernetes的dns服务coredns,Service的名称解析是依赖于dns附件,因此在部署完Kubernetes之后需要在部署dns附件,kubernetes要想给客户端提供网络功能,需要依赖第三方的网络插件(flannel,calico等)。Service可以通过不同的方式暴露,包括 ClusterIP、NodePort、LoadBalancer 和 ExternalName。以下是每种类型的简要介绍:
- ClusterIP:创建一个在集群内部可访问的虚拟IP,可以通过该IP访问Service。这种类型通常用于在集群内部暴露服务,对外部不可见。
- NodePort:在每个节点上都开放一个端口,通过该端口可以访问 Service。这种类型适用于需要从集群外部访问服务的场景,但不适用于大规模生产环境。
- LoadBalancer:在云提供商中创建一个负载均衡器,可以将外部流量路由到 Service。这种类型通常用于公开服务并提供负载均衡。
- ExternalName:将 Service 映射到集群外部的任意名称,通常用于将Kubernetes内部服务映射到外部服务,或者将服务与外部 DNS 记录关联起来。
Service的实现方式:
- iptables:客户端ip请求时直接请求service的ip,这个请求报文被本地内核空间中的service规则所截取,进而直接调度给相关的pod,这个方式是直接工作在内核空间,由iptables规则直接实现。
- ipvs:客户端请求到达内核空间之后直接由ipvs规则来调度到相关的pod资源。ipvs如果没有被激活就会自动降级为iptables
kubernetes集群中的三类ip地址:node network(节点网络),Pod network(Pod 网络),这两种网络地址是我们实实在在配置的,其中节点网络地址是配置在节点接口之上,而Pod网络地址是配置在Pod资源之上的,因此这些地址都是配置在某些设备之上的,这些设备可能是硬件,也可能是软件模拟的,cluster network(集群地址,也成为service network),这个地址是虚拟的地址(virtual ip),没有配置在某个接口上,只是出现在Service的规则当中。
kube-proxy组件:它运行在每个节点上,并负责为Service提供网络代理和负载均衡功能。
它主要有以下几个作用:
- 服务代理:Kube-proxy监听Kubernetes API Server中的 Service和 Endpoints对象的变化。当Service被创建、更新或删除时,kube-proxy 会相应地更新节点上的 iptables 规则,以便将流量转发到正确的Pod。
- 负载均衡:对于 Service 类型为NodePort和LoadBalancer的服务,kube-proxy会在每个节点上设置相应的iptables规则,将访问 Service 的流量分发到后端 Pod。对于NodePort 类型的服务,kube-proxy 还会在每个节点上开放一个相同的端口,以便外部客户端可以通过该端口访问 Service。
- 服务发现:kube-proxy通过监视Endpoints对象的变化来实现服务发现功能。当Pod启动或停止时,对应的Endpoints也会相应地更新,kube-proxy会更新相应的iptables规则以确保流量被正确路由到新的Pod上。
简单的yaml文件
apiVersion: v1
kind: Service
metadata:
name: ser-nginx
labels:
app: nginx
namespace: default
spec:
ports:
- name: port-nginx
appProtocol: TCP
port: 80
nodePort: 30001
targetPort: 80
selector:
ser: nginx-test
type: NodePort
注:service下的service.spec.selector选择的是控制器下的模板定义的标签,而非控制器metadata下定义的标签。
访问的流量走向:访问物理节点(master节点或者node节点)ip:宿主机映射的端口—>service ip:service的端口----->pod ip:container port
查看yaml在spec.ports下有三个字段port、nodePort、targetPort:
- port:service暴露在cluster ip上的端口,clusterIP:port是提供给集群内部客户访问service的入口。
- nodePort:首先nodePort是kubernetes提供给集群外部客户访问service入口的一种方式(另一种方式是LoadBalancer),所以nodeIP:nodePort 是提供给集群外部客户访问service的入口。
- targetPort:targetPort是pod上的端口。targetPort配置在Service,它是Service映射到Pod上的端口。从nodePort–>port上来的数据,经过kube-proxy流入到后端pod上的targetPort最终进入容器。而
port、nodePort:总的来说,port和nodePort都是service的端口,前者暴露给集群内客户访问服务,后者暴露给集群外客户访问服务。从这两个端口到来的数据都需要经过反向代理kube-proxy流入后端pod的targetPod,从而到达pod中的容器
typ的类型:即Service暴露方式。
没有selector的Service
apiVersion: v1
kind: Endpoints
metadata:
name: mysql-svc-192
labels:
svc: mysql
namespace: default
subsets:
- addresses:
- ip: 192.168.0.103
ports:
- name: mysql-svc-30002
port: 3306
---
apiVersion: v1
kind: Service
metadata:
name: mysql-svc-192
labels:
svc: mysql
namespace: default
spec:
ports:
- protocol: TCP
name: mysql-svc-30002
nodePort: 30002
port: 3306
targetPort: 3306
type: NodePort
在这个yaml文件中service代理了外部的一个mysql服务,由于没有selector,首先要创建Endpoints对象,创建的Service和Endpoints中name字段要上下保持一致。(service.metadata.name和endpoints.metatada.name、spec.ports.name和subsets.ports.name)
ExternalName类型的Service
apiVersion: v1
kind: Service
metadata:
name: exter-url
labels:
svc: pod-uer
namespace: hello
spec:
type: ExternalName
externalName: example.com
ExternalName类型的Service用于将Kubernetes中的服务映射到集群外部的服务地址。当使用 ExternalName 类型的 Service时,集群内的应用程序可以通过该服务名访问位于集群外的服务。
访问方式serviceName.namespace(名称空间).svc.cluster.loacl
访问后会返回example.com(外部域名)的一个A记录。
Headless Service
是一种不分配ClusterIP的服务,通常用于服务发现。它会创建与 Service 同名的 DNS 记录,解析到该 Service 中所有 Pod 的IP地址,但不会为该Service分配一个 ClusterIP。这意味着无法使用 ClusterIP 来访问 Headless Service,而是通过DNS名称直接访问Pod。可以通过指定 Cluster IP(spec.clusterIP)的值为 “None” 来创建 Headless Service。
不使用ip和port。无头服务根据我自己的理解来说,我们访问一个服务正常是访问的4层代理service或者7层代理ingress。而无头服务就是跳过这个4层或者7层代理,直接与pod的真实Ip进行访问。正常我们通过nslookup解析到service的ip,而无头服务,通过解析后得到的是后端pod的ip地址