Istio(十一):向istio服務網格中引入虛擬機

一.模塊概覽

在本模塊中,我們將了解如何將運行在虛擬機上的工作負載納入istio服務網格。

向istio服務網格中引入虛擬機的前提是已經安裝好了istio,關於istio的安裝部署,請查看博客《Istio(二):在Kubernetes(k8s)集群上安裝部署istio1.14》//www.cnblogs.com/renshengdezheli/p/16836404.html

二.系統環境

服務器版本 docker軟件版本 Kubernetes(k8s)集群版本 Istio軟件版本 CPU架構
CentOS Linux release 7.4.1708 (Core) Docker version 20.10.12 v1.21.9 Istio1.14 x86_64

Kubernetes集群架構:k8scloude1作為master節點,k8scloude2,k8scloude3作為worker節點

服務器 操作系統版本 CPU架構 進程 功能描述
k8scloude1/192.168.110.130 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kube-apiserver,etcd,kube-scheduler,kube-controller-manager,kubelet,kube-proxy,coredns,calico k8s master節點
k8scloude2/192.168.110.129 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kubelet,kube-proxy,calico k8s worker節點
k8scloude3/192.168.110.128 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kubelet,kube-proxy,calico k8s worker節點

三.虛擬機負載

3.1 虛擬機負載

如果我們有在虛擬機上運行的工作負載,我們可以將它們連接到 Istio 服務網格,使其成為網格的一部分

帶有虛擬機的 Istio 服務網格有兩種架構:單網絡架構和多網絡架構

3.2 單網絡架構

在這種情況下,有一個單一的網絡。Kubernetes 集群和在虛擬機上運行的工作負載都在同一個網絡中,它們可以直接相互通信。

image-20221027210520843

控制平面的流量(配置更新、證書籤署)是通過 Gateway 發送的

虛擬機被配置了網關地址,所以它們在啟動時可以連接到控制平面

3.3 多網絡架構

多網絡架構橫跨多個網絡。Kubernetes 集群在一個網絡內,而虛擬機則在另一個網絡內。這使得 Kubernetes 集群中的 Pod 和虛擬機上的工作負載無法直接相互通信。

image-20221027210623059

所有的流量,控制平面和 pod 到工作負載的流量都流經網關,網關作為兩個網絡之間的橋樑。

3.4 Istio 中如何表示虛擬機工作負載?

在 Istio 服務網格中,有兩種方式來表示虛擬機工作負載

工作負載組(WorkloadGroup 資源)類似於 Kubernetes 中的部署(Deployment),它代表了共享共同屬性的虛擬機工作負載的邏輯組。

描述虛擬機工作負載的第二種方法是使用工作負載條目(WorkloadEntry 資源)。工作負載條目類似於 Pod,它代表了一個虛擬機工作負載的單一實例。

請注意,創建上述資源將不會提供或運行任何虛擬機工作負載實例。這些資源只是用來引用或指向虛擬機工作負載的。Istio 使用它們來了解如何適當地配置網格,將哪些服務添加到內部服務註冊表中,等等。

為了將虛擬機添加到網格中,我們需要創建一個工作負載組,作為模板。然後,當我們配置並將虛擬機添加到網格中時,控制平面會自動創建一個相應的 WorkloadEntry。

我們已經提到,WorkloadEntry 的作用類似於 Pod。在添加虛擬機時,會創建 WorkloadEntry 資源,而當虛擬機的工作負載從網格中移除時,該資源會被自動刪除。

除了 WorkloadEntry 資源外,我們還需要創建一個 Kubernetes 服務。創建一個 Kubernetes 服務給了我們一個穩定的主機名和 IP 地址,以便使用選擇器字段訪問虛擬機工作負載和 pod。這也使我們能夠通過 DestinationRule 和 VirtualService 資源使用 Istio 的路由功能

四.實戰:向istio Mesh中引入虛擬機

4.1 將虛擬機引入到 Istio Mesh

在這個實驗中,我們將學習如何將虛擬機上運行的工作負載連接到 Kubernetes 集群上運行的 Istio 服務網格。Kubernetes 集群和虛擬機都將在谷歌雲平台(GCP)上運行。我們將使用單一網絡架構

在我們創建了一個 Kubernetes 集群後,我們可以下載、安裝和配置 Istio。

4.2 在 Kubernetes 集群上安裝 Istio

讓我們下載 Istio 1.10.3。

 $ curl -L //istio.io/downloadIstio | ISTIO_VERSION=1.10.3 sh -

下載了 Istio 後,我們可以使用 IstioOperator 來安裝它,它可以設置網格 ID、集群名稱和網絡名稱。網絡名稱將是空的,因為我們要用單一網絡架構

讓我們來設置幾個環境變量,我們將在本實驗中使用這些變量。

 export SERVICE_ACCOUNT="vm-sa"
 export VM_APP="hello-vm"
 export VM_NAMESPACE="vm-namespace"
 export WORK_DIR="${HOME}/vmfiles"
 export CLUSTER_NETWORK=""
 export VM_NETWORK=""
 export CLUSTER="Kubernetes"

我們還可以創建 $WORK_DIR,在這裡我們將存儲證書和其他我們必須複製到虛擬機上的文件。

 mkdir -p $WORK_DIR

接下來,我們將初始化 Istio Operator 並安裝 Istio:

 getmesh istioctl operator init

一旦 Operator 被初始化,我們就可以創建 IstioOperator 資源,指定網格 ID、集群名稱和網絡,並安裝 Istio

 cat <<EOF | kubectl apply -f -
 apiVersion: install.istio.io/v1alpha1
 kind: IstioOperator
 metadata:
   name: istio
   namespace: istio-system
 spec:
   values:
     global:
       meshID: mesh1
       multiCluster:
         clusterName: "${CLUSTER}"
       network: "${CLUSTER_NETWORK}"
 EOF

我們可以使用 kubectl get iop -n istio-system 命令來檢查 Istio 安裝狀態何時變為健康

在下一步,我們將安裝東西向網關。這是控制平面用來與虛擬機工作負載對話的網關,反之亦然。

gen-eastwest-gateway.sh 腳本是我們之前下載的 Istio 包的一部分。將文件夾改為 istio-1.10.3(或你解壓 Istio 的文件夾)並運行以下命令:

 samples/multicluster/gen-eastwest-gateway.sh --single-cluster | istioctl install -y -f -

gen-eastwest-gateway.sh 腳本使用一個 IstioOperator 來部署一個額外的網關,名為 istio-eastwestgateway,並配置了服務端口。

我們可以通過查看 istio-system 命名空間中的 Kubernetes 服務來檢查新網關。

最後,我們還需要配置網關,通過它來暴露控制平面(istiod)。我們可以通過部署 expose-istiod.yaml 文件來做到這一點:

 $ kubectl apply -n istio-system -f samples/multicluster/expose-istiod.yaml
 gateway.networking.istio.io/istiod-gateway created
 virtualservice.networking.istio.io/istiod-vs created

4.3 準備虛擬機命名空間和文件

對於虛擬機工作負載,我們必須創建一個單獨的命名空間來存儲 WorkloadEntry 資源和任何其他虛擬機工作負載相關的資源。此外,我們還必須導出集群環境文件、令牌、證書和其他我們必須轉移到虛擬機上的文件

我們將把所有文件存放在我們在實驗開始時創建的 $WORK_DIR 中。

讓我們在同一命名空間中創建虛擬機命名空間和我們將用於虛擬機工作負載的服務賬戶

 $ kubectl create ns "${VM_NAMESPACE}"
 namespace/vm-namespace created
 
 $ kubectl create serviceaccount "${SERVICE_ACCOUNT}" -n "${VM_NAMESPACE}"
 serviceaccount/vm-sa created

現在我們可以創建一個 WorkloadGroup 資源並將其保存到 workloadgroup.yaml 中:

 cat <<EOF > workloadgroup.yaml
 apiVersion: networking.istio.io/v1alpha3
 kind: WorkloadGroup
 metadata:
   name: "${VM_APP}"
   namespace: "${VM_NAMESPACE}"
 spec:
   metadata:
     labels:
       app: "${VM_APP}"
   template:
     serviceAccount: "${SERVICE_ACCOUNT}"
     network: "${VM_NETWORK}"
 EOF

虛擬機需要關於集群和 Istio 的控制平面的信息來連接到它。為了生成所需文件,我們可以運行 getmesh istioctl x workload entry 命令。我們將所有生成的文件保存到 $WORK_DIR 中:

 $ getmesh istioctl x workload entry configure -f workloadgroup.yaml -o "${WORK_DIR}" --clusterID "${CLUSTER}"
 Warning: a security token for namespace "vm-namespace" and service account "vm-sa" has been generated and stored at "/vmfiles/istio-token" 
 configuration generation into directory /vmfiles was successful

4.4 配置虛擬機

現在是時候創建和配置一個虛擬機了。我在 GCP 中運行這個虛擬機,就像 Kubernetes 集群一樣。虛擬機使用的是 Debian GNU/Linux 10(Buster)鏡像。確保你在防火牆部分勾選了 “允許 HTTP 流量”,並且你有 SSH 訪問該實例的權限。

在這個例子中,我們在 80 端口運行一個簡單的 Python HTTP 服務器。你可以在不同的端口上配置任何其他服務。只要確保你配置了相應的安全和防火牆規則

  1. $WORK_DIR 中的文件複製到實例的主文件夾中。相應地替換 USERNAMEINSTANCE_IP
 $ scp $WORK_DIR/* [USERNAME]@[INSTANCE_IP]:~
 Enter passphrase for key '/Users/peterj/.ssh/id_rsa':
 bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
 cluster.env                                          100%  589    12.6KB/s   00:00
 hosts                                                100%   38     0.8KB/s   00:00
 istio-token                                          100%  906    19.4KB/s   00:00
 mesh.yaml                                            100%  667    14.4KB/s   00:00
 root-cert.pem                                        100% 1094    23.5KB/s   00:00

或者,你可以使用 gcloud 命令和實例名稱: gcloud compute scp --zone=us-west1-b ${WORK_DIR}/* [INSTANCE_NAME]:~

  1. SSH 進入實例,將根證書複製到 /etc/certs
 sudo mkdir -p /etc/certs
 sudo cp root-cert.pem /etc/certs/root-cert.pem
  1. 拷貝 istio-token 文件到 /var/run/secrets/tokens 目錄:
 sudo mkdir -p /var/run/secrets/tokens
 sudo cp istio-token /var/run/secrets/tokens/istio-token
  1. 下載和安裝 Istio sidecar 包:
 curl -LO //storage.googleapis.com/istio-release/releases/1.10.3/deb/istio-sidecar.deb
 sudo dpkg -i istio-sidecar.deb
  1. 拷貝 cluster.env/var/lib/istio/envoy/
 sudo cp cluster.env /var/lib/istio/envoy/cluster.env
  1. 將 Mesh 配置(mesh.yaml)添加到 /etc/istio/config/mesh
 sudo cp mesh.yaml /etc/istio/config/mesh
  1. 將 istiod host 添加到 /etc/hosts 文件中:
 sudo sh -c 'cat $(eval echo ~$SUDO_USER)/hosts >> /etc/hosts'
  1. /etc/certs/var/lib/istio/envoy 的所有者修改為 Istio proxy:
 sudo mkdir -p /etc/istio/proxy
 sudo chown -R istio-proxy /var/lib/istio /etc/certs /etc/istio/proxy /etc/istio/config /var/run/secrets /etc/certs/root-cert.pem

以上都就緒後,就可以在虛擬機中啟動 Istio:

 sudo systemctl start istio

此刻,虛擬機被配置為與 Kubernetes 集群中 Istio 的控制平面通信。

4.5 從虛擬機訪問服務

讓我們在 Kubernetes 集群中部署一個 Hello world 應用程序。首先,我們需要在 default 命名空間中啟用自動 sidecar 注入:

 $ kubectl label namespace default istio-injection=enabled
 namespace/default labeled

接下來,創建 Hello world 的部署和服務。

 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: hello-world
   labels:
     app: hello-world
 spec:
   replicas: 1
   selector:
     matchLabels:
       app: hello-world
   template:
     metadata:
       labels:
         app: hello-world
     spec:
       containers:
         - image: gcr.io/tetratelabs/hello-world:1.0.0
           imagePullPolicy: Always
           name: svc
           ports:
             - containerPort: 3000
 ---
 kind: Service
 apiVersion: v1
 metadata:
   name: hello-world
   labels:
     app: hello-world
 spec:
   selector:
     app: hello-world
   ports:
     - port: 80
       name: http
       targetPort: 3000

將上述文件保存為 hello-world.yaml,並使用 kubectl apply -f hello-world.yaml 進行部署。

等待 Pod 準備好,然後回到虛擬機上,嘗試訪問 Kubernetes 服務:

 $ curl //hello-world.default
 Hello World

你可以從虛擬機上訪問在你的 Kubernetes 集群內運行的任何服務。

4.6 在虛擬機上運行服務

我們也可以在虛擬機上運行一個工作負載。切換到實例上,運行一個簡單的 Python HTTP 服務器:

 $ sudo python3 -m http.server 80
 Serving HTTP on 0.0.0.0 port 8000 (//0.0.0.0:8000/) ...

如果你試圖直接 curl 到實例 IP,你會得到一個響應(目錄列表)。:

 $ curl [INSTANCE_IP]
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "//www.w3.org/TR/html4/strict.dt
 d">
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <title>Directory listing for /</title>
 </head>
 <body>
 <h1>Directory listing for /</h1>
 <hr>
 ...

但我們要做的是將工作負載(Python HTTP 服務)添加到網格中。出於這個原因,我們在前面創建了虛擬機命名空間。所以讓我們創建一個代表虛擬機工作負載的 Kubernetes 服務。注意,名稱和標籤值等於我們之前設置的 VM_APP 環境變量的值。不要忘記將服務部署到 VM_NAMESPACE

 apiVersion: v1
 kind: Service
 metadata:
   name: hello-vm
   labels:
     app: hello-vm
 spec:
   ports:
   - port: 80
     name: http-vm
     targetPort: 80
   selector:
     app: hello-vm

將上述文件保存為 hello-vm-service.yaml,並使用 kubectl apply -f hello-vm-service.yaml -n vm-namespace 將其部署到 VM 命名空間。

因為我們沒有使用實驗性的虛擬機自動註冊,它將自動創建 WorkloadEntry 資源,我們需要手動創建它們

我們需要一個代表虛擬機工作負載的 WorkloadEntry 資源——該資源使用虛擬機服務賬戶(SERVICE_ACCOUNT)和標籤中的應用程序名稱(VM_APP)。

請注意,我們還需要獲得虛擬機的內部 IP 地址,這樣 Istio 就知道在哪裡可以到達虛擬機。讓我們把它存儲在另一個環境變量中(確保用你的值替換 INSTANCE_NAMEZONE)。

 export VM_IP=$(gcloud compute instances describe [INSTANCE_NAME] --format='get(networkInterfaces[0].networkIP)' --zone=[ZONE])

我們現在可以創建 WorkloadEntry 資源:

 cat <<EOF > workloadentry.yaml
 apiVersion: networking.istio.io/v1alpha3
 kind: WorkloadEntry
 metadata:
   name: ${VM_APP}
   namespace: ${VM_NAMESPACE}
 spec:
   serviceAccount: ${SERVICE_ACCOUNT}
   address: ${VM_IP}
   labels:
     app: ${VM_APP}
     instance-id: vm1
 EOF

將上述文件保存為 workloadentry.yaml,然後在 $VM_NAMESPACE 命名空間創建資源:

 kubectl apply -n ${VM_NAMESPACE} -f workloadentry.yaml

為了把虛擬機的工作負載加入到網格內,我們還需要定義 ServiceEntry:

 cat <<EOF > serviceentry.yaml
 apiVersion: networking.istio.io/v1alpha3
 kind: ServiceEntry
 metadata:
   name: ${VM_APP}
 spec:
   hosts:
   - ${VM_APP}
   location: MESH_INTERNAL
   ports:
   - number: 80
     name: http
     protocol: HTTP
     targetPort: 80
   resolution: STATIC
   workloadSelector:
     labels:
       app: ${VM_APP}

請注意,WorkloadEntry 和 ServiceEntry 在未來最終將自動創建。

$VM_NAMESPACE 中創建服務條目資源。

 kubectl apply -n ${VM_NAMESPACE} -f serviceentry.yaml

我們現在可以使用 Kubernetes 服務名稱 hello-vm.vm-namespace 來訪問虛擬機上的工作負載。讓我們在集群內運行一個 Pod,並嘗試從那裡訪問該服務:

 $ kubectl run curl --image=radial/busyboxplus:curl -i --tty
 If you don't see a command prompt, try pressing enter.
 [ root@curl:/ ]$

在你得到 Pod 中的命令提示後,你可以運行 curl 並訪問工作負載。你應該看到一個目錄列表的響應。同樣地,你會注意到在 HTTP 服務器運行的實例上有一個日誌條目:

 [ root@curl:/ ]$ curl hello-vm.vm-namespace
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "//www.w3.org/TR/html4/strict.dt
 d">
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <title>Directory listing for /</title>
 </head>
 <body>
 <h1>Directory listing for /</h1>
 <hr>
 ...