云原生工程师-6.k8s四层负载均衡-Service

五.k8s四层负载均衡-Service

个人博客

5.1-什么是Service

5.1.1-Service作用

在 kubernetes 中,Pod 是有生命周期的,如果 Pod 重启它的 IP 很有可能会发生变化。如果我们的服 务都是将 Pod 的 IP 地址写死,Pod 挂掉或者重启,和刚才重启的 pod 相关联的其他服务将会找不到它所 关联的 Pod,为了解决这个问题,在 kubernetes 中定义了 service 资源对象,Service 定义了一个服务 访问的入口,客户端通过这个入口即可访问服务背后的应用集群实例,service 是一组 Pod 的逻辑集合, 这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector 实现的。

在这里插入图片描述

5.1.2-Service概念

​ service 是一个固定接入层,客户端可以通过访问 service 的 ip 和端口访问到 service 关联的后端pod,这个 service 工作依赖于在 kubernetes 集群之上部署的一个附件,就是kubernetes 的 dns 服务(不同 kubernetes 版本的 dns 默认使用的也是不一样的,1.11 之前的版本使用的是 kubeDNs,较新的版本使用的是 coredns),service 的名称解析是依赖于 dns 附件的,因此在部署完 k8s 之后需要再部署 dns附件,kubernetes 要想给客户端提供网络功能,需要依赖第三方的网络插件(flannel,calico 等)。每个 K8s 节点上都有一个组件叫做 kube-proxy,kube-proxy 这个组件将始终监视着 apiserver 中有关service 资源的变动信息,需要跟 master 之上的 apiserver 交互,随时连接到 apiserver 上获取任何一个与 service 资源相关的资源变动状态,这种是通过 kubernetes 中固有的一种请求方法 watch(监视)来实现的,一旦有 service 资源的内容发生变动(如创建,删除),kube-proxy 都会将它转化成当前节点之上的能够实现 service 资源调度,把我们请求调度到后端特定的 pod 资源之上的规则,这个规则可能是 iptables,也可能是 ipvs,取决于 service 的实现方式。

5.1.3-Service工作原理

​ k8s 在创建 Service 时,会根据标签选择器 selector(lable selector)来查找 Pod,据此创建与 Service 同名的 endpoint 对象,当 Pod 地址发生变化时,endpoint 也会随之发生变化,service 接收前 端 client 请求的时候,就会通过 endpoint,找到转发到哪个 Pod 进行访问的地址。(至于转发到哪个节 点的 Pod,由负载均衡 kube-proxy 决定)

k8s中有三类网络地址

1.Node network :宿主机网络

2.Pod network :pod网络,创建的pod具有的IP

#node和pod网络是物理存在的,节点网络地址配置在节点接口至上,pod网络地址配置在pod资源至上。/硬件,软件模拟

3.Cluster Network:集群地址,虚拟的地址,没有接口,只是在service规则中

service 只要创建完成,我们就可以直接解析它的服务名,每一个服务创建完成后都会在集群 dns 中 动态添加一个资源记录,添加完成后我们就可以解析了,资源记录格式是:

SVC_NAME.NS_NAME.DOMAIN.LTD.

服务名.命名空间.域名后缀

集群默认的域名后缀是 svc.cluster.local.

如:在web 命名空间创建一个nginx-web服务的解析为

nginx-web.web.svc.cluster.local

5.2-Service 代理:kube-proxy 组件详解

5.2.1-kube-proxy 组件介绍

service 只是把应用对外提供服务的方式做了抽象,真正的应用跑在 Pod 中的container 里,我们的请求转到 nodes 对应的 nodePort 上,就是通过 kube-proxy 实现的

kube-proxy 部署在 k8s 的每一个 Node 节点上,是 Kubernetes 的核心组件,我们创建一个 service 的时候,kube-proxy 会在 iptables 中追加一些规则,为我们实现路由与负载均衡的功能。

在 k8s1.8 之前,kube-proxy 默认使用的是 iptables 模式,通过各个 node 节点上的iptables 规则来实现 service 的负载均衡,但是随着 service 数量的增大,iptables 模由于线性查找匹配、全量更新等特点,其性能会显著下降。

从 k8s 的 1.8 版本开始,kube-proxy 引入了 IPVS 模式,IPVS 模式与 iptables 同样基于Netfilter,但是采用的 hash表,因此当 service 数量达到一定规模时,hash 查表的速度优势就会显现出来,从而提高 service 的服务性能。

service 是一组 pod 的服务抽象,相当于一组 pod 的 LB,负责将请求分发给对应的 pod。

service 会 为这个 LB 提供一个 IP,一般称为 cluster IP。kube-proxy 的作用主要是负责 service 的实现,具体来 说,就是实现了内部从 pod 到 service 和外部的从 node port 向 service 的访问。

1、kube-proxy 其实就是管理 service 的访问入口,包括集群内 Pod 到 Service 的访问和集群外访问
service。
2、kube-proxy 管理 sevice 的 Endpoints,该 service 对外暴露一个 Virtual IP,也可以称为是Cluster IP, 集群内通过访问这个 Cluster IP:Port 就能访问到集群内对应的 serivce 下的 Pod。

5.2.2-Kube-proxy三种工作模式

1.Userspace方式:

在这里插入图片描述

​ Client Pod 要访问 Server Pod 时,它先将请求发给内核空间中的 service iptables 规则,由它再 将请求转给监听在指定套接字上的 kube-proxy 的端口,kube-proxy 处理完请求,并分发请求到指定Server Pod 后,再将请求转发给内核空间中的 service ip,由 service iptables 将请求转给各个节点中 的 Server Pod。

​ 该模式的问题:客户端请求先进入内核空间的,又进去用户空间访问 kube-proxy,由 kube-proxy 封装完成后再进去内核空间的 iptables,再根据 iptables 的规则分发给各节点的用户空间 的 pod。由于其需要来回在用户空间和内核空间交互通信,因此效率很差。在 Kubernetes 1.1 版本之 前,userspace 是默认的代理模型。

2.iptables方式:

在这里插入图片描述

​ 客户端 IP 请求时,直接请求本地内核 service ip,根据 iptables 的规则直接将请求转发到到各 pod 上,因为使用 iptable NAT 来完成转发,也存在不可忽视的性能损耗。另外,如果集群中存上万的 Service/Endpoint,那么 Node 上的 iptables rules 将会非常庞大,性能还会再打折。1.2为默认模式

3.ipvs方式:

在这里插入图片描述

​ 客户端请求时到达内核空间时,根据 ipvs 的规则直接分发到各 pod 上。kube-proxy 会监视 Kubernetes Service 对象和 Endpoints,调用 netlink 接口以相应地创建 ipvs 规则并定期与 Kubernetes Service 对 象和 Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务时,流量将被重定向到其 中一个后端 Pod。与 iptables 类似,ipvs 基于 netfilter 的 hook 功能,但使用哈希表作为底层数据结 构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能.1.11默认

​ ipvs 为负载均衡算法提供了更多选项:rr:轮询,lc:最小链接数,dh:目标哈希,sh:源哈希,sed:最短期望延迟,nq:不排队调度.

​ 如果某个服务后端 pod 发生变化,标签选择器适应的 pod 又多一个,适应的信息会立即反映到apiserver 上,而 kube-proxy 一定可以 watch 到 etc 中的信息变化,而将它立即转为 ipvs 或者 iptables中的规则,这一切都是动态和实时的,删除一个 pod 也是同样的原理。如图:

在这里插入图片描述

​ 以上不论哪种,kube-proxy 都通过 watch 的方式监控着 apiserver 写入 etcd 中关于 Pod 的最新状 态信息,它一旦检查到一个 Pod 资源被删除了或新建了,它将立即将这些变化,反应再 iptables 或 ipvs 规则中,以便 iptables 和 ipvs 在调度 Clinet Pod 请求到 Server Pod 时,不会出现 Server Pod 不存在 的情况.k8s1.11 以后,service 默认使用 ipvs 规则,若 ipvs 没有被激活,则降级使用 iptables 规 则.

5.2.3-kube-proxy 生成的 iptables 规则分析

在 k8s 创建的 service,虽然有 ip 地址,但是 service 的 ip 是虚拟的,不存在物理机上的,是在 iptables 或者 ipvs 规则里的。

1.service的type是clusterIP类型

[root@master2 sc]# kubectl get svc
NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
aliyun-mysql   ClusterIP   10.104.231.127   <none>        3306/TCP   31m
kubernetes     ClusterIP   10.96.0.1        <none>        443/TCP    5d20h
[root@master2 sc]# iptables -t nat -L |grep 10.104.231.127
KUBE-SVC-3Z5SCIS6QZ4IIO7R  tcp  --  anywhere             10.104.231.127       /* default/aliyun-mysql cluster IP */ tcp dpt:mysql
KUBE-MARK-MASQ  tcp  -- !192.168.0.0/16       10.104.231.127       /* default/aliyun-mysql cluster IP */ tcp dpt:mysql

创建的 service,会通过 kube-proxy 在 iptables 中生成一个规则,来实现 流量路由,一系列目标为 KUBE-SVC-xxx 链的规则,每条规则都会匹配某个目标 ip 与端口。也就是说访问某个 ip:port 的请求会由 KUBE-SVC-xxx 链来处理。这个目标 IP 其实就是 service ip。

2.sercice的type是nodePort类型

[root@master2 sc]# kubectl get svc -n kubernetes-dashboard
NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)         AGE
dashboard-metrics-scraper   ClusterIP   10.98.37.51     <none>        8000/TCP        3d23h
kubernetes-dashboard        NodePort    10.101.159.76   <none>        443:30443/TCP   3d23h
[root@master2 sc]# iptables -t nat -L | grep 10.101.159.76
KUBE-SVC-CEZPIJSAUFW5MYPQ  tcp  --  anywhere             10.101.159.76        /* kubernetes-dashboard/kubernetes-dashboard cluster IP */ tcp dpt:https
KUBE-MARK-MASQ  tcp  -- !192.168.0.0/16       10.101.159.76        /* kubernetes-dashboard/kubernetes-dashboard cluster IP */ tcp dpt:https
[root@master2 sc]# iptables -t nat -S | grep 30443
-A KUBE-NODEPORTS -p tcp -m comment --comment "kubernetes-dashboard/kubernetes-dashboard" -m tcp --dport 30443 -j KUBE-EXT-CEZPIJSAUFW5MYPQ

5.3-Service服务发现:coredns

5.3.1-什么是coredns

CoreDNS 其实就是一个 DNS 服务,而 DNS 作为一种常见的服务发现手段,所以很多开源项目以及 工程师都会使用 CoreDNS 为集群提供服务发现的功能,Kubernetes 就在集群中使用 CoreDNS 解决服务 发现的问题。 作为一个加入 CNCF(Cloud Native Computing Foundation)的服务, CoreDNS 的实现 非常简单。

在线下载配置文件地址是:https://docs.tigera.io/calico/3.25/getting-started/kubernetes/quickstart

kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/tigera-operator.yaml

kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/custom-resources.yaml

[root@master2 sc]# kubectl get svc |grep kubernetes
kubernetes     ClusterIP   10.96.0.1        <none>        443/TCP          5d20h

5.4-Service的应用

5.4.1-Service的资源清单
apiVersion: v1
kind: Service
metada:
  name:
  labels:
    key: value
spec:
  clusterIP: #动态分配地址,创建指定无法修改
  ports: #定义 service 端口,用来和后端 pod 建立联系
  selector <map[string]string> #通过标签选择器选择关联的 pod 有哪些
  sessionAffinity <string> 
  sessionAffinityConfig <Object> 
#service 在实现负载均衡的时候还支持 sessionAffinity,sessionAffinity 什么意思?会话联系,默认是 none,随机调度的(基于 iptables 规则调度的);如果我们定义sessionAffinity 的 client ip,那就表示把来自同一客户端的 IP 请求调度到同一个 pod上
  type <string> #定义 service 的类型
#四种类型:
#1.ExternalName: 类似于仅主机模式,容器访问外部,无selctor无端口无endpoint
#示例
kind: Service 
apiVersion: v1 
metadata: 
  name: my-service 
  namespace: prod 
spec: 
  type: ExternalName 
  externalName: my.database.example.com
#将 prod 名称空间中的 my-service 服务映射到 my.database.example.com,当查询主机 my-service.prod.svc.cluster.local 时,群集 DNS 将返回值为my.database.example.com 的 CNAME 记录。
#2.ClusterIP:通过集群内部IP暴露服务,只能内部访问,默认的
#3.Nodeport: 通过node的IP和容器的端口映射,实现访问
#Client----->NodeIP:NodePort----->Service Ip:ServicePort----->PodIP:ContainerPort。
#4.LoadBalancer: 使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和ClusterIP 服务。 
5.4.2-Service的端口
#service.spec.ports
spec:
  ports:
  - name:
    nodePort: #宿主机上的映射端口
    port: #service的端口,集群内部
    protocol: #协议
    targetPort: #pod上的端口从 port 和 nodePort 上来的流量,经过 kube-proxy 流入到后端 pod的 targetPort 上,最后进入容器。
5.4.3-ClusterIP 类型
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-web
spec:
  replicas: 2
  selector:
    matchLabels:
      demo: web
      version: v1
  template:
    metadata:
      labels:
        demo: web
        version: v1
    spec:
      containers:
      - name: demo-web-deploy
        image: docker.io/library/nginx:1.23.3
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: demo-web-sc
  labels:
    demo: web
spec:
  type: ClusterIP
  ports:
  - port: 80 #集群内部中暴露的端口
    protocol: TCP
    targetProt: 80 #容器端口
  selector:
    demo: web
[root@master2 sc]# kubectl get svc -l demo=web
NAME          TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
demo-web-sc   ClusterIP   10.105.73.16   <none>        80/TCP    69s
#在 k8s 控制节点访问 service 的 ip:端口就可以把请求代理到后端 pod
[root@master2 sc]# kubectl describe svc demo-web-sc|grep End
Endpoints:         192.168.137.84:80,192.168.180.41:80
#访问192.168.137.84和service IP是一样的;

#service 可以对外提供统一固定的 ip 地址,并将请求重定向至集群中的 pod。其中“将请求重定向至集群中的 pod”就是通过 endpoint 与 selector 协同工作实现。selector 是用于选择 pod,由selector 选择出来的 pod 的 ip 地址和端口号,将会被记录在 endpoint 中。endpoint 便记录了所有 pod的 ip 地址和端口号。当一个请求访问到 service 的 ip 地址时,就会从 endpoint 中选择出一个 ip 地址和端口号,然后将请求重定向至 pod 中。具体把请求代理到哪个 pod,需要的就是 kube-proxy 的轮询实现的。service 不会直接到 pod,service 是直接到 endpoint 资源,就是地址加端口,再由 endpoint 再关联到 pod。
#client→service→selector选择pod的ip和端口,记录到endpoint→kube-proxy轮询→service→client
5.4.4-NodePort类型
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-web
spec:
  replicas: 2
  selector:
    matchLabels:
      demo: web
      version: v1
  template:
    metadata:
      labels:
        demo: web
        version: v1
    spec:
      containers:
      - name: demo-web-deploy
        image: docker.io/library/nginx:1.23.3
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: demo-web-sc
  labels:
    demo: web
spec:
  type: NodePort
  ports:
  - port: 80 #集群内部中暴露的端口
    protocol: TCP
    targetPort: 80 #容器端口
    nodePort: 30080
  selector:
    demo: web
#更新启动验证结果
[root@master2 deploy]# kubectl apply -f nginx.yaml
deployment.apps/demo-web unchanged
service/demo-web-sc configured
[root@master2 deploy]# kubectl get svc
NAME          TYPE           CLUSTER-IP     EXTERNAL-IP                        PORT(S)        AGE
demo-nginx    ExternalName   <none>         kubernetes.default.svc.nginx.com   80/TCP         5d17h
demo-web-sc   NodePort       10.105.73.16   <none>                             80:30080/TCP   17h
kubernetes    ClusterIP      10.96.0.1      <none>                             443/TCP        5d19h
[root@master2 deploy]# curl localhost:30080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

服务走向:

Client-→node ip:30080->service ip:80-→pod ip:container port

5.4.5-ExternalName类型

ExternalName类型类似于仅主机模式,容器访问外部,应用场景:跨名称空间访问

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-busybox
spec:
  replicas: 1
  selector:
    matchLabels:
      demo: busybox
      version: v1
  template:
    metadata:
      labels:
        demo: busybox
        version: v1
    spec:
      containers:
      - name: demo-busybox
        image: docker.io/library/busybox:latest
        command: ["/bin/sh","-c","sleep 36000"]
---
apiVersion: v1
kind: Service
metadata:
  name: demo-busybox-svc
  labels:
    demo: busybox
spec:
  type: ExternalName
  externalName: demo-web-sc.default.svc.cluster.local
  ports:
  - port: 80 #集群内部中暴露的端口
    protocol: TCP
    targetPort: 80 #容器端口
  selector:
    demo: busybox
[root@master2 sc]# kubectl exec -it demo-busybox-bc9fd6585-wvwn2 -- sh
/ # wget -q -o - demo-web-sc.default.svc.cluster.local
/ # wget -q -o - demo-web-sc.default.svc.cluster.local
wget: can't open 'index.html': File exists
5.4.6-K8s最佳实践:映射外部MySQL服务

1.在集群外部署一个mysql服务。以下为docker-compse部署

[root@my cfsql]# cat docker-compose.yaml
version: "3.3"

services:
  cfdb:
    image: mysql:5.7
    volumes:
      - ./db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: #自定义
    ports:
      - "3306:3306"
[root@my cfsql]# docker-compose ps
    Name                 Command             State                 Ports
--------------------------------------------------------------------------------------
cfsql_cfdb_1   docker-entrypoint.sh mysqld   Up      0.0.0.0:3306->3306/tcp, 33060/tcp

2.k8s集群编写SVC的服务

apiVersion: v1
kind: Service
metadata:
  name: aliyun-mysql
  labels:
    app: online
spec:
  type: ClusterIP
  ports:
  - port: 3306
---
apiVersion: v1 
kind: Endpoints 
metadata: 
  name: aliyun-mysql 
subsets: 
- addresses: 
  - ip: 47.109.97.22 #集群外部服务IP地址
  ports: 
  - port: 3306 #放开的端口
[root@master2 sc]# kubectl get svc |grep ali
aliyun-mysql   ClusterIP   10.104.231.127   <none>        3306/TCP   17s
[root@master2 sc]# mysql -u root -h 10.104.231.127 -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 39056
Server version: 5.7.40 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]>

现在即可用集群内部访问clusterIP+端口进行访问部署在阿里云的mysql;在生产环境中,如果用的公有云数据库,可用此方案。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永恒布gg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值