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