前面的章節我們每次要跟服務互動時都必須使用「Port-Forward」的方式,但畢竟這真的只能用來Demo或是臨時測試使用,如果要在實戰上使用會先累死自己的XD,在K8s中,Service提供了一個抽象層次,用於管理應用程式中的不同部分之間的通信,它提供一個固定的IP地址和DNS名稱,讓多個Pod能夠共享這個地址和名稱,從而實現服務的暴露和訪問。

Service就好像一個快遞站,而Pod就是快遞員。假設你要寄送一個包裹,你需要把包裹交給快遞員,讓他送到指定的地址。但是,如果你不知道快遞員的具體位置,你就需要打電話查詢他的位置,然後再交給他包裹。這樣很麻煩,效率也不高。如果有一個快遞站,快遞員可以把包裹交給這個快遞站,讓它進行分發和管理。當你要寄送包裹時,你只需要把包裹交給這個快遞站,讓它根據地址進行分發,就可以了。這樣就可以省去查詢快遞員位置的麻煩,也提高了效率和可靠性。



編寫Service定義檔

撰寫Service一樣可以直接輸入「k-service」

把指令產生的Service貼到之前Deployment範例底下
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wadweb-deployment
  labels:
    app: wadweb-deployment
spec:
  selector:
    matchLabels:
      app: wadweb
  replicas: 1
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
    type: RollingUpdate
  template:
    metadata:
      name: wadweb
      labels:
        app: wadweb
    spec:
      containers:
      - name: wadwebapp
        image: "你的registryURL/wad-web:v2.0.0"
				imagePullPolicy: Always
        resources:
          limits:
            cpu: 200m
            memory: 500Mi
          requests:
            cpu: 100m
            memory: 200Mi
        ports:
          - containerPort: 80
            name: http
      - name: wadwebapi
        image: "你的registryURL/wad-web-api:v2.0.0"
				imagePullPolicy: Always
        resources:
          limits:
            cpu: 2000m
            memory: 2Gi
          requests:
            cpu: 1000m
            memory: 1Gi
        env:
          - name: DOTNET_RUNNING_IN_CONATINER
            value: "true"
          - name: ASPNETCORE_ENVIRONMENT
            value: "Development"
          - name: ASPNETCORE_URLS
            value: "http://+:8080"
          - name: ConnectionStrings__DefaultConnection
            value: "Server=你的SQLServer Connection;Initial Catalog=WADREQM;Persist Security Info=False;User ID=你的SQLServer帳號;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=true;Connection Timeout=30;"
          - name: Connection__Key
            value: "我是密碼"
          - name: HostUrl__RemoteHost
            value: "http://192.168.1.34:8080"
          - name: TZ
            value: "Asia/Taipei"
        volumeMounts:
          - name: fileupload
            mountPath: /app/upload
        ports:
          - containerPort: 8080
            name: http        
      volumes:
        - name: fileupload
          hostPath:
            path: /run/desktop/mnt/host/d/Kubernetes/fileupload
            type: DirectoryOrCreate
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: wadweb-service
spec:
  type: NodePort
  selector:
    app: wadweb
  ports:
  - name: wadwebapp-port
    port: 80
    targetPort: 80
    nodePort: 30014
  - name: wadapiapp-port
    port: 8080
    targetPort: 8080
    nodePort: 30015
  • spec.type:這裡有多種模式:「ClusterIP」「NodePort」「LoadBalancer」「ExternalName」
    • ClusterIP:預設設定,只會在集群內部暴露一個IP地址,用於集群內部通信。這個IP地址只能在集群內部進行訪問,對外部不可見。
      💡 適合場景:內部Pod服務有相關聯並且需要互相溝通時可以使用
    • NodePort:在每個節點上暴露一個固定的Port,用於從集群外部訪問這個 Service,缺點是Service數量很多時,管理Port會變得麻煩
      💡 適合場景:Demo用,臨時的服務等等
    • LoadBalancer:在集群外部使用一個負載均衡器,用於實現外部訪問這個 Service,是讓外部連線到Pod服務的標準方式,前提是要使用雲端以及其外部的負載平衡器
      💡 適合場景:適合在雲端,並且雲端提供外部的負載平衡器時
    • ExternalName:是一個命名代理,用於將K8s中的服務命名映射到集群外部的服務
      💡 適合場景:需要訪問集群外部的服務時,例如外部的資料庫、API 服務等等
  • spec.selector:讓Service選擇到指定的Pod(這裡設定的是Pod的Label)
    💡 如果沒有設置selector屬性,它仍然可以將請求轉發到相應的Pod,但是它只能通過設置spec.clusterIP=None的方式實現。這種方式創建的Service稱為Headless Service,它不提供負載均衡和故障轉移的功能,而是將請求直接轉發到相應的 Pod,從而實現直接的訪問。這裡就不詳細介紹什麼是Headless Service了因為很少有機會用到,我自己也沒有用過XD
  • spec.ports[].port:服務的外部接收Port
  • spec.ports[].targetPort:Pod的Port(spec.containers[].ports.containerPort)
  • spec.ports[].nodePort:當type設定為NodePort時才有效,如果沒設定K8s會隨機指定一個Port(範圍是30000 ~ 32767)
💡 Service主要是通過kube-proxy實現的。kube-proxy是K8s的一個核心組件,它負責實現Service的負載均衡和故障轉移功能,每個Node都會運行一個kube-proxy進程。kube-proxy接收來自Service的流量,然後根據Service的類型(ClusterIP、NodePort、LoadBalancer或ExternalName)進行處理




執行Service

設定好Service後就可以部署上去,然後使用指令來查看Service的情況,可以看到Cluster內部通信的Port和外部通信的NodePort
kubectl get service


使用NodePort時就要使用對應的NodePort來連線,我們在Chrome上直接輸入「30014」和「30015」的Port進行連線



Service的NodePort設定是不是很方便簡單呢!不!在真實的環境可不是一個NodePort就可以解決的呢XD,還有其它的如:「ClusterIP」「LoadBalancer」「ExternalName」等等加上後面還有延伸的「Ingress」,所以在實戰中還是會根據不同的需求選擇不同的 Service 類型,從而實現不同的功能哦!




儘管NodePort是一個很方便的方式,但也存在一些缺點:
  1. 安全性:由於NodePort公開了一個範圍的端口到集群節點上,這可能會增加攻擊面。攻擊者可以直接通過節點IP和NodePort來進行攻擊,並且可以使用暴力攻擊的方式來猜測NodePort的值。
  2. 擴展性:使用NodePort需要開放一定範圍的端口,如果Kubernetes集群中有多個服務需要公開,可能會出現端口不足的情況。而且當Kubernetes集群擴展到多個節點時,需要管理和維護多個節點上的NodePort。
  3. 設置困難:在NodePort模式下,需要手動配置防火牆規則以允許對NodePort的流量進行轉發。這可能需要一些網絡和安全方面的知識,對於一些新手來說可能比較困難。
  4. 辨識度不高:使用NodePort你的URL勢必會變成http://192.168.1.1:xxxxx,服務一旦很多時,也不容易從URL去辨識這個服務在幹嘛
因此對於需要更高安全性和擴展性的情況,可以考慮使用其他方式,如使用LoadBalancer或Ingress等哦!