1. Service 定义
在 Kubernetes 中,Service 是一种抽象,用于定义一组 Pod 的访问方式和网络策略。Service 允许将一组具有相同标签的 Pod 组合在一起,并为它们提供一个统一的访问入口,从而实现服务发现和负载均衡。
Service 的 yaml 格式的定义文件的完整内容如下:
apiVersion: v1
kind: Service
metadata:
name: string
namespace: string
labels:
- name: string
annotations:
- name: string
spec:
selector: []
type: string
clusterIP: string
sessionAffinity: string
ports:
- name: string
protocol: string
port: int
targetPort: int
nodePort: int
status:
loadBalancer:
ingress:
ip: string
hostname: string
YAML对 Service 的定义文件模板的各属性的说明如下表所示:
属性名称 | 取值类型 | 是否必选 | 取值说明 |
apiVersion | string | 必选 | v1 |
kind | string | 必选 | Service |
metadata | object | 必选 | 元数据 |
metadata.name | string | 必选 | Service 名称,须符合 RFC 1035 规范 |
metadata.namespace | string | 必选 | 名称空间,不指定时系统将使用名为“default”的命名空间。 |
metadata. labels | |||
2. Service 详解
2.1. service 使用示例
创建一个包含多个 nginx pod 副本的集合:
# webapp-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 2
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: webapp
image: nginx:1.26.2
ports:
- containerPort: 80
YAML创建:
kubectl apply -f webapp-deployment.yaml
ShellScript查看 pod ip 地址:
kubectl get pod -l app=webapp -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
webapp-78c9968fc-4zhsk 1/1 Running 0 30s 10.244.3.87 k8s-node-04-r-214 <none> <none>
webapp-78c9968fc-j5vc5 1/1 Running 0 30s 10.244.5.79 k8s-node-08-u-218 <none> <none>
ShellScript在集群内访问该 pod:
curl 10.244.3.87:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
ShellScript此时我们通过 pod 的 ip+端口可以访问到 nginx 服务,但 pod 的 ip 是动态变化的(例如发生故障后控制器重启了 pod),同时在运行的过程中 pod 副本的数量是可能变化的,因此对于客户端应用来说,要实现动态感知服务后端实例的变化,以及实现将请求发送到多个后端实例上的负载均衡机制,都会大大增加客户端系统实现的复杂度。
所以,Kubernetes 的 Service 就是用于解决这些问题的核心组件。通过 Service 的定义,可以对客户端应用屏蔽后端实例数量及 PodIP 地址的变化,通过负载均衡策略实现将请求转发到后端实例上,为客户端应用提供一个稳定的服务访问入口地址。Service 实现的是微服务架构中的几个核心功能:全自动的服务注册、服务发现、服务负载均衡等。
为上面创建的 pod 创建 service:
# Kubernetes 提供了一种快速的方法,即通过 kubectl expose 令来创建 Service
kubectl expose deployment webapp service
# 为了更精细化管理,可以通过 yaml 文件来创建 service
# webapp-service.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: webapp
ShellScript查看 service 的 ip 地址:
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webapp ClusterIP 10.100.238.224 <none> 80/TCP 23s
ShellScript访问 service:
curl 10.100.238.224:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
ShellScript同样可以访问到 nginx 服务,请求会被负载均衡的分发给两个 nginx 服务。
一个 Service 对应的“后端”由 Pod 的 IP 地址和容器端口号组成,即一个完整的”IP:Port”
访问地址,它在 Kubernetes 系统中被称作 Endpoint (端点)。通过查看 Service 的详细信息,可以看到其后端 Endpoint 列表:
kubectl describe service webapp
Name: webapp
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=webapp
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.100.238.224
IPs: 10.100.238.224
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.5.79:80,10.244.3.87:80
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
ShellScript2.2. service 负载均衡机制
2.1.1. kube-proxy 的代理模式
- iptables 模式(仅适用于 Linux 系统)
- ipvs 模式(仅适用于 Linux 系统)
- kernelspace 模式(适用于 windows 系统)
2.1.2. 会话保持机制
Service 支持通过设置 sessionAffinity 来实现基于客户端 IP 地址的会话保持机制,即首次将某个客户端来源IP地址发起的请求转发到后端的某个 Pod 上,之后从相同的客户端 IP地址发起的请求都将被转发到相同的后端 Pod上,配置参数为 service.spec.sessionAffinity,示例:
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
sessionAffinity: ClientIP
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: webapp
YAML同时,用户可以设置会话保持的最长时间,在此时间之后重置客户端来源 IP 地址的保持规则,配置参数为 service.spec.sessionAffinityConfig.clientIP.timeoutSeconds。例如下面的服务将会话保持时间设置为 10800s(3h):
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: webapp
YAML2.1.3. 流量策略
- spec.internalTrafficPolicy:内部流量策略,该策略是控制从内部源发起的流量的路由策略,可选配置项包括Cluster和 Local。
- Cluster:将从内部源发起的流量路由到所有处于 Ready 状态的 Endpoint。
- Local:将从内部源发起的流量仅路由到本地 Node 上处于 Ready 状态的Endpoint 如果本地 Node 上没有处于 Ready 状态的 Endpoint,kube-proxy 会丢弃该流量。
- spec.externalTrafficPolicy:外部流量策略,该策略是控制从外部源发起的流量的路由策略,可选配置项包括 Cluster 和 Local。
- Cluster:将从外部源发起的流量路由到所有处于 Ready 状态的 Endpoint。
- Local:将从外部源发起的流量仅路由到本地 Node 上处于 Ready 状态的Endpoint,如果本地 Node 上没有处于 Ready 状态的 Endpoint,,kube-proxy会丢弃该流量。
- ProxyTerminatingEndpoints:发往正在停止的 Endpoint 的流量策略
2.3. service 的多端口设置
service 可以设置多个端口来提供不同的服务,示例:
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- port: 80
targetPort: 80
name: web
- port: 81
targetPort: 81
name: management
selector:
app: webapp
YAML同一个端口使用不同的协议示例:
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "KubeDNS"
spec:
selector:
k8s-app: kube-dns
clusterIP: 169.169.0.100
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
YAML2.4. 将外部服务定义为 service
普通的 Service 通过标签选择器对后端 Endpoints 列表进行了一层抽象,如果后端的 Endpoint 不是由 Pod 副本集提供的,则 Service 还可以抽象定义任意其他服务,将一个 Kubernetes 集群外的已知服务定义为 Kubernetes 内的一个 Service,供集群中的其他应用访问,常见的应用场景如下:
- 已部署的一个集群外服务,例如数据库服务、缓存服务等。
- 其他 Kubernetes 集群的某个服务。
- 迁移过程中对某个服务进行 Kubernetes 内服务名访问机制的验证。
对于这些应用场景,用户在创建 Service 资源对象时不设置标签选择器(后端 Pod 也不存在),同时再定义一个与 Service 关联的 Endpoints 资源对象,在 Endpoints 对象中设置外部服务的 IP 地址和端口号,示例:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
---
kind: Endpoints
apiVersion: v1
metadata:
name: my-service
subsets:
- addresses:
- IP: 192.168.2.201
ports:
- port: 80
YAML将 ClusterIP 设置为 None 表示该 Service 不会分配一个固定的 ClusterIP 地址。这意味着该 Service 不会有一个固定的虚拟 IP 地址,而是通过 Endpoints 对象中定义的具体 IP 地址和端口来实现服务发现和负载均衡。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ClusterIP
clusterIP: None # clusterIP 设置为 None
ports:
- name: port
port: 80
---
apiVersion: v1
kind: Endpoints
metadata:
name: my-service # 名称必须和 Service 一致
subsets:
- addresses:
- IP: 192.168.2.201 # Service 将连接重定向到 endpoint
ports:
- name: port
port: 80
YAML2.5. service 的类型
service 的类型如下:
- ClusterIP:Kubernetes 默认会自动设置 Service 的虚拟 IP 地址,仅可被集群中的客户端应用访问。
- NodePort:将 Service 的端口号映射到每个 Node 的一个端口号上,这样集群中的任意 Node 都可以作为 Service 的访问入口地址,即 NodeIP:NodePort。
- LoadBalancer:将 Service 映射到一个已存在的负载均衡器的 IP 地址上,通常在公有云环境下使用。
- ExternalName:将 Service 映射为一个外部域名地址,通过 externalName 字段进行设置。
2.5.1. ClusterIP 类型
这是 Kubernetes 为 Service 设置 IP 地址的默认类型,kube-apiserver 服务的启动参数 –service-cluster-ip-range 设置了 Service 的 IP 地址范围,系统将自动从这个IP地址池中为
Service 分配一个可用的 IP 地址。
用户也可以手动指定一个 ClusterIP 地址,可以通过 spec.clusterlP 字段进行设置,需要确保该 IP 地址在可用的 ClusterIP 地址池内,并且没有被其他 Service 使用。示例如下:
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
type: ClusterIP
clusterIP: 10.100.238.224
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: webapp
YAML2.5.2. NodePort 类型
NodePort 类型用于将 Service 暴露到 Node 上,这样集群外的客户端可以通过 Node 的
IP地址访问集群中的 Service。使用 NodePort 类型时,系统会在 Node 上开启一个端口号,考虑到可能会有其他应用程序已经占用了一些端口号(例如操作系统的系统服务占用的端口号),要求在部署 Kubernetes 集群时,通过 kube-apiserver 的启动参数 –service-node-port-range 指定可以分配的 NodePort 端口号范围,默认值为[30000~32767]。
在Service的定义中,可以在每个竭口的配置中,通过字段 nodePort 指定一个值,如果不指定,Kubernetes 会基于端口号范围给每个端口号自动分配一个端口号。示例如下:
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30000
selector:
app: webapp
YAML2.5.3. LoadBalancer 类型
2.5.4. ExternalName 类型
ExternalName 类型的服务用于将集群外的服务定义为 Kubernetes 的集群的 Service,并且通过 externalName 字段指定外部服务的地址,可以使用域名或 IP 格式。集群中的客户端应用通过访问这个 Service 就能访问外部服务了。
使用域名示例:
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: www.baidu.com
YAML使用 ip+port 示例:见 2.4 小节。
2.6. Headless Service
Headless Service 是指服务没有入口访问地址无(ClusterIP 地址),kube-proxy 不会为其创建负载转发规则,而服务名(DNS域名)的解析机制根据该 Headless Service 是否设置了标签选择器而有所不同。下面通过几个例子来说明 Headless Service 的概念和用法。
2.6.1. Headless Service 设置了标签选择器
如果 Headless Service 设置了标签选择器,Kubernetes 将根据标签选择器查询后端 Pod列表,自动创建 Endpoints 列表,将服务名(DNS城名)的解析机制设置为:当客户端访问该服务名时,得到的是全部 Endpoints 列表,而不是一个单独的IP地址。示例如下:
# headless-service-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.26.2
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
clusterIP: None
selector:
app: nginx
YAML创建示例:
kubectl apply -f headless-service-test.yaml
ShellScript查看创建的 pod ip:
kubectl get pods -l app=nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-567cbc4d7c-5bs62 1/1 Running 0 2m25s 10.244.5.80 k8s-node-08-u-218 <none> <none>
nginx-567cbc4d7c-dwff7 1/1 Running 0 2m25s 10.244.2.83 k8s-node-01-c-211 <none> <none>
nginx-567cbc4d7c-sfnrd 1/1 Running 0 2m25s 10.244.3.89 k8s-node-04-r-214 <none> <none>
ShellScript查看 Headless Service 详细信息:
kubectl describe svc nginx
Name: nginx
Namespace: default
Labels: app=nginx
Annotations: <none>
Selector: app=nginx
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: None
IPs: None
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.5.80:80,10.244.2.83:80,10.244.3.89:80
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
ShellScriptFQDN 域名格式:(podname).(headless server name).namespace.svc.cluster.local
可以使用 nslookup 工具解析 ip 地址:
# 创建一个 ubuntu 系统 pod,并进入 pod
kubectl exec -it ubuntu-pod -- bash
# 解析 headless server 将会返回全部的 endpoint 的 ip 地址
nslookup nginx.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: nginx.default.svc.cluster.local
Address: 10.244.5.80
Name: nginx.default.svc.cluster.local
Address: 10.244.2.83
Name: nginx.default.svc.cluster.local
Address: 10.244.3.89
ShellScriptstatefulSet 创建的 Pod 有 DNS 地址,通过解析 Pod 的 DNS 可以返回 Pod 的 IP 地址而 deployment 创建的 Pod 没有 DNS,则无法解析。我们可以通过设置 hostname 和 subdomain 字段来自定义一个域名。示例如下:
# headless-service-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
hostname: nginx-01
subdomain: nginx
containers:
- name: nginx
image: nginx:1.26.2
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
clusterIP: None
selector:
app: nginx
YAMLnslookup nginx-01.nginx.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: nginx-01.nginx.default.svc.cluster.local
Address: 10.244.5.84
ShellScript官方参考文档:https://kubernetes.io/zh-cn/docs/concepts/services-networking/dns-pod-service