2020-01-25

Kubernetesクラスターと同期する、マイクロサービスのためのローカル開発環境

biosugar0

去年入社し、主にバックエンドエンジニアとして働いている@biosugar0です。

去年の記事でも紹介したとおり、我々は日々マイクロサービスアーキテクチャでの開発を行っています。

最近は開発用のkubernetesクラスターに構築されたマイクロサービスをうまく利用して、効率的にマイクロサービスの開発を行う環境が整ってきたので紹介します。

これまでの開発環境

マイクロサービスの開発では、各サービスは基本的に疎結合にするという前提があるのですが、そうは言っても依存関係は存在し、必要なサービスが連携する環境をローカルの環境に整える必要がありました。

我々は各サービスを個別のGitリポジトリで管理しているので、特にフロントエンドでは依存するサービスが最新かどうか確認してすべてをローカルで起動してからAPIの繋ぎこみを確認するなど、つらみがありました。

Kubernetesクラスターと同期する、マイクロサービスのためのローカル開発環境

せっかくKubernetesを使うことが決まっていたので、Telepresenceというツールによって効率的に開発を行う環境を整備しました。最近はこの構成で開発を行っています。

1. 開発用kubernetesクラスターとCI/CD

我々は開発用にEKSを使用した kubernetesクラスターを使用しています。 このクラスターには開発しているすべてのマイクロサービスがデプロイされています。

各サービスのリポジトリのdevelopmentブランチに変更がマージされると、Github ActionsとArgo CDによるCI/CDが走り、常に最新のマイクロサービスがデプロイされている状態になっています。

2. Telepresence

ローカルで開発する際には、Telepresenceというツールを使います。

Telepresenceを使うと、一つのサービスをローカルで実行しながら、そのサービスをkubernetesクラスターに接続することができます。 これにより、ローカルで動くサービスがkubernetesクラスター上にある最新の依存サービスと通信できるようになります。

これが何を意味しているかというと、非常に便利なことに、例えばフロントエンドでは バックエンドのサービスがローカルになくても開発できるようになります。

実際の開発環境

Telepresenceを調べるとよく--swap-deploymentオプションを使用してkubernetesクラスター上のサービスを自分のローカルのサービスで置き換える事例が出てくるのですが、複数人で共通の開発用クラスターを使用していると全員に影響が及んでしまうので使っていません。

以下のように --new-deployment を使用して新しいサービスとして k8s上に上げます。こうすることで、他の人に影響せずにクラスターに自分の開発中のサービスを接続することができます。

※フロントエンドの一例

telepresence --namespace default --new-deployment smartmat-$service-$user  --expose $port --run yarn dev

だれのものかわかりやすいように、新しくクラスターに上げるサービスの名前は開発用環境に既に存在する最新サービスの後ろに自分の名前をつけるようにしています。

フロントエンド(nuxt)の場合は、--run によって直接開発用のコマンドを実行することでクラスターに接続しながらホットリロードも効く環境ができあがります。

バックエンドでホットリロードを効かせる

Telepresenceの起動には遅くて約30秒くらいの時間がかかるので、バックエンド(Go言語)でもホットリロードがしたいところです。 そこで我々は、バックエンドのホットリロード用にreflex をTelepresenceと組み合わせて使っています。

ここで注意なのが、reflexによるホットリロードでは一度Killして再起動するということを行うからか、--run では2回目がうまくサービスが起動しなくなります。 なので--docker-run オプションを使用します。 reflexの起動コマンドと組み合わせるので、ECRにreflexをインストールしたGo言語の開発環境Dockerイメージを保存していて、それを使っています。

run="bash -c \"reflex -d fancy -r '(\.go$|go\.mod)' -s -- bash -c 'CGO_ENABLED=0 go run -v main.go -env development'\""
`aws ecr get-login --no-include-email --region ap-northeast-1`
telepresence --namespace default --new-deployment smartmat-$service-$user --expose $port --docker-run -p $port:$port --env -v ${PWD}:/app -v `go env GOCACHE`:/root/.cache/go-build -v `go env GOPATH`/pkg/mod:/go/pkg/mod --rm -it *********.dkr.ecr.ap-northeast-1.amazonaws.com/hot-reload-base:latest $run"

Dockerを使っているため、コンテナに今いるプロジェクトのディレクトリをマウントしています。 こうすることで、ローカルで行った変更がクラスターに接続されたコンテナにも伝わり、reflexによるホットリロードが実行されます。なにかコードを変更してみると、サービスが再起動されて変更が反映されます。

複数のサービス間の通信

マイクロサービス間の通信をテストしたいときもあると思いますが、 クラスターに存在する最新の各マイクロサービスはTelepresenceによって作成された新しいサービスに通信してくれません。 なので、通信したいサービスの通信の向き先をTelepresenceによって作られたものに変更してそれぞれのサービスに対してTelepresenceを実行する必要があります。

kubernetes上のサービス同士は<サービス名> .<ネームスペース> でアクセスできるので、新しく作成したサービスの名前とネームスペースを呼び出し元のアプリケーション側で環境変数などを使用して設定します。 その状態でそれぞれTelepresenceを実行することで、サービス間の通信をテストすることができます。

まとめ

kubernetesとTelepresenceを使用することで、常に最新のサービスが置いてあるクラスターと接続しながら開発ができるので、マイクロサービスの開発がだいぶ快適になりました。

懸念点もあり、各開発中のサービスは新しくDeploymentを作ることで他の人への影響を減らせますが、データベースは共通のものを使っているのでなにかDBを壊すようなことをすると開発環境を使用する全員が困ってしまいます。なにかいい方法がないか検討中です。

とはいえ、とても便利なのでぜひkubernetes環境で開発をしている方はTelepresenceを試してみてください。

最新の記事