k8s之service服务

service服务介绍

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

为了解决这个问题,k8s提供了service资源,service会对提供同一个服务的多个pod进行聚合,并且提供一个统一的入口地址。通过访问service的入口地址就能访问到后面的pod服务

Service服务也是Kubernetes里的核心资源对象之一,Kubernetes里的每个Service其实就是我们经常提起的微服务架构中的一个微服务,受kube-proxy管理,运行在每个Node上的kube-proxy进程其实就是一个智能的软件负载均衡器,负责把对Service的请求转发到后端的某个Pod实例上,并在内部实现服务的负载均衡与会话保持机制。

可以看到上面的架构图,service服务通过标签选择器定位后端pod,前提是service的selector必须和后端Pod标签对应上才能找到相对应的Pod,而前段frontend通过service就可以访问到后端提供服务的pod了,而service默认IP类型为主要分为:

  • ClusterIP:主要是为集群内部提供访问服务的 (默认类型)
  • NodePort:可以被集群外部所访问,访问方式为    宿主机:端口号
  • LoadBalancer:在NodePort的基础上,借助cloud provider(云提供商)创建一个外部负载均衡器并将请求转发到NodePort
  • ExternalName: 把集群外部的访问引入到集群内部来,在集群内部直接使用,没有任何代理被创建 

当Service一旦被创建,Kubernetes就会自动为它分配一个可用的Cluster IP,而且在Service的整个生命周期内,它的Cluster IP不会发生改变,service会通过标签选择器与后端的pod进行连接并被kubo-poxry监控,当后端pod被重建时会通过标签自动加入到对应的service服务中,从而避免失联。

service三种代理模式

  • userspace

这种模式,kube-proxy 会监视 Kubernetes 控制平面对 Service 对象和 Endpoints 对象的添加和移除操作。 对每个 Service,它会在本地 Node 上打开一个端口(随机选择)。 任何连接到“代理端口”的请求,都会被代理到 Service 的后端 Pods 中的某个上面(如 Endpoints 所报告的一样)。 使用哪个后端 Pod,是 kube-proxy 基于 SessionAffinity 来确定的。

  • iptables

这种模式,kube-proxy 会监视 Kubernetes 控制节点对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会配置 iptables 规则,从而捕获到达该 Service 的 clusterIP 和端口的请求,进而将请求重定向到 Service 的一组后端中的某个 Pod 上面。 对于每个 Endpoints 对象,它也会配置 iptables 规则,这个规则会选择一个后端组合。

  • ipvs

在 ipvs 模式下,kube-proxy 监视 Kubernetes 服务和端点,调用 netlink 接口相应地创建 IPVS 规则, 并定期将 IPVS 规则与 Kubernetes 服务和端点同步。该控制循环可确保 IPVS 状态与所需状态匹配。访问服务时,IPVS 将流量定向到后端 Pod 之一。

IPVS 代理模式基于类似于 iptables 模式的 netfilter 挂钩函数, 但是使用哈希表作为基础数据结构,并且在内核空间中工作。 这意味着,与 iptables 模式下的 kube-proxy 相比,IPVS 模式下的 kube-proxy 重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。 与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。

IPVS 提供了更多选项来平衡后端 Pod 的流量。这些是:

  • rr:轮替(Round-Robin)
  • lc:最少链接(Least Connection),即打开链接数量最少者优先
  • dh:目标地址哈希(Destination Hashing)
  • sh:源地址哈希(Source Hashing)
  • sed:最短预期延迟(Shortest Expected Delay)
  • nq:从不排队(Never Queue)
说明:

要在 IPVS 模式下运行 kube-proxy,必须在启动 kube-proxy 之前使 IPVS 在节点上可用。

当 kube-proxy 以 IPVS 代理模式启动时,它将验证 IPVS 内核模块是否可用。 如果未检测到 IPVS 内核模块,则 kube-proxy 将退回到以 iptables 代理模式运行。

ipvs模式设置

先下载ipvsadm

然后修改kube-poxy的configmap

[root@master ~]# kubectl edit cm kube-proxy -n kube-system
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  config.conf: |-
    apiVersion: kubeproxy.config.k8s.io/v1alpha1
    bindAddress: 0.0.0.0
    bindAddressHardFail: false
    clientConnection:
      acceptContentTypes: ""
      burst: 0
      contentType: ""
      kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
      qps: 0
    clusterCIDR: 10.244.0.0/16
    configSyncPeriod: 0s
    conntrack:
      maxPerCore: null
      min: null
      tcpCloseWaitTimeout: null
      tcpEstablishedTimeout: null
    detectLocalMode: ""
    enableProfiling: false
    healthzBindAddress: ""
    hostnameOverride: ""
    iptables:
      masqueradeAll: false
      masqueradeBit: null
      minSyncPeriod: 0s
      syncPeriod: 0s
    ipvs:
      excludeCIDRs: null
      minSyncPeriod: 0s
      scheduler: ""
      strictARP: false
      syncPeriod: 0s
      tcpFinTimeout: 0s
      tcpTimeout: 0s
      udpTimeout: 0s
    kind: KubeProxyConfiguration
    metricsBindAddress: ""
    mode: "ipvs"  #将模式改为ipvs
    nodePortAddresses: null
    oomScoreAdj: null
    portRange: ""
    showHiddenMetricsForVersion: ""
    udpIdleTimeout: 0s
    winkernel:
      enableDSR: false
      networkName: ""
      sourceVip: ""
  kubeconfig.conf: |-
    apiVersion: v1
    kind: Config
    clusters:
    - cluster:
        certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        server: https://192.168.248.129:6443
      name: default
    contexts:
    - context:
        cluster: default
        namespace: default
        user: default
      name: default
    current-context: default
    users:
    - name: default
      user:
        tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token

删除当前的kube-poxy。

[root@master ~]# kubectl get pod -n kube-system|grep kube-proxy|awk '{system("kubectl delete pod "$1" -n kube-system")}'

 

ClusterIP(内部集群访问)

下面演示ClusterIP的service

先创建一个yaml文件

 
apiVersion: v1
kind: Service
metadata:
  labels:                          
    app: web
  name: nginx-svc
  namespace: default
spec:
  ports:
  - name: http
    port: 3001                      # service暴露的端口
    protocol: TCP
    targetPort: 80                  #后端容器的端口
  selector:                       #标签选择器与deployment一致
    app: web                 
  type: ClusterIP
---

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: deployment-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: nginx:1.21.4
        name: nginx
        ports:
        - name: http
          containerPort: 80       #容器端口

 

然后创建service和deployment

[root@master ~]# kubectl apply -f deploymen_nginx_serivce.yaml 
service/nginx-svc created
deployment.apps/web created

查看service和pod状态

[root@master ~]# kubectl get svc -o wide                       
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE   SELECTOR
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP    40m   <none>
nginx-svc    ClusterIP   10.103.130.138   <none>        3001/TCP   12m   app=web
[root@master ~]# kubectl get pod -o wide    
NAME                                READY   STATUS    RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
deployment-nginx-64fc98d58f-6llbw   1/1     Running   0          10m   10.101.11.32    node2   <none>           <none>
deployment-nginx-64fc98d58f-9s6mt   1/1     Running   0          10m   10.101.11.31    node2   <none>           <none>
deployment-nginx-64fc98d58f-fkcmf   1/1     Running   0          10m   10.101.149.15   node1   <none>           <none>

此时我们发现nginx-svc有了一个CLUSTER-IP:10.103.130.138, 用curl检查一下是否能访问10.103.130.138:3001

[root@master ~]# curl 10.103.130.138:3001
<!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>

NodePod(外部访问)

下面创建一个NodePort的service演示,只需要将上面yaml文件中的ClusterIP改为NodePort

apiVersion: v1
kind: Service
metadata:
  labels:
    app: web
  name: nginx-svc
  namespace: default
spec:
  ports:
  - name: http
    port: 3001     
    protocol: TCP
    targetPort: 80
  selector:
    app: web
  type: NodePort
---

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

执行yaml文件后查看pod和svc的信息

[root@master ~]# kubectl get svc -o wide                       
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE   SELECTOR
kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP          59m   <none>
nginx-svc    NodePort    10.99.62.52   <none>        3001:31966/TCP   3s    app=web
[root@master ~]# kubectl get node -owide
NAME     STATUS   ROLES                  AGE   VERSION   INTERNAL-IP       EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION           CONTAINER-RUNTIME
master   Ready    control-plane,master   79d   v1.21.0   192.168.248.129   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   docker://20.10.14
node1    Ready    <none>                 79d   v1.21.0   192.168.248.128   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   docker://20.10.14
node2    Ready    <none>                 79d   v1.21.0   192.168.248.130   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   docker://20.10.14
[root@master ~]# kubectl get pod -o wide   
NAME                                READY   STATUS    RESTARTS   AGE    IP              NODE    NOMINATED NODE   READINESS GATES
deployment-nginx-64fc98d58f-b5s2t   1/1     Running   0          112s   10.101.11.36    node2   <none>           <none>
deployment-nginx-64fc98d58f-gd5fw   1/1     Running   0          112s   10.101.11.35    node2   <none>           <none>
deployment-nginx-64fc98d58f-p6tw5   1/1     Running   0          112s   10.101.149.17   node1   <none>           <none>

此时发现svc的PORT变成3001:31966/TCP ,其中3001为svc的端口,31966为随机生成的node端口,我们可以通过nodeIP加端口访问

 

 

headless service(无头服务)

headless service就是无头service(也就是没有ip的service),这种无头服务对于有状态应用来说很重要,我们可以利用service的labels关联后端pod,我们访问的流量就可以直接到达pod而不再需要service负载均衡至后端pod

定义无头服务如下:

apiVersion: v1
kind: Service
metadata:
  name: redis
  labels:
    app: redis
spec:
  ports:
  - port: 6379
    name: redis-port
  clusterIP: None
  selector:
    app: redis
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  serviceName: "redis"
  replicas: 3 # 默认值是 1
  selector:
      matchLabels:
        app: redis
  template:
    metadata:
      labels:
        app: redis # 必须匹配 .spec.selector.matchLabels
    spec:
      containers:
      - name: redis
        image: redis
        ports:
        - containerPort: 6379
          name: redis

查看pod以及service

[root@master ~]# kubectl get pod -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
redis-0   1/1     Running   0          29s   10.101.11.37    node2   <none>           <none>
redis-1   1/1     Running   0          23s   10.101.149.18   node1   <none>           <none>
redis-2   1/1     Running   0          21s   10.101.11.38    node2   <none>           <none>
[root@master ~]# kubectl get svc -o wide   
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE   SELECTOR
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP    89m   <none>
redis        ClusterIP   None         <none>        6379/TCP   40s   app=redis

查看coredns地址

[root@master ~]# kubectl get pod -n kube-system -o wide
NAME                                       READY   STATUS    RESTARTS   AGE   IP                NODE     NOMINATED NODE   READINESS GATES
calico-kube-controllers-65898446b5-qtcgv   1/1     Running   1          79d   10.101.149.1      node1    <none>           <none>
calico-node-7wcsl                          1/1     Running   0          79d   192.168.248.128   node1    <none>           <none>
calico-node-8sjcz                          1/1     Running   0          79d   192.168.248.129   master   <none>           <none>
calico-node-f84fw                          1/1     Running   0          79d   192.168.248.130   node2    <none>           <none>
coredns-545d6fc579-w5rj2                   1/1     Running   0          79d   10.101.11.1       node2    <none>           <none>
coredns-545d6fc579-xr62w                   1/1     Running   0          79d   10.101.11.2       node2    <none>           <none>
etcd-master                                1/1     Running   0          79d   192.168.248.129   master   <none>           <none>
kube-apiserver-master                      1/1     Running   0          79d   192.168.248.129   master   <none>           <none>
kube-controller-manager-master             1/1     Running   0          79d   192.168.248.129   master   <none>           <none>
kube-proxy-2qx8x                           1/1     Running   0          79d   192.168.248.129   master   <none>           <none>
kube-proxy-6khnn                           1/1     Running   0          79d   192.168.248.128   node1    <none>           <none>
kube-proxy-znv97                           1/1     Running   0          79d   192.168.248.130   node2    <none>           <none>
kube-scheduler-master                      1/1     Running   0          79d   192.168.248.129   master   <none>           <none>

解析service服务

[root@master ~]# dig redis.default.svc.cluster.local @10.101.11.1              

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.9 <<>> nginx.default.svc.cluster.local @10.101.11.1
;; 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: 49448
;; 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:
;nginx.default.svc.cluster.local. IN    A

;; ANSWER SECTION:
nginx.default.svc.cluster.local. 30 IN  A       10.101.11.38     # 这个地址对应的是statefulset集群的的redis-2的地址
nginx.default.svc.cluster.local. 30 IN  A       10.101.149.18       
nginx.default.svc.cluster.local. 30 IN  A       10.101.11.37

;; Query time: 7 msec
;; SERVER: 10.101.11.1#53(10.101.11.1)
;; WHEN: 三 7月 27 23:10:03 CST 2022
;; MSG SIZE  rcvd: 201

 

ExternalName 类型

类型为 ExternalName 的服务将服务映射到 DNS 名称,而不是典型的选择算符,例如 my-service 或者 cassandra。 你可以使用 spec.externalName 参数指定这些服务。

例如,以下 Service 定义将 prod 名称空间中的 my-service 服务映射到 my.database.example.com

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

当查找主机 my-service.prod.svc.cluster.local 时,集群 DNS 服务返回 CNAME 记录, 其值为 my.database.example.com。 访问 my-service 的方式与其他服务的方式相同,但主要区别在于重定向发生在 DNS 级别,而不是通过代理或转发。 如果以后你决定将数据库移到集群中,则可以启动其 Pod,添加适当的选择算符或端点以及更改服务的 type

外部 IP

如果外部的 IP 路由到集群中一个或多个 Node 上,Kubernetes Service 会被暴露给这些 externalIPs。 通过外部 IP(作为目的 IP 地址)进入到集群,打到 Service 的端口上的流量, 将会被路由到 Service 的 Endpoint 上。 externalIPs 不会被 Kubernetes 管理,它属于集群管理员的职责范畴。

根据 Service 的规定,externalIPs 可以同任意的 ServiceType 来一起指定。 在上面的例子中,my-service 可以在 "80.11.12.10:80"(externalIP:port) 上被客户端访问。

 

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
  externalIPs:
    - 80.11.12.10

 

 

 

posted @ 2022-07-27 23:23  李志锋  阅读(1743)  评论(0编辑  收藏  举报