Ingress學習筆記

 Ingress是管理外部網咯訪問K8S集羣中Service的API對象(典型就是HTTP),它可以提供負載均衡、SSL終端以及基於名字的虛擬host。簡單點來說它就是外網訪問集羣應用的媒介,流程如下:

+----------+  Ingress   +---------+
| internet | ---------> | Service |
+----------+            +---------+

Ingress Controller 負責實現入口(即ingress的意思),通常使用負載均衡器,但它也可以配置邊緣路由器或其他前端以幫助處理流量。Ingress不會隨意暴露端口或協議,向Internet公開HTTP和HTTPS以外的服務通常使用Service.Type=NodePortService.Type=LoadBalancer類型的服務。

 和其他K8S資源一樣,Ingress必須要有一個 Ingress Controller,僅有 Ingress Resource 是無效的,Ingress也是無法正常工作的。Ingress Controller不會隨着集羣的啓動而自動啓動,且Ingress Controller有多種,目前K8S只支持和維護GCE(Google Kubernetes Engine)和nginx2種Controller。

【Ingress Resource】

 Ingress Resource 則和大部分的K8S資源差不多,需要apiVersionkindmetadata等字段,下面一個最小ingress resource的示例:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        backend:
          serviceName: test
          servicePort: 80

Ingress通常都是使用註解annotations來配置某些選項,具體可配置選項、支持哪些註解取決於Ingress Controller類型,上述是一個rewrite-target的註解,spec字段包含了負載均衡器或代理服務器的配置,最重要的是它包含了所有傳入請求的匹配規則。注:Ingress Resource僅支持HTTP通信的規則。

 Ingress rules是Ingress中比較重要的一部分,即上述示例中的rules字段的含義(它是一個可以包含多種rule對象的數組),每個Http的rule都應該包含如下的信息:

  • host:可選參數,用於限定rule適用的host,如指定爲foo.bar.com,那該rule只應用於這個host,上述示例中未指定,則該rule適用於通過指定IP地址的所有入站HTTP流量;
  • -paths:一系列的path對象組成的數組,上述示例中只有一個path爲/testpath,每個path都有一個backend對象(代表的是後端服務)與之對應;
  • backend:是一個serviceNameservicePort的組合對象,只有hostpath都匹配成功的情況下,LoadBalancer纔會將流量導向引用的服務。

 通常Ingress會有一個 Default Backend,當在上述我們自定義的Ingress Resource沒有匹配的rule對象時,流量就會被導向這個Default Backend。當然這個default backend需要我們自己去配,通常不會在Ingress Resource中配置default backend,而是應該在Ingress Ctontroller中進行指定。

【Ingress的類型】

1.Single Service Ingress

 即單服務Ingress,K8S當前是支持暴露單個服務的,可以通過在沒有規則的情況下指定默認後端來使用ingress來完成此操作,如:

# ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
spec:
  backend:
    serviceName: testsvc
    servicePort: 80

注:

安裝完ingress後,還要執行:

# 創建命名空間 ingress-nginx
kubectl create namespace ingress-nginx
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/rbac.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/with-rbac.yaml

kubectl apply -f ingress.yaml即可,通過kubectl get ingress test-ingress可以查看詳情,包括Ingress分配給這個default backend的IP。

2.Simple fanout

 fanout基於請求的HTTP URI將配置流量從單個IP路由到多個服務,Ingress 是允許將負載平衡器的數量降到最低,如:

foo.bar.com -> 178.91.123.132 -> / foo    service1:4200
                                 / bar    service2:8080

上述的過程需要一個如下的Ingress(同一個域名配置了2個path):

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: simple-fanout-example
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /foo
        backend:
          serviceName: service1
          servicePort: 4200
      - path: /bar
        backend:
          serviceName: service2
          servicePort: 8080

3.基於名字虛擬host

 和2類似,這種類型的Ingress支持將HTTP流量路由到在同一個IP上的多個host name,流程如下:

foo.bar.com --|                 |-> foo.bar.com s1:80
              | 178.91.123.132  |
bar.foo.com --|                 |-> bar.foo.com s2:80

此時的Ingress Resource應該爲:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
  - host: bar.foo.com
    http:
      paths:
      - backend:
          serviceName: service2
          servicePort: 80

此時Ingress會告訴負載均衡器將請求根據Host header路由到指定地方。如果不指定host,任意到Ingress Controller IP地址的Web流量都可以匹配,而不需要基於名稱的虛擬主機。

4.TLS

 可以指定包含一對TLS私鑰、證書的方式來保護Ingress,當前Ingress只支持TLS 443端口和TLS終端,TLS必須包含tls.crttls.key,如:

apiVersion: v1
kind: Secret
metadata:
  name: testsecret-tls
  namespace: default
data:
  tls.crt: base64 encoded cert
  tls.key: base64 encoded key
type: kubernetes.io/tls

在Ingress中引用這個密鑰將告訴Ingress Controller使用TLS保護Ingress從客戶端到負載平衡器的通道。此外,還需要確保創建的TLS機密來自包含CN的證書,如:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: tls-example-ingress
spec:
  tls:
  - hosts:
    - sslexample.foo.com
    secretName: testsecret-tls
  rules:
    - host: sslexample.foo.com
      http:
        paths:
        - path: /
          backend:
            serviceName: service1
            servicePort: 80

注:TLS對於nginx和GCE Controller稍有不同

【Update Ingress】

 更新一個已有的Ingress,可以使用如下的流程:

# 查看已有配置
kubectl describe ingress test
# 修改配置
kubectl edit ingress test

保存後將會更新資源,它將告訴Ingress Controller重新配置負載均衡器,當然也可以使用kubectl replace -f xx.yaml來重新載入配置即可。

注:
ingress的文檔比較好用,貼一下Annotations,這裏有個坑,如果使用rancher進行配置,那所有字段儘量不要再用''""包括,rancher的解析做的不是很好,他會將引號作爲內容的一部分設置爲對應的註解值,很坑!

附:常用ingress配置

 常用的ingress配置模板如下:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: {{ .Values.deploy.app }}
  annotations:
    {{- range $key, $value := .Values.service.ingress.annotations }}
      {{ $key }}: {{ $value | quote }}
    {{- end }}
  labels:
    service-cluster: {{ .Values.deploy.serviceCluster }}
    app: {{ .Values.deploy.app }}
    project-type: java
spec:
  rules:
  # 可以有多個
  - host: {{ .hosts }}
    http:
      paths:
        # 可以有多個(可以正則)
        - path: {{ .path }}
          backend:
            serviceName: {{ .serviceName }}
            servicePort: {{ .servicePort }}
  # 可以有多個
  tls:
    # 可以有多個
    - hosts:
      {{- range .hosts }}
        - {{ . }}
      {{- end }}
      secretName: {{ .secretName }}

1. 跨域(單個或多個)

 在配置跨域時,如果使用rancher可視化面板配置nginx.ingress.kubernetes.io/enable-corsnginx.ingress.kubernetes.io/cors-allow-origin註解時千萬不能加引號,不然找半天找不到錯誤在哪。但寫Chart的yaml配置文件時,該怎麼寫就怎麼寫,string類型的還是需要加上引號,跨域的示例配置爲:

# 方式1:直接使用註解(但此時只能配置一個域跨域)
annotations: 
  # 使用rewrite配置contextPath
  nginx.ingress.kubernetes.io/rewrite-target: /account/$1
  # 配置跨域
  nginx.ingress.kubernetes.io/enable-cors: "true"
  nginx.ingress.kubernetes.io/cors-allow-origin: "http://k8s-www.hhu.com"
  nginx.ingress.kubernetes.io/cors-allow-methods: "GET, PUT, POST, DELETE, PATCH, OPTIONS"
  nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization"

# 方式2:使用自定代碼段註解,自定寫跨域(這種方式可以配置多個域名跨域)
nginx.ingress.kubernetes.io/server-snippet: |
  # 配置跨域(多個,單個可用註解)
  set $corsHost '';
  set $httpHost '';
  set $httpHeaders 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,Content-Type,X-Forwarded-For,xfilecategory,xfilename,xfilesize';
  if ( $http_origin ~ http://k8s-www.hhu.com){
          set $corsHost $http_origin;
          set $httpHost 'k8s-www.hhu.com';
  }
  if ( $http_origin ~ http://k8s-m.hhu.com){
          set $corsHost $http_origin;
          set $httpHost 'k8s-m.hhu.com';
  }

  add_header Access-Control-Allow-Origin $corsHost always;
  add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS, PATCH';
  add_header Access-Control-Allow-Headers $httpHeaders;
  add_header Access-Control-Allow-Credentials 'true';

比較推崇的是上述第二種的配置方式,注意,在添加Access-Control-Allow-Origin的header時必須接上always,否則不生效

2. ingress中的rewrite

 ingress中配置rewrite有一個自己的註解nginx.ingress.kubernetes.io/rewrite-target,可以自己配置爲指定值,我工作中更習慣將其配置爲各個服務自己的contextPath,比如:

metadata:
    annotations: 
      ...
      nginx.ingress.kubernetes.io/rewrite-target: /account/$1

spec:
  rules:
  # 可以有多個
  - host: www.hhu.com
    http:
      paths:
        # 可以有多個(可以正則)
        - path: /($/.*)
          backend:
            serviceName: server-account
            servicePort: 80

上述path使用的是一個正則/($/.*),可以匹配/或者任意以/開頭任意多個字符,我比較推薦使用這個正則/($/.*),而不推薦使用/(.*),因爲.*要求你至少匹配1個字符,即匹配不到/的path。可以配置www.hhu.com域名下任意路徑,在其前面加上/account(比如訪問的是www.hhu.com/info會被路由到www.hhu.com/account/info)。

 ingress中的rewrite同樣可以使用nginx.ingress.kubernetes.io/server-snippet註解來更加多樣化的配置,示例如下:

  nginx.ingress.kubernetes.io/server-snippet: |
    # analyzer
    rewrite /behavior/data($|.*) /analyzer/behavior/data$1 break;
    rewrite /hhu/event($|.*) /analyzer/hhu/event$1 break;

    # mine 或者寫成 rewrite /private(/|$)(.*) /mine/private$1$2 break;
    rewrite /private($|.*) /mine/private$1 break; 
    rewrite /mine($|.*) /mine$1 break; 

    # shop
    rewrite /shops($|.*) /shops$1 break; 

    # task
    rewrite /internal($|.*) /task/internal$1 break; 

    # portal
    rewrite /($|.*) /portal/$1 break; 

上述注意,break千萬不能丟,像類似於/($|.*)全匹配的正則一定要寫到最下面,否則它會最先匹配(原因未知,不是說好的路徑最長匹配嗎……)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章