K8S之Service詳解


K8S之Service詳解

簡介

在K8S中,Pod是容器運行的載體,我們可以訪問Pod內部的容器來訪問相對應的服務,但是Pod的IP地址不是固定的,因此這也就意味我們不方便直接采用IP地址對Pod進行訪問管理。

在K8S中提供我們 service 服務來解決上述問題, service 對一組pod進行聚合,並且提供一個統一的外部接口地址,我們通過訪問該接口地址就可以訪問其所對應的pod服務。

本質上來說其實 service 只是一個概念,真正起到轉發數據的是 kube-proxy 服務進程,每個節點之上都運行一個 kube-proxy 服務進程,當創建 service 的時候 API Service 會監聽 service 然后向 etcd 寫入 service 的信息, kube-proxy 會監聽 etcdservice 的信息並且將 service 信息轉發成對應的訪問規則。

s

kube-proxy工作模式

userspace

在該模式下 kube-proxy 會為每一個 service 創建一個監聽端口,發送給 Cluseter IP 請求會被 iptable 重定向給 kube-proxy 監聽的端口上,其中 kube-proxy 會根據 LB 算法將請求轉發到相應的pod之上。

該模式下,kube-proxy充當了一個四層負載均衡器的角色。由於kube-proxy運行在userspace中,在進行轉發處理的時候會增加內核和用戶空間之間的數據拷貝,雖然比較穩定,但是效率非常低下。

user

iptables

iptables模式下 kube-proxy 為每一個pod創建相對應的 iptables 規則,發送給 ClusterIP 的請求會被直接發送給后端pod之上

在該模式下 kube-proxy 不承擔負載均衡器的角色,其只會負責創建相應的轉發策略,該模式的優點在於較userspace模式效率更高,但是不能提供靈活的LB策略,當后端Pod不可用的時候無法進行重試。

iptab

ipvs模式

ipvs模式與iptable模式類型, kube-proxy 會根據pod的變化創建相應的 ipvs 轉發規則,ipvs相對iptable來說轉發效率更加高效,同時提供了大量的負責均衡算法。

ipvs

使用ipvs模式必須安裝ipvs內核模塊,否則會自動降級為iptables

# 編輯配置文件 搜索43行mode將其修改為ipvs
[root@master k8s]# kubectl edit cm kube-proxy -n kube-system

# 刪除原有的代理
[root@master k8s]# kubectl delete pod -l k8s-app=kube-proxy -n kube-system

# 查看
[root@master k8s]# ipvsadm -Ln

image-20211030172547155

service類型

資源清單

apiVersion: v1 # 版本
kind: Service # 類型
metadata: # 元數據
  name: # 資源名稱
  namespace: # 命名空間
spec:
  selector: # 標簽選擇器,用於確定當前Service代理那些Pod
    app: nginx
  type:  # Service的類型,指定Service的訪問方式
  clusterIP: # 虛擬服務的IP地址
  sessionAffinity: # session親和性,支持ClientIP、None兩個選項,默認值為None
  ports: # 端口信息
    - port: 8080 # Service端口
      protocol: TCP # 協議
      targetPort : # Pod端口
      nodePort:  # 主機端口
  • ClusterIP:默認類型,處於該模式下K8S會自動為service分配地址,其只能在集群內部訪問。
  • NodePort:將Service通過指定的Node上的端口暴露給外部,通過此方法,就可以在集群外部訪問服務。
  • LoadBalancer:使用外接負載均衡器完成到服務的負載分發,注意此模式需要外部雲環境的支持。
  • ExternalName:把集群外部的服務引入集群內部,直接使用。

service應用

Clusterip service

cat > service-clusterip.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  name: service-clusterip
  namespace: dev
spec:
  selector:
    app: nginx-pod
  type: ClusterIP
  # service IP地址 如果不寫默認會生成一個
  clusterIP: 10.97.97.97
  ports:
    # service端口
    - port: 80
      # 目標pod端口
      targetPort: 80
EOF

# 創建service
[root@master k8s]# kubectl create -f service-clusterip.yaml 

# 查看service
[root@master k8s]# kubectl get -n dev service service-clusterip -o wide

創建pod

cat > deployment.yaml << EOF
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
              protocol: TCP
EOF

# 創建pod
[root@master k8s]# kubectl create -f deployment.yaml 

# 查看pod
[root@master k8s]# kubectl get pod -n dev -o wide --show-labels

image-20211031172624022

# 任意節點發起請求
[root@master k8s]# curl 10.244.2.64

image-20211031173316240

# 由於pod節點都是nginx默認界面都是一樣的為了方便測試修改默認界面
# 進入容器
[root@master k8s]# kubectl exec -it -n dev pc-deployment-7d7dd5499b-j5hnp /bin/sh

# 三個節點寫入數據
echo "Current Request is 10.244.1.64" > /usr/share/nginx/html/index.html
echo "Current Request is 10.244.1.63" > /usr/share/nginx/html/index.html
echo "Current Request is 10.244.1.62" > /usr/share/nginx/html/index.html


# 發起請求
[root@master k8s]# curl 10.244.2.64
[root@master k8s]# curl 10.244.2.63
[root@master k8s]# curl 10.244.2.62

image-20211031173932500

創建services

cat > service-clusterip.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  name: service-clusterip
  namespace: dev
spec:
  selector:
    app: nginx-pod
  type: ClusterIP
  # service IP地址 如果不寫默認會生成一個
  clusterIP: 10.97.97.97
  ports:
    # service端口
    - port: 80
      # 目標pod端口
      targetPort: 80
EOF

# 創建service
[root@master k8s]# kubectl create -f service-clusterip.yaml 

# 查看service
[root@master k8s]# kubectl get -n dev service service-clusterip -o wide

image-20211031174525393

# 查看詳情
[root@master k8s]# kubectl describe svc service-clusterip -n dev

image-20211031174702153

# 多次訪問后端節點
[root@master k8s]# curl 10.97.97.97

image-20211031174928125

Endpoint

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

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

end

# 查看endpoints
[root@master k8s]# kubectl get endpoints -n dev 

image-20211031222101664

負載分發策略

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

  • 如果不定義,默認使用kube-proxy的策略,比如隨機、輪詢等。
  • 基於客戶端地址的會話保持模式,即來自同一個客戶端發起的所有請求都會轉發到固定的一個Pod上,這對於傳統基於Session的認證項目來說很友好,此模式可以在spec中添加sessionAffinity: ClusterIP選項。
# 修改分發策略
apiVersion: v1
kind: Service
metadata:
  name: service-clusterip
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: 10.97.97.97 # service的IP地址,如果不寫,默認會生成一個
  type: ClusterIP
  sessionAffinity: ClientIP # 修改分發策略為基於客戶端地址的會話保持模式
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口    
[root@master k8s]# kubectl create -f service-clusterip.yaml

# 循環測試
[root@master k8s]# while true;do curl 10.97.97.97:80; sleep 5; done;

image-20211031222807592

HeadLiness services

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

創建service

cat > service-headliness.yaml << EOF
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 # Service的端口
      targetPort: 80 # Pod的端口
EOF

[root@master k8s]# kubectl create -f service-headliness.yaml

# 查看service
[root@master k8s]# kubectl get svc service-headliness -n dev -o wide

image-20211031233312112

# 查看詳情
[root@master k8s]# kubectl describe svc service-headliness -n dev

image-20211031233406900

# 查看pod
[root@master k8s]# kubectl get pod -n dev

image-20211031233453549

# 進入Pod
[root@master k8s]# kubectl exec -it -n dev pc-deployment-7d7dd5499b-jc84t /bin/bash
# 查看域名
root@pc-deployment-7d7dd5499b-jc84t:/# cat /etc/resolv.conf 

image-20211031233631713

# 通過Service的域名進行查詢:
# 默認訪問規則service名稱.名稱空間.svc.cluster.local

# 安裝dig
[root@master k8s]# yum -y install bind-utils

# 訪問
[root@master k8s]# dig @10.96.0.10 service-headliness.dev.svc.cluster.local

NodePort service

在上述案例之中 service 暴露的IP 只能供集群內部訪問,但是我們創建資源即暴露給用戶使用,因此 K8S 為我們提供了 NodePort service

其會將service 的端口與 node 的端口進行映射,當我們訪問 node 的 IP + Port 即為訪問 service 所對應的資源

nodeport

創建 service

# 創建 service
cat > cat service-nodeport.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: dev
spec:
  selector:
    app: nginx-pod
  type: NodePort # Service類型為NodePort
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口
      nodePort: 30002 # 指定綁定的node的端口(默認取值范圍是30000~32767),如果不指定,會默認分配
EOF

# 創建pod
[root@master k8s]# kubectl create -f service-nodeport.yaml

# 查看詳情
[root@master k8s]# kubectl get svc -n dev -o wide

image-20211108102608132

# 訪問測試 由於更換機器此時 master 的地址由 10.1.1.2 更改為 172.16.137.128
curl 172.16.137.128:30002

image-20211108103002569

LoadBalancer service

通過上圖可以看到使用 NodePort 模式中對負載均衡能力不是很友好,external 類型與 nodeport 類似都是對於外部暴露一個訪問端口

區別在於使用該模式會在集群外部添加一個負載均衡設備,外部訪問負載均衡設置,由負載均衡設備在根據一定的算法轉發到后端服務節點

load

ExternalName service

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

exte

創建 service

cat > service-externalname.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  name: service-externalname
  namespace: dev
spec:
  type: ExternalName # Service類型為ExternalName
  externalName: www.baidu.com # 改成IP地址也可以
EOF

[root@master k8s]# kubectl create -f service-externalname.yaml

# 查看 service
[root@master k8s]# kubectl get svc service-externalname -n dev

image-20211108140945724

# 安裝域名解析
[root@master k8s]# yum install bind-utils

# 域名解析
[root@master k8s]# dig @10.96.0.10 service-externalname.dev.svc.cluster.local

Ingress

簡介

在上述中 service 對外提供負載均衡主要有 nodeport 與 loadblancer 兩種方式,但是這兩種方式各自都有一定的缺點,在 nodeport 方式中 service 會與 node 節點進行映射,這樣會占用大量的端口,當 service 過多的時候可能會導致節點端口不夠,在 loadblancer 中每一個service 都需要一個 LB,並且需要外部的負載均衡設備進行支持

基於上述問題,在 K8S 中提出了 ingress 資源對象,該資源對象只需要一個 nodeport 或者一個 LB 就可以滿足暴露多個 service 需求

ingress
實際上 Ingress 類似於一個七層的負載均衡器,是由 K8S 對反向代理的抽象,其工作原理類似於 Nginx 可以理解為Ingress里面建立了諸多映射規則,Ingress Controller通過監聽這些配置規則並轉化為Nginx的反向代理配置,然后對外提供服務。

  • Ingress:kubernetes中的一個對象,作用是定義請求如何轉發到Service的規則。

  • Ingress Controller:具體實現反向代理及負載均衡的程序,對Ingress定義的規則進行解析,根據配置的規則來實現請求轉發,實現的方式有很多,比如Nginx,Contour,Haproxy等。

  • 其工作原理如下

    • 用戶編寫 Ingress 規則說明那個域名對應那個 service
    • Ingress Contoller 動態感知 ingress 編寫的規則,然后生成對應的反向代理規則
    • ingress 控制器會根據生成代理規則寫入到代理服務中
    • 客戶端請求代理服務,由代理服務轉發到后端 pod 節點

ha

Ingress 使用

環境搭建

mkdir ingress-controller && cd ingress-controller

# 下載service 與控制器
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
  
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
  
# 更換網絡源
sed -i 's#quay.io/kubernetes-ingress-controller/nginx-ingress-controller#registry.cn-qingdao.aliyuncs.com/kubernetes_xingej/nginx-ingress-controller#g' mandatory.yaml

# 使用配置文件
kubectl apply -f ./

# 查看pod
[root@master ~]# kubectl get -n ingress-nginx pod

# 查看svc
[root@master ~]# kubectl get -n ingress-nginx svc

image-20211109224550664

准備Service和pod

cat > tomcat-nginx.yaml << EOF
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
EOF

# 創建pod
[root@master ~]# kubectl create -f tomcat-nginx.yaml

# 查看
[root@master ~]# kubectl get svc,pod -n dev

image-20211109225128565

HTTP代理

cat > ingress-http.yaml << EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-http
  namespace: dev
spec:
    # 代理規則
  rules:
    # 綁定域名
  - host: nginx.sr.com
    http:
      paths:
        # 綁定路徑
      - path: /
        backend:
          serviceName: nginx-service
           # service暴露端口
          servicePort: 80
  - host: tomcat.sr.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 8080
EOF
 
# 創建
[root@master ~]# kubectl create -f ingress-http.yaml

# 查看
[root@master ~]# kubectl get pod -n dev -o wide

image-20211109225900016

# 查看詳情
[root@master ~]# kubectl describe ingress ingress-http -n dev

image-20211109225951385

配置本地host文件將master的IP地址與上述域名綁定

# 訪問nginx
curl nginx.sr.com:30771

image-20211109230744863

# 訪問tomcat
curl tomcat.sr.com:30378

image-20211109230847610

HTTPS 代理

# 生成證書
[root@master ~]# 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=sr.com"

# 生成秘鑰
[root@master ~]# kubectl create secret tls tls-secret --key tls.key --cert tls.crt

# 生成配置文件
cat > ingress-https.yaml << EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-https
  namespace: dev
spec:
  tls:
    - hosts:
      - nginx.sr.com
      - tomcat.sr.com
      secretName: tls-secret # 指定秘鑰需要與上述生成的秘鑰名稱相同
  rules:
  - host: nginx.sr.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-service
          servicePort: 80
  - host: tomcat.sr.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 8080
EOF

# 加載配置文件
[root@master k8s]# kubectl create -f ingress-https.yaml

# 查看配置文件
[root@master k8s]# kubectl get ingress ingress-https -n dev

image-20211118164057887

# 查看詳情
[root@master k8s]# kubectl describe -n dev ingress ingress-https

image-20211118165502336

# 查看service
[root@master k8s]# kubectl get -n ingress-nginx service

image-20211118165641222


免責聲明!

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



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