Kubernetes:Ingress剖析

一. 簡介

Ingress 服務是為全局的、代理不同后端 Service 而設置的負載均衡服務。所以,所謂 Ingress,就是 Service 的 “Service”。

1.2 場景

Kubernetes 的 Service 只有四層代理,暫時只支持這樣的格式:IP:Port訪問。
Ingress api 支持實現七層代理,可以用來綁定外部域名。

1.2 組成

Ingress 由兩部分組成:

  • Ingress Controller
    這是一個標準,可以有很多實現,例如下文的 ingress-nginx,它以pod形式運行的。
  • IngressRule
    以 yaml 形式為載體的一組聲明式的策略,ingress-controller 會動態地按照策略生成配置文件(如:nginx.conf)

關于本文的項目的代碼,都放于鏈接:GitHub資源

二. IngressRule

IngressRuleKey,就叫做:host。它必須是一個標準的域名格式(Fully Qualified Domain Name)的字符串,而不能是 IP 地址。

2.1 案例

“demo-ingress.yaml” 文件內容如下:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: demo-ingress
  annotations:
    # use the shared ingress-nginx
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:
  - hosts:
    - ingress.wyatt.plus
    secretName: demo-secret
  rules:
  - host: ingress.wyatt.plus
    http:
      paths:
      - path: /svc1
        backend:
          serviceName: svc-1
          servicePort: 80
      - path: /svc2
        backend:
          serviceName: svc-2
          servicePort: 80

2.2 分析

上面的IngressRule 規則的定義,則依賴于 path 字段。這里的每一個 path 都對應一個后端 Service。所以在我們的例子里,定義了兩個 path,它們分別對應 svc-1svc-2 這兩個 DeploymentService

三. Ingress Controller

Ingress Controller 會根據定義的 Ingress 對象,提供對應的代理能力。目前,業界常用的各種反向代理項目,比如 Nginx、HAProxy、Envoy、Traefik 等,都已經為 Kubernetes 專門維護了對應的 Ingress Controller。

當一個新的 Ingress 對象由用戶創建后,nginx-ingress-controller 就會根據 Ingress 對象里定義的內容,生成一份對應的 Nginx 配置文件(/etc/nginx/nginx.conf),并使用這個配置文件啟動一個Nginx 服務。而一旦 Ingress 對象被更新,nginx-ingress-controller 就會更新這個配置文件。

3.1 案例(mac平臺)

對于 “deploy.yaml” 文件,我截取核心內容如下:

# Source: ingress-nginx/templates/controller-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    helm.sh/chart: ingress-nginx-3.23.0
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 0.44.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/instance: ingress-nginx
      app.kubernetes.io/component: controller
  revisionHistoryLimit: 10
  minReadySeconds: 0
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/component: controller
    spec:
      dnsPolicy: ClusterFirst
      containers:
        - name: controller
          image: k8s.gcr.io/ingress-nginx/controller:v0.44.0@sha256:3dd0fac48073beaca2d67a78c746c7593f9c575168a17139a9955a82c63c4b9a
          imagePullPolicy: IfNotPresent
          lifecycle:
            preStop:
              exec:
                command:
                  - /wait-shutdown
          args:
            - /nginx-ingress-controller
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
            - --election-id=ingress-controller-leader
            - --ingress-class=nginx
            - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
            - --validating-webhook=:8443
            - --validating-webhook-certificate=/usr/local/certificates/cert
            - --validating-webhook-key=/usr/local/certificates/key
          securityContext:
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            runAsUser: 101
            allowPrivilegeEscalation: true
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: LD_PRELOAD
              value: /usr/local/lib/libmimalloc.so
          livenessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 5
          readinessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 3
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
            - name: https
              containerPort: 443
              protocol: TCP
            - name: webhook
              containerPort: 8443
              protocol: TCP
          volumeMounts:
            - name: webhook-cert
              mountPath: /usr/local/certificates/
              readOnly: true
          resources:
            requests:
              cpu: 100m
              memory: 90Mi
      nodeSelector:
        kubernetes.io/os: linux
      serviceAccountName: ingress-nginx
      terminationGracePeriodSeconds: 300
      volumes:
        - name: webhook-cert
          secret:
            secretName: ingress-nginx-admission

3.2 分析

以看到,在上述 YAML 文件中,我們定義了一個使用 nginx-ingress-controller 鏡像的 Pod。需要注意的是,這個 Pod 的啟動命令需要使用該 Pod 所在的 Namespace 作為參數。而這個信息,當然是通過 Downward API 拿到的,即:Pod 的 env 字段里的定義(env.valueFrom.fieldRef.fieldPath)

四. Demo

我們做一個如下的場景站點:https://ingress.wyatt.plus ,這是一個微服務系統。

由于每個平臺配置文件有區別,所以我將按照mac平臺的方式按照配置文件。各平臺的nginx controller配置

4.1 按照 Ingress Controller

我使用了Docker for mac相關的配置,執行如下指令:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.44.0/deploy/static/provider/cloud/deploy.yaml

4.2 配置Service與Deployment

4.2.1 demo-svc-1

該文件包了 Deployment 和 Service 這倆個配置,內容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: svc-1
spec:
  replicas: 2
  selector:
    matchLabels:
      app: svc-1
  template:
    metadata:
      labels:
        app: svc-1
    spec:
      containers:
      - name: svc-1
        image: nginxdemos/hello:plain-text
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: svc-1
spec:
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  selector:
    app: svc-1

該配置通過 label selector 來進行匹配 app: svc-1 的 Pod。

4.2.2 demo-svc-2

該文件包了 Deployment 和 Service 這倆個配置,內容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: svc-2
spec:
  replicas: 3
  selector:
    matchLabels:
      app: svc-2 
  template:
    metadata:
      labels:
        app: svc-2
    spec:
      containers:
      - name: svc-2
        image: nginxdemos/hello:plain-text
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: svc-2
spec:
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  selector:
    app: svc-2

該配置通過 label selector 來進行匹配 app: svc-2 的 Pod。

4.3 創建Secret

創建 Secret 有2種方式,如下:

4.3.1 命令行創建

kubectl create secret tls demo-secret --key tls.key --cert tls.crt

4.3.2 以 YAML方式創建

apiVersion: v1
kind: Secret
metadata:
  name: demo-secret
type: Opaque
data:
  tls.crt: **************************
  tls.key: **************************

4.4 配置Ingress

注意 Ingress 的版本,最新的 API 變動較多,最新的為 networking.k8s.io/v1
“demo-ingress.yaml” 配置如下:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: demo-ingress
  annotations:
    # use the shared ingress-nginx
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:
  - hosts:
    - ingress.wyatt.plus
    secretName: demo-secret
  rules:
  - host: ingress.wyatt.plus
    http:
      paths:
      - path: /svc1
        backend:
          serviceName: svc-1
          servicePort: 80
      - path: /svc2
        backend:
          serviceName: svc-2
          servicePort: 80

當我們執行 kubectl apply 創建成功后,我們可以再通過如下指令查看 Ingress 情況。

kubectl get ingress
# result
NAME           CLASS    HOSTS                ADDRESS     PORTS     AGE
demo-ingress   <none>   ingress.wyatt.plus   localhost   80, 443   110m

4.5 檢查

關于當前的 “demo-ingress” ,我們可以通過更詳細的指令查看里面內容:

kubectl describe ingress demo-ingress
# result
Name:             demo-ingress
Namespace:        default
Address:          localhost
Default backend:  default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
TLS:
  demo-secret terminates ingress.wyatt.plus
Rules:
  Host                Path  Backends
  ----                ----  --------
  ingress.wyatt.plus
                      /svc1   svc-1:80 (10.1.3.124:80,10.1.3.126:80)
                      /svc2   svc-2:80 (10.1.3.123:80,10.1.3.125:80,10.1.3.127:80)
Annotations:          kubernetes.io/ingress.class: nginx

可以看到,這個Ingress 對象最核心的部分,正是Rules 字段。其中,我們定義的 Host 是 ingress.wyatt.plus,它有兩條轉發規則(Path),分別轉發給 /svc1/svc2

實際執行結果如下:


demo-ingress

4.6 驗證

我們可以通過訪問這個 Ingress 的地址和端口,訪問到我們前面部署的應用,
我們使用瀏覽器可以訪問 https://ingress.wyatt.plus/svc1https://ingress.wyatt.plus/svc2 這倆個 url 。
我們將分別看到如下的倆個頁面:

  • svc1
Server address: 10.1.3.124:80
Server name: svc-1-67c6fdbf4d-bwwlp
Date: 31/Mar/2021:22:53:06 +0000
URI: /svc1
Request ID: d37a5c0b9aaae3c71865ff7fcb3c615b

我們可以看到,訪問這個 URL 得到的返回信息是:Server name: svc-1-67c6fdbf4d-bwwlp 。這正是 svc-1 這個 Deployment 的名字。

  • svc2
Server address: 10.1.3.127:80
Server name: svc-2-77f44d76b-bx77x
Date: 31/Mar/2021:22:54:13 +0000
URI: /svc2
Request ID: 17645ccb197fe3fb6daf6200aba289bf

我們可以看到,訪問這個 URL 得到的返回信息是:Server name: svc-2-77f44d76b-bx77x 。這正是 svc-2 這個 Deployment 的名字。

可以確認 Nginx Ingress Controller 創建的 Nginx 負載均衡器,已經成功地將請求轉發給了對應的后端 Service。

我們也可以在 Kubernetes Dashboard 里面查看 Ingress 對于的路徑匹配規則,如下圖:


Kubernetes Dashboard

五. Ingress部署方式

5.1 Deployment + LoadBalancer 模式的 Service

5.1.1 細節

如果要把 Ingress 部署在公有云,那可以選擇這種方式。用 Deployment 部署 ingress-controller ,創建一個 type 為 LoadBalancer 的 Service 關聯這組 pod 。大部分公有云,都會為 LoadBalancer 的 Service 自動創建一個負載均衡器,通常還綁定了公網地址。只要把域名解析指向該地址,就實現了集群服務的對外暴露。
缺點:

  • 需要額外購買公有云的服務,與平臺掛鉤。

5.1.2 架構圖

Deployment + LoadBalancer

5.2 Deployment + NodePort 模式的 Service

5.2.1 細節

同樣用 Deployment 模式部署 ingress-controller ,并創建對應的服務,但是 type 為 NodePort。這樣,ingress 就會暴露在集群節點ip的特定端口上。由于 NodePort 暴露的端口是隨機端口,一般會在前面再搭建一套負載均衡器來轉發請求。該方式一般用于宿主機是相對固定的環境ip地址不變的場景。

缺點:

  • NodePort 方式暴露 ingress 雖然簡單方便,但是 NodePort 多了一層 NAT ,在請求量級很大時可能對性能會有一定影響。
  • 請求節點會是類似 https://www.wyatt.plus:30076, 其中 30076kubectl get svc -n ingress-nginx 的 svc 暴露出來的 NodePort 端口。

5.2.2 架構

Deployment + NodePort

5.3 DaemonSet + HostNetwork + nodeSelector

5.3.1 細節

DaemonSet 結合 nodeSelector 來部署 ingress-controller 到特定的node上,然后使用 HostNetwork 直接把該 pod 與宿主機 node 的網絡打通,直接使用宿主機的 80/433 端口就能訪問服務。這時,ingress-controller 所在的node機器就很類似傳統架構的邊緣節點,比如機房入口的nginx服務器。
優點:

  • 該方式整個請求鏈路最簡單,性能相對 NodePort 模式更好。
    缺點:
  • 由于直接利用宿主機節點的網絡和端口,一個 node 只能部署一個 ingress-controller pod。

5.2.3 架構

DaemonSet + HostNetwork + nodeSelector

六. 總結

Ingress 只能工作在七層,而 Service 只能工作在四層。所以當我們想要在 Kubernetes 里為應用進行 TLS 配置等 HTTP 相關的操作時,都必須通過Ingress 來進行。
有了 Ingress 這個抽象,我們就可以根據自己的需求來自由選擇 Ingress Controller。我們只需要做很少的編程工作,就可以實現一個自己的Ingress Controller

Ingress 帶來的靈活度和自由度,對于使用容器時代來說,其實是非常有意義的。

歡迎收藏個人博客: Wyatt's Blog ,非常感謝~

Reference

https://kubernetes.github.io/ingress-nginx/
https://github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md#docker-for-mac
https://time.geekbang.org/column/article/69214?utm_campaign=guanwang&utm_source=baidu-ad&utm_medium=ppzq-pc&utm_content=title&utm_term=baidu-ad-ppzq-title
https://www.cnblogs.com/baoshu/p/13255909.html
https://github.com/kubernetes/ingress-nginx/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370