Kubernetes 之 Ingress 剖析
【導讀】本文詳細介紹了 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 資源 (https://github.com/wyattup/devops/tree/main/demos/kubernetes/Ingress)
二. IngressRule
IngressRule
的 Key
,就叫做: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-1
和 svc-2
這兩個 Deployment
的 Service
。
三. 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 ,這是一個微服務系統。
-
https://ingress.wyatt.plus/svc1,對應的是 “微服務 - 1”
-
https://ingress.wyatt.plus/svc2,對應的是 “微服務 - 2”
這兩個系統,分別由名叫 “svc-1” 和 “svc-2” 這樣兩個 Deployment 來提供服務。
由於每個平臺配置文件有區別,所以我將按照 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/svc1
和 https://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, 其中
30076
是kubectl 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 帶來的靈活度和自由度,對於使用容器時代來說,其實是非常有意義的。
轉自:
jianshu.com/p/75942f76467b
Go 開發大全
參與維護一個非常全面的 Go 開源技術資源庫。日常分享 Go, 雲原生、k8s、Docker 和微服務方面的技術文章和行業動態。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/j-aqoFnF3vi8Q_0yNXxoBw