为什么要有Service?

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

1、pod ip经常变化,service是pod的代理,我们客户端访问,只需要访问service,就会把请求代理到Pod
2、pod ip在k8s集群之外无法访问,所以需要创建service,这个service可以在k8s集群外访问的。

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的实现方式。

Service工作原理

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

kubernetes集群中有三类IP地址

1、Node Network(节点网络):物理节点或者虚拟节点的网络,如ens33接口上的网路地址

[root@master1 ~]# ip a
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:0c:29:5a:43:35 brd ff:ff:ff:ff:ff:ffinet 192.168.0.180/24 brd 192.168.0.255 scope global noprefixroute ens33

2、Pod network(pod 网络),创建的Pod具有的IP地址

[root@master1 ~]# kubectl get pod -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
myapp-v1-67fd9fc9c8-5nc87   1/1     Running   0          38m   10.1.104.44    node2   <none>           <none>

Node Network和Pod network这两种网络地址是我们实实在在配置的,其中节点网络地址是配置在节点接口之上,而pod网络地址是配置在pod资源之上的,因此这些地址都是配置在某些设备之上的,这些设备可能是硬件,也可能是软件模拟的

3、Cluster Network(集群地址,也称为service network),这个地址是虚拟的地址(virtual ip),没有配置在某个接口上,只是出现在service的规则当中。

[root@master1 ~]# kubectl get svc
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP

Service资源字段

  • 查看定义Service资源需要的字段有哪些?
[root@master1 ~]# kubectl explain service
KIND:     Service
VERSION:  v1
DESCRIPTION:Service is a named abstraction of software service (for example, mysql)consisting of local port (for example 3306) that the proxy listens on, andthe selector that determines which pods will answer requests sent throughthe proxy.
FIELDS:apiVersion   <string>  #service资源使用的api组kind   <string>           #创建的资源类型metadata   <Object>      #定义元数据spec  <Object>     
  • 查看service的spec字段如何定义?
[root@master1 ~]# kubectl explain service.spec
KIND:     Service
VERSION:  v1
RESOURCE: spec <Object>
DESCRIPTION:Spec defines the behavior of a service.https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-statusServiceSpec describes the attributes that a user creates on a service.
FIELDS:allocateLoadBalancerNodePorts    <boolean>clusterIP    <string>
#动态分配的地址,也可以自己在创建的时候指定,创建之后就改不了了clusterIPs    <[]string>externalIPs <[]string>externalName    <string>externalTrafficPolicy <string>healthCheckNodePort   <integer>ipFamilies   <[]string>ipFamilyPolicy  <string>loadBalancerIP    <string>loadBalancerSourceRanges  <[]string>ports   <[]Object>  #定义service端口,用来和后端pod建立联系publishNotReadyAddresses  <boolean>selector <map[string]string> #通过标签选择器选择关联的pod有哪些sessionAffinity    <string>sessionAffinityConfig <Object>
#service在实现负载均衡的时候还支持sessionAffinity,sessionAffinity
什么意思?会话联系,默认是none,随机调度的(基于iptables规则调度的);如果我们定义sessionAffinity的client ip,那就表示把来自同一客户端的IP请求调度到同一个pod上topologyKeys   <[]string>type    <string>  #定义service的类型

Service的四种类型

  • 查看定义Service.spec.type需要的字段有哪些?
[root@master1 ~]# kubectl explain service.spec.type
KIND:     Service
VERSION:  v1
FIELD:    type <string>
DESCRIPTION:type determines how the Service is exposed. Defaults to ClusterIP. Validoptions are ExternalName, ClusterIP, NodePort, and LoadBalancer."ClusterIP" allocates a cluster-internal IP address for load-balancing toendpoints. Endpoints are determined by the selector or if that is notspecified, by manual construction of an Endpoints object or EndpointSliceobjects. If clusterIP is "None", no virtual IP is allocated and theendpoints are published as a set of endpoints rather than a virtual IP."NodePort" builds on ClusterIP and allocates a port on every node whichroutes to the same endpoints as the clusterIP. "LoadBalancer" builds onNodePort and creates an external load-balancer (if supported in the currentcloud) which routes to the same endpoints as the clusterIP. "ExternalName"aliases this service to the specified externalName. Several other fields donot apply to ExternalName services. More info:https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types

ExternalName:

适用于k8s集群内部容器访问外部资源,它没有selector,也没有定义任何的端口和Endpoint。
以下Service 定义的是将prod名称空间中的my-service服务映射到my.database.example.com
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
当查询主机 my-service.prod.svc.cluster.local 时,群集DNS将返回值为my.database.example.com的CNAME记录。

service的FQDN是: <service_name>..svc.cluster.local
my-service.prod. svc.cluster.local

ClusterIP:

通过k8s集群内部IP暴露服务,选择该值,服务只能够在集群内部访问,这也是默认的ServiceType。

NodePort:

通过每个Node节点上的IP和静态端口暴露k8s集群内部的服务。通过请求:可以把请求代理到内部的pod。Client----->NodeIP:NodePort----->Service Ip:ServicePort----->PodIP:ContainerPort。

LoadBalancer:

使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到NodePort服务和ClusterIP 服务。

Service的端口

  • 查看service的spec.ports字段如何定义?
[root@master1 ~]# kubectl explain service.spec.ports
KIND:     Service
VERSION:  v1
RESOURCE: ports <[]Object>
DESCRIPTION:The list of ports that are exposed by this service. More info:https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxiesServicePort contains information on service's port.
FIELDS:appProtocol  <string>name  <string>  #定义端口的名字nodePort    <integer>
|#宿主机上映射的端口,比如一个Web应用需要被k8s集群之外的其他用户访问,那么需要配置type=NodePort,若配置nodePort=30001,那么其他机器就可以通过浏览器访问scheme://k8s集群中的任何一个节点ip:30001即可访问到该服务,例如http://192.168.1.63:30001。如果在k8s中部署MySQL数据库,MySQL可能不需要被外界访问,只需被内部服务访问,那么就不需要设置NodePortport  <integer> -required-  #service的端口,这个是k8s集群内部服务可访问的端口protocol   <string>targetPort    <string>
# targetPort是pod上的端口,从port和nodePort上来的流量,经过kube-proxy流入到后端pod的targetPort上,最后进入容器。与制作容器时暴露的端口一致(使用DockerFile中的EXPOSE),例如官方的nginx暴露80端口。

创建Service:type类型是ClusterIP

  • 创建Pod
[root@master1 svc]# cat pod_test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: my-nginx
spec:selector:matchLabels:run: my-nginxreplicas: 2template:metadata:labels:run: my-nginxspec:containers:- name: my-nginximage: nginximagePullPolicy: IfNotPresentports:- containerPort: 80   # Pod容器中要暴露的端口[root@master1 svc]# kubectl apply -f pod_test.yaml
deployment.apps/my-nginx created
  • 查看刚才创建的Pod ip地址
[root@master1 svc]# kubectl get pod -l run=my-nginx -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
my-nginx-69f769d56f-2rrs2   1/1     Running   0          46s   10.1.104.46   node2   <none>           <none>
my-nginx-69f769d56f-rvq4x   1/1     Running   0          46s   10.1.104.47   node2   <none>           <none>
  • 请求pod ip地址,查看结果
[root@master1 svc]# curl  10.1.104.46
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>root@my-nginx-69f769d56f-rvq4x:/# curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>

需要注意的是,pod虽然定义了容器端口,但是不会使用调度到该节点上的80端口,也不会使用任何特定的NAT规则去路由流量到Pod上。 这意味着可以在同一个节点上运行多个 Pod,使用相同的容器端口,并且可以从集群中任何其他的Pod或节点上使用IP的方式访问到它们。

  • 误删除其中一个Pod
[root@master1 svc]# kubectl delete pods my-nginx-69f769d56f-2rrs2
pod "my-nginx-69f769d56f-2rrs2" deleted
[root@master1 svc]# kubectl get pod -l run=my-nginx -o wide
NAME                        READY   STATUS    RESTARTS   AGE    IP             NODE    NOMINATED NODE   READINESS GATES
my-nginx-69f769d56f-qpn9w   1/1     Running   0          14s    10.1.166.186   node1   <none>           <none>
my-nginx-69f769d56f-rvq4x   1/1     Running   0          5m8s   10.1.104.47    node2   <none>           <none>

通过上面可以看到重新生成了一个pod :my-nginx-69f769d56f-qpn9w,ip是10.1.166.186,在k8s中创建pod,如果pod被删除了,重新生成的pod ip地址会发生变化,所以需要在pod前端加一个固定接入层。接下来创建service:

创建Service

[root@master1 svc]# cat service-test.yaml
apiVersion: v1
kind: Service
metadata:name: my-nginxlabels:run: my-nginx
spec:type: ClusterIPports:- port: 80   # service的端口,暴露给k8s集群内部服务访问protocol: TCPtargetPort: 80 # Pod容器定义的端口selector:run: my-nginx

上述yaml文件将创建一个 Service,具有标签run=my-nginx的Pod,目标TCP端口 80,并且在一个抽象的Service端口(targetPort:容器接收流量的端口;port:抽象的 Service 端口,可以使任何其它 Pod访问该 Service 的端口)上暴露。

[root@master1 svc]# kubectl apply -f service-test.yaml
service/my-nginx created[root@master1 svc]# kubectl get svc -l run=my-nginx -o wide
NAME       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE   SELECTOR
my-nginx   ClusterIP   10.108.227.57   <none>        80/TCP    91s   run=my-nginx
  • 在k8s控制节点访问service的ip:端口就可以把请求代理到后端pod
[root@master1 svc]# curl 10.108.227.57:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>

#通过上面可以看到请求service IP:port跟直接访问pod ip:port看到的结果一样,这就说明service可以把请求代理到它所关联的后端pod
注意:上面的10.108.227.57:80地址只能是在k8s集群内部可以访问,在外部无法访问,比方说我们想要通过浏览器访问,那么是访问不通的,如果想要在k8s集群之外访问,是需要把service type类型改成nodePort的

  • 查看service详细信息
[root@master1 svc]# kubectl describe svc my-nginx
Name:              my-nginx
Namespace:         default
Labels:            run=my-nginx
Annotations:       <none>
Selector:          run=my-nginx
Type:              ClusterIP
IP Families:       <none>
IP:                10.108.227.57
IPs:               10.108.227.57
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.1.104.47:80,10.1.166.186:80
Session Affinity:  None
Events:            <none>[root@master1 svc]# kubectl describe endpoints my-nginx
Name:         my-nginx
Namespace:    default
Labels:       run=my-nginx
Annotations:  endpoints.kubernetes.io/last-change-trigger-time: 2022-08-04T02:56:43Z
Subsets:Addresses:          10.1.104.47,10.1.166.186NotReadyAddresses:  <none>Ports:Name     Port  Protocol----     ----  --------<unset>  80    TCPEvents:  <none>

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。

service只要创建完成,我们就可以直接解析它的服务名,每一个服务创建完成后都会在集群dns中动态添加一个资源记录,添加完成后我们就可以解析了,资源记录格式是:
SVC_NAME.NS_NAME.DOMAIN.LTD.
服务名.命名空间.域名后缀
集群默认的域名后缀是svc.cluster.local.
就像我们上面创建的my-nginx这个服务,它的完整名称解析就是
my-nginx.default.svc.cluster.local

[root@master1 svc]# kubectl exec -it my-nginx-69f769d56f-qpn9w -- /bin/bash
root@my-nginx-69f769d56f-qpn9w:/# curl my-nginx.default.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>root@my-nginx-69f769d56f-qpn9w:/# cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

创建Service:type类型是NodePort

[root@master1 svc]# cat nodeport.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: my-nginx-nodeport
spec:selector:matchLabels:run: my-nginx-nodeportreplicas: 2template:metadata:labels:run: my-nginx-nodeportspec:containers:- name: my-nginx-nodeportimage: nginximagePullPolicy: IfNotPresentports:- containerPort: 80
  • 创建Pod
[root@master1 svc]# kubectl apply -f nodeport.yaml
deployment.apps/my-nginx-nodeport created
[root@master1 svc]# kubectl get pod -l run=my-nginx-nodeport -o wide
NAME                                 READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
my-nginx-nodeport-7c8f496f9f-g92p7   1/1     Running   0          50s   10.1.104.48    node2   <none>           <none>
my-nginx-nodeport-7c8f496f9f-x2kwg   1/1     Running   0          50s   10.1.166.187   node1   <none>           <none>
  • 创建service,代理pod
[root@master1 svc]# cat service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:name: my-nginx-nodeportlabels:run: my-nginx-nodeport
spec:type: NodePortports:- port: 80 protocol: TCPtargetPort: 80nodePort: 30380selector:run: my-nginx-nodeport
[root@master1 svc]# kubectl apply -f service-nodeport.yaml
service/my-nginx-nodeport created
[root@master1 svc]#  kubectl get svc -l run=my-nginx-nodeport
NAME                TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
my-nginx-nodeport   NodePort   10.104.180.39   <none>        80:30380/TCP   30s
[root@master1 svc]# curl 10.104.180.39
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>

注意:
10.104.180.39是k8s集群内部的service ip地址,只能在k8s集群内部访问,在集群外无法访问。

  • 在集群外访问service
[root@master1 svc]# curl 192.168.0.184:30380
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>
  • 浏览器访问

服务请求走向:Client-node ip:30380->service ip:80-pod ip:container port

创建Service:type类型是ExternalName

应用场景:跨名称空间访问

需求:default名称空间下的client 服务想要访问nginx-ns名称空间下的nginx-svc服务

[root@master1 svc]# cat client.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: client
spec:replicas: 2selector: matchLabels:app: busyboxtemplate:metadata:labels:app: busyboxspec:containers:- name: busyboximage: busyboximagePullPolicy: IfNotPresentcommand: ['/bin/sh','-c','sleep 36000']
[root@master1 svc]# kubectl apply -f client.yaml
deployment.apps/client configured
[root@master1 svc]# kubectl get pods
NAME                      READY   STATUS        RESTARTS   AGE
client-77b58844fd-8mtfs   1/1     Running       0          12s
client-77b58844fd-x68xm   1/1     Running       0          11s
[root@master1 svc]# kubectl create namespace nginx-ns
namespace/nginx-ns created
[root@master1 svc]# cat client-svc.yaml
apiVersion: v1
kind: Service
metadata:name: busyboxlabels:app: busybox
spec:type: ExternalNameexternalName: nginx-svc.nginx-ns.svc.cluster.localports:- name: testport: 80targetPort: 80
[root@master1 svc]# kubectl apply -f client-svc.yaml
service/busybox created

该文件中指定了到 nginx-svc 的软链,让使用者感觉就好像调用自己命名空间的服务一样。

[root@master1 svc]# cat server-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: nginxnamespace: nginx-ns
spec:replicas: 1selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginximagePullPolicy: IfNotPresent
[root@master1 svc]# kubectl apply -f server-nginx.yaml
deployment.apps/nginx created
[root@master1 svc]# kubectl get pods -n nginx-ns
NAME                     READY   STATUS    RESTARTS   AGE
nginx-7cf7d6dbc8-rctnm   1/1     Running   0          32s
[root@master1 svc]# cat nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:name: nginx-svcnamespace: nginx-ns
spec:selector:app: nginxports:- name: testprotocol: TCPport: 80targetPort: 80
[root@master1 svc]# kubectl apply -f nginx-svc.yaml
service/nginx-svc created
  • 登录到client pod访问测试
    wget -q -O - budybox-svc.default.svc.cluster.local
    wget -q -O - nginx-svc.nginx-ns.svc.cluster.local
    上面两个请求的结果一样
[root@master1 svc]# kubectl exec -it client-77b58844fd-8mtfs -- /bin/sh
/ #  wget -q -O - busybox.default.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>
/ # wget -q -O - nginx-svc.nginx-ns.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>

k8s最佳实践:映射外部服务实战案例

场景1:k8s集群引用外部的mysql数据库

在node2节点上安装mysql数据库

[root@node2 ~]# yum install mariadb-server.x86_64 -y
[root@node2 ~]# systemctl start mariadb
[root@master1 mysql]# cat mysql-service.yaml
apiVersion: v1
kind: Service
metadata:name: mysql
spec:type: ClusterIPports:- port: 3306
[root@master1 mysql]# kubectl apply -f mysql-service.yaml
service/mysql created
[root@master1 mysql]# kubectl get svc | grep mysql
mysql        ClusterIP   10.102.174.113   <none>        3306/TCP   21s
[root@master1 mysql]# kubectl describe svc mysql
Name:              mysql
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Families:       <none>
IP:                10.102.174.113
IPs:               10.102.174.113
Port:              <unset>  3306/TCP
TargetPort:        3306/TCP
Endpoints:         <none>    # 还么有endpoints
Session Affinity:  None
Events:            <none>
[root@master1 mysql]# cat mysql_endpoint.yaml
apiVersion: v1
kind: Endpoints
metadata:name: mysql
subsets:
- addresses:- ip: 192.168.0.184ports:- port: 3306
[root@master1 mysql]# kubectl apply -f mysql_endpoint.yaml
endpoints/mysql created
[root@master1 mysql]# kubectl describe svc mysql
Name:              mysql
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Families:       <none>
IP:                10.102.174.113
IPs:               10.102.174.113
Port:              <unset>  3306/TCP
TargetPort:        3306/TCP
Endpoints:         192.168.0.184:3306
Session Affinity:  None
Events:            <none>

上面配置就是将外部IP地址和服务引入到k8s集群内部,由service作为一个代理来达到能够访问外部服务的目的。

Service代理:kube-proxy组件详解

kube-proxy组件介绍

Kubernetes service只是把应用对外提供服务的方式做了抽象,真正的应用跑在Pod中的container里,我们的请求转到kubernetes nodes对应的nodePort上,那么nodePort上的请求是如何进一步转到提供后台服务的Pod的呢? 就是通过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。

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将会非常庞大,性能还会再打折
iptables代理模式由Kubernetes 1.1版本引入,自1.2版本开始成为默认类型。

3、ipvs方式:


Kubernetes自1.9-alpha版本引入了ipvs代理模式,自1.11版本开始成为默认设置。客户端
请求时到达内核空间时,根据ipvs的规则直接分发到各pod上。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:不排队调度

如果某个服务后端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规则.

kube-proxy生成的iptables规则分析

1、service的type类型是ClusterIp,iptables规则分析

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

[root@master1 svc]# kubectl apply -f pod_test.yaml
deployment.apps/my-nginx created
[root@master1 svc]# kubectl apply -f service-test.yaml
service/my-nginx created[root@master1 svc]# kubectl get svc -l run=my-nginx
NAME       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.108.158.116   <none>        80/TCP    19m
[root@master1 svc]# kubectl get pods -l run=my-nginx -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
my-nginx-69f769d56f-vj47j   1/1     Running   0          19m   10.1.166.191   node1   <none>           <none>
my-nginx-69f769d56f-wcvvw   1/1     Running   0          19m   10.1.104.51    node2   <none>           <none>
[root@master1 ~]# iptables -t nat -L | grep 10.108.158.116
KUBE-MARK-MASQ  tcp  -- !10.1.0.0/16          10.108.158.116       /* default/my-nginx cluster IP */ tcp dpt:http
KUBE-SVC-L65ENXXZWWSAPRCR  tcp  --  anywhere             10.108.158.116       /* default/my-nginx cluster IP */ tcp dpt:http
[root@master1 ~]#  iptables -t nat -L | grep KUBE-SVC-L65ENXXZWWSAPRCR
KUBE-SVC-L65ENXXZWWSAPRCR  tcp  --  anywhere             10.108.158.116       /* default/my-nginx cluster IP */ tcp dpt:http
Chain KUBE-SVC-L65ENXXZWWSAPRCR (1 references)[root@master1 ~]# iptables -t nat -L | grep 10.1.166.191
KUBE-MARK-MASQ  all  --  10.1.166.191         anywhere             /* default/my-nginx */
DNAT       tcp  --  anywhere             anywhere             /* default/my-nginx */ tcp to:10.1.166.191:80
[root@master1 ~]# iptables -t nat -L | grep 10.1.104.51
KUBE-MARK-MASQ  all  --  10.1.104.51          anywhere             /* default/my-nginx */
DNAT       tcp  --  anywhere             anywhere             /* default/my-nginx */ tcp to:10.1.104.51:80

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

2、service的type类型是nodePort,iptables规则分析

[root@master1 svc]# kubectl apply -f nodeport.yaml
deployment.apps/my-nginx-nodeport created
[root@master1 svc]# kubectl apply -f service-nodeport.yaml
service/my-nginx-nodeport created[root@master1 svc]#  kubectl get pods -l  run=my-nginx-nodeport
NAME                                 READY   STATUS    RESTARTS   AGE
my-nginx-nodeport-7c8f496f9f-6w8vx   1/1     Running   0          3m6s
my-nginx-nodeport-7c8f496f9f-9fcdp   1/1     Running   0          3m6s
[root@master1 svc]# kubectl get svc -l run=my-nginx-nodeport
NAME                TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
my-nginx-nodeport   NodePort   10.108.175.213   <none>        80:30380/TCP   3m19s
[root@master1 ~]# iptables -t nat -S | grep 30380
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp --dport 30380 -j KUBE-MARK-MASQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp --dport 30380 -j KUBE-SVC-J5QV2XWG4FEBPH3Q[root@master1 ~]# iptables -t nat -S | grep KUBE-SVC-J5QV2XWG4FEBPH3Q
-N KUBE-SVC-J5QV2XWG4FEBPH3Q
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp --dport 30380 -j KUBE-SVC-J5QV2XWG4FEBPH3Q
-A KUBE-SERVICES -d 10.108.175.213/32 -p tcp -m comment --comment "default/my-nginx-nodeport cluster IP" -m tcp --dport 80 -j KUBE-SVC-J5QV2XWG4FEBPH3Q
-A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-MHF5N3UV7NBWYFWH
-A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -j KUBE-SEP-LVF3XOSN4K3ORMRF[root@master1 ~]# iptables -t nat -S | grep  KUBE-SEP-MHF5N3UV7NBWYFWH
-N KUBE-SEP-MHF5N3UV7NBWYFWH
-A KUBE-SEP-MHF5N3UV7NBWYFWH -s 10.1.104.52/32 -m comment --comment "default/my-nginx-nodeport" -j KUBE-MARK-MASQ
-A KUBE-SEP-MHF5N3UV7NBWYFWH -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp -j DNAT --to-destination 10.1.104.52:80
-A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-MHF5N3UV7NBWYFWH[root@master1 ~]# iptables -t nat -S | grep KUBE-SVC-J5QV2XWG4FEBPH3Q
-N KUBE-SVC-J5QV2XWG4FEBPH3Q
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp --dport 30380 -j KUBE-SVC-J5QV2XWG4FEBPH3Q
-A KUBE-SERVICES -d 10.108.175.213/32 -p tcp -m comment --comment "default/my-nginx-nodeport cluster IP" -m tcp --dport 80 -j KUBE-SVC-J5QV2XWG4FEBPH3Q
-A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-MHF5N3UV7NBWYFWH
-A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -j KUBE-SEP-LVF3XOSN4K3ORMRF[root@master1 ~]# iptables -t nat -S | grep  KUBE-SEP-MHF5N3UV7NBWYFWH
-N KUBE-SEP-MHF5N3UV7NBWYFWH
-A KUBE-SEP-MHF5N3UV7NBWYFWH -s 10.1.104.52/32 -m comment --comment "default/my-nginx-nodeport" -j KUBE-MARK-MASQ
-A KUBE-SEP-MHF5N3UV7NBWYFWH -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp -j DNAT --to-destination 10.1.104.52:80
-A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-MHF5N3UV7NBWYFWH

Service服务发现:coredns组件详解

DNS是什么?

DNS全称是Domain Name System:域名系统,是整个互联网的电话簿,它能够将可被人理解的域名翻译成可被机器理解IP地址,使得互联网的使用者不再需要直接接触很难阅读和理解的IP地址。域名系统在现在的互联网中非常重要,因为服务器的 IP 地址可能会经常变动,如果没有了 DNS,那么可能 IP 地址一旦发生了更改,当前服务器的客户端就没有办法连接到目标的服务器了,如果我们为 IP 地址提供一个『别名』并在其发生变动时修改别名和 IP 地址的关系,那么我们就可以保证集群对外提供的服务能够相对稳定地被其他客户端访问。DNS 其实就是一个分布式的树状命名系统,它就像一个去中心化的分布式数据库,存储着从域名到 IP 地址的映射。

CoreDNS?

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

[root@master1 coredns]# cat dns.yaml
apiVersion: v1
kind: Pod
metadata:name: dns
spec:containers:- name: dnsimage: busyboximagePullPolicy: IfNotPresentcommand: ["/bin/sh","-c","sleep 3600"]restartPolicy: Always
root@master1 coredns]# kubectl apply -f dns.yaml
pod/dns created[root@master1 coredns]# kubectl exec -it dig -- /bin/sh
/ # nslookup kubernetes
Server:         10.96.0.10
Address:        10.96.0.10#53Name:   kubernetes.default.svc.cluster.local
Address: 10.96.0.1/ # nslookup 10.96.0.1
Server:         10.96.0.10
Address:        10.96.0.10#531.0.96.10.in-addr.arpa  name = kubernetes.default.svc.cluster.local

在k8s中创建service之后,service默认的FQDN是..svc.cluster.local,那么k8s集群内部的服务就可以通过FQDN访问

k8s之四层负载均衡Service:概念、原理解读相关推荐

  1. lvs工作在第几层_LVS 原理(调度算法、四种模式、四层负载均衡和七层 的区别)...

    目录 lvs的调度算法 lvs的四种模式 四层均衡负载和七层的区别 1.lvs调度算法(最常用的四种) 轮叫调度(rr) 调度器通过外部请求的顺序轮流分配到集群中的真实服务器上,对每台服务器都是均等的 ...

  2. CDN基于谷歌开源Katran四层负载均衡方案的应用 - 原理篇

    CDN作为一种分布式缓存加速服务,对于访问量级大的区域,单个节点带宽建设一般可达百Gb量级,对外配置虚拟IP提供服务,节点内部需要通过多层负载均衡(LB)将请求分发到内部的若干台服务器进行处理.一般情 ...

  3. LVS四层负载均衡原理解析

    一.网络层协议 网络协议是由七层ISO协议定义,自底向上分为物理层.数据链路层.网络层.传输层.会话层.表现层和应用层. 四层负载均衡主要工作在网络七层ISO协议的第四层,即传输层.传输层的代表协议为 ...

  4. k8s 如何实现负载均衡?

    文章目录 一.四层与七层 1.四层负载均衡 2.七层负载均衡 二.四层负载均衡实现:kube-proxy 1.userspace 模式 2.iptables 模式 3.ipvs 模式 三.七层负载均衡 ...

  5. LVS四层负载均衡集群

    大型网站高并发解决方案LVS 文章目录 大型网站高并发解决方案LVS 一.集群功能分类 LB 图示 负载均衡产品分类 软件负载均衡设备 硬件负载均衡设备 HA 二.负载均衡 负载均衡的主要方式 htt ...

  6. Nginx基于TCP的四层负载均衡介绍

    Nginx Plus的商业授权版开始具有TCP负载均衡的功能.从Nginx 1.7.7版本开始加入的,现在变成了一个商业收费版本,想要试用,需要在官网申请.也就是说,Nginx除了以前常用的HTTP负 ...

  7. f5 会话保持 负载均衡_四层负载均衡和七层负载均衡区别在哪里?

    年后至今这段时间工作重心都在调整公司现有API Gateway的系统架构以及对现有技术栈选型.经过对主流互联网网关所实现各种方案的调研,我们在API Gateway前置一层接入层,接入层主要用于实现限 ...

  8. linux nginx 安装stream,Centos7-64bit-编译安装配置Nginx stream四层负载均衡 动态加载

    Centos7-64bit-编译安装配置Nginx stream四层负载均衡 动态加载 2018-08-10 17:12 分享人:老牛 yum install screen -y && ...

  9. 利用集群技术实现Web服务器的负载均衡 集群和负载均衡的概念

    集群和负载均衡的概念 集群(Cluster) 所谓集群是指一组独立的计算机系统构成的一个松耦合的多处理器系统,它们之间通过网络实现进程间的通信.应用程序可以通过网络共享内存进行消息传送,实现分布式计算 ...

  10. 负载均衡集群 [ 1 ] ---集群的认识,四层负载,七层负载 ,LVS 实现四层负载均衡

    文章目录 负载均衡集群 1.集群是什么? 2.负载均衡集群技术 3.负载均衡集群技术的实现 4.负载均衡分类 5.四层负载均衡(基于IP+端口的负载均衡) 6.七层的负载均衡(基于虚拟的URL或主机I ...

最新文章

  1. jittor 和pytorch的生成网络对比之aae
  2. python io流a+_Python基础——文件操作及IO流
  3. php npm,php – Laravel中NPM,Composer和Bower的区别?
  4. Tensorflow基础语法和概念
  5. Javascript与未来十年的数据编程
  6. 产品固件(系统)升级——curl/wget
  7. Java系统架构的演化之路
  8. 清新脱俗的Java选课系统
  9. VBS表白代码以及一些注意事项
  10. 计算机无法安装网卡驱动,电脑经验遇到网卡驱动安装不上的问题,该如何解决呢?一起来看看...
  11. 信捷无线触摸屏与plc实现多从站MODBUS通讯
  12. 中国传统色彩十六进制颜色码图片大全
  13. 电商后台管理系统——商品管理
  14. MMD布料解算【新手小白】
  15. Python爬虫:对Uniqlo、Zara、HM等快销品牌的门店数量作统计并展示
  16. 【图像处理】PS曲线工具matlab实现 交互自定义灰度映射(Gray Level Transformation)附代码链接
  17. 《学习的本质》读后感
  18. 中西方对时间的差异_中西方时间表达的认知差异
  19. 实时视频通话超低延迟架构的思考与实践
  20. 抽象类和接口的小程序

热门文章

  1. apache实验报告 linux_Apache服务器配置实验报告
  2. Win10电脑主板型号怎么查看
  3. SQL从特定位置检索记录
  4. 【云驻共创】华为云数据库之大数据入门与应用(全)
  5. Elasticsearch:在华为大数据集群 从c80升级至651版本后,执行es命令修改配置失败
  6. linux强制修改只读文件权限
  7. 栈(LIFO)的存储
  8. java后端必会【基础知识点】
  9. 概率分布 ---- 泊松分布
  10. 用Gitolite 构建 Git 服务器