目錄
- 概述
- Ingress架構及原理
- Nginx Ingress配置
- 1. Ingress Controller部署
- 2. 配置Ingress資源以實作流量接入
- 3. 配置Ingress TLS
- 建立secret
- 修改ingress,開啟tls
- 4. 通過ingress暴露tcp服務
- 5. 通過configmap修改nginx controller的全局變量
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。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuM3clJ3Zul2LcN3clJ3Zul2LcNXZ0VmbyVmY1t2LcJXZ0NXYt9CX3Fmcvw1cjlGc512LcV3ZvVncuFWevwVbvNmLuVXepxWYuUGZvN2Lc9CX6MHc0RHaiojIsJye.png)
針對上述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即可。
ingress-nginx代碼托管位址:https://github.com/kubernetes/ingress-nginx
ingress-nginx官方文檔位址:https://kubernetes.github.io/ingress-nginx/
官方的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
...
修改說明:
- 将nginx監聽的端口直接通過host模式監聽至主控端
- 将ingress controller運作在帶有ingress-nginx=enable标簽的節點上(是以需要給叢集中要運作該ingress controller的節點打上對應的标簽)
- 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
前面我們說到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
預設情況下,ingress隻提供了http服務,而沒有https服務,要部署一個https服務,首先得有https證書。證書的生成,這裡不做說明。
假定,我們現在已經有了一個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
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
通過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 端口。
通過上面啟動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
檔案,以确認相應配置是否生效