K8s Nginx Ingress 介紹


作者:漠然,原文發布於2017年3月4日,原文鏈接

一、Ingress 介紹

Kubernetes 暴露服務的方式目前只有三種:LoadBlancer Service、NodePort Service、Ingress;前兩種估計都應該很熟悉,具體的可以參考下這篇文章;下面詳細的嘮一下這個 Ingress 。

1.1、Ingress 是個什么玩意

可能從大致印象上 Ingress 就是能利用 Nginx、Haproxy 啥的負載均衡器暴露集群內服務的工具;那么問題來了,集群內服務想要暴露出去面臨着幾個問題:

1.2、Pod 漂移問題

眾所周知 Kubernetes 具有強大的副本控制能力,能保證在任意副本(Pod)掛掉時自動從其他機器啟動一個新的,還可以動態擴容等,總之一句話,這個 Pod 可能在任何時刻出現在任何節點上,也可能在任何時刻死在任何節點上;那么自然隨着 Pod 的創建和銷毀,Pod IP 肯定會動態變化;那么如何把這個動態的 Pod IP 暴露出去?這里借助於 Kubernetes 的 Service 機制,Service 可以以標簽的形式選定一組帶有指定標簽的 Pod,並監控和自動負載他們的 Pod IP,那么我們向外暴露只暴露 Service IP 就行了;這就是 NodePort 模式:即在每個節點上開起一個端口,然后轉發到內部 Pod IP 上,如下圖所示

1.3、端口管理問題

采用 NodePort 方式暴露服務面臨一個坑爹的問題是,服務一旦多起來,NodePort 在每個節點上開啟的端口會及其龐大,而且難以維護;這時候引出的思考問題是 “能不能使用 Nginx 啥的只監聽一個端口,比如 80,然后按照域名向后轉發?” 這思路很好,簡單的實現就是使用 DaemonSet 在每個 node 上監聽 80,然后寫好規則,因為 Nginx 外面綁定了宿主機 80 端口(就像 NodePort),本身又在集群內,那么向后直接轉發到相應 Service IP 就行了,如下圖所示

1.4、域名分配及動態更新問題

從上面的思路,采用 Nginx 似乎已經解決了問題,但是其實這里面有一個很大缺陷:每次有新服務加入怎么改 Nginx 配置?總不能手動改或者來個 Rolling Update 前端 Nginx Pod 吧?這時候 “偉大而又正直勇敢的” Ingress 登場,如果不算上面的 Nginx,Ingress 只有兩大組件:Ingress Controller 和 Ingress 。

Ingress 這個玩意,簡單的理解就是你原來要改 Nginx 配置,然后配置各種域名對應哪個 Service,現在把這個動作抽象出來,變成一個 Ingress 對象,你可以用 yml 創建,每次不要去改 Nginx 了,直接改 yml 然后創建/更新就行了;那么問題來了:“Nginx 咋整?”

Ingress Controller 這東西就是解決 “Nginx 咋整” 的。Ingress Controler 通過與 Kubernetes API 交互,動態的去感知集群中 Ingress 規則變化,然后讀取他,按照他自己模板生成一段 Nginx 配置,再寫到 Nginx Pod 里,最后 reload 一下,工作流程如下圖

當然在實際應用中,最新版本 Kubernetes 已經將 Nginx 與 Ingress Controller 合並為一個組件,所以 Nginx 無需單獨部署,只需要部署 Ingress Controller 即可。

二、懟一個 Nginx Ingress

上面啰嗦了那么多,只是為了講明白 Ingress 的各種理論概念,下面實際部署很簡單。

2.1、部署默認后端

我們知道前端的 Nginx 最終要負載到后端 service 上,那么如果訪問不存在的域名咋整?官方給出的建議是部署一個默認后端,對於未知請求全部負載到這個默認后端上;這個后端啥也不干,就是返回 404,部署如下。

$ kubectl create-f default-backend.yamldeployment
default-http-backend"createdservice
default-http-backend"created

這個 default-backend.yaml 文件可以在 官方 Ingress 倉庫找到,由於篇幅限制這里不貼了,倉庫位置如下。

2.2、部署 Ingress Controller

部署完了后端就得把最重要的組件 Nginx + Ingress Controller(官方統一稱為 Ingress Controller) 部署上。

$ kubectl create -f nginx-ingress-controller.yaml
daemonset "nginx-ingress-lb" created

注意:官方的 Ingress Controller 有個坑,至少我看了 DaemonSet 方式部署的有這個問題:沒有綁定到宿主機 80 端口,也就是說前端 Nginx 沒有監聽宿主機 80 端口(這還玩個卵啊);所以需要把配置搞下來自己加一下 hostNetwork,截圖如下。

同樣配置文件自己找一下,地址 點這里,倉庫截圖如下。

當然它支持以 deamonset 的方式部署,這里用的就是(個人喜歡而已),所以你發現我上面截圖是 deployment,但是鏈接給的卻是 daemonset,因為我截圖截錯了…..

2.3、部署 Ingress

這個可就厲害了,這個部署完就能裝逼了。

咳咳,回到正題,從上面可以知道 Ingress 就是個規則,指定哪個域名轉發到哪個 Service,所以說首先我們得有個 Service,當然 Service 去哪找這里就不管了;這里默認為已經有了兩個可用的 Service,以下以 Dashboard 和 Kibana 為例。

先寫一個 Ingress 文件,語法格式啥的請參考官方文檔,由於我的 Dashboard 和 Kibana 都在 kube-system 這個命名空間,所以要指定 namespace,寫之前 Service 分布如下。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: dashboard-kibana-ingress
  namespace: kube-system
spec:
  rules:
  - host: dashboard.mritd.me
    http:
      paths:
      - backend:
          serviceName: kubernetes-dashboard
          servicePort: 80
  - host: kibana.mritd.me
    http:
      paths:
      - backend:
          serviceName: kibana-logging
          servicePort: 5601

裝逼成功截圖如下

三、部署 Ingress TLS

上面已經搞定了 Ingress,下面就順便把 TLS 懟上;官方給出的樣例很簡單,大致步驟就兩步:創建一個含有證書的 secret、在 Ingress 開啟證書;但是我不得不噴一下,文檔就提那么一嘴,大坑一堆,比如多域名配置,還有下面這文檔特么的是逗我玩呢?

3.1、創建證書

首先第一步當然要有個證書,由於我這個 Ingress 有兩個服務域名,所以證書要支持兩個域名;生成證書命令如下:

# 生成 CA 自簽證書
mkdir cert && cd cert
openssl genrsa -out ca-key.pem 2048
openssl req -x509 -new -nodes -key ca-key.pem -days 10000 -out ca.pem -subj "/CN=kube-ca"

# 編輯 openssl 配置
cp /etc/pki/tls/openssl.cnf .
vim openssl.cnf

# 主要修改如下
[req]
req_extensions = v3_req # 這行默認注釋關着的 把注釋刪掉
# 下面配置是新增的
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = dashboard.mritd.me
DNS.2 = kibana.mritd.me

# 生成證書
openssl genrsa -out ingress-key.pem 2048
openssl req -new -key ingress-key.pem -out ingress.csr -subj "/CN=kube-ingress" -config openssl.cnf
openssl x509 -req -in ingress.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out ingress.pem -days 365 -extensions v3_req -extfile openssl.cnf

3.2、創建 secret

創建好證書以后,需要將證書內容放到 secret 中,secret 中全部內容需要 base64 編碼,然后注意去掉換行符(變成一行);以下是我的 secret 樣例(上一步中 ingress.pem 是證書,ingress-key.pem 是證書的 key)。

apiVersion: v1
data:
  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM5akNDQWQ2Z0F3SUJBZ0lKQU5TR2dNNn...
  tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBdXJ3bUExe...
kind: Secret
metadata:
  name: ingress-secret
  namespace: kube-system
type: Opaque

創建完成后 create 一下就可。

$  ~ kubectl create -f ingress-secret.yml
secret "ingress-secret" created

其實這個配置比如證書轉碼啥的沒必要手動去做,可以直接使用下面的命令創建,這里寫這么多只是為了把步驟寫清晰。

kubectl create secret tls ingress-secret --key cert/ingress-key.pem --cert cert/ingress.pem

3.3、重新部署 Ingress

生成完成后需要在 Ingress 中開啟 TLS,Ingress 修改后如下:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: dashboard-kibana-ingress
  namespace: kube-system
spec:
  tls:
  - hosts:
    - dashboard.mritd.me
    - kibana.mritd.me
    secretName: ingress-secret
  rules:
  - host: dashboard.mritd.me
    http:
      paths:
      - backend:
          serviceName: kubernetes-dashboard
          servicePort: 80
  - host: kibana.mritd.me
    http:
      paths:
      - backend:
          serviceName: kibana-logging
          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 證書可能需要等一段時間才會生效。

最后重新部署一下即可。

$  ~ kubectl delete -f dashboard-kibana-ingress.yml
ingress "dashboard-kibana-ingress" deleted
$  ~ kubectl create -f dashboard-kibana-ingress.yml
ingress "dashboard-kibana-ingress" created

注意:部署 TLS 后 80 端口會自動重定向到 443 。

歷時 5 個小時鼓搗,到此結束。

轉載請注明出處,本文采用 CC4.0 協議授權。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM