k8s-Service(三)

k8s-Service(三)

端口轉(zhuǎn)發(fā)暴露

kubectl port-forward <command>
## 把pod或者其他資源的端口映射到宿主機端口,宿主機端口在前
## pod
kubectl port-forward kubia-bb-44b69 8888:8080 
kubectl port-forward pods/kubia-bb-44b69 8888:8080
# deployment
kubectl port-forward deployment/xxxxx 8888:8080 
# rs
kubectl port-forward rs/kubia-bb 8888:8080 
# rc
kubectl port-forward rc/kubia-bb 8888:8080
# svc
kubectl port-forward svc/kubia-bb 8888:8080

eg:master節(jié)點

[root@master1 ~]# kubectl get po
NAME                 READY   STATUS    RESTARTS   AGE
gateway-test-rmkb2   1/1     Running   1          4h35m
kubia-bb-44b69       1/1     Running   0          14m
kubia-bb-lldsx       1/1     Running   0          14m
kubia-bb-lmhtc       1/1     Running   0          14m
[root@master1 ~]# kubectl get svc
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
gateway-test   ClusterIP   10.99.255.190   <none>        8080/TCP   4h33m
kubernetes     ClusterIP   10.96.0.1       <none>        443/TCP    18d
kubia-bb       ClusterIP   10.108.42.73    <none>        8080/TCP   13m
[root@master1 ~]# kubectl port-forward svc/kubia-bb 8888:8080
Forwarding from 127.0.0.1:8888 -> 8080
Forwarding from [::1]:8888 -> 8080

從新開個shell訪問

[root@master1 ~]# curl localhost:8888
You've hit kubia-bb-44b69
[root@master1 ~]# 

ERROR-socat

訪問時,如果控制臺出現(xiàn)以下錯誤(執(zhí)行kubectl port-forward命令后會hold住,訪問時會有輸出)

[root@master1 ~]# kubectl port-forward kubia-bb-lldsx 8888:8080
Forwarding from 127.0.0.1:8888 -> 8080
Forwarding from [::1]:8888 -> 8080
E0617 15:56:10.760510   15379 portforward.go:400] an error occurred forwarding 8888 -> 8080: error forwarding port 8080 to pod 2e7f0d46cd611de497a3aa179d689e51852392e1e7c279c179adbe675b1d979c, uid : unable to do port forwarding: socat not found

此種情況是socat這個命令沒有安裝。在master和slave節(jié)點都裝上就ojbk了。

$ yum install -y socat

Service暴露

yaml資源定義及演示

yaml定義

apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: kubia

apply兩個pod,用來測試service

[root@master1 kubeyaml]# kubectl apply -f kubia-rs.yaml
replicaset.apps/kubia created
[root@master1 kubeyaml]# kubectl get rs
NAME    DESIRED   CURRENT   READY   AGE
kubia   2         2         2       59m
[root@master1 kubeyaml]# kubectl get po
NAME          READY   STATUS    RESTARTS   AGE
kubia-gc4ds   1/1     Running   0          16s
kubia-llv9w   1/1     Running   0          16s
[root@master1 kubeyaml]#

通過這節(jié)的yaml定義,創(chuàng)建service,并在master節(jié)點通過curl訪問

[root@master1 kubeyaml]# kubectl apply -f kubia-service.yaml
service/kubia created
[root@master1 kubeyaml]# kubectl get service
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   4d
kubia        ClusterIP   10.106.29.201   <none>        80/TCP    29s
[root@master1 kubeyaml]# curl 10.106.29.201
You've hit kubia-llv9w
[root@master1 kubeyaml]# curl 10.106.29.201
You've hit kubia-gc4ds
[root@master1 kubeyaml]# 

親和性

普通的方式,請求來之后,即使是同一個客戶端的請求,也可能會打到不同的Pod節(jié)點上,通常來講這是沒有問題的。但是如果你的服務很特殊,你希望來自某個客戶端的請求每次都打到同一個Pod。你可以設(shè)置服務的親和性,service.spec.sessionAffinity設(shè)置為ClientIp,而不是None,None是默認值。

apiVersion: v1
kind: Service
spec:
 sessionAffinity: ClientIp
 ...

這種方式將會使服務代理將來自同 一個 client IP 的所有請求轉(zhuǎn)發(fā)至同 一個 pod 上。

Kubernetes 僅僅支持兩種形式的會話親和性服務: None 和 Client工P。你或許驚訝競?cè)徊恢С只?cookie 的會話親和性的選項,但是你要了解Kubernetes 服務不是在 HTTP 層面上工作。服務處理 TCP 和 UDP 包,并不關(guān)心其中的載荷內(nèi)容。 因為 cookie 是 HTTP 協(xié)議中的一部分,服務并不知道它們,這就是為什么會話 親和性不能基于cookie。

同一個服務暴露多個端口

service.spec.ports是個數(shù)組,暴露多個端口直接寫就好

apiVersion: v1
kind: Service
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: https
    port: 443
    targetPort: 8443

基于端口命名暴露

比如你有個命名的端口Pod

## 其他配置和kubia-rs.yaml一樣,只改了ports部分
spec:
  template:
    spec:
      containers:
        ports:
        - name: http              ## 端口名為http
          containerPort: 8080

service也改為基于端口命名的

apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  ports:
  - name: http              
    port: 80
    targetPort: http        ### 暴露80端口,指向目標名稱為http的端口,也就是上面的8080
  selector:
    app: kubia

刪除已有svc,rs,測試基于端口命名的service

[root@master1 kubeyaml]# kubectl delete rs kubia
replicaset.apps "kubia" deleted
[root@master1 kubeyaml]# kubectl delete svc kubia
service "kubia" deleted
[root@master1 kubeyaml]# kubectl apply -f kubia-rs-port-name.yaml
replicaset.apps/kubia created
[root@master1 kubeyaml]# kubectl get po
NAME          READY   STATUS    RESTARTS   AGE
kubia-n6sgn   1/1     Running   0          50s
kubia-sx7wp   1/1     Running   0          50s
[root@master1 kubeyaml]# kubectl apply -f kubia-service-port-name.yaml 
service/kubia created
[root@master1 kubeyaml]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP   4d1h
kubia        ClusterIP   10.110.168.137   <none>        80/TCP    4s
[root@master1 kubeyaml]# curl 10.110.168.137
You've hit kubia-sx7wp
[root@master1 kubeyaml]# curl 10.110.168.137
You've hit kubia-n6sgn
[root@master1 kubeyaml]#

基于端口命名的最大好處就是即使更換端口號也無須更改服務的spec.

Endpoints

介紹

查看Service

[root@master1 ~]# kubectl describe svc kubia
Name:              kubia
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=kubia         
Type:              ClusterIP
IP:                10.110.168.137
Port:              http  80/TCP
TargetPort:        http/TCP
Endpoints:         100.109.35.205:8080,100.109.35.216:8080,100.109.35.222:8080
Session Affinity:  None
Events:            <none>
[root@master1 ~]# 

其中selector是用于創(chuàng)建endpoint列表的服務Pod選擇器,Endpoints代表服務endpoint的Pod的IP和端口列表。

Endpoints資源就是暴露一個服務的IP地址和端口列表。

查看之前的服務自動生成的Endpoints

[root@master1 ~]# kubectl get svc kubia
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubia        ClusterIP   10.110.168.137   <none>        80/TCP    4h23m
[root@master1 ~]# kubectl get endpoints kubia
NAME         ENDPOINTS                                                     AGE
kubia        100.109.35.205:8080,100.109.35.216:8080,100.109.35.222:8080   4h24m
[root@master1 ~]# kubectl get po -o wide
NAME                 READY   STATUS    RESTARTS   AGE     IP     
kubia-m982c          1/1     Running   0          158m    100.109.35.205  
kubia-n6sgn          1/1     Running   0          4h28m   100.109.35.216 
kubia-sx7wp          1/1     Running   0          4h28m   100.109.35.222 
[root@master1 ~]# 

可以看到Service kubia有一個同名的Endpoints,Endpoints的三個ip指向了三個Pod.

選擇器用于構(gòu)建 IP 和端口列表,然后存儲在 Endpoint 資源中。當客戶端連 接到服務時,服務代理選擇這些 IP 和端口對(保存在Endpoints)中的一個,并將傳入連接重定向到監(jiān)聽的服務器。

手動配置值Endpoints資源

你可能已經(jīng)意識到這一點,Service與 Endpoint 解耦后,可以分別手動配置和 更新它們。 如果創(chuàng)建了不包含 pod選擇器的服務,Kubemetes 將不會創(chuàng)建 Endpoint 資源(畢 竟,缺少選擇器,將不會知道服務中包含哪些 pod)。這樣就需要創(chuàng)建 Endpoint 資源 來指定該服務的 endpoint 列表。 要使用手動配置 endpoint 的方式創(chuàng)建服務,需要創(chuàng)建服務和 Endpoint 資源

創(chuàng)建沒有Pod選擇器的Service

apiVersion: v1
kind: Service
metadata:
  name: kubia-svc       ### 服務名字必須和Endpoints名字匹配
spec:
  ports:
  - port: 80
[root@master1 kubeyaml]# kubectl apply -f kubia-endpoints-service.yaml 
service/kubia-svc created
[root@master1 kubeyaml]# kubectl get svc 
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP   4d6h
kubia        ClusterIP   10.110.168.137   <none>        80/TCP    5h20m
kubia-svc    ClusterIP   10.104.51.11     <none>        80/TCP    11s
[root@master1 kubeyaml]#

yaml定義

創(chuàng)建Endpoints(使用之前存在的幾個Pod的ip)

[root@master1 kubeyaml]# kubectl get po -o wide
NAME                 READY   STATUS    RESTARTS   AGE     IP      
kubia-m982c          1/1     Running   0          3h38m   100.109.35.205 
kubia-n6sgn          1/1     Running   0          5h28m   100.109.35.216 
kubia-sx7wp          1/1     Running   0          5h28m   100.109.35.222 
[root@master1 kubeyaml]# 
apiVersion: v1
kind: Endpoints
metadata:
  name: kubia-svc         ### Endpoints名字必須和Service名字匹配
subsets:
  - addresses:            ### Pod的IP列表
    - ip: 100.109.35.205   
    - ip: 100.109.35.216
    ports:
    - port: 8080           ### Pod的端口

apply這個Endpoint資源

[root@master1 kubeyaml]# kubectl apply -f kubia-endpoints.yaml
endpoints/kubia-svc created
[root@master1 kubeyaml]# kubectl get endpoints kubia-svc
NAME         ENDPOINTS                                                     AGE
kubia-svc    100.109.35.205:8080,100.109.35.216:8080                       1m44s
[root@master1 kubeyaml]#

訪問服務試試

[root@master1 kubeyaml]# kubectl get po -o wide
NAME                 READY   STATUS    RESTARTS   AGE     IP      
kubia-m982c          1/1     Running   0          3h38m   100.109.35.205 
kubia-n6sgn          1/1     Running   0          5h28m   100.109.35.216 
kubia-sx7wp          1/1     Running   0          5h28m   100.109.35.222
[root@master1 kubeyaml]# kubectl get svc kubia-svc
NAME        TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kubia-svc   ClusterIP   10.104.51.11   <none>        80/TCP    12m
[root@master1 kubeyaml]# curl 10.104.51.11
You've hit kubia-n6sgn
[root@master1 kubeyaml]# curl 10.104.51.11
You've hit kubia-m982c
[root@master1 kubeyaml]#

因為我們的Endpoints只給了兩個節(jié)點,所以無論我們curl多少次,都只會打到100.109.35.205和100.109.35.216這兩個Pod.

NodePort暴露服務

將一組pod公開給外部客戶端的第一種方法是創(chuàng)建一個服務并將其類型設(shè)置為 NodePort。 通過創(chuàng)建NodePort服務, 可以讓k8s在其所有節(jié)點上保留一 個端口(所有節(jié)點上都使用相同的端口號), 并將傳入的連接轉(zhuǎn)發(fā)給作為服務部分的 pod。

這與常規(guī)服務類似(它們的實際類型是ClusterIP), 但是不僅可以通過服務 的內(nèi)部集群IP訪問NodePort服務, 還可以通過任何節(jié)點的IP和預留節(jié)點端口訪問NodePort服務.

yaml定義

apiVersion: v1
kind: Service
metadata:
  name: kubia-nodeport
spec:
  type: NodePort           ### 設(shè)置服務類型為NodePort
  ports:
  - port: 80               ### 服務集群IP的端口號
    targetPort: 8080       ### 背后Pod的目標端口號
    nodePort: 30123        ### 通過集群節(jié)點Node的30123端口可以訪問該服務
  selector:
    app: kubia

apply NodePort服務

[root@master1 kubeyaml]# kubectl apply -f kubia-service-nodeport.yaml
service/kubia-nodeport created
[root@master1 kubeyaml]# kubectl get svc kubia-nodeport
NAME             TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubia-nodeport   NodePort   10.110.69.140   <none>        80:30123/TCP   11s
[root@master1 kubeyaml]# 

查看集群節(jié)點Node的IP(只保留了重要輸出)

[root@master1 kubeyaml]# kubectl get node -o wide
NAME             STATUS   ROLES    AGE   VERSION   INTERNAL-IP
master1.wt.com   Ready    master   27d   v1.19.0   192.168.2.14 
node1.wt.com     Ready    <none>   27d   v1.19.0   192.168.2.15
[root@master1 kubeyaml]# 

好了,從上面兩段輸出中,可以得到服務集群IP10.110.69.140和集群節(jié)點Node的IP192.168.2.15.我這里k8s是虛擬機節(jié)點安裝的,Node節(jié)點的IP實際上就是虛擬機的IP.

通過兩種方式訪問服務

[root@master1 kubeyaml]# curl 10.110.69.140
You've hit kubia-sx7wp
[root@master1 kubeyaml]# curl 192.168.2.15:30123
You've hit kubia-sx7wp
[root@master1 kubeyaml]# curl 192.168.2.14:30123
You've hit kubia-sx7wp
[root@master1 kubeyaml]#

可以看到,從服務集群IP,和所有節(jié)點IP:30123訪問都能正確打到對應的Pod.通過節(jié)點Node訪問時,直接在宿主機(也就是外部)就可以直接訪問了。

通過Node節(jié)點訪問流程

service-nodeport-process.png

LoadBalance暴露服務

LoadBalancer,該服務將從k8s集群的基礎(chǔ)架構(gòu)獲取負載均衡平衡器,如果k8s在不支持Load Badancer服務的環(huán)境中運行時,不會調(diào)配負載平衡器,但該服務仍將表現(xiàn)得像一 個NodePort服務。 這是因為Load Badancer服務是NodePort服務的擴展

yaml定義

apiVersion: v1
kind: Service
metadata:
  name: kubia-lb
spec:
  type: LoadBalancer            ### Service類型指定為LB
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 32143              ### LB是nodePort的擴展,所以也是由這個值指定端口,如果不指定,會隨機給
  selector:
    app: kubia

apply這個LB

[root@master1 kubeyaml]# kubectl apply -f kubia-service-lb.yaml
service/kubia-lb created
[root@master1 kubeyaml]# kubectl get svc kubia-lb
NAME       TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubia-lb   LoadBalancer   10.100.161.153   <pending>     80:32143/TCP   2m16s

我這個可能是不支持LB服務吧,這個EXTERNAL-IP一直是pending狀態(tài)。除開這個,其他都跟NodePort模式一樣,服務集群IP可以訪問Pod,集群Node節(jié)點IP也可以訪問Pod。

親和性失效?

瀏覽器訪問過程中,你可能會發(fā)現(xiàn)每次瀏覽器都會碰到同一個pod(NodePort也會這樣), 我們不是有三個Pod嗎,我們使用curl的時候都是輪詢的,為什么到瀏覽器就不行了?
你會發(fā)現(xiàn)你如果重新開一個窗口,就會碰到另一個Pod.但是也是一直碰到這個.

解答: 瀏覽器使用keep-alive連接, 并通過單個連接發(fā)送所有請求,而curl每次都會打開一個新連接。
服務在連接級別工作, 所以當首次打開與服務的連接時, 會選擇一個隨機集群,
然后將屬于該連接的所有網(wǎng)絡數(shù)據(jù)包全部發(fā)送到單個集群。 
即使會話親和性設(shè)置為None, 用戶也會始終使用相同的 pod (直到連接關(guān)閉)

訪問流程

訪問流程和NodePort很像,只是外部套了一個負載均衡器。

這個負載均衡器的IP就是上面一直pending的那個EXTERNAL-IP.我這個沒法演示了,貼個圖。

service-lb-process.png

外部連接的網(wǎng)絡跳數(shù)

當外部客戶端通過節(jié)點端口連接到服務時(這也包括先通過負載均衡器時的情 況), 隨機選擇的pod并不一定在接收連接的同 一節(jié)點上運行,對照上面流程圖,比如說通過節(jié)點1訪問Pod,由于會經(jīng)過服務,所以可能會打到節(jié)點2上的Pod。所以可能需要額外的網(wǎng)絡跳轉(zhuǎn)才能到達Pod。

如果想避免這種跳數(shù),可以設(shè)置Service.spec.externalTrafficPolicy=Local來使用本地Pod,保證哪個節(jié)點接收的請求,就由哪臺節(jié)點的Pod處理。

如果沒有本地pod存在, 則連接將掛起, 它不會將其轉(zhuǎn)發(fā)到隨機的全局pod.

對負載均衡不友好,如下圖

service-policy-local.png

客戶端IP

通常,當集群內(nèi)的客戶端連接到服務時,支持服務的pod可以獲取客戶端的IP地址。但是,當通過節(jié)點端口接收到連接時,由于對數(shù)據(jù)包執(zhí)行了源網(wǎng)絡地址轉(zhuǎn)換(SNAT), 因此數(shù)據(jù)包的源IP將發(fā)生更改。后端的pod無法看到實際的客戶端IP, 
這對于某些需要了解客戶端IP的應用程序來說可能是個問題。例如,對于Web服務器,這意味著訪問日志無法顯示瀏覽器的IP。
剛剛描述的local外部流量策略會有客戶端IP的保留,因為在接收連接的節(jié)點和托管目標pod的節(jié)點之間沒有額外的跳躍(不執(zhí)行SNAT)。

Ingress暴露服務

Ingress 在網(wǎng)絡棧 (HTTP) 的應用層(第7層)工作,并且可以提供一些服務不能實現(xiàn)的功能,諸如基于 cookie 的會話親和性 (session affinity) 等功能

為什么需要Ingress,一個重要的原因是每個 LoadBalancer 服務都需要自己的負載均衡器, 以及 獨有的公有 IP 地址, 而 Ingress 只需要一個公網(wǎng) IP 就能為許多服務提供訪問。 當客 戶端向 Ingress 發(fā)送 HTTP 請求時, Ingress 會根據(jù)請求的主機名和路徑?jīng)Q定請求轉(zhuǎn) 發(fā)到的服務.

ingress-process.png

Ingress控制器

使用Ingress必須要安裝Ingress控制器,具體的安裝方式可以在【k8安裝】這篇筆記里面看到。

yaml定義

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kubia-ingress
spec :
  rules:
  - host: kubia.example.com      ### Ingress將域名映射到你的服務
    http:
      paths:
      - path: /kubia             ### 所有/kubia的請求,全部發(fā)送到kubia-nodeport服務的80端
        pathType: Prefix
        backend:
          service:
            name: kubia-nodeport   ### Nodeport那節(jié)創(chuàng)建的Service
            port: 
              number: 80

TLS

ingress部署之后有點問題,沒有Address

[root@master1 ~]# kubectl get ingress
NAME                     CLASS    HOSTS               ADDRESS   PORTS   AGE
kubia-ingress   <none>   kubia.example.com             80      99m
[root@master1 ~]# 

好了,TLS就暫且略過了,后續(xù)有空再來搞它。

Headless Service

通過設(shè)置service.spec.clusterIp=None來使服務成為Headless Service,k8s不會為其分配集群IP。

yaml定義

apiVersion: v1
kind: Service
metadata:
  name: kubia-headless
spec:
  clusterIP: None        ## 設(shè)置為Headless Service 
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: kubia

演示

[root@master1 kubeyaml]# vi kubia-service-headless.yaml 
[root@master1 kubeyaml]# kubectl apply -f kubia-service-headless.yaml
service/kubia-headless created
[root@master1 kubeyaml]# kubectl get svc
NAME                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes                ClusterIP   10.96.0.1        <none>        443/TCP        14d
kubia-headless            ClusterIP   None             <none>        80/TCP         4s
[root@master1 kubeyaml]# kubectl describe svc kubia-headless
Name:              kubia-headless
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=kubia
Type:              ClusterIP
IP:                None
Port:              <unset>  80/TCP
TargetPort:        8080/TCP
Endpoints:         100.109.35.233:8080,100.109.35.240:8080,100.109.35.241:8080
Session Affinity:  None
Events:            <none>
[root@master1 kubeyaml]# kubectl get endpoints
NAME                      ENDPOINTS                                                     AGE
kubernetes                192.168.2.14:6443                                             14d
kubia-headless            100.109.35.233:8080,100.109.35.240:8080,100.109.35.241:8080   35s
[root@master1 kubeyaml]#

故障排查TIPS

? 首先, 確保從集群內(nèi)連接到服務的集群IP,而不是從外部。
? 不要通過ping服務IP 來判斷服務是否可訪問(請記住,服務的集群IP是虛擬IP, 是無法ping通的)。
? 如果已經(jīng)定義了就緒探針, 請確保 它返回成功;否則該pod不會成為服務的一部分 。
? 要確認某個容器是服務的一部分,請使用kubectl ge七 endpoints來檢查相應的端點對象。
? 如果嘗試通過FQDN或其中一部分來訪問服務(例如,myservice.mynamespace.svc.cluster.local或
  myservice.mynamespace), 但并不起作用,請查看是否可以使用其集群IP而不是FQDN來訪問服務 。
? 檢查是否連接到服務公開的端口,而不是目標端口 。
? 嘗試直接連接到podIP以確認pod正在接收正確端口上的連接。
? 如果甚至無法通過pod的IP 訪問應用,請確保應用不是僅綁定 到本地主機
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容