Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

軽く自己紹介 20B、学部新3年 ヒヨッコWebエンジニア(学習始めてからまだ1年弱位) 今年はtraQのフルスタックなメンテナーだった 最近Goでサーバーばっか書いてる たまにReact + TSでクライアントも書く 最近はgRPC、Kubernetes、イベント駆動アーキテクチャ辺りがマイブーム 春休みにやってたこと KLab様とpixiv様のインターンに連続して参加していた(だいぶ体力がヤバい) 3/31まで続くので死なないように頑張ります 何個かブログを書いていた

Slide 3

Slide 3 text

Dockerの説明は割愛 まあ簡単に言うとパソコンの中で仮想のパソコンを立ち上げられるやつ ↑を実現する技術の中でもトップレベルに軽い アプリケーションを動かすとき、すごく管理がしやすい 結論: Dockerはいいぞ

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

Protocol Buffersとは 公式ページ: https://developers.google.com/protocol-buffers 通称Protobuf Googleが開発した 公式ページの紹介文をふんわり翻訳すると 言語に依存しない プラットフォームに依存しない 前方互換・後方互換を保ったまま 構造化されたデータをシリアライズするための 拡張可能なメカニズム

Slide 6

Slide 6 text

??????????????

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

シンプルに理解しよう Protobufとは 主にネットワークを介したAPI通信に使われる 多くのAPI通信で application/json が担っている部分を、 より軽量に より安全に より簡単に より広域で 扱うために開発された技術 ということさえ押さえられれば今回の話は理解できます

Slide 9

Slide 9 text

Jsonと比較して特徴を理解する

Slide 10

Slide 10 text

より軽量 JSON JSON文字列(text) <-> インメモリデータ(言語によるデータ型) の変換 Protobuf バイナリ <-> インメモリデータ(言語によるデータ型) の変換 バイナリへエンコードされる 連想配列のキー名など、最低限の伝達を担保するのに不必要な情報をギリギ リまで削っているので、JSONよりもわずかに軽い

Slide 11

Slide 11 text

より安全 スキーマの型安全性が、デフォルトでわかりやすく確保できる! Proto言語 Protobufのデータ構造を定義する言語 これが「Protobuf」と呼ばれることも あって紛らわしい 「message」という単位でスキーマを定義 フィールドにはID・名前・型をつける 「repeated」prefixで配列になる 書きやすい・わかりやすい YAML・JSONに比べてより言語ライク

Slide 12

Slide 12 text

より簡単 / より広域 Protobufは、Proto言語で書かれた定義ファイルから スキーマに対応するクラス・構造体 バイナリへのエンコード・バイナリからのデコードをする関数 を自動生成(コンパイル)して使う(バイナリの中身が共通化される) Protoc(Protobuf Compiler)を使って、数多くの言語実装をコンパイルできる デフォルト - C++ / C# / Java / JS / Kotlin / Objective-C / Python / Ruby アドオンを入れると - Go / Dart / C / PHP / Rust / Scala / TSなどなど API通信をする双方のサービスで、どちらも同じProto言語定義からコンパイルす ればよいので、スキーマ駆動な開発がしやすい!

Slide 13

Slide 13 text

より簡単 / より広域 JSONだったら... JSON文字列へのエンコード・デコードは言語頼り 完全に共通化されてはいない スキーマ駆動にしたい場合、SwaggerやらOpenAPIが主流 でもYAMLは長いしダルい Protobufは、コンパイル環境を整え、コンパイルされる関数の使い方さえ押さえ てしまえば、すごく手軽にスキーマ駆動な開発が始められる

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

CI / CDワークフロー上でのコンパイル CI 自分の場合 クライアント: TypeScript サーバー: Go GitHub Actionsを使う場合、 arduino/setup-protoc が使える 有志が用意してくれた、actions内でProtocバイナリを用意してくれるjob アドオン TSはnpmのパッケージをインストールするだけ Goの場合 go install で入れられる 🎉解決🎉

Slide 17

Slide 17 text

CI / CDワークフロー上でのコンパイル CD 自分の場合 Dockerでビルドし、ghcrに上げる Protobufのコンパイルをどこでするか プログラムのビルドをDockerのフロー内でするなら、Protobufのコンパイル もそうあった方が美しい Dockerのフロー内で用意するもの Protocのバイナリ アドオン

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

できたDockerfile

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

ちなみに ls したらちゃんと指定した場所にDLされていた which protoc ではちゃんと /temp/protobuf/bin/protoc が返ってくる

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

解決策 調べた限りでは 1. alpineのパッケージマネージャー、apkからインストール 2. ソースからビルド 3. alpineをあきらめる

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

マルチ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

Slide 37

Slide 37 text

またお前か

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

正解 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アーキテクチャの理解がおざなりなままマルチアーキテクチャ向けビルドを したツケが回ってきた

Slide 40

Slide 40 text

ARMにも対応させる ARM64用のバイナリ(aarch_64)があるので、対応は可能 どう切り替えようか?

Slide 41

Slide 41 text

TARGETARCH ARG (BuildKitを使用している時のみ)あらかじめ定義されたARG変数として使える TARGETPLATFORM ビルド結果のプラットフォーム linux/amd64、 linux/arm/v7、 windows/amd64 など TARGETARCH TARGETPLATFORM のアーキテクチャー部分 これ使えば、ビルドするアーキテクチャに合わせて変えられる! ただ、 amd64 / arm64 の形なので x86_64 と aarch_64 に直す必要が

Slide 42

Slide 42 text

shell芸で筋肉解決

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

ありがとうございました~ 参考文献 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