背景
前兩章中我們將應用部署到了 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
yaml 檔案的內容是一樣的。
不過要注意安裝之後可能容器狀態一直處於 Pending 狀態,檢視容器的事件時會發現映象拉取失敗。
k describe pod ingress-nginx-controller-7cdfb9988c-lbcst -n ingress-nginx
describe 是一個用於檢視 k8s 物件詳細資訊的命令。
在剛才那份 yaml 檔案中可以看到有幾個映象需要拉取,我們可以先在本地手動拉取映象:
docker pull registry.k8s.io/ingress-nginx/controller:v1.8.2
如果依然無法拉取,可以嘗試配置幾個國內映象源映象拉取:
我這裡使用的 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
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
看這個內容也很容易理解,建立了一個 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
我們們這個規則很簡單,就是將兩個不同的域名路由到兩個不同的 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)
})
由於我實際並沒有 www.service1.io/www.service2.io
這兩個域名,所以只能在本地配置 host 進行模擬。
10.0.0.37 www.service1.io
10.0.0.37 www.service2.io
我測試所使用的 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
我們也可以直接使用 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>
如果我們手動新增一個域名解析:
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>
會直接 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