suusan2号の戯れ

SIerでインフラSE⇛WEB系でエンジニアのおっさん

Netflixでデビルマンを見た

devilman-crybaby.com

深夜に調べ物をしていてそのお供に見た。ながら見だったんだけど、割とトラウマになった。。。どれくらいかというと、ぼーっとしてるとその辛いシーンが思い出されて陰鬱な気分になるレベル。 ただ本当に傑作だと思った。

以下激しくネタバレ含みます。

続きを読む

2017年の振り返り

2017年は上半期で振り返ってた suzan2go.hatenablog.com

2017年の下半期振り返り

11月に転してた。

7月

7月の最初には関わっていた決済回りの機能追加プロジェクトが無事リリース。かなりリファクタリングもしたし、決して単純な機能ではなかったが大きなバグも起こさずにいけたのは安心したし自信になった。

7月の後半からマネージャーから打診を受けてリーダー的な役割をやることになった。この時点で退職を考えていたというか既に内定が出ていたのでリーダー的な役割を引き受けるか迷ったけど、正直に話したところそれでもやってみてほしいということだったので頑張ることに。

8月

リーダー的な立場になり数値を追っていくことや、ビズデブ的な人たちとの関わりなどこれまで以上にやることになったが、1つ視座が上がった感じで割と楽しめた。

KARTEというツールを導入して、キャンペーンとかサイト内プロモーションをエンジニア無しに大体できるようになったのが自分のやった仕事としては大きいと思う。導入までは結構頑張ってその後の運用は優秀な新卒氏に任せたんだけど、ガンガン使い込んでいてとても頼もしかった。半分冗談でKARTE大臣!と呼んで任せていたら本当にめちゃくちゃ詳しくなっていって、名をつけて任せるって大事だなーと実感した。 karte.io

今も活用されているようで嬉しい。 qiita.com

9月

転職後に触ることになりそうなKotlin + Spring Bootの勉強をプライベートで始めていた。

github.com

9月後半からは社内でScalaも始まってたので、Kotlinで勉強していたことやSpringでのレイヤーわけは地味に役に立った。

10月

サービスの運用負荷を下げるためのサポートの人向け機能開発とか、自分が過去に開発に関わったサービスのリアーキテクチャとか、自分が rails new したサービスの面倒を見ていた。サービスのリアーキテクチャは、完全に自分が(期間的に)やりきれない案件だったので、自分の理想を押し付ける形にならないように、実際に手を動かす人と認識を合わせることを意識した。その後どうなったかは聞けていないんだけど、頑張って欲しい…すみません…

このタイミングで祖母が亡くなったりしてめちゃくちゃドタバタしたものの、サポートの人にお願いされていた管理系の機能は全てリリースできたのでよかった。

そして10月の後半は2年半在籍したスタートアップ?の最終出社日だった。2度目の転職。何で転職したかというと主に理由は二つ。

  • 条件面
  • 技術的に今と違うことがやりたくなった

会社の公式エンジニアブログに何故か退職エントリーを書きました…

11月

今思うとツールだけの問題ではないんだけど、前職で当たり前に使っていたツールが使えないので転職してすぐは結構テンションが下がっていた。

あと入社してしばらくお腹こわしていたんだけど、今思うとこれ明らかにストレスだな…

この辺のお気持ちは以下で表明している。 suzan2go.hatenablog.com

仕事内容的な話でいうとgRPC + Spring Boot + Rails(これはこれから) + Nuxt.js なプロジェクトをほぼほぼ1人で回している。当初はgRPCやるつもりなかったんだけど、ちょっと触ってみると思ったよりも良いものだった。

DDDを実践するためにこういう本を買って読んでいるんだけど、自分がやっているのはレイヤードアーキテクチャ(or オニオンアーキテクチャ or クリーンアーキテクチャ)なだけで所謂 軽量DDD の域を出ていないなぁと思う。大分ドメインの知識がついてきたので、今年はドメインエキスパートと話して諸々すり合わせをしていきたい。

実践ドメイン駆動設計

実践ドメイン駆動設計

12月

Slackが使いたすぎて、Slackを入れたいという話をして資料を作って、偉い人に持っていったら何やかんやあって導入が決まった。過去の経緯とか聞いてると、色んな人の下準備が色々あったので、自分の提案で通ったって感じっぽい。

他のチャットツールをディスるわけではないけど、現時点でやっぱり開発用のチャットとしてはSlackが最強だなーと。GitHub(会社ではGitLabだけど…)の通知を垂れ流すだけでも情報共有という意味ではかなり価値があるし、セキュリティや言語、FW関連の情報をRSSで流せるのもいい。

あと振り返り・プランニングの習慣を他チームに広めたりしていた。振り返りでとりあえずKPTやってみたんだけど、めちゃくちゃいろんな意見が出て他チームながらとても有意義だった。Problemとしてとても重い課題(組織構造的な話)がでたんだけど、それでも「XXと話す場を作って相談してみる」という具体的なTryが出て実行されたのはとても良かった。

まとめ

11月の転職が大きな出来事でした。前職は不満が1つもないといったら嘘になるけど、エンジニアとして働く環境という意味ではとても良い会社だったなーと改めて気付かされました。そして、自分が当たり前に享受していた諸々は決して当たり前ではなかったなーというのを実感して、感謝の気持ちでいっぱいです。

現職はポジティブに捉えると改善しがいがあることが沢山あるし、色んなことを任せてもらっているので、メインのプロジェクトと平行して頑張っていきたい。とはいえ段々と手が足りなくなってきた感はあるので手伝いたい人ご連絡ください。

改善しがいがあることが多い(しかも自分の経験が役に立ちそう)というのはとても嬉しいことなんだけど、一方でそれをやるために転職したわけではない…という思いは正直ある。知り合いから「組織を変えるには、それなりの立場、権限、報酬がセットじゃないと消耗するだけで得るものがない」とも言われているので、かかる労力や期間がどれくらいかというのはぶっちゃけ結構シビアに考えている。

2017年下半期の抱負の進捗

機械学習

進捗ダメです

アプリ

Spring Boot + Kotlin + gRPCの勉強に消えていきました・・・

2018年の抱負

力尽きたのでまた週末に…

自分が仕事で使うツールやサービスは割とモチベーションに影響するのだなーという話

※あくまで自分にはそうだったという話

所謂スタートアップと言われる会社から、それなりの規模の会社に転職してもうすぐ2ヶ月くらいになる。

今の会社に転職してくるときに、開発に使っているツールについては説明を受けていたし、当然それを知った上で入社したのだけれど、実際に使ってみると思ったよりもテンションが下がった自分がいた。

特に以下のようなソフトウェアエンジニアとして日常的に使うツールのグレードが下がると(正確には必ずしもグレードが下がったとはいえないものもあるんだけど)、辛かった。

こういう周辺のツールにモチベーションが左右されてしまうのはどうなんだろうと思っていたし、転職で会社を選ぶ時にも割と軽視していたのだけれど、自分には重要な要素だったようだ。

正直な話、入社して最初の週末は人事の方や誘ってくれた人に頭下げて辞めさせてもらおうか…という考えが頭をよぎる程度にはテンション下がっていた(今思うと単純にツール・サービスだけの話しではなく、環境が変わったとか複合的な要素が絡んで必要以上にナーバスになっていたのだけれど)。

ただ「今いる環境を良くしていくことが出来なければ同じことをどこに行っても繰り返すことになるのでは?」と思い直し、「もっとこうしたい」ということを発信してみると、賛同してくれる人がそれなりにいることがわかった。

そういった声をいわゆる偉い人達も後押ししてくれていたり、他にもXXを改善したい!と手を上げる人が出てきたりして、今は色々と良い方向に改善していきつつあると感じる。

またおこがましい言い方なんだけど、自分がそういう流れを作れた気がしていてそれがちょっと嬉しい。

全くまとまりのない文章だけど、Slack導入が承認されたのでその記念に。

grpc-gatewayでMetadataに詰めたエラーの内容をJSONに詰めてRESTクライアントに返したい

引き続きgRPCの話。

gRPCでエラーをクライアントに返したい場合、通常だとステータスコードとエラーメッセージしか返せない。例えばアプリケーションレベルのバリデーションエラーみたいなものを返したい時、メルカリさんの資料によるとMetadataに詰めて送ると良いらしい、

speakerdeck.com

gRPCがクライアントのときは詰めたMetadataを読み出せばいいんだけど、grpc-gatewayでRESTのクライアントに返す時にはどうしたらよいか調べた。

grpc-gatewayのエラーハンドリングをカスタマイズする

実はGitHubwikiHow to customize your gateway というのがあって、そこに結構色々と書かれている。

How to customize your gateway · grpc-ecosystem/grpc-gateway Wiki · GitHub

で、Wikiから辿った先にあるブログに実際に結構丁寧にエラーレスポンスのカスタマイズ方法が乗ってるので、それを参考にすればよい。

My Code Smells!

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を使う

github.com

これは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

そうなんだけど実際に試してみると、クライアント側でセットしたメタデータがサーバーまでたどり着かない。

github.com

調べてみたら同様の症状の人が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を使う

shiladitya-bits.github.io

クライアント側はこれが参考になった。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アノテーションを付与するだけで、 SpringgRPC を共存させることができる。

github.com

    @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するためのツールを作っている方もいるみたい。

github.com

現在のところはgradleのビルドの中でRubyクライアントを作っている。クライアントが決まっていて1言語だけなら、これがコスパよいかなという気がする。

github.com

このgradleのプラグインで、Rubyクライアントも生成できそうなんだけど、なぜかCLIgrpc_tools_ruby_protoc -Iproto --ruby_out=lib --grpc_out=lib proto/*.proto としたときにできる *_services_pb.rb が生成されない。 このIssueと関連ありそうな気がするけど、コードまでは追えてない。

github.com

しょうが無いので、無理やりgradleの中でコマンドを実行してRubyのクライアントを生成するようにしている。 キレイにやるならCIで生成するとかかなぁ。

エラーハンドリング

まだ良くわかってないが、この辺を読んで見るつもり。というか、この公式のexamplesもっと早く見ておけばよかった・・・

色々資料を読んでいると、エラーの詳細を返したいときはメタデータに入れるのが一般的なのかなー。例えばバリデーション的なものをやりたいときに、gRPCからどんな風にエラーを返せばいいんだろ。

grpc/examples/ruby/errors_and_cancellation at master · grpc/grpc · GitHub

今のところの感想

なんとなく行けそうなイメージは出てきたけど、まだまだ色々調べなきゃいけないことが多い。

gRPCだけでなく、SpringBootやgradleとかもまだまだ「全然わからない。俺は雰囲気で(ry」って感じなので、ちゃんと作っていくとまだまだ色々ありそうな予感。

GoでGitHubのrepositoryをキレイにするツール作ってた

作ったのは大分前なんだけど、そういえばブログに書いてなかった。

github.com

こういうのの名前考えるのめっちゃ苦手で、好きな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というのが混在する形になっている。なのでGitHubAPIから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ツールができてしまって感動。

github.com

今後の展望

放置されたIssueやPRを閉じる機能とかそのうち作ろうかな‐と思っていたものの、今の職場はGitLabなんでどうしたもんかな。 GitLab悪くないんだけど、やっぱりGitHubが恋しいよ。