五分鐘K8S實戰-使用Ingress

2023.09.15

五分鐘K8S實戰-使用Ingress

Ingress 通常是充當網關的作用,後續我們在使用Istio 時,也可以使用Istio 所提供的控制器來替換掉Ingress-nginx,可以更方便的管理內外網流量。

背景

技術、生活、觀點、原創。原創公眾號; 主要關注Go、JVM、並發、分佈式、網絡等相關技術。

前兩章中我們將應用部署到了k8s 中,同時不同的服務之間也可以通過 service 進行調用,現在還有一個步驟就是將我們的應用暴露到公網,並提供域名的訪問。

這一步類似於我們以前配置Nginx 和綁定域名,提供這個能力的服務在k8s 中成為Ingress。

通過這個描述其實也能看出Ingress 是偏運維的工作,但也不妨礙我們作為研發去了解這部分的內容;了解整個系統是如何運轉的也是研發應該掌握的技能。

安裝Ingress 控制器

在正式使用Ingress 之前需要給k8s 安裝一個Ingress 控制器,我們這里安裝官方提供的Ingress-nginx 控制器。

當然還有社區或者企業提供的各種控制器:

圖片圖片

有兩種安裝方式: helm 或者是直接apply 一個資源文件。

關於 helm 我們會在後面的章節單獨講解。

這裡就直接使用資源文件安裝即可,我已經上傳到GitHub 可以在這裡訪問:https://github.com/crossoverJie/k8s-combat/blob/main/deployment/ingress-nginx.yaml

其實這個文件也是直接從官方提供的複製過來的,也可以直接使用這個路徑進行安裝:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml
  • 1.

yaml 文件的內容是一樣的。

不過要注意安裝之後可能容器狀態一直處於Pending 狀態,查看容器的事件時會發現鏡像拉取失敗。

k describe pod ingress-nginx-controller-7cdfb9988c-lbcst -n ingress-nginx
  • 1.

describe 是一個用於查看k8s 對象詳細信息的命令。

在剛才那份yaml 文件中可以看到有幾個鏡像需要拉取,我們可以先在本地手動拉取鏡像:

圖片圖片

docker pull registry.k8s.io/ingress-nginx/controller:v1.8.2
  • 1.

如果依然無法拉取,可以嘗試配置幾個國內鏡像源鏡像拉取:

圖片圖片

我這裡使用的docker-desktop 自帶的k8s,推薦讀者朋友也使用這個工具。

創建Ingress

使用剛才的yaml 安裝成功之後會在 ingress-nginx 命名空間下創建一個Pod,通過get 命令查看狀態為Running 即為安裝成功。

$ k get pod -n ingress-nginx
NAME                            READY   STATUS    RESTARTS      AGE
ingress-nginx-controller-7cdf   1/1     Running   2 (35h ago)   3d
  • 1.
  • 2.
  • 3.

Namespace 也是k8s 內置的一個對象,可以簡單理解為對資源進行分組管理,我們通常可以使用它來區分各個不同的環境,比如dev/test/prod 等,不同命名空間下的資源不會互相干擾,且相互獨立。

之後便可以創建Ingress 資源了:

apiVersion: networking.k8s.io/v1  
kind: Ingress  
metadata:  
  name: k8s-combat-ingress  
spec:  
  ingressClassName: nginx  
  rules:  
    - host: www.service1.io  
      http:  
        paths:  
          - backend:  
              service:  
                name: k8s-combat-service  
                port:  
                  number: 8081  
            path: /  
            pathType: Prefix  
    - host: www.service2.io  
      http:  
        paths:  
          - backend:  
              service:  
                name: k8s-combat-service-2  
                port:  
                  number: 8081  
            path: /  
            pathType: Prefix
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

看這個內容也很容易理解,創建了一個 Ingress 的對象,其中的重點就是這裡的規則是如何定義的。

在k8s 中今後還會接觸到各種不同的Kind。

這裡的 ingressClassName: nginx 也是在剛開始安裝的控制器裡定義的名字,由這個資源定義。

apiVersion: networking.k8s.io/v1  
kind: IngressClass  
metadata:  
  labels:  
    app.kubernetes.io/component: controller  
    app.kubernetes.io/instance: ingress-nginx  
    app.kubernetes.io/name: ingress-nginx  
    app.kubernetes.io/part-of: ingress-nginx  
    app.kubernetes.io/version: 1.8.2  
  name: nginx
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

咱們這個規則很簡單,就是將兩個不同的域名路由到兩個不同的service。

這里為了方便測試又創建了一個 k8s-combat-service-2 的service,和 k8s-combat-service 是一樣的,只是改了個名字而已。

測試

也是為了方便測試,我在應用鏡像中新增了一個接口,用於返回當前Pod 的hostname。

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {  
   name, _ := os.Hostname()  
   fmt.Fprint(w, name)  
})
  • 1.
  • 2.
  • 3.
  • 4.

由於我實際並沒有 www.service1.io/www.service2.io 這兩個域名,所以只能在本地配置host 進行模擬。

10.0.0.37 www.service1.io
10.0.0.37 www.service2.io
  • 1.
  • 2.

我測試所使用的k8s 部署在我家裡一台限制的Mac 上,所以這裡的IP 它的地址。

當我們反復請求兩次這個接口,會拿到兩個不同的hostname,也就是將我們的請求輪訓負載到了這兩個service 所代理的兩個Pod 中。

❯ curl http://www.service1.io/
k8s-combat-service-79c5579587-b6nlj%
❯ curl http://www.service1.io/
k8s-combat-service-79c5579587-bk7nw%
❯ curl http://www.service2.io/
k8s-combat-service-2-7bbf56b4d9-dkj9b%
❯ curl http://www.service2.io/
k8s-combat-service-2-7bbf56b4d9-t5l4g
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

我們也可以直接使用describe 查看我們的ingress 定義以及路由規則:

圖片圖片

$ k describe ingress k8s-combat-ingress
Name:             k8s-combat-ingress
Labels:           <none>
Namespace:        default
Address:          localhost
Ingress Class:    nginx
Default backend:  <default>
Rules:
  Host             Path  Backends
  ----             ----  --------
  www.service1.io
                   /   k8s-combat-service:8081 (10.1.0.65:8081,10.1.0.67:8081)
  www.service2.io
                   /   k8s-combat-service-2:8081 (10.1.0.63:8081,10.1.0.64:8081)
Annotations:       <none>
Events:            <none>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

如果我們手動新增一個域名解析:

10.0.0.37 www.service3.io
❯ curl http://www.service3.io/
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

會直接404,這是因為沒有找到這個域名的規則。

訪問原理

圖片圖片

整個的請求路徑如上圖所示,其實我們的Ingress 本質上也是一個service(所以它也可以啟動多個副本來進行負載),只是他的類型是LoadBalancer,通常這種類型的service 會由雲廠商綁定一個外部IP,這樣就可以通過這個外部IP 訪問Ingress 了。

而我們應用的service 是ClusterIP,只能在應用內部訪問。

圖片圖片

通過service 的信息也可以看到,我們ingress 的service 綁定的外部IP 是 localhost(本地的原因)。

總結

Ingress 通常是充當網關的作用,後續我們在使用Istio 時,也可以使用Istio 所提供的控制器來替換掉Ingress-nginx,可以更方便的管理內外網流量。

本文的所有源碼在這裡可以訪問:https://github.com/crossoverJie/k8s-combat