4-1、service资源

service资源
一个service刻意看作一组提供相同服务的Pod的对外的访问接口;
service作用于哪些pod时通过标签选择器来定义的;
service强依赖dns解析服务:coredns,kube-dns(k8s1.1以前版本)
https://cloud.tencent.com/developer/article/1718429
service工作模式:userspace,iptables,ipvs
    userspace:kubernetes 1.1-
    iptables:kubernetes 1.10-
    ipvs:kubernetes1.11+

userspace代理模式

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

最后,它配置 iptables 规则,捕获到达该 Service 的 clusterIP(是虚拟 IP)和 Port 的请求,并重定向到代理端口,代理端口再代理请求到 backend Pod。

默认情况下,userspace模式下的kube-proxy通过循环算法选择后端。

默认的策略是,通过 round-robin 算法来选择 backend Pod。

iptables 代理模式

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



默认的策略是,kube-proxy在iptables模式下随机选择一个backend。



使用 iptables 处理流量具有较低的系统开销,因为流量由Linux netfilter 处理,而无需在用户空间和内核空间之间切换。 这种方法也可能更可靠。



如果kube-proxy在 iptables模式下运行,并且所选的第一个 Pod 没有响应,则连接失败。 这与userspace模式不同:在这种情况下,kube-proxy 将检测到与第一个 Pod 的连接已失败,并会自动使用其他后端Pod重试。



我们可以使用 Pod readiness 探测器验证后端 Pod 是否可以正常工作,以便 iptables 模式下的 kube-proxy 仅看到测试正常的后端。这样做意味着可以避免将流量通过 kube-proxy 发送到已知已失败的Pod。

IPVS 代理模式

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

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

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

rr: round-robin

lc: least connection (smallest number of open connections)

dh: destination hashing

sh: source hashing

sed: shortest expected delay

nq: never queue

注意:要在 IPVS 模式下运行 kube-proxy,必须在启动 kube-proxy 之前使 IPVS Linux 在节点上可用。 当 kube-proxy 以 IPVS 代理模式启动时,它将验证 IPVS 内核模块是否可用。 如果未检测到 IPVS 内核模块,则 kube-proxy 将退回到以 iptables 代理模式运行。

service服务类型:
ClusterIP:默认类型,自动分配一个仅Cluster内部可以访问的虚拟IP

NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。以ClusterIP为基础,NodePort 服务会路由到 ClusterIP 服务。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个集群内部的 NodePort 服务。

LoadBalancer:使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。

ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。没有任何类型代理被创建。
headless:

需要注意的是:Service 能够将一个接收port映射到任意的targetPort。默认情况下,targetPort将被设置为与port字段相同的值。
Service域名格式:(servicename).(namespace).svc.cluster.local,其中cluster.local为指定的集群的域名

service主要包含:apiVsersion,kind,metadata,spec几个字段
kubectl explain svc
spec.ports字段:
  nodePort:节点上面的端口,该端口一定不能被占用
  port:service的端口,提供该服务的端口
  targetPort:容器上的端口

命令式:

kubectl expose deployment goweb --name=gowebsvc --port=80  --target-port=8000  
声名式:yaml

资源记录:
    SERVIC_NAME.NAMESPACE_NAME.DOMAIN>LTD
    svc.cluster.local.   如:redis.default.svc.cluster.local.

service -->endpoint-->pod

ClusterIP类型:
# vi redis-svc.yaml
apiVersion: v1
kind: Service
metadata:
    name: redis
    namespace: default
spec:
    selector:
        app: redis
        role: logstor      
    clusterIP: 10.97.97.97
    type: ClusterIP
    ports:
    - port: 6397
      targetPort: 6397
    
# kubectl apply -f redis-svc.yaml 
# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP    28h
redis        ClusterIP   10.97.97.97      <none>        6397/TCP   8s

# kubectl describe svc redis
Name:              redis
Namespace:         default
Labels:            <none>
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"redis","namespace":"default"},"spec":{"clusterIP":"10.97.97.97","...
Selector:          app=redis,role=logstor
Type:              ClusterIP
IP:                10.97.97.97
Port:              <unset>  6397/TCP
TargetPort:        6397/TCP
Endpoints:         <none>
Session Affinity:  None
Events:            <none>


NodePort类型:
# vi myapp-np-svc.yaml
apiVersion: v1
kind: Service
metadata:
    name: myapp-np
    namespace: default
spec:
    selector:
        app: myapp-np
        release: canary     
    clusterIP: 10.98.98.98
    type: NodePort
    ports:
    - port: 800
      targetPort: 800
      nodePort: 30800

# kubectl apply -f myapp-np-svc.yaml
# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)         AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP         28h
myapp-np     NodePort    10.98.98.98      <none>        800:30800/TCP   32m

# kubectl describe svc myapp-np
Name:                     myapp-np
Namespace:                default
Labels:                   <none>
Annotations:              kubectl.kubernetes.io/last-applied-configuration:
                            {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp-np","namespace":"default"},"spec":{"clusterIP":"10.98.98.98...
Selector:                 app=myapp-np,release=canary
Type:                     NodePort
IP:                       10.98.98.98
Port:                     <unset>  800/TCP
TargetPort:               800/TCP
NodePort:                 <unset>  30800/TCP
Endpoints:                <none>
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

# curl 192.168.31.11:30800/hostname.html   #轮询
# kubectl path svc myapp-np -p '{"spec":{"sessionAffinity":"ClientIP"}}'    #打补丁直接生效,会话保持
# curl 192.168.31.11:30800/hostname.html 

# kubectl get svc -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   28h

查看解析:
# dig -t A myapp-np.default.svc.cluster.local. @10.96.0.10

; <<>> DiG 9.9.4-RedHat-9.9.4-50.el7 <<>> -t A myapp-np.default.svc.cluster.local. @10.96.0.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9942
;; 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-np.default.svc.cluster.local. IN    A

;; ANSWER SECTION:
myapp-np.default.svc.cluster.local. 5 IN A    10.98.98.98

;; Query time: 41 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sun Apr 07 00:22:36 CST 2019
;; MSG SIZE  rcvd: 113
------------------------

Deployment的yaml信息
cat myapp-deploy.yaml 

apiVersion: apps/v1

kind: Deployment

metadata:

  name: myapp-deploy

  namespace: default

spec:

  replicas: 3

  selector:

    matchLabels:

      app: myapp

      release: v1

  template:

    metadata:

      labels:

        app: myapp

        release: v1

        env: test

    spec:

      containers:

      - name: myapp

        image: registry.cn-beijing.aliyuncs.com/google_registry/myapp:v1

        imagePullPolicy: IfNotPresent

        ports:

        - name: http

          containerPort: 80
# kubectl apply -f myapp-deploy.yaml 
# kubectl get pod -owide

NAME                            READY   STATUS    RESTARTS   AGE   IP            NODE   NOMINATED NODE   READINESS GATES

myapp-deploy-577fbb58c6-j45bg   1/1     Running   0          35s   10.244.1.30   vm2    <none>           <none>

myapp-deploy-577fbb58c6-pdcpr   1/1     Running   0          35s   10.244.2.3    vm3    <none>           <none>

myapp-deploy-577fbb58c6-wb45k   1/1     Running   0          35s   10.244.2.2    vm3    <none>           <none>

[root@vm1 manifest]# kubectl get rs

NAME                      DESIRED   CURRENT   READY   AGE

myapp-deploy-577fbb58c6   3         3         3       38s


# curl 10.244.1.30
# curl 10.244.1.30/hostname.html

ClusterIP类型示例

yaml文件
# cat myapp-svc-ClusterIP.yaml 

apiVersion: v1

kind: Service

metadata:

  name: myapp-clusterip

  namespace: default

spec:

  type: ClusterIP  # 可以不写,为默认类型

  selector:

    app: myapp

    release: v1

  ports:

  - name: http

    port: 80

    targetPort: 80
# kubectl apply -f myapp-svc-ClusterIP.yaml
# kubectl get svc

NAME              TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE

kubernetes        ClusterIP   10.1.0.1      <none>        443/TCP   42d

myapp-clusterip   ClusterIP   10.1.186.57   <none>        80/TCP    5s

# curl 10.1.186.57
# curl 10.1.186.57/hostname.html

Headless Services

有时不需要或不想要负载均衡,以及单独的Service IP。遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 “None” 来创建 Headless Service。



这对headless Service并不会分配 Cluster IP,kube-proxy不会处理它们,而且平台也不会为它们进行负载均衡和路由。



使用场景:



第一种:自主选择权,有时候client想自己来决定使用哪个Real Server,可以通过查询DNS来获取Real Server的信息。

第二种:Headless Services还有一个用处(PS:也就是我们需要的那个特性)。Headless Service对应的每一个Endpoints,即每一个Pod,都会有对应的DNS域名;这样Pod之间就可以互相访问。【结合statefulset有状态服务使用,如Web、MySQL集群】

# cat myapp-svc-headless.yaml 

apiVersion: v1

kind: Service

metadata:

  name: myapp-headless

  namespace: default

spec:

  selector:

    app: myapp

    release: v1

  clusterIP: "None"

  ports:

  - port: 80

    targetPort: 80
# kubectl apply -f myapp-svc-headless.yaml
# kubectl get svc

NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE

kubernetes       ClusterIP   10.1.0.1     <none>        443/TCP   42d

myapp-headless   ClusterIP   None         <none>        80/TCP    16s

# kubectl describe svc myapp-headless

Name:              myapp-headless

Namespace:         default

Labels:            <none>

Annotations:       <none>

Selector:          app=myapp,release=v1

Type:              ClusterIP

IP Families:       <none>

IP:                None

IPs:               <none>

Port:              <unset>  80/TCP

TargetPort:        80/TCP

Endpoints:         10.244.1.30:80,10.244.2.2:80,10.244.2.3:80

Session Affinity:  None

Events:            <none>


service只要创建成功就会写入到coredns。得到coredns IP的命令如下:
kubectl get pod -o wide -A | grep 'coredns'

kube-system   coredns-7ff77c879f-5w99f        0/1     running   9          28m   10.244.0.6     vm1    <none>           <none>

kube-system   coredns-7ff77c879f-f5vtn        0/1     running   249        28h   10.244.1.26    vm2    <none>           <none>

coredns记录信息如下

# 其中 10.244.0.61 为 coredns IP

# myapp-headless.default.svc.cluster.local 为Headless Service域名。格式为:$(service name).$(namespace).svc.cluster.local,其中 cluster.local 指定的集群的域名

# nslookup myapp-headless.default.svc.cluster.local 10.244.0.61
# dig -t A myapp-headless.default.svc.cluster.local. @10.244.0.61


NodePort类型示例

如果将 type 字段设置为 NodePort,则 Kubernetes 控制层面将在 --service-node-port-range 标志指定的范围内分配端口(默认值:30000-32767)。



# cat myapp-svc-NodePort.yaml 

apiVersion: v1

kind: Service

metadata:

  name: myapp-nodeport

  namespace: default

spec:

  type: NodePort

  selector:

    app: myapp

    release: v1

  ports:

  - name: http

    # 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。

    port: 80         # Service对外提供服务端口

    targetPort: 80   # 请求转发后端Pod使用的端口

    nodePort: 31682  # 可选字段,默认情况下,为了方便起见,Kubernetes 控制层面会从某个范围内分配一个端口号(默认:30000-32767)
# kubectl apply -f myapp-svc-NodePort.yaml
# kubectl get svc

NAME             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE

kubernetes       ClusterIP   10.1.0.1       <none>        443/TCP        42d

myapp-nodeport   NodePort    10.1.122.197   <none>        80:31682/TCP   15s


端口查看,可见在本地宿主机监听了相应的端口(备注:集群所有机器都监听了该端口)
tcp        0      0 0.0.0.0:31682           0.0.0.0:*               LISTEN      2534/kube-proxy

curl通过节点IP访问
# curl 192.168.1.85:31682
# curl 192.168.1.85:31682/hostname.html


ExternalName类型示例
这种类型的Service通过返回CNAME和它的值,可以将服务映射到externalName字段的内容(例如:my.k8s.example.com;可以实现跨namespace名称空间访问)。ExternalName Service是Service的特例,它没有selector,也没有定义任何的端口和Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式提供服务。
# cat myapp-svc-ExternalName.yaml 

apiVersion: v1

kind: Service

metadata:

  name: myapp-externalname

  namespace: default

spec:

  type: ExternalName

  externalName: my.k8s.example.com
# kubectl apply -f myapp-svc-ExternalName.yaml 

 

posted @ 2022-11-14 23:48  Sky-wings  阅读(30)  评论(0编辑  收藏  举报