一 Service 简介
1.1 Service 概念
Service 是 Kubernetes 的核心概念, 通过创建 Service, 可以为一组具有相同功能的容器应用提供一个统一的入口地址, 并且将请求负载分发到后端的各个容器应用上.
1.2 Service 定义详解
- apiVersion: v1 # 必须, API 版本
- kind: Service # 必须, 类型为 Service
- metadata: # 必须, 元数据
- name: string # 必须, Service 名称
- namespace: string # 必须, 命名空间, 默认为 default
- labels: # 自定义标签属性列表
- - name: string
- annotations: # 自定义注解属性列表
- - name: string
- spec: # 必须, 详细描述
- selector: [] # 必须, Label Selector 配置
- type: ClusterIP # 必须, Serice 类型, 详见如下
- sessionAffinity: string # 虚拟服务 IP 地址, 当选择 type=ClusterIP 时, 若不指定, 则系统进行自动分配; 当 type=LoadBalancer 时, 需要指定
- ports: #Service 需要暴露的端口列表
- - name: string # 端口名称
- protocol: # 端口协议, 支持 TCP 和 UDP, 默认为 TCP
- port: int # 服务监听的端口号
- targetPort: 8080 # 需要转发到后端 Pod 的端口号
- nodePort: int # 当 spec.type=NodePort 时, 指定映射到物理机的端口号
- status: # 当 spec.type=LoadBalancer 时, 设置外部负载均衡的地址, 用于公有云
- loadBalancer: # 外部负载均衡器
- ingress: # 外部负载均衡器
- ip: string # 外部负载均衡器的 IP 地址
- hostname: string # 外部负载均衡器的主机名
spec.type:Service 的类型, 指定 Service 的访问方式, 默认为 ClusterIP.
ClusterIP: 虚拟的服务 IP 地址, 该地址用于 Kubernetes 集群内部的 Pod 访问, 在 Node 上 kube-proxy 通过设置的 iptables 规则进行转发;
NodePort: 使用宿主机的端口, 使能够访问各 Node 的外部客户端通过 Node 的 IP 地址和端口号就能访问服务;
LoadBalancer: 使用外接负载均衡器完成到服务的负载分发, 需要在 spec.status.loadBalancer 字段指定外部负载均衡器的 IP 地址, 并同时定义 nodePort 和 clusterIP, 用于公有云.
二 Service 基本使用
2.1 Service 的基本用法
一般来说, 对外提供服务的应用程序需要通过某种机制来实现, 对于容器应用最简便的方式就是通过 TCP/IP 机制及监听 IP 和端口号来实现.
示例: 定义一个提供 web 服务的 RC, 由两个 Tomcat 容器副本组成, 每个容器都通过 containerPort 设置提供服务的端口号为 8080.
- [root@k8smaster01 study]# cat webapp-rc.YAML
- apiVersion: v1
- kind: ReplicationController
- metadata:
- name: webapp
- spec:
- replicas: 2
- template:
- metadata:
- name: webapp
- labels:
- App: webapp
- spec:
- containers:
- - name: webapp
- image: tomcat
- ports:
- - containerPort: 8080
- [root@k8smaster01 study]# kubectl create -f webapp-rc.YAML
- [root@k8smaster01 study]# kubectl get pods -l App=webapp -o YAML | grep podIP
- podIP: 172.24.9.88
- podIP: 172.24.9.199
- [root@k8smaster01 study]# curl 172.24.9.88:8080
直接通过 Pod 的 IP 地址和端口号可以访问到容器应用内的服务, 但是 Pod 的 IP 地址是不可靠的, 例如当 Pod 所在的 Node 发生故障时, Pod 将被 Kubernetes 重新调度到另一个 Node,Pod 的 IP 地址将发生变化.
如果容器应用本身是分布式的部署方式, 通过多个实例共同提供服务, 就需要在这些实例的前端设置一个负载均衡器来实现请求的分发. Kubernetes 中的 Service 就是用于解决这些问题的核心组件.
Service 示例: 以如上 webapp 应用为例, 为了让客户端应用访问到两个 Tomcat Pod 实例, 可以创建一个 Service 来提供服务. Kubernetes 提供了一种快速的方法, 即通过 kubectl expose 命令来创建 Service.
- [root@k8smaster01 study]# kubectl expose rc webapp
- [root@k8smaster01 study]# kubectl get svc | grep -E 'NAME|webapp'
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- webapp ClusterIP 10.10.10.51 <none> 8080/TCP 45s
- [root@k8smaster01 study]# curl 10.10.10.51:8080 # 测试访问
提示: 如上 Service 地址 10.10.10.51:8080 的访问被自动负载分发到后端两个 Pod.
Service 示例 2: 通过 Service 配置文件暴露服务.
- [root@k8smaster01 study]# vi webappsvc.YAML
- apiVersion: v1
- kind: Service
- metadata:
- name: webappservice
- spec:
- ports:
- - port: 8081
- targetPort: 8080
- selector:
- App: webapp
- [root@k8smaster01 study]# kubectl create -f webappsvc.YAML
- [root@k8smaster01 study]# kubectl get svc | grep -E 'NAME|webappser'
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- webappservice ClusterIP 10.10.10.83 <none> 8081/TCP 22s
提示: 如上 Service 定义中的关键字段是 ports 和 selector. 本例中 ports 定义部分指定了 Service 所需的虚拟端口号为 8081, 由于与 Pod 容器端口号 8080 不一样, 所以需要再通过 targetPort 来指定后端 Pod 的端口号. selector 定义部分设置的是后端 Pod 所拥有的 label:App=webapp.
[root@k8smaster01 study]# curl 10.10.10.83:8081 # 访问测试
Service 负载分发策略: RoundRobin 和 SessionAffinity
RoundRobin: 轮询模式, 即轮询将请求转发到后端的各个 Pod 上.
SessionAffinity: 基于客户端 IP 地址进行会话保持的模式, 即第 1 次将某个客户端发起的请求转发到后端的某个 Pod 上, 之后从相同的客户端发起的请求都将被转发到后端相同的 Pod 上.
在默认情况下, Kubernetes 采用 RoundRobin 模式对客户端请求进行负载分发, 同时可以通过设置 service.spec.sessionAffinity=ClientIP 来启用 SessionAffinity 策略. 这样, 同一个客户端 IP 发来的请求就会被转发到后端固定的某个 Pod 上了.
通过 Service 的定义, Kubernetes 实现了一种分布式应用统一入口的定义和负载均衡机制. Service 还可以进行其他类型的设置, 例如设置多个端口号, 直接设置为集群外部服务, 或实现为 Headless Service(无头服务) 模式.
2.2 多端口 Service
有时一个容器应用也可能提供多个端口的服务, 那么在 Service 的定义中也可以相应地设置为将多个端口对应到多个应用服务.
示例 1: 如下, Service 设置了两个端口号, 并且为每个端口号都进行了命名.
- [root@k8smaster01 study]# vi twoportservice.YAML
- apiVersion: v1
- kind: Service
- metadata:
- name: webapp
- spec:
- ports:
- - port: 8080
- targetPort: 8080
- name: Web
- - port: 8005
- targetPort: 8005
- name: management
- selector:
- App: webapp
- [root@k8smaster01 study]# vi kubednsservice.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
2.3 外部服务 Service
在某些环境中, 应用系统需要将一个外部数据库, 另一个集群或 Namespace 中的服务作为服务的后端, 则可通过创建一个无 Label Selector 的 Service 来实现.
- [root@k8smaster01 study]# vi noselectorservice.YAML
- apiVersion: v1
- kind: Service
- metadata:
- name: my-service
- spec:
- ports:
- - protocol: TCP
- port: 80
- targetPort: 80
- [root@k8smaster01 study]# kubectl create -f noselectorservice.YAML
如上定义创建的是一个不带标签选择器的 Service, 即无法选择后端的 Pod, 系统不会自动创建 Endpoint, 因此需要手动创建一个和该 Service 对应的 Endpoint, 用于指向实际的后端访问地址.
如下所示的 Endpoint 的定义文件:
- [root@k8smaster01 study]# vi noselectorendpoint.YAML
- apiVersion: v1
- kind: Endpoints
- metadata:
- name: my-service
- subsets:
- - addresses:
- - IP: 47.96.145.131
- ports:
- - port: 80
- [root@k8smaster01 study]# kubectl create -f noselectorendpoint.YAML
- [root@k8smaster01 study]# kubectl get svc | grep -E 'NAME|my-service'
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- my-service ClusterIP 10.10.10.211 <none> 80/TCP 3s
- [root@k8smaster01 study]# curl 10.10.10.211
提示: 如上所示, 访问没有标签选择器的 Service 和带有标签选择器的 Service 一样, 请求将会被路由到由用户手动定义的后端 Endpoint 上.
三 Headless Service
3.1 无头服务简介
在某些应用场景中, 若需要人为指定负载均衡器, 不使用 Service 提供的默认负载均衡的功能, 或者应用程序希望知道属于同组服务的其他实例. Kubernetes 提供了 Headless Service 来实现这种功能, 即不为 Service 设置 ClusterIP(入口 IP 地址), 仅通过 Label Selector 将后端的 Pod 列表返回给调用的客户端.
此场景中, Service 就不再具有一个特定的 ClusterIP 地址, 对其进行访问将获得包含 Label"app=nginx" 的全部 Pod 列表, 然后客户端程序自行决定如何处理这个 Pod 列表.
例如, StatefulSet 就是使用 Headless Service 为客户端返回多个服务地址的.
对于 "去中心化" 类的应用集群, Headless Service 非常适合.
3.2 Nginx 场景实验
通过对 Headless Service 搭建 Nginx 集群, 从而自动实现应用集群的创建.
- [root@k8smaster01 study]# vi nginx-service.YAML # 创建 Service
- apiVersion: v1
- kind: Service
- metadata:
- labels:
- name: nginx-svc
- name: nginx-svc
- spec:
- ports:
- - protocol: TCP
- port: 80
- targetPort: 80
- selector:
- name: nginx-demo # 定义 selector
- clusterIP: None
- [root@k8smaster01 study]# kubectl create -f nginx-service.YAML
- [root@k8smaster01 study]# vi nginx-deployment.YAML
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- labels:
- name: nginx-demo
- name: nginx-demo
- spec:
- replicas: 2
- selector:
- matchLabels:
- name: nginx-demo
- template:
- metadata:
- labels:
- name: nginx-demo
- spec:
- containers:
- - name: nginx
- image: nginx:1.7.9
- ports:
- - containerPort: 80
- name: Web
- [root@k8smaster01 study]# kubectl create -f nginx-deployment.YAML
- [root@k8smaster01 study]# kubectl create -f nginx-service.YAML
- [root@k8smaster01 study]# kubectl get svc -o wide
- [root@k8smaster01 study]# kubectl get pods -o wide
- [root@k8smaster01 study]# nslookup nginx-svc.default.svc.cluster.local 10.10.190.170
提示: 由上可知, 通过解析 SVC 的地址, 直接解析出来的为 Pod 的 IP.
来源: https://www.cnblogs.com/itzgr/p/12461800.html