Kubernetes在设计之初就充分考虑了针对容器的服务发现与负载均衡机制,提供了Service资源——一种将运行在一组Pod上的应用公开为网络服务的抽象方法。使用Kubernetes,我们无需修改应用程序即可使用不熟悉的服务发现机制。Kubernetes为Pod提供自己的IP地址和一组Pod的单个DNS名称,并且可以在它们之间进行负载平衡。
本文主要参考官方文档对Kubernetes的Service进行一个总结。
本文所使用的环境如下:
Kubernetes的Pod是有生命周期的。他们可以被创建,销毁不会再启动。 如果使用Deployment来运行应用程序,则它可以动态创建和销毁Pod。每个Pod都有自己的IP地址,但是在Deployment中,在同一时刻运行的Pod集合可能与稍后运行该应用程序的Pod集合不同。这导致了一个问题:如果一组Pod(称为“后端”)为群集内的其他Pod(称为“前端”)提供功能,那么前端如何找出并跟踪要连接的IP地址,以便前端可以使用工作量的后端部分?Service
的出现就是为了解决这些问题。例如下图:
Nginx Pod作为客户端要访问Tomcat Pod中的应用,但是Pod删除重建或更新之后,Pod对象的IP地址等都会发生新的变化。IP的变动或规模的缩减会导致客户端访问错误。而Pod规模的扩容又会使得客户端无法有效的使用新增的Pod对象,从而影响达成规模扩展之目的。为此,Kubernetes特地设计了Service
来解决此类问题。
Kubernetes的Service
定义了这样一种抽象:逻辑上的一组Pod
,一种可以访问它们的策略——通常称为微服务。 这一组Pod
能够被Service
访问到,通常是通过selector实现的。例如下图:
图中有名为Tomcat的Deployment创建Tomcat的Pod
并维持3个副本。一个名为Tomcat的Service
通过标签app=webapp
来选择满足条件的Pod
,这一组Pod
能够被该Service
访问到,只要Pod
的标签中包含app=webapp
即满足条件。客户端通过该Service
即可访问到Tomcat的Pod
,客户端不需要关心它们调用了Tomcat的哪个Pod
副本。 尽管组成这一组Tomcat的Pod
实际上可能会发生变化,客户端不应该也没必要知道,而且也不需要跟踪这一组Pod
的状态。 Service
定义的抽象能够解耦这种关联。
一个Service
在Kubernetes中是一个REST对象,和Pod
类似。像所有的REST对象一样,Service
定义可以基于POST
方式,请求API server创建新的实例。下面创建myapp-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
labels:
app: MyApp
spec:
replicas: 3
selector:
matchLabels:
app: MyApp
template:
metadata:
labels:
app: MyApp
spec:
containers:
- name: nginx
image: nginx:alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
创建名为myapp-deployment
的Deployment:
kubectl apply -f myapp-deployment.yaml
Deployment会控制RS创建Pod
并维持至3个副本数,这组Pod
中的nginx容器需要监听80
端口,同时Pod
还被打上app=MyApp
标签。查看所有Pod
:
创建myapp-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 88
targetPort: 80
需要注意的是,
Service
能够将一个接收port
映射到任意的targetPort
。默认情况下targetPort
将被设置为与port
字段相同的值。
创建名为myapp-service
的Service
:
kubectl apply -f myapp-service.yaml
该配置创建一个名称为myapp-service
的 Service
对象,它会将请求(clusterIP:port
)代理到使用TCP的端口80
(targetPort
)并且具有标签"app=MyApp"
的Pod
上。Kubernetes为该服务分配一个IP地址,称为 “cluster IP” ,该IP地址由服务代理使用。服务选择器的控制器不断扫描与其选择器匹配的 Pod
,然后将所有更新发布到也被称为myapp-service
的Endpoint
对象。查看所有Service
和myapp-service
详细信息:
由于本文示例的k8s集群使用的IPVS模式,查看当前虚拟服务列表可知Service
通过IPVS规则实现代理和负载均衡:
服务的默认协议是TCP,还可以使用任何其他受支持的协议。由于许多服务需要公开多个端口,因此Kubernetes在服务对象上支持多个端口定义。每个端口定义可以具有相同的protocol
,也可以具有不同的协议。
Service
最常见的是抽象化对Kubernetes的Pod
的访问,但是它们也可以抽象化其他种类的后端。例如:
在上面这些场景中可以定义没有selector的Service
。例如,创建myapp-without-selector-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: myapp-without-selector-service
spec:
ports:
- protocol: TCP
port: 88
targetPort: 80
创建myapp-without-selector-service
:
kubectl apply -f myapp-without-selector-service.yaml
查看所有Service
和myapp-without-selector-service
详细信息:
查看当前虚拟服务列表:
根据以上信息可知由于该Service
没有selector,因此不会自动创建相应的Endpoint
对象。 可以通过手动添加Endpoint
对象,将服务手动映射到运行该服务的网络地址和端口。创建myapp-without-selector-service-endpoints.yaml
:
apiVersion: v1
kind: Endpoints
metadata:
name: myapp-without-selector-service
subsets:
- addresses:
- ip: 10.244.1.7
- ip: 10.244.2.41
- ip: 10.244.2.42
ports:
- port: 80
通过以上配置创建Endpoint
对象,创建Endpoint
对象会添加到myapp-without-selector-service
中:
kubectl apply -f myapp-without-selector-service-endpoints.yaml
查看所有Service
和myapp-without-selector-service
详细信息:
查看当前虚拟服务列表:
根据以上信息可知Endpoint
对象已成功创建并添加到myapp-without-selector-service
中。
注意:
Endpoint
的IP地址不能是loopback(IPv4的127.0.0.0/8
,IPv6的::1/128
),或link-local(IPv4的169.254.0.0/16
和224.0.0.0/24
,IPv6的fe80::/64
)。Endpoint
的IP地址不能是其他Kubernetes的Service的ClusterIP,因为kube-proxy不支持将虚拟IP作为目标。
访问没有selector的Service
,与有selector的Service
的原理相同。请求将被路由到上面的YAML
中用户定义的10.244.1.7:80,10.244.2.41:80,10.244.2.42:80
(TCP)这个Endpoint
。ExternalName
类型的Service
是Service
的特例,它没有selector,也没有使用DNS名称代替。
Endpoint Slice是一种API资源,可以为Endpoint提供更可扩展的替代方案。尽管从概念上讲与Endpoint非常相似,但Endpoint Slice允许跨多个资源分布网络端点。默认情况下,一旦到达100个Endpoint,该Endpoint Slice将被视为已满,届时将创建其他Endpoint Slice来存储任何其他Endpoint。Endpoint Slice提供了附加的属性和功能。
对于某些服务,需要暴露多个端口。Kubernetes允许在Service对象上配置多个端口定义。为服务使用多个端口时,必须提供所有端口名称,以使它们无歧义。下面创建myapp-multi-port-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: myapp-multi-port-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
创建myapp-multi-port-service
:
kubectl apply -f myapp-multi-port-service.yaml
查看所有Service
和myapp-multi-port-service
详细信息:
查看当前虚拟服务列表(只截取了部分内容):
注意:与一般的Kubernetes名称一样,端口名称只能包含小写字母数字字符和
-
。端口名称还必须以字母数字字符开头和结尾。例如,名称123-abc
和web
有效,但是123_abc
和-web
无效。
在Service
创建的请求中,可以通过spec.clusterIP
字段来指定自己的clusterIP地址。 比如,希望替换一个已经已存在的DNS条目,或者遗留系统已经配置了一个固定的 IP 且很难重新配置。自己指定的IP地址必须合法,并且这个IP地址在service-cluster-ip-range
CIDR范围内,这对API Server来说是通过一个标识来指定的。如果IP地址不合法,API Server会返回HTTP状态码422,表示值不合法。下面创建myapp-specify-cluster-ip-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: myapp-specify-cluster-ip-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 88
targetPort: 80
clusterIP: 10.96.59.136
创建myapp-specify-cluster-ip-service
:
kubectl apply -f myapp-specify-cluster-ip-service.yaml
查看所有Service
和myapp-specify-cluster-ip-service
详细信息:
查看当前虚拟服务列表(只截取了部分内容):
在Kubernetes集群中,每个Node运行一个kube-proxy
进程。kube-proxy
负责为Service
实现了一种VIP(虚拟 IP)的形式,而不是ExternalName
的形式。
Kubernetes v1.0开始至Kubernetes v1.1(包括v1.1),默认使用userspace模式。
这种模式,kube-proxy会监视Kubernetes控制节点对Service
和Endpoint
对象的添加和移除。对每个Service
,它会在在本地Node上随机打开一个端口(代理端口)。任何连接到代理端口的请求,都会被代理到Service
后端中的某个Pod
上面,至于被代理到当前Service
后端的哪个Pod
对象取决于当前Service
的调度方式,是kube-proxy基于SessionAffinity
确定的。默认的调度策略是kube-proxy在userspace模式下通过轮询(round-robin)算法来选择后端的Pod
。最后,它会配置iptables规则,捕获到达该Service
的clusterIP
和port
的请求并重定向到代理端口,代理端口再代理请求到后端的Pod
。
userspace代理的过程为:请求到达service
后,其被转发至内核空间,经由套接字送往用户空间的kube-proxy
,然后再由它送回内核空间,并调度至后端Pod
。传输效率较低。
Kubernetes v1.1添加了iptables代理模式,从Kubernetes v1.2开始,默认使用iptables模式。
这种模式,kube-proxy会监视Kubernetes控制节点对Service
和Endpoint
对象的添加和移除。对每个Service
,它会配置iptables规则,从而捕获到达该Service
的clusterIP
和port
的请求并将请求重定向到Service
的后端中的某个Pod
上面。对于每个Endpoint
对象,它也会配置选择一个后端Pod
的iptables规则。默认的调度策略是kube-proxy在iptables模式下通过随机(random)算法来选择一个后端的Pod
。
iptables代理过程为:请求到达service
后,其被相关service
上的iptables规则进行调度和目标地址转换后再转发至集群内的Pod
对象之上。使用iptables处理流量具有较低的系统开销,因为流量由Linux netfilter处理,而无需在用户空间和内核空间之间切换。
如果kube-proxy在iptables模式下运行,并且所选的第一个Pod没有响应,则连接失败。而userspace模式在这种情况下,kube-proxy将检测到与第一个Pod的连接已失败,并会自动使用后端的其他Pod重试。可以使用Pod的readiness探测器验证后端Pod可以正常工作,以便iptables模式下的kube-proxy仅看到测试正常的后端Pod。这样可以避免将流量通过kube-proxy发送到已知已失败的Pod上。
Kubernetes v1.8添加了ipvs代理模式。
在ipvs
模式下,kube-proxy监视Kubernetes的Service
和Endpoint
,调用netlink
接口相应地创建IPVS规则,并定期将IPVS规则与Kubernetes的Service
和Endpoint
同步。该控制循环可确保IPVS状态与所需状态匹配。访问服务时,IPVS将流量定向到其中一个后端Pod。
IPVS代理模式基于类似于iptables模式的netfilter挂钩函数,但使用哈希表作为基础数据结构并在内核空间中工作。这意味着,与iptables模式下的kube-proxy相比,IPVS模式下的kube-proxy重定向通信的延迟更短,并且在同步代理规则时具有更好的性能。与其他代理模式相比,IPVS模式还支持更高的网络流量吞吐量。IPVS还为负载均衡提供了更多选项。这些选项如下:
rr
:轮询(round-robin)lc
:最小连接数(least connection)dh
:目标哈希(destination hashing)sh
:源哈希(source hashing)sed
:最短期望延迟(shortest expected delay)nq
:永不排队(never queue)注意:要在IPVS模式下运行kube-proxy,必须在启动kube-proxy之前使IPVS Linux在节点上可用。当 kube-proxy以IPVS代理模式启动时,它将验证IPVS内核模块是否可用。如果未检测到IPVS内核模块,则kube-proxy将退回到以iptables代理模式运行。
如果要确保每次都将来自特定客户端的连接传递到同一Pod,可以通过将service.spec.sessionAffinity
设置为"ClientIP",默认值是 “None”,来基于客户端的IP地址选择会话关联。可以通过service.spec.sessionAffinityConfig.clientIP.timeoutSeconds
来设置最大会话停留时间,默认值为10800秒,即3小时。
为了使用户能够为他们的Service选择一个端口号,必须确保不能有2个Service发生冲突。Kubernetes通过为每个Service分配它们自己的 IP地址来实现。为了保证每个Service被分配到一个唯一的IP,需要一个内部的分配器能够原子地更新etcd中的一个全局分配映射表,这个更新操作要先于创建每一个Service。为了使Service能够获取到IP,这个映射表对象必须在注册中心存在,否则Service创建将会失败并显示一条指示一个IP不能被分配的消息。
在控制平面中,一个后台Controller负责创建映射表(需要支持从使用了内存锁的Kubernetes的旧版本迁移过来)。同时Kubernetes会通过控制器检查不合理的分配(如管理员干预导致的)以及清理已被分配但不再被任何Service使用的IP地址。
Service
的IP不像Pod
的IP地址实际路由到一个固定的目的地,它实际上不能通过单个主机来进行应答。所以使用iptables来定义一个虚拟IP地址(VIP),它可以根据需要透明地进行重定向。当客户端连接到VIP时,它们的流量会自动地传输到一个合适的Endpoint。Service
的环境变量和DNS实际上会根据Service
的虚拟IP地址和端口来进行填充。kube-proxy支持三种代理模式各自的操作略有不同。
userspace模式,创建Service
时,Kubernetes master会给它分配一个虚拟IP地址,该Service
会被集群中所有的kube-proxy
实例观察到。当代理看到一个新的Service
时,它会打开一个新的随机端口(代理端口),建立一个从该虚拟IP地址重定向到新端口的iptables,并开始接收请求连接。当一个客户端连接到一个Service
的虚拟IP地址时,iptables规则开始起作用,它会重定向该数据包到Service
的代理端口。Service
代理选择一个后端的Pod
并将客户端的流量代理到该后端Pod
上。这意味着Service
的所有者能够选择他们想使用的任何端口而不会发生冲突。客户端可以简单地连接到一个IP和端口,而不需要知道实际访问了哪些Pod
。
iptables模式,创建Service
时,Kubernetes控制面板会给它分配一个虚拟IP地址,该Service
会被集群中所有的kube-proxy
实例观察到。当代理看到一个新的Service
时, 它会配置一系列从虚拟IP地址重定向per-Service
规则的iptables规则。该per-Service
规则链接到重定向(使用目标NAT)到后端的Pod
的per-Endpoint
规则。当一个客户端连接到一个Service
的虚拟IP地址时,iptables规则开始起作用。一个后端Pod
会被选择(或者根据会话亲和性或者随机)并且数据包被重定向到该后端Pod
。与userspace代理不同,数据包从来不会拷贝到用户空间,kube-proxy不是必须为该虚拟IP工作而运行,并且节点会看到来自未更改的客户端IP地址的流量。当流量通过节点端口或负载均衡器进入时,尽管在这种情况下,客户端IP发生更改,也会执行相同的基本流程。
IPVS模式,专为负载均衡设计,并基于内核中的哈希表。在大规模集群中,iptables操作会显着降低速度时。可以通过基于IPVS的kube-proxy在大量服务中实现性能一致性。 同时,基于IPVS的kube-proxy具有更复杂的负载均衡算法。
Kubernetes支持2种基本的服务发现模式——环境变量和DNS。
当Pod
运行在 Node
上,kubelet会为每个活跃的Service
添加一组环境变量。它同时支持Docker links兼容变量、简单的{SVCNAME}_SERVICE_HOST
和{SVCNAME}_SERVICE_PORT
变量,这里Service
的名称需大写,横线被转换成下划线。举个例子,一个名为redis-master
的Service
暴露了TCP端口6379,同时给它分配了Cluster IP地址10.0.0.11
,这个Service
生成了如下环境变量:
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
注意:当有需要访问服务的Pod时,并且正在使用环境变量方法将端口和群集IP发布到客户端Pod时,必须在客户端Pod出现之前创建服务。 否则,这些客户端Pod将不会设定其环境变量。如果仅使用DNS查找服务的群集IP,则无需担心此设定问题。
可以使用附加组件为Kubernetes集群设置DNS服务。支持群集的DNS服务器(例如CoreDNS)监视Kubernetes API中的新服务,并为每个服务创建一组DNS记录。如果在整个群集中都启用了DNS,则所有Pod都应该能够通过其DNS名称自动解析服务。例如,如果在 Kubernetes命名空间my-ns
中有一个名为my-service"
的服务,则控制平面和DNS服务共同为my-service.my-ns
创建DNS记录。my-ns
命名空间中的Pod应该能够通过简单地对my-service
进行名称查找来找到它( my-service.my-ns
也可以)。其他命名空间中的Pod必须将名称限定为my-service.my-ns
。这些名称将解析为为服务分配的群集IP。
Kubernetes还支持命名端口的DNS SRV(服务)记录。如果my-service.my-ns
服务具有名为http
的端口,且协议设置为TCP
,则可以对_http._tcp.my-service.my-ns
执行DNS SRV查询查询以发现该端口号,http
以及IP地址。Kubernetes DNS服务器是唯一的一种能够访问ExternalName
类型的Service
的方式。
对于应用的某些部分,比如前端,可能希望暴露一个服务到Kubernetes集群外部的一个外部IP地址上。Kubernetes的ServiceTypes
允许指定Service的类型,Service有ClusterIP
、NodePort
、LoadBalancer
和ExternalName
这四种类型,默认是ClusterIP
类型。也可以使用Ingress来暴露自己的服务。Ingress不是服务类型,但它充当集群的入口点。它可以将路由规则整合到一个资源中,因为它可以在同一IP地址下暴露多个服务。
通过集群的内部IP暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的ServiceType
。
下面创建mynginx-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mynginx-deployment
namespace: default
labels:
app: MyNginx
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: MyNginx
version: v1
template:
metadata:
labels:
app: MyNginx
version: v1
env: test
spec:
containers:
- name: nginx
image: nginx:alpine
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
创建Deployment以及查看Pod
信息:
修改3个pod
的index.html
内容为各自的Pod
名称:
创建mynginx-service-clusterip.yaml
:
apiVersion: v1
kind: Service
metadata:
name: mynginx-service-clusterip
namespace: default
spec:
type: ClusterIP
selector:
app: MyNginx
version: v1
ports:
- name: http
port: 88
targetPort: 80
创建mynginx-service-clusterip
,查看所有Service
和mynginx-service-clusterip
详细信息:
查看当前虚拟服务列表:
访问mynginx-service-clusterip
,可知负载均衡策略为轮询:
也可以通过域名访问,格式为
。下面要使用nslookup和dig命令,如果没有这两个命令则需要先安装bind-utils
包:
yum install -y bind-utils
查询DNS记录,其中的10.244.0.16
是其中一个CoreDNS的IP:
对该域名进行解析:
通过每个Node上的IP和静态端口(NodePort
)暴露服务。NodePort
服务会路由到ClusterIP
服务,这个ClusterIP
服务会自动创建。通过请求
,可以从集群的外部访问一个NodePort
服务。Service通过
和spec.clusterIp:spec.ports[*].port
而对外可见。
如果将type
字段设置为NodePort
,则Kubernetes控制平面将在--service-node-port-range
指定的范围内分配端口,默认值:30000-32767。每个节点将那个端口(每个节点上端口号相同)代理到服务中。通过.spec.ports[*].nodePort
字段指定要分配的NodePort
端口,需要注意可能发生的端口冲突且必须使用配置用于NodePort的范围内的有效的端口号。如果想指定特定的IP代理端口,可以将kube-proxy中的--nodeport-addresses
设置为特定的IP块。该标志采用逗号分隔的IP块列表(例如10.0.0.0/8,192.0.2.0/25
)来指定kube-proxy认为是此节点本地的IP地址范围。下面创建mynginx-service-nodeport.yaml
:
apiVersion: v1
kind: Service
metadata:
name: mynginx-service-nodeport
namespace: default
spec:
type: NodePort
selector:
app: MyNginx
version: v1
ports:
- name: http
port: 88
targetPort: 80
nodePort: 30008
创建mynginx-service-nodeport
,查看所有Service
和mynginx-service-nodeport
详细信息:
在两个节点上分别查看当前虚拟服务列表:
通过外部访问mynginx-service-nodeport
,这里在Windows上访问:
使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到NodePort
服务和ClusterIP
服务。
如果将type
字段设置为LoadBalancer
,可以使用支持外部负载均衡器的云提供商的服务,将为Service
提供负载均衡器。负载均衡器是异步创建的,被提供的负载均衡器的信息将会通过Service
的status.loadBalancer
字段发布出去。
apiVersion: v1
kind: Service
metadata:
name: mynginx-service-loadbalancer
namespace: default
spec:
selector:
app: MyNginx
version: v1
ports:
- name: http
port: 88
targetPort: 80
clusterIP: 10.0.171.239
loadBalancerIP: 78.11.24.19
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 146.148.47.155
某些云提供商允许设置loadBalancerIP
,将根据用户设置的loadBalancerIP
来创建负载均衡器,如果没有设置loadBalancerIP
,将会给负载均衡器指派一个临时IP。如果设置了loadBalancerIP
,但云提供商并不支持这种特性,那么设置的loadBalancerIP
值将会被忽略。由于云提供商的负载均衡服务都是第三方付费服务,这里就不演示了,更多LoadBalancer的内容可以参考官方文档。
通过返回CNAME
和它的值,可以将服务映射到externalName
字段的内容。例如,foo.bar.example.com
。没有任何类型代理被创建。需要CoreDNS 1.7或更高版本才能使用ExternalName
类型。
ExternalName
服务将服务映射到DNS名称,对于运行在集群外部的服务,ExternalName
服务通过返回外部服务的别名的方式来提供服务,可以通过spec.externalName
参数指定这些外部服务。下面创建mydb-service-externalname.yaml
:
apiVersion: v1
kind: Service
metadata:
name: mydb-service-externalname
namespace: default
spec:
type: ExternalName
externalName: test.mydatabase.com
该Service
将default
名称空间中的mydb-service-externalname
服务映射到test.mydatabase.com
,当查找主机mydb-service-externalname.default.svc.cluster.local
时,群集DNS服务返回CNAME
记录,其值为test.mydatabase.com
。访问mydb-service-externalname
的方式与其他服务的方式相同,但主要区别在于重定向发生在DNS级别,而不是通过代理或转发。如果以后要将外部服务移到群集内部,则可以启动其Pod并添加适当的selector或endpoint以及更改服务的类型。下面创建mydb-service-externalname
,查看所有Service
和mydb-service-externalname
详细信息:
查询DNS记录,这里test.mydatabase.com
是假设的所以找不到主机:
对该域名进行解析:
如果外部的IP路由到集群中一个或多个Node上,Kubernetes的Service
会被暴露给这些externalIPs
。通过外部IP(作为目的IP地址)进入到集群,到达Service
的端口上的流量,将会被路由到Service
的Endpoint上。externalIPs
不会被Kubernetes管理,它属于集群管理员的职责范畴。在Service
的spec中,externalIPs
可以同任意的ServiceType
来一起指定。 例如以下示例,mynginx-service-externalips
可以在80.11.12.10:80
(externalIP:port
)上被客户端访问:
apiVersion: v1
kind: Service
metadata:
name: mynginx-service-externalips
namespace: default
spec:
selector:
app: MyNginx
version: v1
ports:
- name: http
port: 88
targetPort: 80
externalIPs:
- 80.11.12.10
有时不需要或不想要负载均衡以及单独的Service IP,可以通过指定ClusterIP(spec.clusterIP
)的值为"None"
来创建Headless Service。可以使用headless Service与其他服务发现机制进行对接,而不必与Kubernetes的实现捆绑在一起。对这类headless Service 并不会分配Cluster IP,kube-proxy不会处理它们,而且平台也不会为它们进行负载均衡和路由。DNS如何实现自动配置,依赖于Service
是否定义了selector。
对定义了selector的Headless Service,Endpoint控制器在API中创建了Endpoints
记录,并且修改DNS配置返回A记录(地址),通过这个地址直接到达Service
的后端Pod
上。下面创建mynginx-headless-service-with-selector.yaml
:
apiVersion: v1
kind: Service
metadata:
name: mynginx-headless-service-with-selector
namespace: default
spec:
clusterIP: "None"
selector:
app: MyNginx
version: v1
ports:
- name: http
port: 88
targetPort: 80
创建mynginx-headless-service-with-selector
,查看所有Service
和mynginx-headless-service-with-selector
详细信息:
查询DNS记录:
对该域名进行解析:
对没有定义selector的Headless Service,Endpoint控制器不会创建Endpoints
记录。Dns配置分为两种情形,对ExternalName
类型的服务创建CNAME
记录,见上文ExternalName
类型部分内容。对其他三种类型,则为那些与当前Service
共享名称的所有Endpoints
对象创建一条记录。下面创建mynginx-headless-service-no-selector.yaml
:
apiVersion: v1
kind: Service
metadata:
name: mynginx-headless-service-no-selector
namespace: default
spec:
clusterIP: "None"
ports:
- name: http
port: 88
targetPort: 80
创建mynginx-headless-service-no-selector
,查看所有Service
和mynginx-headless-service-no-selector
详细信息:
创建mynginx-headless-service-no-selector-endpoints.yaml
:
apiVersion: v1
kind: Endpoints
metadata:
name: mynginx-headless-service-no-selector
subsets:
- addresses:
- ip: 10.244.1.8
- ip: 10.244.2.45
- ip: 10.244.2.46
ports:
- port: 80
通过以上配置创建Endpoint
对象,创建Endpoint
对象会添加到mynginx-headless-service-no-selector
中,查看mynginx-headless-service-no-selector
详细信息:
查询DNS记录:
对该域名进行解析:
protocol
值作为alpha功能。要启用此功能,集群管理员需要在apiserver上启用SCTPSupport
功能,例如--feature-gates = SCTPSupport = true,…
。启用此功能后,可以将Service,Endpoint,NetworkPolicy或Pod的protocol
字段设置为SCTP
。Kubernetes相应地为SCTP关联设置网络,就像为TCP连接一样。