每天净瞎搞

关注:AI/CS/数学/自我提升等

0%

k8s培训第6课 Service

  • Q:Service是什么?
    • k8s的service定义了这样一种抽象:一个pod的逻辑分组,一种可以访问它们的策略——通常称为微服务。这一组Pod能够被Service访问到,通常是通过Label Selector
    • 只提供4层负载均衡能力,而没有7层功能,有时可能需要更多的匹配规则来转发请求,此时4层负载均衡是不支持的
  • Q:Service有哪4种类型?
    • ClusterIp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP
    • NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过: NodePort 来访问该服务
    • LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到: NodePort
    • ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建, 这只有 kubernetes 1.7 或更高版本的 kube-dns 才支持
  • Q:Service的实现流程是怎样?
    • 1.监听服务的端点是由apiserver完成的,通过kube-proxy监控,进行服务和端点信息的发现
    • 2.kube-proxy负责监控标签匹配的Pod的信息,并把它写入到iptables的规则里
    • 3.当客户端想去访问svc的时候,其实访问的是iptables,并导向后端的pod的信息,即iptables通过kube-proxy写入的
  • Q:service有哪3种代理模式?
    • userspace代理模式
    • iptables代理模式
    • ipvs代理模式
    • ipvs模式下,kube-proxy会监视k8s service对象和endpoints,调用netlink接口以相应地创建ipvs规则并定期与k8s service对象和endpoints对象同步ipvs规则,以确保ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端Pod
  • Q:ClusterIP的工作方式是什么?
    • ClustuerIP主要在每个node节点使用iptables,将发向clusterIP对应端口的数据,转发到kube-proxy中。然后kube-proxy自己内部实现负载均衡的方法,并可以查询到这个service下对应pod的地址和端口,进而把数据转发给对应的Pod的地址和端口
    • 为了实现图上的功能,主要需要以下几个组件的协同工作:
      • apiserver用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求后将数据存储到etcd中
      • kube-proxy k8s的每个节点中都有一个叫做kube-proxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables规则中
      • iptables使用NAT等技术将virtualIP的流量转至endpoint中
  • Q:ClusterIP的实验演示
    • 创建deployment:myapp-deploy.yaml
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      apiVersion: apps/v1
      kind: Deployment
      metadata:
      name: myapp-deploy
      namespace: default
      spec:
      replicas: 3
      selector:
      matchLabels:
      app: myapp
      release: stabel
      template:
      metadata:
      labels:
      app: myapp
      release: stabel
      env: test
      spec:
      containers:
      - name: myapp
      image: wangyanglinux/myapp:v2
      imagePullPolicy: IfNotPresent
      ports:
      - name: http
      containerPort: 80
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      shiqi-lu@k8s-master:~/6$ kubectl apply -f myapp-deploy.yaml
      deployment.apps/myapp-deploy created
      shiqi-lu@k8s-master:~/6$ kubectl get pod
      NAME READY STATUS RESTARTS AGE
      myapp-deploy-c7b5fb585-lw9rr 0/1 ContainerCreating 0 22s
      myapp-deploy-c7b5fb585-pqd8j 0/1 ContainerCreating 0 22s
      myapp-deploy-c7b5fb585-stkdm 0/1 ContainerCreating 0 22s
      shiqi-lu@k8s-master:~/6$ kubectl get pod -o wide
      NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
      myapp-deploy-c7b5fb585-lw9rr 1/1 Running 0 62m 10.244.6.101 zhangliang <none> <none>
      myapp-deploy-c7b5fb585-pqd8j 1/1 Running 0 62m 10.244.8.108 kongming <none> <none>
      myapp-deploy-c7b5fb585-stkdm 1/1 Running 0 62m 10.244.4.51 laojun <none> <none>
      shiqi-lu@k8s-master:~/6$ curl 10.244.6.101
      Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
  • 创建service信息:myapp-service.yaml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    apiVersion: v1
    kind: Service
    metadata:
    name: myapp
    namespace: default
    spec:
    type: ClusterIP
    selector:
    app: myapp
    release: stabel
    ports:
    - name: http
    port: 80
    targetPort: 80
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    shiqi-lu@k8s-master:~/6$ kubectl apply -f myapp-service.yaml
    service/myapp created
    shiqi-lu@k8s-master:~/6$ kubectl get svc
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    myapp ClusterIP 10.101.48.92 <none> 80/TCP 20s
    shiqi-lu@k8s-master:~/6$ kubectl get svc -n default
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 114d
    shiqi-lu@k8s-master:~/6$ curl 10.101.48.92
    Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
    • 可以使用命令:ipvsadm -Ln查看ipvs的情况
    • 查看服务轮训的情况
      1
      2
      3
      4
      5
      6
      7
      8
      shiqi-lu@k8s-master:~/6$ curl 10.101.48.92/hostname.html
      myapp-deploy-c7b5fb585-pqd8j
      shiqi-lu@k8s-master:~/6$ curl 10.101.48.92/hostname.html
      myapp-deploy-c7b5fb585-stkdm
      shiqi-lu@k8s-master:~/6$ curl 10.101.48.92/hostname.html
      myapp-deploy-c7b5fb585-lw9rr
      shiqi-lu@k8s-master:~/6$ curl 10.101.48.92/hostname.html
      myapp-deploy-c7b5fb585-lw9rr
  • Q:Headless Service是什么?
    • 有时不需要或不想要负载均衡,以及单独的Service IP。遇到这种情况,可以通过制定ClusterIP(spec.clusterIP)的值为”None”来创建Headless Service。这类Service并不会分配Cluster IP,kube-proxy不会处理它们,而且平台也不会为它们进行负载均衡和路由器
    • 通过这个方式来解决clustername和podname变化问题,即通过它来绑定
  • Q:Headless Service的实验
    • 创建myapp-svc-headless.yaml
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      apiVersion: v1
      kind: Service
      metadata:
      name: myapp-headless
      namespace: default
      spec:
      selector:
      app: myapp
      clusterIP: "None"
      ports:
      - port: 80
      targetPort: 80
      t: 8```
      - ```shell
      shiqi-lu@k8s-master:~/6$ kubectl apply -f myapp-svc-headless.yaml
      service/myapp-headless created
      shiqi-lu@k8s-master:~/6$ kubectl get svc
      NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
      myapp ClusterIP 10.101.48.92 <none> 80/TCP 46m
      myapp-headless ClusterIP None <none> 80/TCP 3m11s```
      - 发现底下的地址为空
      - ```shell
      shiqi-lu@k8s-master:~/6$ kubectl get pod -n kube-system -o wide
      NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
      coredns-7ff77c879f-kdvsd 1/1 Running 1 10d 10.244.8.16 kongming <none> <none>
      coredns-7ff77c879f-vc9gn 1/1 Running 2 10d 10.244.1.116 shennong <none> <none>```
      - 从coredns中找一个ip,下面这个命令试试来解析,格式是Service的名字+namespace+host
      - ```shell
      shiqi-lu@k8s-master:~/6$ dig -t A myapp-headless.shiqi-lu.svc.cluster.local. @10.244.8.16

      ; <<>> DiG 9.11.3-1ubuntu1.13-Ubuntu <<>> -t A myapp-headless.shiqi-lu.svc.cluster.local. @10.244.8.16
      ;; 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: 54521
      ;; 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
      ; COOKIE: a253bf35c4375eaf (echoed)
      ;; QUESTION SECTION:
      ;myapp-headless.shiqi-lu.svc.cluster.local. IN A

      ;; ANSWER SECTION:
      myapp-headless.shiqi-lu.svc.cluster.local. 30 IN A 10.244.4.51
      myapp-headless.shiqi-lu.svc.cluster.local. 30 IN A 10.244.6.101
      myapp-headless.shiqi-lu.svc.cluster.local. 30 IN A 10.244.8.108

      ;; Query time: 0 msec
      ;; SERVER: 10.244.8.16#53(10.244.8.16)
      ;; WHEN: Tue Oct 27 20:05:35 CST 2020
      ;; MSG SIZE rcvd: 253
      shiqi-lu@k8s-master:~/6$ kubectl get pod -o wide
      NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
      myapp-deploy-c7b5fb585-lw9rr 1/1 Running 0 122m 10.244.6.101 zhangliang <none> <none>
      myapp-deploy-c7b5fb585-pqd8j 1/1 Running 0 122m 10.244.8.108 kongming <none> <none>
      myapp-deploy-c7b5fb585-stkdm 1/1 Running 0 122m 10.244.4.51 laojun <none> <none>```
      - 虽然headless没有IP,但仍然可以通过访问域名的方式访问
      - Q:NodePort是什么?
      - 原理是在node上开了一个端口,将向该端口的流量导入到kube-proxy,然后由kube-proxy进一步给到对应的Pod
      - 创建nodeport.yaml,除了type中不一样,其它几乎不变
      - ```yaml
      apiVersion: v1
      kind: Service
      metadata:
      name: myapp
      namespace: default
      spec:
      type: NodePort
      selector:
      app: myapp
      release: stabel
      ports:
      - name: http
      port: 80
      targetPort: 80
      1
      2
      3
      4
      5
      6
      7
      8
      shiqi-lu@k8s-master:~/6$ kubectl apply -f nodeport.yaml
      service/myapp configured
      shiqi-lu@k8s-master:~/6$ kubectl get svc
      NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
      myapp NodePort 10.101.48.92 <none> 80:30707/TCP 64m
      myapp-headless ClusterIP None <none> 80/TCP 21m
      shiqi-lu@k8s-master:~/6$ curl localhost:30707
      Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
  • Q:LoadBalancer是什么?
    • 和nodePort其实是同一种方式,区别在于loadBalancer比nodePort多了一步,就是可以调用cloud provider去创建LoadBalancer来向节点导流
  • Q:ExternalName是什么?
    • 这种类型的Service通过返回CNAME和它的值,可以将服务映射到externalName字段的内容(例如:hub.atguigu.com)。ExternalName Service是Service的特例,它没有selector,也没有定义任何端口和endpoint。相反,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务
      1
      2
      3
      4
      5
      6
      7
      8
      kind: Service
      apiVersion: v1
      metadata:
      name: my-service-1
      namespace: default
      spec:
      type: ExternalName
      externalName: hub.atguigu.com
    • 当查询主机my-service-1.default.svc.cluster.local (SVC_NAME.NAMESPACE.svc.cluster.local)时,集群的DNS服务将返回一个值hub.atguigu.com的CNAME记录。访问这个服务的工作方式和其它的相同,唯一不同的是重定向发生在DNS层,而且不会进行代理或转发
  • Q:ExternalName的创建实验
    • 创建ex.yaml
      1
      2
      3
      4
      5
      6
      7
      8
      kind: Service
      apiVersion: v1
      metadata:
      name: my-service-1
      namespace: default
      spec:
      type: ExternalName
      externalName: hub.atguigu.com
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      shiqi-lu@k8s-master:~/6$ kubectl create -f ex.yaml
      service/my-service-1 created
      shiqi-lu@k8s-master:~/6$ kubectl get svc
      NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
      my-service-1 ExternalName <none> hub.atguigu.com <none> 9s
      myapp NodePort 10.101.48.92 <none> 80:30707/TCP 91m
      myapp-headless ClusterIP None <none> 80/TCP 47m
      shiqi-lu@k8s-master:~/6$ dig -t A my-service-1.shiqi-lu.svc.cluster.local. @10.244.8.16

      ; <<>> DiG 9.11.3-1ubuntu1.13-Ubuntu <<>> -t A my-service-1.shiqi-lu.svc.cluster.local. @10.244.8.16
      ;; 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: 60219
      ;; 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
      ; COOKIE: ed3b24efd39612a1 (echoed)
      ;; QUESTION SECTION:
      ;my-service-1.shiqi-lu.svc.cluster.local. IN A

      ;; ANSWER SECTION:
      my-service-1.shiqi-lu.svc.cluster.local. 30 IN CNAME hub.atguigu.com.

      ;; Query time: 39 msec
      ;; SERVER: 10.244.8.16#53(10.244.8.16)
      ;; WHEN: Tue Oct 27 20:45:03 CST 2020
      ;; MSG SIZE rcvd: 148
  • Q:nginx-ingress的工作原理
    • 客户端访问域名,ingress必须先绑定一个域名,不同域名访问nginx,nginx会反向代理,负载均衡选择后端的service,每个service会有一些容器
    • nginx其实使用nodeport的方式
    • 首先k8s的apiserver和store建立监听状态,这里的监听方式是以协程的Pod的形式向apiserver发起连接的方案进行监听,如果发生新的数据写入,会被写入到updateChannel的循环队列里面,然后有一个nginxController的主进程会去监听这个循环队列里面的资源和事件,发生一个循环以后会更新一个事件,把它写入到我们的同步队列里去,等待被协程去更改配置文件,协程会定期的从队列里拉去执行添加的一些任务,如果有一些必要的直接需要去修改的,不需要去等待直接要更新的一些任务会自动发直接发送到SyncQueue协程,两个协程之间协商直接沟通,收取到所有的要更新的数据以后会去他去判断是否要进行一个重载(reload),写入数据的这么一个方案,还是等等再去添加,如果有的话就会写入nginx主配置文件,然后重新载入nginx的一些数据,那如果不需要的话就等待后面去执行,直接发送构建的post的数据接口,也就是一些不需要存在的数据即可,最后以nginx模块去运行,那这就是nginx-ingress的进程以及协程的沟通方案