K8S-Service资源对象

一、概述

在kubernetes中,pod是应用程序的载体,我们可以通过pod的ip来访问应用程序,但是pod的ip地址不是固定的,这也就意味着不方便直接采用pod的ip对服务进行访问。

为了解决这个问题,kubernetes提供了Service资源,Service会对提供同一个服务 的多个pod进行聚合,并且提供一个统一的入口地址。通过访问Service的入口地址就能访问到后面的pod服务。 Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行着一个kube-proxy服务进程。当创建Service的时候会通过api-server向etcd写入创建的service的信息,而kube-proxy会基于监听的机制发现这种Service的变动,然后它会将最新的Service信息转换成对应的访问规则。

二、 Kube-proxy的三种模式

2.1 userspace 模式

userspace模式下,kube-proxy会为每一个Service创建一个监听端口,发向Cluster IP的请求被Iptables规则重定向到kube-proxy监听的端口上,kube-proxy根据LB(LoadBalance,负载均衡)算法选择一个提供服务的Pod并和其建立链接,以将请求转发到Pod上。 **该模式下,kube-proxy充当了一个四层负载均衡器的角色。**由于kube-proxy运行在userspace中,在进行转发处理时会增加内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率比较低。

2.2 iptables 模式

iptables模式下,kube-proxy为service后端的每个Pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod IP。 该模式下kube-proxy不承担四层负载均衡器的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的LB策略,当后端Pod不可用时也无法进行重试。

2.3 ipvs 模式

ipvs模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对iptables转发效率更高。除此以外,ipvs支持更多的LB算法。

复制代码
# 此模式必须安装ipvs内核模块(集群部署的时候已安装),否则会降级为iptables;modprobe ip_vs
# 开启ipvs,cm: configmap
[root@k8s-master01 ~]# modprobe ip_vs
[root@k8s-master01 ~]# yum install -y ipvsadm
[root@k8s-master01 ~]# kubectl edit cm kube-proxy -n kube-system
# 修改mode: "ipvs"
[root@k8s-master01 ~]# kubectl delete pod -l k8s-app=kube-proxy -n kube-system
[root@node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.32.128:30080 rr
  -> 172.16.79.79:80              Masq    1      0          0         
TCP  172.16.32.128:32665 rr
  -> 172.16.79.82:8443            Masq    1      0          0         
TCP  172.17.0.1:30080 rr
  -> 172.16.79.79:80              Masq    1      0          0         
TCP  172.17.0.1:32665 rr
  -> 172.16.79.82:8443            Masq    1      0          0         
TCP  192.168.115.161:30080 rr
  -> 172.16.79.79:80              Masq    1      0          0         
TCP  192.168.115.161:32665 rr
  -> 172.16.79.82:8443            Masq    1      0          0         
TCP  192.168.115.166:30080 rr
  -> 172.16.79.79:80              Masq    1      0          0         
TCP  192.168.115.166:32665 rr
  -> 172.16.79.82:8443            Masq    1      0          0         
TCP  10.10.0.1:443 rr
  -> 192.168.115.161:6443         Masq    1      0          0         
  -> 192.168.115.162:6443         Masq    1      0          0         
  -> 192.168.115.163:6443         Masq    1      1          0         
TCP  10.10.0.10:53 rr
  -> 172.16.122.139:53            Masq    1      0          0         
  -> 172.16.122.140:53            Masq    1      0          0         
TCP  10.10.0.10:9153 rr
  -> 172.16.122.139:9153          Masq    1      0          0         
  -> 172.16.122.140:9153          Masq    1      0          0         
TCP  10.10.39.128:8000 rr
  -> 172.16.79.80:8000            Masq    1      0          0         
TCP  10.10.128.23:443 rr
  -> 172.16.79.81:443             Masq    1      0          0         
TCP  10.10.166.16:8000 rr
  -> 172.16.79.79:80              Masq    1      0          0         
TCP  10.10.195.192:443 rr
  -> 172.16.79.82:8443            Masq    1      0          0         
UDP  10.10.0.10:53 rr
  -> 172.16.122.139:53            Masq    1      0          0         
  -> 172.16.122.140:53            Masq    1      0          0       

三、Service资源类型

常见的Service资源清单

复制代码
apiVersion: v1
kind: Service
matadata:                                #元数据
  name: string                           #service的名称
  namespace: string                      #命名空间  
  labels:                                #自定义标签属性列表
    - name: string
  annotations:                           #自定义注解属性列表  
    - name: string
spec:                                    #详细描述
  selector: []                           #label selector配置,将选择具有label标签的Pod作为管理范围
  type: string                           #service的类型,指定service的访问方式,默认为clusterIp              
  clusterIP: string                      #虚拟服务地址      
  sessionAffinity: string                #是否支持session
  ports:                                 #service需要暴露的端口列表
  - name: string                         #端口名称
    protocol: string                     #端口协议,支持TCP和UDP,默认TCP
    port: int                            #服务监听的service端口号
    targetPort: int                      #需要转发到后端Pod的端口号
    nodePort: int                        #当type = NodePort时,指定映射到物理机的端口号
  status:                                #当spce.type=LoadBalancer时,设置外部负载均衡器的地址
    loadBalancer:                        #外部负载均衡器    
      ingress:                           #外部负载均衡器 
        ip: string                       #外部负载均衡器的Ip地址值
        hostname: string                 #外部负载均衡器的主机名
​

资源清单案例

复制代码
apiVersion: v1  # 资源版本
kind: Service  # 资源类型
metadata: # 元数据
  name: service # 资源名称
  namespace: dev # 命名空间
spec: # 描述
  selector: # 标签选择器,用于确定当前service代理哪些pod
    app: nginx
  type: # Service类型,指定service的访问方式
  clusterIP:  # 虚拟服务的ip地址
  sessionAffinity: # session亲和性,支持ClientIP、None两个选项
  ports: # 端口信息
    - protocol: TCP 
      port: 3017  # service端口
      targetPort: 5003 # pod端口
      nodePort: 31122 # 主机端口

Service中的service.spec.type类型

类型 含义
ClusterIP 意味着服务仅在集群内部可用,只能通过集群IP访问。
ExternalName 意味着服务仅包含一个对外部名称的引用,Kubedns或等价物将返回作为CNAME记录,不会涉及任何容器的暴露或代理。
LoadBalancer 意味着服务将通过外部负载均衡器(如果云提供商支持的话)进行暴露,除了NodePort类型之外。
NodePort 意味着服务将在每个节点的一个端口上进行暴露,除了ClusterIP类型之外。

Service中的三类IP地址

IP类型 作用
Node IP 节点IP是Kubernetes集群中每个节点的唯一标识符。它代表了节点的网络接口,用于在集群内部进行通信。节点IP通常是一个私有IP地址范围,用于在集群内部进行通信。节点IP是Service在集群内部提供服务的唯一标识,用于路由流量到指定的Pod。
Pod IP Pod IP是Kubernetes中每个Pod的唯一标识符。它代表了Pod的网络接口,用于在集群内部进行通信。Pod IP通常是一个私有IP地址范围,用于在集群内部进行通信。Pod IP是Service在集群内部提供服务的唯一标识,用于路由流量到指定的Pod。
Cluster IP Cluster IP是Kubernetes集群中Service的IP地址。它代表了Service在集群内部提供的服务,用于在集群内部进行通信。Cluster IP通常是一个私有IP地址范围,用于在集群内部进行通信。Cluster IP是Service在集群内部提供服务的唯一标识,用于路由流量到指定的Pod。

综上所述,Kubernetes中的三类IP地址分别是Node IP、Pod IP和Cluster IP。这些IP地址用于在不同网络之间路由流量,以便外部和内部应用程序可以访问Kubernetes集群中的服务和Pod。

四、Service实战案例

实验环境准备

在使用service之前,首先利用Deployment创建出3个pod,注意要为pod设置app=nginx-pod的标签

创建deployment.yaml,内容如下:

复制代码
apiVersion: apps/v1
kind: Deployment      
metadata:
  name: pc-deployment
  namespace: dev
spec: 
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
复制代码
[root@k8s-master01 ~]# kubectl create -f deployment.yaml
deployment.apps/pc-deployment created
​
# 查看pod详情
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide --show-labels
NAME                             READY   STATUS    RESTARTS   AGE   IP              NODE           NOMINATED NODE   READINESS GATES   LABELS
pc-deployment-59c564ffb7-8vzp5   1/1     Running   0          89s   172.16.79.83    k8s-worker01   <none>           <none>            app=nginx-pod,pod-template-hash=59c564ffb7
pc-deployment-59c564ffb7-fg2j8   1/1     Running   0          89s   172.16.69.206   k8s-worker02   <none>           <none>            app=nginx-pod,pod-template-hash=59c564ffb7
pc-deployment-59c564ffb7-sprp7   1/1     Running   0          89s   172.16.69.207   k8s-worker02   <none>           <none>            app=nginx-pod,pod-template-hash=59c564ffb7
​
# 为了方便后面的测试,修改下三台nginx的index.html页面(三台修改的IP地址不一致)
[root@k8s-master01 ~]# kubectl exec -it pc-deployment-59c564ffb7-8vzp5 -n dev -- /bin/sh
root@pc-deployment-59c564ffb7-8vzp5:/# echo "172.16.79.83" > /usr/share/nginx/html/index.html 
[root@k8s-master01 ~]# kubectl -n dev exec -it pc-deployment-59c564ffb7-fg2j8 -- /bin/bash
root@pc-deployment-59c564ffb7-fg2j8:/# echo "172.16.69.206" > /usr/share/nginx/html/index.html
[root@k8s-master01 ~]# kubectl -n dev exec -it pc-deployment-59c564ffb7-sprp7 -- /bin/bash
root@pc-deployment-59c564ffb7-sprp7:/# echo "172.16.69.207" > /usr/share/nginx/html/index.html 
#修改完毕之后,访问测试
[root@k8s-master01 ~]# curl 172.16.79.83
172.16.79.83
[root@k8s-master01 ~]# curl 172.16.69.206
172.16.69.206
[root@k8s-master01 ~]# curl 172.16.69.207
172.16.69.207

4.1 ClusterIP类型的Service

创建service-clusterip.yaml文件

复制代码
apiVersion: v1
kind: Service
metadata:
  name: service-clusterip
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: 10.10.97.97 # service的ip地址,如果不写,默认会生成一个
  type: ClusterIP
  ports:
  - port: 80  # Service端口       
    targetPort: 80 # pod端口
复制代码
# 创建service
[root@k8s-master01 ~]# kubectl create -f service-clusterip.yaml
service/service-clusterip created
​
# 查看service
[root@k8s-master01 ~]# kubectl get svc -n dev 
[root@k8s-master01 ~]# kubectl -n dev get svc
NAME                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service-clusterip   ClusterIP   10.10.97.97   <none>        8001/TCP   10s
​
# 查看service的详细信息
# 在这里有一个Endpoints列表,里面就是当前service可以负载到的服务入口
[root@k8s-master01 ~]# kubectl describe service -n dev
Name:              service-clusterip
Namespace:         dev
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.10.97.97
IPs:               10.10.97.97
Port:              <unset>  8001/TCP
TargetPort:        80/TCP
Endpoints:         172.16.69.206:80,172.16.69.207:80,172.16.79.83:80
Session Affinity:  None
Events:            <none>
​
# 查看ipvs的映射规则
[root@k8s-master01 ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr
  -> 10.10.1.39:80               Masq    1      0          0
  -> 10.10.1.40:80               Masq    1      0          0
  -> 10.10.2.33:80               Masq    1      0          0
​
# 访问10.10.97.97:8001观察效果
​
[root@k8s-master01 ~]# curl 10.10.97.97:8001
172.16.79.83
[root@k8s-master01 ~]# curl 10.10.97.97:8001
172.16.69.207
[root@k8s-master01 ~]# curl 10.10.97.97:8001
172.16.69.206
​

Endpoint解析

  • Endpoint是kubernetes中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址,它是根据service配置文件中selector描述产生的。

  • 一个Service由一组Pod组成,这些Pod通过Endpoints暴露出来,Endpoints是实现实际服务的端点集合。换句话说,service和pod之间的联系是通过endpoints实现的。

负载分发策略

对Service的访问被分发到了后端的Pod上去,目前kubernetes提供了两种负载分发策略:

  • 如果不定义,默认使用kube-proxy的策略,比如随机、轮询

  • 基于客户端地址的会话保持模式,即来自同一个客户端发起的所有请求都会转发到固定的一个Pod上

复制代码
# 查看ipvs的映射规则【rr 轮询】
[root@k8s-master01 ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr
  -> 10.10.1.39:80               Masq    1      0          0
  -> 10.10.1.40:80               Masq    1      0          0
  -> 10.10.2.33:80               Masq    1      0          0
​
# 循环访问测试
[root@k8s-master01 ~]# while true;do curl 10.97.97.97:80; sleep 5;  done;
10.10.1.40
10.10.1.39
10.10.2.33
10.10.1.40
10.10.1.39
10.10.2.33
​
# 修改分发策略----sessionAffinity:ClientIP
​
# 查看ipvs规则【persistent 代表持久】
[root@k8s-master01 ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr persistent 10800
  -> 10.10.1.39:80               Masq    1      0          0
  -> 10.10.1.40:80               Masq    1      0          0
  -> 10.10.2.33:80               Masq    1      0          0
​
# 循环访问测试
[root@k8s-master01 ~]# while true;do curl 10.97.97.97; sleep 5; done;
10.10.2.33
10.10.2.33
10.10.2.33
  
# 删除service
[root@k8s-master01 ~]# kubectl delete -f service-clusterip.yaml
service "service-clusterip" deleted
[root@k8s-master01 ~]# kubectl delete service service-clusterip -n dev
service "service-clusterip" deleted
​

4.2 HeadLiness类型的Service

在某些场景中,开发人员可能不想使用Service提供的负载均衡功能,而希望自己来控制负载均衡策略,针对这种情况,kubernetes提供了HeadLiness Service,这类Service不会分配Cluster IP,如果想要访问service,只能通过service的域名进行查询。

创建service-headliness.yaml

复制代码
apiVersion: v1
kind: Service
metadata:
  name: service-headliness
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: "None" # 将clusterIP设置为None,即可创建headliness Service
  type: ClusterIP
  ports:
  - port: 80    
    targetPort: 80
​
复制代码
# 创建service
[root@k8s-master01 ~]# kubectl apply -f deployment-nginx-headliness.yaml
service/service-headliness created
​
# 获取service, 发现CLUSTER-IP未分配
[root@k8s-master01 ~]# kubectl -n dev get svc
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service-headliness   ClusterIP   None         <none>        80/TCP    10s
​
# 查看service详情
[root@k8s-master01 ~]# kubectl describe svc -n dev
Name:              service-headliness
Namespace:         dev
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                None
IPs:               None
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         172.16.69.206:80,172.16.69.207:80,172.16.79.83:80
Session Affinity:  None
Events:            <none>
​
​
# 查看域名的解析情况
[root@k8s-master01 ~]# kubectl  -n dev exec -it pc-deployment-59c564ffb7-8vzp5 -- /bin/bash 
[root@k8s-master01 ~]# kubectl  -n dev exec -it pc-deployment-59c564ffb7-8vzp5 -- /bin/bash 
root@pc-deployment-59c564ffb7-8vzp5:/#
root@pc-deployment-59c564ffb7-8vzp5:/# cat /etc/resolv.conf 
search dev.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.10.0.10
options ndots:5
​
[root@k8s-master01 ~]# dig @10.96.0.10 service-headliness.dev.svc.cluster.local
service-headliness.dev.svc.cluster.local. 30 IN A 172.16.79.83
service-headliness.dev.svc.cluster.local. 30 IN A 172.16.69.206
service-headliness.dev.svc.cluster.local. 30 IN A 172.16.69.207
​
#删除service
[root@k8s-master01 ~]# kubectl delete -n dev svc service-headliness
service "service-headliness" deleted

4.3 NodePort类型的Service

在之前的样例中,创建的Service的ip地址只有集群内部才可以访问,如果希望将Service暴露给集群外部使用,那么就要使用到另外一种类型的Service,称为NodePort类型。NodePort的工作原理其实就是将service的端口映射到Node的一个端口上,然后就可以通过NodeIp:NodePort来访问service了。

创建service-nodeport.yaml

复制代码
apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: dev
spec:
  selector:
    app: nginx-pod
  type: NodePort # service类型
  ports:
  - port: 80
    nodePort: 30002 # 指定绑定的node的端口(默认的取值范围是:30000-32767), 如果不指定,会默认分配
    targetPort: 80
复制代码
# 创建service
[root@k8s-master01 ~]# kubectl apply -f deployment-nginx-nodeport.yaml 
service/service-nodeport created
​
​
# 查看service
[root@k8s-master01 ~]# kubectl -n dev get svc -o wide 
NAME               TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE   SELECTOR
service-nodeport   NodePort   10.10.25.194   <none>        80:30002/TCP   29s   app=nginx-pod
​
# 接下来可以通过电脑主机的浏览器去访问集群中任意一个nodeip的30002端口,即可访问到pod
#删除service
[root@k8s-master01 ~]# kubectl delete -n dev svc service-nodeport 
service "service-nodeport" deleted

4.4 LoadBalancer类型的Service

LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer会在集群的外部再来做一个负载均衡设备,而这个设备需要外部环境支持的,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。

4.5 ExternalName类型的Service

ExternalName类型的Service用于引入集群外部的服务,它通过externalName属性指定外部一个服务的地址,然后在集群内部访问此service就可以访问到外部的服务了。

创建service-externalname.yaml

复制代码
apiVersion: v1
kind: Service
metadata:
  name: service-externalname
spec:
  type: ExternalName # service类型
  externalName: www.baidu.com
​
复制代码
# 创建service
[root@k8s-master01 ~]# kubectl apply -f deployment-nginx-externalname.yaml
service/service-externalname created
[root@k8s-master01 ~]# kubectl -n dev get svc -o wide 
NAME                   TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)   AGE   SELECTOR
service-externalname   ExternalName   <none>       www.baidu.com   <none>    7s    <none>
[root@k8s-master01 ~]# kubectl describe -n dev svc
Name:              service-externalname
Namespace:         dev
Labels:            <none>
Annotations:       <none>
Selector:          <none>
Type:              ExternalName
IP Families:       <none>
IP:                
IPs:               <none>
External Name:     www.baidu.com
Session Affinity:  None
Events:            <none>
​
# 域名解析
[root@k8s-master01 ~]# dig @10.10.0.10 service-externalname.dev.svc.cluster.local
​
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.15 <<>> @10.10.0.10 service-externalname.dev.svc.cluster.local
; (1 server found)
;; 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: 33580
;; flags: qr aa rd; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
​
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;service-externalname.dev.svc.cluster.local. IN A
​
;; ANSWER SECTION:
service-externalname.dev.svc.cluster.local. 5 IN CNAME www.baidu.com.
www.baidu.com.      5   IN  CNAME   www.a.shifen.com.
www.a.shifen.com.   5   IN  A   39.156.66.18
www.a.shifen.com.   5   IN  A   39.156.66.14
​
;; Query time: 127 msec
;; SERVER: 10.10.0.10#53(10.10.0.10)
;; WHEN: 四 1月 25 15:04:37 CST 2024
;; MSG SIZE  rcvd: 247
​
相关推荐
是Yu欸30 分钟前
从Ascend C算子开发视角看CANN的“软硬协同”
c语言·开发语言·云原生·昇腾·ascend·cann·开放社区
光头熊35 分钟前
一次 nerdctl prune -a 导致 Kubernetes 节点不可用的复盘
kubernetes
码界奇点1 小时前
基于微服务架构的企业身份与访问管理系统设计与实现
微服务·云原生·架构·车载系统·毕业设计·源代码管理
一点晖光3 小时前
docker配置npm环境变量出现问题
docker·容器·npm
一分半心动4 小时前
windows docker desktop 安装VibeVoice
运维·docker·容器
LucidX4 小时前
Docker核心操作实战
运维·docker·容器
隔壁阿布都4 小时前
Docker Compose中的网络管理
运维·docker·容器
yuxb736 小时前
kubernetes弹性伸缩
笔记·kubernetes
ice_bird6 小时前
Ansible一键部署k8s1.28.2集群
kubernetes·ansible
2301_767902646 小时前
Containerd 从入门到实战
运维·容器