Istioの新しいデータプレーンであるIstio ambient modeを使用して、サービスメッシュ内のトラフィック管理、可視化をどのように実現するのか体験します。
なお、本chapterでは、Istio、およびサービスメッシュの内容には触れていませんので、詳細に関しては、chapter_istioを参照してください。
Istio ambientモードは、サイドカーを使用しない新しいデータプレーンモードで、このモードで作成されるサービスメッシュをIstio ambient meshと呼びます。従来のサイドカーモードのIstioは多くの本番運用実績がありますが、データプレーンとアプリケーションの分離ができず、下記のような課題があげられています。
- データプレーンはサイドカーとしてアプリケーションpodに注入されるため、Istioデータプレーンのインストール、アップグレード時はpodの再起動が必要になり、アプリケーションワークロードを阻害してしまう
- データプレーンが提供する機能の選択ができないため、一部の機能(mTLS実装のみ等)しか使用しないワークロードにとっては不要なリソースをpodに確保する必要があり、全体のリソースを効率的に使用できなくなる
- HTTP準拠でないアプリケーション実装をしている場合、解析エラーや、誤ったL7プロトコルの解釈を引き起こす可能性がある
Istio ambientモードはこれらの問題を解決する目的で、Google, Solo.ioが主導して開発が始まりました。2023年2月にmain branchにマージされて以来、2024年5月にリリースされたv1.22でproduction-readyなベータ版となり、2024年11月にリリースされたv1.24でついにGAとなりました。
L4、L7機能の全てを管理しているサイドカーモードとは異なり、ambientモードではデータプレーンの機能を2つの層に分けて管理をします。
(出展元: https://istio.io/v1.16/blog/2022/introducing-ambient-mesh/)
サービスメッシュ内のワークロード間のセキュアな通信を確立する層で、ztunnelというコンポーネントによって管理されます。Ztunnelの主な役割は1)通信暗号のためのmTLS確立、2)L4レベルの認可、3)TCPメトリクス、ログ収集です。
ZtunnelはKubernetes DaemonSetとしてデプロイされます。サイドカーモードでは、Envoyが各pod内で通信のproxyをしますが、ambientモードではztunnelがメッシュ内のワークロードをnode単位でproxyします。また、サービスメッシュ内のKubernetes node間通信は、Istio v1.16リリースで公開されたHTTP/2のCONNECTメソッドをベースにしたHBONE(HTTP-Based Overlay Network Environment)というトンネリングプロトコルを用いたmTLS接続によって行われます。
(出展元: https://istio.io/v1.16/blog/2022/introducing-ambient-mesh/)
1)HTTPプロトコル、2)L7レベルの認可、3)HTTPメトリクス、ログ収集等のL7の管理をする層です。Waypoint proxyの実態はEnvoyをベースとしたIstio Proxyコンテナイメージを使用したKubernetes Gateway APIのGatewayリソースが作成、管理するKubernetes podです。Ztunnelによるsecure overlay layer作成後にKubernetes namespaceごとにwaypoint proxyを作成することで、Istioが提供するL7機能を使用することができます。また、waypoint proxyはワークロード(Kubernetes pod IP、VM IP)、または、Kubernetes service単位で作成することができます。Waypoint proxyが作成されると、ztunnelによって作成されたsecure overlay layerはトラフィックをwaypoint proxyにルーティングすることでL7機能が使えるようになります。
Note
Istio ambientモードではCNIとしてCiliumを使用することができますが、制限事項があります。chapter_cluster-createはCNIとしてCiliumを使用しているため、本chapter用に新たにKind clusterを作成します。 制限事項につきましては、 Ciliumのドキュメントを参照してください。
kind create cluster --config kind/config.yaml
Kubernetesクラスター作成後、現在のKubernetes contextがkind-istio-ambient
であることを確認してください。
kubectl config current-context
# 結果表示
kind-istio-ambient
もし、結果が異なる場合は、contextを変更してください(kind-istio-ambient
の場合はスキップしてください)。
kubectl config use-context kind-istio-ambient
現在のnodeを確認してください。
kubectl get nodes
# 実行結果
NAME STATUS ROLES AGE VERSION
istio-ambient-control-plane Ready control-plane 2m33s v1.31.0
istio-ambient-worker Ready <none> 2m17s v1.31.0
Istio ambientコンポーネントと併せて、Kiali, Prometheusをインストールします。PrometheusはKialiでグラフを表示するために必要となります。
Note
KialiはIstioサービスメッシュ用のコンソールであり、Kialiが提供するダッシュボードから、サービスメッシュの構造の確認、トラフィックフローの監視、および、サービスメッシュ設定の確認、変更をすることが可能です。本chapterでは説明は省略していますので、詳細はこちらをご確認ください。
helmfile sync -f helm/helmfile.yaml
作成されるリソースは下記のとおりです(Prometheusコンポーネントは省略しています。)
kubectl get services,daemonsets,deployments -n istio-system
# 実行結果(cluster IPは異なる可能性があります。)
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/istiod ClusterIP 10.96.45.99 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 116s
service/kiali NodePort 10.96.103.147 <none> 20001:32766/TCP 86s
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/istio-cni-node 2 2 2 2 2 kubernetes.io/os=linux 86s
daemonset.apps/ztunnel 2 2 2 2 2 kubernetes.io/os=linux 86s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/istiod 1/1 1 1 116s
deployment.apps/kiali 1/1 1 1 86s
ハンズオン用のアプリケーションをデプロイします。
kubectl apply -f ../chapter_cluster-create/manifest/app/serviceaccount.yaml -l color=blue
kubectl apply -f ../chapter_cluster-create/manifest/app/deployment.yaml -l color=blue
kubectl apply -f ../chapter_cluster-create/manifest/app/service.yaml
作成されるリソースは下記のとおりです。
kubectl get services,deployments -l app=handson
# 実行結果(cluster IPは異なる可能性があります。)
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/handson ClusterIP 10.96.47.9 <none> 8080/TCP 7s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/handson-blue 1/1 1 1 8s
アプリケーションpodがambient meshの一部になるように、デプロイ先のKubernetes namespaceにラベルを追加します。
kubectl label namespace default istio.io/dataplane-mode=ambient
ラベルが追加されたことを確認してください。
kubectl get namespace default --show-labels
# 実行結果
NAME STATUS AGE LABELS
default Active 3m43s istio.io/dataplane-mode=ambient,kubernetes.io/metadata.name=default
Ambient mesh内でアプリケーションが正しく起動しているかを確認をするために疎通確認を行います。なお、Kubernetes cluster外からはアクセス出来ないため、handsonアプリケーションのKubernetes serviceをポートフォワードしてホスト側から疎通確認を行います。
kubectl port-forward service/handson 8081:8080 >/dev/null &
アプリケーションにアクセスします。
curl -I http://127.0.0.1:8081/
HTTPステータスコード200が返却されれば疎通確認完了です。HTTPステータスコードが5XXを返した場合は、handson-blue
ワークロードを再起動して再度疎通確認を行ってください。
(HTTPステータスコードが5XXの時のみ実施。)
kubectl rollout restart deploy/handson-blue
疎通確認完了後、port forwardを実行しているjobを停止します。
jobs
# 実行結果
[1]+ Running kubectl port-forward service/handson 8081:8080 > /dev/null &
kubectl port-forward
を実行しているjobを停止。
kill %1
Kialiを用いてIstioサービスメッシュ内のトラフィックを見てみましょう。Kialiはすでにインストールされており、外部からアクセス出来るようになっています。ブラウザからhttp://kiali-ambient.example.com:28080にアクセスをしてKialiダッシュボードが表示されることを確認してください。
Ztunnelによって管理されるL4レベルのトラフィックに対し、Istio Authorization Policyを作成してアクセス管理を実装します。サービスメッシュ内において、あるワークロードに対して、特定のワークロードからのL4レベルでのアクセス制御をしたい時がユースケースとして挙げられます。本ケースでは、handson-blue
ワークロードが待ち構えているport 8080へアクセスするワークロードを2つ用意し、ひとつのワークロードからは許可を、もうひとつからは拒否をするケースを想定します。
セットアップが完了していることを前提とします。
TCPトラフィックの状態を確認するために、TOP画面左のサイドメニューのTraffic Graph
をクリックし、下記のとおり設定をしてください。
Namespace
のdefault
にチェック
Traffic
のTcp
のみにチェック
Versioned app graph
からWorkload graph
に変更
グラフの種類は他にも選択できますが、今回はアプリケーションのバージョン管理を行なっていないため、ワークロード単位でトラフィックの流れを可視化できるようにWorkload graph
を選択しています。グラフの種類はIstio公式ドキュメントである、Visualizing Your Mesh#Generating a graphの第9項を参照してください。
- グラフ更新期間を
Every 1m
からEvery 10s
に変更
handson-blue
ワークロードにアクセスする追加のワークロード2つをデプロイします。
kubectl apply -f app/curl-allow.yaml,app/curl-deny.yaml
作成されるリソースは下記の通りです。
kubectl get pods -l content=layer4-authz
# 実行結果
NAME READY STATUS RESTARTS AGE
curl-allow 1/1 Running 0 9s
curl-deny 1/1 Running 0 9s
それではcurl-allow
, curl-deny
podから、handson-blue
ワークロードに対してリクエストを送信します。
while :; do
kubectl exec curl-allow -- /bin/sh -c "echo -n 'curl-allow: ';curl -s -o /dev/null handson:8080 -w '%{http_code}\n'";
kubectl exec curl-deny -- /bin/sh -c "echo -n 'curl-deny: ';curl -s -o /dev/null handson:8080 -w '%{http_code}\n'";
echo ----------------;sleep 1;
done
双方のワークロードからのリクエストが成功していることが分かります。
# 出力結果
curl-allow: 200
curl-deny: 200
----------------
curl-allow: 200
curl-deny: 200
----------------
curl-allow: 200
curl-deny: 200
----------------
.
.
.
リクエスト送信を続けたまま、Kiali dashboardからトラフィックを確認してみましょう。curl-allow
, curl-deny
podのワークロードがhandson-blue
ワークロードにアクセス出来ていることが確認できます(紺色の矢印はTCP通信を表しています)。グラフが表示されない場合は、Kialiダッシュボード右上の青いRefresh
ボタンを押して状態を更新してください。
確認ができたら、リクエストを停止してください。
それでは、Istio Authorization Policyを作成して、curl-deny
ワークロードからのport 8080宛のリクエストを拒否する設定を追加します。
kubectl apply -f networking/L4-authorization-policy.yaml
作成されるリソースは下記の通りです。
kubectl get authorizationpolicies -l content=layer4-authz
# 実行結果
NAME ACTION AGE
layer4-authz DENY 5s
再度リクエストをします。
while :; do
kubectl exec curl-allow -- /bin/sh -c "echo -n 'curl-allow: ';curl -s -o /dev/null -w '%{http_code}\n' handson:8080"
kubectl exec curl-deny -- /bin/sh -c "echo -n 'curl-deny: ';curl -s -o /dev/null -w '%{http_code}\n' handson:8080"
echo ----------------;sleep 1
done
しばらくすると、curl-deny
podからのリクエストは拒否されます。
# 出力結果例
curl-allow: 200
curl-deny: 200
----------------
curl-allow: 200
curl-deny: 200
----------------
curl-allow: 200
curl-deny: 000
command terminated with exit code 56
----------------
curl-allow: 200
curl-deny: 000
command terminated with exit code 56
----------------
curl-allow: 200
curl-deny: 000
command terminated with exit code 56
----------------
.
.
.
Http status code 000はレスポンスが何もなかったという意味で、command terminated with exit code 56
はcurlがデータを何も受け取らなかった(コネクションがリセットされた)ということを意味しています(参考: curl man page/Exit Codes 56)。
リクエストを流したまま、再度Kiali dashboardを見てみましょう。しばらくすると、curl-allow
podからのリクエストのみグラフに表示されるようになります(グラフに変化が見られない場合は、Kialiダッシュボード右上の青いRefresh
ボタンを押して状態を更新してください)。これはcurl-deny
podからのport 8080のリクエストをztunnelがAuthorization Policyの設定に基づいてhandson-blue
ワークロードへのproxyを拒否しているためです。
リクエストを停止してください。
それでは、Istio Authorization Policyによってアクセスが拒否されたcurl-deny
podのみからリクエストをしてztunnelのログを見てみましょう。
ログを見る前に、まずは各podのIPを確認します。
kubectl get pods -o=custom-columns='Name:.metadata.name, IP:.status.podIP'
# 実行結果(pod IPは異なる可能性があります。)
Name IP
curl-allow 10.244.1.14
curl-deny 10.244.1.13
handson-blue-5cb87cdfc8-24npj 10.244.1.12
curl-deny
podからhandson
サービスに向けてリクエストをします。
for _ in $(seq 2); do
kubectl exec curl-deny -- /bin/sh -c "curl -s -o /dev/null handson:8080 -w '%{http_code}\n'"
sleep 1
done
# 出力結果
000
command terminated with exit code 56
000
command terminated with exit code 56
それではztunnelのログを確認します。
ZTUNNEL_POD=$(kubectl get pod -n istio-system -l app=ztunnel --field-selector=spec.nodeName=istio-ambient-worker -o=jsonpath={.items..metadata.name})
kubectl logs "$ZTUNNEL_POD" -n istio-system --tail 4
# 実行結果(jqコマンドで成形をしています。)
{
"level": "error",
"time": "2024-10-19T01:39:58.302146Z",
"scope": "access",
"message": "connection complete",
"src.addr": "10.244.1.13:52252",
"src.workload": "curl-deny",
"src.namespace": "default",
"src.identity": "spiffe://cluster.local/ns/default/sa/curl-deny",
"dst.addr": "10.244.1.12:15008",
"dst.hbone_addr": "10.244.1.12:8080",
"dst.service": "handson.default.svc.cluster.local",
"dst.workload": "handson-blue-5cb87cdfc8-24npj",
"dst.namespace": "default",
"dst.identity": "spiffe://cluster.local/ns/default/sa/handson-blue",
"direction": "inbound",
"bytes_sent": 0,
"bytes_recv": 0,
"duration": "0ms",
"error": "connection closed due to policy rejection"
}
{
"level": "error",
"time": "2024-10-19T01:39:58.302311Z",
"scope": "access",
"message": "connection complete",
"src.addr": "10.244.1.13:35262",
"src.workload": "curl-deny",
"src.namespace": "default",
"src.identity": "spiffe://cluster.local/ns/default/sa/curl-deny",
"dst.addr": "10.244.1.12:15008",
"dst.hbone_addr": "10.244.1.12:8080",
"dst.service": "handson.default.svc.cluster.local",
"dst.workload": "handson-blue-5cb87cdfc8-24npj",
"dst.namespace": "default",
"dst.identity": "spiffe://cluster.local/ns/default/sa/handson-blue",
"direction": "outbound",
"bytes_sent": 0,
"bytes_recv": 0,
"duration": "0ms",
"error": "http status: 401 Unauthorized"
}
.
.
.
出力されたログは2行で1セットになっています。1行目がinbound用、つまりリクエストを受ける側、2行目がoutbount用、リクエストを送る側です。これを念頭に、出力されたログを見てみましょう。
-
Inbound方向では、
curl-deny
pod(IP address: 10.244.1.13)からHBONE経由でのhandson-blue-5cb87cdfc8-24npj
(IP address: 10.244.1.12)へのリクエストは、拒否ポリシーによってコネクションが閉じられていることが分かります。 -
Outbound方向では、
curl-deny
podからhandson-blue-5cb87cdfc8-24npj
podへのリクエストはクライアントエラー(HTTP status: 401)によって失敗しています。
この2つのログをまとめると、curl-deny
podからhandson-blue-5cb87cdfc8-24npj
podへのリクエストは先に設定したIstio Authorization Policyにより拒否された結果、outbound方向では401エラーを、inbound方向では、リクエストが到達せず、結果コネクションが閉じられたということになります。
なお、ログの出力項目にあるsrc.identity="spiffe://~~"
に関して、IstioはmTLSによるサービスメッシュ内のサービス間通信を実現させるための方法としてSPIFF(Secure Production Identity Framework for Everyone)を採用しています。SPIFFEは、クラウドやコンテナ化された環境で動作するサービスやアプリケーションに対して、標準化されたアイデンティティの枠組みを提供するためのオープンスタンダードで、従来のユーザー認証の代わりに、サービス(マイクロサービスやアプリケーション)同士が相互に信頼できるアイデンティティを持つように設計されており、SPIFF IDと呼ばれるアイデンティティを使用してmTLSによる認証と暗号化を実現します。SPIFFの詳細に関しては、こちらをご確認ください。
Ztunnelが管理するIstio ambient mesh内のL4レベルのトラフィックにおいて、Istio Authorization Policyを使用してアクセス管理を実装しました。Istioの機能を使うことで、アプリケーション側にロジックを追加することなくL4レベルのアクセス管理を実現することができます。
kubectl delete -f networking/L4-authorization-policy.yaml
kubectl delete -f app/curl-allow.yaml,app/curl-deny.yaml
handson-blue
podのみが動作していることを確認してください。
kubectl get pods
# 実行結果(pod名は異なる可能性があります。)
NAME READY STATUS RESTARTS AGE
handson-blue-5cb87cdfc8-24npj 1/1 Running 0 14m
Waypoint proxyによって管理されるL7レベルのトラフィックに対し、Istio Authorization Policyを作成してアクセス管理を実装します。サービスメッシュ内の、あるワークロードに対して、特定のワークロードからのL7レベルでのアクセス制御をしたい時がユースケースとして挙げられます。本ケースではhandson-blue
ワークロードにアクセスをするワークロードを1つ用意し、GETメソッドのみ許可(削除、更新系のメソッドは拒否)をするケースを想定します。
セットアップが完了していることを前提とします。
HTTPトラフィックの状態を確認するために、TOP画面左のサイドメニューのTraffic Graphをクリックし、下記のとおり設定してください(設定済みの項目はスキップしてください。)
Namespace
のdefault
にチェック
Traffic
のHttp
のみにチェック
Versioned app graph
からWorkload graph
に変更
- グラフ更新期間を
Every 1m
からEvery 10s
に変更
Waypoint proxyを有効にするにはKubernetes Gateway APIのgateway
リソースが必要になるため、まずはKubernetes Gateway API CRDsをインストールします。
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml
# 実行結果
customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/grpcroutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io created
Note
Kubernetes Gateway APIはSIG-Networkコミニティによって開発が行われている、Kubernetesサービスを外部公開するために用いられるKubernetes APIリソースです。本chapterでは説明は省略していますので、詳細はこちらをご確認ください。
Kubernetes Gateway APIのgateway
リソースを作成して、waypoint proxyをデプロイします。
kubectl apply -f networking/k8s-gateway.yaml
作成されるリソースは下記の通りです。
kubectl get gateways,deployments,services -l app.kubernetes.io/component=waypoint-proxy
# 実行結果(gateway address, cluster IPは異なる可能性があります。)
NAME CLASS ADDRESS PROGRAMMED AGE
gateway.gateway.networking.k8s.io/waypoint istio-waypoint 10.96.172.234 True 7s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/waypoint 1/1 1 1 7s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/waypoint ClusterIP 10.96.172.234 <none> 15021/TCP,15008/TCP 7s
Waypoint proxyをデプロイしただけでは、サービスメッシュ内のリソースはこのwaypoint proxyを使用しません。Ztunnelを通過したトラフィックがwaypoint proxyを経由するようにするために、対象のwaypoint proxy名を、先ほど作成したKubernetes gatewayリソースのnamespaceにラベルとして追加する必要があります。
kubectl label namespace default istio.io/use-waypoint=waypoint
該当のKubernetes namespaceにラベルが追加されたか確認をします。
kubectl get namespace default --show-labels
# 実行結果
NAME STATUS AGE LABELS
default Active 154m istio.io/dataplane-mode=ambient,istio.io/use-waypoint=waypoint,kubernetes.io/metadata.name=default
Note
Waypoint proxyを使用するKubernetesリソースを特定のネームスペース全体ではなく、Kubernetesサービスで指定することも可能です。その場合は、対象のKubernetesサービスにistio.io/use-waypoint=/対象のwaypoint proxy名/
のラベルを付与することで実現が可能です。
これで、waypoint proxyを使用する設定が完了したので、このwaypoint proxyを使用するリソースをデプロイします。
handson-blue
ワークロードにアクセスするpodをデプロイします。
kubectl apply -f app/curl.yaml
作成されるリソースは下記の通りです。
kubectl get pods,serviceaccounts -l content=layer7-authz
# 実行結果
NAME READY STATUS RESTARTS AGE
pod/curl 1/1 Running 0 26s
NAME SECRETS AGE
serviceaccount/curl 0 26s
それでは、curl
podからhandson-blue
ワークロードに対してリクエストをします。
while :; do kubectl exec curl -- curl -s -o /dev/null handson:8080 -w '%{http_code}\n';sleep 1;done
リクエストは成功していることを確認してください。
# 出力結果
200
200
200
.
.
.
Kiali dashboardからも確認してみましょう。リクエストを流した状態でブラウザからhttp://kiali-ambient.example.com:28080にアクセスをしてください。curl
podからhandson-blue
ワークロードにアクセス出来ていることが確認できます(緑色の矢印はHTTP通信を表しています)。グラフが表示されない場合は、Kialiダッシュボード右上の青いRefreshボタンを押して状態を更新してください。
確認ができたら、リクエストを停止してください。
それでは、Istio Authorization Policyを適用して、curl ワークロードからのGETリクエストのみを許可し、削除、更新系のメソッドを拒否します。
kubectl apply -f networking/L7-authorization-policy.yaml
作成されたリソースは下記の通りです。
kubectl get authorizationpolicies -l content=layer7-authz
# 実行結果
NAME ACTION AGE
layer7-authz DENY 4s
まずは確認のためにGETリクエストをします(明示的にGETを指定しています)。
while :; do kubectl exec curl -- curl -s -X GET -o /dev/null -w '%{http_code}\n' handson:8080;sleep 1;done
先ほどと同じく、リクエストが成功していることを確認してください。
# 実行結果
200
200
200
.
.
.
リクエストを停止してください。
それでは、POSTメソッドでリクエストをしてみましょう。handson-blue
ワークロードにPOSTメソッドは実装されていないので、空データを使用します。
while :; do kubectl exec curl -- curl -X POST -s -o /dev/null -d '{}' -w '%{http_code}\n' handson:8080;sleep 1;done
しばらくすると、403にて拒否されるようになります。
# 実行結果
200
200
403
403
403
.
.
.
改めてKiali dashboardから確認してみましょう。ブラウザからhttp://kiali-ambient.example.com:28080にアクセスをしてください。しばらくすると、curl
ワークロードからのPOSTリクエストは拒否されていることが確認できます(赤色の矢印は4XXのエラーを表しています)。グラフに変化が見られない場合は、Kialiダッシュボード右上の青いRefresh
ボタンを押して状態を更新してください。
確認ができたらリクエストを停止してください。
それでは、Istio Authorization Policyを有効にしたことによって、ztunnelとwaypoint proxyが、それぞれどのような動きをしたのかログを確認してみましょう。
まずは各service, podのIPを確認します。
echo
echo "> Kubernetes service:"
kubectl get svc -o custom-columns="Name:.metadata.name,cluster IP:.spec.clusterIP"
echo "-------------------------------------------"
echo "> Kubernetes pods:"
kubectl get pods -o=custom-columns='Name:.metadata.name, IP:.status.podIP'
# 実行結果(cluster IP, pod IPは異なる可能性があります。)
> Kubernetes service:
Name cluster IP
handson 10.96.47.9
kubernetes 10.96.0.1
waypoint 10.96.172.234
-------------------------------------------
> Kubernetes pods:
Name IP
curl 10.244.1.16
handson-blue-5cb87cdfc8-24npj 10.244.1.12
waypoint-57f4fcd686-6vwck 10.244.1.15
curl
podからhandon
サービスに向けてリクエストをします。
for _ in $(seq 2); do
kubectl exec curl -- curl -X POST -s -o /dev/null -d '{}' -w '%{http_code}\n' handson:8080
sleep 1
done
# 実行結果
403
403
それでは、ztunnelのログを見てみましょう。
ZTUNNEL_POD=$(kubectl get pod -n istio-system -l app=ztunnel --field-selector=spec.nodeName=istio-ambient-worker -o=jsonpath={.items..metadata.name})
kubectl logs "$ZTUNNEL_POD" -n istio-system --tail 2
# 実行結果(jqコマンドで成形をしています。)
{
"level": "info",
"time": "2024-10-19T02:01:06.549342Z",
"scope": "access",
"message": "connection complete",
"src.addr": "10.244.1.16:40270",
"src.workload": "curl",
"src.namespace": "default",
"src.identity": "spiffe://cluster.local/ns/default/sa/curl",
"dst.addr": "10.244.1.15:15008",
"dst.hbone_addr": "10.96.47.9:8080",
"dst.service": "handson.default.svc.cluster.local",
"dst.workload": "waypoint-57f4fcd686-6vwck",
"dst.namespace": "default",
"dst.identity": "spiffe://cluster.local/ns/default/sa/waypoint",
"direction": "outbound",
"bytes_sent": 147,
"bytes_recv": 239,
"duration": "5ms"
}
.
.
.
curl
pod(IP address: 10.244.1.16)からhandson-blue
ワークロードへのリクエストは、HBONEトネリングプロトコルを用いて行なっています。ここで注目するべきは、先述したようにztunnelはL4管理しか行わないということです。L7管理はwaypoint proxyによって行われるため、ztunnelの仕事はやってきたトラフィックをwaypoit proxyにproxyするまでです。ログからも見てわかるように、dst.service
ではクライアントが最終的に到達をしようとしているKubernetes serviceを示していますが、dst.workload
はwaypoint podになっており、これはztunnelが実際にリクエストを送信(厳密にはproxy)する宛先となっています。クライアントであるcurl
podからは実際403が返却されていますが、ztunnelからは通信は無事に終了し、message
にはconnection complete
と出力されています。
それでは、次は、waypoint proxyのログを確認してみましょう。
WAYPOINT_PROXY_POD=$(kubectl get pods -l app.kubernetes.io/component=waypoint-proxy -o=jsonpath={.items..metadata.name})
kubectl logs "$WAYPOINT_PROXY_POD" --tail 2
# 実行結果(jqコマンドで成形をしています。)
{
"bytes_received": 0,
"x_forwarded_for": null,
"route_name": "default",
"path": "/",
"upstream_local_address": null,
"request_id": "ebcf8abf-d7b9-410c-a218-5e68d829e073",
"upstream_cluster": "inbound-vip|8080|http|handson.default.svc.cluster.local;",
"upstream_host": null,
"method": "POST",
"authority": "handson:8080",
"start_time": "2024-10-19T02:01:06.547Z",
"upstream_transport_failure_reason": null,
"duration": 0,
"connection_termination_details": null,
"downstream_local_address": "10.96.47.9:8080",
"response_flags": "-",
"bytes_sent": 19,
"protocol": "HTTP/1.1",
"downstream_remote_address": "10.244.1.16:34498",
"requested_server_name": null,
"user_agent": "curl/8.10.1",
"response_code_details": "rbac_access_denied_matched_policy[ns[default]-policy[layer7-authz]-rule[0]]",
"response_code": 403,
"upstream_service_time": null
}
.
.
.
Ztunnelからトラフィックのproxyを受けたwaypoint proxyは、upstream_cluster
としてhandson
serviceを認識していますが、response_code_details
では先ほど設定をしたIstio Authorization Policy(layer7-authz)の1つ目のルールによって403を返却していることが確認できます。
最後にDELETEメソッドも拒否されるか確認してみましょう。handson-blue
ワークロードにDELETEメソッドは実装されていないので、ダミーIDを削除することとします。
while :; do kubectl exec curl -- curl -X DELETE -s -o /dev/null -w '%{http_code}\n' handson:8080/id/123;sleep 1;done
こちらも、403にて拒否されることを確認してください。
# 実行結果
403
403
403
.
.
.
確認ができたら、リクエストを停止してください。
Waypoint proxyが管理するIstio ambient mesh内のL7レベルのトラフィックにおいて、Istio Authorization Policyを使用してアクセス管理を実装しました。Istioの機能を使うことで、アプリケーション側にロジックを追加することなくL7レベルのアクセス管理を実現することができます。
kubectl delete -f networking/L7-authorization-policy.yaml,networking/k8s-gateway.yaml
kubectl delete -f app/curl.yaml
サイドカーを用いないIstioの新しいデータプレーンである、Istio ambientモードによって作成されるIstio ambient meshを使用することで、アプリケーションと、データプレーンの分離が可能になります。これにより、データプレーン起因によるアプリケーションワークロードの阻害を防止することができます。さらに、サイドカーを使用せずに、ztunnel, waypoint proxyを用いることにより、L4, L7管理をアプリケーションの必要に応じて実装することができるようになります。
本chapter用に作成したKindクラスターを削除します。
kind delete cluster --name istio-ambient
すでにchapter_cluster-createでkindクラスターを作成していて、他のchapterに進む場合は、クラスター削除後にKubernetes contextをkind-kind
に設定してください。
kubectl config use-context kind-kind
現在のKubernetes contextのがkind-kind
であることを確認してください。
kubectl config current-context
# 実行結果
kind-kind