本文主要介紹了K8S集群中的服務(wù)發(fā)現(xiàn)和流量暴露機(jī)制焰望,包括K8S中的workload類型、service類型已亥、DNS解析原理以及四層服務(wù)暴露和七層服務(wù)暴露的規(guī)則熊赖。
1、云原生基礎(chǔ)概念
1.1 K8S架構(gòu)
下圖為K8S官方文檔中對K8S架構(gòu)設(shè)計的一個簡要介紹示意圖陷猫,這個架構(gòu)圖側(cè)重于從云廠商的角度展示了云廠商的API
秫舌、K8S集群中控制面(Control Plane)
和工作節(jié)點(diǎn)(Node)
之間的關(guān)系,但是將留給第三方實現(xiàn)的如CRI绣檬、CNI足陨、CSI
等從中剝離出去了。
[圖片上傳失敗...(image-4becbc-1668327890312)]
在官方架構(gòu)圖的基礎(chǔ)上我們將CRI和CNI引入到架構(gòu)圖中娇未,可以得到下面的這個模型:
[圖片上傳失敗...(image-70cc98-1668327890312)]
-
kube-apiserver
對外暴露了Kubernetes API墨缘。它是的 Kubernetes 前端控制層。它被設(shè)計為水平擴(kuò)展零抬,即通過部署更多實例來縮放镊讼。 -
etcd
用于 Kubernetes 的后端存儲。etcd 負(fù)責(zé)保存Kubernetes Cluster的配置信息和各種資源的狀態(tài)信息平夜,始終為 Kubernetes 集群的 etcd 數(shù)據(jù)提供備份計劃蝶棋。當(dāng)數(shù)據(jù)發(fā)生變化時,etcd 會快速地通知Kubernetes相關(guān)組件忽妒。 -
kube-scheduler
主要的工作就是調(diào)度新創(chuàng)建的Pod
玩裙,當(dāng)集群中出現(xiàn)了新的Pod還沒有確定分配到哪一個Node節(jié)點(diǎn)的時候兼贸,kube-scheduler
會根據(jù)各個節(jié)點(diǎn)的負(fù)載,以及應(yīng)用對高可用吃溅、性能溶诞、數(shù)據(jù)親和性的需求等各個方面進(jìn)行分析并將其分配到最合適的節(jié)點(diǎn)上。 -
kube-controller-manager
運(yùn)行控制器决侈,它們是處理集群中常規(guī)任務(wù)的后臺線程螺垢。邏輯上,每個控制器是一個單獨(dú)的進(jìn)程赖歌,但為了降低復(fù)雜性枉圃,它們都被編譯成獨(dú)立的可執(zhí)行文件,并在單個進(jìn)程中運(yùn)行俏站。這些控制器包括:節(jié)點(diǎn)控制器(Node Controller
)讯蒲、副本控制器(Replication Controller
)、端點(diǎn)控制器(Endpoints Controller
)肄扎、服務(wù)帳戶和令牌控制器(Service Account & Token Controllers
)等 -
kube-proxy
是集群中每個節(jié)點(diǎn)上運(yùn)行的網(wǎng)絡(luò)代理, kube-proxy通過維護(hù)主機(jī)上的網(wǎng)絡(luò)規(guī)則并執(zhí)行連接轉(zhuǎn)發(fā)赁酝,實現(xiàn)了Kubernetes服務(wù)抽象犯祠。service在邏輯上代表了后端的多個Pod,外界通過service訪問Pod酌呆。service接收到的請求就是通過kube-proxy轉(zhuǎn)發(fā)到Pod上的衡载,kube-proxy服務(wù)負(fù)責(zé)將訪問service的TCP/UDP數(shù)據(jù)流轉(zhuǎn)發(fā)到后端的容器。如果有多個副本隙袁,kube-proxy會實現(xiàn)負(fù)載均衡痰娱。 - K8S的三大插件分別管控運(yùn)行時、網(wǎng)絡(luò)和存儲菩收,即
Container Runtime Interface (CRI)
梨睁、Container Network Interface (CNI)
和Container-Storage-Interface (CSI)
。注意CRI和CNI是每個K8S集群都必須要部署的基礎(chǔ)組件娜饵,而CSI則不一定坡贺,一般來說只有在我們需要運(yùn)行有狀態(tài)服務(wù)的時候才需要用到CSI。
1.2 CNI基礎(chǔ)
K8S本身不實現(xiàn)集群內(nèi)的網(wǎng)絡(luò)模型箱舞,而是通過將其抽象出來提供了CNI接口給第三方實現(xiàn)遍坟,這樣一來節(jié)省了開發(fā)資源可以集中精力到K8S本身,二來可以利用開源社區(qū)的力量打造一整個豐富的生態(tài)晴股,CNI的一些實現(xiàn)細(xì)節(jié)和要求我們都可以在github上面找到愿伴,我們這里暫不深入解析。
重點(diǎn)來看一下K8S對集群內(nèi)的網(wǎng)絡(luò)模型定義:
- K8S集群中任意兩個POD可以直接通信电湘,并且不需要進(jìn)行NAT
- K8S集群中的每個Pod都必須要有自己的唯一隔节、獨(dú)立且可被訪問的IP(IP-per-Pod)
K8S并不關(guān)心各個CNI如何具體實現(xiàn)上述基礎(chǔ)規(guī)則万搔,只要最終的網(wǎng)絡(luò)模型符合標(biāo)準(zhǔn)即可。因此我們可以確保不論使用什么CNI官帘,K8S集群內(nèi)的Pod網(wǎng)絡(luò)都是一張巨大的平面網(wǎng)絡(luò)瞬雹,每個Pod在這張網(wǎng)絡(luò)中地位是平等的,這種設(shè)計對于集群內(nèi)的服務(wù)發(fā)現(xiàn)刽虹、負(fù)載均衡酗捌、服務(wù)遷移、應(yīng)用配置等諸多場景都帶來了極大的便利涌哲。
1.3 Overlay networks
[圖片上傳失敗...(image-cf5405-1668327890312)]
Overlay網(wǎng)絡(luò)可以理解為建立在另一個網(wǎng)絡(luò)之上的虛擬網(wǎng)絡(luò)胖缤,這個概念在SDN里面里面經(jīng)常出現(xiàn)。和虛擬網(wǎng)卡需要依賴實際存在的物理網(wǎng)卡才能通信類似阀圾,Overlay網(wǎng)絡(luò)也不能憑空出現(xiàn)哪廓,它需要依賴的底層網(wǎng)絡(luò)通常被稱為Underlay網(wǎng)絡(luò)。Underlay 網(wǎng)絡(luò)是專門用來承載用戶 IP 流量的基礎(chǔ)架構(gòu)層初烘,它與 Overlay 網(wǎng)絡(luò)之間的關(guān)系有點(diǎn)類似物理機(jī)和虛擬機(jī)涡真。Underlay 網(wǎng)絡(luò)和物理機(jī)都是真正存在的實體,它們分別對應(yīng)著真實存在的網(wǎng)絡(luò)設(shè)備和計算設(shè)備肾筐,而 Overlay 網(wǎng)絡(luò)和虛擬機(jī)都是依托在下層實體使用軟件虛擬出來的層級哆料。
在使用了Overlay網(wǎng)絡(luò)的K8S集群中,我們可以把底層的Underlay網(wǎng)絡(luò)看作是K8S集群的Node節(jié)點(diǎn)所在的網(wǎng)絡(luò)吗铐,而上層的Overlay網(wǎng)絡(luò)一般用來處理Pod之間的網(wǎng)絡(luò)通信东亦。正常情況下,Underlay網(wǎng)絡(luò)和Overlay網(wǎng)絡(luò)之間互不干擾唬渗,兩者并不知道對方的網(wǎng)絡(luò)情況典阵。但是由于Overlay網(wǎng)絡(luò)是需要依賴Underlay網(wǎng)絡(luò)進(jìn)行傳輸數(shù)據(jù)的,因此在Overlay網(wǎng)絡(luò)的數(shù)據(jù)發(fā)送到Underlay網(wǎng)絡(luò)進(jìn)行傳輸?shù)臅r候镊逝,需要進(jìn)行數(shù)據(jù)包的封裝壮啊,將其變?yōu)閁nderlay網(wǎng)絡(luò)可以理解的數(shù)據(jù)包;反之當(dāng)數(shù)據(jù)從Underlay網(wǎng)絡(luò)傳送回Overlay網(wǎng)絡(luò)的時候需要進(jìn)行數(shù)據(jù)包的解封蹋半。在K8S的Overlay網(wǎng)絡(luò)實現(xiàn)中他巨,用于封裝的兩種常見網(wǎng)絡(luò)協(xié)議是 VXLAN 和 IP-in-IP。
使用Overlay網(wǎng)絡(luò)的主要優(yōu)點(diǎn)是:
- 高度靈活性减江,Overlay網(wǎng)絡(luò)和底層硬件網(wǎng)絡(luò)設(shè)施分離染突,因此在跨機(jī)房、跨數(shù)據(jù)中心等場景有著傳統(tǒng)的Underlay網(wǎng)絡(luò)無法比擬的優(yōu)勢
使用Overlay網(wǎng)絡(luò)的主要缺點(diǎn)是:
- 輕微的性能影響辈灼。封裝數(shù)據(jù)包的過程占用少量 CPU份企,數(shù)據(jù)包中用于編碼封裝(VXLAN 或 IP-in-IP 標(biāo)頭)所需的額外字節(jié)減少了可以發(fā)送的內(nèi)部數(shù)據(jù)包的最大大小,進(jìn)而可以意味著需要為相同數(shù)量的總數(shù)據(jù)發(fā)送更多數(shù)據(jù)包巡莹。
- Pod IP 地址不可在集群外路由司志。
1.4 邊界網(wǎng)關(guān)協(xié)議(BGP)
BGP(Border Gateway Protocol/邊界網(wǎng)關(guān)協(xié)議)是一種基于標(biāo)準(zhǔn)的網(wǎng)絡(luò)協(xié)議甜紫,用于在網(wǎng)絡(luò)中共享路由。它是互聯(lián)網(wǎng)的基本組成部分之一骂远,具有出色的擴(kuò)展特性囚霸。在K8S中,BGP是出場率很高的一個路由協(xié)議激才,有很多相關(guān)的CNI或者是LoadBalancer都會使用BGP協(xié)議來實現(xiàn)諸如路由可達(dá)或者是ECMP等特性拓型。
目前對BGP協(xié)議支持最好、使用最廣泛的CNI應(yīng)該是Calico瘸恼,另外Cilium也有仍處于beta階段的BGP模式的支持劣挫。
1.5 可路由性(routability)
不同的K8S集群網(wǎng)絡(luò)的一個重要區(qū)別就是Pod的IP在K8S集群外的可路由性。
由于K8S集群內(nèi)的Pod之間必然是路由可達(dá)的东帅,因此這里探討的是集群外的服務(wù)到集群內(nèi)的Pod之間的路由可達(dá)压固。
[圖片上傳失敗...(image-55cd03-1668327890312)]
路由不可達(dá)
所謂路由不可達(dá),即K8S集群外的機(jī)器沒辦法和集群內(nèi)的Pod直接建立連接靠闭,集群外的服務(wù)器不知道如何將數(shù)據(jù)包路由到 Pod IP帐我。
這種情況下當(dāng)集群內(nèi)的Pod需要主動和集群外的服務(wù)建立連接的時候,會通過K8S進(jìn)行SNAT(Source Network Address Translation)阎毅。此時在集群外的服務(wù)器看到的連接對端IP是這個Pod所在的K8S集群節(jié)點(diǎn)的Node IP而不是Pod自身的IP焚刚,對于集群外的服務(wù)器發(fā)送返回數(shù)據(jù)的目的IP也永遠(yuǎn)都是這個K8S集群節(jié)點(diǎn)的Node IP,數(shù)據(jù)在Node IP上面再轉(zhuǎn)換發(fā)送回Pod扇调。這種情況下,集群外的服務(wù)器是無法得知Pod的IP抢肛,也無法直接獲取真實的請求IP狼钮。
反之則更復(fù)雜,因為集群外的服務(wù)器不知道如何將數(shù)據(jù)包路由到 Pod IP 捡絮,所以也沒辦法主動去請求這些Pod熬芜,此時只能通過K8S的services(NodePort、LoadBalancer福稳、Ingress)來將服務(wù)暴露到集群外涎拉,此時集群外的服務(wù)器的訪問對象是某個K8S的服務(wù),而不是具體的某個Pod的圆。
路由可達(dá)
如果 Pod IP 地址可在集群外部路由鼓拧,則 pod 可以在沒有 SNAT 的情況下直接連接到集群外的服務(wù)器,而集群外的服務(wù)器也可以直接連接到 pod越妈,而無需通過 通過K8S的services(NodePort季俩、LoadBalancer、Ingress)梅掠。
可在集群外路由的 Pod IP 地址的優(yōu)點(diǎn)是:
- 減少網(wǎng)絡(luò)層級酌住、降低網(wǎng)絡(luò)層面架構(gòu)復(fù)雜性店归、降低使用人員的理解成本、維護(hù)成本和Debug成本等
- 針對一些特殊的應(yīng)用場景(如集群外的機(jī)器需要直接和Pod進(jìn)行連接)酪我,在這種架構(gòu)中實現(xiàn)更加簡單
可在集群外路由的 Pod IP 地址的主要缺點(diǎn)是:
- Pod IP 在集群外的網(wǎng)絡(luò)中也必須要唯一消痛。如果有多個K8S集群都需要實現(xiàn)集群外路由可達(dá),那么就需要給每個集群的Pod使用不同的CIDR都哭。這對內(nèi)部IP的使用規(guī)劃有一定的要求秩伞,并且當(dāng)集群足夠大的時候,還需要考慮內(nèi)網(wǎng)IP耗盡的可能质涛。
可路由性的決定因素
- 如果集群使用的是overlay網(wǎng)絡(luò)稠歉,一般來說Pod IP無法在集群外部路由
- 如果不使用overlay網(wǎng)絡(luò),則取決于部署的環(huán)境(云廠商/本地部署)汇陆、使用的CNI(Calico-BGP怒炸、Cilium-BGP等)以及實際網(wǎng)絡(luò)規(guī)劃等
- 目前K8S網(wǎng)絡(luò)的集群外可路由性實現(xiàn)一般都是通過BGP協(xié)議
2、K8S服務(wù)暴露
正常情況下毡代,我們部署在K8S集群中的工作負(fù)載是需要對外提供服務(wù)的阅羹。這里的“對外”指的是對該負(fù)載以外的所有外部服務(wù)器提供服務(wù),而根據(jù)這些外部服務(wù)器是否位于K8S集群中教寂,我們可以分為K8S集群內(nèi)部流量和K8S集群外流量捏鱼。
2.1 Workload與SVC
開始之前,我們要明確幾個點(diǎn):
- K8S中的工作負(fù)載(Workload)一般指的是集群中的真實工作任務(wù)酪耕。比如無狀態(tài)服務(wù)常用的
deployments
导梆、有狀態(tài)服務(wù)常用的statefulsets
、一些特殊用途的daemonsets
迂烁、定時服務(wù)常用的cronjobs
等看尼,這些都屬于是K8S中的工作負(fù)載(Workload)。 - K8S中的service(SVC)更多傾向于是一種規(guī)則集合盟步,將符合某些特定條件的pod全部歸屬到一個Service中藏斩,然后組成一個特定的Service。注意這些pod是可以屬于不同的工作負(fù)載(Workload)却盘。
- K8S中的每個SVC都會有一個對應(yīng)的域名狰域,域名的組成格式為
$service_name.$namespace_name.svc.$cluster_name
,一般來說k8s集群中的$cluster_name
就是cluster.local
黄橘,這個字段一般在集群創(chuàng)建的時候就會設(shè)定好兆览,之后想要再更改會十分麻煩。
綜上所訴旬陡,我們可以得出以下結(jié)論:
- K8S中的工作負(fù)載(Workload)和服務(wù)暴露(service)是相互隔離開來的拓颓,分別交給不同的
api
來實現(xiàn) - 每個SVC都會有一個
服務(wù)名+命名空間+svc+集群名
/$service_name.$namespace_name.svc.$cluster_name
的域名可以用來訪問(如app.namespace.svc.cluster.local
) - K8S集群內(nèi)的服務(wù)之間訪問主要就是通過這個域名來實現(xiàn)的
2.2 SVC的種類
一般來說svc可以分為四類:Headless
、ClusterIP
描孟、NodePort
驶睦、LoadBalancer
砰左。四者之間的關(guān)系并非是完全互斥,具體如下:
[圖片上傳失敗...(image-55de8f-1668327890312)]
Headless Services
-
Headless
類型服務(wù)和其他三者完全互斥场航,可以通過指定 Cluster IP(spec.clusterIP
)的值為"None"
來創(chuàng)建Headless
Service缠导; - 此時該服務(wù)的域名解析的結(jié)果就是這個服務(wù)關(guān)聯(lián)的所有Pod IP,使用該域名訪問的時候請求會直接到達(dá)pod溉痢;
- 這時的負(fù)載均衡策略相當(dāng)于是僅使用了DNS解析做負(fù)載均衡僻造,并沒有使用k8s內(nèi)置的kube-proxy進(jìn)行負(fù)載均衡;
-
Headless
類型服務(wù)不會創(chuàng)建對應(yīng)域名所屬的SRV記錄孩饼;
[圖片上傳失敗...(image-b2294d-1668327890312)]
Headless Services
這種方式優(yōu)點(diǎn)在于足夠簡單髓削、請求的鏈路短,但是缺點(diǎn)也很明顯镀娶,就是DNS的緩存問題帶來的不可控立膛。很多程序查詢DNS并不會參考規(guī)范的TTL值,要么頻繁的查詢給DNS服務(wù)器帶來巨大的壓力梯码,要么查詢之后一直緩存導(dǎo)致服務(wù)變更了還在請求舊的IP宝泵。
ClusterIP Services
在 Kubernetes 集群中,每個 Node 運(yùn)行一個 kube-proxy
進(jìn)程轩娶。 kube-proxy
負(fù)責(zé)為 Service 實現(xiàn)了一種 VIP(虛擬 IP)的形式儿奶,一般稱之為ClusterIP Services
。
[圖片上傳失敗...(image-f68016-1668327890312)]
-
ClusterIP
是最常用的服務(wù)類型鳄抒,也是默認(rèn)的服務(wù)類型闯捎,同時也是NodePort
、LoadBalancer
這兩個服務(wù)的基礎(chǔ)许溅; - 對于ClusterIP類型的服務(wù)隙券,K8S會給該服務(wù)分配一個稱為
CLUSTER-IP
的VIP; - ClusterIP是單獨(dú)的IP網(wǎng)段闹司,區(qū)別于K8S的宿主機(jī)節(jié)點(diǎn)IP網(wǎng)段和Pod IP網(wǎng)段,也是在集群初始化的時候定義的沐飘;
- ClusterIP可以在每一臺k8s宿主機(jī)節(jié)點(diǎn)上面的
kube-ipvs0
網(wǎng)卡里面看到游桩; - ClusterIP類型的服務(wù)的域名解析的結(jié)果就是這個VIP,請求會先經(jīng)過VIP耐朴,再由kube-proxy分發(fā)到各個pod上面借卧;
- 如果k8s使用了ipvs,可以在K8S宿主機(jī)節(jié)點(diǎn)上面使用ipvsadm命令來查看這些負(fù)載均衡的轉(zhuǎn)發(fā)規(guī)則筛峭;
-
ClusterIP
類型服務(wù)還會創(chuàng)建對應(yīng)域名所屬的SRV記錄铐刘,SRV記錄中的端口為ClusterIP的端口
ClusterIP Services
這種方式的優(yōu)點(diǎn)是有VIP位于Pod前面,可以有效避免前面提及的直接DNS解析帶來的各類問題影晓;缺點(diǎn)也很明顯镰吵,當(dāng)請求量大的時候檩禾,kube-proxy組件的處理性能會首先成為整個請求鏈路的瓶頸。
NodePort Service
- 從NodePort開始疤祭,服務(wù)就不僅局限于在K8S集群內(nèi)暴露盼产,開始可以對集群外提供服務(wù)
- NodePort類型會從K8S的宿主機(jī)節(jié)點(diǎn)上面挑選一個端口分配給某個服務(wù)(默認(rèn)范圍是30000-32767),用戶可以通過請求任意一個K8S節(jié)點(diǎn)IP的該指定端口來訪問這個服務(wù)
- NodePort服務(wù)域名解析的解析結(jié)果是一個
CLUSTER-IP
勺馆,在集群內(nèi)部請求的負(fù)載均衡邏輯和實現(xiàn)與ClusterIP Service
是一致的 - NodePort服務(wù)的請求路徑是從K8S節(jié)點(diǎn)IP直接到Pod戏售,并不會經(jīng)過ClusterIP,但是這個轉(zhuǎn)發(fā)邏輯依舊是由
kube-proxy
實現(xiàn)
[圖片上傳失敗...(image-5f5c96-1668327890312)]
NodePort Service
這種方式的優(yōu)點(diǎn)是非常簡單的就能把服務(wù)通過K8S自帶的功能暴露到集群外部草穆;缺點(diǎn)也很明顯:NodePort本身的端口限制(數(shù)量和選擇范圍都有限)以及請求量大時的kube-proxy組件的性能瓶頸問題灌灾。
LoadBalancer Service
- LoadBalancer服務(wù)類型是K8S對集群外服務(wù)暴露的最高級最優(yōu)雅的方式,同時也是門檻最高的方式悲柱;
- LoadBalancer服務(wù)類型需要K8S集群支持一個云原生的LoadBalancer锋喜,這部分功能K8S本身沒有實現(xiàn),而是將其交給云廠商/第三方诗祸,因此對于云環(huán)境的K8S集群可以直接使用云廠商提供的LoadBalancer跑芳,當(dāng)然也有一些開源的云原生LoadBalancer,如MetalLB直颅、OpenELB博个、PureLB等;
- LoadBalancer服務(wù)域名解析的解析結(jié)果是一個
CLUSTER-IP
功偿; - LoadBalancer服務(wù)同時會分配一個
EXTERNAL-IP
盆佣,集群外的機(jī)器可以通過這個EXTERNAL-IP
來訪問服務(wù); - LoadBalancer服務(wù)默認(rèn)情況下會同時創(chuàng)建NodePort械荷,也就是說一個LoadBalancer類型的服務(wù)同時是一個NodePort服務(wù)共耍,同時也是一個clusterIP服務(wù);一些云原生LoadBalancer可以通過指定
allocateLoadBalancerNodePorts: false
來拒絕創(chuàng)建NodePort服務(wù)吨瞎;
我們還是借用OpenELB官網(wǎng)的圖來解釋一下這個流程痹兜,注意這里為BGP模式。
[圖片上傳失敗...(image-2053f5-1668327890312)]
LoadBalancer Service
這種方式的優(yōu)點(diǎn)是方便颤诀、高效字旭、適用場景廣泛,幾乎可以覆蓋所有對外的服務(wù)暴露崖叫;缺點(diǎn)則是成熟可用的云原生LoadBalancer選擇不多遗淳,實現(xiàn)門檻較高。
2.3 Port概念辨析
在我們進(jìn)行SVC和Workload部署配置的時候心傀,經(jīng)常會碰到各種名字中帶有Port
的配置項屈暗,這也是K8S中容易讓人混淆的幾個概念之一,這里主要介紹一下NodePort
、Port
养叛、targetPort
和containerPort
這個四個概念种呐。四者的關(guān)系我們可以通過下面這種圖比較清晰地區(qū)分出來:
[圖片上傳失敗...(image-619a86-1668327890312)]
-
nodePort
: 只存在于Loadbalancer服務(wù)和NodePort服務(wù)中,用于指定K8S集群的宿主機(jī)節(jié)點(diǎn)的端口一铅,默認(rèn)范圍是30000-32767
陕贮,K8S集群外部可以通過NodeIP:nodePort
來訪問某個service; -
port
: 只作用于CLUSTER-IP
和EXTERNAL-IP
潘飘,也就是對Loadbalancer服務(wù)肮之、NodePort服務(wù)和ClusterIP服務(wù)均有作用,K8S集群內(nèi)部可以通過CLUSTER-IP:port
來訪問卜录,K8S集群外部可以通過EXTERNAL-IP:port
來訪問戈擒; -
targetPort
: Pod的外部訪問端口,port和nodePort的流量會參照對應(yīng)的ipvs規(guī)則轉(zhuǎn)發(fā)到Pod上面的這個端口艰毒,也就是說數(shù)據(jù)的轉(zhuǎn)發(fā)路徑是NodeIP:nodePort -> PodIP:targetPort
筐高、CLUSTER-IP:port -> PodIP:targetPort
、EXTERNAL-IP:port -> PodIP:targetPort
-
containerPort
:和其余三個概念不屬于同一個維度丑瞧,containerPort
主要是在工作負(fù)載(Workload)
中配置柑土,其余三者均是在service
中配置。containerPort
主要作用在Pod內(nèi)部的container绊汹,用來告知K8S這個container內(nèi)部提供服務(wù)的端口稽屏,因此理論上containerPort
應(yīng)該要和container內(nèi)部實際監(jiān)聽的端口一致,這樣才能確保服務(wù)正常西乖;但是實際上由于各個CNI的實現(xiàn)不通以及K8S配置的網(wǎng)絡(luò)策略差異狐榔,containerPort
的作用并不明顯,很多時候配置錯誤或者是不配置也能正常工作获雕;
綜上所述薄腻,我們可以得知四者的主要區(qū)別,那么我們在實際使用的時候届案,最好就需要確保targetPort
庵楷、containerPort
和Pod里面運(yùn)行程序?qū)嶋H監(jiān)聽的端口三者保持一致,即可確保請求的數(shù)據(jù)轉(zhuǎn)發(fā)鏈路正常楣颠。
3嫁乘、K8S中的DNS服務(wù)
眾所周知,在K8S中球碉,IP是隨時會發(fā)生變化的,變化最頻繁的就是Pod IP
仓蛆,Cluster IP
也并不是一定不會發(fā)生變化睁冬,EXTERNAL-IP
雖然可以手動指定靜態(tài)IP保持不變,但是主要面向的是集群外部的服務(wù);因此在K8S集群中豆拨,最好的服務(wù)之間相互訪問的方式就是通過域名直奋。
3.1 DNS創(chuàng)建規(guī)則
在K8S集群中,Kubernetes 為 Service 和 Pod 創(chuàng)建 DNS 記錄施禾。
前面我們介紹了K8S中的每個SVC都會有一個對應(yīng)的域名脚线,域名的組成格式為$service_name.$namespace_name.svc.$cluster_name
,同時也會給這個SVC下的所有Pod都創(chuàng)建一個$pod_name.$service_name.$namespace_name.svc.$cluster_name
的這種域名弥搞,這個域名的解析結(jié)果就是Pod IP邮绿。
Pod域名有兩個比較明顯的特征:
- 一是域名的組成比較特殊,因為域名中使用了Pod的名稱攀例,而pod名稱在K8S中是會發(fā)生變化的(例如在服務(wù)更新或者滾動重啟時)船逮,同時由于默認(rèn)情況下Pod的命名是沒有太明顯的規(guī)律(大部分名字中會包含一串隨機(jī)UUID)
- 二是域名的解析結(jié)果特殊,相較于集群內(nèi)的其他類型域名粤铭,Pod域名的解析是可以精確到特定的某個Pod挖胃,因此一些特殊的需要點(diǎn)對點(diǎn)通信的服務(wù)可以使用這類Pod域名
3.2 DNS策略配置
DNS 策略可以逐個 Pod 來設(shè)定。目前 Kubernetes 支持以下特定 Pod 的 DNS 策略梆惯。 這些策略可以在 Pod 規(guī)約中的 dnsPolicy
字段設(shè)置:
-
Default
: Pod 從運(yùn)行所在的K8S宿主機(jī)節(jié)點(diǎn)繼承域名解析配置酱鸭; -
ClusterFirst
: 不指定任何dnsPolicy
配置情況下的默認(rèn)選項,所有查詢的域名都會根據(jù)生成的集群的K8S域名等信息生成的/etc/resolv.conf
配置進(jìn)行解析和轉(zhuǎn)發(fā)到集群內(nèi)部的DNS服務(wù)進(jìn)行解析垛吗; -
ClusterFirstWithHostNet
:主要用于以hostNetwork
方式運(yùn)行的 Pod凹髓,如果這些pod想要使用K8S集群內(nèi)的DNS服務(wù),則可以配置為這個字段职烧; -
None
: 此設(shè)置允許 Pod 忽略 Kubernetes 環(huán)境中的 DNS 設(shè)置扁誓,Pod 會使用其dnsConfig
字段 所配置的 DNS 設(shè)置;
說明: 下面主要介紹ClusterFirst
模式
3.3 DNS解析規(guī)則
DNS 查詢參照 Pod 中的 /etc/resolv.conf
配置蚀之,kubelet 會為每個 Pod 生成此文件蝗敢。因此在每個pod里面都有一個類似下面這樣的 /etc/resolv.conf
文件,通過修改其中的配置可以更改DNS的查詢規(guī)則:
nameserver 10.32.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
這里的配置有幾個需要注意的點(diǎn):
-
nameserver
:集群中的DNS服務(wù)器IP足删,一般來說就是CoreDNS
的ClusterIP
-
search
:需要搜索的域寿谴,默認(rèn)情況下會從該pod所屬的namespace開始逐級補(bǔ)充 -
options ndots
:觸發(fā)上面的search
的域名點(diǎn)數(shù),默認(rèn)為1失受,上限15讶泰,在K8S中一般為5;例如在Linux中tinychen.com
這個域名的ndots
是1拂到,tinychen.com.
這個域名的ndots
才是2(需要注意所有域名其實都有一個根域.
痪署,因此tinychen.com
的全稱應(yīng)該是tinychen.com.
)
這是一個比較通用的案例,我們再來看一個比較特殊的配置
# 首先進(jìn)入一個pod查看里面的DNS解析配置
[root@tiny-calico-master-88-1 tiny-calico]# kubectl exec -it -n ngx-system ngx-ex-deploy-6bf6c99d95-5qh2w /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
[root@ngx-ex-deploy-6bf6c99d95-5qh2w /]# cat /etc/resolv.conf
nameserver 10.88.0.10
search ngx-system.svc.cali-cluster.tclocal svc.cali-cluster.tclocal cali-cluster.tclocal k8s.tcinternal
options ndots:5
[root@ngx-ex-deploy-6bf6c99d95-5qh2w /]# exit
這個pod里面的/etc/resolv.conf
配置文件有兩個和前面不同的地方:
-
cluster.local
變成了cali-cluster.tclocal
這里我們可以看到coredns的配置中就是配置的
cali-cluster.tclocal
兄旬,也就是說/etc/resolv.conf
中的配置其實是和coredns
中的配置一樣狼犯,更準(zhǔn)確的說是和該K8S集群初始化時配置的集群名一樣# 再查看K8S集群中的coredns的configmap [root@tiny-calico-master-88-1 tiny-calico]# kubectl get configmaps -n kube-system coredns -oyaml apiVersion: v1 data: Corefile: | .:53 { errors health { lameduck 5s } ready kubernetes cali-cluster.tclocal in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa ttl 30 } prometheus :9153 forward . 10.31.100.100 { max_concurrent 1000 } cache 30 loop reload loadbalance } kind: ConfigMap metadata: creationTimestamp: "2022-05-06T05:19:08Z" name: coredns namespace: kube-system resourceVersion: "3986029" uid: 54f5f803-a5ab-4c77-b149-f02229bcad0a
-
search
新增了一個k8s.tcinternal
實際上我們再查看K8S的宿主機(jī)節(jié)點(diǎn)的DNS配置規(guī)則時會發(fā)現(xiàn)這個
k8s.tcinternal
是從宿主機(jī)上面繼承而來的# 最后查看宿主機(jī)節(jié)點(diǎn)上面的DNS解析配置 [root@tiny-calico-master-88-1 tiny-calico]# cat /etc/resolv.conf # Generated by NetworkManager search k8s.tcinternal nameserver 10.31.254.253
3.4 DNS解析流程
溫馨提示:閱讀這部分內(nèi)容的時候要特別注意域名結(jié)尾是否有一個點(diǎn)號
.
當(dāng)ndots小于options ndots
前面我們說過options ndots的值默認(rèn)情況下是1,在K8S中為5,為了效果明顯悯森,我們這里使用K8S中的5作為示例:
這里同樣是在一個命名空間demo-ns
中有兩個SVC宋舷,分別為demo-svc1
和demo-svc2
,那么他們的/etc/resolv.conf
應(yīng)該是下面這樣的:
nameserver 10.32.0.10
search demo-ns.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
我們在demo-svc1
中直接請求域名demo-svc2
瓢姻,此時ndots為1祝蝠,小于配置中的5,因此會觸發(fā)上面的search
規(guī)則幻碱,這時第一個解析的域名就是demo-svc2.demo-ns.svc.cluster.local
绎狭,當(dāng)解析不出來的時候繼續(xù)下面的demo-svc2.svc.cluster.local
、demo-svc2.cluster.local
收班,最后才是直接去解析demo-svc2.
坟岔。
注意上面的規(guī)則適用于任何一個域名,也就是當(dāng)我們試圖在pod中去訪問一個外部域名如tinychen.com
的時候也會依次進(jìn)行上述查詢摔桦。
當(dāng)ndots大于等于options ndots
我們在demo-svc1
中直接請求域名demo-svc2.demo-ns.svc.cluster.local
社付,此時的ndots為4,還是會觸發(fā)上面的search規(guī)則邻耕。
而請求域名demo-svc2.demo-ns.svc.cluster.local.
氨淌,ndots為5俺陋,等于配置中的5,因此不會觸發(fā)上面的search
規(guī)則,直接去解析demo-svc2.demo-ns.svc.cluster.local.
這個域名并返回結(jié)果
如果我們請求更長的域名如POD域名pod-1.demo-svc2.demo-ns.svc.cluster.local.
丰榴,此時的ndots為6者祖,大于配置中的5缴川,因此也不會觸發(fā)上面的search
規(guī)則厕诡,會直接查詢域名并返回解析
小結(jié)
通過上面的分析我們不難得出下面幾點(diǎn)結(jié)論:
- 同命名空間(namespace)內(nèi)的服務(wù)直接通過
$service_name
進(jìn)行互相訪問而不需要使用全域名(FQDN),此時DNS解析速度最快削解; - 跨命名空間(namespace)的服務(wù)富弦,可以通過
$service_name.$namespace_name
進(jìn)行互相訪問,此時DNS解析第一次查詢失敗氛驮,第二次才會匹配到正確的域名腕柜; - 所有的服務(wù)之間通過全域名(FQDN)
$service_name.$namespace_name.svc.$cluster_name.
訪問的時候DNS解析的速度最快; - 在K8S集群內(nèi)訪問大部分的常見外網(wǎng)域名(ndots小于5)都會觸發(fā)
search
規(guī)則矫废,因此在訪問外部域名的時候可以使用FQDN盏缤,即在域名的結(jié)尾配置一個點(diǎn)號.
4、四層服務(wù)暴露
對于K8S集群中的服務(wù)暴露到集群外部提供服務(wù)蓖扑,一般的方式可以分為四層服務(wù)暴露和七層服務(wù)暴露唉铜,因為前者一般來說是后者的基礎(chǔ),因此這里我們先對四層服務(wù)暴露進(jìn)行介紹律杠。
在開始之前我們需要明確四層的概念打毛,這里的四層指的是OSI七層模型中的第四層柿赊,即TCP、UDP協(xié)議所處于的傳輸層幻枉,也就是我們常說的協(xié)議+IP+端口
層面的負(fù)載均衡,常見的四層負(fù)載均衡器有LVS诡蜓、DPVS熬甫、Haproxy(四層七層均可)、nginx(四層七層均可)等蔓罚,在K8S中的四層服務(wù)暴露最常用的兩種手段就是我們前面提及的Nodeport和LoadBalancer椿肩。
我們先來看下面的這個架構(gòu)圖,注意整個藍(lán)色的點(diǎn)線范圍內(nèi)的是一個K8S集群豺谈,為了方便區(qū)分郑象,我們這里假設(shè)從集群外部進(jìn)來的流量都是南北流量(客戶端-服務(wù)器流量),集群內(nèi)部的流量全部都是東西流量(服務(wù)器-服務(wù)器流量)茬末。
[圖片上傳失敗...(image-20d0cc-1668327890312)]
我們從下到上開始看起
圖中有
frontend
厂榛、backend
、devops
等多個命名空間丽惭,這是常見的用來隔離不同資源的手段击奶,在實際的落地場景中可以根據(jù)不同的業(yè)務(wù)、使用人群等進(jìn)行劃分责掏,具體的劃分維度和標(biāo)準(zhǔn)最好視實際業(yè)務(wù)情況而定柜砾;實際上不止是workload,service也是會根據(jù)不同的namespace進(jìn)行劃分换衬,包括K8S集群中的大部分api痰驱,我們在查找的時候都是需要指定namespace
在k8s service這一層,圖中主要展示了用于集群內(nèi)部訪問的Cluster-IP和Headless兩種方式
需要注意的是Headless服務(wù)是和其余三種服務(wù)類型相斥的瞳浦,同時它也不會經(jīng)過kube-proxy進(jìn)行負(fù)載均衡担映,因此在Headless服務(wù)的藍(lán)色實線框中是空白的,而Cluster-IP中則是kube-proxy組件
再往上就是每個K8S集群中一般都會有的DNS服務(wù)术幔,CoreDNS從K8S的v1.11版本開始可以用來提供命名服務(wù)另萤,從v1.13 開始替代
kube-dns
成為默認(rèn)DNS 服務(wù)在 Kubernetes 1.21 版本中,kubeadm 移除了對將
kube-dns
作為 DNS 應(yīng)用的支持诅挑。 對于kubeadm
v1.24四敞,所支持的唯一的集群 DNS 應(yīng)用是 CoreDNSCoreDNS本身也是一個
workload
,它是位于kube-system
這個命名空間下的一個deployments.apps
CoreDNS也是通過請求K8S集群內(nèi)的api-server來獲取k8s集群內(nèi)的服務(wù)信息
再往上就是位于整個K8S集群的邊界處拔妥,這里首先必然會有一個api-server忿危,它會同時對集群內(nèi)和集群外暴露控制接口,我們可以通過這個接口來獲取K8S集群的信息
api-server本身并不存儲信息没龙,K8S集群本身的集群信息大部分都是存儲在etcd服務(wù)中铺厨,api-server會去etcd中讀取相關(guān)數(shù)據(jù)并返回缎玫;
最后就是用來暴露四層服務(wù)的服務(wù),一般是NodePort或者是LoadBalancer解滓,因為端口和IP等原因赃磨,實際上使用的時候大部分都是以LoadBalancer的方式暴露出去;
LoadBalancer服務(wù)對外暴露的并不是一個IP洼裤,而是一個
IP+端口
邻辉,也就是說實際上一個IP的多個端口可以為不同類型的服務(wù)提供服務(wù),例如80和443端口提供http/https服務(wù)腮鞍,3306提供數(shù)據(jù)庫服務(wù)等值骇;K8S并沒有內(nèi)置LoadBalancer,因此需要實現(xiàn)LoadBalancer移国,目前主流有兩種方式:一是使用云廠商如AWS吱瘩、Azure、阿里騰訊等提供的LoadBalancer迹缀,這些LoadBalancer基本都是閉源的解決方案使碾,基本僅適用于他們自家的云環(huán)境,但是作為收費(fèi)服務(wù)裹芝,在技術(shù)支持和售后已經(jīng)產(chǎn)品成熟度方面均有不錯的表現(xiàn)部逮;二是使用已有的一些開源LoadBalancer,主要就是MetalLB嫂易、OpenELB以及PureLB兄朋,關(guān)于三者的詳細(xì)介紹,之前已經(jīng)寫過相關(guān)的文章怜械,有興趣的可以點(diǎn)擊鏈接進(jìn)去看看颅和。
開源的LoadBalancer基本都是擁有兩種主要工作模式:Layer2模式和BGP模式。無論是Layer2模式還是BGP模式缕允,核心思路都是通過某種方式將特定VIP的流量引到k8s集群中峡扩,然后再通過kube-proxy將流量轉(zhuǎn)發(fā)到后面的特定服務(wù)。
5障本、七層服務(wù)暴露
四層LoadBalancer服務(wù)暴露的方式優(yōu)點(diǎn)是適用范圍廣教届,因為工作在四層,因此幾乎能適配所有類型的應(yīng)用驾霜。但是也有一些缺點(diǎn):
- 對于大多數(shù)應(yīng)用場景都是http協(xié)議的請求來說案训,并不需要給每個服務(wù)都配置一個
EXTERNAL-IP
來暴露服務(wù),這樣一來資源嚴(yán)重浪費(fèi)(公網(wǎng)IP十分珍貴)粪糙,二來包括IP地址已經(jīng)HTTPS使用的證書管理等均十分麻煩 - 比較常見的場景是對應(yīng)的每個配置都配置一個域名(virtual host)或者是路由規(guī)則(routing rule)强霎,然后統(tǒng)一對外暴露一個或者少數(shù)幾個
EXTERNAL-IP
,將所有的請求流量都導(dǎo)入到一個統(tǒng)一個集中入口網(wǎng)關(guān)(如Nginx)蓉冈,再由這個網(wǎng)關(guān)來進(jìn)行各種負(fù)載均衡(load balancing)城舞、路由管理(routing rule)轩触、證書管理(SSL termination)等等
在K8S中,一般交由ingress來完成上述這些事情家夺。
5.1 Ingress
Ingress 是對集群中服務(wù)的外部訪問進(jìn)行管理的 API 對象脱柱,典型的訪問方式是 HTTP。Ingress 可以提供負(fù)載均衡(load balancing)拉馋、路由管理(routing rule)褐捻、證書管理(SSL termination)等功能。Ingress 公開從集群外部到集群內(nèi)服務(wù)的 HTTP 和 HTTPS 路由椅邓。 流量路由由 Ingress 資源上定義的規(guī)則控制。
下圖為K8S官方提供的一個關(guān)于ingres工作的示意圖:
[圖片上傳失敗...(image-240122-1668327890312)]
這里我們需要注意區(qū)分Ingress
和Ingress Controllers
昧狮,兩者是兩個不同的概念景馁,并不等同
- 從概念上來看,
Ingress
和前面提到的service很像逗鸣,Ingress本身并不是一個真實存在的工作負(fù)載(workload) - 而
Ingress Controllers
更傾向于部署在K8S集群中的一些特殊網(wǎng)關(guān)(如NGX)合住,以K8S官方維護(hù)的ingress-nginx
為例,它本質(zhì)上其實是一個帶有Ingress資源類型的特殊deployments撒璧,Ingress Controllers
是Ingress
的具體實現(xiàn)
5.2 Ingress Controllers
上面我們得知所謂的Ingress Controllers
本身其實就是特殊的workload(一般是deployment)透葛,因此它們本身也是需要通過某種方式暴露到集群外才能提供服務(wù),這里一般都是選擇通過LoadBalancer進(jìn)行四層服務(wù)暴露卿樱。
[圖片上傳失敗...(image-f28845-1668327890312)]
針對上面四層服務(wù)暴露的架構(gòu)圖僚害,我們在入口處的LoadBalancer后面加入一個Ingress,就可以得到七層Ingress服務(wù)暴露的架構(gòu)圖繁调。
- 在這個圖中的處理邏輯和四層服務(wù)暴露一致萨蚕,唯一不同的就是HTTP協(xié)議的流量是先經(jīng)過入口處的loadbalancer,再轉(zhuǎn)發(fā)到ingress里面蹄胰,ingress再根據(jù)里面的ingress rule來進(jìn)行判斷轉(zhuǎn)發(fā)岳遥;
- k8s的ingress-nginx是會和集群內(nèi)的api-server通信并且獲取服務(wù)信息,因為
Ingress Controllers
本身就具有負(fù)載均衡的能力裕寨,因此在把流量轉(zhuǎn)發(fā)到后端的具體服務(wù)時浩蓉,不會經(jīng)過ClusterIP(就算服務(wù)類型是ClusterIP也不經(jīng)過),而是直接轉(zhuǎn)發(fā)到所屬的Pod IP
上宾袜;
6捻艳、總結(jié)
到這里關(guān)于K8S的基本服務(wù)暴露所需要了解的知識就介紹完了,由于K8S本身確實十分復(fù)雜试和,本文在介紹的時候只能蜻蜓點(diǎn)水讯泣,隨著K8S的不斷發(fā)展,現(xiàn)在普通的服務(wù)暴露已經(jīng)不能很好的滿足部分場景的高端需求阅悍,隨后又引發(fā)了很多諸如服務(wù)網(wǎng)格(service mesh)好渠、邊車模型(sidecar)昨稼、無邊車模型(sidecarless)等等的進(jìn)化。