• Q:Service是什么?
    • k8s的service定义了这样一种抽象:一个pod的逻辑分组,一种可以访问它们的策略——通常称为微服务。这一组Pod能够被Service访问到,通常是通过Label Selector
    • 只提供4层负载均衡能力,而没有7层功能,有时可能需要更多的匹配规则来转发请求,此时4层负载均衡是不支持的
  • Q:Service有哪4种类型?
    • ClusterIp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP
    • NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过NodeIP: NodePort 来访问该服务
    • LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到NodeIP: 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
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
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
apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: myapp
    release: stabel
  ports:
  - name: http
    port: 80
    targetPort: 80
    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的情况
- 查看服务轮训的情况
    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
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
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。相反,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务
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
kind: Service
apiVersion: v1
metadata:
  name: my-service-1
  namespace: default
spec:
  type: ExternalName
  externalName: hub.atguigu.com
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的进程以及协程的沟通方案
Last Updated:
Contributors: Shiqi Lu