K8s Service原理介绍

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

  

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

  

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

  

  注:
  以上不论哪一种,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是否被移除或新增了,而后动态更新调度的节点数。git

#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集群的域名。github

 

建立一个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,进行直接访问。web

示例: 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。redis

  

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

 

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

 

建立名称空间的方式:
  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颁发的证书,这是怎么回事?
  #目前我尚未找到答案,但愿路过的大牛们,多多指点,万分感谢....

  

相关文章
相关标签/搜索