Slide 1

Slide 1 text

関東財務局長(少額短期保険業)第87号 AWS CDKのKotlinラッパーを作った話 0 2021.8.25 toliner

Slide 2

Slide 2 text

1 自己紹介 ■ Toliner(トリナー) ◆ Twitter: @toliner_ ◆ GitHub: toliner ■ 大学3年生(情報系学科) ■ Kotlin歴は4年ほど ◆ ただしAndroidはほぼやった事が無い

Slide 3

Slide 3 text

2 サマリー ■ AWS公式のIaaCライブラリ/ツールであるAWS CDKを使う事になった ■ 当時のサーバー側主力言語はKotlinなのでKotlinで良い感じに書くために CDK Javaのラッパーを作る事にした ■ CDKのコード量は膨大なので自動生成する事にした ■ 中身が闇の魔術

Slide 4

Slide 4 text

3 AWS CDK Kotlin DSL概要 ■ AWS CDK JavaのKotlinラッパー ■ Kotlin DSL形式で書けるようにラッパー関数を多数提供 ■ Kotlin Poetによるコード自動生成 ■ CIによるAWS CDK Javaの更新の自動検知 ■ GradleによるAWS CDKの新バージョン検知 -> コード生成 -> ビルド -> 公開の自動化 ■ OSS(現在別のところに移管済み)

Slide 5

Slide 5 text

4 コード生成の仕組み ■ AWS CDK JavaはjsiiというツールによりAWS CDK TypeScript のラッパーが生成されている ◆ つまりAWS CDK Javaは自動生成されたコード ■ 自動生成されたコードなら一定のパターンが存在する ◆ 特にAWS CDKは元のコードがデータの種類が多いだけでAPIとして はほぼ同じなのでその傾向が強い ■ AWS CDKのパッケージの全クラスをスキャンし、条件にマッ チするクラスを抽出・そのクラスのラッパーを生成する

Slide 6

Slide 6 text

5 生成されたコードとAPI比較 ■ AWS CDK Java ■ AWS CDK Kotlin DSL ■ 生成されたコード

Slide 7

Slide 7 text

6 生成されたコードとAPI比較 ■ AWS CDK Java ■ AWS CDK Kotlin DSL ■ 生成されたコード BuilderScope???

Slide 8

Slide 8 text

7 BuilderScope ■ DSLの処理の実態を持っている部分 ■ 設定可能なプロパティを全部保持し、build()でAWS CDK Java のクラスに引き渡す ■ 実質的にAWS CDK Javaのほぼ全クラスを再定義している ◆ よってファイルサイズが莫大に(S3モジュールで4700行以上)

Slide 9

Slide 9 text

8 生成されたコードのビルド ■ コードの生成や成果物の公開はAWS CDKのモジュール毎に行いたい ◆ AWS CDKを全て単一のモジュールに纏めると総サイズが無駄に肥大化 ■ ビルドはGradleで行うので、AWS CDKのモジュールに対応した Gradleのモジュールを追加すると楽にできそう ■ AWS CDKにどんなモジュールがあるかは生成時にしか分からない ■ => Gradleのビルドスクリプトも自動生成すれば良い

Slide 10

Slide 10 text

9 ビルドスクリプトを生成するビルドスクリプト ■ GradleにはbuildSrcという、「Gradleのビルド実行前にビルドされ、ビル ドスクリプトから参照可能」な特殊なモジュールが存在する ■ ここにビルドスクリプトの生成処理や生成されたビルドスクリプトを用い たGradleビルドの発行処理を記述し、一定の処理単位毎にGradleのタスク 設定する ■ ビルドスクリプトの生成自体は単純にStringリテラルを用いたテンプレー ト型 ◆ 依存関係やMavenリポジトリのクレデンシャル等が生成時に埋め込まれる

Slide 11

Slide 11 text

10 生成されたプロジェクトの構造 ■ 1つの被生成コード用のプロジェクトを作成し、各モジュール をルートプロジェクトに追加 ■ 各CDKモジュール毎に「コード生成担当」のモジュールと「被 生成コード担当」のモジュールを作成 ■ コード生成器は事前に”dsl-generator”という名前でmavenLocal に公開しておき、コード生成担当のモジュールが参照して利用 ■ 被生成コード担当は依存関係等のMaven Artifactに関する設定を 主に行い、ビルド&アーティファクト生成&アップロードまで担 当 ■ 公開用のモジュールにdsl-generatorへの依存をさせたくなかっ た 図: プロジェクト構造

Slide 12

Slide 12 text

11 パッケージ/モジュール管理 ■ 生成を自動で行うにはまずAWS CDKのモジュール一覧を取得し、 コード生成を行うモジュールとバージョンの決定が必要 ■ buildSrc内でMaven Centralの検索とスクレイピングを行う ■ AWS CDKの更新検知のためには過去に生成を行った最新バージ ョンを知る必要があるため、AWS CDK Kotlin DSLのリポジトリ の各モジュールのmaven-metadata.xmlもスクレイピング ■ Gradleタスク毎にこの2つの情報を組み合わせて生成対象の決定 ◆ タスク例: 指定したモジュールの最新版, 指定したバージョンの全モ ジュール, 過去の生成と最新のCDKとの差分 ■ スクレイピングだけで数分かかる 参考: GH上のパッケージ一覧 1ページ=最大30パッケージ

Slide 13

Slide 13 text

12 全体の処理フロー 1. Maven CentralをスクレイピングしてAWS CDKの存在するモ ジュールの情報を取得 2. (必要であれば) AWS CDK Kotlin DSLの過去に公開したモジュ ールをスクレイピング 3. タスクに応じて生成対象となるモジュール/バージョンの決定 4. ビルドスクリプトの生成 5. -genモジュールで生成処理 6. 被生成モジュールのビルド 7. (必要であれば)Mavenリポジトリへのアップロード&公開

Slide 14

Slide 14 text

13 余談 ■ ビルドが滅茶苦茶重い & 時間がかかる ◆ スクレイピングは数が多いので地味に時間がかかる。多すぎてロー カルでバッグしてるとたまにリクエストが弾かれる。 ◆ 並列ビルドを有効にすると100を超えるモジュールの並列ビルドが 走るのでCPU資源を食い尽くす。I7-8750H(6C12T)でも数分かかる。 ◆ CIだと性能が低くて並列ビルドすると余計に時間がかかる/下手する と落ちるので並列ビルドをオフにする。すると10分以上かかる。 ■ ファイルが大きい & 数が膨大 ◆ 生成対象のモジュールの大きさにファイルサイズが比例する。1万 行超えも存在。 ◆ コード生成 & ビルドを行ったフォルダはサイズが5GB以上

Slide 15

Slide 15 text

14 余談 ■ buildSrc部分のデバッグが滅茶苦茶やりにくい ◆ デバッガーが繋げないのでprintlnデバッグ ◆ デバッグ用のGradleタスクを作って試さないといけない ■ => 教訓: buildSrcにロジックを詰めすぎない ■ 元々Bintrayを利用していたが突然の閉鎖で爆発四散した ◆ 移行先の検討、対応、既存のパッケージの移行、etc… ◆ GH Packagesを採用したが同時アップロードするパッケージが多い せいか時々謎のConflictが出て途中でアップロードが止まったり ◆ GH Packages, 利便性でBintrayに劣っていてつらい

Slide 16

Slide 16 text

15 まとめ/感想 ■ コード生成を上手く使えば膨大なサイズのライブラリのラッパ ーを記述できたりしてQoLが上がる ■ コード生成自体はちゃんとロジックと生成したいコードが明確 ならそれほど黒魔術ではない ■ コード生成よりもビルドが大変 ■ Gradleは何もかも自由 ■ ビルド芸、やりすぎると黒魔術 ■ キャッシュの活用など、Gradleビルドの高速化にも取り組んで みたい

Slide 17

Slide 17 text

16 最後に ■ justInCaseではバックエンドエンジニアを絶賛大募集中です ■ サーバーサイドKotlin, フレークワークにSpringを使っています ■ サーバーサイドKotlinの経験はないけれどSlerでJavaでの経験を お持ちの方もOK ■ webフロントエンドもこれまた募集しています (TypeScript, React) ■ 採用ページはこちら