在Kubernetes中,服務和Pod的IP地址僅能夠在集羣網絡內部使用,對於集羣外的應用是不可見的。爲了使外部的應用可以訪問集羣內的服務,在Kubernetes中目前提供瞭如下幾種方案:html
在以前的博文中介紹過NodePort,簡單來講,就是經過service這種資源對象,爲後端pod提供一個統一的訪問接口,而後將service的統一訪問接口映射到羣集節點上,最終實現client經過映射到羣集節點上的端口訪問到後端pod提供的服務。node
可是,這種方式有一個弊端,就是當新生成一個pod服務就須要建立對應的service將其映射到節點端口,當運行的pod過多時,咱們節點暴露給client端的端口也會隨之增長,這樣咱們整個k8s羣集的危險係數就會增長,由於咱們在搭建羣集之處,官方明確指出,必須關閉firewalld防火牆及清空iptables規則,如今咱們又暴露了那麼多端口給client,安全係數可想而知。nginx
有沒有更安全又簡便的一種方法呢?答案是確定的,就是來利用Ingress這種資源對象來實現。git
博文大綱:
1、Ingress-nginx介紹
2、Ingress-nginx配置示例github
- ingress-nginx-controller:根據用戶編寫的ingress規則(建立的ingress的yaml文件),動態的去更改nginx服務的配置文件,而且reload重載使其生效(是自動化的,經過lua腳原本實現);
- ingress資源對象:將Nginx的配置抽象成一個Ingress對象,每添加一個新的Service資源對象只需寫一個新的Ingress規則的yaml文件便可(或修改已存在的ingress規則的yaml文件)
1)動態配置服務
若是按照傳統方式, 當新增長一個服務時, 咱們可能須要在流量入口加一個反向代理指向咱們新的k8s服務. 而若是用了Ingress-nginx, 只須要配置好這個服務, 當服務啓動時, 會自動註冊到Ingress的中, 不須要而外的操做。
2)減小沒必要要的端口映射
配置過k8s的都清楚, 第一步是要關閉防火牆的, 主要緣由是k8s的不少服務會以NodePort方式映射出去, 這樣就至關於給宿主機打了不少孔, 既不安全也不優雅. 而Ingress能夠避免這個問題, 除了Ingress自身服務可能須要映射出去, 其餘服務都不要用NodePort方式web
1)ingress controller經過和kubernetes api交互,動態的去感知集羣中ingress規則變化,
2)而後讀取它,按照自定義的規則,規則就是寫明瞭哪一個域名對應哪一個service,生成一段nginx配置,
3)再寫到nginx-ingress-controller的pod裏,這個Ingress controller的pod裏運行着一個Nginx服務,控制器會把生成的nginx配置寫入/etc/nginx.conf文件中,
4)而後reload一下使配置生效。以此達到域名分別配置和動態更新的問題。docker
注:文中全部的yaml文件及鏡像均可以經過個人網盤連接下載(httpd和tomcat除外)。vim
#運行registry私有倉庫 [root@master ~]# docker run -tid --name registry -p 5000:5000 --restart always registry [root@master ~]# vim /usr/lib/systemd/system/docker.service #修改配置文件,指定私有倉庫 ExecStart=/usr/bin/dockerd -H unix:// --insecure-registry 192.168.20.6:5000 #將修改後的文件發送到k8s羣集中的其餘節點 [root@master ~]# scp /usr/lib/systemd/system/docker.service root@node01:/usr/lib/systemd/system/ docker.service 100% 1637 1.6KB/s 00:00 [root@master ~]# scp /usr/lib/systemd/system/docker.service root@node02:/usr/lib/systemd/system/ #在各個節點包括master上執行如下命令,重啓docker,使更改生效 [root@master ~]# systemctl daemon-reload [root@master ~]# systemctl restart docker #上傳測試所需的鏡像到私有倉庫 [root@master ~]# docker push 192.168.20.6:5000/httpd:v1 [root@master ~]# docker push 192.168.20.6:5000/tomcat:v1
[root@master ~]# kubectl create ns test-ns #建立名稱空間test-ns [root@master ~]# kubectl get ns #確認建立成功
[root@master test]# vim httpd-01.yaml #編寫基於httpd服務的資源對象 kind: Deployment apiVersion: extensions/v1beta1 metadata: name: web01 namespace: test-ns spec: replicas: 3 template: metadata: labels: app: httpd01 spec: containers: - name: httpd image: 192.168.20.6:5000/httpd:v1 --- apiVersion: v1 kind: Service metadata: name: httpd-svc namespace: test-ns spec: selector: app: httpd01 ports: - protocol: TCP port: 80 targetPort: 80 [root@master test]# kubectl apply -f httpd-01.yaml #執行yaml文件
[root@master test]# vim tomcat-01.yaml #編寫yaml文件以下 kind: Deployment apiVersion: extensions/v1beta1 metadata: name: web02 namespace: test-ns spec: replicas: 3 template: metadata: labels: app: tomcat01 spec: containers: - name: tomcat image: 192.168.20.6:5000/tomcat:v1 --- apiVersion: v1 kind: Service metadata: name: tomcat-svc namespace: test-ns spec: selector: app: tomcat01 ports: - protocol: TCP port: 8080 targetPort: 8080 [root@master test]# kubectl apply -f tomcat-01.yaml #執行yaml文件
[root@master test]# kubectl get po -n test-ns #肯定pod是正常運行狀態 NAME READY STATUS RESTARTS AGE web01-757cfc547d-fmjnt 1/1 Running 0 8m24s web01-757cfc547d-pjrrt 1/1 Running 0 9m30s web01-757cfc547d-v7tdb 1/1 Running 0 8m24s web02-57c46c759d-l9qzx 1/1 Running 0 4m9s web02-57c46c759d-vs6mg 1/1 Running 0 4m9s web02-57c46c759d-zknrw 1/1 Running 0 4m9s [root@master test]# kubectl get svc -n test-ns #確認SVC建立成功 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd-svc ClusterIP 10.107.211.219 <none> 80/TCP 10m tomcat-svc ClusterIP 10.101.159.1 <none> 8080/TCP 5m8s #訪問SVC的clusterIP+端口,肯定能夠訪問到後端Pod [root@master test]# curl -I 10.101.159.1:8080 #訪問tomcat HTTP/1.1 200 #返回狀態碼爲200 Content-Type: text/html;charset=UTF-8 Transfer-Encoding: chunked Date: Fri, 22 Nov 2019 12:34:32 GMT [root@master test]# curl -I 10.107.211.219:80 #訪問httpd HTTP/1.1 200 OK #狀態碼爲200 Date: Fri, 22 Nov 2019 12:34:39 GMT Server: Apache/2.4.41 (Unix) #版本號也有 Last-Modified: Sat, 16 Nov 2019 10:00:39 GMT ETag: "1a-59773c95e7fc0" Accept-Ranges: bytes Content-Length: 26 Content-Type: text/html #若是在上述訪問測試中,沒有訪問到相應的pod,建議使用「kubectl describe svc」命令, #查看相應的service中的Endpoints列中有沒有關聯後端pod。
下載我提供的鏡像並導入到須要運行Ingress-nginx的節點上,也能夠選擇自行下載其餘鏡像。後端
方法1:去gitlab搜索Ingress-nginx,點擊「deploy」,再點擊頁面下的跳轉連接,便可看到以下命令:api
#不要直接複製命令到終端執行,先將yaml文件下載下來 [root@master test]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml [root@master test]# vim mandatory.yaml #修改其yaml文件 #修改如下內容 spec: #這是在212行的spec字段 hostNetwork: true #添加這行,表示使用主機網絡 # wait up to five minutes for the drain of connections terminationGracePeriodSeconds: 300 serviceAccountName: nginx-ingress-serviceaccount nodeSelector: Ingress: nginx #設置節點的標籤選擇器,指定在哪臺節點上運行 containers: - name: nginx-ingress-controller image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1 #上面是指定使用什麼鏡像,若須要更改鏡像名稱,改這裏便可,我保持默認。 #修改後保存退出便可 [root@master test]# kubectl label nodes node01 Ingress=nginx #對node01節點打相應的標籤,以便指定Ingress-nginx運行在node01 #能夠執行下面的命令,查看node01的標籤是否存在 [root@master test]# kubectl get nodes node01 --show-labels #在node01節點執行下面命令,導入Ingress-nginx鏡像(自行上傳這個包,我提供的網盤連接中有) [root@node01 ~]# docker load < nginx-ingress-controller.0.26.1.tar #手動將ingress-nginx鏡像導入到node01節點 #回到master節點,執行yaml文件 [root@master test]# kubectl apply -f mandatory.yaml #執行ingress-nginx的yaml文件
關於上面yaml文件中寫入的「hostNetwork: true」具體解釋:若是使用此網絡參數,那麼pod中運行的應用程序能夠直接使用Node節點端口,這樣node節點主機所在的網絡的其餘主機,均可以經過訪問到此應用程序。
[root@master test]# kubectl get pod -n ingress-nginx -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-ingress-controller-77c8f6577b-6shdc 1/1 Running 0 107s 192.168.20.7 node01 <none> <none>
[root@master test]# vim ingress.yaml #編寫yaml文件以下 apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress namespace: test-ns annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: www.test01.com http: paths: - path: / backend: serviceName: httpd-svc servicePort: 80 - path: /tomcat backend: serviceName: tomcat-svc servicePort: 8080 [root@master test]# kubectl apply -f ingress.yaml #執行ingress規則的yaml文件 [root@master test]# kubectl get ingresses -n test-ns #查看ingresses規則資源對象 NAME HOSTS ADDRESS PORTS AGE test-ingress www.test01.com 80 28s
其實,至此已經實現了咱們想要的功能,如今就能夠經過www.test01.com 來訪問到咱們後端httpd容器提供的服務,經過www.test01.com/tomcat 來訪問咱們後端tomcat提供的服務,固然,前提是自行配置DNS解析,或者直接修改client的hosts文件。訪問頁面以下(注意:必定要本身解決域名解析的問題,若不知道域名對應的是哪一個IP,請跳過這兩個圖,看下面的文字解釋):
訪問httpd服務(首頁內容是我自定義的):
訪問tomcat服務:
在上面的訪問測試中,雖然訪問到了對應的服務,可是有一個弊端,就是在作DNS解析的時候,只能指定Ingress-nginx容器所在的節點IP。而指定k8s集羣內部的其餘節點IP(包括master)都是不能夠訪問到的,若是這個節點一旦宕機,Ingress-nginx容器被轉移到其餘節點上運行(不考慮節點標籤的問題,其實保持Ingress-nginx的yaml文件中默認的標籤的話,那麼每一個節點都是有那個標籤的)。隨之還要咱們手動去更改DNS解析的IP(要更改成Ingress-nginx容器所在節點的IP,經過命令「kubectl get pod -n ingress-nginx -o wide」能夠查看到其所在節點),非常麻煩。
有沒有更簡單的一種方法呢?答案是確定的,就是咱們爲Ingress-nginx規則再建立一個類型爲nodePort的Service,這樣,在配置DNS解析時,就可使用www.test01.com 綁定全部node節點,包括master節點的IP了,非常靈活。
就在剛纔找到Ingress-nginx的yaml文件的頁面,而後下拉頁面,便可看到如下,能夠根據k8s集羣環境來選擇適合本身的yaml文件,假如本身是在Azure雲平臺搭建的K8s集羣,則選擇複製Azure下面的命令便可,我這裏是本身的測試環境,因此選擇Bare-metal下面的yaml文件:
建立這個Service有兩種方法,一是直接複製其web頁面的命令到master節點上執行,二是將其連接地址複製到終端使用wget下載下來再執行,我選擇第二種方法,由於我想看看裏面的內容:
#將其下載到本地 [root@master test]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml [root@master test]# cat service-nodeport.yaml #僅僅是查看一下內容,並不須要修改 apiVersion: v1 kind: Service metadata: name: ingress-nginx namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx spec: type: NodePort ports: - name: http port: 80 targetPort: 80 protocol: TCP - name: https port: 443 targetPort: 443 protocol: TCP selector: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx --- [root@master test]# kubectl apply -f service-nodeport.yaml #執行下載下來的yaml文件 [root@master test]# kubectl get svc -n ingress-nginx #查看運行的service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx NodePort 10.109.106.246 <none> 80:30465/TCP,443:32432/TCP 56s #能夠看到service分別將80和443端口映射到了節點的30645和32432端口(隨機映射的,也能夠修改yaml文件指定端口)。
至此,這個www.test01.com 的域名便可和羣集中任意節點的30465/32432端口進行綁定了。
測試以下(域名解析對應的IP能夠是k8s羣集內的任意節點IP):
至此,就實現了最初的需求。
若是如今是另外一種需求,我須要將www.test01.com 和www.test02.com 都對應上我後端的httpd容器提供的服務,那麼此時應該怎麼配置?
[root@master test]# vim ingress.yaml #修改ingress規則的yaml文件以下 apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress namespace: test-ns annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: www.test02.com #增長這一段host配置 http: paths: - path: / backend: serviceName: httpd-svc #綁定和www.test01相同的service名字便可 servicePort: 80 - host: www.test01.com http: paths: - path: / backend: serviceName: httpd-svc servicePort: 80 - path: /tomcat backend: serviceName: tomcat-svc servicePort: 8080 #增長完上述的host字段保存退出便可 [root@master test]# kubectl apply -f ingress.yaml #從新執行yaml文件
至此,便可實現訪問www.test01.com 和www.test02.com 均可以訪問到後端的httpd提供的頁面(自行解決域名解析問題),以下:
訪問www.test01.com:30465
訪問www.test02.com:30465
總結上述示例的pod是如何一步一步可使client訪問到的,總結以下:
後端pod===》service====》ingress規則====》寫入Ingress-nginx-controller配置文件並自動重載使更改生效===》對Ingress-nginx建立service====》實現client不管經過哪一個K8節點的IP+端口均可以訪問到後端pod
在上面的操做中,實現了使用ingress-nginx爲後端全部pod提供一個統一的入口,那麼,有一個很是嚴肅的問題須要考慮,就是如何爲咱們的pod配置CA證書來實現HTTPS訪問?在pod中直接配置CA麼?那須要進行多少重複性的操做?並且,pod是隨時可能被kubelet殺死再建立的。固然這些問題有不少解決方法,好比直接將CA配置到鏡像中,可是這樣又須要不少個CA證書。
這裏有更簡便的一種方法,就拿上面的狀況來講,後端有多個pod,pod與service進行關聯,service又被ingress規則發現並動態寫入到ingress-nginx-controller容器中,而後又爲ingress-nginx-controller建立了一個Service映射到羣集節點上的端口,來供client來訪問。
在上面的一系列流程中,關鍵的點就在於ingress規則,咱們只須要在ingress的yaml文件中,爲域名配置CA證書便可,只要能夠經過HTTPS訪問到域名,至於這個域名是怎麼關聯到後端提供服務的pod,這就是屬於k8s羣集內部的通訊了,即使是使用http來通訊,也無傷大雅。
接下來的配置與上面的配置基本沒什麼關係,可是因爲上面已經運行了Ingress-nginx-controller容器,因此這裏就沒有必要再運行了。只須要配置pod、service、ingress規則便可。
#建立CA證書(測試環境,本身建立吧) [root@master https]# openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc" #當前目錄下會生成兩個文件,以下: [root@master https]# ls #肯定當前目錄下有這兩個文件 tls.crt tls.key #將生成的CA證書存儲到etcd [root@master https]# kubectl create secret tls tls-secret --key=tls.key --cert tls.crt #建立deploy、service、ingress資源對象 [root@master https]# vim httpd03.yaml #編寫yaml文件 kind: Deployment apiVersion: extensions/v1beta1 metadata: name: web03 spec: replicas: 2 template: metadata: labels: app: httpd03 spec: containers: - name: httpd3 image: 192.168.20.6:5000/httpd:v1 --- apiVersion: v1 kind: Service metadata: name: httpd-svc3 spec: selector: app: httpd03 ports: - protocol: TCP port: 80 targetPort: 80 --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress3 spec: tls: - hosts: - www.test03.com secretName: tls-secret #這裏是指定的是etcd存儲的CA證書名稱 rules: - host: www.test03.com http: paths: - path: / backend: serviceName: httpd-svc3 servicePort: 80 [root@master https]# kubectl apply -f httpd03.yaml #執行yaml文件
確認建立的資源對象正常運行:
[root@master https]# kubectl get svc #查看svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd-svc3 ClusterIP 10.98.180.104 <none> 80/TCP 31s kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19d [root@master https]# kubectl get pod #查看pod NAME READY STATUS RESTARTS AGE web03-66dfbc8cf-w6vvp 1/1 Running 0 34s web03-66dfbc8cf-zgxd7 1/1 Running 0 34s [root@master https]# kubectl describe ingresses #查看ingress規則 Name: test-ingress3 Namespace: default Address: 10.109.106.246 Default backend: default-http-backend:80 (<none>) TLS: tls-secret terminates www.test03.com Rules: Host Path Backends ---- ---- -------- www.test03.com / httpd-svc3:80 (10.244.1.13:80,10.244.2.9:80) #肯定關聯到對應的service及後端的pod
https訪問測試:
使用https://www.test03.com 進行訪問(自行解決域名解析問題)
———————— 本文至此結束,感謝閱讀 ————————