バックエンドエンジニア兼SREの@biosugar0です。
以前の記事では、Telepresenceを使った弊社のマイクロサービスのローカル開発環境について紹介しました。 その後、Goで書き直されアーキテクチャも一新されたTelepresence2が発表され、以前紹介した主にPythonで書かれたバージョンはtelepresence-legacyと呼ばれることになり、サポートされなくなりました。
最近、我々もTelepresence2への移行が完了したので移行完了までにやったことを紹介します。(※KubernetesクラスタはEKSを前提とします)
まず設定完了後にどういう風に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
でデーモンを終了したほうがいいかもしれません。
それでは上記の使い方をするための設定の紹介に入りましょう。
先述したとおり、Telepresence2では、まず
telepresence connect
コマンドでKubernetesクラスタと接続します。 これでKubernetesクラスタと通信ができるようになるのですが、もし開発しているサービスがAWS ElastiCacheのようなプライベートIPアドレスを持つクラスタ外のインスタンスと接続する必要がある場合はそのままでは接続できないので、いくつか設定をする必要があります。
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に接続します。
上記の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エンドポイントのような接続先とも通信できるようになります。
ここまでの設定でサービスは立ち上げることができるようになるのですが、運用上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
}
これは何をやっているかというと、以下のようなことをしています。
--dry-run
をつけて実行することで標準出力のみに出力dry-run
結果(認証情報更新済み)をconfigとして使用するようにし、認証情報 certificate-authority-data
と エンドポイント server
を取り出す。dry-run
結果を~/.kube/config
に保存certificate-authority-data
と server
をdry-run
結果(認証情報更新済み)のものに更新これで k-update
をzsh叩くことで認証情報とエンドポイントが更新され、手で書き加えたextentions設定も消えないということになります。
これまでに紹介した設定によって、1.telepresence2の使い方で紹介した使い方ができるようになり、telepresence-legacyからtelepresence2へ移行することができました。 今回はシェルを駆使したのですが、他にもっとスマートなやり方を見つけたら追記しようと思います。