問題描述
僅當在 Kubernetes Cluster 中部署 Ingress Controller 之后,我們定義的 Ingress 資源才能生效。當然 Ingress Controller 有很多實現,比如 NGINX Ingress Controller、Traefik Ingress Controller、HAProxy Ingress 等等(參考 Ingress Controllers | Kubernetes 頁面)。
針對我們的場景,我們使用 NGINX Ingress Controller 實現,這完全是基於我們具有很多 Nginx 使用經驗,此外 Nginx 完全能滿足我們的需要。即使日后 NGINX Ingress Controller 無法滿足需求,我們亦可安裝其他 Ingress Controller 實現,它們之間能夠並存。
該筆記將記錄:在 Kubernetes Cluster 中,如何部署 NGINX Ingress Controller 組件,以及相關問題處理。
解決方案
我們參照官方 Installation Guide 文檔,並結合我們自身需求,我們整理出此文,所以該筆記是環境相關的,並不一定適合每種環境。我們強烈建議參照官方文檔,並結合自己的環境進行部署。
部署環境
我們為 Kubernetes v1.18.9 on Bare-metal 環境,即自建 Kubernetes 集群,如果是在 minikube、AWS、Azure 中部署,需要執行不同的命令,參考 Installation Guide 文檔。
第一步、下載並且部署
官方提供 deploy.yaml 部署文件(如果無法下載,使用 ./deploy.yaml 文件):
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.45.0/deploy/static/provider/baremetal/deploy.yaml # 注意事項,因為 Deploymnet 的 nodeSelector: kubernetes.io/os: linux 設置, # 因此保證 lable 包含 kubernetes.io/os: linux 的節點存在於集群中,額外留意就好,一般無需設置。 kubectl apply -f deploy.yaml
第二步、查看容器狀態
查看 NGINX Ingress Controller 組件的狀態:
# kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx --watch NAMESPACE NAME READY STATUS RESTARTS AGE ingress-nginx ingress-nginx-admission-create-hq52n 0/1 Completed 0 6m52s ingress-nginx ingress-nginx-admission-patch-9xqhj 0/1 Completed 0 6m52s ingress-nginx ingress-nginx-controller-cdfb85746-5qqlp 1/1 Running 0 6m52s
第三步、關於 Ingress 訪問
在這之前,我們已經完成 Ingress 部署,但是還有個非常重要的問題:Ingress Controller 的本質作用是作為 WebServer 存在,接收 HTTP 與 HTTPS 請求,然后它根據 Ingress 資源的定義來完成請求的傳發。但是 Ingress Controller 是以 Pod 運行的,它所監聽的端口是內部網絡可見,外部是無法直接訪問的。即使通過 Service NodePort 暴露出來,也並非 80 443 端口,因此用戶訪問的時候,需要使用 http://hostname:nodePort/ 的形式來訪問站點。很顯然,這是不能接受的。
注意事項:我們這些的討論都是基於 Bare-metal 部署的(即自建 Kubernetes 集群),其他部署環境也許會有類似問題。
針對該問題,官方文檔(Bare-metal considerations)提出以下解決方案:
1)MetalLB(純軟件解決方案)
2)NodePort
3)Host network
4)Using a self-provisioned edge(NodePort + LoadBalancer)
5)External IPs
篇幅原因,我們這里不再贅述,建議詳細閱讀官方文檔。針對我們的場景,方案 1 3 4 皆可,我們最終選擇 4 方案(這里面有些不可抗力的存在)。而我們在測試環境中采用 3 方案,因為測試在內網,沒有不是 LoadBalancer 服務。方案 1 其實是最好的,但是我們不想折騰了:-)
針對 4 方案,我們還有個需要考慮的問題:使用三層(TCP),還是七層(HTTP(s)):
最后,我們選擇方案 4 的三層(TCP)做法。這么完全就是為了使用 Let's Encrypt 免費證書。如果我們出現需要客戶端正式地址的方案,我們將在部署七層(HTTP(s))。最后會形成“三層與七層並存,針對不用業務采用不同 LoadBalancer 線路”的做法。
這里沒有很多需要配置的東西,更多的是各種技術方案的取舍。唯一需要我們配置的就是:修改 Service 的 NodePort 為 固定為 30080 30443 端口,然后配置 LoadBalancer 進行 80 443 的轉發,這里不再展開。
第四步、部署測試資源
當部署 NGINX Ingress Controller 組件部署完成時,便能夠定義 Ingress、Server、Deployment 資源進行驗證。
定義資源文件並應用(./ingress-example.yaml):
kubectl apply -f ingress-example.yaml
然后,在瀏覽器中訪問,以驗證 Ingress 能夠正常工作。
簡單 Ingress 管理
# 查看生成的配置文件 kubectl exec -it -n "ingress-nginx" nginx-ingress-controller-67956bf89d-fv58j cat /etc/nginx/nginx.conf # 查看當前 Nginx 版本 kubectl exec -it $POD_NAME -n $POD_NAMESPACE -- /nginx-ingress-controller --version
補充說明
部署 Ingress Controller 時,不建議使用 DaemonSet 類型,而應該使用 Deployment 類型,然后將 POD 示例固定在某個節點上,或者使用 NodePort 方式。為什么這樣做?假如我們的集群有 1000 個節點時,在每個節點都運行 Ingress Controller 並不是最優的。如此大規模的集群,一般僅會有少數節點接受外部請求。
常見問題匯總
nginx-ingress-controller in pending state
當我們部署 deploy.yaml 后,ingress-nginx Pod 一直處於 Pending 狀態。當我們部署自定義的 Deployment 進行測試時,該普通的 Deployment 也處於 Pending 狀態。
當我們向集群添加新節點后,調度到新節點的 Pod 能夠正常運行,而不是 Pending 狀態。因此我們懷疑是節點存在問題,而且其他節點的 Docker 版本是 19.3 版本,而這個節點是 20.10 版本(都是自己埋的坑)。
# 04/22/2021 因此我們嘗試重啟節點……然后問題得到解決(這並不算解決問題。也許是以為某些熱修改的配置沒有正確加載)。
# 04/23/2021 今天這個問題又出現了,我們查看節點的 kubelet 日志,發現 CNI 插件相關的錯誤。不管怎樣,這個問題與 NGINX Ingress Controller 無關,因此這里不再討論。
相關文章
「NGINX Ingress Controller」- 修改默認重定向狀態碼(IE8 無法跳轉)
「K8s」- Traefik Installation
「Kubernetes」- Traefik Kubernetes Ingress
參考文獻
Accessing Kubernetes Pods from Outside of the Cluster
Deployments | Kubernetes
How to sign in kubernetes dashboard? - Stack Overflow
ingress is not listening on port 80 #4799
Installing the Ingress Controller
k8s之nginx-ingress、 Daemonset實現生產案例
NGINX Ingress Controller for Kubernetes
NGINX Ingress Controller/Installation Guide
NGINX Ingress Controller/Troubleshooting
Bare-metal considerations - NGINX Ingress Controller
