一、Kubernetes 服務暴露介紹
Service 和 Ingress
1、service的三種類型(四層 ip+端口)
1.1、LoadBlancer
LoadBlancer Service 是 kubernetes 深度結合雲平台的一個組件;當使用 LoadBlancer Service 暴露服務時,實際上是通過向底層雲平台申請創建一個負載均衡器來向外暴露服務;目前 LoadBlancer Service 支持的雲平台已經相對完善,比如國外的 GCE、DigitalOcean,國內的阿里雲,私有雲 Openstack 等等,由於 LoadBlancer Service 深度結合了雲平台,所以只能在一些雲平台上來使用
1.2、NodePort
NodePort Service 顧名思義,實質上就是通過在集群的每個 node 上暴露一個端口,然后將這個端口映射到某個具體的 service 來實現的,雖然每個 node 的端口有很多(0~65535),但是由於安全性和易用性(服務多了就亂了,還有端口沖突問題)實際使用可能並不多。
# kubectl expose deployment java-web2 --port=80 --target-port=8080 --type=NodePort --name=my-svc --port clusterIP對應端口 --target-port 容器應用的端口 --type=NodePort 節點監聽端口 # kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-svc NodePort 10.100.102.20 <none> 80:30264/TCP 10m # ss -anpt | grep 30264 LISTEN 0 128 *:30264 *:* users:(("kube-proxy",pid=3143,fd=11)) apiVersion: v1 kind: Service metadata: labels: app: java-web2 name: java-web2 namespace: default spec: ports: - port: 80 protocol: TCP targetPort: 8080 nodePort: 30045 #注意大小寫 selector: app: java-web2 type: NodePort
1.3、cluster ip
只能只能集群內部訪問
2、Ingress (七層HTTP協議 域名、url)
使用 Ingress 時一般會有三個組件:
- 反向代理負載均衡器 (nginx)
- Ingress Controller (監視器,nginx和Ingress Controller已經合成一個組件,nginx無需單獨部署)
- Ingress (規則定義)
1.3.1、反向代理負載均衡器
反向代理負載均衡器很簡單,就是 nginx、apache 什么的;在集群中反向代理負載均衡器可以自由部署,可以使用 Replication Controller、Deployment、DaemonSet 等等,不過個人喜歡以 DaemonSet 的方式部署,感覺比較方便
1.3.2、Ingress Controller
Ingress Controller 實質上可以理解為是個監視器,Ingress Controller 通過不斷地跟 kubernetes API 打交道,實時的感知后端 service、pod 等變化,比如新增和減少 pod,service 增加與減少等;當得到這些變化信息后,Ingress Controller 再結合下文的 Ingress 生成配置,然后更新反向代理負載均衡器,並刷新其配置,達到服務發現的作用
1.3.3、Ingress
Ingress 簡單理解就是個規則定義,是授權入站連接到達集群服務的規則集合;比如說某個域名對應某個 service,即當某個域名的請求進來時轉發給某個 service;這個規則將與 Ingress Controller 結合,然后 Ingress Controller 將其動態寫入到負載均衡器配置中,從而實現整體的服務發現和負載均衡
從上圖中可以很清晰的看到,實際上請求進來還是被負載均衡器攔截,比如 nginx,然后 Ingress Controller 通過跟 Ingress 交互得知某個域名對應哪個 service,再通過跟 kubernetes API 交互得知 service 地址等信息;綜合以后生成配置文件實時寫入負載均衡器,然后負載均衡器 reload 該規則便可實現服務發現,即動態映射
了解了以上內容以后,這也就很好的說明了我為什么喜歡把負載均衡器部署為 Daemon Set;因為無論如何請求首先是被負載均衡器攔截的,所以在每個 node 上都部署一下,同時 hostport 方式監聽 80 端口;那么就解決了其他方式部署不確定 負載均衡器在哪的問題,同時訪問每個 node 的 80 都能正確解析請求;如果前端再 放個 nginx 就又實現了一層負載均衡
二、ingress存在理由和部署
1、集群內服務想要暴露出去面臨着幾個問題
1.1、Pod 漂移問題
眾所周知 Kubernetes 具有強大的副本控制能力,能保證在任意副本(Pod)掛掉時自動從其他機器啟動一個新的,還可以動態擴容等,總之一句話,這個 Pod 可能在任何時刻出現在任何節點上,也可能在任何時刻死在任何節點上;那么自然隨着 Pod 的創建和銷毀,Pod IP 肯定會動態變化;那么如何把這個動態的 Pod IP 暴露出去?這里借助於 Kubernetes 的 Service 機制,Service 可以以標簽的形式選定一組帶有指定標簽的 Pod,並監控和自動負載他們的 Pod IP,那么我們向外暴露只暴露 Service IP 就行了;這就是 NodePort 模式:即在每個節點上開起一個端口,然后轉發到內部 Pod IP 上,如下圖所示
1.2、端口管理問題
采用 NodePort 方式暴露服務面臨一個坑爹的問題是,服務一旦多起來,NodePort 在每個節點上開啟的端口會及其龐大,而且難以維護;這時候引出的思考問題是 “能不能使用 Nginx 啥的只監聽一個端口,比如 80,然后按照域名向后轉發?” 這思路很好,簡單的實現就是使用 DaemonSet 在每個 node 上監聽 80,然后寫好規則,因為 Nginx 外面綁定了宿主機 80 端口(就像 NodePort),本身又在集群內,那么向后直接轉發到相應 Service IP 就行了
1.3、域名分配及動態更新問題
從上面的思路,采用 Nginx 似乎已經解決了問題,但是其實這里面有一個很大缺陷:每次有新服務加入怎么改 Nginx 配置?總不能手動改或者來個 Rolling Update 前端 Nginx Pod 吧?這時候 “偉大而又正直勇敢的” Ingress 登場,如果不算上面的 Nginx,Ingress 只有兩大組件:Ingress Controller 和 Ingress
Ingress 這個玩意,簡單的理解就是 你原來要改 Nginx 配置,然后配置各種域名對應哪個 Service,現在把這個動作抽象出來,變成一個 Ingress 對象,你可以用 yml 創建,每次不要去改 Nginx 了,直接改 yml 然后創建/更新就行了;那么問題來了:”Nginx 咋整?”
Ingress Controller 這東西就是解決 “Nginx 咋整” 的;Ingress Controoler 通過與 Kubernetes API 交互,動態的去感知集群中 Ingress 規則變化,然后讀取他,按照他自己模板生成一段 Nginx 配置,再寫到 Nginx Pod 里,最后 reload 一下。
在實際應用中,最新版本 Kubernetes 已經將 Nginx 與 Ingress Controller 合並為一個組件,所以 Nginx 無需單獨部署,只需要部署 Ingress Controller 即可
2.部署默認后端
我們知道 前端的 Nginx 最終要負載到后端 service 上,那么如果訪問不存在的域名咋整?官方給出的建議是部署一個 默認后端,對於未知請求全部負載到這個默認后端上;這個后端啥也不干,就是返回 404,部署如下
~ kubectl create -f default-backend.yaml deployment "default-http-backend" created service "default-http-backend" created
這個 default-backend.yaml
文件可以在 官方 Ingress 倉庫 找到,有待補充
3、部署 Ingress Controller
部署完了后端就得把最重要的組件 Nginx+Ingres Controller(官方統一稱為 Ingress Controller) 部署上
~ kubectl create -f nginx-ingress-controller.yaml daemonset "nginx-ingress-lb" created
注意:官方的 Ingress Controller 有個坑, DaemonSet 方式部署的有這個問題:沒有綁定到宿主機 80 端口,也就是說前端 Nginx 沒有監聽宿主機 80 端口;所以需要把配置搞下來自己加一下 hostNetwork
,截圖如下
yaml文件去官網下載
4、部署ingress
Ingress 就是個規則,指定哪個域名轉發到哪個 Service,所以說首先我們得有個 Service,當然 Service 去哪找這里就不管了;這里默認為已經有了兩個可用的 Service,以下以 Dashboard 和 kibana 為例
先寫一個 Ingress 文件,語法格式啥的請參考 官方文檔,由於我的 Dashboard 和 Kibana 都在 kube-system 這個命名空間,所以要指定 namespace,寫之前 Service 分布如下
vim dashboard-kibana-ingress.yml 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
TLS是安全傳輸層協議,ssl的升級版
官方給出的樣例很簡單,大致步驟就兩步:創建一個含有證書的 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)
vim ingress-secret.yml apiVersion: v1 data: tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM5akNDQWQ2Z0F3SUJBZ0lKQU5TR2dNNnYvSVd5TUEwR0NTcUdTSWIzRFFFQkJRVUFNQkl4RURBT0JnTlYKQkFNTUIydDFZbVV0WTJFd0hoY05NVGN3TXpBME1USTBPRFF5V2hjTk1UZ3dNekEwTVRJME9EUXlXakFYTVJVdwpFd1lEVlFRRERBeHJkV0psTFdsdVozSmxjM013Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUUM2dkNZRFhGSFpQOHI5Zk5jZXlkV015VVlELzAwQ2xnS0M2WjNpYWZ0QlRDK005TmcrQzloUjhJUE4KWW00cjZOMkw1MmNkcmZvQnBHZXovQVRIT0NJYUhJdlp1K1ZaTzNMZjcxZEVLR09nV21LMTliSVAzaGpSeDZhWQpIeGhEVWNab3ZzYWY1UWJHRnUydEF4L2doMTFMdXpTZWJkT0Y1dUMrWHBhTGVzWWdQUjhFS0cxS0VoRXBLMDFGCmc4MjhUU1g2TXVnVVZmWHZ1OUJRUXExVWw0Q2VMOXhQdVB5T3lMSktzbzNGOEFNUHFlaS9USWpsQVFSdmRLeFYKVUMzMnBtTHRlUFVBb2thNDRPdElmR3BIOTZybmFsMW0rMXp6YkdTemRFSEFaL2k1ZEZDNXJOaUthRmJnL2NBRwppalhlQ01xeGpzT3JLMEM4MDg4a0tjenJZK0JmQWdNQkFBR2pTakJJTUM0R0ExVWRFUVFuTUNXQ0VtUmhjMmhpCmIyRnlaQzV0Y21sMFpDNXRaWUlQYTJsaVlXNWhMbTF5YVhSa0xtMWxNQWtHQTFVZEV3UUNNQUF3Q3dZRFZSMFAKQkFRREFnWGdNQTBHQ1NxR1NJYjNEUUVCQlFVQUE0SUJBUUNFN1ByRzh6MytyaGJESC8yNGJOeW5OUUNyYVM4NwphODJUUDNxMmsxUUJ1T0doS1pwR1N3SVRhWjNUY0pKMkQ2ZlRxbWJDUzlVeDF2ckYxMWhGTWg4MU9GMkF2MU4vCm5hSU12YlY5cVhYNG16eGNROHNjakVHZ285bnlDSVpuTFM5K2NXejhrOWQ1UHVaejE1TXg4T3g3OWJWVFpkZ0sKaEhCMGJ5UGgvdG9hMkNidnBmWUR4djRBdHlrSVRhSlFzekhnWHZnNXdwSjlySzlxZHd1RHA5T3JTNk03dmNOaQpseWxDTk52T3dNQ0h3emlyc01nQ1FRcVRVamtuNllLWmVsZVY0Mk1yazREVTlVWFFjZ2dEb1FKZEM0aWNwN0sxCkRPTDJURjFVUGN0ODFpNWt4NGYwcUw1aE1sNGhtK1BZRyt2MGIrMjZjOVlud3ROd24xdmMyZVZHCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBdXJ3bUExeFIyVC9LL1h6WEhzblZqTWxHQS85TkFwWUNndW1kNG1uN1FVd3ZqUFRZClBndllVZkNEeldKdUsramRpK2RuSGEzNkFhUm5zL3dFeHpnaUdoeUwyYnZsV1R0eTMrOVhSQ2hqb0ZwaXRmV3kKRDk0WTBjZW1tQjhZUTFIR2FMN0duK1VHeGhidHJRTWY0SWRkUzdzMG5tM1RoZWJndmw2V2kzckdJRDBmQkNodApTaElSS1N0TlJZUE52RTBsK2pMb0ZGWDE3N3ZRVUVLdFZKZUFuaS9jVDdqOGpzaXlTcktOeGZBREQ2bm92MHlJCjVRRUViM1NzVlZBdDlxWmk3WGoxQUtKR3VPRHJTSHhxUi9lcTUycGRadnRjODJ4a3MzUkJ3R2Y0dVhSUXVhelkKaW1oVzRQM0FCb28xM2dqS3NZN0RxeXRBdk5QUEpDbk02MlBnWHdJREFRQUJBb0lCQUJtRmIzaVVISWVocFYraAp1VkQyNnQzVUFHSzVlTS82cXBzenpLVk9NTTNLMk5EZUFkUHhFSDZhYlprYmM4MUNoVTBDc21BbkQvMDdlQVRzClU4YmFrQ2FiY2kydTlYaU5uSFNvcEhlblFYNS8rKys4aGJxUGN6cndtMzg4K0xieXJUaFJvcG5sMWxncWVBOW0KVnV2NzlDOU9oYkdGZHh4YzRxaUNDdmRETDJMbVc2bWhpcFRKQnF3bUZsNUhqeVphdGcyMVJ4WUtKZ003S1p6TAplYWU0bTJDR3R0bmNyUktodklaQWxKVmpyRWoxbmVNa3RHODFTT3QyN0FjeDRlSnozbmcwbjlYSmdMMHcwU05ZCmlwd3I5Uk5PaDkxSGFsQ3JlWVB3bDRwajIva0JIdnozMk9Qb2FOSDRQa2JaeTEzcks1bnFrMHBXdUthOEcyY00KLzY4cnQrRUNnWUVBN1NEeHRzRFFBK2JESGdUbi9iOGJZQ3VhQ2N4TDlObHIxd2tuTG56VVRzRnNkTDByUm1uZAp5bWQ4aU95ME04aUVBL0xKb3dPUGRRY240WFdWdS9XbWV5MzFVR2NIeHYvWlVSUlJuNzgvNmdjZUJSNzZJL2FzClIrNVQ1TEMyRmducVd2MzMvdG0rS0gwc0J4dEM3U2tSK3Y2UndVQk1jYnM3c0dUQlR4NVV2TkVDZ1lFQXlaaUcKbDBKY0dzWHhqd1JPQ0FLZytEMlJWQ3RBVmRHbjVMTmVwZUQ4bFNZZ3krZGxQaCt4VnRiY2JCV0E3WWJ4a1BwSAorZHg2Z0p3UWp1aGN3U25uOU9TcXRrZW04ZmhEZUZ2MkNDbXl4ZlMrc1VtMkxqVzM1NE1EK0FjcWtwc0xMTC9GCkIvK1JmcmhqZW5lRi9BaERLalowczJTNW9BR0xRVFk4aXBtM1ZpOENnWUJrZGVHUnNFd3dhdkpjNUcwNHBsODkKdGhzemJYYjhpNlJSWE5KWnNvN3JzcXgxSkxPUnlFWXJldjVhc0JXRUhyNDNRZ1BFNlR3OHMwUmxFMERWZWJRSApXYWdsWVJEOWNPVXJvWFVYUFpvaFZ0U1VETlNpcWQzQk42b1pKL2hzaTlUYXFlQUgrMDNCcjQ0WWtLY2cvSlplCmhMMVJaeUU3eWJ2MjlpaWprVkVMRVFLQmdRQ2ZQRUVqZlNFdmJLYnZKcUZVSm05clpZWkRpNTVYcXpFSXJyM1cKSEs2bVNPV2k2ZlhJYWxRem1hZW1JQjRrZ0hDUzZYNnMyQUJUVWZLcVR0UGxKK3EyUDJDd2RreGgySTNDcGpEaQpKYjIyS3luczg2SlpRY2t2cndjVmhPT1Z4YTIvL1FIdTNXblpSR0FmUGdXeEcvMmhmRDRWN1R2S0xTNEhwb1dQCm5QZDV0UUtCZ0QvNHZENmsyOGxaNDNmUWpPalhkV0ZTNzdyVFZwcXBXMlFoTDdHY0FuSXk5SDEvUWRaOXYxdVEKNFBSanJseEowdzhUYndCeEp3QUtnSzZmRDBXWmZzTlRLSG01V29kZUNPWi85WW13cmpPSkxEaUU3eFFNWFBzNQorMnpVeUFWVjlCaDI4cThSdnMweHplclQ1clRNQ1NGK0Q5NHVJUmkvL3ZUMGt4d05XdFZxCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== 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
參考以下博文:
https://mritd.me/2016/12/06/try-traefik-on-kubernetes/
https://mritd.me/2017/03/04/how-to-use-nginx-ingress/
https://blog.csdn.net/zll_0405/article/details/88723082
https://www.kubernetes.org.cn/1885.html