あまり知られていないような気がするが、Istioは仮想マシン(VM)もMeshに取り込むことが出来る。
これを利用すると以下のような事が出来るようになる。
- Istioで保護されたネットワーク内でPod↔VM間通信が出来るようになる
→DBだけVMを使いたいようなケースでもMeshを利用することが出来る - VM側でKubernetesの名前を使ってクラスタ内のサービスにアクセスできる
(例:curl nginx.mynamespace.svc
みたいなのが通るようになる)
めちゃくちゃ便利そうなので実際に構築して挙動を確認する。
ここではIstioのVirtual Machine Installationを参考に進める。
基礎知識
ネットワークをまたぐ構成でPodとVMを直接通信する場合のアーキテクチャは以下となる。
(引用元:Virtual Machine Architecture)
IstiodはKubernetes側で用意し、DataplaneをVMに配置するような形となる。
PodとVM間はIstioのGatewayを介して通信する形となる。
注意点として、VM上に展開するサイドカーのパッケージが、RPMだとCentOS8しかサポートしていないようなので、試すならCentOS8を使うかUbuntu/Debian系のディストリビューションにしておく方が楽ができる。
前提
以下を用意する
- Kubernetesクラスタ
- VM
- istioctl
- istioctlのインストール時にダウンロードされるディレクトリ・ファイル一式
今回はKubernetesクラスタにvSphere with Tanzuで構築したKubernetesクラスタを使い、VMはUbuntuを使用した。
また検証するIstioの構成としては、
- Multi Network(VMとKubernetesで別のネットワークを利用)
- WorkloadEntry(後述)の自動作成を有効
の構成とする。
検証
事前準備
Multi Network構成ではspec.values.global.network
にKubernetes側のネットワーク名を適当に定義してIstioをインストールする。
インストールに使うYAMLファイルを作成する。
export CLUSTER=tkg-cluster-2
export CLUSTER_NETWORK=kube-network
cat << EOF > ./vm-cluster2.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio
spec:
values:
global:
meshID: mesh1
multiCluster:
clusterName: "${CLUSTER}"
network: "${CLUSTER_NETWORK}"
EOF
istioctl
でインストールする。WorkloadEntryの自動作成を有効にするために設定用パラメータもコマンドラインに追加して実行する。
istioctl install -f vm-cluster2.yaml --set values.pilot.env.PILOT_ENABLE_WORKLOAD_ENTRY_AUTOREGISTRATION=true --set values.pilot.env.PILOT_ENABLE_WORKLOAD_ENTRY_HEALTHCHECKS=true -y
East-Westで通信するためのEast-West Gatewayをインストールする。
samples/multicluster/gen-eastwest-gateway.sh \
--mesh mesh1 --cluster "${CLUSTER}" --network "${CLUSTER_NETWORK}" | \
istioctl install -y -f -
Istiodを公開するためのkind: VirtualService
とkind: Gateway
を作成し、Namespace
のistio-system
にネットワークが複数ある場合にどのネットワークを使うかのラベルを付与する。
kubectl apply -n istio-system -f samples/multicluster/expose-istiod.yaml
kubectl apply -n istio-system -f samples/multicluster/expose-services.yaml
kubectl label namespace istio-system topology.istio.io/network="${CLUSTER_NETWORK}"
VMを管理するためのNamespace
とServiceAccount
を作成する。
kubectl create ns vmtest
kubectl create sa vmtestsa -n vmtest
次にWorkloadGroup
を作成する。WorkloadGroup
というのはMeshに追加するワークロード(VM)のグループを定義したものの模様。
Pod
に対するDeployment
のようなものらしい。
なお、Pod
に相当するものはWorkloadEntry
になり、今回の手順ではこのWorkloadEntry
を自動でつくる手筈で進めている。
WorkloadGroup
の仕様はこちら参照。
ということで、以下で作成してクラスタに適用する。
cat <<EOF > workloadgroup.yaml
apiVersion: networking.istio.io/v1alpha3
kind: WorkloadGroup
metadata:
name: "ubuntu"
namespace: "vmtest"
spec:
metadata:
labels:
app: "ubuntu"
template:
serviceAccount: "vmtestsa"
network: "${CLUSTER_NETWORK}"
EOF
kubectl apply -f workloadgroup.yaml -n vmtest
ちなみにWorkloadGroup
側でVMにReadinessProbeみたいな感じでヘルスチェックを設定することも出来るが、ここでは割愛する。
作成したら、istioctl
に食わせて設定ファイルを生成する。
mkdir vmfiles
istioctl x workload entry configure -f workloadgroup.yaml -o ./vmfiles --clusterID "${CLUSTER}" --autoregister
生成されたファイルはそれぞれ以下となる。
cluster.env | サービス名やNamespaceなどの複数の環境変数を定義したファイル |
istio-token | CAから証明書を取得するのに使うKubernetesトークン |
mesh.yaml |
discoveryAddress を設定するためのProxyConfig
|
root-cert.pem | 認証に使用されるルート証明書 |
hosts | /etc/hostsに追記する内容(istio-eastwestgatewayのExternal IPとistiod.istio-system.svcの組が記載) |
ちなみにProxyConfig
とMeshConfig
の違いはWorkloadやNamespace単位など細かな単位でも設定出来るのがProxyConfig
で、Mesh全体への設定を行うのがMeshConfig
だそう。
生成されたファイルをVMに転送する。
scp ./vmfiles/* ubuntu@10.215.76.71:
以上で下準備は終了となる。
VM側の設定
ルート証明書を配置する。
sudo mkdir -p /etc/certs
sudo cp ./root-cert.pem /etc/certs/root-cert.pem
Kubernetesトークンを配置する。
sudo mkdir -p /var/run/secrets/tokens
sudo cp ./istio-token /var/run/secrets/tokens/istio-token
サイドカーをインストールする。なお、ここではDebian系のパッケージで入れたが、rpmも用意されている。(ただしCentOS8しかサポートしていない点に注意)
curl -LO https://storage.googleapis.com/istio-release/releases/1.20.2/deb/istio-sidecar.deb
sudo dpkg -i istio-sidecar.deb
ちなみにこれの中身は以下のようになっている。
$ dpkg-deb -c istio-sidecar.deb
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./lib/
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./lib/systemd/
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./lib/systemd/system/
-rw-r--r-- 0/0 382 2024-01-03 14:37 ./lib/systemd/system/istio.service
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./var/
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./var/lib/
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./var/lib/istio/
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./var/lib/istio/envoy/
-rw-r--r-- 0/0 4818 2024-01-03 14:37 ./var/lib/istio/envoy/sidecar.env
-rw-r--r-- 0/0 22679 2024-01-03 14:37 ./var/lib/istio/envoy/envoy_bootstrap_tmpl.json
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./usr/
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./usr/local/
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./usr/local/bin/
-rwxr-xr-x 0/0 44118016 2024-01-03 14:38 ./usr/local/bin/pilot-agent
-rwxr-xr-x 0/0 5847 2024-01-03 14:37 ./usr/local/bin/istio-start.sh
-rwxr-xr-x 0/0 114688376 2024-01-03 14:38 ./usr/local/bin/envoy
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./usr/share/
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./usr/share/doc/
drwxr-xr-x 0/0 0 2024-01-03 14:42 ./usr/share/doc/istio-sidecar/
-rw-r--r-- 0/0 133 2024-01-03 14:42 ./usr/share/doc/istio-sidecar/changelog.gz
また、パッケージインストール時にistio-proxy
というユーザが作成される。
環境変数を定義したファイルをEnvoyのディレクトリにコピーする。
sudo cp ./cluster.env /var/lib/istio/envoy/cluster.env
ProxyConfig
をIstioのMeshの設定ディレクトリにコピーする。
sudo cp ./mesh.yaml /etc/istio/config/mesh
istio-eastwestgatewayにアクセスするために/etc/hostsに追記する。
sudo sh -c 'cat $(eval echo ~$SUDO_USER)/hosts >> /etc/hosts'
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
以上でVM上でIstioのサイドカーを動かすための設定は終了となる。
次にIstioのサイドカーを起動する。
sudo systemctl start istio
起動すると以下のような感じのstatusとなる。
$ systemctl status istio
● istio.service - istio-sidecar: The Istio sidecar
Loaded: loaded (/lib/systemd/system/istio.service; disabled; vendor preset: enabled)
Active: active (running) since Mon 2024-01-29 07:40:15 UTC; 15s ago
Docs: http://istio.io/
Process: 24624 ExecStartPre=/usr/bin/install -d -o istio-proxy -m 0755 /var/run/secrets (code=exited, status=0/SUCCESS)
Main PID: 24625 (sudo)
Tasks: 16 (limit: 1012)
Memory: 34.5M
CPU: 2.934s
CGroup: /system.slice/istio.service
├─24625 sudo -E -u istio-proxy -s /bin/bash -c "ulimit -n 1024; INSTANCE_IP=10.215.76.71 POD_NAME=ubuntuguest POD_NAMESPACE=vmtest exec /usr/local>
├─24711 /usr/local/bin/pilot-agent proxy
└─24720 /usr/local/bin/envoy -c etc/istio/proxy/envoy-rev.json --drain-time-s 45 --drain-strategy immediate --local-address-ip-version v4 --file-f>
Jan 29 07:40:15 ubuntuguest istio-start.sh[24701]: -A ISTIO_OUTPUT -o lo -p tcp -m tcp ! --dport 53 -m owner ! --gid-owner 997 -j RETURN
Jan 29 07:40:15 ubuntuguest istio-start.sh[24701]: -A ISTIO_OUTPUT -m owner --gid-owner 997 -j RETURN
Jan 29 07:40:15 ubuntuguest istio-start.sh[24701]: -A ISTIO_OUTPUT -d 127.0.0.53/32 -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 15053
Jan 29 07:40:15 ubuntuguest istio-start.sh[24701]: -A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
Jan 29 07:40:15 ubuntuguest istio-start.sh[24701]: -A ISTIO_OUTPUT -j ISTIO_REDIRECT
Jan 29 07:40:15 ubuntuguest istio-start.sh[24701]: -A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
Jan 29 07:40:15 ubuntuguest istio-start.sh[24701]: COMMIT
Jan 29 07:40:15 ubuntuguest istio-start.sh[24701]: # Completed on Mon Jan 29 07:40:15 2024
Jan 29 07:40:15 ubuntuguest sudo[24625]: root : PWD=/ ; USER=istio-proxy ; COMMAND=/bin/bash -c '\\/bin\\/bash -c ulimit\\ -n\\ 1024\\;\\ INSTANCE_IP\\=10\>
Jan 29 07:40:15 ubuntuguest sudo[24625]: pam_unix(sudo:session): session opened for user istio-proxy(uid=997) by (uid=0)
ただし、正常にサービスが起動してもエラーとなってることもあるので、挙動がおかしいと思ったら/var/log/istio/istio.log
や/var/log/istio/istio.err.log
を確認するとよい。
また、正常に起動すればistioctl proxy-status
でホスト名.Namespace名で見えるようになる。
$ istioctl proxy-status
NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION
istio-eastwestgateway-6fdf6bdf69-tzssk.istio-system tkg-cluster-2 SYNCED SYNCED SYNCED NOT SENT NOT SENT istiod-75f9cbf8b7-249xh 1.20.2
istio-ingressgateway-846c5b8ccd-d7kwl.istio-system tkg-cluster-2 SYNCED SYNCED SYNCED NOT SENT NOT SENT istiod-75f9cbf8b7-249xh 1.20.2
ubuntuguest.vmtest tkg-cluster-2 SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-75f9cbf8b7-249xh 1.20.2
また、クラスタ内にkind: WorkloadEntry
のオブジェクトが自動生成されることも確認できる。
$ kubectl get workloadentries.networking.istio.io -n vmtest
NAME AGE ADDRESS
ubuntu-10.215.76.71 4m3s 10.215.76.71
動作確認
ここではVMからPodへのアクセス、およびPodからVMへのアクセスを確認する。
まず、検証用にVMからアクセスする検証用のPod(nginx)を起動し公開する。
SAMPLE_NS=nginx-ns
kubectl create ns $SAMPLE_NS
kubectl label namespace $SAMPLE_NS istio-injection=enabled
kubectl run nginx --image nginx -n $SAMPLE_NS
kubectl expose pod nginx --port 80 -n $SAMPLE_NS
また、VMにアクセスするPod(CentOS)も用意する。
kubectl run --image centos centos -n nginx-ns -- sleep infinity
また、VM側でも検証用にHTTPサーバを構築する。
Pythonでhttp.serverモジュールを呼べばポート8000で公開してくれる。
python3 -m http.server &
このHTTPサーバにPodからアクセスするためにはKubernetes側でService
を作成する必要があるので、以下のように作成する。
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: ubuntu-svc
namespace: vmtest
labels:
app: ubuntu
spec:
ports:
- port: 8000
name: tcp
selector:
app: ubuntu
EOF
以上で検証用のサービスの準備は完了となる。
まず、VMからPodへのアクセスを確認する。Pod間通信と同じ要領で先程作成したnginxのService(nginx.nginx-ns.svc
)にアクセスする。
$ curl nginx.nginx-ns.svc
<!DOCTYPE html>
<html>
<head>
:(省略)
問題なくアクセスできた。
次は逆向き、KubernetesクラスタからVMにアクセスしてみる。
先程作成したUbuntu用のServiceにCentOSからアクセスする。
$ kubectl exec -it centos -n nginx-ns -- curl ubuntu-svc.vmtest.svc:8000
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
:(省略)
こちらも問題なくアクセスできた。
所感
今回はVMをMeshに追加してPodとVM間で通信する方法を確認した。
特にDNSなどの設定をせずにPodとVM間をKubernetes上の名前でアクセスできるのは非常に便利だと感じた。
ただ、以下あたりはちょっと課題なのかなとも感じた。
- Control PlaneはKubernetes上に必要
→Kubernetes抜きでの構成は組めなさそう - RedHat系のサポートOSがCentOS8のみ
→エンタープライズ系のOSサポートが必要な環境だと利用が厳しい
上記の課題が将来的に解決できればService Meshの幅がかなり広がりそうなので、将来的に改善されることを期待したい。
おまけ:可視化
Kiali、Grafanaを入れて可視化してみる。
なお、本来はKubernetes内の名前を使ってVMのメトリクスを取りたかったのだが、PodからVMのメトリクスのエンドポイントへのアクセス時にエラーが出て断念し、VMのIPを直接叩いてメトリクスを取得した。
(起きた問題は多分これと同件っぽい)
まずメトリクスを取得するためにPrometheusをインストールする。
Istioに付属しているPrometheusのYAMLを以下のように修正し、VMをスクレイピングの対象とする。
--- prometheus.yaml.orig 2024-02-02 14:47:15
+++ prometheus.yaml 2024-02-02 15:42:20
@@ -43,6 +43,15 @@
- /etc/config/rules
- /etc/config/alerts
scrape_configs:
+ - job_name: ubuntu
+ scrape_interval: 15s
+ scrape_timeout: 10s
+ scheme: http
+ metrics_path: /metrics
+ static_configs:
+ - targets:
+ - 10.215.76.71:15020
+ #- ubuntu-svc.vmtest.svc:15020
- job_name: prometheus
static_configs:
- targets:
修正したManifestを使ってPrometheusをインストールする。
kubectl apply -f samples/addons/prometheus.yaml
次にIstioに付属しているKiali、Grafanaをインストールする。
kubectl apply -f samples/addons/kiali.yaml -f samples/addons/grafana.yaml
適当にPodからVMに負荷を掛けた状態でKialiにアクセスしてみる。
上手く見えているようだ。
Grafanaにもアクセスしてみる。
こちらも問題なさそうだ。
これを上手く活用すれば、Prometheusのメトリクスのエンドポイントを持たないアプリケーションを動作させているVM環境でも、Prometheusによる監視やKiali,Grafanaによる可視化を実現できそうである。