建立 Service 和 Ingress

本文說明如何在 Google Distributed Cloud 的使用者叢集中,建立 Kubernetes Ingress 物件。Ingress 會與一或多個服務建立關聯,而每個服務都會與一組 Pod 建立關聯。

事前準備

取得管理工作站的 SSH 連線

可建立部署作業

以下是部署的資訊清單。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-deployment
spec:
  selector:
    matchLabels:
      greeting: hello
  replicas: 3
  template:
    metadata:
      labels:
        greeting: hello
    spec:
      containers:
      - name: hello-world
        image: "gcr.io/google-samples/hello-app:2.0"
        env:
        - name: "PORT"
          value: "50000"
      - name: hello-kubernetes
        image: "gcr.io/google-samples/node-hello:1.0"
        env:
        - name: "PORT"
          value: "8080"

針對本練習的目的,請特別注意下列幾點關於 Deployment 資訊清單的事項:

  • 屬於 Deployment 的每個 Pod 都有 greeting: hello 標籤。

  • 每個 Pod 都有兩個容器。

  • env 欄位指定 hello-app 容器監聽 TCP 通訊埠 50000,而 node-hello 容器監聽 TCP 通訊埠 8080。如要查看 PORT 環境變數的效果,請查看 hello-apphello-app 原始碼

將資訊清單複製到名為「hello-deployment.yaml」的檔案,然後建立 Deployment:

kubectl apply --kubeconfig USER_CLUSTER_KUBECONFIG -f hello-deployment.yaml

其中 USER_CLUSTER_KUBECONFIG 是指使用者叢集的 kubeconfig 檔案路徑。

使用 Service 公開 Deployment

如要為用戶端提供穩定的方式,將要求傳送至 Deployment 的 Pod,請建立 Service。

以下是 Service 的資訊清單,可向叢集內的用戶端公開 Deployment:

apiVersion: v1
kind: Service
metadata:
  name: hello-service
spec:
  type: ClusterIP
  selector:
    greeting: hello
  ports:
  - name: world-port
    protocol: TCP
    port: 60000
    targetPort: 50000
  - name: kubernetes-port
    protocol: TCP
    port: 60001
    targetPort: 8080

將資訊清單儲存到名為「hello-service.yaml」的檔案,然後建立 Service:

kubectl apply --kubeconfig USER_CLUSTER_KUBECONFIG -f hello-service.yaml

查看 Service:

kubectl --kubeconfig USER_CLUSTER_KUBECONFIG get service hello-service --output yaml

輸出內容會顯示已指派給 Service 的 clusterIP 值。 例如:

apiVersion: v1
kind: Service
metadata:
  annotations:
    ...
spec:
  clusterIP: 10.96.14.249
  clusterIPs:
  - 10.96.14.249
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - name: world-port
    port: 60000
    protocol: TCP
    targetPort: 50000
  - name: kubernetes-port
    port: 60001
    protocol: TCP
    targetPort: 8080
  selector:
    greeting: hello
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

在上述輸出內容中,ports 欄位是 Kubernetes ServicePort 物件的陣列:一個名為 world-port,另一個名為 kubernetes-port

用戶端呼叫服務的方式如下:

  • 使用 world-port在其中一個叢集節點上執行的用戶端,會將要求傳送至 port 上的 clusterIP。在本範例中,為 10.96.14.249:60000。 要求會轉送至 targetPort 上的成員 Pod。在本範例中,POD_IP_ADDRESS:50000。

  • 使用 kubernetes-port:在其中一個叢集節點上執行的用戶端會將要求傳送至 port 上的 clusterIP。在本範例中,靜態位址為 10.96.14.249:60001。要求會轉送至 targetPort 上的成員 Pod。在本範例中,POD_IP_ADDRESS:8080。

輸入元件

以下是與 Ingress 相關的叢集元件:

  • istio-ingress Deployment。這是連入 Proxy。Ingress Proxy 會根據 Ingress 物件中指定的規則,將流量轉送至內部服務。

  • istio-ingress 服務。這項 Service 會公開 istio-ingress Deployment。

  • istiod Deployment。這是 Ingress 控制器。輸入控制器會監控 Ingress 物件的建立作業,並據此設定輸入 Proxy。

建立 Ingress

以下是 Ingress 的資訊清單:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
  - http:
      paths:
      - path: /greet-the-world
        pathType: Exact
        backend:
          service:
            name: hello-service
            port:
              number: 60000
      - path: /greet-kubernetes
        pathType: Exact
        backend:
          service:
            name: hello-service
            port:
              number: 60001

將資訊清單複製到名為「my-ingress.yaml」的檔案,然後建立 Ingress:

kubectl apply --kubeconfig USER_CLUSTER_KUBECONFIG -f my-ingress.yaml

建立使用者叢集時,請在叢集設定檔中指定 loadbalancer.ingressVip 的值。這個 IP 位址是在叢集負載平衡器上設定。建立 Ingress 時,系統會將這個 VIP 做為 Ingress 的外部 IP 位址。

當用戶端將要求傳送至使用者叢集 Ingress VIP 時,要求會轉送至負載平衡器。負載平衡器會使用 istio-ingress 服務將要求轉送至 Ingress Proxy,該 Proxy 會在使用者叢集中執行。系統會設定 Ingress Proxy,根據要求網址中的路徑,將要求轉送至不同的後端。

/greet-the-world 路徑

在 Ingress 資訊清單中,您會看到路徑 /greet-the-worldserviceName: hello-serviceservicePort: 60000 相關聯的規則。請注意,60000 是服務 hello-serviceworld-port 區段中 port 的值。

- name: world-port
    port: 60000
    protocol: TCP
    targetPort: 50000

Ingress 服務會將要求轉送至 clusterIP:50000。要求接著會傳送至 hello-service Service 的其中一個成員 Pod。該 Pod 中監聽通訊埠 50000 的容器會顯示 Hello World! 訊息。

/greet-kubernetes 路徑

在 Ingress 資訊清單中,您會看到路徑 /greet-kubernetesserviceName: hello-serviceservicePort: 60001 相關聯的規則。請注意,60001 是 hello-service 服務的 kubernetes-port 區段中 port 的值。

- name: kubernetes-port
    port: 60001
    protocol: TCP
    targetPort: 8080

Ingress 服務會將要求轉送至 clusterIP: 8080。要求接著會傳送至 hello-service Service 的其中一個成員 Pod。該 Pod 中監聽通訊埠 8080 的容器會顯示 Hello Kubernetes! 訊息。

測試 Ingress

使用 /greet-the-world 路徑測試 Ingress:

curl USER_CLUSTER_INGRESS_VIP/greet-the-world

USER_CLUSTER_INGRESS_VIP 替換為 Ingress 的外部 IP 位址。

輸出結果會顯示一則 Hello, world! 訊息:

Hello, world!
Version: 2.0.0
Hostname: ...

使用 /greet-kubernetes 路徑測試 Ingress:

curl USER_CLUSTER_INGRESS_VIP/greet-kubernetes

輸出結果會顯示一則 Hello, Kubernetes! 訊息:

Hello Kubernetes!

為 Ingress 設定 HTTPS

如果要接受來自用戶端的 HTTPS 要求,Ingress Proxy 必須要有憑證,以向用戶端證明其身分。這個 Proxy 也必須要有私密金鑰才能完成 HTTPS 握手。

以下範例使用這些實體:

  • Ingress Proxy:參與 HTTPS 交握,然後將封包轉送至 hello-service Service 的成員 Pod。

  • hello-service 服務的網域:Example Org 中的 altostrat.com

步驟如下:

  1. 建立根憑證和私密金鑰。本範例在 Root CA Example Org 中使用 root.ca.example.com 的根憑證授權單位。

    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=Root CA Example Inc./CN=root.ca.example.com' -keyout root-ca.key -out root-ca.crt
    
  2. 建立憑證簽署要求:

    openssl req -out server.csr -newkey rsa:2048 -nodes -keyout server.key -subj "/CN=altostrat.com/O=Example Org"
    
  3. 為 Ingress Proxy 建立服務憑證。

    openssl x509 -req -days 365 -CA root-ca.crt -CAkey root-ca.key -set_serial 0 -in server.csr -out server.crt
    

    您現在已建立下列憑證和金鑰:

    • root-ca.crt:根 CA 的憑證
    • root-ca.key:根 CA 的私密金鑰
    • server.crt:Ingress 代理伺服器的服務憑證
    • server.key:Ingress 代理伺服器的私密金鑰
  4. 建立包含服務憑證和金鑰的 Kubernetes 密鑰。

    kubectl create secret tls example-server-creds --key=server.key --cert=server.crt --namespace gke-system
    

    產生的 Secret 名為 example-server-creds

建立 Deployment 和 Service

如果您在本指南的 HTTP 部分中建立部署和服務,請保留這些項目。如果沒有,請按照為 HTTP 說明的步驟建立憑證。

建立 Ingress

為 HTTPS 建立 Ingress 與為 HTTP 建立 Ingress 類似,但 HTTPS 的 Ingress 規格包含 tls 區段,用於指定主機和密鑰。tls 部分的 hosts 必須與 rules 部分的 host 完全相符。

如果後端服務位於不同的命名空間,您需要在與 Ingress 相同的命名空間中,建立 ExternalName 類型的服務,將流量路由至後端服務。

如果您先前在 HTTP 部分建立了 Ingress,請先刪除該 Ingress,再繼續操作。

刪除 Ingress:

kubectl --kubeconfig USER_CLUSTER_KUBECONFIG delete ingress my-ingress

如要處理先前建立的 Service 流量,請建立具有 tls 區段的新 Ingress。這樣一來,用戶端與 Ingress Proxy 之間就會啟用 HTTPS。

以下是 Ingress 的資訊清單:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress-2
spec:
  tls:
  - hosts:
    - altostrat.com
    secretName: example-server-creds
  rules:
  - host: altostrat.com
    http:
      paths:
      - path: /greet-the-world
        pathType: Exact
        backend:
          service:
            name: hello-service
            port:
              number: 60000
      - path: /greet-kubernetes
        pathType: Exact
        backend:
          service:
            name: hello-service
            port:
              number: 60001

將資訊清單儲存到名為 my-ingress-2.yaml 的檔案,然後建立 Ingress:

kubectl apply --kubeconfig USER_CLUSTER_KUBECONFIG -f my-ingress-2.yaml

請透過測試確認。

  • 測試 /greet-the-world 路徑:

    curl -v --resolve altostrat.com:443:USER_CLUSTER_INGRESS_VIP https://altostrat.com/greet-the-world --cacert root-ca.crt
    

    輸出:

    Hello, world!
    Version: 2.0.0
    Hostname: hello-deployment-5ff7f68854-wqzp7
    
  • 測試 /greet-kubernetes 路徑:

    curl -v --resolve altostrat.com:443:USER_CLUSTER_INGRESS_VIP https://altostrat.com/greet-kubernetes --cacert root-ca.crt
    

    輸出:

    Hello Kubernetes!
    

正在清除所用資源

刪除 Ingress:

kubectl --kubeconfig USER_CLUSTER_KUBECONFIG delete ingress INGRESS_NAME

INGRESS_NAME 替換為 Ingress 的名稱,例如 my-ingressmy-ingress-2

刪除服務:

kubectl --kubeconfig USER_CLUSTER_KUBECONFIG delete service hello-service

刪除部署作業:

kubectl --kubeconfig USER_CLUSTER_KUBECONFIG delete deployment hello-deployment