k8s基础()之Service

什么是Service (服务)

k8s的service怎么使用grpc协议 k8s中service_k8s

RC与Service的关系.png

Service是Kubernetes里最核心的资源对象之一,Service定义了一个服务的访问入口地址,前端的应用(Pod)通过这个入口地址访问其背后的一组由Pod副本组成的集群实例。 Service与其后端Pod副本集群之间则是通过Label Selector来实现"无缝对接"。而RC的作用实际上是保证Service 的服务能力和服务质量处于预期的标准。

k8s的service怎么使用grpc协议 k8s中service_kubernetes_02

通过分析、识别并建模系统中所有服务为微服务—Kubernetes Service,最终我们的系统由多个提供不同业务能力而又彼此独立的微服务单元组成,服务之间通过TCP/IP进行通信,从而拥有了强大的分布式能力、弹性扩展能力、容错能力

既然每个Pod都会被分配一个单独的IP地址,而且每个Pod都提供了一个独立的Endpoint**(Pod IP+ContainerPort)**以被客户端访问,现在多个Pod副本组成了一个集群来提供访问。

Kubernetes 需要在每个Node上安装kube-proxy,kube-proxy进程其实就是一个智能的软件负载均衡器,它负责把对Service的请求转发到后端的某个Pod实例上,并在内部实现服务的负载均衡与会话保持机制。

Kubernetes发明了一个很巧明的设计,Service不是共用一个负载均衡器的IP地址,而是每个Service分配了一个全局唯一的虚拟IP地址,这个虚拟IP被称为**Cluster IP**。这样每个服务就变成了具备唯一IP地址的"通信节点",服务调用就变成了最基础的TCP网络通信问题

Pod的Endpoint地址会随着Pod的销毁和重新创建而发生改变,因为新的Pod地址与之前的旧的Pod不同。而Service一旦被创建,Kubernetes就会自动为它分配一个可用的Cluster IP,而且在Service的整个声明周期内,它的Cluster IP不会发生改变。所以只要将Service的name与Service的Cluster IP地址做一个DNS域名映射即可解决问题


创建一个Service

案例:手动创建一个Service

cat > abcdocker-server.yaml <<EOF 
kind: Service
apiVersion: v1
metadata:
  name: mallh5-service
  namespace: abcdocker
spec:
  selector:
    app: mallh5web
  type: NodePort
  ports:
    - protocol: TCP      #仅支持TCP和UDP,不写默认TCP
      port: 3017
      targetPort: 5003
      nodePort: 31122
EOF

参数

解释

Port

port表示:service暴露在cluster ip(Seriver ip )上的端口,:port 是提供给集群内部客户访问service的入口。

NodePort

nodePort是kubernetes提供给集群外部客户访问service入口的一种方式(另一种方式是LoadBalancer,:nodePort 是提供给集群外部客户访问service的入口。

targetPort

targetPort很好理解,targetPort是pod上的端口,从port和nodePort上到来的数据最终经过kube-proxy流入到后端pod的targetPort上进入容器。

创建

[root@master test]# kubectl create -f abcdocker-server.yaml
service/mallh5-service created

查看server详细信息

[root@master test]# kubectl get service --namespace=abcdocker
NAME             TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
mallh5-service   NodePort   10.254.22.153   <none>        3017:31122/TCP   13m

[root@master test]# kubectl get service
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes      ClusterIP   10.254.0.1       <none>        443/TCP        35d
nginx-service   NodePort    10.254.200.178   <none>        80:31000/TCP   34d

通过kubectl get endpoints可以查看到网络

svc详细信息

[root@abcdockeryaml]# kubectl describe svc myserver
Name:              myserver     #名称
Namespace:         default      #命名空间
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx    #匹配Pod标签
Type:              ClusterIP    #svc ip类型
IP:                10.254.219.137   #svc IP地址
Port:              mynginx-http  80/TCP   #svc端口(可以根据业务进行调整端口)
TargetPort:        80/TCP       #pod 端口
Endpoints:         172.30.144.2:80  #匹配pod IP
Session Affinity:  None
Events:            <none>

温馨提示: targetPort支持字符串 场景: 如果有2个pod lables标签都为nginx,但是我们的业务知识匹配其中一个,就可以在其中一个pod中为containerPort下添加一个name变量,然后通过svc引用的时候,targetPort直接写入变量就可以

## deployment 文件如下
---
apiVersion: v1
kind: ReplicationController
metadata:
  name: rc-demo
  labels:
     app: rc-demo
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx-demo
          image: nginx
          resources:
              requests:
                cpu: 100m
          ports:
          - containerPort: 80
            name: nginxweb
#将ports容器端口添加一个名称,在svc中就可以直接使用名称


##svc yaml
apiVersion: v1
kind: Service
metadata:
  name: myserver
  namespace: default
spec:
  selector:
    app: nginx
  ports:
    - name: mynginx-http
      protocol: TCP
      port: 80
      targetPort: nginxweb

#targerPort直接引用就可以


#describe查看结果
[root@abcdocker yaml]# kubectl describe svc myserver
Name:              myserver
Namespace:         default
Labels:            <none>
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myserver","namespace":"default"},"spec":{"ports":[{"name":"myngin...
Selector:          app=nginx
Type:              ClusterIP
IP:                10.254.219.137
Port:              mynginx-http  80/TCP
TargetPort:        nginxweb/TCP
Endpoints:         <none>
Session Affinity:  None
Events:            <none>

外部系统访问Service问题

我们需要掌握kubernetes中的三种IP

1.Node IP:Node节点IP地址 2.Pod IP:Pod的IP地址 3.Cluster IP:Service的IP地址

参数解释: 1.Node IP是Kubernetes集群中每个节点的物理网卡的IP地址,这是一个真实存在的物理网络,所有属于这个网络的服务器之间都能通过这个网络直接通讯,不管他们中间是否含有不属于Kubernetes集群中的节点。想Kubernetes之外的节点访问Kubernetes集群内的节点或者TCP/IP服务时,必须通过Node IP

k8s的service怎么使用grpc协议 k8s中service_k8s_03

server访问图.png

2.Pod IP是每个Pod的IP地址,它是Docker Engine 根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络,Kubernetes要求位于不同Node上的Pod能够彼此直接通讯,所以Kubernetes里一个Pod里的容器访问另外一个Pod里的容器,就是通过Pod IP所在的虚拟二层网络进行通信,而真实的TCP/IP流量则是通过Node IP所在的物理网卡流出

3.Cluster IP,它是一个虚拟IP,但更像是一个伪造的IP网络

(1)Cluster IP仅仅作用于Kubernetes Service对象,并由Kubernetes管理和分配IP地址(来源于Cluster IP地址池)

(2)Cluster IP无法被Ping,因为没有一个"实体网络对象"来响应

(3)在Kubernetes集群内,Node IP、Pod IP、Cluster IP之间的通信,采用的是Kubernetes自己设计的特殊路由规则

简述NodePort实现方式

NodePort的实现方式是在Kubernetes集群里的每个Node上为需要外部访问的Service开启一个对应的TCP请求,外部系统只要用任意Node的IP地址+具体的NodePort端口即可访问服务,在任意node上运行netstat服务,我们可以看到NodePort端口被监听 [

k8s的service怎么使用grpc协议 k8s中service_TCP_04

但NodePort还没有完全解决外部访问Service的所有问题,比如负载均衡的问题,假如说我们集群中有10个Node,则此时最好有一个负载均衡,外部的请求只需要访问此负载均衡器的IP地址,由负载均衡负责转发流量到后面某个NodePort上

k8s的service怎么使用grpc协议 k8s中service_kubernetes_05

上图中Load balancer组件独立于Kubernetes集群之外,通常是一个硬件的负载均衡器,或者是以软件方式实现的,例如Haproxy或者Nginx。对于每个Service,我们通常配置一个对应的Load balancer实例来转发流量到后端的Node。

例子

---
apiVersion: v1
kind: Service
metadata:
  name: myserver
  namespace: default
spec:
  selector:
    app: nginx
  type: NodePort
  ports:
    - name: mynginx-http
      protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30001       #如果我们不指定nodeport端口,会生成一个随机端口30000-32767之间

创建完成进行查看svc

[root@abcdocker yaml]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.254.0.1       <none>        443/TCP        83d
myserver     NodePort    10.254.201.121   <none>        80:30001/TCP   5s

[root@abcdocker yaml]# kubectl describe svc myserver
Name:                     myserver
Namespace:                default
Labels:                   <none>
Annotations:              kubectl.kubernetes.io/last-applied-configuration:
                            {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myserver","namespace":"default"},"spec":{"ports":[{"name":"myngin...
Selector:                 app=nginx
Type:                     NodePort
IP:                       10.254.201.121
Port:                     mynginx-http  80/TCP
TargetPort:               80/TCP
NodePort:                 mynginx-http  30001/TCP
Endpoints:                172.30.144.2:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

接下来就可以使用浏览器进行访问了

k8s的service怎么使用grpc协议 k8s中service_k8s_06


我们可以看到,svc访问实际上是通过kube-proxy来生成,下面就可以看看kube-proxy的原理

[root@abcdocker yaml]# netstat -lntup|grep 30001
tcp6       0      0 :::30001                :::*                    LISTEN      933/kube-proxy

Kube-proxy 工作原理

1.Service在很多情况下只是一个概念,真正起到Service作用的是kube-proxy服务进程。

2.每个Node节点上都会运行一个kube-proxy服务进程

3.对每个TCP类型的Kubernetes Service,Kube-proxy都会在本地Node节点上建立一个SocketServer来负责接收请求,然后均匀发送到后端某个Pod的端口上,这个过程默认采用Round Robin(rr)负责均衡算法

4.kube-proxy通过查询和监听Api Server中Service与Endpoints的变化,为每个Service都建立一个"服务代理对象",并自动同步。服务代理对象是kube-proxy程序内部的一种架构,它包括一个用于监听此服务请求的SocketServer,SocketServer的端口是随机选择一个本地空闲端口,此外kube-proxy内部创建了一个负载均衡器LoadBalancer

5.针对发生变化的Service列表,kube-proxy会逐个处理

  • 如果没有设置集群IP,则不做任何处理,否则,取该Service的所有端口定义列表
  • 为Service端口分配服务代理对象并为该Service创建相关的iptables规则
  • 更新负载均衡组件中对应Service的转发地址列表

6.kube-proxy在启动时和监听到Service或Endpoint的变化后,会在本机Iptables的NAT表中添加4条规则链。

  • KUBE-PORTABLS-CONTAINER: 从容器中通过Cluster IP和端口号访问service
  • KUBE-PORTALS-HOST: 从主机中通过Cluster IP和端口号访问service
  • KUBE-NODEPORT-CONTAINER: 从容器中通过NODE IP和端口号访问service
  • KUBE-NODEPORT-HOST: 从主机中通过Node IP和端口号访问service

rvice或Endpoint的变化后,会在本机Iptables的NAT表中添加4条规则链。

  • KUBE-PORTABLS-CONTAINER: 从容器中通过Cluster IP和端口号访问service
  • KUBE-PORTALS-HOST: 从主机中通过Cluster IP和端口号访问service
  • KUBE-NODEPORT-CONTAINER: 从容器中通过NODE IP和端口号访问service
  • KUBE-NODEPORT-HOST: 从主机中通过Node IP和端口号访问service

注意:我们也可以给kube-proxy设置一个默认的策略是随机选择一个 pod。 我们也可以实现基于客户端 IP 的会话亲和性,可以将 service.spec.sessionAffinity的值设置为 “ClientIP”(默认值为 “None”)。