service

一种可以访问后端pod的策略,通过访问service来访问到service关联的后端pod,通常是通过Label Selector实现的。

在kubernetes集群中有三类ip地址:

node network(节点网络),pod network(pod 网络),这两种网络地址是我们实实在在配置的,其中节点网络地址是配置在节点接口之上,而pod网络地址是配置在pod资源之上的,因此这些地址都是配置在某些设备之上的,这些设备可能是硬件,也可能是软件模拟的,cluster network(集群地址,也成为service network),这个地址是虚拟的地址(virtual ip),没有配置在某个接口上,只是出现在service的规则当中(无法ping 通)。

每个K8s节点上都有一个工作的组件叫做kube-proxy,这个组件将始终监视着apiserver中有关service资源的变动信息,需要跟master之上的apiserver交互,随时连接到apiserver上获取任何一个与service资源相关的资源变动状态,这种是通过kubernetes中固有的一种请求方法watch(监视)来实现的,一旦有service资源的内容发生变动(如创建,删除),kube-proxy都会将它转化成当前节点之上的能够实现service资源调度,把我们请求调度到后端特定的pod资源之上的规则,这个规则可能是iptables,也可能是ipvs,取决于service的实现方式

service实现方式

第一种:iptables
客户端ip请求时直接请求service的ip,这个请求报文被本地内核空间中的service规则所截取,进而直接调度给相关的pod,这个方式是直接工作在内核空间,由iptables规则直接实现

第二种:ipvs
客户端请求到达内核空间之后直接由ipvs规则来调度到相关的pod资源

1.11之前的版本使用的是iptables,1.11+版本使用的是ipvs,ipvs如果没有被激活就会自动降级为iptables。 如果某个服务背后的pod资源发生改变,比如service的标签选择器适用的pod又多了一个,这个pod使用的信息会立即反应在apiserver上,kube-proxy能监听到这个service的变化,将其立即转为service规则(如iptables规则)

通过Service访问pod

(1)创建pod

[root@master ~]# vim nginx.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
     containers:
     - name: nginx
       image: nginx
       ports:
       - containerPort: 80

查看创建的pod
[root@master ~]# kubectl apply -f nginx.yaml

[root@master ~]# kubectl get pod -l app=nginx -owide
NAME                               READY   STATUS    RESTARTS   AGE   IP            NODE    
nginx-deployment-d46f5678b-pl96x   1/1     Running   0          63s   10.244.1.56   node1  
nginx-deployment-d46f5678b-tbhzb   1/1     Running   0          63s   10.244.1.55   node1   
[root@master ~]# curl 10.244.1.56
Welcome to nginx!
也可以这样查看后端IP
[root@master ~]# kubectl get pod -l app=nginx -o yaml | grep podIP:
      cni.projectcalico.org/podIP: 10.244.1.56/32
    podIP: 10.244.1.56
      cni.projectcalico.org/podIP: 10.244.1.55/32
    podIP: 10.244.1.55

(2)创建ClusterIP类型service

[root@master ~]# vim service.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    app: nginx

查看service、后端endpoint IP

[root@master ~]# kubectl get svc
NAME           TYPE      CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
nginx-svc    ClusterIP   10.99.155.188   <none>        80/TCP    5s
[root@master ~]# kubectl describe service nginx-svc
Name:              nginx-svc
Namespace:         default
Labels:            app=nginx
Annotations:       <none>
Selector:          app=nginx
Type:              ClusterIP
IP Families:       <none>
IP:                10.99.155.188   #ClusterIP(service的)访问时会自动负载均衡到后端IP
IPs:               <none>
Port:              <unset>  80/TCP    #service的端口
TargetPort:        80/TCP				#容器接收流量的端口
Endpoints:         10.244.1.55:80,10.244.1.56:80     #后端IP
Session Affinity:  None
Events:            <none>
[root@master ~]# kubectl get ep nginx-svc
NAME        ENDPOINTS                       AGE
nginx-svc   10.244.1.55:80,10.244.1.56:80   5m3s
[root@master ~]# curl 10.99.155.188
Welcome to nginx!

service默认的是ClusterIP,只能集群内部访问,此IP无法ping通,存放在IPtables规则里

[root@master ~]# kubectl explain service.spec.type
KIND:     Service
VERSION:  v1

FIELD:    type <string>

DESCRIPTION:
     type determines how the Service is exposed. Defaults to ClusterIP. Valid
     options are ExternalName, ClusterIP, NodePort, and LoadBalancer.
     "ExternalName" maps to the specified externalName. "ClusterIP" allocates a
     cluster-internal IP address for load-balancing to endpoints. Endpoints are
     determined by the selector or if that is not specified, by manual
     construction of an Endpoints object. If clusterIP is "None", no virtual IP
     is allocated and the endpoints are published as a set of endpoints rather
     than a stable IP. "NodePort" builds on ClusterIP and allocates a port on
     every node which routes to the clusterIP. "LoadBalancer" builds on NodePort
     and creates an external load-balancer (if supported in the current cloud)
     which routes to the clusterIP. More info:

type的DESCRIPTION可选项 ExternalName, ClusterIP, NodePort, and LoadBalancer四种

ClusterlP:通过集群的内部IP暴露服务,只能够在集群内部可以访问,默认的Type.
NodePort: 通过每个Node 上的IP和静态端口(NodePort)暴露服务。可以通过浏览器访问,NodePort服务会路由到ClusterlP服务
LoadBalancer: 使用云提供商的负载局衡器,外部的负载均衡器可以路由到NodePort 服务和lusterlP服务。
ExternalName :容器可以使用自定义的域名访问外网,通过返回CNAME和它的值,可以将服务映射到externalName字段的内容

(2)创建NodePort类型service

[root@master ~]# vim service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx
spec:
  type: NodePort
  ports:
  - port: 80
    protocol: TCP
  selector:
    app: nginx

[root@master ~]# kubectl apply -f service.yaml
service/nginx-svc configured
[root@master ~]# kubectl get svc 
NAME      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
nginx-svc   NodePort    10.99.155.188   <none>   80:32052/TCP  57m

此时可以完成集群的外部访问 浏览器访问node节点:端口号,http://192.168.1.12:32052/

Welcome to nginx!

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

(3)没有selector的Service

准备一台apache服务器

[root@node3 ~]# yum -y install httpd
[root@node3 html]# echo 1111 > /var/www/html/index.html
[root@node3 html]# systemctl start httpd
[root@node3 html]# curl 192.168.1.13
1111

创建没有selector的Service

[root@master ~]# vim ser-noselect.yaml
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
由于这个Service没有selector,就不会创建相关的Endpoints对象。可以手动将Service映射到指定的Endpoints:
[root@master ~]# vim endpoint.taml
kind: Endpoints
apiVersion: v1
metadata:
  name: my-service
subsets:
  - addresses:
      - ip: 192.168.1.13  #可以是集群外部的地址,必须同网段
    ports:
      - port: 80

[root@master ~]# kubectl apply -f endpoint.taml
[root@master ~]# kubectl apply -f ser-noselect.yaml
[root@master ~]# kubectl get svc
NAME                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   
my-service                ClusterIP   10.105.253.111   <none>        80/TCP     
[root@master ~]# kubectl get endpoints
NAME                      ENDPOINTS                       AGE
my-service                192.168.1.13:80                 4m29s

访问没有 selector的Service,与有selector的Service的原理相同。请求将被路由到用户定义的Endpoint(该示例中为192.168.1.13:80)。

[root@node1 ~]# curl 10.105.253.111
1111

ExternalName是Service的特例,它没有selector,也没有定义任何的端口和 Endpoint。当查询主机my-service.prod.svc.cluster.local时,集群的DNS服务将返回一个值为my.database.example.com的CNAME记录。(???)

[root@master ~]# vim externalname.yaml
kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: database.example.com
[root@master ~]# kubectl apply -f externalname.yaml
service/my-service configured
[root@master ~]# kubectl get svc -n prod
NAME         TYPE           CLUSTER-IP   EXTERNAL-IP          PORT(S)   AGE
my-service   ExternalName   <none>       database.example.com   <none>    69s

Headless Service

有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为None来创建 Headless Service。
headless允许开发人员自由寻找他们自己的方式,从而降低与 Kubernetes 系统的耦合性。 应用仍然可以使用一种自注册的模式和适配器,对其它需要发现机制的系统能够很容易地基于这个 API 来构建。访问用 servicename.namespace.svc.cluster.local

[root@master ~]# vim headless-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx
spec:
  clusterIP: None
  ports:
  - port: 80
    protocol: TCP
  selector:
    app: nginx

[root@master ~]# kubectl apply -f  service.yaml
service/nginx-svc created
[root@master ~]# kubectl get svc
NAME      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
nginx-svc   ClusterIP   None            <none>     80/TCP      43s

定义selector的Headless Service,Endpoint 控制器在 API 中创建了 Endpoints 记录,并且修改 DNS 配置返回 A 记录(地址),通过这个地址直接到达Service的后端Pod上
不配置 Selector对没有定义 selector 的 Headless Service,Endpoint 控制器不会创建Endpoints记录。

[root@master ~]# kubectl get ep
NAME                      ENDPOINTS           AGE
nginx-svc                 <none>              33s
Logo

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

更多推荐