Service 的实现
将在集群中运行的应用通过同一个面向外界的端点公开出去,即使工作负载分散于多个后端也完全可行。
Kubernetes 中 Service 是 将运行在一个或一组 Pod 上的网络应用程序公开为网络服务的方法。
Kubernetes 中 Service 的一个关键目标是无需修改现有应用以使用某种不熟悉的服务发现机制。 可以在 Pod 集合中运行代码,无论该代码是为云原生环境设计的,还是被容器化的老应用。可以使用 Service 让一组 Pod 可在网络上访问,这样客户端就能与之交互。
如果使用 Deployment 来运行应用, Deployment 可以动态地创建和销毁 Pod。 在任何时刻,不知道有多少个这样的 Pod 正在工作以及它们健康与否; 可能甚至不知道如何辨别健康的 Pod。 Kubernetes Pod 的创建和销毁是为了匹配集群的预期状态。 Pod 是临时资源(你不应该期待单个 Pod 既可靠又耐用)。
每个 Pod 会获得属于自己的 IP 地址(Kubernetes 期待网络插件来保证这一点)。 对于集群中给定的某个 Deployment,这一刻运行的 Pod 集合可能不同于下一刻运行该应用的 Pod 集合。
Kubernetes 中的 Service
Service API 是 Kubernetes 的组成部分,它是一种抽象,帮助你将 Pod 集合在网络上公开出去。 每个 Service 对象定义端点的一个逻辑集合(通常这些端点就是 Pod)以及如何访问到这些 Pod 的策略。
考虑一个无状态的图像处理后端,其中运行 3 个副本(Replicas)。 这些副本是可互换的 —— 前端不需要关心它们调用的是哪个后端。 即便构成后端集合的实际 Pod 可能会发生变化,前端客户端不应该也没必要知道这些, 而且它们也不必亲自跟踪后端的状态变化。Service 抽象使这种解耦成为可能。
如果你的工作负载使用 HTTP 通信,你可能会选择使用 Ingress 来控制 Web 流量如何到达该工作负载。Ingress 不是一种 Service,但它可用作集群的入口点。 Ingress 能让你将路由规则整合到同一个资源内,这样你就能将工作负载的多个组件公开出去, 这些组件使用同一个侦听器,但各自独立地运行在集群中。
用于 Kubernetes 的 Gateway API 能够提供 Ingress 和 Service 所不具备的一些额外能力。 Gateway 是使用 CustomResourceDefinitions 实现的一系列扩展 API。 你可以添加 Gateway 到你的集群中,之后就可以使用它们配置如何访问集群中运行的网络服务
云原生服务发现
如果你想要在自己的应用中使用 Kubernetes API 进行服务发现,可以查询 API 服务器, 寻找匹配的 EndpointSlice 对象。 只要 Service 中的 Pod 集合发生变化,Kubernetes 就会为其更新 EndpointSlice。
对于非本地应用,Kubernetes 提供了在应用和后端 Pod 之间放置网络端口或负载均衡器的方法。
无论采用那种方式,你的负载都可以使用这里的服务发现机制找到希望连接的目标。
服务类型
对一些应用的某些部分(如前端),你可能希望将其公开于某外部 IP 地址, 也就是可以从集群外部访问的某个地址。
Kubernetes Service 类型允许指定你所需要的 Service 类型。
可用的 type
值及其行为有:
ClusterIP
- 通过集群的内部 IP 公开 Service,选择该值时 Service 只能够在集群内部访问。 这也是你没有为 Service 显式指定
type
时使用的默认值。 你可以使用 Ingress 或者 Gateway API 向公共互联网公开服务。 NodePort
- 通过每个节点上的 IP 和静态端口(
NodePort
)公开 Service。 为了让 Service 可通过节点端口访问,Kubernetes 会为 Service 配置集群 IP 地址, 相当于你请求了type: ClusterIP
的 Service。
LoadBalancer
- 使用云平台的负载均衡器向外部公开 Service。Kubernetes 不直接提供负载均衡组件; 你必须提供一个,或者将你的 Kubernetes 集群与某个云平台集成。
ExternalName
- 将服务映射到
externalName
字段的内容(例如,映射到主机名api.foo.bar.example
)。 该映射将集群的 DNS 服务器配置为返回具有该外部主机名值的CNAME
记录。 集群不会为之创建任何类型代理。
服务 API 中的 type
字段被设计为层层递进的形式 - 每层都建立在前一层的基础上。 但是,这种层层递进的形式有一个例外。 你可以在定义 LoadBalancer
Service 时禁止负载均衡器分配 NodePort
。
ClusterIP
[root@master svc]# kubectl get po -l app=nginx-cx -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-cx1-5c86fddfc5-4wdvg 1/1 Running 0 4h4m 172.16.84.190 node-1 <none> <none> nginx-cx1-5c86fddfc5-8qgqb 1/1 Running 0 4h4m 172.16.167.141 node <none> <none> nginx-cx1-5c86fddfc5-jgtt5 1/1 Running 0 4h4m 172.16.247.37 node-2 <none> <none> [root@master svc]# cat svc.yaml apiVersion: v1 kind: Service metadata: name: nginx-cx1 spec: type: ClusterIP #sessionAffinity: ClientIP ports: - port: 80 targetPort: 80 selector: app: nginx-cx [root@master svc]# kubectl apply -f svc.yaml service/nginx-cx1 created [root@master svc]# kubectl describe svc nginx-cx1 Name: nginx-cx1 Namespace: default Labels: <none> Annotations: <none> Selector: app=nginx-cx Type: ClusterIP IP Family Policy: SingleStack IP Families: IPv4 IP: 10.107.129.30 IPs: 10.107.129.30 Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 172.16.167.141:80,172.16.247.37:80,172.16.84.190:80 Session Affinity: None Events: <none>
NodePort
[root@master svc]# cat svc-1.yaml apiVersion: v1 kind: Service metadata: name: nginx-ro spec: type: NodePort #sessionAffinity: ClientIP ports: - port: 80 targetPort: 80 nodePort: 30080 selector: app: nginx-ro [root@master svc]# kubectl get po -l app=nginx-ro -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-deployment-ro-56d78cc78d-5sskn 1/1 Running 0 5h32m 172.16.84.188 node-1 <none> <none> nginx-deployment-ro-56d78cc78d-77m8k 1/1 Running 0 5h32m 172.16.247.35 node-2 <none> <none> nginx-deployment-ro-56d78cc78d-kvhsw 1/1 Running 0 5h32m 172.16.84.187 node-1 <none> <none> [root@master svc]# kubectl apply -f svc-1.yaml service/nginx-ro created [root@master svc]# kubectl describe svc kubernetes nginx-cx1 nginx-ro riodseconds-exec riodseconds-exec-1 [root@master svc]# kubectl describe svc nginx-ro Name: nginx-ro Namespace: default Labels: <none> Annotations: <none> Selector: app=nginx-ro Type: NodePort IP Family Policy: SingleStack IP Families: IPv4 IP: 10.107.71.223 IPs: 10.107.71.223 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 30080/TCP Endpoints: 172.16.247.35:80,172.16.84.187:80,172.16.84.188:80 Session Affinity: None External Traffic Policy: Cluster Events: <none>
externalName
[root@master dpment]# cat dpment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment namespace: qa labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-svc namespace: qa spec: selector: app: nginx ports: - port: 80 targetPort: 80 [root@master dpment]# kubectl apply -f dpment.yaml deployment.apps/nginx-deployment created service/nginx-svc created [root@master dpment]# kubectl get po -n qa NAME READY STATUS RESTARTS AGE nginx-deployment-86dcfdf4c6-2lj7t 1/1 Running 0 14s nginx-deployment-86dcfdf4c6-bbnbm 1/1 Running 0 14s nginx-deployment-86dcfdf4c6-k6w7l 1/1 Running 0 14s [root@master dpment]# kubectl get svc -n qa NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-svc ClusterIP 10.108.11.102 <none> 80/TCP 20s [root@master dpment]# kubectl describe svc -n qa Name: nginx-svc Namespace: qa Labels: <none> Annotations: <none> Selector: app=nginx Type: ClusterIP IP Family Policy: SingleStack IP Families: IPv4 IP: 10.108.11.102 IPs: 10.108.11.102 Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 172.16.247.38:80,172.16.84.129:80,172.16.84.191:80 Session Affinity: None Events: <none> [root@master svc]# cat svc-2.yaml apiVersion: v1 kind: Service metadata: name: nginx-ro spec: type: ExternalName externalName: nginx-svc.qa.svc.cluster.local #sessionAffinity: ClientIP ports: - port: 80 targetPort: 80 nodePort: 30080 selector: app: nginx-ro [root@master svc]# kubectl apply -f svc-2.yaml service/nginx-ro configured [root@master svc]# kubectl describe svc nginx-ro Name: nginx-ro Namespace: default Labels: <none> Annotations: <none> Selector: app=nginx-ro Type: ExternalName IP Families: <none> IP: IPs: <none> External Name: nginx-svc.qa.svc.cluster.local Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 172.16.247.35:80,172.16.84.187:80,172.16.84.188:80 Session Affinity: None Events: <none> [root@master ~]# kubectl run -it test --image=busybox --restart=Never -- /bin/sh If you don't see a command prompt, try pressing enter. / # nslookup nginx-svc.qa.svc.cluster.local Server: 10.96.0.10 Address: 10.96.0.10:53 Name: nginx-svc.qa.svc.cluster.local Address: 10.108.11.102 / # curl nginx-svc.qa.svc.cluster.local /bin/sh: curl: not found / # wget nginx-svc.qa.svc.cluster.local Connecting to nginx-svc.qa.svc.cluster.local (10.108.11.102:80) saving to 'index.html' index.html 100% |**********************************************************************| 612 0:00:00 ETA 'index.html' saved / # exit [root@master ~]# kubectl delete po test pod "test" deleted [root@master ~]# kubectl run -it test --image=busybox --restart=Never -- /bin/sh If you don't see a command prompt, try pressing enter. / # wget nginx-cx2 Connecting to nginx-cx2 (10.108.11.102:80) saving to 'index.html' index.html 100% |**********************************************************************| 612 0:00:00 ETA 'index.html' saved