こんにちは、バックエンドのリードエンジニア兼バックエンド基盤チームのマネージャーの夏(なつ)と申します。バックエンド基盤チームは、バックエンドエンジニアの生産性向上やコスト削減を目的に、エンジニア主導で課題を発見・解決している部署です。
今回は基盤チームが行った、Google CloudのIdentity-Aware Proxy(IAP)を利用したローカル環境での開発体験の向上について紹介したいと思います。この改善により、ローカル環境(Mac)上でも開発環境のデータを参照しながら開発が行えるようになり、開発体験が大幅に向上しました。
背景
基盤チームでは定期的にバックエンドチームのメンバーに対して「開発者体験に関するアンケート」を実施しています。基盤チームにとっての直接のユーザはバックエンドメンバーであるため、生の声を聞くことで、現在解決しようとしている問題や開発リソースの配分、優先順位が適切かどうかを判断しやすくなります。
基盤チームのメンバーも以前はプロダクトチームに所属していたため、現場での開発風景はおおよそ想像できます。しかし、それらの機能開発から離れて久しいため、現在プロダクト開発で何が最大の障壁となっているのか、どのような改善があり得るのかを直接ヒアリングできる機会は貴重です。
実施したアンケートは結果を集計し、個人を特定できない形でバックエンドメンバーに共有しています。これにより「みんな同じところに課題を感じているのだなあ」とか「こういう解決法が提案されているのだなあ」みたいなことを可視化できます。また、みんなから収集した課題や改善案に対して基盤チームから「なぜ現在この問題を優先的に取り組んでいるのか」「なぜこの問題への対応は優先順位を下げているのか、あるいは対応しないのか」を(独断と偏見にはなるが)共有することでチーム間の わかりあう願いをつなげる ことを目指しています。(ちなみにミラティブ社のミッションは「わかりあう願いをつなごう」です)
リリース速度への課題感
アンケートを実施してみると開発環境・本番環境を問わずリリースに時間がかかることが『開発体験を阻害する要因』として最も多く挙げられていました。
Mirrativのバックエンド開発をする際、一般的なフローは以下のようになっています。 (各メンバーによって多少異なりますが)
- 手元のMac上でDocker Composeを利用してユニットテストやE2Eテストで動作確認を行う
- 開発環境にリリースし、開発版アプリを通してアプリとの結合テスト、チーム内でのテストプレイやQAを行う
- QAチームに依頼して開発環境上で検証、分析ログの確認・修正を行う
- 本番環境にリリースする
現状、開発環境にリリースを行う際、リリース処理の最適化がまだ十分でないため、反映完了まで30分近くかかる場合があり、この点に関してはインフラチームと協力しながら高速化を進めている最中です。
一方で、それらの改善が完了したとしても数秒~数十秒でリリースが完了する世界線に辿り着くイメージは持てていません。検証中に発生した不具合を調査する場合、開発環境のデータが必要になることが多く、ログを仕込んでリリースするにしても一回の試行に10分以上かかるようでは集中力が途切れてしまい、開発体験が損なわれます。
そこで、リリースの高速化と並行して、ローカル環境でも開発環境のデータを参照しながら開発できるようにしたいと考えました。これにより不具合調査のためのリリースが削減でき、また、手元でシードデータを生成することなく開発環境のデータを使用して動作確認ができます。
構成
Mirrativの開発で活用しているGoogle CloudにはIdentity-Aware Proxy(IAP)という仕組みがあります。これを用いることでGoogleアカウントで認証されたユーザだけがProxyを通してCompute Engineなどと通信できるようになります。
https://cloud.google.com/security/products/iap?hl=ja
また、監査ログを通してどのユーザがどのポートで通信しているかなどの証跡が残るため、セキュリティインシデントが発生しても追跡可能です。
ただ、Mirrativの開発環境の一部は IDCF 上に自分たちで構築したプライベートクラウドを利用しているため、Google CloudのIAPに加え、更にTCP Proxyを多段で構成することでローカル環境とIDCF上のミドルウェアを通信させています。IAP の On-Permコネクタ と違い直接任意のミドルウェアに対して通信するため用意しています。 TCP Proxyは開発者ごとに必要になったタイミングで起動し、接続が開きっぱなしにならないよう、一定時間で自動終了する仕組みを入れています。
注: RadishaはRedis互換の内製のミドルウエア tech.mirrativ.stream
以下の3つのシステムをGoで実装し、 github.com/urfave/cli
を活用してサブコマンドで制御できるようになっています。
private_cloud_proxy
- IDCF上のprivate-cloud-proxyインスタンス上で実行される
- 空いているポートを探索し、ミドルウェアごとに割り当てる
- ユーザや実行ごとにポートを変更することで追跡しやすくする
- go-tcp-proxy をベースにした内製のTCP Proxyを起動する
- ポートのマッピング情報をコマンドライン引数で指定されたパスに書き出す
- 後述する実行元のiap_bastion_proxyサブコマンドによって収集される
ポートのマッピング情報は以下のような形式となっています
db_user_shard1: 40001 db_user_shard1_replica: 40002 db_user_shard2: 40003 db_user_shard2_replica: 40004 radisha: 40005
iap_bastion_proxy
- Google Cloud上のIAP用の踏み台インスタンス上で実行される
- private-cloud-proxyインスタンスにsshし、private_cloud_proxyサブコマンドを実行しながら、ポートのマッピング情報を取得し、TCP Proxyを起動する
- ポートのマッピング情報をコマンドライン引数で指定されたパスに書き出す
- 後述する実行元のiapサブコマンドによって収集される
- iptablesでprivate-cloud-proxyインスタンスの特定のポート以外への通信はDROPするよう設定
/etc/sudoers.d/google_sudoers_overwrite
を書き換えることで任意のコマンドをsudoで実行できる状態を防ぐ この他にもプライベートクラウドの動的なマシン構成変更に追従するため、 Envoyのdynamic_resource を使って動的な経路変更も行っています
iap
- ローカル環境で実行される
gcloud compute ssh ${iap_bastion_instance_name} --tunnel-through-iap
でIAP踏み台にsshしiap_bastion_proxyサブコマンドを実行する- バックエンドメンバーのGoogle Groupに
roles/iap.tunnelResourceAccessor
の権限を付与しておく
- バックエンドメンバーのGoogle Groupに
- iap_bastion_proxyサブコマンドの準備が完了し、ポート情報が出力されたらそれを取得し、該当の全ポートに対して
gcloud compute start-iap-tunnel ${iap_bastion_instance_name} ${port} --local-host-port=localhost:${port}
でIAPを1つずつ起動していく - ローカルファイルにポートのマッピング情報をYAML形式で書き込み、アプリケーションで利用できるようにする
- コンテナ内部からは host.lima.internal でアクセス可能
- (Docker Desktopを利用している場合は host.docker.internal でホストにアクセスできるようですが、社内ではDocker Desktopの代替としてColimaを推奨しており、その場合は host.lima.internal でしかアクセスできなかった)
利用方法
こんな感じでコマンドをMac上で実行すると30秒~1分ほどで準備が完了します
% ENV=local go run ./cmd/script iap --instance-name ${iap_bastion_instance_name} 2024-04-04 17:33:02 ⚡ Starting... 2024-04-04 17:33:18 ⏳ Waiting iap_bastion_proxy... 2024-04-04 17:33:24 ⏳ Waiting private_cloud_proxy... 2024-04-04 17:33:33 ⏳ Waiting iap tunnel... 2024-04-04 17:33:45 🚀 Ready!
あとは別ターミナルでDockerコンテナ内から以下のようなコマンドを実行して適切に開発環境のデータを取得できていることが確認できます
% ENV=local NAMESPACE=qa8 docker compose run app ... # go run ./cmd/script read_recently_created_users name created_at foo 2024-01-01 12:34:59 bar 2024-01-01 12:34:58 baz 2024-01-01 12:34:57 qux 2024-01-01 12:34:56 ...
ここまでくれば後はコマンドを変えるだけで開発環境のデータベースなどを参照しながら、WebやDaemonなどの開発が可能となります。
その後
ここまで実装し、Slackで宣伝してみたものの、実際にメンバーに使ってもらうまでにはそれなりに時間がかかりました。 「すごい」的なリアクションは付くものの、現状でも開発はできているのにわざわざ重い腰をあげて新しいツールを試してみるというのはハードルが高いのだと感じました。 (特に最初の段階ではツール自体の品質もまだ上がりきっておらず、さらにフルリモート環境なのでなおさら浸透が難しい。。。)
そのため、少しでも使ってみようかなと思われた方には基盤チームの方から積極的に導入支援を行いました。 また、定例や振り返りのたびに宣伝活動を行ったり、すでに利用してくださった方に便利さを語ってもらうことで、徐々に利用者の輪を広げることができました。 (この点は、一般的なサービス開発とも似ていますね)
愛用しているメンバーが増えてきたため、今後はMac上のシミュレータや手元の実機からもアクセスできるようにし、さらなる開発体験の向上を目指していきたいと考えています。
🙌 We are hiring!
株式会社ミラティブ では一緒に開発者体験を向上してくれるエンジニアを募集しています!