【k8s 系列】k8s 学习十九,service 2
之前我们简单的了解一下 k8s 中 service 的玩法,今天我们来分享一下 service 涉及到的相关细节,我们开始吧
为什么要有 服务 Service?
因为服务可以做到让外部的客户端不用关心服务器的数量,服务内部有多少个 pod,也可以正常连接到服务器,并可以正常进行业务处理
咱们可以举一个例子,客户端 --> 前端 --> 后台
客户端将流量打到前端的 Service 上,前端的 Service 会将流量给到任意一个 pod 上面,然后 流量进而打到后台服务的 Service 上,最终请求到后台服务的任意 pod 上面
这个时候,客户端无需知道到底是哪个 pod 提供的服务,也无需知道提供 pod 的地址,只需要知道前端服务的 地址和端口即可
新建一个 demo 服务
咱们可以简单些一个 Service 的 yaml 文件,然后部署起来,对于 Service 资源管控哪一些 pod ,我们仍然是使用 标签来进行管控的
例如我们的 minikube 环境中有这样 3 个 标签为 appxmt-kubia
的 pod
我们可以这样写 Service 清单:
kubia-service.yaml
apiVersion: v1 kind: Service metadata: name: kubia-service spec: ports: - port: 80 targetPort: 8080 selector: app: xmt-kubia
此处的 selector 选择器,和前面我们说到的 ReplicaSet 的做法是一致的,此处是 Service 暴露一个 80 端口,映射到 pod 的 8080 端口
我们也可以暴露多个端口,但是暴露多个端口的话,必须要写 name ,例如这样
运行 kubectl create -f kubia-service.yaml ,部署一个 Service 资源
kubectl get svc 可以看到如下 svc 的资源列表
可以通过在 pod 内部访问 svc 的 ip 来查看是否可以请求成功
通过命令选中任意 pod,在 pod 中执行 curl 命令,请求 http 接口
kubectl exec kubia-rs-sxgcq -- curl -s 10.106.228.254
果然是可以请求通过的
当然,我们也是可以通过完全进入到 pod 内部,来访问 Service 的地址
kubectl exec -it kubia-rs-sxgcq -- /bin/sh
此处的 Cluster IP , 意思是集群内部的 IP,只用于在集群内部进行访问的,当前我们创建的服务的目的就是,集群内部的其他 pod ,能够通过访问这个 Service 的 IP 10.106.228.254 来访问 该 Service 管控的一组 pod
通过日志,我们可以看出实际上请求成功了的,我们使用任意一个 pod,来请求 Service,然后 Service 来将流量打到自己管控的一组 pod 中,最终得到响应,可以画个图理解一波
Endpoint
看了上面 Service 和 pod 的关系,给人的感觉是不是 Service 和 pod 好像是直连的,其实并不是这样的,其实 他俩之间还有一个关键的资源,那就是 Endpoint
Endpoints 这个资源就是暴露一个服务的 IP 地址和端口的列表
kubectl get endpoints kubia-service
Endpoints: 172.17.0.6:8080,172.17.0.7:8080,172.17.0.8:8080
如上我们可以看到 Endpoints 暴露了服务的 IP 地址和端口列表
当有客户端连接到服务的时候,服务代理就会选择这些 IP 和 端口 对中的一个发送请求
暴露服务
上面的做法都是在 k8s 集群内部暴露服务的 IP ,**这个 IP 是虚拟 IP ,只能在集群内部访问,**且需要访问对应的端口才行
那么,我们如何暴露服务的端口,供外部的客户端,或者是外部的服务进行调用呢?
类似于这样的请求
关于 Service 资源暴露的方式有如下 3 种:
- NodePort
- LoadBalance
- Ingress
三种方式各有优劣,下面我们来详细的看看
service 之 NodePort
NodePort ,看到命名我们就可以知道是在每个集群节点都会打开一个端口,外部客户端可以访问集群节点的 IP + PORT 就可以访问到我们暴露的服务,进而可以将流量请求到 服务管控的一组任意 pod 上
我们可以看看当前我的实验环境的 service 类型
上述 service 类型都是 Cluster IP 的,因此,我们需要将现有 svc 修改成 NodePort 或者重新创建一个新的 service ,就可以把这里的 TYPE 设置为 NodePort
编写 NodePort 类型的 Service
kubia-service-nodeport.yaml
apiVersion: v1 kind: Service metadata: name: kubia-svc spec: type: NodePort ports: - port: 80 targetPort: 8080 nodePort: 31200 selector: app: xmt-kubia
- 编写基本的 NodePort 类型的 Service 资源清单
- 指定服务类型为 NodePort
- 注意此处是 ports , 也就是说可以配置多个端口映射,我们也可以暴露多个端口,之前的文章有说到,暴露多个端口的时候,是需要写 name 的,name 记得需要符合 k8s 的命名规范
- 指定暴露的端口为 31200 (外部客户端通过 IP + 31200 就能够访问到服务管理的一组 pod 的,pod 的端口是 8080)
- 注意,如果这里不指定 nodePort端口,那么 k8s 会随机分配一个端口,默认端口范围是 30000 - 32767, 我们也可以修改 k8s 默认端口范围,修改位置为:
/etc/kubernetes/manifests/kube-apiserver.yaml
运行后查看效果
这个时候,该服务已经暴露了 31200 端口了,我们可以在我们的 window 上通过 telnet ip port
的方式来访问我们这台云服务实验环境,但是请记得在云服务器的防火墙处打开 31200 端口
我们通过外部客户端请求工作节点的 IP + 暴露的 port,是这样的流程:
外部客户端流量从节点的 31200 端口打入,会被重定向到 service 管控的一组任意 pod 上
service 之 LoadBalance
我们可以想一下上面的 NodePort 的方式有什么缺点?
上面暴露了服务的端口,但是我们访问的时候需要指定工作节点的 ip + port,**如果我们指定的工作节点出现了故障,那么外部客户端请求服务就会出现无响应,**除非客户端知道其他正常工作节点的 IP
因此,这个时候,负载均衡器就上场了
创建 LoadBalance 类型的 服务,只需要将上述 NodePort 的资源清单中 type: NodePort
修改成 type: LoadBalance
即可
LoadBalance
LoadBalance 负载均衡器,我们可以放在节点的前面,确保外部客户端发送的请求能够发送到健康的节点上,并且绝对不会将外部的请求发送到状态异常的工作节点上
使用 LoadBalance 之后,上述的流程就变成了这个样子的:
这个时候,对于客户端,只需要访问固定的 IP + port 就可以轻松的访问到健康的工作节点上的 pod 了
service 之 Ingress
现在我们一起来看看暴露服务的第三种方法,使用 ingress 控制器
为什么要有 ingress ?
往上面看,使用 LoadBalance 的时候,LoadBalance 需要一个公网的 IP,且只能提供给一个 Service
那么如果是有多个 Service 的时候,我们就要部署多个 LoadBalance 负载均衡器 了,因此 ingress 就出马了
ingress 可以做什么呢?
ingress 可以做到只需要一个公网 IP,就可以为多个 Service 提供准入和访问,我们可以理解为 ingress 像 nginx 一样通过路由来识别给多个服务的请求
简单流程可以是这个样子的:
这样,使用 ingress 就比使用 LoadBalance 要方便多了,但是具体使用的时候,还是看自己的需求是什么,来选择合适的方式
写一个 ingress 的demo
写 ingress demo 之前,我们先确认我们实验环境中是否开启了 ingress 控制器
minikube addons list
我的环境是开启的,如果没有开启的话,可以执行命令开启 ingress 控制
minikube addons enable ingress
开启时候,我们可以查看到有关于 ingress 的 pod
kubectl get po --all-namespaces
开始创建 ingress 资源
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: kubia-ingress spec: ingressClassName: nginx rules: - host: hello.example.com http: paths: - path: / pathType: Prefix backend: service: name: kubia-nodeport-svc port: number: 80
- ingress 资源,填写的 apiVersion 为
networking.k8s.io/v1
,如果 k8s 是 1.17 之前的,那么 apiVersion 需要填写extensions/v1beta1
ingressClassName: nginx
指定规则会加入到 nginx 中- rules ,我们可以看出这个规则是可以添加多条的,这也证明了 ingress 是可以通过多个路由来给多个服务提供访问的
创建 ingress 资源,查看效果
现在,我们就可以在任意客户端(前提是能够访问的通 k8s 这台机器或集群),配置一下本地 host,hello.example.com 指向 ingress 的 ip 即可
配置完成后,客户端可以向 hello.example.com
发起请求了,可以愉快地玩耍
咱最后来看看外部客户端访问 ingress 地址后原理是怎样的?
如上图,我们可以看出,外部客户端访问域名:hello.example.com
- 外部客户端先去找 DNS 拿到 hello.example.com 对应的 IP(ingress 控制器的 ip)
- 客户端向 ingress 控制器 发送 http 请求,host 中会带上 域名,ingress 会去查询 该域名对应的服务(找 ingress 资源查),进而找到 endpoint ip 和 port 映射列表
- 最终 ingress 控制器,直接把流量打到 service 管控的一组任意 pod 上面
这里的流量是 ingress 控制器直接打到 pod 上,而不是通过 service 转发
今天就到这里,学习所得,若有偏差,还请斧正
欢迎点赞,关注,收藏
朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力
好了,本次就到这里
技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。
我是阿兵云原生,欢迎点赞关注收藏,下次见~