k8s service(kube-proxy)

kube-proxy和service

kube-proxy 是k8s中的组件之一,是以Pod 形式真实运行的进程,而service 是它的代理功能的具体实现,service 是k8s 中一种资源类型,里面定义的是集群Pod 的访问转发规则。

存在的意义

1、pod 是短暂的-它们随时会启动或者关闭, 无论是为了给其他 pod 提供空间而从节点中被移除, 或者是减少了 pod 的数量, 又或者是因为集群中存在节点异常。
 
2、Kubernetes 在 pod 启动前会给已经调度到节点上的pod 分配 IP 地址----因此客户端不能提前知道提供服务的 pod 的 IP 地址。
 
3、水平仲缩意味着多个 pod 可能会提供相同的服务每个 pod 都有自己的 IP地址, 客户端无须关心后端提供服务 pod 的数量, 以及各自对应的 IP 地址。它们无须记录每个 pod 的 IP 地址。 相反, 所有的 pod       可以通过一个单一的IP 地址进行访问。
 
为了解决上述问题, Kubemetes 提供了一种资源类型一服务 (service),一个service 对应一个服务,每个后端服务都有一个端口,service 通过映射内部服务端口对外提供访问,每个服务运行一组pod 。

service 作用

服务发现(防止pod 失联)

负载均衡(轮询转发请求到后端集群中的pod)

service与Pod 关联

两种方式

1、service 通过标签选择器selector 关联pod 的标签,找到pod 。

2、service 通过关联deployment 等资源,通过deployment 等资源间接关联pod。注意写yaml文件创建service 关联deployment 通过标签选择器配置成 selector: app: pay 形式关联的是pod 的标签不是                      deployment   的标签,可以通过expose命令关联dep资源创建service。

service 的三种形式

ClusterIP  

集群IP,仅供k8s内部访问(只能在pod 或node 上访问,无法外部访问),相当于service 加了1个vip,通过vip 提供访问地址,再转发给各个Pod

NodePort  

在每个node 节点为相应Pod启动一个对外端口(默认30000起步),映射pod 内部端口。通过任意一个Pod 所在的节点ip+port 就能访问pod ,多个pod 需要在service 前面加一个LB(lvs/proxy)把每个节点的ip+port 加入,才能实现负载均衡,这样每个服务都得添加一次,增加了管理维护成本。

Loadblance   

云服务厂商提供的,自动添加service 映射对外端口到负载上面,例如阿里云可以通过SLB为service 提供负载均衡。只有云服务厂商的k8s 才有此形式。

相关操作

 service  的创建

方式一 expose

kubectl  expose  --help

kubectl     expose     deployment       nginx-dep1   --port=2022         --target-port=80    --type=NodePort         -n kzf

                    service代理资源类型    资源名称   代理对外端口          pod 中内部端口      端口暴露类型(默认ClusterIp)   命名空间
View Code

方式二  创建yaml

apiVersion: v1
kind: Service
metadata:
  name: service-kzf1
  namespace: asdf
spec:
  clusterIP: NodePort            #不写默认类型为Clusterip
  ports:
  - port: 2021                 #clusterip 集群端口
    targetPort: 80              #pod 内部端口
  selector:
    app: nginx-label             #通过标签选择pod 
View Code
拓展
1、创建的服务可以暴露一个端口,也可以暴露多个端口。比如,pod 监听两个端口,比如 HTTP 监听 8080 端口、 HTTPS 监听 8443 端口,可以使用 一个服务从端口 80 和 443 转发至 pod端口 8080 和 8443 2、可以对pod 的端口号命名,然后定义service 的时候targetport 使用端口号的名字。这样实现了解耦,即使pod 端口号变了,但是service 端无需更改spec。
3、注意 标签选择器应用于整个服务,不能对每个端 做单独的配直 如果不同的pod 有不同的端 映射关系,需要创建两个服务

service  查看

kubectl get svc -n kzf
  NAME        TYPE      CLUSTER-IP    EXTERNAL-IP    PORT(S)              AGE
  nginx-dep1    NodePort    xxx.xx.x.x     <none>       2022:31992/TCP          27m
  service-kzf   NodePort    xxx.xx.x.x     <none>       2020:32341/TCP          2d1h
  service-kzf1   ClusterIP   xxx.xx.x.x     <none>       2021/TCP             52m

   nginx-ingress  LoadBalancer   xxx.xx.x.x   xx.xx.xxx.xx   80:3xxxx/TCP,443:xxxx6/TCP     29d

注解:

type     分为三种类型NodePort/ClusterIP/LoadBalancer 

CLUSTER-IP  无论哪种类型都有clusterip 因为Node port 和loadbalancer 都是在它的基础上拓展的

EXTERNAL-IP 只有LoadBalancer 有,这是个负载均衡器的对外IP

Port     service 集群CLUSTER-IP IP 的端口:nodeport 类型节点上的端口

age      创建时间多久
View Code

 服务发现

当service 创建完成后pod 客户端怎么发现它的连接地址和端口呢,有以下方式

通过环境变量发现服务

pod 开始运 时候 k8s 会初始化一系列的环境变量指向现在存在服务。 你创建的service 早于客户端pod的创建, pod 上的进程可以根据环境变量获得服务 IP 地址和端口号。如果pod 早于service 创建可以删除pod 重新建立新pod 来获取service 信息。可以通过如下命令获取service 连接信息
kubectl exec kubia-2isdly env

通过DNS发现服务

在kube-system  命名空间有个dns 的pod 负载dns服务。在集群中的其他 pod 都被配置成使用其作为 dns ( Kubemetes 通过修改每 容器的/ etc/reso conf 实现)。

注:pod 是否使用内部DNS服务器是根据 pod spec dnsPolicy属性来决定的,每个服务从内部 DNS服务器中获得DNS条目,客户端 pod在知道服务名称的情况下可通过全限定域名 CFQDN来访问,而不是诉诸于环境变量。
 
关于FQDN 
backend.database.default.svc.cluster.local 
backend database 对应于服务名称, default 表示服务在其中定义的名称空间,而 svc.cluster.local 是在所有集群本地服务名称中使用的可配置集群域后缀。
 
注意:客户端仍然必须知道服务的端 如果服务使用标准端 号(例如,HTTP 80 ),这样是没问题的 如果并不是标准端口,客户端可以从环境变量中获取端口号

Tip:

service 集群 IP个虚拟 IP ,ping 的时候无法ping通,只有在与服务端口结合时才有意义。

 

连接集群外部的服务

通过 Kubernetes 服务特性暴露外部服务(不在k8s集群中,例如是虚拟机运行的服务)的情况,不要让service 将连接重定向到集群中的 pod ,而是让它重定向到外部 IP 和端口这样做可以让你充分利用service负载平衡和服务发现。在集群中运行的客户端
pod 可以像连接到内部服务那样连接到外部服务

介绍服务 endpoint

service 并不是和 pod 直接相连的。相反,有一种资源介于两者之间-它就是 Endpoint 资源。Endpoint资源就是暴露一个服务的IP 地址和端口的列表,当创建service的时候如果通过标签选择器关联了pod那么就会自动创建一个同名的Endpoint 资源,里面记录了后端的ip:port 的映射列表和其他Kubernetes资源一样,所以可以使用kubectl describe 来获取它的基本信息。

手动配置服务的 endpoint

service 的 endpoint 与service 解耦后,可以分别手动配置和更新它们,如果创建了不包含 pod选择器的service,Kubemetes 将不会创建 Endpoint 资源(毕竟,缺少选择器,将不会知道服务中包含哪些 pod)。这样就需要创建 Endpoint 资源来指定该服务的 endpoint 列表。要使用手动配置 endpoint 的方式创建服务,需要创建服务和 Endpoint 资源。
创建没有选择器的服务首先为服务创建一个YAML文件,如下面所示。
apiVersion: vl
kind: Service
metadata:
  name: external-service
  spec:
    ports:
    - port:80
View Code

为没有选择器的service创建endpoints。Endpoint是一个单独的资源并不是service的一个属性。由于创建的资源中并不包含选择器,相关的Endpoints资源并没有自动创建,所以必须手动创建。如下所示

apiVersion: vl
kind: Endpoints
metadata:
  name: external-service      # Endpoint的名称必须和服务的名称相匹配
subsets:
- addresses:
- ip: 11.11.11.11
- ip: 22.22.22.22
  ports:
  - port: 80
View Code
Endpoint对象需要与服务具有相同的名称,并包含该服务的目标IP地址和端口列表。服务和Endpoint资源都发布到服务器后,这样服务就可以像具有pod选择器那样的服务正常使用。在服务创建后创建的容器
将包含服务的环境变量,并且与其IP : port对应的所有连接都将在服务端点之间进行负载均衡。
 
Tip: 如果以后决定将外部服务迁移到Kubemetes中运行的pod,可以为服务添加选择器,从而对Endpoint进行自动管理。反过来也是一样的——将选择器从服务中移除,Kubemetes将停止更新Endpoints。这意味着服务的IP地址可以保持不变,同时服务的实际实现却发生了改变。

为外部服务创建别名

除了手动配置服务的Endpoint来代替公开外部服务方法,有一种更简单的方法,就是通过其完全限定域名(FQDN)访问外部服务
 
创建ExtemalName类型的服务
要创建一个具有别名的外部服务的服务时,要将创建服务资源的一个type字段设置为ExternalName。例如,设想一 下在api.somecompany.com上有公共可用的APL可以定义一个指向它的服务,如下所示
apiVersion: vl 
kind: Service 
metadata: 
  name: external-service 
spec: 
  type: ExternalName 
  externalName: someapi.asdf.com 
  ports: 
  - port: 80
View Code
service 创建完成后,pod可以 通 过external-service.default . SVC .cluster.local域名(甚至是external-service)连接到外部服务,而不是使用服务的实际FQDN。这隐藏了实际的服务名称及其使用该服务的pod 的位置,允许修改服务定义,并且在以后如果将其指向不同的服务,只需简单地修改 externalName属性,或者将类型重新变回Cluster IP并为服务创建Endpoint­无论是手动创建,还是对服务上指定标签选择器使其自动创建。ExternalName服务仅在DNS级别实施一为服务创建了简单的CNAME DNS记录。 因此, 连接到服务的客户端将直接连接到外部服务, 完全绕过服务代理。 出于这个原因, 这些类型的服务甚至不会获得集群 IP。 注意 CNAME记录指向完全限定的域名而不是数字 IP地址。

将服务暴漏给内部客户端

ClusterIp 类型即可,Clusterip 就是一个虚拟vip ,代理后端的一组pod,只能在集群内部访问(pod 里面访问/集群节点访问)

将服务暴露给外部客户端

方式一:NodePort

将服务的类型设置成NodePort每个集群节点都会在节点上打开 一个端口, 对于NodePort服务, 每个集群节点在节点本身(因此得名叫NodePort)上打开一个端口,并将在该端口上接收到的流量重定向到基础服务。该服务仅在内部集群 IP 和端口上才可访间, 但也可通过任意一个节点上的专用端口访问。因为nodeport 是clusterip 的拓展,所以此类型既可以通过clusterip 在集群内部访问也可以通过node-IP:port 在外部访问
$ kubectl get svc kubia-nodeport 
  NAME               CLUSTER-IP                 EXTERNAL-IP     PORT(S)         AGE 
  kubia-nodeport     10.111.254.223             <nodes>         80:30123/TCP     2m

EXTERNAL-IP列。 它显示nodes 表明服务可通过任何集群节点的IP地址访问。 PORT(S)列显示集群IP (8 0) 的内部端口和节点端口(30123), 可
以通过以下地址访问该服务:
• 10.11.254.223:80<ls七node'sIP>:30123
• <2ndnode'sIP>:30123, 等等
View Code
在第一个节点的端口30123收到的连接, 可以被重定向到第一节点个上运行的pod, 也可能是第二个节点上运行的pod。注意:更改防火墙规则, 让外部客户端访问我们的 NodePort 服务
访问示意图

 

 

 

 

方式二:LoadBalance

将服务的类型设置成LoadBalance, NodePort类型的一 种扩展一这使得服务可以通过一个专用的负载均衡器来访问, 这是由Kubernetes中正在运行的云基础设施提供的。 负载均衡器将流量重定向到跨所有节点的节点端口。客户端通过负载均衡器的 IP 连接到服务。
kubect1 get svc kubia-loadbalancer
  NAME            CLUSTER-IP      EXTERNAL-IP      PORT(S)      AGE
  kubia-loadbalancer   10.111.241.153    130.211.53.173    80:32143/TCP  lm

 在这种情况下, 负载均衡器的IP地址为130.211.53.173, 因此现在可以通过该 IP 地址访问该服务: 
 $ curl h七七p://130.211.53.173
View Code

访问流程图

 

 

 

方式三:Ingress

创建一 个Ingress资源, 这是一 个完全不同的机制, 通过一 个IP地址公开多个服务——它运行在 HTTP 层(网络协议第7 层)上, 因此可以提供比工作在第4层的服务更多的功能。详情间ingress 博文。
 

使用 headless服务来发现独立的pod 

到服务的每个连接都被转发到一个随机选择的 pod 上。但是如果客户端需要链接到所有的 pod 呢?如果后端的 pod 都需要连接到所有其他pod 呢?
通常, 当执行服务的 DNS 查找时, DNS 服务器会返回单个 IP一服务的集群 IP。 但是, 如果告诉Kubemetes, 不需要为服务提供集群 IP (通过在服务 spec 中将 clusterIP 字段设置为 None 来完成此操作), 则 DNS 服务器将返回 podIP 而不是单个服务 IP。
DNS 服务器不会返回单个 DNSA记录, 而是会为该服务返回多个A记录, 每个记录指向当时支持该服务的单个 pod的 IP。 客户端因此可以做一个简单的 DNS A记录查找并获取属于该服务一部分的所有 pod的 IP。 客户端可以使用该信息连接到其中的一个、 多个或全部。

创建headless服务 

apiVersion: vl 
kind: Service 
metadata:
  name: kubia-headless 
spec: 
  clusterIP: None      #这使得服务成为headless
  ports: 
  - port: 80
  targetPort: 8080
selector: 
  app: kubia

 service 与就绪探针

启动容器时,可以为 bernetes 配置 等待时间,经过等待时间后才可以执行第一次准备就绪检查 之后,它会周期性地调用探针,并根据就绪探针的结果采取行动。如果某个 po 报告它尚未准备就绪,则会从该服务中删除该 pod 。如果再次准备就绪,则重新添加 pod
 
与存活探针不同,如果容器未通过准备检 ,则不会被终止或重新启动 这是存活探针与就绪探针之间的重要区别 存活探针通过杀死异常的容器并用新的正常容器替代它们来保持 pod 正常工作,而就绪探针确保只有准备好处理请求的 pod 才可以接收它们(请求)。 这在容器启动时最为必要, 当然在容器运行一段时间后也是有用的。

service 负载功能实现原理

service 负载均衡实现主要有两种方式

iptables(早期)

iptables  是一个工具它通过linux 的netfilter 来进行ip 包的过滤处理。主要通过维护一张规则表,从上到下匹配表中规则,每次变动都不是增量而是全表变动。随着表的增大效率降低。

ipvs(k8s version:1.11以后)

ipvs实际上就是lvs 的原理。采用它的轮询策略(w,rr/wrr.lc,wlc),内核级别的,效率高。

posted @ 2019-11-28 19:50  fanggege  阅读(4206)  评论(0编辑  收藏  举报