在本模块中,我们将了解如何将运行在虚拟机上的工作负载纳入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 集群和在虚拟机上运行的工作负载都在同一个网络中,它们可以直接相互通信。
控制平面的流量(配置更新、证书签署)是通过 Gateway 发送的。
虚拟机被配置了网关地址,所以它们在启动时可以连接到控制平面。
3.3 多网络架构
多网络架构横跨多个网络。Kubernetes 集群在一个网络内,而虚拟机则在另一个网络内。这使得 Kubernetes 集群中的 Pod 和虚拟机上的工作负载无法直接相互通信。
所有的流量,控制平面和 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 服务器。你可以在不同的端口上配置任何其他服务。只要确保你配置了相应的安全和防火墙规则。
- 将
$WORK_DIR
中的文件复制到实例的主文件夹中。相应地替换USERNAME
和INSTANCE_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]:~
。
- SSH 进入实例,将根证书复制到
/etc/certs
:
sudo mkdir -p /etc/certs
sudo cp root-cert.pem /etc/certs/root-cert.pem
- 拷贝
istio-token
文件到/var/run/secrets/tokens
目录:
sudo mkdir -p /var/run/secrets/tokens
sudo cp istio-token /var/run/secrets/tokens/istio-token
- 下载和安装 Istio sidecar 包:
curl -LO https://storage.googleapis.com/istio-release/releases/1.10.3/deb/istio-sidecar.deb
sudo dpkg -i istio-sidecar.deb
- 拷贝
cluster.env
到/var/lib/istio/envoy/
:
sudo cp cluster.env /var/lib/istio/envoy/cluster.env
- 将 Mesh 配置(
mesh.yaml
)添加到/etc/istio/config/mesh
:
sudo cp mesh.yaml /etc/istio/config/mesh
- 将 istiod host 添加到
/etc/hosts
文件中:
sudo sh -c 'cat $(eval echo ~$SUDO_USER)/hosts >> /etc/hosts'
- 将
/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_NAME
和 ZONE
)。
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>
...