10-Kubernetes Service资源

一、Service(svc)

1、理论知识

  • 因为Pod是有生命周期的,所以为了能够给对应的客户端,提供一个固定的访问端点,需要在客户端与服务端(服务Pod资源)之间加一个固定的中间层(service)。service的真正工作还要严重依赖于,在kubermetes集群上部署的一个附件(kubernetes的dns服务),不同版本的kubernetes上的 附件不同,新版本中叫做coredns,kubernetes的1.11版本之前,叫做kube-dns
  • service的名称解析是强依赖于dns附件的,所以,部署完kubernetes集群之后,需要部署一个coredns或者kube-dns
    kubernetes集群要想,能够向客户端提供网络功能,需要依赖于第三方的方案(这种第三方方案可通过cni——container network interface容器网络插件标准的接口,接入任何遵循这种插件标准的第三方方案。)第三方方案有flannel、calico等等。
  • kubernetes集群中有三类网络:【1】、node网络【2】、pod网络(node网络和pod网络是真实的网络,是实实在在配置在某个设备上的,这个设备可能是硬件的,也可能是软件模拟的。);【3】、集群地址(cluster network),也称为service network(这种网络是虚拟的网络,Virtual IP,因为这些IP没有实实在在配置在哪个设备上,它仅是出现在service的规则当中)。
  • 在一定程度上讲,组件kube-proxy始终监视着在Apiserver组件中的有关service的变动信息。一旦有service资源的内容发生变动,包括创建,kube-proxy都要把它转换为当前节点上的,能够实现service资源调度的(包括将用户请求调度到特定pod资源)之上的规则,这种规则可能是iptables,也可能是ipvs,取决于service的实现方式。

10-Kubernetes Service资源_第1张图片

  • 在kubernetes上,service的实现方式有三种模式,【1】、UserSapce(用户空间,这种方式效率很低);【2】、iptables;【3】、ipvs。kubernetes1.1之前的版本用的是userspace,1.1至1.10的版本用的是iptables;1.11后的版本用的是ipvs。如果ipvs没有被激活,则自动降级为iptables。

UserSpace模式

10-Kubernetes Service资源_第2张图片

 iptables工作模式10-Kubernetes Service资源_第3张图片

ipvs工作模式

10-Kubernetes Service资源_第4张图片

2、实践

(1)有关apiserver的IP地址

[root@master manifests]# kubectl get svc   #可以看到安装好集群之后,集群内有一个kubernetes服务,集群中的组件就是通过这个服务与apiserver组件进行通信的。既可以认为i这个IP地址是apiserver的Ip地址,用于集群内部的通信
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1            443/TCP   11d
[root@master manifests]# kubectl get pods -o wide -n kube-system   #下面这个apiserver的Ip地址是用于集群外部通信的
NAME                             READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
coredns-546565776c-448jk         1/1     Running   16         11d   10.244.0.36   master              
coredns-546565776c-k7whb         1/1     Running   16         11d   10.244.0.35   master              
etcd-master                      1/1     Running   18         11d   10.0.2.2      master              
kube-apiserver-master            1/1     Running   18         11d   10.0.2.2      master              
kube-controller-manager-master   1/1     Running   33         11d   10.0.2.2      master              
kube-flannel-ds-amd64-2zd7f      1/1     Running   9          10d   10.0.2.4      node02              
kube-flannel-ds-amd64-6b8pq      1/1     Running   7          10d   10.0.2.3      node01              
kube-flannel-ds-amd64-hs2ck      1/1     Running   25         11d   10.0.2.2      master              
kube-proxy-n7cmq                 1/1     Running   18         11d   10.0.2.2      master              
kube-proxy-qfc2v                 1/1     Running   8          10d   10.0.2.3      node01              
kube-proxy-vpflj                 1/1     Running   9          10d   10.0.2.4      node02              
kube-scheduler-master            1/1     Running   35         11d   10.0.2.2      master              

(2)service的四种类型

[root@master manifests]# kubectl explain svc.spec
    clusterIP    
    ports        <[]Object>
    selector     
    type       默认为ClusterIP
#type有四种模式:ExternalName, ClusterIP, NodePort,  LoadBalancer。
【1】、ExternalName:把集群外部的服务引入到集群内部。
【2】、ClusterIP:仅用于集群内部进行访问的Ip地址
【3】、NodePort:可以接入集群外部的流量
【4】、LoadBalancer:把Kubernetes集群部署在虚拟机上,而虚拟机工作在云环境中,而云环境支持LBAAS(负载均衡及服务的一键调用)。

(3)ClusterIP类型的service

[root@master manifests]# vim deploy-redis.yaml    #用deployment启动一个提供redis服务的Pod
apiVersion: apps/v1
kind: Deployment
metadata:
        name: redis
        namespace: default
spec:
        replicas: 1
        selector:
                matchLabels:
                        app: redis
                        role: logstor
        template:
                metadata:
                        labels:
                                app: redis
                                role: logstor
                spec:
                        containers:
                        - name: redis
                          image: redis:4.0-alpine
                          ports:
                          - name: redis
                            containerPort: 6379


[root@master manifests]# kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
redis-588694bf8c-5mdhr   1/1     Running   0          6s    10.244.1.69   node01              



[root@master manifests]# vim redis-svc.yaml    #为提供redis服务的Pod创建一个service
apiVersion: v1
kind: Service
metadata:
        name: redis
        namespace: default
spec:
        clusterIP: 10.97.97.97
        type: ClusterIP
        ports:
        - port: 6379     #service端口
          targetPort: 6379    #pod端口
        selector:     #要注意这里的标签必须是提供service的pod的标签
                app: redis
                role: logstor


[root@master manifests]# kubectl apply -f redis-svc.yaml 


[root@master manifests]# kubectl get svc -o wide   #可以看到IP和端口都是自己指定的
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE   SELECTOR
kubernetes   ClusterIP   10.96.0.1             443/TCP    11d   
redis        ClusterIP   10.97.97.97           6379/TCP   9s    app=redis,role=logstor



[root@master manifests]# kubectl describe svc redis    #可以看到这个service匹配到的endpoints为10.244.1.69:6379(其实endpoint就是Pod的IP地址+Pod端口)。
Name:              redis
Namespace:         default
Labels:            
Annotations:       Selector:  app=redis,role=logstor
Type:              ClusterIP
IP:                10.97.97.97
Port:                6379/TCP
TargetPort:        6379/TCP
Endpoints:         10.244.1.69:6379
Session Affinity:  None
Events:            
  • service创建完之后,都会在coredns/kube-dns中自动动态添加记录(不止一个记录。资源记录的格式:服务名.命名空间的名字.资源域名后缀,即SVC_NAME.NS_NAME.DOMAIN.LTD.。service的资源域名后缀默认为:svc.cluster.local.。eg:redis这个服务的域名为:redis.default.svc.cluster.local.),所以,只要Kubernetes集群上的coredns/kube-dns是存在的,就可以直接解析service的服务名。
[root@master manifests]# kubectl get svc -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10           53/UDP,53/TCP,9153/TCP   11d


[root@master manifests]# dig -t A myapp.default.svc.cluster.local. @10.96.0.10
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-16.P2.el7_8.6 <<>> -t A myapp.default.svc.cluster.local. @10.96.0.10
;; 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: 25886
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

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

;; ANSWER SECTION:
myapp.default.svc.cluster.local. 30 IN  A       10.99.99.99   #可以看到:解析到的正是myapp这个Service的IP地址

;; Query time: 6 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Fri Sep 11 07:52:55 EDT 2020
;; MSG SIZE  rcvd: 107



[root@master manifests]# kubectl get svc myapp -o wide
NAME    TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE    SELECTOR
myapp   NodePort   10.99.99.99           80:30080/TCP   116m   app=myapp,release=canary

(4)service是如何到达Pod的?

service——EndPoint(Pod的IP地址+Pod的端口)——Pod

(5)NodePort类型的service

[root@master manifests]# cat deploy-demo.yaml     #通过创建deployment来创建pod,以此来提供myapp服务
apiVersion: apps/v1
kind: Deployment
metadata:
        name: myapp-deploy
        namespace: default
spec:
        replicas: 3 
        selector:
                matchLabels:
                        app: myapp
                        release: canary
        template:
                metadata:
                        labels:
                                app: myapp
                                release: canary
                spec:
                        containers:
                        - name: myapp
                          image: ikubernetes/myapp:v3
                          ports:
                          - name: http
                            containerPort: 80

[root@master manifests]# kubectl apply -f deploy-demo.yaml 



[root@master manifests]# vim myapp-svc.yaml 
apiVersion: v1
kind: Service
metadata:
        name: myapp
        namespace: default
spec:
        clusterIP: 10.99.99.99
        type: NodePort
        ports:
        - port: 80
          targetPort: 80
          nodePort: 30080   #nodePort可以不指定,会自动分配(30000-32767)。即使类型是NodePort。如果指定了nodePort,则要确保Node节点(Node01和Node02都要确保)的该节点没有被占用。
        selector:
                app: myapp
                release: canary



[root@master manifests]# kubectl apply -f myapp-svc.yaml 


[root@master manifests]# kubectl get svc -o wide    #可以看到nginx服务中的80(service的端口)映射到了node节点上的30080(node的端口)
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE     SELECTOR
kubernetes   ClusterIP   10.96.0.1             443/TCP        11d     
myapp        NodePort    10.99.99.99           80:30080/TCP   2m38s   app=myapp,release=canary
redis        ClusterIP   10.97.97.97           6379/TCP       128m    app=redis,role=logstor


root@master manifests]# kubectl get pods -o wide
NAME                            READY   STATUS    RESTARTS   AGE     IP            NODE     NOMINATED NODE   READINESS GATES
myapp-deploy-6b9865d969-4rhd8   1/1     Running   0          3m21s   10.244.1.71   node01              
myapp-deploy-6b9865d969-77vwb   1/1     Running   0          3m21s   10.244.2.79   node02              
myapp-deploy-6b9865d969-dr9js   1/1     Running   0          3m21s   10.244.2.78   node02              
redis-588694bf8c-5mdhr          1/1     Running   1          124m    10.244.1.70   node01              
  • 这样,就可以在集群外部的主机上通过命令:"curl http://node01的IP地址或是node02的Ip地址:30080"进行访问Pod了。其过程是:node01的IP地址或node02的IP地址:30080——service的IP地址:80(这里的80为service的端口)——service所管理的Pod的IP地址:80(这里的80为Pod的端口)。下面进行演示:可以在master、node01、node02中的任意一台主机进行测试(因为master、node01、node02相当于集群外部)
[root@master manifests]# while true; do curl http://node01:30080/hostname.html; sleep 1; done
myapp-deploy-6b9865d969-4rhd8
myapp-deploy-6b9865d969-77vwb
myapp-deploy-6b9865d969-4rhd8
myapp-deploy-6b9865d969-77vwb
myapp-deploy-6b9865d969-77vwb
myapp-deploy-6b9865d969-dr9js
#从上面的结果可以看到:自动实现了多个pod之间的轮询。

(6)NodePort类型的service——设置轮询的方式

[root@master manifests]# kubectl explain svc.spec
    sessionAffinity          #有两种方法:ClientIP(来自同一个客户端IP的请求
调度到同一个Endpoint)和None(不做后端映射,随机调取,基于iptables的规则进行调度)。默认是None
[root@master manifests]# kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"ClientIP"}}'     #修改service的调度方式
service/myapp patched
[root@master manifests]# kubectl describe svc myapp
Name:                     myapp
Namespace:                default
Labels:                   
Annotations:              Selector:  app=myapp,release=canary
Type:                     NodePort
IP:                       10.99.99.99
Port:                       80/TCP
TargetPort:               80/TCP
NodePort:                   30080/TCP
Endpoints:                10.244.1.71:80,10.244.2.78:80,10.244.2.79:80
Session Affinity:         ClientIP    #可以看到这里的调度方式为ClientIP。
External Traffic Policy:  Cluster
Events:                   
  • 测试:
[root@master manifests]# while true; do curl http://node01:30080/hostname.html; sleep 1; done     #可以看到来自同一客户端的请求,访问到的Pod是同一个
myapp-deploy-6b9865d969-4rhd8
myapp-deploy-6b9865d969-4rhd8
myapp-deploy-6b9865d969-4rhd8
myapp-deploy-6b9865d969-4rhd8
myapp-deploy-6b9865d969-4rhd8

(7)headless  service(无头service)——在StatefulSet中可以用到

[root@master manifests]# vim myapp-svc-headless.yaml 
apiVersion: v1
kind: Service
metadata:
        name: myapp-svc
        namespace: default
spec:
        clusterIP: None    #指定clusterIP为None即可构造headless service
        ports:
        - port: 80
          targetPort: 80
        selector:
                app: myapp
                release: canary


[root@master manifests]# kubectl apply -f myapp-svc-headless.yaml 


[root@master manifests]# kubectl get svc -o wide   #可以看到myapp-svc这个服务的ClusterIP是None
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE     SELECTOR
kubernetes   ClusterIP   10.96.0.1             443/TCP        11d     
myapp        NodePort    10.99.99.99           80:30080/TCP   86m     app=myapp,release=canary
myapp-svc    ClusterIP   None                  80/TCP         7s      app=myapp,release=canary
redis        ClusterIP   10.97.97.97           6379/TCP       3h32m   app=redis,role=logstor


[root@master manifests]# kubectl get svc -n kube-system   #可以看到kube-system这个命名空间中有kube-dns这个dns服务。
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10           53/UDP,53/TCP,9153/TCP   11d
[root@master manifests]# dig -t A myapp-svc.default.svc.cluster.local. @10.96.0.10  #使用10.96.0.10这个Ip地址对应的kube-dns来解析myapp-svc.default.svc.cluster.local.这个域名

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-16.P2.el7_8.6 <<>> -t A myapp-svc.default.svc.cluster.local. @10.96.0.10
;; 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: 61246
;; flags: qr aa 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-svc.default.svc.cluster.local. IN        A

;; ANSWER SECTION:
myapp-svc.default.svc.cluster.local. 30 IN A    10.244.1.71   #可以看到解析到的IP地址正是Service所管理的后端Pod的IP地址
myapp-svc.default.svc.cluster.local. 30 IN A    10.244.2.78
myapp-svc.default.svc.cluster.local. 30 IN A    10.244.2.79

;; Query time: 0 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Fri Sep 11 07:47:12 EDT 2020
;; MSG SIZE  rcvd: 217


[root@master manifests]# kubectl get pods -o wide --show-labels -l app=myapp
NAME                            READY   STATUS    RESTARTS   AGE    IP            NODE     NOMINATED NODE   READINESS GATES   LABELS
myapp-deploy-6b9865d969-4rhd8   1/1     Running   0          113m   10.244.1.71   node01                          app=myapp,pod-template-hash=6b9865d969,release=canary
myapp-deploy-6b9865d969-77vwb   1/1     Running   0          113m   10.244.2.79   node02                          app=myapp,pod-template-hash=6b9865d969,release=canary
myapp-deploy-6b9865d969-dr9js   1/1     Running   0          113m   10.244.2.78   node02                          app=myapp,pod-template-hash=6b9865d969,release=canary

(8)客户端Pod是如何访问Service所管理的Pod的

情况1:Service是有ClusterIP的

  • 【1】、客户端Pod访问Service的域名
  • 【2】、coredns将Service的域名解析为ClusterIP(ClusterIP只有1个)
  • 【3】、Service将请求调度到多个Endpoints

情况2:Service没有ClusterIP(这种Service称为headless Service)

  • 【1】、客户端Pod访问Service的域名
  • 【2】、coredns将Service的域名解析为多个Endpoints的IP地址(即多个后端Pod的地址)

你可能感兴趣的:(kuber)