Kubernetes指南:深入理解service
Service是Kubernetes的核心概念,通过创建Service, 可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上;
定义详解
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
selector:
app: MyApp
type: NodePort/LoadBalancer/ClusterIP
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
属性名称 | 类型 | 是否必选 | 取值说明 |
---|---|---|---|
spec.selector | list | Required | 选择具有特定Label标签的Pod作为管理范围 |
spec.type | string | Required | Service的类型,指定Service的访问方式,默认值为ClusterIP,包括ClusterIP/NodePort/LoadBalancer |
spec.clusterIP | string | 虚拟服务IP地址 | |
spec.sessionAffinity | string | 是否支持Session | |
spec.ports[] | list | Service需要暴露的端口列表 | |
spec.ports[].name | string | 端口名称 | |
spec.ports[].protocol | string | 端口协议,支持TCP和UDP,默认值为TCP | |
spec.ports[].port | int | 服务监听的端口号 | |
spec.ports[].targetPort | int | 需要转发到后端Pod的端口号 | |
spec.ports[].nodePort | int | spec.type=NodePort时,指定映射到物理机的端口号 | |
status | object | 当spec.type=LoadBalancer时,设置外部负载均衡器的地址,用于公有云环境 | |
status.loadBalancer | object | 外部负载均衡器 | |
status.loadBalancer.ingress | object | 外部负载均衡器 | |
status.loadBalancer.ingress.ip | string | 外部负载均衡器IP地址 | |
status.loadBalancer.ingress.hostname | string | 外部负载均衡器主机名 |
spec.type服务类型
ClusterIP:通过集群的内部IP暴露服务,选择该值时服务只能够在集群内部访问。 这也是默认的 ServiceType。
NodePort:通过每个节点上的IP和静态端口(NodePort)暴露服务。 NodePort服务会路由到自动创建的ClusterIP服务。 通过请求 <节点 IP="">:<节点端口>,你可以从集群的外部访问一个 NodePort服务。节点端口>节点>
LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的NodePort服务和ClusterIP服务上。
ExternalName:通过返回CNAME和对应值,可以将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。 无需创建任何类型代理。
基本用法
多端口Service
1.一个容器应用也可能提供多个端口的服务,那么在Service的定 义中也可以相应地设置为将多个端口对应到多个应用服务
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
selector:
app: MyApp
type: NodePort/LoadBalancer/ClusterIP
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
2.两个端口号使用了不同的4层协议—TCP和UDP:
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
selector:
app: MyApp
type: NodePort/LoadBalancer/ClusterIP
ports:
- name: dns
protocol: UDP
port: 53
- name: dns-tcp
protocol: TCP
port: 53
外部服务Service
在某些环境中,应用系统需要将一个外部数据库作为后端服务进行连接, 或将另一个集群或Namespace中的服务作为服务的后端,这时可以通过创建一个无Label Selector的Service来实现;
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
通过无Label Selector的Service定义创建的是一个不带标签选择器的Service,即无法选择后端的Pod, 系统不会自动创建Endpoint,因此需要手动创建一个和该 Service同名的Endpoint,用于指向实际的后端访问地址;
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
namespace: default
subsets:
- addresses:
- IP: 1.2.3.4
ports:
port: 80
基本原理
kube-proxy
Service只是一个概念,而真正将Service的作用落实的是它背后的kube-proxy服务进程。 只有理解了kube-proxy的原理和机制,我们才能真正理解Service背后的实现逻辑;
两种模式
iptables
在集群中的Service和Pod大量增加以后,iptables中的规则会急速膨胀, 导致性能显著下降,在某些极端情况下甚至会出现规则丢失的情况,并且这种故障难以重现与排查;
IPVS(IP Virtual Server)
iptables与IPVS虽然都是基于Netfilter实现的,但因为定位不同, 二者有着本质的差别:iptables是为防火墙而设计的; IPVS则专门用于高性能负载均衡,并使用更高效的数据结构(Hash表), 允许几乎无限的规模扩张,因此被kube-proxy采纳为第三代模式;
-
为大型集群提供了更好的可扩展性和性能;
-
支持比iptables更复杂的复制均衡算法(最小负载、最少连接、加权等);
-
支持服务器健康检查和连接重试等功能;
-
可以动态修改ipset的集合,即使iptables的规则正在使用这个集合;
集群外部访问Pod或Service
Pod和Service都是Kubernetes集群范围内的虚拟概念,所以集群外的客户端系统无法通过Pod的IP地址或者Service的虚拟IP地址和虚拟端口号访问它们;
为了让外部客户端可以访问这些服务,可以将Pod或Service的端口号映射到宿主机,以使客户端应用能够通过物理机访问容器应用;
将容器应用的端口号映射到物理机
1.通过设置容器级别的hostPort,将容器应用的端口号映射到物理机上:
containerPort: 8080
hostPort: 8081
2.通过设置Pod级别的hostNetwork=true,该Pod中所有容器的端口号都将被直接映射到物理机上。 在设置hostNetwork=true时需要注意,在容器的ports定义部分如果不指定hostPort,则默认hostPort等于containerPort,如果指定了hostPort,则hostPort必须等于containerPort的值
将Service的端口号映射到物理机
1.通过设置nodePort映射到物理机,同时设置Service的类型为NodePort:
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
type: NodePort
ports:
- port: 8080
targetPort: 8080
nodePort: 8081
selector:
app: webapp
2.通过设置LoadBalancer映射到云服务商提供的LoadBalancer地址。 这种用法仅用于在公有云服务提供商的云平台上设置Service的场景;
Ingress:HTTP 7层路由机制
Service的表现形式为 IP:Port,即工作在TCP/IP层;
对于基于HTTP的服务来说,不同的URL地址经常对应到不同的后端服务或者虚拟服务器(Virtual Host), 这些应用层的转发机制仅通过Kubernetes的Service机制是无法实现的;
从Kubernetes 1.1版本开始新增Ingress资源对象,用于将不同URL的访问请求转发到后端不同的Service,以实现HTTP层的业务路由机制;
Kubernetes使用了一个Ingress策略定义和一个具体的Ingress Controller, 两者结合并实现了一个完整的Ingress负载均衡器;
使用Ingress进行负载分发时,Ingress Controller基于Ingress规则将客户端请求直接转发到Service对应的后端Endpoint(Pod)上, 这样会跳过kube-proxy的转发功能,kube-proxy不再起作用。 如果Ingress Controller提供的是对外服务,则实际上实现的是边缘路由器的功能;
为使用Ingress,需要创建Ingress Controller(带一个默认backend服务)和Ingress策略设置来共同完成;
Ingress Controller
在定义Ingress策略之前,需要先部署Ingress Controller,以实现为所有后端Service都提供一个统一的入口。
Ingress Controller需要实现基于不同HTTP URL向后转发的负载分发规则,并可以灵活设置7层负载分发策略。
如果公有云服务商能够提供该类型的HTTP路由LoadBalancer, 则也可设置其为Ingress Controller;
在Kubernetes中,Ingress Controller将以Pod的形式运行, 监控API Server的/ingress接口后端的backend services,如果Service发生变化, 则Ingress Controller应自动更新其转发规则;
Ingress策略
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
tls:
- hosts:
- https-example.foo.com
secretName: testsecret-tls
rules:
- host: https-example.foo.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service1
port:
number: 80
Ingress策略配置
为了实现灵活的负载分发策略,Ingress策略可以按多种方式进行配置;
几种常见的Ingress转发策略:
1.转发到单个后端服务上
基于这种设置,客户端到Ingress Controller的访问请求都将被转发到后端的唯一Service上, 在这种情况下Ingress无须定义任何rule;
2.同一域名下,不同的URL路径被转发到不同的服务上
这种配置常用于一个网站通过不同的路径提供不同的服务的场景, 例如/web表示访问Web页面,/api表示访问API接口,对应到后端的两个服务, 通过Ingress的设置很容易就能将基于URL路径的转发规则定义出来;
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
rules:
- host: https-example.foo.com
http:
paths:
- path: /web
backend:
service:
name: web-service
port: 80
- path: /api
backend:
service:
name: api-service
port: 8081
3.不同的域名(虚拟主机名)被转发到不同的服务上
这种配置常用于一个网站通过不同的域名或虚拟主机名提供不同服务的场景, 例如foo.bar.com域名由service1提供服务,bar.foo.com域名由service2提供服务;
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
spec:
rules:
- host: foo.bar.com
http:
paths:
- backend:
service:
name: service1
port: 80
- host: bar.foo.com
http:
paths:
- backend:
service:
name: service2
port: 80
4.不使用域名的转发规则
这种配置用于一个网站不使用域名直接提供服务的场景, 此时通过任意一台运行ingress-controller的Node都能访问到后端的服务;
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
rules:
- http:
paths:
- path: /demo
backend:
service:
name: web-service
port: 8080
TLS安全设置
为了Ingress提供HTTPS的安全访问,可以为Ingress中的域名进行TLS安全证书的设置。
设置的步骤:
(1)创建自签名的密钥和SSL证书文件;
(2)将证书保存到Kubernetes中的一个Secret资源对象上;
(3)将该Secret对象设置到Ingress中;
refs
https://kubernetes.io/zh/docs/concepts/services-networking/service/#service-resource