オブジェクトの広場はオージス総研グループのエンジニアによる技術発表サイトです

コンテナ・マイクロサービス

Kubernetes活用への道のり

第3回 開発効率向上編 Telepresenceを活用して他サービスとの接続やテスト環境のデバッグを効率化しよう
オージス総研 技術部 アドバンストテクノロジセンター
山中 克容
2021年9月21日

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のアーキテクチャは下図のようになります。

architecture

各アプリケーション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を起動してご確認いただけます。

構成としては下図のようになります。

develop

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 特定のリクエストだけプロキシする

一歩進んだ使い方として、特定のリクエストだけをローカルにプロキシしてみます。

例えば、テスト環境でテスト中のサービスについて何か不具合が見つかった際に、テスターは引き続きテスト環境にアクセスしてテストするものの、開発者は自分のリクエストだけローカルにプロキシさせてデバッグしたいケースなどが考えられると思います。

usecase

旧バージョンの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」を活用しながら、複数のサービス連携を想定した開発について見ていきます。