Kubernetes Ingress管理


概述

Kubernetes暴露服務的方式目前有三種:

  • LoadBlancer Service
  • NodePort Service
  • Ingress

在《kubernetes資源之service》中我們提到,通過NodePort Service和LoadBlancer Service可以將集群內服務對外暴露。但事實上,各自又存在各自的問題:

  • LoadBalancer Service 通常用於與公有雲廠商對接,當然也可以自行實現其接口以完成與企業自建的負載均衡器對接。事實上LoadBanlacer Service的工作機制就是調用外部的負載均衡器以實現服務暴露,這依托於外部的負載均衡器。

  • NodePort Service 它的實現機制其實就是在每個node節點上都開啟一個端口,並通過iptables的dnat方式將這個宿主機端口映射至集群內部的service ip上。nodeport的問題是,當集群當中的服務越來越多的時候,在每個node上開啟的端口也越來越多,最終我們要維護大量的端口映射關系,這使得業務管理工作變得非常復雜。

為此,從kubernetes 1.2版本開始,官方引入了ingress。

Ingress架構及原理

ingress

針對上述nodeport暴露服務的問題,其實可以有一個解決辦法,即使用一個nginx/haproxy這樣的負載均衡器,只監聽在一個端口上,比如80或443,然后按照域名往后端轉發。將這樣的負載均衡器以pod的方式運行在集群中,並通過hostNetwork或者nodeport的方式只暴露負載均衡器監聽的端口即可。

這里其實有一個問題,就是nginx/haproxy怎么知道什么域名應該轉發至什么后端? 后端應用的pod如果發生變化,nginx/haproxy又應該如何感知到,並同步更新自己的配置文件然后重載配置?

而這就是ingress的作用。官方的ingress由三部分組成:

  • Ingress類型的資源:其實就是個規則文件,其定義流量的轉發規則
  • Ingress Controller: 通過與kubernetes api交互,動態的去感知集群中ingress規則變化,然后讀取它,再按照自己的模板生成一段nginx配置,再寫到nginx pod里,最后reload一下nginx
  • Nginx:真正負責流量轉發的負載均衡器

事實上,kubernetes已經將ingress Controller和nginx結合到一起,統稱之ingress controller,所以在實際部署中,只需要部署ingress controller即可。

Nginx Ingress配置

ingress-nginx代碼托管地址:https://github.com/kubernetes/ingress-nginx

ingress-nginx官方文檔地址:https://kubernetes.github.io/ingress-nginx/

1. Ingress Controller部署

官方的Ingress Controller部署文件: https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.26.2/deploy/static/mandatory.yaml

下面是我修改過的部署文件:

...

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/part-of: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
      annotations:
        prometheus.io/port: "10254"
        prometheus.io/scrape: "true"
    spec:
      # wait up to five minutes for the drain of connections
      terminationGracePeriodSeconds: 300
      serviceAccountName: nginx-ingress-serviceaccount
      hostNetwork: true
      nodeSelector:
        kubernetes.io/os: linux
        ingress-nginx: enable
      tolerations:
        - key: ingress-nginx
          operator: "Exists"
          effect: NoExecute
      containers:
        - name: nginx-ingress-controller
          image: quay.azk8s.cn/kubernetes-ingress-controller/nginx-ingress-controller:0.26.2
          args:
            - /nginx-ingress-controller
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx
            - --annotations-prefix=nginx.ingress.kubernetes.io
          securityContext:
            allowPrivilegeEscalation: true
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            # www-data -> 101
            runAsUser: 101
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
            - name: https
              containerPort: 443
              protocol: TCP
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          lifecycle:
            preStop:
              exec:
                command:
                  - /wait-shutdown
...

修改說明:

  1. 將nginx監聽的端口直接通過host模式監聽至宿主機
  2. 將ingress controller運行在帶有ingress-nginx=enable標簽的節點上(所以需要給集群中要運行該ingress controller的節點打上對應的標簽)
  3. ingress controller能夠容器帶有ingress-nginx污點的節點(如果要讓ingress controller專機專用,可為相應節點打上ingress-nginx=:NoExecute的污點)

部署:

kubectl create -f mandatory.yaml

為ingress controller創建一個service:

# cat ingress-nginx.svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
spec:
  type: ClusterIP
  ports:
  - port: 80
    name: http
    targetPort: 80
  - port: 443
    name: https
    targetPort: 443
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

創建service:

kubectl apply -f ingress-nginx.svc.yaml

創建service之后,集群內部可直接通過這個service的ip訪問到相應的應用(需要加header頭)

還需要說明的是,ingress-nginx部署完成以后,默認提供兩個location,一個是/healthz,用於健康檢查,返回狀態碼200,另外一個是/,默認返回404

2. 配置Ingress資源以實現流量接入

前面我們說到Ingress其實就是個規則,指定哪個域名轉發到哪個service,所以說首先得有個service。不過service的具體配置這里不作說明。我們就以kubernetes-dashboard和kibana為例:

kubectl get svc kubernetes-dashboard --namespace=kube-system
NAME                   CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE       
kubernetes-dashboard   10.254.213.109   <none>        80/TCP    13d       
kibana                 10.254.213.110   <none>        5601/TCP    13d       

創建一個dashboard-kibana-ingress.yml如下:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: dashboard-kibana-ingress
  namespace: kube-system
spec:
  rules:
  - host: dashboard.breezey.top
    http:
      paths:
      - path: /
        backend:
          serviceName: kubernetes-dashboard
          servicePort: 80
  - host: kibana.breezey.top
    http:
      paths:
      - backend:
          serviceName: kibana
          servicePort: 5601

部署:

kubectl create -f dashboard-kibana-ingress.yml

3. 配置Ingress TLS

默認情況下,ingress只提供了http服務,而沒有https服務,要部署一個https服務,首先得有https證書。證書的生成,這里不做說明。

創建secret

假定,我們現在已經有了一個ca.crt的證書文件和一個server.key的密鑰文件。我們需要創建一個secret。在創建secret之前,先要把證書及密鑰內容通過base64編碼。如下:

cat ca.crt | base64 -w 0
cat server.key|base64 -w 0

創建ingress-secret.yml文件,內容如下:

apiVersion: v1
kind: Secret
metadata:
  name: ingress-secret
  namespace: default
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlIWlRDQ0JrMmdBd0lCQWdJTUxQbnRoUStHZlJJOTNHd0dNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1HQXhDekFKDQpCZ05WQkFZVEFrSkZNUmt3RndZRFZRUUtFeEJIYkc5aVlXeFRhV2R1SUc1MkxYTmhNVFl3TkFZRFZRUURFeTFIDQpiRzlpWVd4VGFXZHVJRVJ2YldGcGJpQldZV3hwWkdGMGFXOXVJRU5CSUMwZ1UwaEJNalUySUMwZ1J6SXdIaGNODQpNVGN3TlRJMU1EWTBNRFEyV2hjTk1UZ3hNVEk0TWpNMU9UVTVXakE0TVNFd0h3WURWUVFMRXhoRWIyMWhhVzRnDQpRMjl1ZEhKdmJDQldZV3hwWkdGMFpXUXhFekFSQmdOVkJBTU1DaW91WkhveE1TNWpiMjB3Z2dFaU1BMEdDU3FHDQpTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDaTA5bjg0WGdhWGo0YStvaERyQXJONWVLeEpYbXV0QmJNDQpqNVFJdEZDa2l0dUg3OWxtNmtMcThSL2E0ZHdEc1h6czZXVWNRRHBjbUlqNzdOYlBQYzJrZVZlcDMxeVZLSUpKDQpkYWhFd2V5NlVFBQeWwwS2xOeFdMODhETGtMZUFvby8rNVNBaUIzUktsUUswMXREWnIrem4rYkxZVUQ4YzU4DQ
  tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBb3RQWi9PRjRHbDQrR3ZxSVE2d0t6ZVhpc1NWNXJyUVd6SStVQ0xSUXBJcmJoKy9aClp1cEM2dkVmMnVIY0E3Rjg3T2xsSEVBNlhKaUkrK3pXenozTnBIbFhxZDljbFNpQ1NYV29STUhzdWtKL1RFbUcKRUhsOTI3M1BFZU1QclhqSUpEeC85K1ZQRUlFRnlHc3hYamFaR2FtZnJYNmJvMVVFaExlMlEySVpWMDh1UU1EMQpUTVArb0VyZHY1MkUzZlAyxxxxxNBSjNUNWtrUm5IaE5TWDFIankySnFBcHNDYW5pKzI4MmV2NGlYYkwwCks1NVA4N3BqdGw4WGtWTGZDbXJYTSt6dEs4aGNkQ3ZCOHU0NkpNRWQ2R1JjeXpBWHJ6b1dYM3RxUGVDdGxrazgKcEVMRXVFSmJpV2hYRjZEVUtlK1NpeHIyMTJHdm5JcncreTBxendJREFRQUJBb0lCQVFDTFhTSWQ1R2xrd0RjTgo1bE1OQU1xNmtrRmw5N3BmZ25wbEdack5uRy9OZGFBU2lJS2VLSEdnSDBOeGw1RTFoQXQxeHdvb2xQeWUxbHVnCnJJVHJHbTNSa1o0cm9pYmU

執行創建:

kubect create -f ingress-secret.yml

也可以通過命令行的方式直接創建一個secret:

kubectl create secret tls ingress-secret --key server.key --cert ca.crt

修改ingress,開啟tls

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: dashboard-kibana-ingress
  namespace: kube-system
spec:
  tls:
  - hosts:
    - dashboard.breezey.top
    - kibana.breezey.top
    secretName: ingress-secret
  rules:
  - host: dashboard.breezey.top
    http:
      paths:
      - path: /
        backend:
          serviceName: kubernetes-dashboard
          servicePort: 80
  - host: kibana.breezey.top
    http:
      paths:
      - path: /
        backend:
          serviceName: kibana
          servicePort: 5601

注意:一個 Ingress 只能使用一個 secret(secretName 段只能有一個),也就是說只能用一個證書,更直白的說就是如果你在一個 Ingress 中配置了多個域名,那么使用 TLS 的話必須保證證書支持該 Ingress 下所有域名;並且這個 secretName 一定要放在上面域名列表最后位置,否則會報錯 did not find expected key 無法創建;同時上面的 hosts 段下域名必須跟下面的 rules 中完全匹配

更需要注意一點:Kubernetes Ingress默認情況下,當你不配置證書時,會默認給你一個 TLS 證書的,也就是說你 Ingress 中配置錯了,比如寫了2個 secretName、或者 hosts 段中缺了某個域名,那么對於寫了多個 secretName 的情況,所有域名全會走默認證書;對於 hosts 缺了某個域名的情況,缺失的域名將會走默認證書,部署時一定要驗證一下證書,不能 “有了就行”;更新 Ingress 證書可能需要等一段時間才會生效

一旦部署了https,默認請求的http會自動跳轉到https,所以在同時需要https和http並存的應用場景,也需要注意

最后重新部署下ingress即可:

kubectl delete -f dashboard-kibana-ingress.yml
kubectl create -f dashboard-kibana-ingress.yml

4. 通過ingress暴露tcp服務

通過ingress暴露tcp服務,我們需要先定義一個nginx-tcp-ingress-configmap.yaml的configmap,示例如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: tcp-configmap-example
data:
  9000: "default/redis:6379"

以上表示暴露 default namespace 下服務名為 redis,端口為 6379 的服務到 nginx-ingress-lb 所在節點的 9000 端口。

5. 通過configmap修改nginx controller的全局變量

通過上面啟動nginx controller的yaml文件,其實我們可以看出來,在啟動controller的時候,向啟動命令傳遞了一大堆參數,包括--default-backend-service以及--apiserver-host等。更多的參數,可以直接參考這里

我們知道,nginx controller本質上就是一個nginx代理,這個代理使用了一大堆nginx默認參數啟動。而在某些特定場景下,這些我們需要定制這些參數以更適用於我們的需求。在controller啟動的時候,提供了一個--configmap的參數,我們可以將需要定制的參數保存到一個configmap中,並在controller啟動的時候,來讀取這個configmap,獲取其值,應用到controller中。具體哪些值可以通過configmap來傳遞,可以直接參考這里

下面是一個簡單的示例:

定義一個名為nginx-controller-configmap的yaml內容如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-ingress-configmap
  namespace: default
data:
  proxy-body-size: 1024m

這個configmap定義了一個proxy-body-size的大小為1024m,即nginx中client_max_body_size的參數為1024m。

然后我們通過kubectl exec -it <podname> /bin/sh的方式登錄到新的pod,查看/etc/nginx/nginx.conf文件,以確認相應配置是否生效


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM