• K8s Service原理介绍


    Service的工作方式有三种:
     第一种: 是Userspace方式
      如下图描述, Client Pod要访问Server Pod时,它先将请求发给本机内核空间中的service规则,由它再将请求,
      转给监听在指定套接字上的kube-proxy,kube-proxy处理完请求,并分发请求到指定Server Pod后,再将请求
      递交给内核空间中的service,由service将请求转给指定的Server Pod。
      由于其需要来回在用户空间和内核空间交互通信,因此效率很差,接着就有了第二种方式.

      

     第二种: iptables模型
      此工作方式是直接由内核中的iptables规则,接受Client Pod的请求,并处理完成后,直接转发给指定ServerPod.

      

      第三种: ipvs模型
      它是直接有内核中的ipvs规则来接受Client Pod请求,并处理该请求,再有内核封包后,直接发给指定的Server Pod。

      

      注:
      以上不论哪种,kube-proxy都通过watch的方式监控着kube-APIServer写入etcd中关于Pod的最新状态信息,
      它一旦检查到一个Pod资源被删除了 或 新建,它将立即将这些变化,反应再iptables 或 ipvs规则中,以便
      iptables和ipvs在调度Clinet Pod请求到Server Pod时,不会出现Server Pod不存在的情况。

      自k8s1.1以后,service默认使用ipvs规则,若ipvs没有被激活,则降级使用iptables规则. 但在1.1以前,service
     使用的模式默认为userspace.


    查看k8s集群中API Server面向集群内部的service地址:
     #其中第一个kubernets,类型为ClusterIP,暴露端口为443/tcp的即为APIServer的向集群内部提供服务的Service.
      kubectl get svc 

    创建Service的清单文件:
      kubectl explain svc
      kubectl explain svc.spec
      type:
       service的类型有四种:
      1. ExternalName: 用于将集群外部的服务引入到集群内部,在集群内部可直接访问来获取服务。
          它的值必须是 FQDN, 此FQDN为集群内部的FQDN, 即: ServiceName.Namespace.Domain.LTD.
          然后CoreDNS接受到该FQDN后,能解析出一个CNAME记录, 该别名记录为真正互联网上的域名.
          如: www.test.com, 接着CoreDNS在向互联网上的根域DNS解析该域名,获得其真实互联网IP.
      2. ClusterIP: 用于为集群内Pod访问时,提供的固定访问地址,默认是自动分配地址,可使用ClusterIP关键字指定固定IP.

      3. NodePort: 用于为集群外部访问Service后面Pod提供访问接入端口.
        这种类型的service工作流程为:
          Client----->NodeIP:NodePort----->ClusterIP:ServicePort----->PodIP:ContainerPort
      4. LoadBalancer: 用于当K8s运行在一个云环境内时,若该云环境支持LBaaS,则此类型可自动触发创建
            一个软件负载均衡器用于对Service做负载均衡调度.
        因为外部所有Client都访问一个NodeIP,该节点的压力将会很大, 而LoadBalancer则可解决这个问题。
        而且它还直接动态监测后端Node是否被移除或新增了,然后动态更新调度的节点数。

    #Service清单文件创建示例:
    vim  redis-svc.yaml         #定义一个redis的服务.
    apiVersion: v1
    kind: Service
    metadata:
      name: redis
      namespace: default
    spec:
     selector:                  #指定标签选择器选择的标签范围.
       app: redis
       role: logstor
     clusterIP: 10.97.97.97
     type: ClusterIP
     ports:
     -  name: redis
        port: 6379           #设定Serivce对外提供服务的端口.
        targetPort: 6379     #设定容器(Pod)的端口,即Pod网络的端口。
        nodePort:            #它仅在type为NodePort时才需要指定.

      接着创建服务:
      kubectl apply -f redis-svc.yaml
      kubectl get svc
      kubectl describe svc redis           #可看到service redis它的详细配置信息.
        Endpoints: 10.224.1.3x:6379    #这个就是Pod的地址,Serice和Pod实际上并非直接联系,中间
                       #还有一个Endpoint作为转发。所以这里显示的是Endpoint而非Pod.
    K8s中资源的全局FQDN格式:
      Service_NAME.NameSpace_NAME.Domain.LTD.
      Domain.LTD.=svc.cluster.local.     #这是默认k8s集群的域名。

    创建一个nodePort类型的service,让节点外主机可以访问到服务.
    vim  myapp-svc.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: myapp
      namespace: default
    spec:
      selector:
        app: myapp
        release: canary
      clusterIP: 10.99.99.99  #此地址设定为固定很容易冲突, 建议最好不写,让其自动分配.
      type: NodePort
      ports:
      - port: 80          #设置Service对外提供服务的端口
        targetPort: 80    # 设置Pod对外提供服务的端口.
        nodePort: 30080   #此宿主机端口也可不指定,系统将自动从3万~65535分配,若必须设为80,也可以,
                          #但必须保证所以宿主机上的80端口没有被占用,若有被占用,则该节点上的服务将无法被访问.

    Service的externalName类型原理介绍:
     Pod---->SVC[externalName]------[SNAT]----->宿主机的物理网卡------>物理网关----->Internat上提供服务的服务器.
       注意: Service是externelName类型时, externalName必须是域名,而且此域名必须能被CoreDNS或CoreDNS能通过
         互联网上的根DNS解析出A记录.

    Service在对外提供服务时,还支持会话粘性:
      sessionAffinity : 它支持ClientIP 和 None两种方式.
      None: 即不做会话粘性,进行随机调度.
      ClientIP: 根据客户端IP来调度,同一个客户端IP都调度到同一个后端主机。

    通过打补丁的方式来测试会话粘性:
      #对上面创建的myapp service打补丁,让其支持基于ClientIP的会话粘性.
      kubectl patch svc myapp -p ‘{"spec":{"sessionAffinity":"ClientIP"}}’

      kubectl describe svc myapp    #可以查看到多了一个Session Affinity的字段.


    headless service(无头service):
      所谓headless service指: 没有ClusterIP的service, 它仅有一个service name.这个服务名解析得到的不是
      service的集群IP,而是Pod的IP,当其它人访问该service时,将直接获得Pod的IP,进行直接访问。

    示例:
    vim  myapp-svc-headless.yaml
    apiVersion: v1
    kind: Service
    metadata: 
      name: myapp-headless
      namespace: default
    spec:
      selector:
         app: myapp
         release: canary
      clusterIP: None
      ports:
      - port: 80
        targetPort: 80

      #创建headless service:
      kubectl apply -f myapp-svc-headless.yaml
      kubectl get svc    #将可以看到ClusterIP项为None.

      #这里将显示service name解析为Pod的IP了.
      dig -t A myapp-headless.default.svc.cluster.local. @CoreDNS_IP    

      #CoreDNS_IP的查看方式:
      kubectl get svc -n kube-system

      #查看Pod的IP:
      kubectl get pods -o wide -l app=myapp



    Ingress Controller
      下图即为Ingress Controller这种独特的控制器资源的工作流程图.
      需要注意: Ingress Controller 和 Ingress是两个不同的资源。
      Ingress它是通过headless service的集群内FQDN获取到它后端所有的Pod资源的IP,因为headless Service没有
        ClusterIP,它的域名对应的IP为PodIP。Ingress获取PodIP后,在立刻将其写入到ingress-nginx的配置文件中,
        并触发nginx重读配置文件。实现动态更新upstream。
        另外,外部LB可以直接跳过Service,直接访问到nginx,这需要将nginx这个Pod作为共享宿主机的网络名称空间
        才能实现,这时就需要借助于daemonSet控制器来控制着nginx这种七层反代Pod仅允许在指定节点上,并且
        每个节点上仅运行一个nginx Pod。

      

     #注:
      DaemonSet,RepliceSet,Deployment,StatuefulSet 等它们都是Master上的ControllerManager
     的子组件,而Ingress Controller则是独立运行的一个或一组Pod资源,它通常就是一个拥有七层
       调度能力的应用程序,在K8s上这种应用程序有四种:
      Nginx:一般默认使用Nginx作为这种应用程序。
      Traefik: 它原先设计也是为微服务而生的,就是为了能实现动态生成配置文件。
      Envoy: 在服务网格 或 微服务 中通常会比较喜欢用它.
      Traefik和Envoy: 它们都可以实现动态监控配置文件发生变化,若发生变化,
              则会即时自动重载配置,而无需手动参与。
      HAProxy: 它是最不受欢迎的一种解决方案。

    查看ingress controller的定义:
      kubectl explain ingress
      kubectl explain ingress.spec

    创建名称空间的方式:
      1. 使用命令:
        kubectl create namespace test
        kubectl get ns
        kubectl delete ns/test              #删除一个test名称空间, 需要注意: 删除一个名称空间,则会删除其内所有的资源.
      2. 使用清单创建名称空间:
        apiVersion: v1
        kind: Namespace
        metadata:
          name: test

    实现Ingress-ngnix的示例:
      下面这个项目是kubernetes中ingress-nginx项目安装说明
      https://github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md

    #由于我是直接用VMware上的VM安装的K8s,因此使用裸机(Bare-metal)的方式来安装.
    #首先,下载这个主配置文件,它里面帮我们写好了创建,Ingress-nginx所必须namespace,configMap,ServiceAccount,RBAC,Role,和ingress-nginx等.
    # 在使用下面这清单文件时,建议先把ingress-nginx的镜像先下载下来,避免下载镜像失败导致创建ingress-nginx失败.
      wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml

    #接着, 下载裸机所对应的创建Ingress-nginx的Service配置清单.
      https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml

    #在继续之前先看下,当前的拓扑图

      

        上面mandatory.yaml 帮我们创建了Nginx-Ingres-Controller,另外还有一些资源没有画出来.
       service-nodeport.yaml 帮我们创建了Service/Ingress-nginx

      

    #接着我们需要自己创建Ingress 和 Service/myapp 以及三个myapp Pod
    vim   deploy-demo.yaml 
    apiVersion: v1
    kind: Service       #这部分创建service/myapp
    metadata:
      name: myapp
      namespace: default
    spec:
      selector:         #通过下面两个标签(label)来选择Pod
        app: myapp
        release: canary
      ports:
      - name: http
         targetPort: 80
         port: 80
    
    ---
    apiVersion: apps/v1
    kind: Deployment     #这部分来创建Myapp Pod的deployment.apps控制器
    metadata:
      name: myapp-ingress
      namespace: default
    spec:
      replicas: 3        #设置其副本数量控制器ReplicaSet,监控Pod至少保证有3个
      selector:
        matchLabels:     #它筛选自己管理的Pod时,使用的label是下面两个.
          app: myapp
          release: canary
      template:          #replicaSet发现Pod不足时,使用此模板定义的规则,创建Pod.
        metadata:
          labels:        #每次创建的Pod都打上下面两个label
            app: myapp
            release: canary
        spec:
          containers:
          - name: myapp
             image: harbor.zcf.com/k8s/myapp:v1
             ports:
             - name: http
                containerPort: 80
    #接着创建ingress
    vim   ingress-myapp.yaml 
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: ingress-myapp
      namespace: default      #注意ingress要和后端提供Web服务的Pod处于同一名称空间中.
      annotations:
            #kubernetes.io为前缀,ingress.class:为键名,通过这种定义来告诉ingress,
            #这里使用的ingress-controller为nginx,你在生成配置时,生成nginx相关的配置。
          kubernetes.io/ingress.class: "nginx"   
    spec:                                                                          
     rules:
     ##定义使用虚拟主机来代理后端Web应用.而识别该虚拟主机的域名定义为myapp.test.com
     -  host: myapp.test.com 
        http:
          paths:
          - path:
            backend:
              #这里要指明后端提供Web服务的前端Service的名称,该Service负责筛选出提供Web服务的Pod.
              serviceName: myapp
              servicePort: 80

    #以上四步,都完成后,我们就有了下面这些文件:
      deploy-demo.yaml
      ingress-myapp.yaml
      mandatory.yaml
      service-nodeport.yaml

    1.  kubectl   apply  -f  mandatory.yaml
    #可验证以下信息: 其它信息的验证可参考扩展验证.
    # kubectl get ns
        NAME            STATUS   AGE
        default         Active   3d14h
        ingress-nginx   Active   6h12m
        .....
    # kubectl get deployments
    # kubectl describe  deployments myapp-ingress
    # kubectl get replicaset
    # kubectl describe  replicaset myapp-ingress-5b8676cff7
    
    2.  kubectl  apply  -f  service-nodeport.yaml
    #验证:
    # kubectl get service -n ingress-nginx ingress-nginx
    NAME            TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
    ingress-nginx   NodePort   172.30.245.115   <none>        80:53712/TCP,443:46652/TCP   6h36m
    3. kubectl  apply  -f  deploy-demo.yaml
    4. kubectl  apply  -f  ingress-myapp.yaml
    #验证:
    # kubectl get pod -o wide
        NAME                             READY   STATUS    RESTARTS   AGE     IP            NODE             NOMINATED NODE   READINESS GATES
        client-f5cdb799f-2wsmr           1/1     Running   3          2d18h   10.10.97.37   192.168.111.80   <none>           <none>
        myapp-ingress-5b8676cff7-jxmg8   1/1     Running   0          3h51m   10.10.97.60   192.168.111.80   <none>           <none>
        myapp-ingress-5b8676cff7-s2nsf   1/1     Running   0          3h51m   10.10.171.5   192.168.111.81   <none>           <none>
        myapp-ingress-5b8676cff7-wx5q7   1/1     Running   0          3h51m   10.10.171.4   192.168.111.81   <none>           <none>
    
    # kubectl get deployment
            NAME            READY   UP-TO-DATE   AVAILABLE   AGE
            client          1/1     1            1           2d18h
            myapp-ingress   3/3     3            3           3h52m
    
    # kubectl describe deployment myapp-ingress 
            Name:                   myapp-ingress
            .......
            Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
            StrategyType:           RollingUpdate           #deployment控制器默认的为滚动更新
            MinReadySeconds:        0
            RollingUpdateStrategy:  25% max unavailable, 25% max surge
            ......
            NewReplicaSet:   myapp-ingress-5b8676cff7 (3/3 replicas created)
            ......
    
    # kubectl get replicaset
            NAME                       DESIRED   CURRENT   READY   AGE
            client-f5cdb799f           1         1         1       2d18h
            myapp-ingress-5b8676cff7   3         3         3       3h52m
    
    # kubectl describe replicasets.apps myapp-ingress-5b8676cff7 
            Name:           myapp-ingress-5b8676cff7
            Namespace:      default
            Selector:       app=myapp,pod-template-hash=5b8676cff7,release=canary
                  ..............
            Annotations:    deployment.kubernetes.io/desired-replicas: 3
                                    deployment.kubernetes.io/max-replicas: 4      #最多只允许多出一个副本,这说明,此ReplicaSet更新策略为:滚动更新,即先创建一个,然后在删除一个。
                                    deployment.kubernetes.io/revision: 1
            Controlled By:  Deployment/myapp-ingress        #上游控制器是myapp-ingress,控制器类型: deployment
            Replicas:       3 current / 3 desired                           #副本数量, 当前3个,期望3个
            Pods Status:    3 Running / 0 Waiting / 0 Succeeded / 0 Failed

        5. 进入nginx-ingress-controller中,查看OpenResty的配置文件:
      kubectl exec -n ingress-nginx -it nginx-ingress-controller-.... -- /bin/sh
      $ cat nginx.conf
        #这里面,内容很多,可只看重点部分
        # 1. server_name myapp.test.com    #这个server段,可重点看。
        # 2. upstream upstream_balancer {}    #这段可参考,因为它可能是通过balancer_by_lua_block {} 实现获取Pod列表的.而该段调用的是ruby脚本,我暂时没看懂。

     6. 测试访问:
      再集群外部主机上访问: 必须能解析 myapp.test.com 这个域名.
        http://myapp.test.com:53712/
        <h1>WERCOME TO www.zcf.com WEB SITE | Sun Jul 21 02:13:51 UTC 2019 | myapp-ingress-5b8676cff7-s2nsf | 10.10.171.5 | -v1- | </h1>

    实验总结:
      当ingress创建完成后,就相当于它将ingress-controller 和 后端提供Web服务的Pod关联起来了。
      Pod的前端Service负责实时监控Pod的变化,并反应在自己的Endpoints中,而ingress通过定义backend为后端Service,从而与后端的Service取得联系,并获取Service的Endpoints列表,从而得到它所监控的Pod列表,这些Pod就是实际提供Web服务的后端容器。
      当Ingress获取到后端Pod列表后,它就可以联系前端的ingress-controller,并根据自己annotations中的定义知道前端ingress-controller所使用的反向代理为nginx,然后ingress就会生成nginx的配置信息,并自定写入ingress-controller-nginx中。当ingress-controller-nginx获得配置信息后,它就可以对外提供反向代理服务了。
      另外:
      ingress中定义rules,指明使用虚拟主机,虚拟主机的域名为myapp.test.com,若有多个虚拟主机,则可定义多个。那么它生成的nginx配置就是定义一个server,并指明其servername为myapp.test.com ,该虚拟主机的location / { proxy_pass http://upstream }可以简单这么理解,若有多个虚拟主机,就是定义多个server,每个server都要有独立的域名,比如论坛,商城,博客等。
      location中定义的proxy_pass 的upstream的后端服务器地址列表,则是由ingress中backend定义的serviceName所指定的myapp这个service所监控的后端Pod。
    这样以来整个访问链条就构建完成了。

    以上四步创建的结构如下图 【注: nginx-ingress-controller 就是我要说明的 ingress-controller-nginx,再书写时写错了】
      但需要注意,ingress仅是动态监控service所监控的后端Pod是否发生变化了,若发生变化,它会立即生成nginx的最新upstream配置,然后再次注入配置到ingress-controller-nginx中,这样ingress-controller-nginx就可以实时知道后端Pod的变化,并直接将前端Client的访问流量代理给后端Pod,图中看上去要先经过ingress,再转发给Pod,实际上是ingress-controller-nginx直接将请求转发给Pod,因为ingress已经将Pod的地址写入到nginx的upstream中了。

    下面示例演示配置一个tomcat的七层代理应用:
    vim  tomcat-deploy.yaml
    apiVersion: v1
    kind: Service
    metadata:
       #这个名字可根据需要定义, 如: 是一个电商站点,则可取名为 eshop 等名字.
       name: tomcat           
       namespace: default
    spec:
      selector:
         app: tomcat
         release: canary
      ports:
      -  name: http
         targetPort: 8080
         port: 8080
      -  name: ajp
         targetPort: 8009
         port: 8009
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: tomcat-deploy
      namespace: default
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: tomcat
          release: canary
      template:
        metadata:
          labels: 
            app: tomcat
            release: canary
        spec:
           containers:
           -  name: tomcat
              image: tomcat:8.5.32-jre8-alpine
              ports:
                - name: http
                  containerPort: 8080
              ports:
                - name: ajp
                  containerPort: 8009

      以上配置说明:
      它定义了一个Deployment的控制器,它下面有3个tomcat Pod,并且都暴露了8080和8009端口,然后还定义了一个service,此service将自己的8080端口映射到Pod的8080端口上,8009也一样。这个service并不作为访问这些tomcat Pod的统一入口,而是作为ingress获取这些tomcat Pod的IP而存在的。

    下面测试创建一个TLS类型的secret,然后实现使用同一套证书为两个网站提供HTTPS

    1. 使用Kubeasz部署的集群在制作证书时,必须使用cfssl工具来做,因为kubeasz是使用此工具来做证书的,它制作证书的字符编码与OpenSSL的字符编码不同,因此你若使用cfssl制作的ca证书给Openssl制作的证书签证,是不可以的,但若能修改Openssl默认证书中的字符编码,应该是可以的,但我没有研究过如何修改。

      cd  /etc/kubernetes/ssl      #这是kubeasz部署后,CA证书默认存放位置。

           cp  admin-csr.json   test.com-csr.json

    vim  test.com-csr.json
    {
      "CN": "test.com",
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [
        {
          "C": "CN",
          "ST": "HangZhou",
          "L": "XS"
        }
      ]
    }  
    
      # grep -C1 'profile' ca-config.json
      },
      "profiles": {
        "kubernetes": {
    
    # cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes test.com-csr.json | cfssljson -bare test.com

    #创建好的证书,不能直接放到nginx中使用,这个证书必须做出k8s中的资源对象,然后,让nginx来引用这个对象才行.
      kubectl create secret tls website-ingress-secret --cert=website.crt --key=website.key
        注:
               tls: 是要创建secret的类型。
          tomcat-ingress-secret:是这个secret的名字.
          --cert: 指明TLS类型的secret所使用的证书在哪里。
          --key: 指明证书的私钥的位置

      kubectl get secret                  #查看创建的secret对象
      kubectl describe secret website-ingress-secret      #查看tomcat-ingress-secret对象的详细信息。

    有了secret对象后,就可以创建基于HTTPS的tomcat访问了.

    vim  ingress-tomcat-tls.yaml
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
       name: ingress-website-tls
       namespace: default
       annotations:
            kubernetes.io/ingress.class: “nginx”
    spec:
      tls:
      - hosts:
         #这里的列表可定义多个主机, 意思是这个多个主机都将使用相同的证书来加密响应数据.这就是所谓的SNI
         -  tomcat.test.com  
         -  myapp.test.com
         secretName:  website-ingress-secret
      rules:         #rules可定义多个, 每一个就是一个虚拟主机 或 URL映射.
      -  host: tomcat.test.com
         http:
           paths:
             - path:
               backend:
                 serviceName: tomcat
                 servicePort: 8080
      -  host: myapp.test.com
         http:
           paths:
           - path:
             backend:
                serviceName:  myapp
                servicePort: 80

      以上做好以后,就可以创建支持TLS的ingress了.
      kubectl apply -f ingress-tomcat-tls.yaml
      kubectl get ingress
      kubectl describe ingress ingress-tomcat-tls

      # kubectl get svc -n ingress-nginx
        NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
        ingress-nginx NodePort 172.30.245.115 <none> 80:53712/TCP,443:46652/TCP 9h


      kubectl exec -n ingress-nginx -it nginx-ingress-controller-x... -- /bin/sh
      #登陆后可查看nginx的配置文件,确认一下tomcat是否支持HTTPS.

    最后,测试访问:
      https://tomcat.test.com:46652/
      #测试发现,Client打开网页后,查看证书,竟然是Kubernetes Ingress Controller颁发的证书,这是怎么回事?
      #目前我还没有找到答案,希望路过的大牛们,多多指点,万分感谢....

      

  • 相关阅读:
    MySQL语法
    SQL必知必会
    大话设计模式
    软件工程
    myeclipse中git的使用
    提取文件中图片标签
    pandas 分析各地区男女的分布情况
    提取图片标签
    Requwsts+正则表达式爬取猫眼电影Top100
    PyCharm激活码
  • 原文地址:https://www.cnblogs.com/wn1m/p/11288131.html
Copyright © 2020-2023  润新知