kubernetes活用のための第3回はTelepresenceを活用して疎通確認やテスト環境のデバッグなどを効率よく実施します。
1. はじめに
Kubernetes上にシステムを開発する場合、マイクロサービスを採用するケースが多いと思います。(もちろんそうでないケースもありますが)
全体がマイクロサービス化されたシステムの1サービスを担当する場合、他サービスと接続しながらのデバッグやテスト環境上で発生している問題の確認など、ローカル環境だけでは実施が難しい場面にたびたび遭遇します。もちろん、ローカルに環境を再現したり、デバッグログを仕込んだイメージをテスト環境にデプロイして確認することも可能ですが、もう少し簡単に実施したいですよね。今回はそういった場合にローカル環境上で自身のアプリケーションを動作させながら、別の環境にあるクラスタと通信する方法を検証してみます。
本記事ではKubernetesの基本的な概要などについては記載しておりません。適宜各公式ドキュメントを参照してください
2. Telepresence
Telepresenceとは、Kubernetesクラスタ上のリクエストをローカルに転送することで、ローカルにKubernetesクラスタを構築しなくても簡単にマイクロサービスアプリケーションの動作を確認することができるプロダクトです。アプリケーションをローカルで起動しているのに、Kubernetesクラスタ上で起動しているかのように動作させることができます。
公式サイトでは以下の5つの特徴が説明されています。
- Debug Locally
- ローカルでお気に入りのデバッグツールを用いてKubernetesサービスのデバッグが可能
- Instant Feedback
- デプロイせずにコードを即座にデバッグできる
- Realistic Environment
- 実際のクラスタ上で開発が行えるため、開発環境と本番環境の際によるバグをなくす
- Minimal Resource Footprint
- 開発環境のリソースを最小化する(実際のサービスを実行するのに必要なリソースのみでよい)
- Use Your Own Tool
- お気に入りのエディタ、デバッガ、プロファイラを自由に使用して開発できる
(注) Telepresenceのバージョンについて
Telepresenceは2021年1月26日にバージョン2が公開されました。バージョン2ではアーキテクチャが刷新されており、使い方も全く違うものになっています。 本記事はバージョン2(v2.4.0)をもとに執筆しております。インターネット上の情報などは旧バージョンの情報も多いため、参照する際は注意が必要です。
2.1 アーキテクチャ
Telepresenceのアーキテクチャは下図のようになります。
各アプリケーションPodにサイドカーとしてTraffic Agentコンテナが常駐します。Traffic AgentコンテナはアプリケーションへのリクエストをTraffic Managerへプロキシし、Traffic Managerとローカル端末のTelepresenceDaemon間で通信を行うことでローカルアプリケーションへリクエストを流す仕組みになっています。
各コンポーネントの説明などは公式のアーキテクチャ図をご参照ください。
※公式のアーキテクチャ図では構成要素の中にAmbassador Cloudが登場します。これについては最後のほうで少し触れます。
2.2 利用シーン
Telepresenceはローカルでの初期実装中や単体テスト中に使うことはあまりないと思います。Kubernetesクラスタの通信をローカルで確認したいときがいつかと考えると、下記のようなユースケースが用途として考えられます。
- 実装完了したアプリケーションを実際の環境で疎通確認したい
- 他サービスとの連携を確認したいが、ローカル環境に他サービスを導入できない(他サービスのイメージを提供されていない、サービス数が多くスペック不足になるなど)
- テスト環境等で発生している障害に対して、実際の環境を使いながらローカルデバッグをしたい
2.3 Telepresenceの導入
今回、Windows上でWSL2(Ubuntu20.04LTS)を用いてTelepresenceを導入します。
※ 2021/8/11にリリースされたv2.4.0よりWindowsネイティブのクライアントも提供されています。こちらを利用すればWSL2を使わずにTelepresenceを動作させることが可能と思われますが、筆者の環境ではインストール中にエラーが発生したため、今回は未検証となります。Windowsネイティブの導入手順についてはこちらから確認してください。(Windowsタブ内に記載があります)
導入の手順は下記の3ステップになります。
(1) クライアント(WSL2 Ubuntu)にTelepresenceCLIを導入
(2) KubernetesにTraffic Managerを導入
(3) ローカルへプロキシしたいサービスにTraffic Agentを導入
※ 事前にWSL2からkubectlでKubernetesクラスタに接続できることを確認しておいてください
(1) クライアント(WSL2 Ubuntu)にTelepresenceCLIを導入
TelepresenceCLIはシングルバイナリで提供されています。環境に応じたバイナリをダウンロードしてきてパスの通ったフォルダに配置すれば利用可能になります。
$ curl -fL https://app.getambassador.io/download/tel2/linux/amd64/latest/telepresence -o telepresence $ chmod 755 telepresence $ sudo mv telepresence /usr/bin/
(2) KubernetesにTraffic Managerを導入
Traffic ManagerはTelepresenceCLIからTelepresenceDaemonを起動すると初回に自動で導入されます。
TelepresenceDaemonは、telepresence connect
とすることで起動します。
$ telepresence connect Launching Telepresence Root Daemon Launching Telepresence User Daemon Connected to context example (https://xxxxxxxxxxxxxxxxxxxxxxxxx.xxx.ap-northeast-3.eks.amazonaws.com)
Kubernetes側の状態を確認すると、ambassadorネームスペースと、その中にService、Deployment、Replicaset、Podが導入されていることが確認できます。
$ kubectl get namespaces NAME STATUS AGE ambassador Active 5m29s ... $ kubectl get all -n ambassador NAME READY STATUS RESTARTS AGE pod/traffic-manager-c6dbb7999-6cwbt 1/1 Running 0 5m46s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/agent-injector ClusterIP 10.100.234.153 <none> 443/TCP 5m47s service/traffic-manager ClusterIP None <none> 8081/TCP 5m47s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/traffic-manager 1/1 1 1 5m48s NAME DESIRED CURRENT READY AGE replicaset.apps/traffic-manager-c6dbb7999 1 1 1 5m48s
(3) ローカルへプロキシしたいサービスにTraffic Agentを導入
第1回の記事でも利用したQuarkusのサンプルアプリ(getting-started)を使ってTraffic Agentを導入します。
まず、telepresence list
で導入したいネームスペースの導入状況を確認します。Traffic Agent未導入時は下記のようにサービス名: ready to intercept (traffic-agent not yet installed)と表示されます。
$ telepresence list -n default getting-started-deployment: ready to intercept (traffic-agent not yet installed)
次にtelepresence intercept
で実際にgetting-started-deploymentにTraffic Agentを導入します。(--portはローカルポート:リモートポートを指定します)
$ telepresence intercept getting-started-deployment --port 8080:8080 Using Deployment getting-started-deployment intercepted Intercept name : getting-started-deployment State : ACTIVE Workload kind : Deployment Destination : 127.0.0.1:8080 Service Port Identifier: 8080 Volume Mount Point : /tmp/telfs-787085176 Intercepting : all TCP connections
getting-startedのPodを確認するとコンテナが2つになっていることが確認できます。
$ kubectl get pods NAME READY STATUS RESTARTS AGE getting-started-deployment-5dbb8f4964-ddls6 2/2 Running 0 44h
また、Pod内の詳細情報を確認すると、アプリケーション以外にtraffic-agentコンテナがデプロイされていることがわかります。
$ kubectl describe pods getting-started-deployment-5dbb8f4964-ddls6 ... Containers: getting-started: Container ID: docker://02661e1eac4c1c05d07e853da25a736378244d132cbcdbbb66680df04ca09be5 ... traffic-agent: Container ID: docker://72ea9866712059fd886e4904607834e4bedbc0168029571d45f4a0387ba57792 Image: docker.io/datawire/tel2:2.4.0 Image ID: docker-pullable://datawire/tel2@sha256:f88e16070521acfaa592b048f7bb431f9ef14268f51b0b7fd83978c0ec800391 Port: 9900/TCP Host Port: 0/TCP ...
また、Serviceを確認するとintercept後はEndpointsが書き換えられ、Traffic Agentのポート(9900)に向いていることがわかります。
intercept前
Name: getting-started-service Namespace: default Labels: app=getting-started app.kubernetes.io/instance=getting-started Annotations: <none> Selector: app=getting-started Type: ClusterIP IP: 172.20.158.40 Port: http 8080/TCP TargetPort: 8080/TCP Endpoints: 10.0.1.101:8080 Session Affinity: None Events: <none>
intercept後
Name: getting-started-service Namespace: default Labels: app=getting-started app.kubernetes.io/instance=getting-started Annotations: telepresence.getambassador.io/actions: {"version":"2.4.0","make_port_symbolic":{"PortName":"http","TargetPort":8080,"SymbolicName":"tx-8080"}} Selector: app=getting-started Type: ClusterIP IP: 172.20.158.40 Port: http 8080/TCP TargetPort: tx-8080/TCP Endpoints: 10.0.1.218:9900 Session Affinity: None Events: <none>
ここまでで、Telepresenceの導入とローカルへ転送する準備まで整いました。
2.4 ローカルアプリケーションへのプロキシ
それでは実際にKubernetes上へ送信したリクエストがローカルアプリケーションへプロキシされることを確認します。
今回TelepresenceをWSL2上で起動しているため、アプリケーションもWSL2上から起動します。(ローカルのTelpresenceDaemonとアプリケーション間がlocalhost通信のため)
※ Windowsネイティブのクライアントを利用している方、またMacやLinuxで確認されている方は、通常通りローカルでVisualStudioCodeを起動してご確認いただけます。
構成としては下図のようになります。
WSL2側ではアプリケーションの準備(Quarkusの場合Java/Mavenの導入、およびアプリケーションをGitからcloneしておく)をしておいてください。そのあと以下のコマンドを実行すると、WSL2にVSCodeServerがインストールされ、Windows上でVisualStudioCodeが起動します。
$ code .
起動したら、レスポンス文字列を「Hello Telepresence」に変更して、Quarkusを起動してみましょう。
画面上に下記の通り表示されたら、ローカル側にリクエストがプロキシされています。また、デバッグ起動してブレークポイントを指定するとデバッグできることも確認できますのでお試しください。
Hello Telepresence
2.5 クラスタで利用している環境変数をローカルで利用
ローカルで動かしているアプリケーションをKubernetesクラスタにデプロイした時と同じ動きにするために、Kubernetesクラスタ側と同じ環境変数をローカルのアプリケーションに読み込ませたいケースがあると思います。その場合下記のように--env-fileを付与すると指定したファイル名に環境変数が出力されますので、それをローカル側で読み込ませることで同様の環境を構築することができます。
$ telepresence intercept <サービス名> --port <ローカルポート>:<リモートポート> --env-file=<ファイル名>
指定したファイルを見ると下記のように環境変数の一覧が出力されているので、これをローカルアプリケーションにロードしましょう。
GETTING_STARTED_SERVICE_PORT=tcp://172.20.158.40:8080 GETTING_STARTED_SERVICE_PORT_8080_TCP=tcp://172.20.158.40:8080 GETTING_STARTED_SERVICE_PORT_8080_TCP_ADDR=172.20.158.40 GETTING_STARTED_SERVICE_PORT_8080_TCP_PORT=8080 GETTING_STARTED_SERVICE_PORT_8080_TCP_PROTO=tcp GETTING_STARTED_SERVICE_SERVICE_HOST=172.20.158.40 GETTING_STARTED_SERVICE_SERVICE_PORT=8080 GETTING_STARTED_SERVICE_SERVICE_PORT_HTTP=8080 ...
2.6 GitOpsとの併用
第1回の記事のようにアプリケーションをArgoCDなどを用いて)GitOpsでデプロイしている場合、デプロイ後のPodにTraffic Agentを直接導入するとGitと状態が異なることになるため都合がよくありません(同期する設定にしているとすぐに消されてしまいます)。こういったケースではMutating Webhookを利用してPodにTraffic Agentを導入する方法があります。
Mutating Webhookの利用はPodのアノテーションで指定します。公式ドキュメントのMutating Webhookに記載がありますが、注意点としてtargetPortはポート番号ではなく名前で指定する必要があるようです。
アノテーションの設定
spec: template: metadata: labels: service: your-service annotations: telepresence.getambassador.io/inject-traffic-agent: enabled spec: containers:
Serviceのポート名を確認
spec: ports: - name: http port: 8080 protocol: TCP targetPort: 8080
interceptはポート名で指定
telepresence intercept <サービス名> --port <ローカルポート>:http
詳しくはドキュメントのMutating Webhookを参照してください。
2.7 特定のリクエストだけプロキシする
一歩進んだ使い方として、特定のリクエストだけをローカルにプロキシしてみます。
例えば、テスト環境でテスト中のサービスについて何か不具合が見つかった際に、テスターは引き続きテスト環境にアクセスしてテストするものの、開発者は自分のリクエストだけローカルにプロキシさせてデバッグしたいケースなどが考えられると思います。
旧バージョンのTelepresenceではこのような動作を実現するための機能はありませんでしたが、v2からはHTTPヘッダを用いてリクエストの振り分けが可能になっています。
ただし、ここで注意点ですが、この振り分け機能はデフォルトのTraffic Agentでは利用できません。Telepresenceの開発元であるAmbassador Labsが提供しているTraffic AgentにはデフォルトのOSS版Traffic Agentと非OSS版のTraffic Agentがあり、OSS版に比べ非OSS版のほうが高機能となります。今回のHTTPヘッダを用いた振り分け機能も非OSS版のみに実装されております。
なお、非OSS版のTraffic Agentを利用するためにはAmbassador Cloudへの登録が必要になります。登録して非OSS版のTraffic Agentを利用するとトラフィックがAmbassador Cloudにも流れるようになり、Ambassador Cloudのコンソール上で状態確認ができるようになります。
(注)Ambassador Cloudの利用に関しましては、オージス総研並びにオブジェクトの広場編集部一切の責任を負いかねます。
詳しくはドキュメントのInterceptsを参照してください。
それでは、HTTPヘッダによる振り分けを確認してみます。振り分けの際には、--http-matchフラグを利用します。
$ telepresence intercept <サービス名> --port <ローカルポート>:<リモートポート> --http-match=<ヘッダ情報>
--http-matchフラグを付与すると自動的に非OSS版のコンテナがPodに付与されます。非OSS版のコンテナを利用する場合はAmbassador Cloudへの登録が必須となるため、未ログイン状態の場合はここでログインを要求されます。
Ambassador Cloudへログインして非OSS版のコンテナがPodに付与されますと、特定のリクエストのみがローカルにプロキシされ、それ以外は通常通りテスト環境のPodへ通信が流れるようになります。
ちなみに、kubectl describe podsでPodのコンテナを見てみると下記のようにOSS版と非OSS版で異なるコンテナになっていることが確認できます。
OSS版のTraffic Agent
... image: datawire/tel2:2.4.0 imageID: docker-pullable://datawire/tel2@sha256:--- ...
非OSS版のTraffic Agent(--http-matchを付与した場合)
... image: datawire/ambassador-telepresence-agent:1.10.0 imageID: docker-pullable://datawire/ambassador-telepresence-agent@sha256:--- ...
2.8 プロキシ設定の解除
確認が終わったらプロキシ設定を解除します。そのままローカルのアプリケーションを落とすとプロキシ先がなくリクエストがエラーとなるだけなので注意してください。(Kubernetes側のアプリケーションに通信されるわけではない)
Traffic Agentを残したままプロキシを解除する場合は下記のようにします。
$ telepresence leave <サービス名>
Traffic Agent自体削除してしまいたい場合は下記のようにします。
$ telepresence uninstall --agent <サービス名>
※ telepresence uninstall
には他にも全Traffic Agentを削除する--all-agentsやTraffic Managerも削除する--everythingフラグもあります。
3. おわりに
マイクロサービスのように小さなサービスを組み合わせてシステムを構築する場合、どうしてもシステム間連携も増えますし、不具合の再現なども難しくなり、結果として開発効率が下がることにもなりかねません。今回利用したTelepresenceは開発中常時必要なプロダクトではありませんが、他サービスとの疎通確認や、テスト環境のデバッグなどをローカル端末から効率よく行うときに力を発揮します。ぜひ一度検討してみてください。
次回はアプリケーション間の呼び出しに関する基本機能を提供する「Dapr」を活用しながら、複数のサービス連携を想定した開発について見ていきます。