官网地址:https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/
Kubernetes 中 Service 是 将运行在一个或一组 Pod 上的网络应用程序公开为网络服务的方法。
Kubernetes 中 Service 的一个关键目标是让你无需修改现有应用程序就能使用不熟悉的服务发现机制。 你可以在 Pod 中运行代码,无需顾虑这是为云原生世界设计的代码,还是为已容器化的老应用程序设计的代码。 你可以使用 Service 让一组 Pod 在网络上可用,让客户端能够与其交互。
如果你使用 Deployment 来运行你的应用, Deployment 可以动态地创建和销毁 Pod。不管是这一刻还是下一刻, 你不知道有多少个这样的 Pod 正在工作以及健康与否;你可能甚至不知道那些健康的 Pod 是如何命名的。 Kubernetes Pod 被创建和销毁以匹配集群的预期状态。 Pod 是临时资源(你不应该期待单个 Pod 既可靠又耐用)。
每个 Pod 获取其自己的 IP 地址(Kubernetes 期待网络插件确保 IP 地址分配)。 对于集群中给定的 Deployment,这一刻运行的这组 Pod 可能不同于下一刻运行应用程序的那组 Pod。
这导致了一个问题: 如果一组 Pod(称为“后端”)为集群内的其他 Pod(称为“前端”)提供功能, 那么前端如何找出并跟踪要连接的 IP 地址,以便前端可以使用提供工作负载的后端部分?
对一些应用的某些部分(如前端),可能希望将其暴露给 Kubernetes 集群外部的 IP 地址。
Kubernetes ServiceTypes
允许指定你所需要的 Service 类型。
可用的 type
值及其行为有:
ClusterIP
通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。 这也是你没有为服务显式指定 type
时使用的默认值。 你可以使用 Ingress 或者 Gateway API 向公众暴露服务。
通过每个节点上的 IP 和静态端口(NodePort
)暴露服务。 为了让节点端口可用,Kubernetes 设置了集群 IP 地址,这等同于你请求 type: ClusterIP
的服务。
使用云提供商的负载均衡器向外部暴露服务。 Kubernetes 不直接提供负载均衡组件;你必须提供一个,或者将你的 Kubernetes 集群与云提供商集成。
将服务映射到 externalName
字段的内容(例如,映射到主机名 api.foo.bar.example
)。 该映射将集群的 DNS 服务器配置为返回具有该外部主机名值的 CNAME
记录。 无需创建任何类型代理。
服务 API 中的 type
字段被设计为层层递进的形式 - 每个级别都建立在前一个级别基础上。 并不是所有云提供商都如此严格要求的,但 Kubernetes 的 Service API 设计要求满足这一逻辑。
此默认服务类型从你的集群中有意预留的 IP 地址池中分配一个 IP 地址。
其他几种服务类型在 ClusterIP
类型的基础上进行构建。
如果你定义的服务将 .spec.clusterIP
设置为 "None"
,则 Kubernetes 不会分配 IP 地址。有关详细信息,请参阅 headless 服务。
在 Service
创建的请求中,可以通过设置 spec.clusterIP
字段来指定自己的集群 IP 地址。 比如,希望替换一个已经已存在的 DNS 条目,或者遗留系统已经配置了一个固定的 IP 且很难重新配置。
用户选择的 IP 地址必须合法,并且这个 IP 地址在 service-cluster-ip-range
CIDR 范围内, 这对 API 服务器来说是通过一个标识来指定的。 如果 IP 地址不合法,API 服务器会返回 HTTP 状态码 422,表示值不合法。
阅读避免冲突, 了解 Kubernetes 如何协助降低两种不同服务试图使用相同 IP 地址的风险和影响。
[root@k8smaster service]# cat nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app.kubernetes.io/name: proxy
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
name: http-web-svc
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app.kubernetes.io/name: proxy
ports:
- name: name-of-service-port
protocol: TCP
port: 80
targetPort: http-web-svc
[root@k8smaster service]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 4m44s 10.244.185.193 k8snode2 <none> <none>
[root@k8smaster service]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 172m
nginx-service ClusterIP 10.98.127.82 <none> 80/TCP 4m55s
[root@k8smaster service]#
[root@k8smaster service]# curl 10.98.127.82
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
如果你将 type
字段设置为 NodePort
,则 Kubernetes 控制平面将在 --service-node-port-range
标志指定的范围内分配端口(默认值:30000-32767)。 每个节点将那个端口(每个节点上的相同端口号)代理到你的服务中。 你的服务在其 .spec.ports[*].nodePort
字段中报告已分配的端口。
使用 NodePort 可以让你自由设置自己的负载均衡解决方案, 配置 Kubernetes 不完全支持的环境, 甚至直接暴露一个或多个节点的 IP 地址。
对于 NodePort 服务,Kubernetes 额外分配一个端口(TCP、UDP 或 SCTP 以匹配服务的协议)。 集群中的每个节点都将自己配置为监听分配的端口并将流量转发到与该服务关联的某个就绪端点。 通过使用适当的协议(例如 TCP)和适当的端口(分配给该服务)连接到所有节点, 你将能够从集群外部使用 type: NodePort
服务。
如果需要特定的端口号,你可以在 nodePort
字段中指定一个值。 控制平面将为你分配该端口或报告 API 事务失败。 这意味着你需要自己注意可能发生的端口冲突。 你还必须使用有效的端口号,该端口号在配置用于 NodePort 的范围内。
以下是 type: NodePort
服务的一个示例清单,它指定了一个 NodePort 值(在本例中为 30007):
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
selector:
app.kubernetes.io/name: MyApp
ports:
# 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
- port: 80
targetPort: 80
# 可选字段
# 默认情况下,为了方便起见,Kubernetes 控制平面会从某个范围内分配一个端口号(默认:30000-32767)
nodePort: 30007
type: NodePort
服务自定义 IP 地址配置你可以在集群中设置节点以使用特定 IP 地址来提供 NodePort 服务。 如果每个节点都连接到多个网络(例如:一个网络用于应用程序流量,另一个网络用于节点和控制平面之间的流量), 你可能需要执行此操作。
如果你要指定特定的 IP 地址来代理端口,可以将 kube-proxy 的 --nodeport-addresses
标志或 kube-proxy 配置文件的等效 nodePortAddresses
字段设置为特定的 IP 段。
此标志采用逗号分隔的 IP 段列表(例如 10.0.0.0/8
、192.0.2.0/25
)来指定 kube-proxy 应视为该节点本地的 IP 地址范围。
例如,如果你使用 --nodeport-addresses=127.0.0.0/8
标志启动 kube-proxy, 则 kube-proxy 仅选择 NodePort 服务的环回接口。 --nodeport-addresses
的默认值是一个空列表。 这意味着 kube-proxy 应考虑 NodePort 的所有可用网络接口。 (这也与早期的 Kubernetes 版本兼容。)
说明:
此服务呈现为 <NodeIP>:spec.ports[*].nodePort
和 .spec.clusterIP:spec.ports[*].port
。 如果设置了 kube-proxy 的 --nodeport-addresses
标志或 kube-proxy 配置文件中的等效字段, 则 <NodeIP>
将是过滤的节点 IP 地址(或可能的 IP 地址)。
[root@k8smaster service]# cat nginx2.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-2
labels:
app.kubernetes.io/name: proxy2
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
name: http-web-svc
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service2
spec:
type: NodePort
selector:
app.kubernetes.io/name: proxy2
ports:
- name: name-of-service-port
protocol: TCP
port: 80
targetPort: http-web-svc
nodePort: 30007
在使用支持外部负载均衡器的云提供商的服务时,设置 type
的值为 "LoadBalancer"
, 将为 Service 提供负载均衡器。 负载均衡器是异步创建的,关于被提供的负载均衡器的信息将会通过 Service 的 status.loadBalancer
字段发布出去。
实例:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
clusterIP: 10.0.171.239
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 192.0.2.127
来自外部负载均衡器的流量将直接重定向到后端 Pod 上,由云提供商决定如何进行负载平衡。
要实现 type: LoadBalancer
的服务,Kubernetes 通常首先进行与请求 type: NodePort
服务等效的更改。 cloud-controller-manager 组件随后配置外部负载均衡器以将流量转发到已分配的节点端口。
你可以将负载均衡服务配置为忽略分配节点端口, 前提是云提供商实现支持这点。
某些云提供商允许设置 loadBalancerIP
。 在这些情况下,将根据用户设置的 loadBalancerIP
来创建负载均衡器。 如果没有设置 loadBalancerIP
字段,将会给负载均衡器指派一个临时 IP。 如果设置了 loadBalancerIP
,但云提供商并不支持这种特性,那么设置的 loadBalancerIP
值将会被忽略掉。
说明:
针对 Service 的 .spec.loadBalancerIP
字段已在 Kubernetes v1.24 中被弃用。
此字段的定义模糊,其含义因实现而异。它也不支持双协议栈联网。 此字段可能会在未来的 API 版本中被移除。
如果你正在集成某云平台,该平台通过(特定于提供商的)注解为 Service 指定负载均衡器 IP 地址, 你应该切换到这样做。
如果你正在为集成到 Kubernetes 的负载均衡器编写代码,请避免使用此字段。 你可以与 Gateway 而不是 Service 集成, 或者你可以在 Service 上定义自己的(特定于提供商的)注解,以指定等效的细节。
特性状态: Kubernetes v1.26 [stable]
默认情况下,对于 LoadBalancer 类型的服务,当定义了多个端口时, 所有端口必须具有相同的协议,并且该协议必须是受云提供商支持的协议。
当服务中定义了多个端口时,特性门控 MixedProtocolLBService
(在 kube-apiserver 1.24 版本默认为启用) 允许 LoadBalancer 类型的服务使用不同的协议。
说明:
可用于负载均衡服务的协议集由你的云提供商决定,他们可能在 Kubernetes API 强制执行的限制之外另加一些约束。
特性状态: Kubernetes v1.24 [stable]
你可以通过设置 spec.allocateLoadBalancerNodePorts
为 false
对类型为 LoadBalancer 的服务禁用节点端口分配。 这仅适用于直接将流量路由到 Pod 而不是使用节点端口的负载均衡器实现。 默认情况下,spec.allocateLoadBalancerNodePorts
为 true
, LoadBalancer 类型的服务继续分配节点端口。 如果现有服务已被分配节点端口,将参数 spec.allocateLoadBalancerNodePorts
设置为 false
时,这些服务上已分配置的节点端口不会被自动释放。 你必须显式地在每个服务端口中删除 nodePorts
项以释放对应端口。
特性状态: Kubernetes v1.24 [stable]
对于 type
设置为 LoadBalancer
的 Service, spec.loadBalancerClass
字段允许你不使用云提供商的默认负载均衡器实现, 转而使用指定的负载均衡器实现。
默认情况下,.spec.loadBalancerClass
未设置,如果集群使用 --cloud-provider
配置了云提供商, LoadBalancer
类型服务会使用云提供商的默认负载均衡器实现。
如果设置了 .spec.loadBalancerClass
,则假定存在某个与所指定的类相匹配的负载均衡器实现在监视服务变化。 所有默认的负载均衡器实现(例如,由云提供商所提供的)都会忽略设置了此字段的服务。.spec.loadBalancerClass
只能设置到类型为 LoadBalancer
的 Service 之上,而且一旦设置之后不可变更。
.spec.loadBalancerClass
的值必须是一个标签风格的标识符, 可以有选择地带有类似 “internal-vip
” 或 “example.com/internal-vip
” 这类前缀。 没有前缀的名字是保留给最终用户的。
在混合环境中,有时有必要在同一(虚拟)网络地址块内路由来自服务的流量。
在水平分割 DNS 环境中,你需要两个服务才能将内部和外部流量都路由到你的端点(Endpoints)。
如要设置内部负载均衡器,请根据你所使用的云运营商,为服务添加以下注解之一:
选择一个标签。
apiVersion: v1
kind: Pod
metadata:
name: mysql
labels:
app.kubernetes.io/name: mysqlproxy
spec:
containers:
- name: mysql
image: mysql
ports:
- containerPort: 3306
name: http-mysql-svc
env:
-name: MYSQL_ROOT_PASSWORD
value: "123456"
---
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
type: NodePort
selector:
app.kubernetes.io/name: mysqlproxy
ports:
- name: name-of-service-port
protocol: TCP
port: 3360
targetPort: http-mysql-svc
nodePort: 30008
[root@k8smaster service]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql 1/1 Running 0 71s 10.244.185.194 k8snode2 <none> <none>
nginx 1/1 Running 0 33m 10.244.185.193 k8snode2 <none> <none>
nginx-2 1/1 Running 0 22m 10.244.249.1 k8snode1 <none> <none>
[root@k8smaster service]# kubectl exec -it mysql -- bash
root@mysql:/# mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.27 MySQL Community Server - GPL
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
在Window的sqlyog进行连接
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: mynginx
spec:
replicas: 3
selector:
matchLabels:
app: mynginx
template:
metadata:
labels:
app: mynginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: mynginx
spec:
type: NodePort
selector:
app: mynginx
ports:
- name: name-of-service-port
protocol: TCP
port: 80
targetPort: 80
nodePort: 30009
[root@k8smaster service]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql 1/1 Running 0 14m 10.244.185.194 k8snode2 <none> <none>
nginx 1/1 Running 0 46m 10.244.185.193 k8snode2 <none> <none>
nginx-2 1/1 Running 0 36m 10.244.249.1 k8snode1 <none> <none>
nginx-deployment-6dcb8ffbc7-bjdth 1/1 Running 0 27s 10.244.249.3 k8snode1 <none> <none>
nginx-deployment-6dcb8ffbc7-gg4v9 1/1 Running 0 27s 10.244.185.195 k8snode2 <none> <none>
nginx-deployment-6dcb8ffbc7-wtkhq 1/1 Running 0 27s 10.244.249.2 k8snode1 <none> <none>