Istio(十一):向istio服务网格中引入虚拟机

时间:2022-10-30 13:04:35

在本模块中,我们将了解如何将运行在虚拟机上的工作负载纳入istio服务网格。

向istio服务网格中引入虚拟机的前提是已经安装好了istio,关于istio的安装部署,请查看博客《Istio(二):在Kubernetes(k8s)集群上安装部署istio1.14》https://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 集群和在虚拟机上运行的工作负载都在同一个网络中,它们可以直接相互通信。

Istio(十一):向istio服务网格中引入虚拟机

控制平面的流量(配置更新、证书签署)是通过 Gateway 发送的

虚拟机被配置了网关地址,所以它们在启动时可以连接到控制平面

3.3 多网络架构

多网络架构横跨多个网络。Kubernetes 集群在一个网络内,而虚拟机则在另一个网络内。这使得 Kubernetes 集群中的 Pod 和虚拟机上的工作负载无法直接相互通信。

Istio(十一):向istio服务网格中引入虚拟机

所有的流量,控制平面和 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 https://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 https://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 http://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 (http://0.0.0.0:8000/) ...

如果你试图直接 curl 到实例 IP,你会得到一个响应(目录列表)。:

 $ curl [INSTANCE_IP]
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://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" "http://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>
 ...