はじめに
非コンテナ環境と比べると、コンテナ化したアプリケーションは1開発サイクルを回すために、より多くの時間と手数が要求されます。この問題の解決策の1つとして、第4回ではSkaffoldを紹介しました。今回は、Skaffoldとは別のアプローチで開発を効率化するTelepresenceについて紹介します。
Telepresenceを使った開発
Telepresenceは、ローカルからKubernetes用のマイクロサービスを開発するためのツールです。Datawireによって開発が始まり、現在はCNCFのSandboxプロジェクトの1つとなっています。
開発効率改善のアプローチとしては、下図のようにコンテナイメージのビルド*1・アップロード・デプロイといった開発ステップを省略する方法を取っています。これは、Kubernetes上のTelepresence Podから、ローカルに起動したアプリケーションへ通信や各種データをプロキシすることで実現しています。
*1:--docker-runオプションを利用する場合は必要になります。これにより、ローカルで起動したアプリケーションはあたかもKubernetes上にデプロイされたかのように動作します。サポートされている機能は、主にコンテナにマウントされたボリュームデータ(ConfigMapやSecret等)へのアクセス、環境変数の読み込み、Service名を使った他のアプリケーションとの通信です。
また、ローカルへのプロキシ方法はinject-tcpと、デフォルトのvpn-tcpの2つが提供されています。それぞれの概要と、主な制約を表にまとめました*2。実行環境や、アプリケーションの言語等に制約がありますので、適切な方を選択してください。
2つのプロキシ方法とその制約
| inject-tcp | vpn-tcp | 概要 | アプリケーションの共有ライブラリを上書きする方法*3 | sshuttleを使ったVPNライクな接続方法 | 主な制約事項 |
|
|
|---|
*3:Linuxの
LD_PRELOADとmacOSのDYLD_INSERT_LIBRARIESを利用した方法で、詳細はこちらのブログで詳しく解説されています。*4:
go buildではなくgccgoによるビルドやGODEBUG環境変数でnetdnsのリゾルバをcgoに変更するなどのワークアラウンドは存在しますが、非推奨です。*5:
--also-proxyフラグによる手動設定は可能です。
第4回と同じく、今回もk8s-sample-applicationを利用します。このアプリケーションはGo言語で書かれていますので、Go言語をサポートするvpn-tcpを使った開発方法を紹介します。
開発の下準備
Telepresenceコマンドのインストール
それでは、実際に検証してみます。macOSの場合はHomebrewを使って下記のようにインストールします。その他の環境へのインストール方法は公式ドキュメントを参照してください。
# telepresenceと依存ツールのインストール
$ brew cask install osxfuse && brew install datawire/blackbird/telepresence
# バージョンの確認
$ telepresence --version
0.108
サンプルアプリケーションのデプロイ
まずは、開発用アプリケーションを通常通りkubectl applyで適用します。前回Gitクローンしたsample-k8s-appディレクトリに移動し、manifestsディレクトリ内のマニフェストを適用してください。念のためkubectl getで正常に動作していることも確認しましょう。DeploymentのREADYカラムが1/1になっていれば大丈夫です。
# (1) 該当ディレクトリに移動
$ cd sample-k8s-app
# (2) アプリケーションマニフェストの適用
$ kubectl apply -f manifests
deployment.apps/myapp created
service/myapp created
# (3) デプロイしたリソースの確認
$ kubectl get deploy,svc myapp
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/myapp 1/1 1 1 33s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/myapp NodePort 10.102.199.85 <none> 80:30216/TCP 33s
Telepresenceによるローカルへのプロキシ
次に、アプリケーションをローカルから開発できるようにtelepresenceコマンドを実行します。--swap-deploymentフラグには置き換え対象となるDeployment名、--exposeフラグにはDeploymentで公開しているポート番号、--runフラグにはアプリケーションを起動するためのコマンドを指定します。
正常にTelepresenceのプロキシ設定が完了すると、Start server...というmyappの起動ログが出力されます。
# (1) telepresence --swap-deployment <Deployment名> --expose <ポート番号> --run <アプリケーションの起動コマンド>
$ telepresence --swap-deployment myapp --expose 8080 --run go run main.go
T: Using a Pod instead of a Deployment for the Telepresence proxy. If you experience problems, please file an issue!
T: Set the environment variable TELEPRESENCE_USE_DEPLOYMENT to any non-empty value to force the old behavior, e.g.,
T: env TELEPRESENCE_USE_DEPLOYMENT=1 telepresence --run curl hello
T: How Telepresence uses sudo: https://www.telepresence.io/reference/install#dependencies
T: Invoking sudo. Please enter your sudo password.
Password: # (2) sudoパスワードの入力
T: Starting proxy with method 'vpn-tcp', which has the following limitations: All processes are affected, only one telepresence can run per
T: machine, and you can't use other VPNs. You may need to add cloud hosts and headless services with --also-proxy. For a full list of method
T: limitations see https://telepresence.io/reference/methods.html
T: Volumes are rooted at $TELEPRESENCE_ROOT. See https://telepresence.io/howto/volumes.html for details.
T: Starting network proxy to cluster by swapping out Deployment myapp with a proxy Pod
T: Forwarding remote port 8080 to local port 8080.
T: Connected. Flushing DNS cache.
T: Setup complete. Launching your command.
# (3) アプリケーションログが標準出力に表示される
2020/09/19 15:07:23 Start server...
では、実際に置き換わったことを別のターミナルから確認しましょう。myappのDeploymentを確認すると、(1)のようにPodのレプリカ数が0に変わっていました。しかし、(2)の通りmyappをプレフィックスに持つPodが起動しています。では、このPodはいったい何者でしょうか?
このPodのコンテナイメージを確認すると、(3)のようにdatawire/telepresence-k8s:0.108が返ってきました。このことから、telepresenceコマンドを実行したタイミングで、指定したmyappがTelepresenceコンテナイメージを持つPodに置き換えられたことが分かります。
また、(4)のようにmyapp ServiceのEndpointもTelepresence Podを参照しています。よって、NodePort経由でアプリケーションにリクエストを送信すると、ローカルで起動しているmyappからレスポンスが返ってきます。
# (1) Deploymentのレプリカ数が0に変わっている
$ kubectl get deploy myapp -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
myapp 0/0 0 0 2m49s sample-k8s-app ladicle/sample-k8s-app app=myapp
# (2) myappの名を持つPodが新たに立ち上がっている
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-51b8df20eef24324959a3f3eb46e7d48 1/1 Running 0 94s 192.168.189.82 worker2 <none> <none>
# (3) PodのコンテナイメージはTelepresenceに置き換わっている
$ kubectl get po myapp-51b8df20eef24324959a3f3eb46e7d48 -o jsonpath='{.spec.containers[0].image}'
datawire/telepresence-k8s:0.108
# (4) myappのService Endpointは、Telepresenceのコンテナイメージを持つPodのIPを参照している
$ kubectl get ep myapp
NAME ENDPOINTS AGE
myapp 192.168.189.82:8080 3m17s
# (5) myapp ServiceのNodePort経由で、ローカルに起動しているmyappからレスポンスを受け取る
$ curl http://:30216/hello
Hello World
開発フローを回す
サンプルアプリケーションの修正と確認
続いて、プログラムを修正して変更内容が適用されていることを確認します。下記のようにmain.goのコメントアウトされている行をアンコメントしてください。
diff --git a/main.go b/main.go
@@ -7,11 +7,11 @@ import (
- // "os"
+ "os"
)
func main() {
- // log.Printf("TELEPRESENCE_ROOT: %v, TELEPRESENCE_MOUNTS: %v", os.Getenv("TELEPRESENCE_ROOT"), os.Getenv("TELEPRESENCE_MOUNTS"))
+ log.Printf("TELEPRESENCE_ROOT: %v, TELEPRESENCE_MOUNTS: %v", os.Getenv("TELEPRESENCE_ROOT"), os.Getenv("TELEPRESENCE_MOUNTS"))
この変更の適用はtelepresenceコマンドを再実行するだけです。実行するとアンコメントしたログ出力が確認できます。このように、Telepresenceではコンテナイメージのビルド・アップロード・ダウンロードというステップを省略することで、修正と動作確認をすばやく繰り返すことができます。
今回はアプリケーションをビルドするため、変更適用時にtelepresenceコマンドを再実行しましたが、Node.jsのnodemonのようなコード変更時に再起動するツールを利用すれば、コマンドの再実行は不要です。また、アプリケーション自体はローカルに立ち上がっていますので、IDE等でローカルデバッグも可能です。
$ telepresence --swap-deployment myapp --expose 8080 --run go run main.go
(省略)
2020/09/20 13:14:12 TELEPRESENCE_ROOT: /tmp/tel-fcw5e_zg/Fs, TELEPRESENCE_MOUNTS: /var/run/secrets/kubernetes.io/serviceaccount
2020/09/20 13:14:12 Start server...
コンテナボリュームのデータ参照
はじめに紹介したように、Telepresenceではコンテナボリュームもローカルから参照できます。試しにdfコマンドでローカルのファイルシステムを見ると、TELEPRESENCE_ROOT環境変数が示すパスにマウントされていました。
それでは、ServiceAccountトークンが格納されているディレクトリも確認してみましょう。(2)のように/var/run/secrets/kubernetes.io/serviceaccountディレクトリを参照すると、コンテナ内と同じ3つのファイルが確認できました。
# (1) ローカルにコンテナボリュームがマウントされていることが確認できる
$ df | grep telepresence
telepresence@127.0.0.1:/ 479101704 13557996 446013480 3% /private/tmp/tel-fcw5e_zg/fs
# (2) `/var/run/secrets/kubernetes.io/serviceaccount` を参照するとコンテナ内と同様にServiceAccountトークンなどが格納されている
$ ls /tmp/tel-fcw5e_zg/fs/var/run/secrets/kubernetes.io/serviceaccount
ca.crt namespace token
もちろん、コンテナ内のファイルパスとは異なりますので、TELEPRESENCE_ROOTが設定されている場合は、その値をパスのルートとするような処理をアプリケーションに入れる必要があります。
おわりに
以上のように、Telepresenceはコンテナイメージのビルド・アップロード・デプロイを省略するため、非コンテナ環境と同じような流れで開発できることが分かりました。
今回は紹介しませんでしたが、コンテナ特有のバグなどを発見するための--docker-runフラグも存在します。これを利用すれば、Dockerでローカル上にアプリケーションコンテナを立ち上げ、そのコンテナに対してプロキシを通すといったこともできます。もちろん、Telepresenceにも制約は色々ありますので、第4回で紹介したSkaffoldと合わせて、ご自身の環境にあった開発を加速させる方法を模索してみてください。
- この記事のキーワード