2021-09-09

Telepresence2へ移行するためにやったこと

biosugar0

バックエンドエンジニア兼SREの@biosugar0です。

以前の記事では、Telepresenceを使った弊社のマイクロサービスのローカル開発環境について紹介しました。 その後、Goで書き直されアーキテクチャも一新されたTelepresence2が発表され、以前紹介した主にPythonで書かれたバージョンはtelepresence-legacyと呼ばれることになり、サポートされなくなりました。

最近、我々もTelepresence2への移行が完了したので移行完了までにやったことを紹介します。(※KubernetesクラスタはEKSを前提とします)

1.telepresence2の使い方

まず設定完了後にどういう風にtelepresence2を使うかを紹介し、そのために必要な設定を紹介することにしましょう。(インストール方法はこちら)

我々は以前の記事でも書いたのと同様、普段telepresence-legacyで言うswap-deployment(サービス置換)は使いません。 主なTelepresenceの用途はローカルで立ち上げた開発中のサービスをEKSクラスタに接続して動作を確認することで、以下のような使い方をします。

Telepresence2では、まず

telepresence connect

コマンドでKubernetesクラスタと接続します。 これでKubernetesクラスタと通信ができるようになります。

その後、

telepresence intercept --local-only -n default $pname -- $run

のように開発しているサービスを立ち上げます。 $pname は立ち上げるサービスの適当な名前をつけてください。自分はよくブランチ名+サービス名で立ち上げます。 $run にはサービスを立ち上げるためのコマンドが入ります。Goだと go run main.go などになります。もちろん、ホットリロードのためのコマンドをかませることもできます。

これでローカルで開発中のサービスが立ち上がり、依存する他のマイクロサービスへの通信はクラスタにあがっているものへ向けることができます。

複数の依存するマイクロサービスをローカルで立ち上げる場合は、アプリケーション側でローカルで立ち上げるマイクロサービスへの向き先を localhost:$portのようにportを分けて変更し、それぞれ上記のinterceptコマンドを実行することで2つだけローカルで立ち上げて、他のマイクロサービスはクラスタ内のものを見るといったことができます。

connectコマンドは一回実行するとデーモンが走っているので、最初の一回だけでよいです。逆にずっとプロセスが動いているのでTelepresenceを使い終わったらtelepresence quit でデーモンを終了したほうがいいかもしれません。

2.プライベートIPアドレスに接続する設定

それでは上記の使い方をするための設定の紹介に入りましょう。

先述したとおり、Telepresence2では、まず

telepresence connect

コマンドでKubernetesクラスタと接続します。 これでKubernetesクラスタと通信ができるようになるのですが、もし開発しているサービスがAWS ElastiCacheのようなプライベートIPアドレスを持つクラスタ外のインスタンスと接続する必要がある場合はそのままでは接続できないので、いくつか設定をする必要があります。

externalName

AWS ElastiCacheの場合はプライベートなDNS名を持っているので、Kubernetes経由でアクセスするためにexternalNameを設定します。 (IPアドレスを直接指定する場合はセレクターなしのServiceとEndPointsを使います。)

kind: Service
apiVersion: v1
metadata:
  name: cache-endpoint
  namespace: default
spec:
  type: ExternalName
  externalName: "hoge.aaaaaa.ng.0001.apne1.cache.amazonaws.com"

設定したら、アプリケーションからは接続の際にそれを見るようにします。この場合は cache-endpoint.default をhostに指定してElastiCacheに接続します。

AlsoProxy設定

上記のexternalNameの設定だけだと、Telepresence2はローカルからのアウトバウンドな通信をプロキシしてElastiCacheと通信してくれません。 クラスタ内からの接続として振る舞うためには、2021年6月14日にリリースされたvesion 2.3.1のアップデートで追加されたAlsoProxy機能を使います。なお、この記事を執筆時点では最新バージョンはv2.4.2です。なるべく最新を使いましょう。

その設定は、以下のようにkubernetesのconfigファイルを用います。

~/.kube/config yaml - cluster: certificate-authority-data: ********************************* server: https://*******.ap-northeast-1.eks.amazonaws.com extensions: - name: telepresence.io extension: also-proxy: - 172.29.0.0/16 name: arn:aws:eks:ap-northeast-1:******:cluster/my-cluster

AlsoProxyの設定は、extensionsの部分です。

    extensions:
    - name: telepresence.io
      extension:
        also-proxy:
        - 172.29.0.0/16

このようにIPアドレスの範囲を指定することで、それに当てはまるアウトバウンドはTelepresenceによってプロキシされてクラスター内からの通信のように振る舞うので、クラスター内からの通信しかできないAWS ElastiCacheエンドポイントのような接続先とも通信できるようになります。

3.kubeconfigの更新方法

ここまでの設定でサービスは立ち上げることができるようになるのですが、運用上1つ課題があります。 それは、EKSの場合kubeconfigの更新をするときに

aws eks update-kubeconfig --name my-cluster

のようにコマンドを実行しますが、これをするとconfigに手で書き加えたextensions設定が消えてしまうのです。 これは開発環境でコスト削減のために夜にクラスタを落として朝に再作成していたりすると困ります。

そこで、更新の際には認証のための情報とエンドポイントのみを書き換えるようにします。

書き換えは以下のようにzsh functionでやるようにしました。

function k-update(){
  clusterName="my-cluster"
  awsID="*****"
  region="ap-northeast-1"
  clusterARN="arn:aws:eks:${region}:${awsID}:cluster/${clusterName}"
  kubeConfig=$(aws eks update-kubeconfig --name ${clusterName} --profile prd-admin --dry-run | base64)

  # 認証情報取得
  jsonPathStringAuth="{.clusters[?(@.name == \"$clusterARN\")].cluster.certificate-authority-data}"
  crt=$(kubectl --kubeconfig <(echo "${kubeConfig}" | base64 -d ) config view --raw -o jsonpath="${jsonPathStringAuth}")

  # EKSエンドポイント取得
  jsonPathStringServer="{.clusters[?(@.name == \"$clusterARN\")].cluster.server}"
  server=$(kubectl --kubeconfig <(echo "${kubeConfig}" | base64 -d ) config view --raw -o jsonpath="${jsonPathStringServer}")

  current=$(kubectl config get-clusters | grep ${clusterARN})
  if [ "${current}" = "" ]; then
      KUBECONFIG=~/.kube/config:<(echo "${kubeConfig}" | base64 -d ) kubectl config view --raw >| ~/.kube/config
      echo "new cluster ${clusterName} added to kubeconfig."
      return
  fi

  kubectl config set "clusters.${clusterARN}.certificate-authority-data" "${crt}"
  kubectl config set "clusters.${clusterARN}.server" "${server}"
  kubectl config use-context $clusterARN
}

これは何をやっているかというと、以下のようなことをしています。

  1. aws eks update-kubeconfig を --dry-run をつけて実行することで標準出力のみに出力
  2. シェルのプロセス置換を利用してkubectl がdry-run結果(認証情報更新済み)をconfigとして使用するようにし、認証情報 certificate-authority-data と エンドポイント server を取り出す。
  3. 新しいクラスターの場合はそのままdry-run結果を~/.kube/configに保存
  4. 既に設定が存在するクラスターの場合は認証情報 certificate-authority-dataserverdry-run結果(認証情報更新済み)のものに更新
  5. Current contextの切り替え

これで k-update をzsh叩くことで認証情報とエンドポイントが更新され、手で書き加えたextentions設定も消えないということになります。

最後に

これまでに紹介した設定によって、1.telepresence2の使い方で紹介した使い方ができるようになり、telepresence-legacyからtelepresence2へ移行することができました。 今回はシェルを駆使したのですが、他にもっとスマートなやり方を見つけたら追記しようと思います。

参考

最新の記事