$30 off During Our Annual Pro Sale. View Details »

DockerでProtobufをコンパイルしたい!

logica
March 27, 2022

 DockerでProtobufをコンパイルしたい!

このスライドは、traP LT 2022( https://connpass.com/event/242713/ )の発表資料です。
Protocol Buffers (Protobuf)を使ったWebアプリを作る機会がありました。デプロイする際Dockerイメージにビルドする必要があるのですが、Protobuf公式からはProtoc (Protobuf Compiler)が動くイメージが公開されていません。「コンパイルで自動生成されたコードをリポジトリに入れるのは美しくない!」というこだわりを持った僕が、DockerでProtocを動かし、Protoファイルから各種コードのコンパイルを成功させるまでの道のりをお話しします。

logica

March 27, 2022
Tweet

More Decks by logica

Other Decks in Technology

Transcript

  1. DockerでProtobufをコンパイルしたい! logica Twitter: @logica_dev GitHub: logica0419 Zenn: logica0419

  2. 軽く自己紹介 20B、学部新3年 ヒヨッコWebエンジニア(学習始めてからまだ1年弱位) 今年はtraQのフルスタックなメンテナーだった 最近Goでサーバーばっか書いてる たまにReact + TSでクライアントも書く 最近はgRPC、Kubernetes、イベント駆動アーキテクチャ辺りがマイブーム 春休みにやってたこと

    KLab様とpixiv様のインターンに連続して参加していた(だいぶ体力がヤバい) 3/31まで続くので死なないように頑張ります 何個かブログを書いていた
  3. Dockerの説明は割愛 まあ簡単に言うとパソコンの中で仮想のパソコンを立ち上げられるやつ ↑を実現する技術の中でもトップレベルに軽い アプリケーションを動かすとき、すごく管理がしやすい 結論: Dockerはいいぞ

  4. 昨年12月のこと traPで冬ハッカソンがあった 自分は5人チームでWebアプリケーションを作ることに サーバー・インフラを担当することになった 自分含めたサーバーサイド二人で技術スタックを選んでいた時 相方「gRPCっての面白そうだから使ってみたい」 自分「おk」 Protocol Buffersの世界に入門した ちなみにgRPCはクライアント

    - サーバー通信には向かなかったので、ハッカ ソンではProtocol Buffersのみ使用 ※ gRPCでも今回の話はほぼ共通 REST APIとWebSocketに乗っけた
  5. Protocol Buffersとは 公式ページ: https://developers.google.com/protocol-buffers 通称Protobuf Googleが開発した 公式ページの紹介文をふんわり翻訳すると 言語に依存しない プラットフォームに依存しない 前方互換・後方互換を保ったまま

    構造化されたデータをシリアライズするための 拡張可能なメカニズム
  6. ??????????????

  7. シンプルに理解しよう 構造化されたデータとは JSONとかRDBの中身とか 「配列と連想配列から構成されたデータ」といえる? API通信で情報を送るとき、整頓したデータが送れるので非常に扱いやすい REST APIなどでよくJSONスキーマが使われる所以 ↑Protobufはちょうど「JSONスキーマ」の部分に相当する概念!

  8. シンプルに理解しよう Protobufとは 主にネットワークを介したAPI通信に使われる 多くのAPI通信で application/json が担っている部分を、 より軽量に より安全に より簡単に より広域で

    扱うために開発された技術 ということさえ押さえられれば今回の話は理解できます
  9. Jsonと比較して特徴を理解する

  10. より軽量 JSON JSON文字列(text) <-> インメモリデータ(言語によるデータ型) の変換 Protobuf バイナリ <-> インメモリデータ(言語によるデータ型)

    の変換 バイナリへエンコードされる 連想配列のキー名など、最低限の伝達を担保するのに不必要な情報をギリギ リまで削っているので、JSONよりもわずかに軽い
  11. より安全 スキーマの型安全性が、デフォルトでわかりやすく確保できる! Proto言語 Protobufのデータ構造を定義する言語 これが「Protobuf」と呼ばれることも あって紛らわしい 「message」という単位でスキーマを定義 フィールドにはID・名前・型をつける 「repeated」prefixで配列になる 書きやすい・わかりやすい

    YAML・JSONに比べてより言語ライク
  12. より簡単 / より広域 Protobufは、Proto言語で書かれた定義ファイルから スキーマに対応するクラス・構造体 バイナリへのエンコード・バイナリからのデコードをする関数 を自動生成(コンパイル)して使う(バイナリの中身が共通化される) Protoc(Protobuf Compiler)を使って、数多くの言語実装をコンパイルできる デフォルト

    - C++ / C# / Java / JS / Kotlin / Objective-C / Python / Ruby アドオンを入れると - Go / Dart / C / PHP / Rust / Scala / TSなどなど API通信をする双方のサービスで、どちらも同じProto言語定義からコンパイルす ればよいので、スキーマ駆動な開発がしやすい!
  13. より簡単 / より広域 JSONだったら... JSON文字列へのエンコード・デコードは言語頼り 完全に共通化されてはいない スキーマ駆動にしたい場合、SwaggerやらOpenAPIが主流 でもYAMLは長いしダルい Protobufは、コンパイル環境を整え、コンパイルされる関数の使い方さえ押さえ てしまえば、すごく手軽にスキーマ駆動な開発が始められる

  14. Protobuf is 神 ...だけど、Gitリポジトリに入れようとしたとき少し悩ましいことがある

  15. 自動生成コードのGitリポジトリ上での扱い 自動生成コードやライブラリのローカルキャッシュ(node_modules) 別のファイルから一意的に状態が復元できる 無駄な容量を食う・生成すると無駄なDiffが増える・生成し忘れが発生するな どの理由で、一部の例外を覗いてGitリポジトリに含めるのは好ましくない Protobufの自動生成コードもこれにあたると自分は考える でも、ビルド時には必要 なんとかしてビルドするタイミングでコンパイルしないといけない! コンパイルに必要なもの Protocのバイナリ

    アドオン
  16. CI / CDワークフロー上でのコンパイル CI 自分の場合 クライアント: TypeScript サーバー: Go GitHub

    Actionsを使う場合、 arduino/setup-protoc が使える 有志が用意してくれた、actions内でProtocバイナリを用意してくれるjob アドオン TSはnpmのパッケージをインストールするだけ Goの場合 go install で入れられる 🎉解決🎉
  17. CI / CDワークフロー上でのコンパイル CD 自分の場合 Dockerでビルドし、ghcrに上げる Protobufのコンパイルをどこでするか プログラムのビルドをDockerのフロー内でするなら、Protobufのコンパイル もそうあった方が美しい Dockerのフロー内で用意するもの

    Protocのバイナリ アドオン
  18. DockerでProtobufをコンパイルしたい! こっからはGoに注目して話を進めます 基本的にNode環境上ででTSにコンパイルするのも同じ

  19. 二つの大きな壁 個人的にめっちゃ苦しめられた壁が二つあった 1. Distroの壁 2. CPUアーキテクチャの壁

  20. Distroの壁 公式から出されている protoc イメージみたいなのが無い 自分でやるしかない... pull時間の短縮のため、軽量なイメージをベースとしたかった golang:1.*.*-alpine を使用 とりあえず、バイナリ取ってきてPATH通せばええやろ!

  21. できたDockerfile

  22. イメージの中でコンパイル 詳細は省くが、 RUN protoc --go_out=proto protobuf/*.proto でできるはず log ... #8

    0.379 /bin/sh: protoc: not found
  23. None
  24. None
  25. イメージの中でコンパイル PATHが通ってないだけかもしれない... フルパスで指定すれば... RUN /temp/protobuf/bin/protoc --go_out=proto protobuf/*.proto log ... #8

    0.480 /bin/sh: /temp/protobuf/bin/protoc: not found
  26. None
  27. None
  28. ちなみに ls したらちゃんと指定した場所にDLされていた which protoc ではちゃんと /temp/protobuf/bin/protoc が返ってくる

  29. 何がいけなかったのか 調べたら... ProtobufのコアがC++で書かれていた ことが大きな理由だったことが判明した 配布バイナリのビルドがGNU C Library(glibc)に強く依存している Alpine Linuxはこの環境がない(muslってやつを使ってる) 対応してない

    = 認識されなかったと推定できる
  30. 解決策 調べた限りでは 1. alpineのパッケージマネージャー、apkからインストール 2. ソースからビルド 3. alpineをあきらめる

  31. apkから メリット 手軽、コマンド一つで入る 速い デメリット ちょっとバージョンが古い 現在最新版は3.19.4 更新が去年10月... Protobufの更新と対応してない

  32. ソースからビルド 必要パッケージをインストールして、alpine上でビルドすればalpine上で動く 公式で説明があるのでやりやすい https://github.com/protocolbuffers/protobuf/blob/master/src/README.md メリット alpineを捨てずに最新版が取ってこれる 一番usageとして綺麗かな~(個人的な感想) デメリット (多分)Dockerビルドが遅い ソースからビルドする時間かかるので...

  33. alpineをあきらめる debianにすれば行けるらしい

  34. alpineをあきらめる ビルドが通った!!!!!

  35. CPUアーキテクチャの壁 手元でのビルドは通った 問題は、GitHub Actionsでビルドをしたときに起こった

  36. マルチCPUアーキテクチャサポート 先輩がやっていたので、マルチCPUアーキテクチャ向けにビルドをしていた Actionsの設定 ... - name: Build and push uses:

    docker/build-push-action@v2 with: context: . push: true platforms: linux/amd64, linux/arm64 tags: `タグ名` 結果 > [linux/arm64 builder 8/8] protoc ... protoc: not found
  37. またお前か

  38. 敗因を探せ! > [linux/arm64 builder 8/8] protoc ... protoc: not found

  39. 正解 wget "https://.../protoc-3.19.4-linux-x86_64.zip" の x86_64 でした AMD64向けバイナリだからARM64で動くわけがない > [linux/arm64 builder

    8/8] protoc ... <- 毎回armだったので、arch怪しそうの顔になる protoc: not found CPUアーキテクチャの理解がおざなりなままマルチアーキテクチャ向けビルドを したツケが回ってきた
  40. ARMにも対応させる ARM64用のバイナリ(aarch_64)があるので、対応は可能 どう切り替えようか?

  41. TARGETARCH ARG (BuildKitを使用している時のみ)あらかじめ定義されたARG変数として使える TARGETPLATFORM ビルド結果のプラットフォーム linux/amd64、 linux/arm/v7、 windows/amd64 など TARGETARCH

    TARGETPLATFORM のアーキテクチャー部分 これ使えば、ビルドするアーキテクチャに合わせて変えられる! ただ、 amd64 / arm64 の形なので x86_64 と aarch_64 に直す必要が
  42. shell芸で筋肉解決

  43. これでマルチアーキテクチャ向けビルドも通る めでたしめでたし

  44. 今後の展望 汎用イメージを作って公開したい 以前一瞬作ったけど、マルチアーキテクチャ向けビルドで引っかかって「ダ メだ!」ってなって削除してしまった ソースからビルドで作れば、軽量な汎用イメージも夢じゃない 管理がめんどいので、dependabotからのアップデートを完全に自動化したい Actions芸頑張ります...

  45. ありがとうございました~ 参考文献 Protocol Buffers - https://developers.google.com/protocol-buffers Alpine Linux 上で gRPC

    を使ってはまった話 - https://qiita.com/takkeybook/items/5eae085d902957f0fe5b "protoc: not found" on an Alpine-based Docker container running Protocol Buffers - https://stackoverflow.com/questions/64447731/protoc-not-found- on-an-alpine-based-docker-container-running-protocol-buffers protoc(Alpine Linux Packages) - https://pkgs.alpinelinux.org/package/edge/main/x86/protoc