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頒發的證書,這是怎麼回事?
  #目前我尚未找到答案,但願路過的大牛們,多多指點,萬分感謝....

  

相關文章
相關標籤/搜索