Sionの技術ブログ

SREとして日々の学習を書いて行きます。twitterは@sion_cojp

SREとバックエンドを統合してバックエンドに転向しました

はじめに

タイトルの通り、SRE歴だと6年 + 5社目ですが、株式会社チカクのSREからバックエンドに転向して2ヶ月経ちました。

この2ヶ月、フェーズが変わったプロダクトに追従出来ていなかったチームの開発プロセスを刷新することにメンバーと注力していました。

それについてお話しします。

SREを無くした

「SREはバックエンドが分かってないと信頼性を担保することは出来ない」

「バックエンドはインフラが分かってないとインフラを意識した設計、冪等性の担保、柔軟でベストな設計が出来ない」

と私は考えており、少人数で複雑なアーキテクチャでなければSREとバックエンドを分離する必要はないと思ってます。

またSREとバックエンド双方に課題があったことから、

ようやくこのタイミングでSREとバックエンドを統合することを、会社的にも良いと判断しそのような体制にすることができました。

SREの課題

弊社SREは私1人で運用していますが、ドキュメント化やdrawioで図にされており、

make apply .... と叩けば誰でもterraformが打てる環境だったりPull Request welcome状態なのですが、

課題として役割的にも知見的にもやりたがる人がいないことでした。

結果、私の知見がドキュメントベースで蓄積するだけで他メンバーのaws, terraformの経験が成熟せず、awsコンポーネントを用いた設計するときがSPOFになる状態でした。

バックエンドの課題

社内事情が色々あって仕方ないタイミングだったと思いますが、「言語とフレームワークは選んだのであとはよろしく」と急に依頼がくるというカオスな状態でした。

「運用するための必要な修正が難しい場合はSREに依頼がくる」ことが多かったり、またDB migrate方法もSREが確立しなければなりませんでした。

なので選ばれた言語/フレームワークを理解する必要があり、これは結構しんどかったので最初の設計段階から関与する必要がありました。

cron(定期実行batch)に関しても「冪等性を担保してないので多重実行禁止だけど、数分単位で動く」というケースもあります。

おそらく「冪等性はインフラレイヤで担保してくれるだろう」という考えからだとは思いますが、ロック機構を作ってまでインフラレイヤで担保するかというところから議論したい気持ちではあります。

個人的には第一にバックエンド側で担保。どうしても無理ならインフラレイヤで担保するのが良いですね。

数分単位で動くのであればデーモン化 + job queueシステム。そんなに実行しないのであればcronにするのがベストだと思ってます。

他にも色々課題が山積みで、あとの項目でも一部紹介します。

SREとバックエンド統合してどうなった?

アーキテクチャ設計段階でSREとバックエンド双方の知識を保有してレビューをするので上記課題も解決できました。

設計時は、全バックエンドメンバーでシーケンス図(mermaid記法)をリアルタイムに書きながら議論し合える環境にしたのも良かったと思います。

課題としてはterraformのコードの質ですね。

terraformはresource名や設定が破壊的変更になったりするので、そこのネームスペースや依存関係をいかに考えて実装しないとサービス断が発生します。

そこは経験かと思います。

バックエンドに転向して何したの?

1. 開発体制を変えた

まず少人数なので性善説ベースで。READMEや自明なものはどんどんセルフマージしてOKにしました。

もし何かあったらその人の責任で、責任分散の意図でレビュー依頼をしましょうというルールです。

Pull Requestに関しては、今までは何も書いてないPRが多かったのですが、絶対書くべき項目として「概要と動作確認」を用意しました。

issueベースでPRがあればベストです。

レビューコメントは「feature/imo/yagni/nits/must」使いつつ、must以外はapprove。

これらに関しては、mergeスピードが速くし開発スピードをあげたい、PRにログ(挙動)を残したい、宗教的論争をなくしたいのが目的です。

実装があってればリファクタコメントはimoにして、気になる人が直せば良いです。

私はこんな感じでレビューコメントをしてたりします。

2. service design docを書いた

github.com

のような、サービスを立ち上げる際に必要なチェックリストです。

「自分たちは自明なことでも、PdMやその他エンジニアがこのシステム大事なことをみれば分かりやすく書く。深ぼりたいときはコードを読む」を意識してます。

現在のリストはこちら。ここから成熟していけたらと思ってます。

- サービス
- Sequence Diagram
- aws architecture diagram
- バックグラウンド
    - ここに存在するサービスがなぜ必要なのか
- 技術
    - 言語
    - ルーティング
    - ORM
    - AWS
    - Monitoring
- 目標と非目標
    - やること、やらないこと
- インターフェイス
    - HTTPならURIをまとめたもの(swagger)のリンクを貼る
- 依存関係
- SLO
- データベース
    - 使ってるDBと権限
- バックアップと復旧
    - RTO(目標復旧時間)
    - RPO(目標復旧時点)
- PI/PII  
- セキュリティに関する考慮事項
- リファレンス
    - このリポジトリを利用する上で参考になるurl

3. Rails, DjangoからGoに移行した

弊社ではRails, Djangoフレームワークを利用してますが、まずこのような課題がありました。

  • 自由にカスタマイズすることに弱い

    • 「ログをこうしてほしい」と伝えても自由にカスタマイズするのが難しいようでした。
    • Djangoのloggerに関してはSREが実装しました。
  • OSSにPRを出すのに弱い

    • 実装に必要だけど対応してないOSSがあった場合、アップデート待ちでした。
    • 全然更新されず、SREがPRを出してたりしました。

これらの原因は技術力ではなく、言語/フレームワークと技術力のミスマッチだと思ってます。

「ある程度フレームワークを使わない実装をGoで書き、適材適所でpackageを用いた方が自由にカスタマイズ出来て良い」と私は判断して会社に通しており、新しいサービスではGoを採用しました。

今後はRails, DjangoサービスもGoに移行する予定です。

4. Goを導入するにあたって

まずtodo-apiのboilerplateを作成しました。

SOLID、凝集度、結合度を意識したクリーンアーキテクチャの実装で、CRUDAPIとそれぞれのテストを書いて、メンバーに解説しました。

新サービスではそれをベースにAPIをメンバーに書いてもらい、batch系は自分が書いております。

それもいずれメンバーに引継ぎますが、シーケンス図、コメントを読みながらテスト環境で実行すればもういけると思います。

またGoのplaygroundリポジトリを作成しました。

  • html/templateをどうやって使うか
  • 並行処理
  • 並行処理中のgraceful shutdown
  • spreadsheetの操作

などさまざまなサンプルを用意しました。

またメソッド名に「自明でもある程度コメントを書く」ということをしてます。

これはRailsで全くコメントを書いてない運用をした結果、初見者が参入しづらい状態になってたのと、

メンバーに「私が同じことしてもいいか?」と聞いたところ「それは厳しい」と言われたのもあります。

(コメントじゃなくてcommit logやPRに書けばいいだけなんですが、まぁcommit log見ない人もいるし、社内リポジトリならこのほうが分かりやすいですよね)

またGoを導入したことで「なければ作ればいい」という発言がよく出るようになったのは一番の成果だと思います。

5. ドキュメントを減らしてmakeに寄せた

開発環境を用意するためにREADMEを見て、このコマンドを打って...

といったオペレーション系ドキュメントはMakefileに寄せた方が冪等性が担保されます。

make help と打つと、開発やオペレーションに必要なコマンドが出てくるようになってます。

こちらはapiの例。

$ make help
dist                    create .tar.gz linux & darwin to /bin
clean                   このMakefileで利用したファイルを docker image 以外クリアにする
clean_all               このMakefileで利用したファイルをクリアにする
build                   build
build/cross             create to build for linux & darwin to bin/
run                     go run
run/binary              run binary
go/get                  特定のパッケージをgo getする
go/mod_tidy             不要なgo packageを削除する
go/test                 go test
go/create_fixtures      fixutresをDBに作る
go/test_with_create_fixtures  fixutresをDBに作りつつtestする
go/test_package         特定のパッケージのみのテストを行う
docker/build            docker build
docker_compose/up       compose起動
docker_compose/down     compose停止
docker_compose/down_f   composeではないけど、dockerで強制停止する. conflict対策
docker_compose/down_all  compose停止 + 全てを初期化する
docker_compose/rebuild  appだけbuildし直す
migrate/up              migration. docker compose up後に実行できる
migrate/down            migrationのrollback. docker compose up後に実行できる
migrate/create          migrationファイル作成. migrations/ にup/downが作成される

# deploy系はdeploy.mkで管理してるこっち
$ make -f deploy.mk
deploy/all              docker build/push -> db migrate/seed -> fargate deploy
login                   ECRにlogin
build                   docker build. linux/amd64用にbuildする
push                    docker push to ECR。ENVタグとrevisionがついたタグ両方をpushする
deploy                  既にあるイメージでfargate deployだけする(dockerbuild/pushはしない)
image_check             ecrにイメージがあるかチェックする。無駄なbuildを無くすため。
db/migrate              db/migrateする

6. メールシステムを新規作成した

前述した通り、RailsのAction Mailerを使ってるとGoでメールを送るシステムがありませんでした。

本当はsendgridを使って運用面もしっかり管理できるような体制にしたかったのですが、期間がなかったので、

SQS -> Lambda -> SESのシステムを作り、GoでSQSにメール用のqueueを送る go-mailer という社内パッケージを作成しました。

// こんなメソッドを用意して使ってます
func (m *Mail) SendMail() error {
    mailer := &gomailer.Mailer{
        AwsProfile: m.AwsProfile,
        AwsRegion:  m.AwsRegion,
        Mail: gomailer.Mail{
            To:       m.ToMailAddress,
            From:     m.FromMailAddress,
            Bcc:      m.BccMailAddress,
            Template: gomailer_template.TemplateTest,
            BodyReplace: map[string]string{
                "FullName":  m.FullName,
            },
        },
    }

    // SQSにqueueを積む
    if err := mailer.Send(); err != nil {
        return err
    }

    return nil
}

パッケージにメールのテンプレートを保管してるので、Goで送るメールは全てそこに集約され管理しやすくもなるといった意図もあります。

また移行するときSESをus-east-1からap-northeast-1に作り直しました。

7. 新サービス用のバッチを書いた

意識したのは冪等性。

一連の流れがあったとして、コンテナに停止シグナルが送られ際に、最後まで実行してgraceful shutdownしてほしいところはどこか。

何回実行しても同じ結果になるか。メンバーとそこを中心に議論とレビューとサンプルを用意して実装しました。

あとはなるべくローカル開発環境で実行させたく、そこの環境構築も力を入れてたのですが、

開発終わり間際でロジックが変わったりして、そこは今微妙になってますが、改善していきます。

(例えばAWS S3の代替は https://github.com/minio/minio を使ったりしてます)

経営的なところを手伝った

あまり話せないのですが、戦略面で2週間ほど色々と手伝ってました。

正直とてもしんどかったですが、私は言語化が得意だったり、場をまとめてポジティブに進めていくのがうまいんだなと再認識できたので、やってよかったです。

次はどうするか

運用面。ドキュメントやテストコードが足りないのでそこは補填していく気持ちです。

またR&D的にflutterのplaygroundを社内で書き始めてます。

そこも色々と課題があってflutterを一度チョイスしてみてます。

あとは色々勉強する必要があるので、今後も引き続き勉強していきたいと思ってます。