未来の開発体験を作る技術基盤チーム | Wantedly Engineer Blog
こんにちは、Wantedly DX Squad の 大坪です。 ...
https://www.wantedly.com/companies/wantedly/post_articles/242144
こんにちは 👋
3週間、DX Squad でインターンしていた大森です。
インターン中いろいろな課題を解決したのでまとめブログを書きます✍️
目次
社内の技術基盤や制度を整えることで社内のエンジニアの生産性を向上させることに責務を持つ Developer eXperience squad
詳しくは DX Squad のリーダの大坪さんの投稿をご覧ください!
3週間のインターンで kube
という社内ツールの改善に取り組みました。kube
とは、kubectl
のラッパーのことで、社内で広く使われています。
kube には様々な機能があるのですが、今回は kube fork という機能をもっと使いやすくすることを目標にインターンを行いました。
マイクロサービスの開発では、様々な言語で書かれたアプリケーションが相互に通信を行っています。そのうちの一部を変更したいと思った時、docker-compose
などを用いて関係するサービスをすべて手元で実行するのは現実的ではありません。
そこで、DX Squad では fork とremote fork というコマンドを実装しています。
詳しくはぜひリンク先を見てほしいのですが、ざっくりまとめると以下のような感じです。
ここからは具体的に fork
と fork remote
を使うと何ができるのかについて説明します。
`kube sandbox fork` を実行
server を起動するコマンドを実行
上記の手順を実行すると、自分のリクエストは自分専用の開発環境に下図のようにルーティングされるようになります。
この状態で、開発環境にブラウザからアクセスすることで実際のリクエストを使ってマイクロサービスの開発をすることができます。
このようにして、開発者一人ひとりが自分専用のクラスタを持っているような開発体験を実現しています。
fork がやっていること
kube sandbox fork
は以下のような操作をしています。
上記の流れのように、トラップした通信を Telepresence を用いて手元のマシンに転送しています。
remote fork は fork と異なり、手元で開発環境を起動する必要がありません。指定した branch の HEAD の状態の Docker Image がデプロイされることになります。この機能の一番の利点は、docker build を待ちさえすれば手元に開発環境がなくても開発を行うことができることです。また、他にも実際のクラスタでアプリケーションが動作するので環境差異がなくなることも利点として挙げられます。
kube sandbox fork remote
は以下のような操作をしています。
remote fork は、Kubernetes クラスタに対して Service とVirtualService をApplyして、特定のヘッダが付いた通信をトラップするところまでは同じなのですが、トラップした通信は Kubernetes 上に Apply した Deployment にルーティングされます。
この機能を使うことで、自分の手元に環境を構築することなく開発が行えるようになります。
ここまで fork, remote fork
の良さを語ってきましたが、実際にはオプションを付けたり、色々考える必要がある状態でした。
これから、インターンで変更したと大きな変更点について説明します。
インターンで変更した点
1. fork 時のデフォルトのプロトコルを指定できるようになった
2. 複数の service を fork できるようになった
3. service を自動で解決するようになった
4. fork remote ですべての service を fork するようになった
5. ログがみやすくなった
6. fork したときに任意のシェルを使えるようになった
7. fork したあとに自動でコマンドを実行できるようになった
8. fork したアプリケーションも newrelic で簡単に計測できるようになった
9. fork remote --grpc してもコマンドが終了しなくなった
変更点の中でも、体験が大きく変わった部分については詳しく説明します。
体験が大きく変わった点
3. service を自動で解決するようになった
4. fork remote ですべての service を fork するようになった
7. fork したあとに自動でコマンドを実行できるようになった
Wantedly では アプリケーション間の通信に gRPC を採用している事が多いです。しかし、移行中などの理由で gRPC と HTTP の両方で通信を受け付けているアプリケーションが多いです。片方のプロトコルにしか実装されていない機能などもあり、どちらのプロトコルで fork するべきかわからないという問題がありました。これは、.kuberc に HTTP or gRPC を記入することでデフォルトのプロトコルをリポジトリごとに決められるようにして解決しました。
$ kube sandbox fork --grpc # before
$ kube sandbox fork # after
Wantedly では基本的に 1つのNamespace にHTTP用の Service と gRPC用の Service の2つがありますが、HTTPに 2つの Service が存在している アプリケーションがありました。既存のインターフェイスでは複数の service を指定できなかったので、どちらか一方を選ぶ必要がありました。kube fork のインターフェイスとして複数の Service を受け入れるようにして解決しました。
$ kube sandbox fork --service-name some-service-internal #
$ kube sandbox fork --service-name some-service-nodeport # Must choose one of these
$ kube sandbox fork \
--service-name some-service-internal \
--service-name some-service-nodeport # After
これまで --service-name を用いて service を指定してきましたが、すべてのアプリケーションの service を探したり覚えたりするのは面倒です。詳しくは後の変更点2 で記載しますが、自動で service を解決できるようにしました。
$ kube sandbox fork --service-name some-service-internal # Before
$ kube sandbox fork $ After
fork remote でも --service-name を指定する必要がありました。詳しくは後の変更点2 で記載しますが、必要な service をすべて fork remote できるようにしました。
$ kube sandbox fork remote --service-name some-service-internal # Before
$ kube sandbox fork remote # After
fork remote 時はコンソールにログが表示されるのですが、ライブラリの問題でログが重複していた問題がありました。stern に変えることでこの問題は解決しました。
fork 時は telepresence を用いてローカルに通信をルーティングしています。telepresence を起動したときにログインシェルを起動せず、環境変数などが足りなくなる問題がありました。
この問題は telepresence --run $SHELL
をするようにして対処しました。
fork した後、起動するためのコマンドを毎回入力するのが手間だという声があったので、コマンドを実行できるようにしました。詳しくは変更点3 で説明します。
Wantedly では newrelic を用いてアプリケーションの性能比較を行っています。newrelicの課金体系はホスト毎なので、コストカットのために特定のラベルがついた Deployment のみ 計測するようになっています。
ここで、 fork remote
時に newrelic 用の Deployment を特定するのが大変でした。
これには、--newrelic
というオプションをつけると newrelic 用のDeploymentが使用されるようにしました。
$ kube sandbox fork remote --deployment-name some-deployment-newrelic # before
$ kube sandbox fork remote --newrelic # After
fork
は --grpc
オプションでアプリケーションのプロトコルの指定ができます。しかし、remote fork
はすべての service を fork するようになったので remote fork
にはこのオプションはありません。それを知らずに --grpc
をつけて実行するとコマンドが終了してしまい、開発者体験が悪くなっていました。
これは仕様なので問題ではないのですが、開発者にわかりやすく伝わるようにメッセージを出力するようにしました。
$ kube sandbox fork remote --grpc # Before
unknown flag: --grpc
$ kube-go sb fork remote --grpc # After
`remote` will resolve service automatically
You don't have to add `--grpc` option
︙
fork を行うときは以下の様にコマンドを実行します。
$ kube sandbox fork --service-name some-app-http --namespace some-app
$ kube sandbox fork --service-name some-app-grpc --grpc -namespace some-app
Wantedly では アプリケーション間の通信に gRPC
を採用している事が多いです。しかし、まだ移行中なので gRPC と HTTP の両方のservice プロトコル別に service が立っています。
ここで、開発者はこのアプリケーションはどちらのプロトコルで、どの service を用いて通信を行っているかを特定する必要があります。
wantedly にはそれぞれのマイクロサービスがどのような internal url を持つかを集めた yaml があり、そのyaml をもとに service を特定する仕組みを実装しました。
また、.kuberc
という設定ファイルに デフォルトのプロトコルを書けるようにしたことで grpc
かどうかを特定する仕組みも実装しました。
この2つの変更が行われたことで、開発者はどのリポジトリでも同じコマンドを入力するだけで fork
を行うことができるようになりました。
$ kube sandbox fork --service-name some-service # before
$ kube sandbox fork # after
remote fork を行うときは以下のようにコマンドを実行します。
$ kube sandbox fork remote --service-name HTTP
$ kube sandbox fork remote --service-name GRPC
ここで問題になるのは 前述したように service 名の特定が難しいことだけでなく、どのように通信を行うかを開発者に考えさせる点です。先述したようにWantedlyのアプリケーションは http と grpc の両方を使って通信を行う場合があります。
- Service 名
- HTTP
- GRPC
上記の様にservice が2つあるアプリケーションに対し、下記のようにremote fork を行った場合選ばなかった方の通信は取りこぼすことになります。
$ kube sandbox fork remote --service-name HTTP
下図のように、必要なだけService と Deployment を作成しすべての通信を fork するようにしました。
結果的に、開発者はどのリポジトリでも同じコマンドを入力するだけで remote fork を行うことができるようになりました。
$ kube sandbox fork remote --service-name some-service # Before (1)
$ kube sandbox fork remote --service-name some-service-grpc # Before (2)
$ kube sandbox fork remote # After
前述したとおり、fork
を行うと手元にクラスタから通信が流れてくるようになります。
しかし、アプリケーションが自動で立ち上がったりはしないので開発者はコマンドを入力してアプリケーションを起動する必要があります。
kube sandbox fork
して待つyarn start
などアプリケーションの起動を行うここで、開発者は一つのアプリケーションを触り続けるわけではない ので、それぞれ異なる起動方法を調べる必要があるという問題が発生します。
以下にアプリケーションの開発に必要なコマンドを例として示します。
頻繁に違うアプリケーションの開発を行う時、READMEを見て環境を整えて... というのはだいぶ手間になってきます。
kube
には .kuberc という設定ファイルを読み込む機能があります。
そこに適切なコマンドを書いておくことで、fork 時に必要なコマンドが自動的に実行される仕組みを導入しました。
fork:
install:
steps:
- run: bash script/bootstrap
start:
http:
steps:
- run: yarn start &
- run: yarn ssr:watch
grpc:
steps:
- run: yarn start
env:
- GRPC=true
この仕組みを導入することで、コマンドを入力して待つだけでアプリケーションが動いている状態にすることができました。
# Before
$ kube sandbox fork
︙ #起動するまで待つ
$ yarn start &
$ yarn ssr:watch
# After
$ kube sandbox fork
もともとの fork
のインターフェイスでは、service 名を指定する必要があったり、そもそもどのように通信がルーティングされるかというKubernetesに関する知識が必要でした。
今回の改善で、開発者は fork
するだけでアプリケーション開発を始められるようになりました。また、fork はアプリケーション開発を始める前に必要なプロセスを省略してくれるので、開発者は変更したいコード自体に集中することができます。
言い換えるとこれは、Kubernetesに関する知識がなくても、アプリケーションに関する知識がなくても開発が行える
ようになったと言えます。
今回加えたいくつかの変更で、DX Squad が目指す 未来の開発体験 に一歩近づいたんじゃないかなと思っています!
実は、先述した体験は理想的なアプリケーションでしか達成できていません。
未来の開発体験を実現するにはまだまだ解決すべき課題はありますが、この三週間で少しは先に進められたと思っています!
リモートでインターンをしてたこともあり、メンターの大坪さんにはとても助けていただきました。
また、リモートランチを一緒になった社員の方々、関わってくださった方々にはとても感謝しています!
3週間ありがとうございました!