k8s service基础

目录

Service 的概念

Service 的类型

四种类型

VIP 和 Service 代理

代理模式的分类

 测试用例

ClusterIP

Headless Service(无头服务)

NodePort

 LoadBalancer(了解即可)

ExternalName


假如说,我们有下面的服务需要部署进入我们的k8s集群中,

k8s service基础_第1张图片

方法:先创建我们的deployment,通过deployment部署我们的NGINX,它的副本数为1,然后去部署php-fpm,副本数为3,再部署MySQL,通过我们的StatefulSet,对于有状态服务,我们一般通过StatefulSet。

意外情况:假如说,有一天我们的php-fpm中有一个突然挂掉了,那么此时我们的副本数目不满足3个了,此时就会再次创建出一个pod,那么新创建出来的这个pod,它的ip就会变化,此时NGINX里面还是原来的那个ip,新的ip它不认识,此时就会报错。。。这样就引入出我们的SVC概念,根据标签匹配至对应的pod,然后负责去监测pod的信息,会同步它所监测的pod信息,我们的NGINX再去反向代理SVC的话,SVC会自动更新,不需要我们的NGINX做任何的修改。

k8s service基础_第2张图片


Service 的概念

Kubernetes  Service  定义了这样一种抽象:一个  Pod  的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。 这一组  Pod  能够被  Service  访问到,通常是通过  Label Selector(如下图的红框)。

k8s service基础_第3张图片

分析:我们的Frontend Deployment创建了三个pod,那么个pod里面有三组不同的标签,上面又定义一个Frontend Service,它所对应的pod标签是当app=webapp,role=frontend的时候,就会匹配上我们的这个SVC,注意,匹配的话,主要满足就行,pod里面那个version=1.0.0不用管。。一旦匹配成功之后,这个pod的信息就会被写入到SVC中,以后我们去访问这个SVC,相当于访问下属的pod,通过轮询的方式访问pod。。并且当我们的pod有死亡的时候,我们新创建出来的pod的信息也会被写入到SVC中去。

Service能够提供负载均衡的能力,但是在使用上有以下限制:

只提供 4 层负载均衡能力(也就是只能基于我们的ip地址和端口进行转发,实现负载均衡),而没有 7 层功能,也就是不能通过我们的主机名和域名的方案去负载均衡(这个可以通过ingress实现七层负载均衡),但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的。

Service 的类型

四种类型

Service 在 K8s 中有以下四种类型:

ClusterIp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP。

NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 : NodePort 来访问该服务。

LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到: NodePort。

ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 kubernetes 1.7 或更高版本的 kube-dns 才支持。

k8s service基础_第4张图片

分析:SVC服务发现需要上图几个组件的配合,首先由我们的apiserver去监听我们的服务和端口,通过我们的kube-proxy,kube-proxy负责去监听对应的pod,把对应pod的信息写入到iptables规则里去,当我们的客户端想要访问SVC的时候,其实就是访问我们的iptables规则。。这里其实几个概念,客户端访问到我们的节点是通过iptables去实现的,iptables的规则是通过我们的kube-proxy去实现的,apiserver通过kube-proxy去实现监听我们服务的端口和发现。kube-proxy会通过pod的标签去判断这个端点信息是否写入到我们的SVC中的端点信息中去。

VIP 和 Service 代理

在 Kubernetes 集群中,每个 Node 运行一个  kube-proxy  进程。 kube-proxy  负责为  Service  实现了一种VIP(虚拟 IP)的形式,而不是  ExternalName  的形式。 在 Kubernetes v1.0 版本,代理完全在 userspace。在Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。 从 Kubernetes v1.2 起,默认就是iptables 代理。 在 Kubernetes v1.8.0-beta.0 中,添加了 ipvs 代理。

在 Kubernetes 1.14 版本开始默认使用 ipvs 代理

在 Kubernetes v1.0 版本, Service 是 “4层”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了Ingress API(beta 版),用来表示 “7层”(HTTP)服务。。

问:为何不使用 round-robin DNS?

回答:DNS会在很多的客户端中进行缓存。。。我们很多服务用DNS进行域名解析之后,不会对DNS进行清除缓存的操作,也就是我们一旦有它的地址信息之后,不管访问几次,还是原来的地址信息。

代理模式的分类

Ⅰ、userspace 代理模式

k8s service基础_第5张图片

分析:我们的客户端访问server的方式是通过iptables(防火墙),再到kube-proxy,然后到server,包括我们的kube-apiserver也会通过kube-proxy去监听所谓的服务的更新以及端点的维护。这样我们的kube-proxy的负载压力很大。

Ⅱ、iptables 代理模式

k8s service基础_第6张图片

分析:我们发现,所有的访问直接由我们的iptables(防火墙)去完成,不需要通过kube-proxy去调度一个,这样我们的访问速度就会增加,以及kube-proxy的稳定性会提高和压力会减小。这个的话,除了防火墙的性能没那么高,,其他没什么影响。

Ⅲ、ipvs 代理模式

这种模式,kube-proxy 会监视 Kubernetes Service 对象和 Endpoints ,调用 netlink 接口以相应地创建ipvs 规则并定期与 Kubernetes Service 对象和 Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod。。

与 iptables 类似,ipvs 于netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs 为负载均衡算法提供了更多选项,例如:

rr :轮询调度

lc :最小连接数

dh :目标哈希

sh :源哈希

sed :最短期望延迟

nq : 不排队调度

注意如下说明:

ipvs模式启动成功的前提是宿主机上安装了ipvs相关模块,如果未安装,则会退回到iptables代理模式

k8s service基础_第7张图片

k8s service基础_第8张图片

k8s service基础_第9张图片

分析:这个模式已经成为标准了,先安装yum install ipvsadm,使用命令:ipvsadm -Ln,这个也是检测我们集群当中有没有开启ipvs模式

k8s service基础_第10张图片

 测试用例

ClusterIP

clusterIP 主要在每个 node 节点使用 iptables/ipvs,将发向 clusterIP 对应端口的数据,转发到 kube-proxy 中。然后 kube-proxy 自己内部实现有负载均衡的方法,并可以查询到这个 service 下对应 pod 的地址和端口,进而把数据转发给对应的 pod 的地址和端口。。。

k8s service基础_第11张图片

分析:Nginx相当于访问者,WebApp-1/2/3相当于被访问者

为了实现图上的功能,主要需要以下几个组件的协同工作:

1. apiserver 用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求后将数据存储到etcd中。

k8s service基础_第12张图片

2. kube-proxy kubernetes的每个节点中都有一个叫做kube-porxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables规则中

3. iptables 使用NAT等技术将virtualIP的流量转至endpoint中

为了演示的效果,我们需要先创建一个deployment,然后去绑定我们的SVC。。。

创建 depl-demo.yaml文件

[root@master1 svc]# ls
depl-demo.yaml  depl-svc.yaml
[root@master1 svc]# cat depl-demo.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      release: stabel
  template:
    metadata:
      labels:
        app: myapp
        release: stabel
        env: test
    spec:
      containers:
      - name: myapp
        image: nginx:alpine
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 8
[root@master1 svc]# 

然后给这个deployment绑定了一个SVC

创建 Service 信息

[root@master1 svc]# cat depl-svc.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: myapp
    release: stabel
  ports:
  - name: http
    port: 80
    targetPort: 80      #后端的pod的真实端口
[root@master1 svc]# 

注意:我们创建的SVC跟上面先创建的deployment实现匹配的上,是根据labels,也就是deployment下面的matchLabels的键值对和SVC中的selector对应的上即可绑定成功。。

k8s service基础_第13张图片

然后下面就是显示的效果:访问SVC的10.1.96.245的80端口,就相当于去访问后端的那三个ip

k8s service基础_第14张图片

rr:表示轮询

注意:我们在用xxx.yaml文件创建pod的时候,如果不想要这个pod了,可以使用kubectl delete -f xxx.yaml命令删除我们这个yaml文件创建的pod。

在 Cluster 内部中,除了可以通过 Cluster IP 访问 Service,Kubernetes 还提供了更为方便的 DNS 访问,在kubeadm 部署时会默认安装 kube-dns 组件。

coredns 是一个 DNS 服务器。每当有新的 Service 被创建,coredns 会添加该 Service 的 DNS 记录。Cluster 中的 Pod 可以通过 . 访问 Service。

比如可以用 myapp.default 访问 Service myapp。

[root@master1 controller]# kubectl run nginx --image=nginx:alpine
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/nginx created
[root@master1 controller]# 
[root@master1 controller]# 
[root@master1 controller]# 
[root@master1 controller]# kubectl get pod 
NAME                            READY   STATUS    RESTARTS   AGE
myapp-deploy-65ff67dcb6-2q92v   1/1     Running   0          8m24s
myapp-deploy-65ff67dcb6-8pbcg   1/1     Running   0          8m24s
myapp-deploy-65ff67dcb6-x88m4   1/1     Running   0          8m24s
nginx-74d5899f46-ggggl          1/1     Running   0          4s
[root@master1 controller]# kubectl exec -it nginx-74d5899f46-ggggl sh 
/ # curl myapp.default



Welcome to nginx!



Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

/ #

上面的就是通过我们重新创建一个pod(nginx-74d5899f46-ggggl),然后验证集群内部通过访问. 访问 Service ,可以看到访问成功。

Headless Service(无头服务)

有时不需要或不想要负载均衡,以及单独的 Service IP 。遇到这种情况,可以通过指定 Cluster

IP(spec.clusterIP) 的值为 “None” 来创建 Headless Service 。这类 Service 并不会分配 Cluster IP, kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。。。

(这类服务的作用主要就是,通过这种方式去解决我们的所谓的hostname和我们的podname的变化问题,即通过它去绑定)

[root@master1 svc]# cat svc-headless.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myapp-headless
  namespace: default
spec:
  selector:
    app: myapp
  clusterIP: "None"
  ports:
  - port: 80
    targetPort: 80
[root@master1 svc]# kubectl get svc 
NAME             TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
kubernetes       ClusterIP   10.1.0.1              443/TCP   63m
myapp            ClusterIP   10.1.96.245           80/TCP    20m
myapp-headless   ClusterIP   None                  80/TCP    47s
[root@master1 svc]# kubectl get pod -o wide -n kube-system
NAME                              READY   STATUS    RESTARTS   AGE   IP               NODE      NOMINATED NODE   READINESS GATES
coredns-645bfc575f-mpmf8          1/1     Running   0          64m   10.244.0.69      master1              
coredns-645bfc575f-vlktl          1/1     Running   0          64m   10.244.0.68      master1              
etcd-master1                      1/1     Running   0          63m   192.168.64.150   master1              
kube-apiserver-master1            1/1     Running   0          63m   192.168.64.150   master1              
kube-controller-manager-master1   1/1     Running   0          63m   192.168.64.150   master1              
kube-flannel-ds-amd64-wn26n       1/1     Running   0          64m   192.168.64.150   master1              
kube-proxy-8vhd7                  1/1     Running   0          46m   192.168.64.150   master1              
kube-scheduler-master1            1/1     Running   0          63m   192.168.64.150   master1              
[root@master1 svc]# 

分析:我们如果访问这个无头服务,会有什么情况发生呢?

我们知道,对应SVC一旦创建成功,就会有一个主机名,它就会写入到我们的coredns中去,它的写入格式是:svc的名称+名字空间的名称+集群的域名(默认为svc.cluster.local)

下面的是通过dig命令去解析一下

最后会得到如下:

[root@master1 svc]# dig  -t A myapp-headless.default.svc.cluster.local. @10.244.0.69

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.5 <<>> -t A myapp-headless.default.svc.cluster.local. @10.244.0.69
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 13719
;; flags: qr rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;myapp-headless.default.svc.cluster.local. IN A

;; ANSWER SECTION:
myapp-headless.default.svc.cluster.local. 22 IN	A 10.244.0.73
myapp-headless.default.svc.cluster.local. 22 IN	A 10.244.0.71
myapp-headless.default.svc.cluster.local. 22 IN	A 10.244.0.72

;; Query time: 0 msec
;; SERVER: 10.244.0.69#53(10.244.0.69)
;; WHEN: 六 8月 28 15:58:36 CST 2021
;; MSG SIZE  rcvd: 237

[root@master1 svc]# 

可以和我的pod对应起来,也就意味着在我们的无头服务中,虽然没有自己的SVC了,但是可以通过访问域名的方案去访问这几个不同的pod上去。

[root@master1 svc]# kubectl get pod -o wide
NAME                            READY   STATUS    RESTARTS   AGE   IP            NODE      NOMINATED NODE   READINESS GATES
myapp-deploy-65ff67dcb6-27vmt   1/1     Running   0          24m   10.244.0.73   master1              
myapp-deploy-65ff67dcb6-lgtg8   1/1     Running   0          24m   10.244.0.71   master1              
myapp-deploy-65ff67dcb6-mcttp   1/1     Running   0          24m   10.244.0.72   master1              
[root@master1 svc]# 
[root@master1 svc]# kubectl exec -it myapp-deploy-65ff67dcb6-27vmt sh 
/ # ping myapp-headless.default.svc.cluster.local -c 3
PING myapp-headless.default.svc.cluster.local (10.244.0.71): 56 data bytes
64 bytes from 10.244.0.71: seq=0 ttl=64 time=0.070 ms
64 bytes from 10.244.0.71: seq=1 ttl=64 time=0.160 ms
64 bytes from 10.244.0.71: seq=2 ttl=64 time=0.193 ms

--- myapp-headless.default.svc.cluster.local ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.141/0.193 ms
/ # 

关于无头服务更加详细的介绍:https://cloud.tencent.com/developer/article/1820094

NodePort

它的概念就是可以在我们的物理机上暴露一个端口,我们就可以通过物理机的ip+端口的方式去访问至集群内部的服务

nodePort 的原理在于在 node 上开了一个端口,将向该端口的流量导入到 kube-proxy,然后由 kube-proxy 进一步到给对应的 pod。

[root@master1 svc]# cat svc-nodeport.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: default
spec:
  type: NodePort
  selector:
    app: myapp
    release: stabel
  ports:
  - name: http
    port: 80
    targetPort: 80

k8s service基础_第15张图片k8s service基础_第16张图片

而且我们发现上面的是端口是kube-proxy去监听的

[root@master1 svc]# lsof -i :32435
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
kube-prox 27288 root   11u  IPv6 243499      0t0  TCP *:32435 (LISTEN)

 LoadBalancer(了解即可)

loadBalancer 和 nodePort 其实是同一种方式。区别在于 loadBalancer 比 nodePort 多了一步,就是可以调用cloud provider (云供应商)去创建 LB 来向节点导流。。。

k8s service基础_第17张图片

ExternalName

这种类型的 Service 通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容( 例如:hub.atguigu.com )。ExternalName Service 是 Service 的特例,它没有 selector(也就是没有标签选择),也没有定义任何的端口和Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。

[root@master1 svc]# cat externalname.yaml 
kind: Service
apiVersion: v1
metadata:
  name: my-service-1
  namespace: default
spec:
  type: ExternalName
  externalName: hub.atguigu.com
[root@master1 svc]# kubectl get svc 
NAME             TYPE           CLUSTER-IP    EXTERNAL-IP       PORT(S)        AGE
kubernetes       ClusterIP      10.1.0.1                  443/TCP        93m
my-service-1     ExternalName           hub.atguigu.com            98s
myapp            NodePort       10.1.52.170               80:32435/TCP   14m
myapp-headless   ClusterIP      None                      80/TCP         30m

 k8s service基础_第18张图片

当查询主机 my-service-1.defalut.svc.cluster.local (SVC_NAME.NAMESPACE.svc.cluster.local )时,集群的DNS 服务将返回一个值hub.atguigu.com 的 CNAME(别名) 记录。访问这个服务的工作方式和其他的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发 。

你可能感兴趣的:(kubernetes,k8s,svc)