IstioのBookinfoサンプルをApp Meshで動かしてみたメモ。
App Meshのコントローラーと、ALB Ingress Controllerが導入された環境を利用する。それぞれのセットアップについては以下を参照。
環境
コンポーネント | バージョン | 備考 |
---|---|---|
eksctl | 0.16.0 | |
Kubernetes バージョン | 1.14 | |
プラットフォームのバージョン | eks.9 | |
Helm | v3.1.2 | |
appmesh-controllerチャート | 0.4.0 | |
appmesh-controller | 0.3.0 | |
appmesh-injectチャート | 0.11.0 | |
appmesh-inject | 0.4.0 | |
aws-alb-ingress-controller | v1.1.4 | |
Istio Bookinfo | 1.5.1 |
サンプルのダウンロード
Istioをダウンロードする。使うのはサンプルのyamlだけ。
ISTIO_VERSION="1.5.1" curl -L https://istio.io/downloadIstio | sh -
サンプルアプリケーションのデプロイ
Namespacesを作成する。
kubectl create namespace bookinfo
サンプルアプリケーションをデプロイする。
kubectl -n bookinfo apply \ -f istio-${ISTIO_VERSION}/samples/bookinfo/platform/kube/bookinfo.yaml
確認する。まだサイドカーのインジェクションを有効にしていないので1/1
となっている。
$ kubectl -n bookinfo get pod,svc --show-labels NAME READY STATUS RESTARTS AGE LABELS pod/details-v1-c5b5f496d-v2ljf 1/1 Running 0 11s app=details,pod-template-hash=c5b5f496d,version=v1 pod/productpage-v1-7d6cfb7dfd-n4787 1/1 Running 0 10s app=productpage,pod-template-hash=7d6cfb7dfd,version=v1 pod/ratings-v1-f745cf57b-hzvlf 1/1 Running 0 11s app=ratings,pod-template-hash=f745cf57b,version=v1 pod/reviews-v1-85c474d9b8-8njvh 1/1 Running 0 11s app=reviews,pod-template-hash=85c474d9b8,version=v1 pod/reviews-v2-ccffdd984-ljt22 1/1 Running 0 11s app=reviews,pod-template-hash=ccffdd984,version=v2 pod/reviews-v3-98dc67b68-vshh7 1/1 Running 0 11s app=reviews,pod-template-hash=98dc67b68,version=v3 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS service/details ClusterIP 10.100.89.229 <none> 9080/TCP 11s app=details,service=details service/productpage ClusterIP 10.100.209.14 <none> 9080/TCP 11s app=productpage,service=productpage service/ratings ClusterIP 10.100.235.113 <none> 9080/TCP 11s app=ratings,service=ratings service/reviews ClusterIP 10.100.40.20 <none> 9080/TCP 11s app=reviews,service=reviews
なお、例えばreviews
Serviceのセレクターはapp=reviews
となっていて、reviews-v1
、reviews-v2
、reviews-v3
のDeployemntのPodは皆このラベルを持っている。
App MeshにはGatewayの概念がないので、まず、ポートフォワードでproductpage
にアクセスしてみる。
kubectl port-forward service/productpage 8888:9080 -n bookinfo
http://localhost:8888/productpageにアクセスすると、おなじみのBookinfoにアクセスできた。reviews
Serviceはランダムに振ってるだけなので、リロードするとランダムに変わる。
productpage
ServiceをALB Ingress Controllerで外部公開する。
cat <<EOF > productpage-ingress.yaml apiVersion: extensions/v1beta1 kind: Ingress metadata: name: productpage annotations: kubernetes.io/ingress.class: alb alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip labels: app: productpage spec: rules: - http: paths: - path: /* backend: serviceName: productpage servicePort: 9080 EOF kubectl apply -f productpage-ingress.yaml -n bookinfo
Ingressを確認する。
$ kubectl get ingress -n bookinfo NAME HOSTS ADDRESS PORTS AGE productpage * 8276906b-bookinfo-productp-c7b2-1223038083.ap-northeast-1.elb.amazonaws.com 80 7s
ALBが作成されるので、このALB経由でもアクセスできることを確認する。
サイドカーインジェクションの有効化
bookinfo
Namespaceにラベルをつけてサイドカーのオートインジェクションを有効にする。
kubectl label \ namespace bookinfo appmesh.k8s.aws/sidecarInjectorWebhook=enabled
Podを削除してサイドカーをインジェクションさせる。
kubectl get pod -n bookinfo -o json | jq -r '.items[].metadata.name' | xargs kubectl delete pod -n bookinfo
サイドカーがインジェクションされたことを確認する。
$ kubectl get pod -n bookinfo NAME READY STATUS RESTARTS AGE details-v1-c5b5f496d-5qd5t 2/2 Running 0 53s productpage-v1-7d6cfb7dfd-5tzr9 2/2 Running 0 53s ratings-v1-f745cf57b-25sbh 2/2 Running 0 53s reviews-v1-85c474d9b8-j99bx 2/2 Running 0 53s reviews-v2-ccffdd984-mmtc9 2/2 Running 0 53s reviews-v3-98dc67b68-xr85m 2/2 Running 0 53s
再びALB経由で/productpage
にアクセスすると、502 Bad Gateway
となり繋がらない。ALBのターゲットがunhealthy
となっている。
ポートフォワードして確認してみる。
kubectl port-forward service/productpage 8888:9080 -n bookinfo
ページは表示されるが、バックエンドにはアクセスできていない。
Istioの場合は、サイドカーをインジェクションしただただけだとそのまま動くが、App Meshの場合はちゃんとCRDを定義してあげないといけないと思われる。
BookinfoのApp Mesh対応
BookinfoがApp Meshで動くようにする。
Meshの作成
メッシュを作成する。
cat <<EOF > bookinfo-mesh.yaml apiVersion: appmesh.k8s.aws/v1beta1 kind: Mesh metadata: name: bookinfo EOF kubectl apply -f bookinfo-mesh.yaml -n bookinfo
K8s ServiceとVirtualNode/VirtualServiceの関係
IstioとApp Meshではアプリケーションの各バージョンの扱い方が異なる。IstioではDestnationRuleでサブセットを定義する。
App Meshではバージョン毎にVirtualNodeを作成する。VirtualNodeはK8s Deploymentに対応するが、エンドポイントが必要なので、各バージョン毎のK8s Serviceが必要となる。すなわちVirtualNodeはK8s Serviceに対応するともいえる。
このため、2種類のやり方がある。
その1
- 各バージョンのDeploymentに対応するバージョン付きのK8s Serviceを作成し、VirtualNodeに対応させる
- こちらはセレクターが必要
- バージョン番号を持たないK8s Serviceを作成する
- このやり方はEKSでは可能だが、ECSではできないかも知れない
その2
- ベースバージョンのDeploymentについては、対応するサービスとしてバージョンを持たないK8s Serviceを作成し、これをVirtualNodeに対応させる
- その他のバージョンについては、各バージョンのDeploymentに対応するバージョンつきのK8s Serviceを作成し、VirtualNodeに対応させる
- こちらはセレクターが必要
- このやり方の場合、作成するK8s Serviceが一つ少なくて済むが、全てのバージョンが対称ではないので、わかりにくい
- App Meshの公式サンプルはこうなっているものがある気がする
Serviceの作成
今回は複数のバージョンを持つreviews
Serviceについて、「その1」のやり方でやることにする。それ以外については、バージョンが1つしかないので、「その2」のやり方とする。
reviews
Serviceについては各バージョン付きのDeploymentに対応するServiceが追加で必要となるので作成する。
cat <<EOF > services.yaml apiVersion: v1 kind: Service metadata: name: reviews-v1 spec: type: ClusterIP ports: - name: http port: 9080 protocol: TCP targetPort: 9080 selector: app: reviews version: v1 --- apiVersion: v1 kind: Service metadata: name: reviews-v2 spec: type: ClusterIP ports: - name: http port: 9080 protocol: TCP targetPort: 9080 selector: app: reviews version: v2 --- apiVersion: v1 kind: Service metadata: name: reviews-v3 spec: type: ClusterIP ports: - name: http port: 9080 protocol: TCP targetPort: 9080 selector: app: reviews version: v3 EOF kubectl apply -f services.yaml -n bookinfo
作ったServiceを確認する。
$ kubectl get svc -n bookinfo NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE details ClusterIP 10.100.89.229 <none> 9080/TCP 19m productpage ClusterIP 10.100.209.14 <none> 9080/TCP 19m ratings ClusterIP 10.100.235.113 <none> 9080/TCP 19m reviews ClusterIP 10.100.40.20 <none> 9080/TCP 19m reviews-v1 ClusterIP 10.100.210.216 <none> 9080/TCP 35s reviews-v2 ClusterIP 10.100.154.102 <none> 9080/TCP 34s reviews-v3 ClusterIP 10.100.161.99 <none> 9080/TCP 34s
VirtualNodeの作成
VirtualNodeを作成する。VirtualNodeはバージョン付きのK8s Serviceに対応する形で必要。
cat <<EOF > virtualnodes.yaml apiVersion: appmesh.k8s.aws/v1beta1 kind: VirtualNode metadata: name: productpage spec: meshName: bookinfo listeners: - portMapping: port: 9080 protocol: http serviceDiscovery: dns: hostName: productpage.bookinfo.svc.cluster.local backends: - virtualService: virtualServiceName: reviews - virtualService: virtualServiceName: details --- apiVersion: appmesh.k8s.aws/v1beta1 kind: VirtualNode metadata: name: details spec: meshName: bookinfo listeners: - portMapping: port: 9080 protocol: http serviceDiscovery: dns: hostName: details.bookinfo.svc.cluster.local --- apiVersion: appmesh.k8s.aws/v1beta1 kind: VirtualNode metadata: name: ratings spec: meshName: bookinfo listeners: - portMapping: port: 9080 protocol: http serviceDiscovery: dns: hostName: ratings.bookinfo.svc.cluster.local --- apiVersion: appmesh.k8s.aws/v1beta1 kind: VirtualNode metadata: name: reviews-v1 spec: meshName: bookinfo listeners: - portMapping: port: 9080 protocol: http serviceDiscovery: dns: hostName: reviews-v1.bookinfo.svc.cluster.local --- apiVersion: appmesh.k8s.aws/v1beta1 kind: VirtualNode metadata: name: reviews-v2 spec: meshName: bookinfo listeners: - portMapping: port: 9080 protocol: http serviceDiscovery: dns: hostName: reviews-v2.bookinfo.svc.cluster.local backends: - virtualService: virtualServiceName: ratings --- apiVersion: appmesh.k8s.aws/v1beta1 kind: VirtualNode metadata: name: reviews-v3 spec: meshName: bookinfo listeners: - portMapping: port: 9080 protocol: http serviceDiscovery: dns: hostName: reviews-v3.bookinfo.svc.cluster.local backends: - virtualService: virtualServiceName: ratings EOF kubectl apply -f virtualnodes.yaml -n bookinfo
VirtualNodeを確認する。
$ kubectl get virtualnode -n bookinfo
NAME AGE
details 48s
productpage 49s
ratings 48s
reviews-v1 48s
reviews-v2 48s
reviews-v3 48s
VirtualServiceの作成
VirtualServiceはアプリケーションの通信の宛先なので、元々のK8s Serviceの単位で作成する。
cat <<EOF > virtualservices.yaml apiVersion: appmesh.k8s.aws/v1beta1 kind: VirtualService metadata: name: details spec: meshName: bookinfo virtualRouter: name: details-router routes: - name: details-route http: match: prefix: / action: weightedTargets: - virtualNodeName: details weight: 100 --- apiVersion: appmesh.k8s.aws/v1beta1 kind: VirtualService metadata: name: ratings spec: meshName: bookinfo virtualRouter: name: ratings-router routes: - name: ratings-route http: match: prefix: / action: weightedTargets: - virtualNodeName: ratings weight: 100 --- apiVersion: appmesh.k8s.aws/v1beta1 kind: VirtualService metadata: name: reviews spec: meshName: bookinfo virtualRouter: name: reviews-router routes: - name: reviews-route http: match: prefix: / action: weightedTargets: - virtualNodeName: reviews-v1 weight: 50 - virtualNodeName: reviews-v2 weight: 50 EOF kubectl apply -f virtualservices.yaml -n bookinfo
確認する。
$ kubectl get virtualservice -n bookinfo
NAME AGE
details 10s
ratings 10s
reviews 10s
操作確認すると、まだ通信出来ない。
Envoyコンテナの環境変数
サイドカーのEnvoyコンテナの環境変数にAPPMESH_VIRTUAL_NODE_NAME
という環境変数があるが、この値がおかしいようだ。自動でDeployment名から定義されている。メッシュ名も意図とは異なる別のメッシュが指定されている。
$ k get po -n bookinfo -o yaml | grep -A 1 APPMESH_VIRTUAL_NODE_NAME - name: APPMESH_VIRTUAL_NODE_NAME value: mesh/dj-app/virtualNode/details-v1-bookinfo -- - name: APPMESH_VIRTUAL_NODE_NAME value: mesh/dj-app/virtualNode/productpage-v1-bookinfo -- - name: APPMESH_VIRTUAL_NODE_NAME value: mesh/dj-app/virtualNode/ratings-v1-bookinfo -- - name: APPMESH_VIRTUAL_NODE_NAME value: mesh/dj-app/virtualNode/reviews-v1-bookinfo -- - name: APPMESH_VIRTUAL_NODE_NAME value: mesh/dj-app/virtualNode/reviews-v2-bookinfo -- - name: APPMESH_VIRTUAL_NODE_NAME value: mesh/dj-app/virtualNode/reviews-v3-bookinfo
APPMESH_VIRTUAL_NODE_NAME
という環境変数のもととなるメッシュ名と仮想ノード名はPodのアノテーションで指定できる。
istio-${ISTIO_VERSION}/samples/bookinfo/platform/kube/bookinfo.yaml
をコピーし、各DeploymentのPodTemplateを修正し、アノテーションを追加する。
$ diff bookinfo.yaml bookinfo-appmesh.yaml 70a71,73 > annotations: > appmesh.k8s.aws/mesh: bookinfo > appmesh.k8s.aws/virtualNode: details-bookinfo 121a125,127 > annotations: > appmesh.k8s.aws/mesh: bookinfo > appmesh.k8s.aws/virtualNode: ratings-bookinfo 172a179,181 > annotations: > appmesh.k8s.aws/mesh: bookinfo > appmesh.k8s.aws/virtualNode: reviews-v1-bookinfo 212a222,224 > annotations: > appmesh.k8s.aws/mesh: bookinfo > appmesh.k8s.aws/virtualNode: reviews-v2-bookinfo 252a265,267 > annotations: > appmesh.k8s.aws/mesh: bookinfo > appmesh.k8s.aws/virtualNode: reviews-v3-bookinfo 316a332,334 > annotations: > appmesh.k8s.aws/mesh: bookinfo > appmesh.k8s.aws/virtualNode: productpage-bookinfo
Bookinfoを更新する。
kubectl -n bookinfo apply \ -f istio-${ISTIO_VERSION}/samples/bookinfo/platform/kube/bookinfo-appmesh.yaml
Podが更新されるので確認する。
$ k get po -n bookinfo -o yaml | grep -A 1 APPMESH_VIRTUAL_NODE_NAME - name: APPMESH_VIRTUAL_NODE_NAME value: mesh/bookinfo/virtualNode/details-bookinfo -- - name: APPMESH_VIRTUAL_NODE_NAME value: mesh/bookinfo/virtualNode/productpage-bookinfo -- - name: APPMESH_VIRTUAL_NODE_NAME value: mesh/bookinfo/virtualNode/ratings-bookinfo -- - name: APPMESH_VIRTUAL_NODE_NAME value: mesh/bookinfo/virtualNode/reviews-v1-bookinfo -- - name: APPMESH_VIRTUAL_NODE_NAME value: mesh/bookinfo/virtualNode/reviews-v2-bookinfo -- - name: APPMESH_VIRTUAL_NODE_NAME value: mesh/bookinfo/virtualNode/reviews-v3-bookinfo
意図通りの設定がされた。
動作確認
ポートフォワードおよびALB経由でアクセスできることを確認する。
kubectl port-forward service/productpage 8888:9080 -n bookinfo
v1とv2を50対50の割合を定義しているので、その割合でルーティングされる。
はまりどころ
最初まったく繋がらず何が悪いのかの判別するのにとても時間がかかった。これもサービスメッシュの難しさ。大きく2箇所ではまった。
APPMESH_VIRTUAL_NODE_NAME
環境変数
APPMESH_VIRTUAL_NODE_NAME
環境変数が正しく設定されていなかった。メッシュ名が違ったのはdj-app
という別のメッシュが既にあったからで、メッシュがbookinfo
しかなければ大丈夫。また、仮想ノード名が適切でなかったのは、reviews
については「その1」のやり方、productpage
とdetails
とratings
「その2」のやり方を採用したからで、全て「その1」のやり方であれば、Deployment名から設定される値で問題なかった。なので、全て「その1」のやり方でやったほうが結局のところ簡単で、こんなにはまらなかった。
VirtualServiceの名前
VirtualServiceの名前をreviews.bookinfo.svc.cluster.local
ではなくreviews
とする必要があった。VirtualNodeのbackendにVirtualServiceの名前を設定する部分があるが、ここもreviews
とした。App Meshの公式サンプルだとドメイン名まで含めているものが多かったように思うので、これはアプリがどのようにバックエンドを呼んでいるかに依存すると思われる。