Google Code Prettify

2020年1月28日 星期二

K8s: Service

繼續「K8s: ReplicaSet」的例子,為了讓 Kubernates 之外的程式可以存取到 pod,需要建立 service (服務),當成外界與 pod 間的橋樑。
  • 建立服務
apiVersion: v1
kind: Service
metadata:
  name: host-http
spec:
  ports:
  - name: http
    port: 9090
    targetPort: 9080
  selector:
    app: host
說明一下,這個 yaml 做了些什麼:
  1. 第 4 行定義了服務的名稱為 host-http
  2. 第 8 行指出服務開放給外界訪問的 port 是 9090
  3. 第 9 行指出 pod 的 port 是 9080
  4. 第 10~11 行指出 pod 上有 label "app=host" 的綁定到這個服務
產生服務後,以如下指令看一下服務內容。
kubectl describe svc host-http
從上面的內容可以看到服務分配到的 IP 是 10.108.180.52 (cluster IP),三個 pod 分配到的 IP 是 10.244.1.42、10.244.1.43、10.244.1.44,這些 IP 都是 Kubernetes 內部的 IP,無法由外部訪問,要測試服務是否正常工作,可以跳入一個 pod,由它對服務發出 request,如下:
kubectl exec host-2vxjs -- curl -s http://10.108.180.52:9090/host/info
注意看到,每次執行不一定會使用到同一個 pod。
  • 發現服務
  • Kubernetes 配發給服務的 IP 在服務整個生命週期不會變,但是,pod 要如何知道服務的 cluster IP ? 我們先刪除所有的 pod,rs 會自動再產生 3 個新的 pod。
    kubectl exec host-kxdtn env
    
    如上,跳入一個 pod 查看環境變數,內容如下,為什麼上面要故意刪除 pod 讓 rs 重新產生? 因為 pod 被新創立時,會根據 Kubernetes 最新狀況生成環境變數,記錄在 pod 裡面。
    PATH=/usr/local/jdk-11/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    HOSTNAME=host-kxdtn
    HOST_HTTP_PORT_9090_TCP=tcp://10.108.180.52:9090
    HOST_HTTP_PORT_9090_TCP_PROTO=tcp
    HOST_HTTP_PORT_9090_TCP_PORT=9090
    KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
    HOST_HTTP_SERVICE_HOST=10.108.180.52
    HOST_HTTP_SERVICE_PORT=9090
    KUBERNETES_SERVICE_PORT_HTTPS=443
    KUBERNETES_PORT=tcp://10.96.0.1:443
    KUBERNETES_PORT_443_TCP_PORT=443
    KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
    HOST_HTTP_SERVICE_PORT_HTTP=9090
    HOST_HTTP_PORT=tcp://10.108.180.52:9090
    HOST_HTTP_PORT_9090_TCP_ADDR=10.108.180.52
    KUBERNETES_SERVICE_HOST=10.96.0.1
    KUBERNETES_SERVICE_PORT=443
    KUBERNETES_PORT_443_TCP_PROTO=tcp
    JAVA_HOME=/usr/local/jdk-11
    HOME=/root
    
    注意看到第 7~8 行,顯示了服務的 cluster IP (10.108.180.52) 及開放出來的 port (9090),新創建的 pod 透過環境變數即可得知。除了這個辦法還有沒有其它辦法? 通常在網路上發現各項服務是透過 DNS,Kubernetes 是否有相同的方式? 答案是有! Kubernetes 提供了 DNS 服務,每個服務的名稱會對映到它的 IP,同時透過 FQDN,這記錄在每個 pod 的 /etc/resolv.conf 檔裡,當服務名稱找不到 IP 時,會根據這個檔的設定加上後綴,再尋找看看,現在再跳入一個 pod 來試看看。
    如上,跳入 pod host-kxdtn,並執行 bash,這樣就可以在裡面下指令,在 pod 裡面要對服務 request,最簡單的就是以服務的名稱當成網址,上面有 ping 看看,真的可以得到服務的 IP,下了 curl http://host-http:9090/host/info 也得到正確的回應,當然啦~ 服務的 port 仍需從環境變數才能得到。
  • 開放服務給客戶端
  • 上面的做法雖然讓 Kubernetes 內的 pod 可以透過服務互相訪問,但是,上面建立服務的方式,沒有將服務暴露出來給外部,K8s 外的客戶端訪問不到,現在修改 yaml 如下:
    apiVersion: v1
    kind: Service
    metadata:
      name: host-http
    spec:
      type: LoadBalancer
      ports:
      - name: http
        port: 9090
        targetPort: 9080
      selector:
        app: host
    
    加入第 6 行的設定,設定 K8s cluster 的架構為 LoadBalancer,刪除服務 host-http 並用這個 yaml 重新建立,接著查詢服務,可以得到如下結果,如果 Kubernetes 有支援負載平衡,在 EXTERNAL-IP 欄位會顯示出開放給外部的 IP,我的環境是自建的私有雲沒有支援,type 被降為 NodePort,該欄位會一直顯示 <pending>,這時候就只能使用 master node 的 IP 為 EXTERNAL-IP,port 如下面顯示的是 32015。
【番外篇】
Kubernetes 本身沒有提供負載平衡的支援,如果是使用 GCP、AWS、Azure … 就會由這些平台提供,為了解決這個問題,出現了 MetalLB 這個解決方案,依該網站提供的安裝程序安裝後,重新建立 service,EXTERNAL-IP 就會有對外 IP 了。

沒有留言:

張貼留言