新聞中心
一文帶你弄懂Kubernetes應(yīng)用訪問(wèn)管理
作者:?jiǎn)炭?2022-09-05 09:25:53
云計(jì)算
云原生 在Kubernetes中,提供了Service和Ingress兩種對(duì)象來(lái)實(shí)現(xiàn)應(yīng)用間訪問(wèn)或外部對(duì)集群應(yīng)用訪問(wèn),這兩種對(duì)象在實(shí)際的工作中會(huì)時(shí)長(zhǎng)使用,非常重要的對(duì)象。

十載的山城網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。營(yíng)銷型網(wǎng)站建設(shè)的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整山城建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)公司從事“山城網(wǎng)站設(shè)計(jì)”,“山城網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
在Kubernetes中,如果僅僅是單純的部署Pod,部署Deployment,并沒(méi)有任何意義,因?yàn)槲覀冏罱K的目的是要為應(yīng)用和用戶提供服務(wù)。
在Kubernetes中,提供了Service和Ingress兩種對(duì)象來(lái)實(shí)現(xiàn)應(yīng)用間訪問(wèn)或外部對(duì)集群應(yīng)用訪問(wèn),這兩種對(duì)象在實(shí)際的工作中會(huì)時(shí)長(zhǎng)使用,非常重要的對(duì)象。
Service
對(duì)于kubernetes整個(gè)集群來(lái)說(shuō),Pod的地址也可變的,也就是說(shuō)如果一個(gè)Pod因?yàn)槟承┰蛲顺隽?,而由于其設(shè)置了副本數(shù)replicas大于1,那么該P(yáng)od就會(huì)在集群的任意節(jié)點(diǎn)重新啟動(dòng),這個(gè)重新啟動(dòng)的Pod的IP地址與原IP地址不同,這對(duì)于業(yè)務(wù)來(lái)說(shuō),就不能根據(jù)Pod的IP作為業(yè)務(wù)調(diào)度。kubernetes就引入了Service的概念,它為Pod提供一個(gè)入口,主要通過(guò)Labels標(biāo)簽來(lái)選擇后端Pod,這時(shí)候不論后端Pod的IP地址如何變更,只要Pod的Labels標(biāo)簽沒(méi)變,那么 業(yè)務(wù)通過(guò)service調(diào)度就不會(huì)存在問(wèn)題。
當(dāng)聲明Service的時(shí)候,會(huì)自動(dòng)生成一個(gè)cluster IP,這個(gè)IP是虛擬IP。我們就可以通過(guò)這個(gè)IP來(lái)訪問(wèn)后端的Pod,當(dāng)然,如果集群配置了DNS服務(wù),比如現(xiàn)在的CoreDNS,那么也可以通過(guò)Service的名字來(lái)訪問(wèn),它會(huì)通過(guò)DNS自動(dòng)解析Service的IP地址。
Service的類型有4種,Cluster IP,LoadBalance,NodePort,ExternalName。其中Cluster IP是默認(rèn)的類型。
- Cluster IP:通過(guò) 集群內(nèi)部IP暴露服務(wù),默認(rèn)是這個(gè)類型,選擇該值,這個(gè)Service服務(wù)只能通過(guò)集群內(nèi)部訪問(wèn)。
- LoadBalance:使用云提供商的負(fù)載均衡器,可以向外部暴露服務(wù),選擇該值,外部的負(fù)載均衡器可以路由到NodePort服務(wù)和Cluster IP服務(wù)。
- NodePort:顧名思義是Node基本的Port,如果選擇該值,這個(gè)Service可以通過(guò)NodeIP:NodePort訪問(wèn)這個(gè)Service服務(wù),NodePort會(huì)路由到Cluster IP服務(wù),這個(gè)Cluster IP會(huì)通過(guò)請(qǐng)求自動(dòng)創(chuàng)建。
- ExternalName:通過(guò)返回 CNAME 和它的值,可以將服務(wù)映射到 externalName 字段的內(nèi)容,沒(méi)有任何類型代理被創(chuàng)建,可以用于訪問(wèn)集群內(nèi)其他沒(méi)有Labels的Pod,也可以訪問(wèn)其他NameSpace里的Service。
kubernetes主要通過(guò)kube-proxy創(chuàng)建iptables和ipvs規(guī)則,在每個(gè)Node節(jié)點(diǎn)上都會(huì)創(chuàng)建這些規(guī)則。
ClusterIP
ClusterIP是Service默認(rèn)的類型,只能在集群的內(nèi)部訪問(wèn),也是工作中最常用的一個(gè)類型,定義如下:
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx
spec:
type: ClusterIP
selector:
app: nginx
ports:
- name: http
port: 80
其中:
- type用戶指定Service類型。
- ports用戶指定Service的端口。
- selector用戶選擇后端的Pod。
如果Service暴露的端口和后端的端口不一致,還可以這樣寫:
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx
spec:
type: ClusterIP
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: 8080
targetPort用于指定后端的Pod,可以直接指定端口,也可以指定后端聲明的端口名字,前提是后端必須聲明端口名,如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.8
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx
spec:
type: ClusterIP
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: http
如上定義好Service并創(chuàng)建過(guò)后,會(huì)在集群生成對(duì)應(yīng)的Service和Endpoints,如下:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc ClusterIP 10.103.74.7080/TCP 12m
$ kubectl get endpoints
NAME ENDPOINTS AGE
nginx-svc 172.16.51.208:80,172.16.51.209:80,172.16.51.237:80 12m
可以看到,endpoints的名字和service的名字一樣,而且我們并沒(méi)有創(chuàng)建endpoints,是由Kubernetes自己創(chuàng)建的。其背后的邏輯是:當(dāng)我們新增Pod或者刪除Pod,是從Endpoints里添加或者剔除,Service本身是不改變的,在同一個(gè)namespace下,Service和Endpoints是通過(guò)名字進(jìn)行關(guān)聯(lián)的。
所以,Endpoints其實(shí)也是一個(gè)對(duì)象,除了由Kubernetes生成還可以自己定義。在實(shí)際工作,有些場(chǎng)景是需要自定義Endpoints的,比如在集群外部署了一個(gè)Redis服務(wù),集群內(nèi)部想通過(guò)Service的方式進(jìn)行訪問(wèn),這時(shí)候就可以通過(guò)自定義Endpints的方式實(shí)現(xiàn),如下:
kind: Service
apiVersion: v1
metadata:
name: custom-endpoint-svc
spec:
ports:
- port: 30018
protocol: TCP
name: http
type: ClusterIP
---
kind: Endpoints
apiVersion: v1
metadata:
name: custom-endpoint-svc
subsets:
- addresses:
- ip: 192.168.32.8
ports:
- port: 8080
name: http
其中,在Endpoints的subsets中用于定義目的地址和端口,需要注意的是:
- Endpoints和Service的metadata.name須保持一致。
- Service.spec.ports[x].name和Endpoints.subsets[x].ports[x].name須保持一致。
NodePort
如果Service的類型是NodePort,表示把Service的端口映射到了Node節(jié)點(diǎn)上,這時(shí)候就可以通過(guò)NodeIP:NodePort進(jìn)行訪問(wèn),可以實(shí)現(xiàn)在外部對(duì)集群內(nèi)應(yīng)用進(jìn)行訪問(wèn)。
NodePort并不是隨便選擇的,當(dāng)安裝好Kubernetes集群后,會(huì)給定一個(gè)默認(rèn)的NodePort范圍,它們是從30000到32767端口,如果沒(méi)有指定特定端口,默認(rèn)會(huì)從這個(gè)區(qū)間范圍內(nèi)隨機(jī)選擇一個(gè)。
一個(gè)NodePort類型的Service定義如下:
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx
spec:
type: NodePort
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: http
除了Type類型不一樣,其他的和定義ClusterIP類型的一樣。
當(dāng)創(chuàng)建完成過(guò)后,生成的Service如下:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc NodePort 10.103.74.7080:32710/TCP 54m
NodePort類型的Service,既可以在集群內(nèi)部通過(guò)ClusterIP:Port進(jìn)行訪問(wèn),也可以在集群外部通過(guò)NodeIP:NodePort進(jìn)行訪問(wèn)。如上,80:32710中,80是集群內(nèi)部端口,32710是暴露出來(lái)的節(jié)點(diǎn)端口。
LoadBalancer
原則上,如果要使用LoadBalancer需要云廠商的支持,因?yàn)楣W(wǎng)IP這些基本都是云廠商來(lái)分配。定義如下:
apiVersion: v1
kind: Service
metadata:
labels:
app: redis
name: redis-master
spec:
clusterIP: 10.96.246.58
externalTrafficPolicy: Local
healthCheckNodePort: 32006
ports:
- name: redis
nodePort: 30997
port: 6379
protocol: TCP
targetPort: 6379
selector:
app: redis
release: redis
role: master
sessionAffinity: None
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 121.189.210.111
這樣就可以直接通過(guò)公網(wǎng)IP和端口(121.189.210.111:6379)進(jìn)行訪問(wèn)。
但是,為了在內(nèi)網(wǎng)環(huán)境也使用LoadBalancer功能,有很多開源方案,其中OpenELB是一個(gè)非常不錯(cuò)的項(xiàng)目,我們下面可以來(lái)簡(jiǎn)單使用一下。
OpenELB
OpenELB 之前叫 PorterLB,是為物理機(jī)(Bare-metal)、邊緣(Edge)和私有化環(huán)境設(shè)計(jì)的負(fù)載均衡器插件,可作為 ?Kubernetes、K3s、KubeSphere 的 LB 插件對(duì)集群外暴露 LoadBalancer 類型的服務(wù),現(xiàn)階段是 CNCF 沙箱項(xiàng)目,核心功能包括:
- 基于 BGP 與 Layer 2 模式的負(fù)載均衡。
- 基于路由器 ECMP 的負(fù)載均衡。
- IP 地址池管理。
- 使用 CRD 進(jìn)行 BGP 配置。
(1)安裝 要安裝OpenELB非常簡(jiǎn)單,直接使用以下命令即可。
$ kubectl apply -f https://raw.githubusercontent.com/openelb/openelb/master/deploy/openelb.yaml
值得注意的是,如果本地不能獲取k8s.gcr.io鏡像(需要翻墻),可以把上面的YAML文件下載下來(lái),根據(jù)里面的注釋做相應(yīng)的鏡像替換即可。
安裝完成過(guò)后,會(huì)生成如下Pod:
$ kubectl get pod -n openelb-system
NAME READY STATUS RESTARTS AGE
openelb-admission-create-ltfcv 0/1 Completed 0 4m19s
openelb-admission-patch-h485q 0/1 Completed 0 4m19s
openelb-keepalive-vip-7mnl7 1/1 Running 0 3m8s
openelb-manager-98764b5ff-4c58s 1/1 Running 0 4m19s
(2)配置 首先,需要為kube-proxy啟動(dòng)strictARP,以便Kubernetes集群中的所有網(wǎng)卡停止響應(yīng)其他網(wǎng)卡的ARP請(qǐng)求,而由OpenELB來(lái)處理ARP請(qǐng)求。
$ kubectl edit configmap kube-proxy -n kube-system
......
ipvs:
strictARP: true
......
然后重啟kube-proxy組件,命令如下:
$ kubectl rollout restart daemonset kube-proxy -n kube-system
如果安裝 OpenELB 的節(jié)點(diǎn)有多個(gè)網(wǎng)卡,則需要指定 OpenELB 在二層模式下使用的網(wǎng)卡,如果節(jié)點(diǎn)只有一個(gè)網(wǎng)卡,則可以跳過(guò)此步驟,假設(shè)安裝了 OpenELB 的 master1 節(jié)點(diǎn)有兩個(gè)網(wǎng)卡(eth0 192.168.0.2 和 ens33 192.168.0.111),并且 eth0 192.168.0.2 將用于 OpenELB,那么需要為 master1 節(jié)點(diǎn)添加一個(gè) annotation 來(lái)指定網(wǎng)卡:
$ kubectl annotate nodes master1 layer2.openelb.kubesphere.io/v1alpha1="192.168.0.2"
接下來(lái)就可以創(chuàng)建一個(gè)EIP對(duì)象來(lái)充當(dāng)OpenELB的IP地址池,YAML清單如下:
apiVersion: network.kubesphere.io/v1alpha2
kind: Eip
metadata:
name: eip-pool
spec:
address: 192.168.205.50-192.168.205.60
protocol: layer2
disable: false
interface: ens33
值得注意的是:
- address用于配置地址池,如果是在layer2層,則需要和集群的地址保持在同一網(wǎng)段。
- protocol用戶指定模式,默認(rèn)是bgp,我們這里指定的是layer2。
- interface用于指定網(wǎng)卡,由于這里是layer2,所以需要指定interface。
- disable表示禁用Eip對(duì)象
創(chuàng)建完成過(guò)后,可以看看Eip地址池狀態(tài):
$ kubectl get eip
NAME CIDR USAGE TOTAL
eip-pool 192.168.205.50-192.168.205.60 1 11
最后,我們創(chuàng)建一個(gè)LoadBalancer類型的Service,如下:
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx
annotations:
lb.kubesphere.io/v1alpha1: openelb
protocol.openelb.kubesphere.io/v1alpha1: layer2
eip.openelb.kubesphere.io/v1alpha2: eip-pool
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: http
注意這里我們?yōu)?Service 添加了幾個(gè) annotations 注解:
- lb.kubesphere.io/v1alpha1: openelb 用來(lái)指定該 Service 使用 OpenELB。
- protocol.openelb.kubesphere.io/v1alpha1: layer2 表示指定 OpenELB 用于 Layer2 模式。
- eip.openelb.kubesphere.io/v1alpha2: eip-pool 用來(lái)指定了 OpenELB 使用的 Eip 對(duì)象,如果未配置此注解,OpenELB 會(huì)自動(dòng)使用與協(xié)議匹配的第一個(gè)可用 Eip 對(duì)象,此外也可以刪除此注解并添加 spec:loadBalancerIP 字段(例如 spec:loadBalancerIP: 192.168.0.108)以將特定 IP 地址分配給 Service。
Service創(chuàng)建過(guò)后,可以查看其狀態(tài)是否正確:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc LoadBalancer 10.103.74.70 192.168.205.50 80:32710/TCP 105m
可以看到在EXTERNAL-IP處為我們分配了一個(gè)Eip,現(xiàn)在就可以直接使用192.168.205.50:80從外部直接訪問(wèn)集群內(nèi)的服務(wù)了。
ExternalName
ExternalName 是 Service 的特例,它沒(méi)有 selector,也沒(méi)有定義任何的端口和 Endpoint。對(duì)于運(yùn)行在集群外部的服務(wù),它通過(guò)返回該外部服務(wù)的別名這種方式來(lái)提供服務(wù)。例如:
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.cdxwcx.com
當(dāng)查詢主機(jī) my-service.prod.svc.cluster.local (后面服務(wù)發(fā)現(xiàn)的時(shí)候我們會(huì)再深入講解)時(shí),集群的 DNS 服務(wù)將返回一個(gè)值為 my.database.cdxwcx.com 的 CNAME 記錄。訪問(wèn)這個(gè)服務(wù)的工作方式與其它的相同,唯一不同的是重定向發(fā)生在 DNS 層,而且不會(huì)進(jìn)行代理或轉(zhuǎn)發(fā)。如果后續(xù)決定要將數(shù)據(jù)庫(kù)遷移到 Kubernetes 集群中,可以啟動(dòng)對(duì)應(yīng)的 Pod,增加合適的 Selector 或 Endpoint,修改 Service 的 type,完全不需要修改調(diào)用的代碼,這樣就完全解耦了。
Headless Service
上面介紹的幾種Service的類型,都會(huì)生成一個(gè)ClusterIP供集群使用,如果在Kubernetes集群中配置了DNS,解析Service Name會(huì)得到ClusterIP。但是在一些特殊的場(chǎng)景下,我們希望直接和應(yīng)用Pod進(jìn)行通信,比如數(shù)據(jù)庫(kù)等有狀態(tài)應(yīng)用,為此,社區(qū)提供了Headless Service,也就是無(wú)頭服務(wù)。
無(wú)頭服務(wù),也就是在配置Service的時(shí)候?qū)pec.clusterIP的值設(shè)置為“None”,如下:
apiVersion: v1
kind: Service
metadata:
name: nginx-headless-service
labels:
name: nginx-headless-service
spec:
clusterIP: None
selector:
name: nginx
ports:
- port: 8000
targetPort: 80
創(chuàng)建過(guò)后,就不會(huì)生成ClusterIP,如下:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 17d
nginx-headless-service ClusterIP None8000/TCP 3s
這種類型的Service主要用在有狀態(tài)服務(wù),后續(xù)在有狀態(tài)應(yīng)用管理的時(shí)候會(huì)感受到具體是如何使用的。
Ingress
上面介紹的Service主要用在集群內(nèi)部,當(dāng)然NodePort和LoadBalancer類型也可以用于外部訪問(wèn),但是它們也有一定的弊端:
- 如果使用NodePort類型,需要維護(hù)好每個(gè)應(yīng)用的端口地址,如果服務(wù)太多就不好管理。
- 如果使用LoadBalancer類型,基本是在云上使用,需要的IP比較多,價(jià)格也比較昂貴。
- 不論是NodePort還是LoadBalancer,都是工作在四層,對(duì)于HTTPS類型的請(qǐng)求無(wú)法直接進(jìn)行SSL校驗(yàn)。
因此,社區(qū)提供了Ingress對(duì)象,為集群提供統(tǒng)一的入口,邏輯如下:
其中Ingress代理的并不是Pod的Service,而是Pod,之所以在配置的時(shí)候是配置的Service,是為了獲取Pod的信息。
Ingress提供七層訪問(wèn)入口,但是Kubernetes的Ingress對(duì)象本身沒(méi)有任何功能,需要借助一些Controller來(lái)實(shí)現(xiàn),常用的有:
- Nginx Ingress Controller
- Traefik Ingress Controller
- Kong Ingress Controller
- APISix Ingress Controller
- .....
最常用的是nginx ingress controller,這里也會(huì)安裝nginx ingress controller來(lái)進(jìn)行介紹。
安裝Nginx Ingress Controller
安裝方式非常簡(jiǎn)單,可以使用Helm和普通的YAML進(jìn)行安裝,由于我們還沒(méi)有學(xué)習(xí)Helm,所以采用YAML清單的方式。
如果網(wǎng)絡(luò)條件好(可以翻墻),可直接使用以下命令安裝:
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.0/deploy/static/provider/cloud/deploy.yaml
如果網(wǎng)絡(luò)條件不好,可以使用:
$ kubectl apply -f https://raw.githubusercontent.com/joker-bai/kubernetes-software-yaml/main/ingress/nginx/ingress-nginx.yaml
安裝完成過(guò)后,資源信息如下:
$ kubectl get all -n ingress-nginx
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-admission-create-kj7ch 0/1 Completed 0 8m16s
pod/ingress-nginx-admission-patch-gtncg 0/1 Completed 0 8m16s
pod/ingress-nginx-controller-d5c4c7ffb-6q5gn 1/1 Running 0 8m16s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.109.208.249 192.168.205.51 80:31306/TCP,443:31079/TCP 8m16s
service/ingress-nginx-controller-admission ClusterIP 10.103.169.99443/TCP 8m16s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 8m16s
NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-nginx-controller-d5c4c7ffb 1 1 1 8m16s
NAME COMPLETIONS DURATION AGE
job.batch/ingress-nginx-admission-create 1/1 5s 8m16s
job.batch/ingress-nginx-admission-patch 1/1 6s 8m16s
在這里,ingress-nginx對(duì)外暴露采用的是LoadBalancer,也就是說(shuō)把域名解析到192.168.205.51地址上,可以直接通過(guò)80端口訪問(wèn)。
暴露第一個(gè)服務(wù)
上面已經(jīng)把ingress-nginx controller部署好了,接下來(lái)就可以創(chuàng)建一個(gè)應(yīng)用進(jìn)行暴露。
我們這里通過(guò)暴露一個(gè)nginx服務(wù),如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.8
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx
spec:
type: ClusterIP
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: nginx.dev.jokerbai.http://fisionsoft.com.cn/article/djdgjcg.html


咨詢
建站咨詢
