一、Kubernetes Service

Service的介绍

Service可以看作是一组提供相同服务的Pod对外的访问接口。借助Service,应用可以方便地实现服务发现和负载均衡。

service默认只支持4层负载均衡能力,没有7层功能。(可以通过Ingress实现)

详细了解负载均衡可以查看:https://blog.csdn.net/qq_35887546/article/details/104406412

service的类型

  • ClusterIP:默认值,k8s系统给service自动分配的虚拟IP,只能在集群内部访问
  • NodePort:将Service通过指定的Node上的端口暴露给外部,访问任意一个NodeIP:nodePort都将路由到ClusterIP。
  • LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部的负载均衡器,并将请求转发到 :NodePort,此模式只能在云服务器上使用。
  • ExternalName:将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定)。

service的优化

Service 是由 kube-proxy 组件,加上 iptables 来共同实现的.

kube-proxy 通过 iptables 处理 Service 的过程,需要在宿主机上设置相当多的 iptables 规则,如果宿主机有大量的Pod,不断刷新iptables规则,会消耗大量的CPU资源。

IPVS模式的service,可以使K8s集群支持更多量级的Pod。

开启kube-proxy的ipvs模式:

# yum install -y ipvsadm 			//所有节点安装
 kubectl edit cm kube-proxy -n kube-system	//修改IPVS模式
    修改: 43     mode: "ipvs"

configmap/kube-proxy edited

在这里插入图片描述
由于有些pod已经在运行,因此需要更新kube-proxy pod:

kubectl get pod -n kube-system |grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}'		//更新kube-proxy pod

但是需要注意的是,在新版的k8s使用ipvs的策略时需要更高的linux的内核(至少4以上),要不然会出现服务不能同步,地址不能解析,负载不能调度的问题。而我们的实验环境rhel7.6的默认内核为3.10,因此我们需要更新内核以正常使用ipvs,首先在阿里镜像站下载内核文件https://developer.aliyun.com/packageSearch?word=kernal,我下载的版本是:kernel-4.9.215-36.el7.x86_64.rpm,然后将集群中的所有主机都更新内核:

yum install -y kernel-4.9.215-36.el7.x86_64.rpm 
cd /boot/grub2/
vim grub.cfg 			#查看你安装的内核的名称
grub2-set-default 'Red Hat Enterprise Linux Server (4.9.215-36.el7.x86_64) 7.6 (Maipo)'		#将默认内核设置为新安装的那个

之后重启查看内核版本已经更新:

# uname -r
4.9.215-36.el7.x86_64

全部更新完毕后即可正常使用。

IPVS模式下,kube-proxy会在service创建后,在宿主机上添加一个虚拟网卡:kube-ipvs0,并分配service IP。
在这里插入图片描述

kube-proxy通过linux的IPVS模块,以rr轮询方式调度service中的Pod。

在这里插入图片描述

Flannel vxlan模式跨主机通信原理

在这里插入图片描述其中两个主机之间(eth0 --> eth0)的通信通过udp进行。

原理具体可以参考:http://www.sel.zju.edu.cn/?p=690

二、Kubernetes Service举例

ClusterIP举例

首先创建pod:

[root@server1 ~]# vim deployment.yaml 
[root@server1 ~]# cat deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-nginx
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: ikubernetes/myapp:v1
        ports:
        - containerPort: 80

[root@server1 ~]# kubectl apply -f deployment.yaml 
deployment.apps/deployment-nginx created
[root@server1 ~]# kubectl get pod -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP            NODE      NOMINATED NODE   READINESS GATES
deployment-nginx-56d786cd98-fcmh9   1/1     Running   0          55s   10.244.2.34   server3   <none>           <none>
deployment-nginx-56d786cd98-pkvpg   1/1     Running   0          55s   10.244.1.32   server2   <none>           <none>

现在可以在集群内部访问这两个pod:

[root@server1 ~]# kubectl run test -it --image=radial/busyboxplus
If you don't see a command prompt, try pressing enter.
/ # curl 10.244.2.34
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
/ # curl 10.244.1.32
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

创建service:(ClusterIP方式):

[root@server1 ~]# vim service.yaml
[root@server1 ~]# cat service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  selector:					#service与pod关联时使用的标签,此处设置的标签要和需要关联的pod标签相同
      app: nginx
  type: ClusterIP

[root@server1 ~]# kubectl apply -f service.yaml 
service/myservice created

service通过标签与pod关联,查看service状态:

[root@server1 ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP   4d2h
myservice    ClusterIP   10.97.61.44   <none>        80/TCP    6s

可以看出service暴露出来的ip为10.97.61.44,我们可以通过这个ip访问pod:

[root@server1 ~]# kubectl attach test -it 
If you don't see a command prompt, try pressing enter.
/ # curl 10.97.61.44
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

NodePort举例

使用NodePort方式可以使我们在集群外访问集群内的pod

在上个实验时我们已经创建了一个ClusterIP方式的service,我们可以直接更改其方式:

[root@server1 ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   4d3h
myservice    ClusterIP   10.111.87.107   <none>        80/TCP    84m
[root@server1 ~]# kubectl edit svc myservice 

在这里插入图片描述
再查看service状态:

[root@server1 ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        4d3h
myservice    NodePort    10.111.87.107   <none>        80:31549/TCP   84m

在这里插入图片描述
可以发现已经变成NodePort方式了,同时也可以看出端口为31549,再集群外部主机访问pod:

[root@foundation63 Desktop]# curl 172.25.63.3:31549
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@foundation63 Desktop]# curl 172.25.63.2:31549
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

访问两个节点的31549端口均可以访问到pod。
实验后删除:

[root@server1 ~]# kubectl delete -f service.yaml 
service "myservice" deleted

同时也可以在创建service的时候就指定为NodePort的方式。

[root@server1 ~]# vim service.yaml 
[root@server1 ~]# cat service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  selector:
      app: nginx
  type: NodePort			#指定方式

[root@server1 ~]# kubectl apply -f service.yaml 
service/myservice created             
[root@server1 ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP        4d3h
myservice    NodePort    10.103.247.110   <none>        80:30256/TCP   12s

实验后删除:

[root@server1 ~]# kubectl delete -f service.yaml 
service "myservice" deleted

Kube-dns举例

Kubernetes 提供了一个 DNS 插件 Service。可以使用以下命令查看:

kubectl get services kube-dns --namespace=kube-system

在这里插入图片描述首先我们运行一个service方式为ClusterIP:

[root@server1 ~]# vim service.yaml 
[root@server1 ~]# cat service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  selector:
      app: nginx
  type: ClusterIP

[root@server1 ~]# kubectl apply -f service.yaml 
service/myservice created
[root@server1 ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP   4d3h
myservice    ClusterIP   10.105.237.243   <none>        80/TCP    23s

通过kube-dns我们可以通过直接访问service的名称来访问pod:

[root@server1 ~]# kubectl attach test -it			#之前使用busyboxplus创建的pod
Defaulting container name to test.
Use 'kubectl describe pod/test -n default' to see all of the containers in this pod.
If you don't see a command prompt, try pressing enter.
/ # nslookup myservice				#查看myservice的解析
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      myservice
Address 1: 10.105.237.243 myservice.default.svc.cluster.local
/ # 
/ # curl myservice.default.svc.cluster.local		#域名格式:$(servicename).$(namespace).svc.cluster.local,也可以直接访问myservice
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
/ # curl myservice.default.svc.cluster.local/hostname.html
deployment-nginx-56d786cd98-fcmh9
/ # curl myservice.default.svc.cluster.local/hostname.html
deployment-nginx-56d786cd98-tg2t6
/ # curl myservice.default.svc.cluster.local/hostname.html
deployment-nginx-56d786cd98-pkvpg
/ # curl myservice.default.svc.cluster.local/hostname.html
deployment-nginx-56d786cd98-k8psl
/ # ping myservice				#直接访问myservice
PING myservice (10.105.237.243): 56 data bytes
64 bytes from 10.105.237.243: seq=0 ttl=64 time=0.140 ms
64 bytes from 10.105.237.243: seq=1 ttl=64 time=0.073 ms

可以看出通过服务名称可以直接访问pod并自动实现负载均衡。

实验后删除:

[root@server1 ~]# kubectl delete -f service.yaml 
service "myservice" deleted

Headless Service举例

Headless Service “无头服务”

  • Headless Service不需要分配一个VIP,而是直接以DNS记录的方式解析出被代理Pod的IP地址
  • 域名格式:$(servicename).$(namespace).svc.cluster.local
[root@server1 ~]# vim service.yaml 
[root@server1 ~]# cat service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  selector:
    app: nginx
  clusterIP: None			#指定为Headless方式


[root@server1 ~]# kubectl apply -f service.yaml 
service/myservice created
[root@server1 ~]# kubectl get svc			#可以看出ip为none
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   4d4h
myservice    ClusterIP   None         <none>        80/TCP    42s

在这里插入图片描述可以直接通过service的名称直接访问到后端的pod:

[root@server1 ~]# kubectl attach test -it
Defaulting container name to test.
Use 'kubectl describe pod/test -n default' to see all of the containers in this pod.
If you don't see a command prompt, try pressing enter.
/ # nslookup myservice			#查看myservice的解析,可以看到后端的4个pod
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      myservice
Address 1: 10.244.2.35 10-244-2-35.myservice.default.svc.cluster.local
Address 2: 10.244.2.34 10-244-2-34.myservice.default.svc.cluster.local
Address 3: 10.244.1.32 10-244-1-32.myservice.default.svc.cluster.local
Address 4: 10.244.1.34 10-244-1-34.myservice.default.svc.cluster.local
/ # curl myservice
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
/ # curl myservice/hostname.html
deployment-nginx-56d786cd98-k8psl
/ # curl myservice/hostname.html
deployment-nginx-56d786cd98-tg2t6
/ # curl myservice/hostname.html
deployment-nginx-56d786cd98-pkvpg
/ # curl myservice/hostname.html
deployment-nginx-56d786cd98-fcmh9		#可以看出可以实现负载均衡

也可以使用dig命令查看解析:

[root@server1 ~]# docker pull slongstreet/bind-utils			#先拉取镜像

[root@server1 ~]# kubectl run demo -it --image=slongstreet/bind-utils
If you don't see a command prompt, try pressing enter.
bash-4.3# 
bash-4.3# dig myservice.default.svc.cluster.local

在这里插入图片描述当pod更新后依然可以解析:

[root@server1 ~]# vim deployment.yaml 
[root@server1 ~]# cat deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-nginx
  labels:
    app: nginx
spec:
  replicas: 4
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: ikubernetes/myapp:v2			#更新到v2
        ports:
        - containerPort: 80

[root@server1 ~]# kubectl apply -f deployment.yaml 
[root@server1 ~]# kubectl attach demo -it
Defaulting container name to demo.
Use 'kubectl describe pod/demo -n default' to see all of the containers in this pod.
If you don't see a command prompt, try pressing enter.
bash-4.3# 
bash-4.3# dig myservice.default.svc.cluster.local

在这里插入图片描述
实验后删除:

[root@server1 ~]# kubectl delete -f service.yaml 
service "myservice" deleted

LoadBalancer举例

从外部访问 Service 的第一种方式为NodePort,之前已经介绍过,下面介绍另外两种方式。

从外部访问 Service 的第二种方式,适用于公有云上的 Kubernetes 服务。这时候,你可以指定一个 LoadBalancer 类型的 Service。

[root@server1 ~]# vim service.yaml 
[root@server1 ~]# cat service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer

在service提交后,Kubernetes就会调用 CloudProvider 在公有云上为你创建一个负载均衡服务,并且把被代理的 Pod 的 IP地址配置给负载均衡服务做后端。

[root@server1 ~]# kubectl apply -f service.yaml 
service/myservice created
[root@server1 ~]# kubectl get svc
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP        4d4h
myservice    LoadBalancer   10.105.70.198   <pending>     80:31184/TCP   45s

在这里插入图片描述

实验后删除:

[root@server1 ~]# kubectl delete svc myservice 
service "myservice" deleted

ExternalName 举例

从外部访问的第三种方式叫做ExternalName,这种方式也可以使pod能够访问外部的地址。

[root@server1 ~]# vim External.yaml 
[root@server1 ~]# cat External.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  type:  ExternalName
  externalName: www.baidu.com			#指定外部有效地址

接下来创建service:

[root@server1 ~]# kubectl apply -f External.yaml 
service/myservice created
[root@server1 ~]# kubectl get svc
NAME         TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)   AGE
kubernetes   ClusterIP      10.96.0.1    <none>          443/TCP   4d4h
myservice    ExternalName   <none>       www.baidu.com   <none>    6s

可以看出并没有分配ip地址,只是在dns里面更新了以下CNAME。
在这里插入图片描述这样的做法有一个好处,即使需要访问的外部地址变了,也只需要更改service的yaml文件即可,依然可以通过访问myservice.default.svc.cluster.local来访问外部地址,如下:

[root@server1 ~]# vim External.yaml 
[root@server1 ~]# cat External.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  type:  ExternalName
  externalName: www.qq.com			#外部网站变更
[root@server1 ~]# kubectl apply -f External.yaml 
service/myservice configured

在这里插入图片描述ExternalName方式的service允许为其分配一个公有IP。

[root@server1 ~]# vim service.yaml 
[root@server1 ~]# cat service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  selector:
    app: nginx
  externalIPs:
    - 172.25.63.100			#指定的ip地址
[root@server1 ~]# kubectl apply -f service.yaml 
service/myservice created
[root@server1 ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP     PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1        <none>          443/TCP   4d4h
myservice    ClusterIP   10.106.224.140   172.25.63.100   80/TCP    10s

查看svc的状态说明指定成功,在集群外访问:

[root@foundation63 Desktop]# curl 172.25.63.100
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
[root@foundation63 Desktop]# curl 172.25.63.100
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
[root@foundation63 Desktop]# curl 172.25.63.100
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>

可以成功访问。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐