自分が仕事で使うツールやサービスは割とモチベーションに影響するのだなーという話
※あくまで自分にはそうだったという話
所謂スタートアップと言われる会社から、それなりの規模の会社に転職してもうすぐ2ヶ月くらいになる。
今の会社に転職してくるときに、開発に使っているツールについては説明を受けていたし、当然それを知った上で入社したのだけれど、実際に使ってみると思ったよりもテンションが下がった自分がいた。
特に以下のようなソフトウェアエンジニアとして日常的に使うツールのグレードが下がると(正確には必ずしもグレードが下がったとはいえないものもあるんだけど)、辛かった。
- チャット
- ソースコード管理
- ドキュメント管理
こういう周辺のツールにモチベーションが左右されてしまうのはどうなんだろうと思っていたし、転職で会社を選ぶ時にも割と軽視していたのだけれど、自分には重要な要素だったようだ。
正直な話、入社して最初の週末は人事の方や誘ってくれた人に頭下げて辞めさせてもらおうか…という考えが頭をよぎる程度にはテンション下がっていた(今思うと単純にツール・サービスだけの話しではなく、環境が変わったとか複合的な要素が絡んで必要以上にナーバスになっていたのだけれど)。
ただ「今いる環境を良くしていくことが出来なければ同じことをどこに行っても繰り返すことになるのでは?」と思い直し、「もっとこうしたい」ということを発信してみると、賛同してくれる人がそれなりにいることがわかった。
そういった声をいわゆる偉い人達も後押ししてくれていたり、他にもXXを改善したい!と手を上げる人が出てきたりして、今は色々と良い方向に改善していきつつあると感じる。
またおこがましい言い方なんだけど、自分がそういう流れを作れた気がしていてそれがちょっと嬉しい。
全くまとまりのない文章だけど、Slack導入が承認されたのでその記念に。
Slack導入のトロフィー解除したぞ!
— すーさん二号 (@suusan2go) 2017年12月19日
grpc-gatewayでMetadataに詰めたエラーの内容をJSONに詰めてRESTクライアントに返したい
引き続きgRPCの話。
gRPCでエラーをクライアントに返したい場合、通常だとステータスコードとエラーメッセージしか返せない。例えばアプリケーションレベルのバリデーションエラーみたいなものを返したい時、メルカリさんの資料によるとMetadataに詰めて送ると良いらしい、
gRPCがクライアントのときは詰めたMetadataを読み出せばいいんだけど、grpc-gatewayでRESTのクライアントに返す時にはどうしたらよいか調べた。
grpc-gatewayのエラーハンドリングをカスタマイズする
実はGitHubのwikiに How to customize your gateway
というのがあって、そこに結構色々と書かれている。
How to customize your gateway · grpc-ecosystem/grpc-gateway Wiki · GitHub
で、Wikiから辿った先にあるブログに実際に結構丁寧にエラーレスポンスのカスタマイズ方法が乗ってるので、それを参考にすればよい。
fun run() error { runtime.HTTPError = CustomHTTPError // 省略 } type errorBody struct { Error string `json:”error"` ErrorDetails []ErrorDetail `json:”errorDetails”` } type errorDetails struct { Field string `json:”field”` Message string `json:”message”` } // おもにgRPCのMetadataをerrorBodyのような構造に変換し、クライアントに返すためのカスタムハンドラー func CustomHTTPError(ctx context.Context, _ *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, _ *http.Request, err error) { const fallback = `{"error": "failed to marshal error message"}` w.Header().Set("Content-type", marshaler.ContentType()) w.WriteHeader(runtime.HTTPStatusFromCode(grpc.Code(err))) eb := errorBody{ Err: grpc.ErrorDesc(err), } md, _ := runtime.ServerMetadataFromContext(ctx) for k, v := range md.TrailerMD { eb.ErrorDetails = append(eb.ErrorDetails, errorDetail{ Field: strings.TrimSuffix(k, "-bin"), // バイナリで帰ってくる文字列は-binのprefixがつくので、クライアントが扱いやすいよう消す Message: v[0], // サーバー側では文字列としてしか入れていないが、何故かArrayで入ってくるの最初のものだけ取得する }) } jErr := json.NewEncoder(w).Encode(eb) if jErr != nil { w.Write([]byte(fallback)) } }
Go書くの久しぶりすぎてこんな感じでよかったか全然自信ないけど、一応やりたいことは実現できた。
gRPCをブラウザで使いたい
引き続きgRPCについて調べている。
2017年11月現在では現状gRPCはそのままブラウザ上で動かすことが出来ない(Node実装はあるけどブラウザでは動かせない)。
現状ブラウザでgRPCを動かしたいと思ったら二つの道があるようだ。
grpc-webを使う
これはgRPCサーバーの前段にプロキシサーバーを立てることにより、ブラウザ環境でgRPCのクライアントを動かせるようにするというもの。
import {grpc, BrowserHeaders, Code} from "grpc-web-client"; // Import code-generated data structures. import {BookService} from "../_proto/examplecom/library/book_service_pb_service"; import {QueryBooksRequest, Book, GetBookRequest} from "../_proto/examplecom/library/book_service_pb"; const queryBooksRequest = new QueryBooksRequest(); queryBooksRequest.setAuthorPrefix("Geor"); grpc.invoke(BookService.QueryBooks, { request: queryBooksRequest, host: "https://example.com", onMessage: (message: Book) => { console.log("got book: ", message.toObject()); }, onEnd: (code: Code, msg: string | undefined, trailers: BrowserHeaders) => { if (code == Code.OK) { console.log("all ok") } else { console.log("hit an error", code, msg, trailers); } } });
実際に試してみると、確かにブラウザからgRPCが使えた。GitHubを見るとGo前提っぽく感じるけど、grpcwebproxyが単体で提供されているので、例えばバックエンドはGoでなくてもgRPCサーバーの前段でこのプロキシサーバーを起動させてあげれば良い。
grpc-web/go/grpcwebproxy at master · improbable-eng/grpc-web · GitHub
そうなんだけど実際に試してみると、クライアント側でセットしたメタデータがサーバーまでたどり着かない。
調べてみたら同様の症状の人がIssueを上げているよう。ここで一旦grpc-webは諦めて、次のgprc-gatewayの方を触ってみた。
grpc-gatewayを使う
こちらはgRPCのバックエンドサーバーの前段において、RESTのエンドポイントを提供するというもの。grpc-webとは違い、protoファイルにも手を加える必要がある。
syntax = "proto3"; package example; + +import "google/api/annotations.proto"; + message StringMessage { string value = 1; } service YourService { - rpc Echo(StringMessage) returns (StringMessage) {} + rpc Echo(StringMessage) returns (StringMessage) { + option (google.api.http) = { + post: "/v1/example/echo" + body: "*" + }; + } }
grpc-gateway は、RESTのエンドポイントを作るための実装だけではなく、swagger定義も出力出来るようになっている。なので、gRPCが使える環境の場合には普通にprotoファイルから生成したクライアントを使い、RESTを使う必要がある場合にはgatewayの出力したswagger定義からRESTクライアントを生成すればよい。
protoc -I/usr/local/include -I. \ -I$GOPATH/src \ -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ --swagger_out=logtostderr=true:. \ path/to/your_service.proto
まとめ
grpc-web
の方がprotoファイルに余計なものを書かなくてすむので後々はこっちの方向性かなーと思った。でも、現状だとまだ grpc-gateway
を使った方が良いのかなーという感触。
Rails + SpringBootでgRPCする方法について色々調べた
RailsのバックエンドとしてSpring Bootを置いてgRPCでやりとりするみたいなのをどう実現するかを調べている
RubyからgRPCを使う
クライアント側はこれが参考になった。proto
からGemの中身を生成して配布するの良さそう。
クライアント側でgRPCを扱うコードはこんな感じになる
stub = Snip::UrlSnipService::Stub.new('0.0.0.0:50052', :this_channel_is_insecure) req = Snip::SnipRequest.new(url: 'http://shiladitya-bits.github.io') resp_obj = stub.snip_it(req)
Spring BootでgRPCを使う
これを使うのが楽そう。 @GRpcService
のアノテーションを付与するだけで、 Spring
と gRPC
を共存させることができる。
@GRpcService public static class GreeterService extends GreeterGrpc.GreeterImplBase{ @Override public void sayHello(GreeterOuterClass.HelloRequest request, StreamObserver<GreeterOuterClass.HelloReply> responseObserver) { final GreeterOuterClass.HelloReply.Builder replyBuilder = GreeterOuterClass.HelloReply.newBuilder().setMessage("Hello " + request.getName()); responseObserver.onNext(replyBuilder.build()); responseObserver.onCompleted(); } }
実際にアプリケーションを作るにあたって
クライアントをどこで作るか
.proto
からクライアントをどこで作って配布するかみたいなのを考えていた。 .proto
を vendoringするためのツールを作っている方もいるみたい。
現在のところはgradleのビルドの中でRubyクライアントを作っている。クライアントが決まっていて1言語だけなら、これがコスパよいかなという気がする。
このgradleのプラグインで、Rubyクライアントも生成できそうなんだけど、なぜかCLIで grpc_tools_ruby_protoc -Iproto --ruby_out=lib --grpc_out=lib proto/*.proto
としたときにできる *_services_pb.rb
が生成されない。
このIssueと関連ありそうな気がするけど、コードまでは追えてない。
しょうが無いので、無理やりgradleの中でコマンドを実行してRubyのクライアントを生成するようにしている。 キレイにやるならCIで生成するとかかなぁ。
エラーハンドリング
まだ良くわかってないが、この辺を読んで見るつもり。というか、この公式のexamplesもっと早く見ておけばよかった・・・
色々資料を読んでいると、エラーの詳細を返したいときはメタデータに入れるのが一般的なのかなー。例えばバリデーション的なものをやりたいときに、gRPCからどんな風にエラーを返せばいいんだろ。
grpc/examples/ruby/errors_and_cancellation at master · grpc/grpc · GitHub
今のところの感想
なんとなく行けそうなイメージは出てきたけど、まだまだ色々調べなきゃいけないことが多い。
gRPCだけでなく、SpringBootやgradleとかもまだまだ「全然わからない。俺は雰囲気で(ry」って感じなので、ちゃんと作っていくとまだまだ色々ありそうな予感。
GoでGitHubのrepositoryをキレイにするツール作ってた
作ったのは大分前なんだけど、そういえばブログに書いてなかった。
こういうのの名前考えるのめっちゃ苦手で、好きなMGSのキャラクターから名前とったものの、結構有名っぽいOSSに同名のものがあってしくじった感ある。いい名前があればIssueください…
何をするツール?
GitHubのrepositoryのお掃除をするツール。と言っても、現在のところはReleaseを消す機能しかない。
# clean suusan2go/hoge repo releases created 1 months ago $ raiden releases clean -r hoge -o suusan2go --months 1
GitHubのRelasesは何か特殊な構造になっていて、GitのタグとGitHubで作成されたReleaseというのが混在する形になっている。なのでGitHubのAPIからReleasesを消してもタグは残っちゃうし、タグだけ消してもReleaseは残るという結構面倒な感じになっているが、このツールなら上記のコマンドで一気に消せる。
ちなみにタグだけを消す方法もあります。
# clean suusan2go/hoge repo releases created 1 months ago $ raiden tags clean -r hoge -o suusan2go --months 1
なんでこんなの作ったの?
会社(前職)のデプロイフローのなかでタグを作ったりReleaseを作ったりしていたんだけど、デプロイの度に作っちゃうもんだから積もり積もって数千件に達していてGitHubからpullしてくるのがちょっと遅くなっていた(らしい。インフラ見てる同僚談) GitHubの画面からポチポチは無理な感じだったので、シュッっと(実際はちょっとかかったけど)Goで作ってみた。
Goに詳しい人から教えてもらって、Cobraという奴を使ってみたんだけど、結構簡単にそれっぽいCLIツールができてしまって感動。
今後の展望
放置されたIssueやPRを閉じる機能とかそのうち作ろうかな‐と思っていたものの、今の職場はGitLabなんでどうしたもんかな。 GitLab悪くないんだけど、やっぱりGitHubが恋しいよ。
4Kディスプレイを買った
4Kディスプレイを買いました。
Dell ディスプレイ モニター U2718Q 27インチ/4K/IPS非光沢/5ms/DP,mDP,HDMI/sRGB 99 %/USBハブ/フリッカーフリー/3年間保証
- 出版社/メーカー: Dell Computers
- 発売日: 2017/09/29
- メディア: Personal Computers
- この商品を含むブログを見る
なぜ買ったのか
前から使ってたディスプレイ(Windows機、MacBookProのサブディスプレイ兼用)に線が入った状態になっていて、買い替えたいなーと思っていましたが、処分とか色々考えると面倒で何となくそのままに。
で、Windows機を新調するタイミングで、古いPCと合わせてディスプレイも引き取ってくれるとのことで、ついでに買い換えることにしました。
検討したやつ
このあたりを検討しました。
LG モニター ディスプレイ 24UD58-B 23.8インチ/4K(3840×2160)/IPS 非光沢/HDMI×2、DisplayPort/ブルーライト低減機能
- 出版社/メーカー: LG Electronics Japan
- 発売日: 2016/09/28
- メディア: Personal Computers
- この商品を含むブログ (1件) を見る
Acer ディスプレイ ゲーミングモニター XB281HKbmiprz 28インチ/4K解像度/1ms/G-Sync/Gaming
- 出版社/メーカー: 日本エイサー
- 発売日: 2015/11/27
- メディア: Personal Computers
- この商品を含むブログを見る
MacBookProとWin機の兼用で4K画質で使えるかが一番大きなポイントで、後は多少値段が上がってもディスプレイの位置とかを調整できたほうが嬉しいなと思い、そのあたり一番良さそうなDellのU2718Qを購入することにしました。
買ってみて
Macから接続するとRetinaディスプレイとして扱えなくて、字が小さすぎる3840x2160かちょっと大きく感じる1920x1080しか高解像度で選べなくて、やべーこれは失敗したかなーと思いました。しかし、試しにHigh SierraにOSを上げてみるとちゃんとRetinaとして認識されてちょうどよい解像度を選択することができました。
30Hzでしか出力できないのは誤算でしたが、ゲームをしたりするわけではないので、今のところそんなに気になっていません。Mac用に4Kディスプレイを買う方は以下をよくチェックしておくと良いです。(自分はMacBook Pro Retina, 13-inch, Mid 2014でギリギリ対応してると思ったら、Mid 2014から対応してるのは15-inchモデルだった…)
SwitchResXというアプリを使うと52Hzまでは上げられそう。ちょっとスクロールがかくつく感じはするので、試してみようかな。 kandrejevs.com
作業環境はこんな感じになった。
IDEやエディタとブラウザ、ターミナルを1画面に並べても十分作業できるし、買ってよかった。これは元の環境には戻れない感ある。
「ジェフ・ベゾス 果てなき野望」を読んだ
- 作者: ブラッド・ストーン,滑川海彦,井口耕二
- 出版社/メーカー: 日経BP社
- 発売日: 2014/01/08
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (23件) を見る
積んでた本を読んでみたシリーズ。きっかけは、この本を本棚から引っ張り出した娘が「パパ!パパ!」と言ってきたから。
どんな本か
Amazonの企業としての歴史を振り返り、社内でどんな事が起こっていたかが実際に社員(もしくは過去社員だった人)からのインタビューを中心にしてまとめられている。
最初の方のAmazonの成り立ちというか起業秘話みたいなところはふーんって感じでしかなかったんだけど(だからそこで止まってた)、以下のエピソードは面白かった。
感想
とにかくベゾスすげーなということに尽きる。自分で大きなビジョンを描いたらそれを達成するために手段を選ばないという感じが本当にすごい。最近では自前で運送もやりはじめているらしいけど、この本を読んだあとだと全く違和感がないというかそりゃやるだろうな…という感じがする。Kindle fireとか個人的には何でAmazonがこれ作る必要があるんだ?と思っていたけど、この本を読んだあとだ全く違和感がないというか、そりゃやるだろうなー・・・という感じだ。
ただAmazonで働いてみたいかというと、結構しんどそうだなーと思った。本当に猛烈に働き続けなきゃいけない感じで、最近よく聞く心理的安全性とか微塵も無さそうw(あくまで本を読んでの感想です。もしかしたら最近は or 部署によっては違うのかもしれない) 一番きっついなーと思ったエピソードは、会議でベゾスに「サポートに電話をかけると何分で繋がる?」と聞かれて、サポート担当のマネージャが「1分くらいです」と適当に答えたら、ベゾスがその場でサポートに電話をかけ始めたという話。数分たっても電話が繋がらず、ベゾスは真っ赤な顔でそのマネージャを罵りまくったとのこと。
とにかくこのベゾスは滅茶苦茶キレる(怒る)人のようで、各エピソードで一回以上はブチ切れてる感じだった。それでも最近は一通りブチ切れた後「でも、よく頑張ってくれた」的なことを言ってくれるらしいが…(改めて読み返してみたらそういう記述が見つけられなかったので、もしかしたら<そうであってほしい>という自分の妄想かもしれないw)
参考
AmazonからGoogleに移ったエンジニアのぶっちゃけ話。前もこれは見たことあったんだけど、本を読んだあとだとアーヤッパソウナンスネという感じだ。 anond.hatelabo.jp