Juju-62q's blog

参加記録やメモ書き、思考のまとめをしています

Telepresenceを使ってみた

Cloud Native Meetup Tokyo #7で聞いたTelepresenceに興味が湧いて触ってみました。

登壇者様の資料は以下です。

speakerdeck.com

TL;DR

  • Kubernetesでマイクロサービスの開発をするときはTelepresenceを使うと便利
  • Secret周りもいい感じに使えるので開発者が管理すべき認証情報が減る

Telepresenceとは

CNCFでSandbox ProjectとしてホストされているKubernetes上のアプリケーションの開発を手助けしてくれるツールです。 一言でTelepresenceについて説明してもわかりにくいので開発フローと合わせてTelepresenceがどんなツールなのかを見ていきます。

Telepresenceがない場合

Kubernetesを利用して特にマイクロサービスのアプリケーションを開発する場合、以下のようなフローで開発を進めることが多いでしょう。

  1. コンテナイメージをビルドし、手元でUnitTestや簡単な動作確認
  2. 動作が正しければ、イメージレジストリへプッシュ
  3. Kubernetesに変更をapplyして全体の挙動を確認

開発では試行錯誤が求められるため、これを幾度となく繰り返します。 何かを試すたびにここまでの作業をしないといけないとなると正直言って非常に骨が折れます。 また、GitOpsを採用している場合に関してはもっと面倒臭いことになり、開発中で試したいだけにも関わらず何かしらをGitリポジトリにpushする必要が生まれます。 こうなるとGitのコミットログも非常に汚くなってしまいます(まぁsquashすればいいが)。

Telepresenceがある場合

Telepresenceは上記の問題を解決しうるツールです。 Telepresenceを使うとKubernetes上で動いているDeploymentのコンテナをローカルマシンで動いているコンテナやプロセスに置き換えることができます。

ということはTelepresenceを使えばイメージレジストリにプッシュせずとも、Kubernetesyamlを変更してapplyせずとも、全体の挙動を確認しながら開発することができるということになります。 クラウドを利用してKubernetesを動かしている場合はマネージドサービスとの連携をモックを使うことなく動作を確認することができます。 こうすることで開発が高速になるだけでなく、開発環境とQAやプロダクション環境の差分をより小さくすることが可能です。 まぁ、少々お金は高くつくわけですが。

詳しいことは公式サイトを見てみるといいと思います!

www.telepresence.io

試験環境

GKE 1.12.7-gke.10
macOS 10.14.4
Google Cloud SDK 242.0.0

GKEに関してはVPCネイティブなネットワーキングをオンにしました。 今回の場合おそらく意味はないと思います。

導入方法

導入はとても簡単です。 Kubernetes側に手を入れなくていいのが嬉しいですよね。

Mac OSの場合

brew cask install osxfuse
brew install datawire/blackbird/telepresence

この後、環境設定でosxfuseを有効化する必要があります。ファイルシステムを制御するためですね。 Telepresenceの実行にはそれなりに大きな権限が必要になるようです。

その他プラットフォームについては試してないので書きませんが、公式を見る限り多くのプラットフォームに対応しています。

www.telepresence.io

実際に使ってみる

以下のパターンについて試してみました。(いずれもDeployment。DaemonSetやStatefulSetは不要だと思ったため未調査)

  • single container in pod
  • single container in pod with secret
  • multi containers in pod with secret

single container in pod

DockerHubのnginx:1.15.12を適当にDeploymentとして走らせて、type: LoadBalancer でアクセスを受けました。 変化を見る方法としてローカル側のコンテナの index.html を変更しました。

コマンドは以下です。

telepresence --swap-deployment ${deployment_name} --docker-run --rm -p 80:80 ${container_image}

f:id:Juju_62q:20190519175911p:plain

こんな感じでまぁ変わっているのがわかると思います。

single container in pod with secret

今度はpythonでむちゃくちゃ簡単なアプリを作ってこれをDeploymentにしました。同じ感じでtype: LoadBalancerを使っています。

from flask import Flask
import json
import os

app = Flask(__name__)

@app.route("/", methods=['GET'])
def hello():
    if 'ENV_VAR' in os.environ:
        return "ENV_VAR is {}".format(os.environ.get('ENV_VAR'))
    else:
        return "ENV_VAR not found"


@app.route("/file", methods=['GET'])
def file():
    try:
        with open('/secret/test.txt') as f:
            return f.read()
    except:
        return 'something failed'

if __name__ == "__main__":
    app.run(host='0.0.0.0',port=5000,debug=True)

Secretの扱いが見たかったのでSecretを環境変数、ファイルとしてマウントしてみました。

apiVersion: v1
kind: Secret
metadata:
  name: test-secret
type: Opaque
data:
  targetEnv: dGVzdAo=
  targetFile: VGhpcyBpcyBhIHNlY3JldCBmaWxlLg==

---

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    run: python-secret
  name: python-secret
spec:
  selector:
    matchLabels:
      run: python-secret
  template:
    metadata:
      labels:
        run: python-secret
    spec:
      containers:
      - name: python-secret
        image: ${image_name}
        ports:
        - containerPort: 5000
        env:
        - name: ENV_VAR
          valueFrom:
            secretKeyRef: 
              name: test-secret
              key: targetEnv
        volumeMounts:
        - name: secretdir
          mountPath: "/secret"
      volumes:
      - name: secretdir
        secret:
          secretName: test-secret
          items:
          - key: targetFile
            path: test.txt

---

apiVersion: v1
kind: Service
metadata:
  labels:
    run: python-secret
  name: python-secret
spec:
  ports:
  - port: 5000
    targetPort: 5000
  selector:
    run: python-secret
  type: LoadBalancer

以下のコマンドを流すとSecretで指定しているファイルや環境変数が参照できているということが確認できました。

telepresence --swap-deployment python-secret --expose 5000 --docker-run --rm -p 5000:5000 ${container_image}

f:id:Juju_62q:20190519175133p:plain

さらに下記のようにdocker runのコマンドでファイルや環境変数を書き換えてみました。

telepresence --swap-deployment python-secret --expose 5000 --docker-run -e ENV_VAR=nya-n --rm -p 5000:5000 ${container_image}

f:id:Juju_62q:20190519175512p:plain

環境変数の追加や書き換え、ローカルのファイルのマウントも正常に動作しました。 開発中にミドルウェアを足したいとか、マネージドで新たなサービスを使う場合にはとても便利だと思います。

multi containers in pod with secret

先ほどとほぼ同様の設定を利用しました。ただし、Flaskの前段でNginxでアクセスを受け、フォワードするようにしました。 デフォルトコンテナはFlaskの方にしています。

下記のようにコマンドを流すと、podのdefaultコンテナが置き換わります。 つまり、今回の場合はFlaskのコンテナが置き換わることになります。

telepresence --swap-deployment ${deployment_name} --docker-run --rm -p 5000:5000 ${container_image}

一方で下記のようにすることでデフォルト以外のコンテナも指定可能です。

telepresence --swap-deployment ${deployment_name}:${container_name} --docker-run --rm -p 80:80 ${container_image}

例えば container_image=nginx:alpine とかにするとforwardされないため Welcome to Nginx が表示されるようになります。また、Flask, Nginxともに環境変数の制御は問題なくできました。

さらに、Telepresenceはpod(コンテナ)をshellで実行できる多くのコマンドに変更することが可能です。 複数のコンテナを同時に変更したい場合にはTelepresenceの --docker-run ではなく --run を利用して、アクセスを受けるコンテナを docker-compose のコマンドで置き換えてしまえば問題なく開発可能です。

補足

contextやnamespaceはオプションにより指定可能です。

telepresence --context ${context_name} --run-docker hoge
telepresence --namespace ${namespace} --swap-deployment ${deployment_name} --run-docker fuga

所感

Telepresence、めっちゃ便利です。 Secretが適切に配置されるのは自分の想像を超えていました。 利用によって開発環境と本番環境の差異が減るので、開発の変な落とし穴を減らすことができると思います。

また、利用する副次的なメリットとして、認証情報を開発機で管理しなくて良いというものがあります。 K8sで適切に管理してあげれば開発者が管理する機密情報が減りそうな感じがしますね。幸せです。