マイクロサービスにおけるAWS X-Rayを利用した分散トレーシングをEKSで試す
「ま、丸見えやないか!!」
以前から興味があったAWS X-Ray。ごっつ便利そうながら、導入の敷居が高そうで触るのを躊躇してました。
そんな悶々とした日々を送っていた矢先、いつもお世話になっているEKS Workshopにおいて、X-Rayをサンプルアプリケーションと共に簡単に試すことができるチュートリアルがあったので、渡りに船とばかり触ってみました。
インフラ側を設定しサンプルアプリケーションを展開することで、各マイクロサービス間のリクエスト内容とリクエストタイム、その分布が丸裸になるという、なんとも興奮する結果が得られました。
実際のアプリケーションに導入するにはひと手間かかりますが、まずはEKSにおいてAWS X-Rayがどのような役割をはたすのかその感触を掴むには最適なので、マイクロサービスな分散トレーシングに興味がある方は、一度触ってみることをオススメいたします。
(祭) ∧ ∧ Y ( ゚Д゚) Φ[_ソ__y_l〉 X-Rayマツリダワッショイ |_|_| し'´J
AWS X-Rayによる分散トレーシングとは
いわゆるAPM(Application Performance Management)に属するサービスです。
近年の分散システムの進化により、そのシステムの監視やデバッグは困難となっています。Kubernetesのようなコンテナオーケストレーションプラットフォームはそれらの多くの問題を解決しますが、サービス間の相互作用やそれぞれの待機時間を把握するのは依然として難しい問題です。
AWS X-Rayは、X-RayエージェントをDaemonSetとして各Workerノードに展開し、アプリケーション内にX-Ray SDKを導入することで、分散トレーシングを可能とします。
今回は、Amazon EKS Workshop内のTracing with X-Rayを利用して、サンプリアプリケーションを利用しながら、AWS X-Rayの挙動を確認してみます。
ほな、いってみよ。
EKS環境の確認
既にEKSのクラスターが用意されている前提で進めていきます。この記事では、EKSクラスターのバージョンは1.12.6です。
$ kubectl version --short Client Version: v1.10.3 Server Version: v1.12.6-eks-d69f1b
AWS X-Rayのセットアップ
アプリケーションへのAWS X-Ray SDKの組み込み
今回はサンプルアプリケーションを利用するのでこの手順は不要です。
自分のアプリケーション内にX-Ray SDKを組み込む場合は、下記公式ドキュメントを参考に、各言語で必要なSDKの組み込みを実施しておきます。
What Is AWS X-Ray? - AWS X-Ray
WorkerノードへのIAMポリシー付与
最初にWorkerノードがX-Rayに対してトレーシング結果を送信できるように、WorkerノードのIAMロールにarn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess
ポリシーが必要です。
WorkerノードのEC2インスタンスに付与されているIAMロール名を確認し、以下のコマンドでポリシーを付与します。
$ aws iam attach-role-policy --role-name $ROLE_NAME \ --policy-arn arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess
X-RayエージェントをDaemonSetとして展開
各WorkerノードにX-Rayエージェントを展開するため、エージェントをDaemonSetとして展開します。
eksworkshopで用意されている、マニフェストファイルをダウンロードします。
$ curl -O https://eksworkshop.com/x-ray/daemonset.files/xray-k8s-daemonset.yaml
マニフェストファイルの内容です。ここでは、DeamonSetのポートに2000を利用しています。
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License is located at # # http://www.apache.org/licenses/LICENSE-2.0 # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. apiVersion: v1 kind: ServiceAccount metadata: labels: app: xray-daemon name: xray-daemon namespace: default --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: xray-daemon labels: app: xray-daemon roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: xray-daemon namespace: default --- apiVersion: extensions/v1beta1 kind: DaemonSet metadata: name: xray-daemon spec: updateStrategy: type: RollingUpdate template: metadata: labels: app: xray-daemon spec: volumes: - name: config-volume configMap: name: xray-config hostNetwork: true containers: - name: xray-daemon image: rnzdocker1/eks-workshop-x-ray-daemon:dbada4c77e6ae10ecf5a7b1c5864aa6522d9fb02 imagePullPolicy: Always command: [ "/usr/bin/xray", "-c", "/aws/xray/config.yaml" ] resources: limits: memory: 24Mi ports: - name: xray-ingest containerPort: 2000 hostPort: 2000 protocol: UDP volumeMounts: - name: config-volume mountPath: /aws/xray readOnly: true --- # Configuration for AWS X-Ray daemon apiVersion: v1 kind: ConfigMap metadata: name: xray-config data: config.yaml: |- # Maximum buffer size in MB (minimum 3). Choose 0 to use 1% of host memory. TotalBufferSizeMB: 0 # Maximum number of concurrent calls to AWS X-Ray to upload segment documents. Concurrency: 8 # Send segments to AWS X-Ray service in a specific region Region: "" # Change the X-Ray service endpoint to which the daemon sends segment documents. Endpoint: "" Socket: # Change the address and port on which the daemon listens for UDP packets containing segment documents. # Make sure we listen on all IP's by default for the k8s setup UDPAddress: 0.0.0.0:2000 Logging: LogRotation: true # Change the log level, from most verbose to least: dev, debug, info, warn, error, prod (default). LogLevel: prod # Output logs to the specified file path. LogPath: "" # Turn on local mode to skip EC2 instance metadata check. LocalMode: false # Amazon Resource Name (ARN) of the AWS resource running the daemon. ResourceARN: "" # Assume an IAM role to upload segments to a different account. RoleARN: "" # Disable TLS certificate verification. NoVerifySSL: false # Upload segments to AWS X-Ray through a proxy. ProxyAddress: "" # Daemon configuration file format version. Version: 1 --- # k8s service definition for AWS X-Ray daemon headless service apiVersion: v1 kind: Service metadata: name: xray-service spec: selector: app: xray-daemon clusterIP: None ports: - name: incoming port: 2000 protocol: UDP
このマニフェストファイルをクラスタに適用します。
$ kubectl create -f xray-k8s-daemonset.yaml serviceaccount "xray-daemon" created clusterrolebinding.rbac.authorization.k8s.io "xray-daemon" created daemonset.extensions "xray-daemon" created configmap "xray-config" created service "xray-service" created
デーモンセットとして正常に展開されているか確認します。Pods Status
が3 Running
になっていれば、きちんとPodとして展開されています。
$ kubectl describe daemonset xray-daemon Name: xray-daemon Selector: app=xray-daemon Node-Selector: <none> Labels: app=xray-daemon Annotations: <none> Desired Number of Nodes Scheduled: 3 Current Number of Nodes Scheduled: 3 Number of Nodes Scheduled with Up-to-date Pods: 3 Number of Nodes Scheduled with Available Pods: 3 Number of Nodes Misscheduled: 0 Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed Pod Template: Labels: app=xray-daemon Containers: xray-daemon: Image: rnzdocker1/eks-workshop-x-ray-daemon:dbada4c77e6ae10ecf5a7b1c5864aa6522d9fb02 Port: 2000/UDP Host Port: 2000/UDP Command: /usr/bin/xray -c /aws/xray/config.yaml Limits: memory: 24Mi Environment: <none> Mounts: /aws/xray from config-volume (ro) Volumes: config-volume: Type: ConfigMap (a volume populated by a ConfigMap) Name: xray-config Optional: false Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulCreate 1m daemonset-controller Created pod: xray-daemon-kzzzr Normal SuccessfulCreate 1m daemonset-controller Created pod: xray-daemon-bgmzs Normal SuccessfulCreate 1m daemonset-controller Created pod: xray-daemon-lvlxd
Podの展開も確認。
$ kubectl get pod --selector app=xray-daemon NAME READY STATUS RESTARTS AGE xray-daemon-bgmzs 1/1 Running 0 2m xray-daemon-kzzzr 1/1 Running 0 2m xray-daemon-lvlxd 1/1 Running 0 2m
こんな感じでログが出力されていればOKです。
$ kubectl logs -l app=xray-daemon 2019-06-01T06:24:37Z [Info] Initializing AWS X-Ray daemon 3.0.0 2019-06-01T06:24:37Z [Info] Using buffer memory limit of 38 MB 2019-06-01T06:24:37Z [Info] 608 segment buffers allocated 2019-06-01T06:24:37Z [Info] Using region: ap-northeast-1 2019-06-01T06:24:37Z [Info] Starting proxy http server on 127.0.0.1:2000 2019-06-01T06:24:34Z [Info] Initializing AWS X-Ray daemon 3.0.0 2019-06-01T06:24:34Z [Info] Using buffer memory limit of 38 MB 2019-06-01T06:24:34Z [Info] 608 segment buffers allocated 2019-06-01T06:24:34Z [Info] Using region: ap-northeast-1 2019-06-01T06:24:34Z [Info] Starting proxy http server on 127.0.0.1:2000 2019-06-01T06:24:34Z [Info] Initializing AWS X-Ray daemon 3.0.0 2019-06-01T06:24:34Z [Info] Using buffer memory limit of 38 MB 2019-06-01T06:24:34Z [Info] 608 segment buffers allocated 2019-06-01T06:24:34Z [Info] Using region: ap-northeast-1 2019-06-01T06:24:34Z [Info] Starting proxy http server on 127.0.0.1:2000
サンプルアプリケーションのデプロイ
eksworkshopには、AWS X-RayのSDKを事前に組み込んだサンプルアプリケーションが用意されているので、それらをデプロイします。
$ kubectl apply -f https://eksworkshop.com/x-ray/sample-front.files/x-ray-sample-front-k8s.yml $ kubectl apply -f https://eksworkshop.com/x-ray/sample-back.files/x-ray-sample-back-k8s.yml
しばらくまったら、デプロイメントを確認し、正常に展開されているか確認します。
$ kubectl describe deployments x-ray-sample-front-k8s x-ray-sample-back-k8s
サービスステータスを確認します。
$ kubectl describe services x-ray-sample-front-k8s x-ray-sample-back-k8s
フロントエンドサービスが正常にデプロイされていることを確認したら、CLBでフロントエンドサービスを公開します。
$ kubectl get service x-ray-sample-front-k8s -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR x-ray-sample-front-k8s LoadBalancer 10.100.X.YYY abcdefg0123456789.ap-northeast-1.elb.amazonaws.com 80:31229/TCP 4m app=x-ray-sample-front-k8s,tier=frontend
ロードバランサーがデプロイされた後、実行結果に表示されているEXTERNAL-IPにブラウザでアクセスし、サンプルアプリケーションのフロント画面が表示されたOKです。
むっちゃ怪しい感じスネ… 分散トレーシングのサンプルとするため、フロントから1秒ごとに自動的にサーバーにリクエストが飛んでいます。
AWS X-Rayコンソールを確認する
ここまでで事前準備はOK。いよいよAWS X-Rayコンソールで、分散トレーシングの内容を確認していきます。
Service Map
無事、X-Rayにデータが転送されていれば、こんな感じでService Mapが表示されています。ここでは、各Pod間のリクエストの依存関係とPod名を確認できます。
各リソースは、同じコンテキスト空間でX-Rayにデータ転送され、グラフとして表示されます。このサンプルサプリケーションでは、x-ray-sample-fornt-k8s
サービスが、1分間あたり43回のりクエストをなげていて、平均レイテンシーが0.6msとなります。x-ray-sample-back-k8s
は、平均トランザクションが0.08msになっていることがわかります。
各サービスをクリックすると、計測範囲時間におけるレスポンスの分布を確認できます。
Traces
左側メニューのTraces
をクリックすると、各リクエストにおけるセグメント単位のリクエストの実行結果をトレースできます。初期表示では、URLをELBのエンドポイントでグループ化したトレース結果が表示されています。
リストのトレースリストをクリックすると、各リクエスト内全ての実行時間を確認できます。
Rawデータも確認ができます。
このGolangのサンプルでは、the main segmentにおいて、xray.Handler helper
を初期化しており、全てのhttpリクエスト内の全ての必要な情報を構造化して送信しています。
アナリティクス
左側メニューのアナリティクスをクリックすると、トレースデータの理解に役立つ、サービス性能の全体的な分析結果を確認できます。最近でた機能らしいですが、なんかすげぇな…
応答時間の分布状況を確認できたり
応答時間でフィルタしたなかでの、HTTPステータスコードの分布、リクエストURI、ユーザーエージェント、HTTPメソッドの分布、レスポンスタイム、そして、そこに含まれる詳細トレースを確認することができます。
サンプルアプリケーションなので、全レスポンスが200だったりあまり意味のあるグラフには見えないのですが、実アプリケーションであれば、応答時間が非常にかかっている部分のリクエストに絞って、そのエラー率や実際のトレース内容を把握するのに威力を発揮しそうです。
AWS X-Rayの料金
無料利用枠が用意されています。とりあえず試してみるにはありがたい。
- 期限なしの無料利用枠
- 毎月、トレースの記録は 10 万回まで無料です。
- 毎月、トレースの取得とスキャンは合わせて 100 万回まで無料です。
- 追加料金
- トレースの記録の無料利用枠を超えた分の料金は、トレースの記録 100 万件あたり 5 USD (トレース 1 件あたり 0.000005 USD) です。
- トレースの取得とスキャンの無料利用枠を超えた分の料金は、トレースの取得とスキャンを合わせて 100 万件あたり 0.50 USD (トレース 1 件あたり 0.0000005 USD) です。
サンプルアプリケーションの後片付け
サンプルアプリケーションは、このままだと延々ログを吐き出してしまうので、検証が終わったらさくっと片付けてしまいましょう。
サンプルアプリケーションの削除はこちら。
$ kubectl delete deployments x-ray-sample-front-k8s x-ray-sample-back-k8s $ kubectl delete services x-ray-sample-front-k8s x-ray-sample-back-k8s
X-Rayデーモンセットの削除はこちら。
$ kubectl delete -f https://eksworkshop.com/x-ray/daemonset.files/xray-k8s-daemonset.yaml
アプリケーションへのSDK組み込みは必須だが、分散トレーシングとして非常にパワフル
サンプルアプリケーションがあるおかげで、Pod間のリクエストのビジュアライズから、各リクエストのトレースと分析結果を垣間見ることができました。むっちゃ面白かったです。
アプリケーションへのX-Ray SDKの組み込みと設定が必須になっている部分は最初の導入で敷居が高いですが、マイクロサービスにおける各サービス間のトレーシングや分析には非常に役立つ機能かと思います。
実際に活きたアプリケーションに導入しないとその威力がわかりにくいと思いますが、サンプルアプリケーションの導入と展開自体はこの記事で書いたように非常に簡単なので、まずはどのようなデータがでるか感触を掴んでいただき、「これ、ごっつええやん(^q^)」と興奮した人であれば、皆さんの運用アプリケーションに組み込んでみてはいかがでしょうか。
それでは、今日はこのへんで。濱田(@hamako9999)でした。