Kubernetes:Service
概述#
- Kubernetes使用Service解决服务发现问题:每个Pod在创建后都会被分配一个IP地址,然而它会随着Pod的重启而改变;
- Service可以通过标签选择器选择一组Pod,然后作为它们共同的对外访问接口。这样我们的应用便可以在不知道Pod的IP地址的情况下,与其通信;
- 当Service的标签选择器选择了多个Pod时,还可以在它们之间做负载均衡;
- 众所周知,Service的中文意为“服务”。但就其功能而言,更像是一个Proxy(代理)或Router(路由)。
配置#
一个典型的Service对象配置如下:
kind: Service
apiVersion: v1
metadata:
name: nginx-server
spec:
clusterIP: 192.168.1.0
selector:
app: nginx
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
- 每个Service都会由系统分配一个虚拟IP作为访问Service的入口IP地址,然后监听
spec.ports.port
字段指定的端口; - Service的IP也可以通过
spec.clusterIP
字段指定,且必须是APIServer中的配置字段service-cluster-ip-range
CIDR范围内的合法地址; - 标签选择器
spec.selector
能够根据labels选择目标Pod,而Service会将外部流量转发到目标Pod的spec.ports.targetPort
端口; - 该Service开放了多个端口(80/443),因此必须定义端口的名称
spec.ports.name
(http/https),以避免歧义; - 当Service被创建后,系统随之创建一个同名的Endpoints对象,它保存了所有匹配标签选择器的Pod的IP地址和端口。
无标签选择器的Service#
上文提到,Service常用于对Pod的流量调度,但也可以用于访问:
- 同一集群不同namespace中的或其他集群中的Service
- 其他的一些后端程序或数据库
此时需要定义一个没有标签选择器的Service,如:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
因为该Service没有标签选择器,相应的Endpoint对象便无法自动创建。因此我们需要手动创建一个Endpoint对象,以便将到达该Service监听的端口的请求映射到后端程序或其他Service的IP地址和端口:
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
subsets:
- addresses:
- ip: 192.0.2.42
ports:
- port: 9376
在上述例子中,Service将请求路由到Endpoint:192.0.2.42:9376 (TCP)。另外,目标IP不能是本机回环地址或虚拟IP地址。
Headless Services#
Headless Service不提供负载均衡的特性,也没有自己的IP地址,kube-proxy并不会处理这类Service。只需要指定spec.clusterIP
字段值为"None",便可以创建一个Headless Service对象。
代理模式#
- iptables:由kube-proxy负责为每一个Service创建和维护iptables的路由规则,其余工作由内核的iptables完成。iptables承担了从Service的IP地址到Pod的IP地址的负载均衡工作。在默认情况下,iptables会将请求随机重定向到由Service代理的一组Pod中的某个Pod上。
- IPVS:kube-proxy监视Service和Endpoint对象的改变,调用netlink接口相应地创建IPVS规则,并定期地将IPVS规则与Service和Endpoint对象同步。IPVS可以转发TCP/UDP请求到实际的服务器上,使得一组实际的服务器(Pod)看起来像是只通过一个单一IP地址(Service)访问的服务一样。
服务发现模式#
Kubernetes支持两种基本的服务发现模式 —— 环境变量和DNS。
环境变量#
kubelet会为节点上活跃的Service对象创建一组环境变量(包括Service的clusterIP、监听的端口等),并在创建Pod时这些环境它们注入其中。使用这种服务发现模式要求Service先于Pod创建,因此有一定的局限性。
DNS#
集群中的DNS服务器(例如CoreDNS)使用Kubernetes的watch api不断监测Service的创建并为每一个Service创建一条DNS记录,从而使Pod可以解析该Service的域名。对于不同类型的Service,其DNS记录的分配方式有所不同:
Headless Service以外的Service#
-
Service将被分配一个A记录,格式为:
<service-name>.<namespace-name>.svc.cluster.local
其中,cluster.local为默认的集群域名,该DNS记录解析到Service的ClusterIP。
-
若Servic存在一个已被命名的端口(
spec.ports.name
字段不为空),则该端口将被分配一个SRV记录,其格式为:<_port-name>.<_port-protocol>.<service-name>.<namespace-name>.svc.cluster.local
则可根据该SRV记录发现该Service的命名端口及IP地址。
Headless Service#
-
已定义标签选择器的Headless Service:Endpoints Controller在Kubernetes API中创建Endpoints对象,并修改DNS配置返回一个A记录(格式与普通的Service相同),指向该Service选取的一组Pod的IP地址。
-
无标签选择器的Headless Service:Endpoints Controller不再创建Endpoints对象。若Service类型为ExternalName,DNS服务返回其CNAME记录;若Service为其他类型,返回与该Service同名的Endpoints对象的A记录。Service的类型将在下一节中进行介绍。
外部访问#
集群中的节点(虚拟机)可以通过网关访问互联网,但Pod的IP地址与其所在节点的IP地址显然不同。由于网关的NAT功能只能转换节点(虚拟机)的IP地址,而无法转换Pod的IP地址。毕竟网关根本无法知晓节点上运行了什么样的Pod,因此Pod是无法访问外网的。而我们常希望将集群中的一些运行前端应用的Pod暴露给集群外部的IP地址,这时便需要改变spec.type
字段定义特殊类型的Service了。
ClusterIP#
默认的Service类型,通过集群中的内部IP暴露Service,这种方式的Service只能在集群内部访问。
NodePort#
通过每个节点上的IP和静态端口(spec.ports.nodePort
)暴露服务,其配置如下:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
selector:
app: MyApp
ports:
# 默认情况下,为了方便起见,targetPort与port字段值相同
- port: 80
targetPort: 80
# 如不指定,则会从30000-32767范围内分配一个端口号
nodePort: 30007
该类型的Service在集群外部可以通过<NodeIP>:<spec.ports.nodePort
>访问,同时也会自动创建一个ClusterIP类型的访问方式:<spec.clusterIp
>:<spec.ports.port
>可供集群内部访问。
LoadBalancer#
使用云提供商的负载均衡器向集群外部暴露服务,同时也会自动创建ClusterIP和NodePort类型的访问方式:
- 在集群内部通过<
spec.clusterIp
>:<spec.ports.port
>访问; - 在集群外部通过<NodeIP>:<
spec.ports.nodePort
>访问; - 在集群外部通过负载均衡器的IP和port访问。
负载均衡器是异步创建的。当LoadBalancer类型的Service创建完成后,负载均衡器的信息将被回写到Service的status.loadBalancer
字段中,例如:
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
selector:
app: example
clusterIP: 10.84.206.2
externalIPs:
- 172.29.245.25
ports:
- nodePort: 31688
port: 8765
protocol: TCP
targetPort: 9376
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 172.29.245.25
Loadbalancer类型的Service处理外网入方向流量的流程如下:
- Loadbalancer类型的Service创建后,Cloud Controller(云服务商提供)将为其创建一个负载均衡器;
- 负载均衡器只能直接和节点(虚拟机)沟通,并不知晓Pod的存在。当数据包从请求方(互联网)到达负载均衡器之后,将被分发到集群中的某一节点上;
- kube-proxy在节点上安装的iptables规则(或IPVS)将数据包的目的地址转换为合适的Pod的IP地址(所谓“合适”,即集群内的负载均衡);
- iptables将数据包转发到对应的Pod。
ExternalName#
ExternalName类型的Service将Service映射到DNS名称,而非标签选择器选取的一组Pod上,其配置如下:
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
当查找域名:my-service.prod.svc.cluster.local时,集群内的DNS服务将返回一条CNAME记录,即spec.externalName
字段的值my.database.example.com。访问该服务的方式与其他类型的Service相同,但主要区别在于重定向发生在DNS级别,而不是通过代理或转发。
External IP#
如果有外部IP路由到Kubernetes集群的一个或多个节点,任意类型的Service可以通过<spec.externalIPs
>:<spec.ports.port
>进行访问。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
externalIPs:
- 80.11.12.10
在上面的例子中,客户端即可通过80.11.12.10:80访问名为my-service的Service对象。
作者:koktlzz
出处:https://www.cnblogs.com/koktlzz/p/14410329.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现