8--k8s之service和ingress詳解


kubernetes的流量負載組件:Service和Ingress

一、 Service介紹

在kubernetes中,pod是應用程序的載體,我們可以通過pod的ip來訪問應用程序,但是pod的ip地址不是固定的,這也就意味着不方便直接采用pod的ip對服務進行訪問。

為了解決這個問題,kubernetes提供了Service資源,Service會對提供同一個服務的多個pod進行聚合,並且提供一個統一的入口地址。通過訪問Service的入口地址就能訪問到后面的pod服務。

Service在很多情況下只是一個概念,真正起作用的其實是kube-proxy服務進程,每個Node節點上都運行着一個kube-proxy服務進程。當創建Service的時候會通過api-server向etcd寫入創建的service的信息,而kube-proxy會基於監聽的機制發現這種Service的變動,然后它會將最新的Service信息轉換成對應的訪問規則

# 10.97.97.97:80 是service提供的訪問入口
# 當訪問這個入口的時候,可以發現后面有三個pod的服務在等待調用,
# kube-proxy會基於rr(輪詢)的策略,將請求分發到其中一個pod上去
# 這個規則會同時在集群內的所有節點上都生成,所以在任何一個節點上訪問都可以。
[root@node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.97.97.97:80 rr
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0

kube-proxy目前支持三種工作模式:

userspace 模式

userspace模式下,kube-proxy會為每一個Service創建一個監聽端口,發向Cluster IP的請求被Iptables規則重定向到kube-proxy監聽的端口上,kube-proxy根據LB算法選擇一個提供服務的Pod並和其建立鏈接,以將請求轉發到Pod上。 該模式下,kube-proxy充當了一個四層負責均衡器的角色。由於kube-proxy運行在userspace中,在進行轉發處理時會增加內核和用戶空間之間的數據拷貝,雖然比較穩定,但是效率比較低。

iptables 模式

iptables模式下,kube-proxy為service后端的每個Pod創建對應的iptables規則,直接將發向Cluster IP的請求重定向到一個Pod IP。 該模式下kube-proxy不承擔四層負責均衡器的角色,只負責創建iptables規則。該模式的優點是較userspace模式效率更高,但不能提供靈活的LB策略,當后端Pod不可用時也無法進行重試。

ipvs 模式

ipvs模式和iptables類似,kube-proxy監控Pod的變化並創建相應的ipvs規則。ipvs相對iptables轉發效率更高。除此以外,ipvs支持更多的LB算法。

# 此模式必須安裝ipvs內核模塊,否則會降級為iptables
# 開啟ipvs
[root@k8s-master01 ~]# kubectl edit cm kube-proxy -n kube-system
# 修改mode: "ipvs"
[root@k8s-master01 ~]# kubectl delete pod -l k8s-app=kube-proxy -n kube-system
[root@node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.97.97.97:80 rr
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0

二、 Service類型

k8s默認的配置文件

[root@k8s-m-01 ~]# cat /etc/kubernetes/manifests/kube-apiserver.yaml

Service的資源清單文件:

kind: Service  # 資源類型
apiVersion: v1  # 資源版本
metadata: # 元數據
  name: service # 資源名稱
  namespace: dev # 命名空間
spec: # 描述
  selector: # 標簽選擇器,用於確定當前service代理哪些pod,只支持精准匹配
    app: nginx
  type: # Service類型,指定service的訪問方式,如果定義NodePort,就要定義他的端口
  clusterIP:  # 虛擬服務的ip地址
  sessionAffinity: # session親和性,支持ClientIP、None兩個選項
  ports: # 端口信息
    - protocol: TCP 
      port: 3017  # service端口
      targetPort: 5003 # pod端口
      nodePort: 31122 # 主機端口,默認是30000-32700
      protocal: TCP     #反向代理的網絡類型
#如果代理多個IP,要用名字區別開來
kind: Service
apiVersion: v1
metadata:
  name: test-svc
  namespace: default
spec:
  ports:
    - port: 80    #service負載均衡器的端口
      targetPort: 80     #容器的端口
      name: http
 - port: 443
      targetPort: 443
      name: https
  • ClusterIP:默認值,它是Kubernetes系統自動分配的虛擬IP,只能在集群內部訪問
  • NodePort:將Service通過指定的Node上的端口暴露給外部,通過此方法,就可以在集群外部訪問服務
  • LoadBalancer:使用外接負載均衡器完成到服務的負載分發,注意此模式需要外部雲環境支持
  • ExternalName: 把集群外部的服務引入集群內部,直接使用

三、 Service使用

1. 實驗環境准備

在使用service之前,首先利用Deployment創建出3個pod,注意要為pod設置app=nginx-pod的標簽

創建deployment.yaml,內容如下:

apiVersion: apps/v1
kind: Deployment      
metadata:
  name: pc-deployment
  namespace: dev
spec: 
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
[root@k8s-master01 ~]# kubectl create -f deployment.yaml
deployment.apps/pc-deployment created

# 查看pod詳情
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide --show-labels
NAME                             READY   STATUS     IP            NODE     LABELS
pc-deployment-66cb59b984-8p84h   1/1     Running    10.244.1.39   node1    app=nginx-pod
pc-deployment-66cb59b984-vx8vx   1/1     Running    10.244.2.33   node2    app=nginx-pod
pc-deployment-66cb59b984-wnncx   1/1     Running    10.244.1.40   node1    app=nginx-pod

# 為了方便后面的測試,修改下三台nginx的index.html頁面(三台修改的IP地址不一致)
# kubectl exec -it pc-deployment-66cb59b984-8p84h -n dev /bin/sh
# echo "10.244.1.39" > /usr/share/nginx/html/index.html

#修改完畢之后,訪問測試
[root@k8s-master01 ~]# curl 10.244.1.39
10.244.1.39
[root@k8s-master01 ~]# curl 10.244.2.33
10.244.2.33
[root@k8s-master01 ~]# curl 10.244.1.40
10.244.1.40

2. ClusterIP類型的Service

k8s默認的類型

創建service-clusterip.yaml文件

apiVersion: v1
kind: Service
metadata:
  name: service-clusterip
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: 10.97.97.97 # service的ip地址,如果不寫,默認會生成一個
  type: ClusterIP
  ports:
  - port: 80  # Service端口       
    targetPort: 80 # pod端口
# 創建service
[root@k8s-master01 ~]# kubectl create -f service-clusterip.yaml
service/service-clusterip created

# 查看service
[root@k8s-master01 ~]# kubectl get svc -n dev -o wide
NAME                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service-clusterip   ClusterIP   10.97.97.97   <none>        80/TCP    13s   app=nginx-pod

# 查看service的詳細信息
# 在這里有一個Endpoints列表,里面就是當前service可以負載到的服務入口
[root@k8s-master01 ~]# kubectl describe svc service-clusterip -n dev
Name:              service-clusterip
Namespace:         dev
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP:                10.97.97.97
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.39:80,10.244.1.40:80,10.244.2.33:80
Session Affinity:  None
Events:            <none>

# 查看ipvs的映射規則
[root@k8s-master01 ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0

# 訪問10.97.97.97:80觀察效果
[root@k8s-master01 ~]# curl 10.97.97.97:80
10.244.2.33

Endpoint

Endpoint是kubernetes中的一個資源對象,存儲在etcd中,用來記錄一個service對應的所有pod的訪問地址,它是根據service配置文件中selector描述產生的。

一個Service由一組Pod組成,這些Pod通過Endpoints暴露出來,Endpoints是實現實際服務的端點集合。換句話說,service和pod之間的聯系是通過endpoints實現的。

負載分發策略

對Service的訪問被分發到了后端的Pod上去,目前kubernetes提供了兩種負載分發策略:

  • 如果不定義,默認使用kube-proxy的策略,比如隨機、輪詢

  • 基於客戶端地址的會話保持模式,即來自同一個客戶端發起的所有請求都會轉發到固定的一個Pod上

    此模式可以使在spec中添加sessionAffinity:ClientIP選項

# 查看ipvs的映射規則【rr 輪詢】
[root@k8s-master01 ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0

# 循環訪問測試
[root@k8s-master01 ~]# while true;do curl 10.97.97.97:80; sleep 5; done;
10.244.1.40
10.244.1.39
10.244.2.33
10.244.1.40
10.244.1.39
10.244.2.33

# 修改分發策略----sessionAffinity: ClientIP

# 查看ipvs規則【persistent 代表持久】
[root@k8s-master01 ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr persistent 10800
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0

# 循環訪問測試
[root@k8s-master01 ~]# while true;do curl 10.97.97.97; sleep 5; done;
10.244.2.33
10.244.2.33
10.244.2.33
  
# 刪除service
[root@k8s-master01 ~]# kubectl delete -f service-clusterip.yaml
service "service-clusterip" deleted

3. HeadLiness類型的Service

在某些場景中,開發人員可能不想使用Service提供的負載均衡功能,而希望自己來控制負載均衡策略,針對這種情況,kubernetes提供了HeadLiness Service,這類Service不會分配Cluster IP,如果想要訪問service,只能通過service的域名進行查詢。

創建service-headliness.yaml

apiVersion: v1
kind: Service
metadata:
  name: service-headliness
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None # 將clusterIP設置為None,即可創建headliness Service
  type: ClusterIP
  ports:
  - port: 80    
    targetPort: 80
# 創建service
[root@k8s-master01 ~]# kubectl create -f service-headliness.yaml
service/service-headliness created

# 獲取service, 發現CLUSTER-IP未分配
[root@k8s-master01 ~]# kubectl get svc service-headliness -n dev -o wide
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service-headliness   ClusterIP   None         <none>        80/TCP    11s   app=nginx-pod

# 查看service詳情
[root@k8s-master01 ~]# kubectl describe svc service-headliness  -n dev
Name:              service-headliness
Namespace:         dev
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP:                None
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.39:80,10.244.1.40:80,10.244.2.33:80
Session Affinity:  None
Events:            <none>

# 查看域名的解析情況
[root@k8s-master01 ~]# kubectl exec -it pc-deployment-66cb59b984-8p84h -n dev /bin/sh
/ # cat /etc/resolv.conf
nameserver 10.96.0.10
search dev.svc.cluster.local svc.cluster.local cluster.local

[root@k8s-master01 ~]# dig @10.96.0.10 service-headliness.dev.svc.cluster.local
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.40
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.39
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.2.33

4. NodePort類型的Service

在之前的樣例中,創建的Service的ip地址只有集群內部才可以訪問,如果希望將Service暴露給集群外部使用,那么就要使用到另外一種類型的Service,稱為NodePort類型。NodePort的工作原理其實就是將service的端口映射到Node的一個端口上,然后就可以通過NodeIp:NodePort來訪問service了。

創建service-nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: dev
spec:
  selector:
    app: nginx-pod
  type: NodePort # service類型
  ports:
  - port: 80
    nodePort: 30002 # 指定綁定的node的端口(默認的取值范圍是:30000-32767), 如果不指定,會默認分配
    targetPort: 80
#指定綁定的node端口可以修改默認值
[root@k8s-m-01 ~]# vim /etc/kubernetes/manifests/kube-apiserver.yaml
--service-node-port-range=0-1000
[root@k8s-m-01 ~]# kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml

# 創建service
[root@k8s-master01 ~]# kubectl create -f service-nodeport.yaml
service/service-nodeport created

# 查看service
[root@k8s-master01 ~]# kubectl get svc -n dev -o wide
NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)       SELECTOR
service-nodeport   NodePort   10.105.64.191   <none>        80:30002/TCP  app=nginx-pod

# 接下來可以通過電腦主機的瀏覽器去訪問集群中任意一個nodeip的30002端口,即可訪問到pod

5. LoadBalancer類型的Service

彈性IP

LoadBalancer和NodePort很相似,目的都是向外部暴露一個端口,區別在於LoadBalancer會在集群的外部再來做一個負載均衡設備,而這個設備需要外部環境支持的,外部服務發送到這個設備上的請求,會被設備負載之后轉發到集群中。
必須要在公有雲上創建集群彈性ip

6 .ExternalName類型的Service

ExternalName類型的Service用於引入集群外部的服務,它通過externalName屬性指定外部一個服務的地址,然后在集群內部訪問此service就可以訪問到外部的服務了。

apiVersion: v1
kind: Service
metadata:
  name: service-externalname
  namespace: dev
spec:
  type: ExternalName # service類型
  externalName: www.baidu.com  #改成ip地址也可以
# 創建service
[root@k8s-master01 ~]# kubectl  create -f service-externalname.yaml
service/service-externalname created

# 域名解析
[root@k8s-master01 ~]# dig @10.96.0.10 service-externalname.dev.svc.cluster.local
service-externalname.dev.svc.cluster.local. 30 IN CNAME www.baidu.com.
www.baidu.com.          30      IN      CNAME   www.a.shifen.com.
www.a.shifen.com.       30      IN      A       39.156.66.18
www.a.shifen.com.       30      IN      A       39.156.66.14

四、 Ingress介紹

在前面課程中已經提到,Service對集群之外暴露服務的主要方式有兩種:NotePort和LoadBalancer,但是這兩種方式,都有一定的缺點:

  • NodePort方式的缺點是會占用很多集群機器的端口,那么當集群服務變多的時候,這個缺點就愈發明顯
  • LB方式的缺點是每個service需要一個LB,浪費、麻煩,並且需要kubernetes之外設備的支持

基於這種現狀,kubernetes提供了Ingress資源對象,Ingress只需要一個NodePort或者一個LB就可以滿足暴露多個Service的需求。工作機制大致如下圖表示:

實際上,Ingress相當於一個7層的負載均衡器,是kubernetes對反向代理的一個抽象,它的工作原理類似於Nginx,可以理解成在Ingress里建立諸多映射規則,Ingress Controller通過監聽這些配置規則並轉化成Nginx的反向代理配置 , 然后對外部提供服務。在這里有兩個核心概念:

  • ingress:kubernetes中的一個對象,作用是定義請求如何轉發到service的規則
  • ingress controller:具體實現反向代理及負載均衡的程序,對ingress定義的規則進行解析,根據配置的規則來實現請求轉發,實現方式有很多,比如Nginx, Contour, Haproxy等等

Ingress(以Nginx為例)的工作原理如下:

  1. 用戶編寫Ingress規則,說明哪個域名對應kubernetes集群中的哪個Service
  2. Ingress控制器動態感知Ingress服務規則的變化,然后生成一段對應的Nginx反向代理配置
  3. Ingress控制器會將生成的Nginx配置寫入到一個運行着的Nginx服務中,並動態更新
  4. 到此為止,其實真正在工作的就是一個Nginx了,內部配置了用戶定義的請求轉發規則

五、 Ingress使用

1 .環境准備

搭建ingress環境

# 創建文件夾
[root@k8s-master01 ~]# mkdir ingress-controller
[root@k8s-master01 ~]# cd ingress-controller/

# 獲取ingress-nginx,本次案例使用的是0.30版本
[root@k8s-master01 ingress-controller]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
[root@k8s-master01 ingress-controller]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml

# 修改mandatory.yaml文件中的倉庫
# 修改quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
# 為quay-mirror.qiniu.com/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
# 創建ingress-nginx
[root@k8s-master01 ingress-controller]# kubectl apply -f ./

# 查看ingress-nginx
[root@k8s-master01 ingress-controller]# kubectl get pod -n ingress-nginx
NAME                                           READY   STATUS    RESTARTS   AGE
pod/nginx-ingress-controller-fbf967dd5-4qpbp   1/1     Running   0          12h

# 查看service
[root@k8s-master01 ingress-controller]# kubectl get svc -n ingress-nginx
NAME            TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx   NodePort   10.98.75.163   <none>        80:32240/TCP,443:31335/TCP   11h

准備service和pod

為了后面的實驗比較方便,創建如下圖所示的模型

創建tomcat-nginx.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
​
---
​
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tomcat-pod
  template:
    metadata:
      labels:
        app: tomcat-pod
    spec:
      containers:
      - name: tomcat
        image: tomcat:8.5-jre10-slim
        ports:
        - containerPort: 8080
​
---
​
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80
​
---
​
apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
  namespace: dev
spec:
  selector:
    app: tomcat-pod
  clusterIP: None
  type: ClusterIP
  ports:
  - port: 8080
    targetPort: 8080
# 創建
[root@k8s-master01 ~]# kubectl create -f tomcat-nginx.yaml
​
# 查看
[root@k8s-master01 ~]# kubectl get svc -n dev
NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
nginx-service    ClusterIP   None         <none>        80/TCP     48s
tomcat-service   ClusterIP   None         <none>        8080/TCP   48s

2 .Http代理

創建ingress-http.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-http
  namespace: dev
spec:
  rules:
  - host: nginx.itheima.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-service
          servicePort: 80
  - host: tomcat.itheima.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 8080
# 創建
[root@k8s-master01 ~]# kubectl create -f ingress-http.yaml
ingress.extensions/ingress-http created

# 查看
[root@k8s-master01 ~]# kubectl get ing ingress-http -n dev
NAME           HOSTS                                  ADDRESS   PORTS   AGE
ingress-http   nginx.itheima.com,tomcat.itheima.com             80      22s

[root@k8s-m-01 ~]# kubectl get svc -n ingress-nginx
...
post(80:32240)/TCP,443:31335/TCP

# 查看詳情
[root@k8s-master01 ~]# kubectl describe ing ingress-http  -n dev
...
Rules:
Host                Path  Backends
----                ----  --------
nginx.itheima.com   / nginx-service:80 (10.244.1.96:80,10.244.1.97:80,10.244.2.112:80)
tomcat.itheima.com  / tomcat-service:8080(10.244.1.94:8080,10.244.1.95:8080,10.244.2.111:8080)
...

# 接下來,在本地電腦上配置host文件,解析上面的兩個域名到192.168.109.100(master)上
# 然后,就可以分別訪問tomcat.itheima.com:32240  和  nginx.itheima.com:32240 查看效果了

3. Https代理

創建證書

# 生成證書
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=itheima.com"

# 創建密鑰
kubectl create secret tls tls-secret --key tls.key --cert tls.crt

創建ingress-https.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-https
  namespace: dev
spec:
  tls:
    - hosts:
      - nginx.itheima.com
      - tomcat.itheima.com
      secretName: tls-secret # 指定秘鑰
  rules:
  - host: nginx.itheima.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-service
          servicePort: 80
  - host: tomcat.itheima.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 8080
# 創建
[root@k8s-master01 ~]# kubectl create -f ingress-https.yaml
ingress.extensions/ingress-https created

# 查看
[root@k8s-master01 ~]# kubectl get ing ingress-https -n dev
NAME            HOSTS                                  ADDRESS         PORTS     AGE
ingress-https   nginx.itheima.com,tomcat.itheima.com   10.104.184.38   80, 443   2m42s

# 查看詳情
[root@k8s-master01 ~]# kubectl describe ing ingress-https -n dev
...
TLS:
  tls-secret terminates nginx.itheima.com,tomcat.itheima.com
Rules:
Host              Path Backends
----              ---- --------
nginx.itheima.com  /  nginx-service:80 (10.244.1.97:80,10.244.1.98:80,10.244.2.119:80)
tomcat.itheima.com /  tomcat-service:8080(10.244.1.99:8080,10.244.2.117:8080,10.244.2.120:8080)
...

# 下面可以通過瀏覽器訪問https://nginx.itheima.com:31335 和 https://tomcat.itheima.com:31335來查看了


免責聲明!

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



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