K8s——Ingress-nginx原理及配置

在Kubernetes中,服務和Pod的IP地址僅能夠在集羣網絡內部使用,對於集羣外的應用是不可見的。爲了使外部的應用可以訪問集羣內的服務,在Kubernetes中目前提供瞭如下幾種方案:html

  1. NodePort
  2. LoadBalancer
  3. Ingress

NodePort,簡單來講,就是經過service這種資源對象,爲後端pod提供一個統一的訪問接口,而後將service的統一訪問接口映射到羣集節點上,最終實現client經過映射到羣集節點上的端口訪問到後端pod提供的服務。node

可是,這種方式有一個弊端,就是當新生成一個pod服務就須要建立對應的service將其映射到節點端口,當運行的pod過多時,咱們節點暴露給client端的端口也會隨之增長,這樣咱們整個k8s羣集的危險係數就會增長,由於咱們在搭建羣集之處,官方明確指出,必須關閉firewalld防火牆及清空iptables規則,如今咱們又暴露了那麼多端口給client,安全係數可想而知。nginx

有沒有更安全又簡便的一種方法呢?答案是確定的,就是來利用Ingress這種資源對象來實現。git

1、lngress-nginx

一、lngress-nginx組成

  • ingress-nginx-controller:根據用戶編寫的ingress規則(建立的ingress的yaml文件),動態的去更改nginx服務的配置文件,而且reload重載使其生效(是自動化的,經過lua腳原本實現);
  • ingress資源對象:將Nginx的配置抽象成一個Ingress對象,每添加一個新的Service資源對象只需寫一個新的Ingress規則的yaml文件便可(或修改已存在的ingress規則的yaml文件)

二、lngress-nginx可解決什麼問題呢?

一、動態配置服務
  若是按照傳統方式, 當新增長一個服務時, 咱們可能須要在流量入口加一個反向代理指向咱們新的k8s服務. 而若是用了Ingress-nginx, 只須要配置好這個服務, 當服務啓動時, 會自動註冊到Ingress的中, 不須要而外的操做。
二、減小沒必要要的端口映射
  配置過k8s的都清楚, 第一步是要關閉防火牆的, 主要緣由是k8s的不少服務會以NodePort方式映射出去, 這樣就至關於給宿主機打了不少孔, 既不安全也不優雅. 而Ingress能夠避免這個問題, 除了Ingress自身服務可能須要映射出去, 其餘服務都不要用NodePort方式github

三、lngress-nginx工做原理

一、ingress controller經過和kubernetes api交互,動態的去感知集羣中ingress規則變化;
二、而後讀取它,按照自定義的規則,規則就是寫明瞭哪一個域名對應哪一個service,生成一段nginx配置;
三、再寫到nginx-ingress-controller的pod裏,這個Ingress controller的pod裏運行着一個Nginx服務,控制器會把生成的nginx配置寫入/etc/nginx.conf文件中;
四、而後reload一下使配置生效。以此達到域名分別配置和動態更新的問題;web

2、Ingress-nginx配置示例

注:docker

一、搭建registry私有庫(可忽略配置私有庫,手動導入所需鏡像到相應節點)

//運行registry私有庫
[root@docker-k8s01 ~]# docker run -itd --name registry -p 5000:5000 --restart always registry
//編輯配置文件,指定私有倉庫
[root@docker-k8s01 ~]# vim /usr/lib/systemd/system/docker.service 
ExecStart=/usr/bin/dockerd -H unix:// --insecure-registry 192.168.171.151:5000
[root@docker-k8s01 ~]# vim /usr/lib/systemd/system/docker.service 
//將配置好的文件發放到其餘兩臺節點
[root@docker-k8s01 ~]# scp /usr/lib/systemd/system/docker.service docker-k8s02:/usr/lib/systemd/system/ 
[root@docker-k8s01 ~]# scp /usr/lib/systemd/system/docker.service docker-k8s03:/usr/lib/systemd/system/
//在各個節點執行以下命令,重啓docker使配置生效
[root@docker-k8s01 ~]# systemctl daemon-reload 
[root@docker-k8s01 ~]# systemctl restart docker 
[root@docker-k8s01 ~]# docker push 192.168.171.151:5000/httpd:v1 
[root@docker-k8s01 ~]# docker push 192.168.171.151:5000/tomcat:v1

二、建立namespace(可忽略,使用默認的default名稱空間也能夠,但須要刪除下面全部yaml文件中關於自定義的名稱空間的配置字段)

//建立名稱空間test-ns
[root@docker-k8s01 ~]# kubectl create ns test-ns
//肯定建立成功
[root@docker-k8s01 ~]# kubectl get ns
NAME              STATUS   AGE
test-ns           Active   6s

三、建立Deployment、Service資源對象

//建立httpd服務及servicevim

---
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.171.151: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
//執行yaml文件
[root@docker-k8s01 test]# kubectl apply -f httpd-01.yaml

//建立tomcat服務及service後端

---
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.171.151: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

//執行yaml文件
[root@docker-k8s01 test]# kubectl apply -f tomcat-01.yaml

//肯定成功建立上述資源對象api

//肯定pod是正常運行狀態
[root@docker-k8s01 test]# kubectl get po -n test-ns 
NAME                     READY   STATUS    RESTARTS   AGE
web01-6849f7dd96-cglhg   1/1     Running   0          8m25s
web01-6849f7dd96-n57kx   1/1     Running   0          8m25s
web01-6849f7dd96-r6m26   1/1     Running   0          8m25s
web02-65b5798c6-8hzf9    1/1     Running   0          77s
web02-65b5798c6-r7wrl    1/1     Running   0          77s
web02-65b5798c6-zxx8h    1/1     Running   0          77s
//肯定SVC已經成功建立
[root@docker-k8s01 test]# kubectl get svc -n test-ns 
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
httpd-svc    ClusterIP   10.107.178.33    <none>        80/TCP     8m8s
tomcat-svc   ClusterIP   10.110.129.210   <none>        8080/TCP   94s
//訪問SVC的clusterIP+端口,肯定能夠訪問到後端Pod
[root@docker-k8s01 test]# curl -I 10.107.178.33:80     //訪問httpd
HTTP/1.1 200 OK
Date: Sat, 12 Sep 2020 03:35:27 GMT
Server: Apache/2.4.46 (Unix)       //版本號也可看到
Last-Modified: Sat, 12 Sep 2020 03:05:22 GMT
ETag: "11-5af15126f2080"
Accept-Ranges: bytes
Content-Length: 17
Content-Type: text/html

[root@docker-k8s01 test]# curl -I 10.105.121.123:8080    //訪問tomcat
HTTP/1.1 200 
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 12 Sep 2020 04:04:22 GMT
#若是在上述訪問測試中,沒有訪問到相應的pod,建議使用「kubectl describe svc」命令,
#查看相應的service中的Endpoints列中有沒有關聯後端pod。

四、建立Ingress-nginx資源對象

方法1:去gitlab搜索Ingress-nginx,點擊「deploy」,再點擊頁面下的跳轉連接,便可看到以下命令:

K8s——Ingress-nginx原理及配置

//這裏先不要直接複製命令到終端先將yaml下載下來
[root@docker-k8s01 test]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
//編輯其yaml文件
[root@docker-k8s01 test]# vim mandatory.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: registry.aliyuncs.com/google_containers/nginx-ingress-controller:0.29.0            //這裏我替換爲了阿里雲的鏡像
//上面是指定使用什麼鏡像,若須要更改鏡像名稱,改這裏便可
//對docker-k8s02打上相應的標籤,以便指定ingress-nginx運行在docker-k8s02上
[root@docker-k8s01 test]# kubectl label nodes docker-k8s02 Ingress=nginx
//執行以下命令查看標籤是否存在
[root@docker-k8s01 test]# kubectl get nodes docker-k8s02 --show-labels 
//切換到k8s02節點上執行以下命令,手動導入Ingress-nginx鏡像(自行上傳)
[root@docker-k8s02 ~]# docker load < nginx-ingress-controller-0.29.0.tar
//回到k8s01節點執行yaml文件
[root@docker-k8s01 test]# kubectl apply -f mandatory.yaml

關於上面yaml文件中寫入的「hostNetwork: true」具體解釋:若是使用此網絡參數,那麼pod中運行的應用程序能夠直接使用Node節點端口,這樣node節點主機所在的網絡的其餘主機,均可以經過訪問到此應用程序。

肯定Ingress-nginx的容器運行正常

[root@docker-k8s01 test]# kubectl get pod -n ingress-nginx -o wide
NAME                                        READY   STATUS             RESTARTS   AGE   IP                NODE           NOMINATED NODE   READINESS GATES
nginx-ingress-controller-845fbb7b4b-5gtkc   0/1     ImagePullBackOff   0          70s   192.168.171.150   docker-k8s02   <none>           <none>

五、定義Ingress規則(編寫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.test01.com
    http:
      paths:
      - path: /
        backend:
          serviceName: httpd-svc
          servicePort: 80
      - path: /tomcat
        backend:
          serviceName: tomcat-svc
          servicePort: 8080
//執行ingress規則的yaml文件
[root@docker-k8s01 test]# kubectl apply -f ingress.yaml 
//查看ingress規則資源對象
[root@docker-k8s01 test]# kubectl get ingresses -n test-ns 
NAME           HOSTS            ADDRESS   PORTS   AGE
test-ingress   www.test01.com             80      14s

其實,至此已經實現了咱們想要的功能,如今就能夠經過www.test01.com 來訪問到咱們後端httpd容器提供的服務,經過www.test01.com/tomcat 來訪問咱們後端tomcat提供的服務,固然,前提是自行配置DNS解析,或者直接修改client的hosts文件。訪問頁面以下(注意:必定要本身解決域名解析的問題,若不知道域名對應的是哪一個IP,請跳過這兩個圖,看下面的文字解釋):

//訪問httpd服務

K8s——Ingress-nginx原理及配置

//訪問Tomcat

K8s——Ingress-nginx原理及配置

在上面的訪問測試中,雖然訪問到了對應的服務,可是有一個弊端,就是在作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規則建立一個service

剛纔找到Ingress-nginx的yaml文件的頁面,而後下拉頁面,便可看到如下,能夠根據k8s集羣環境來選擇適合本身的yaml文件,假如本身是在Azure雲平臺搭建的K8s集羣,則選擇複製Azure下面的命令便可,我這裏是本身的測試環境,因此選擇Bare-metal下面的yaml文件:

K8s——Ingress-nginx原理及配置

建立這個Service有兩種方法,一是直接複製其web頁面的命令到master節點上執行,二是將其連接地址複製到終端使用wget下載下來再執行,我選擇第二種方法,由於我想看看裏面的內容:

[root@docker-k8s01 test]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml
//查看內容,不須要修改
[root@docker-k8s01 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

---
//執行yaml文件
[root@docker-k8s01 test]# kubectl apply -f service-nodeport.yaml 
service/ingress-nginx created
//查看運行的service
[root@docker-k8s01 test]# kubectl get svc -n ingress-nginx 
NAME            TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx   NodePort   10.104.185.116   <none>        80:30483/TCP,443:32362/TCP   8s
//能夠看到service分別將80和443端口映射到了節點的30483和32362端口(隨機映射的,也能夠修改yaml文件指定端口)

至此,域名解析對應的IP能夠是k8s羣集內的任意節點IP

K8s——Ingress-nginx原理及配置

K8s——Ingress-nginx原理及配置

基於虛擬主機的Ingress規則

若是如今是另外一種需求,我須要將www.test01.com 和www.test02.com 都對應上我後端的httpd容器提供的服務,那麼此時應該怎麼配置?

//修改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
//從新執行yaml文件
[root@docker-k8s01 test]# kubectl apply -f ingress.yaml

訪問www.test01.com:30483

K8s——Ingress-nginx原理及配置

訪問www.test02.com:30483

K8s——Ingress-nginx原理及配置

上述示例的pod是如何一步一步可使client訪問到的呢?

後端Pod---->service---->ingress規則---->寫入ingress-nginx-controller配置文件並自動重載使之生效---->對ingress-nginx建立service---->實現client不管經過哪一個k8節點的IP+端口均可以訪問到後端pod

3、配置HTTPS

在上面的操做中,實現了使用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@docker-k8s01 https]# openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"
//查看生成的兩個文件
[root@docker-k8s01 https]# ls
tls.crt  tls.key
//將生成的CA證書存儲到etcd
[root@docker-k8s01 https]# kubectl create secret tls tls-secret --key=tls.key --cert tls.crt
//建立deploy、service、ingress資源對象
//編寫yaml配置文件
kind: Deployment
apiVersion: extensions/v1beta1
metadata: 
  name: web03
spec: 
  replicas: 2
  template: 
    metadata: 
      labels: 
        app: httpd03
    spec: 
      containers: 
      - name: httpd3
        image: 192.168.171.151: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
  rules: 
  - host: www.test03.com
    http: 
      paths:
      - path: /
        backend: 
          serviceName: httpd-svc3
          servicePort: 80
//執行yaml文件
[root@docker-k8s01 https]# kubectl apply -f httpd02.yaml

//確認建立的資源對象正常運行

//查看svc
[root@docker-k8s01 https]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
httpd-svc3   ClusterIP   10.110.199.104   <none>        80/TCP    5m35s
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP   26d
//查看pod
[root@docker-k8s01 https]# kubectl get pod 
NAME                    READY   STATUS    RESTARTS   AGE
web03-c496b69f5-7288z   1/1     Running   0          6m23s
web03-c496b69f5-hnmrg   1/1     Running   0          6m23s
//查看ingress規則
[root@docker-k8s01 https]# kubectl describe ingresses 
Name:             test-ingress3
Namespace:        default
Address:          10.104.185.116
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.8:80,10.244.2.8:80)
//肯定關聯到對應的service及後端pod

//https訪問測試

訪問https://www.test03.com

K8s——Ingress-nginx原理及配置
K8s——Ingress-nginx原理及配置

相關文章
相關標籤/搜索