Upgrade to Pro — share decks privately, control downloads, hide ads and more …

作るべきものと向き合う - ecspresso 8年間の開発史から学ぶ技術選定 / 技術選定c...

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

作るべきものと向き合う - ecspresso 8年間の開発史から学ぶ技術選定 / 技術選定con findy 2026

技術選定を突き詰める Online Conferenc​​e ――逆境を乗り越える意思決定プロセス https://findy.connpass.com/event/380974/

Avatar for FUJIWARA Shunichiro

FUJIWARA Shunichiro

February 26, 2026
Tweet

More Decks by FUJIWARA Shunichiro

Other Decks in Technology

Transcript

  1. 自己紹介 @fujiwara (X, GitHub, Bluesky) @sfujiwara (hatena, mixi2) 2011〜2024 面白法人カヤック

    2025-02〜 さくらインターネット ISUCON 優勝4回 / 運営(出題)4回 github.com/kayac/ecspresso github.com/fujiwara/lambroll github.com/fujiwara/apprun-cli
  2. ecspresso の特徴と設計思想 Amazon ECSの管理と操作を行う デプロイ(IaC)ツール deploy rollback status verify diff

    scale ... ECS「タスク定義」 「サービス」を AWS SDK / AWS-CLI と同じ構造の JSONで管理 それ以外のリソース(VPC、LBなど)は あえて管理しない
  3. ecspressoの設計思想「操作範囲をECSに限定する」 EC2上での協業で開発とインフラ管理の責任が曖昧になり双方が苦労した経験から 「ECSとそれ以外の管理を分ける」提案 → 結果として生まれたのが ecspresso 責任範囲 システム構成 アプリケーション Consul,nginx,Fluentd...

    EC2 OS IAM, VPC, LB, RDS... 👥 カヤック 👥 MSP → 責任範囲 システム構成 ECS App,nginx,Fluentd... IAM, VPC, LB, RDS... 👥 カヤック 👥 MSP 思想として言語化したのは3年後のことなので実は後付け
  4. 「ecspressoの設計思想に至る道」とは 教訓:設計とは「どこで切るか」 適切な境界線は組織構成によって異なる 責任範囲と操作する範囲を一致させることが重要 変更頻度が少ないインフラ(VPC, LBなど)は Terraform / CloudFormation などで管理

    変更頻度が高いアプリケーション(ECSタスク定義、サービス)は ecspresso で管理 インフラ要素のIDは tfstate, CFn Outputs/Exports, SSM から参照 それぞれの管理はチームが分かれることも多い お互いの変更による影響を最小限に抑え、開発効率と安定性を両立
  5. ecspresso JSON + テンプレート記法 JSON は AWS SDK と同じ構造 テンプレート記法

    {{ }} を使って動 的な値を注入できる {{ must_env }} : 環境変数を参照 {{ tfstate }} : Terraformで作成し たリソースをtfstateファイルから参照 {{ ssm }} : SSMパラメータストア から参照
  6. AWS Copilot CLI の抽象化がもたらした制約 例: (ECSがサポートしている) 複数LBをサポートできない → DSLの構造が1アプリケーション ==

    1LBだったため 例: 対応していないリソース、対応していても高度な設定はDSLでは表現できない → CFnテンプレートを直接記述 / 自動生成されたCFnにYAML patch 世の中のすべてのアプリケーションを「簡単に」抽象化するのは不可能 簡単に始められる → 複雑な要件に対応できない、というトレードオフ ECSのアプリケーションが必要とするリソースを全て管理するのは無理 → 究極的には CFn/CDK/Terraform と同じものを別に作ることになる
  7. ecspresso が採用した抽象化レベル 操作は便利に抽象化 deploy タスク定義の登録とサービスの更新、待機を一括で 複数のAPIを実行 ECS Rolling, B/G, CodeDeploy

    どのデプロイ方式も同じコマンドで rollback ネイティブなロールバック機能が最近までなかったのを当初から提供 diff タスク定義、サービス定義の差分をわかりやすく表示 verify デプロイ前に妥当性(依存リソースが存在するか?など)をチェック
  8. ecspressoの設定ファイル文法 デプロイするたびに変わる要素(例: イメージタグ)を注入する必要がある 2017〜 JSON + text/template記法 { "family": "myapp",

    "containerDefinitions": [ { "name": "myapp", "image": "myapp:{{ must_env `IMAGE_TAG` }}", ... } ] } 「 『JSONに値を埋めるだけ』 — Go の text/template で十分だね」 github.com/kayac/go-config を作って ecspresso にも採用
  9. テンプレート記法とJSONの衝突 — 文字列の外では壊れる 例: desiredCount (希望するタスクの数) は数値 文字列以外を注入するには "" を外す必要がある

    { "desiredCount": {{ must_env `DESIRED_COUNT` }}, "networkConfiguration": { ... これはJSONとしてはinvalidな構造( {{ ) ecspresso はテンプレート展開 → JSONパースの順で処理するため 動くことは動くがエディタでの自動整形などが壊れる ただし「そこまで致命的ではない」問題 大抵のユースケースでは文字列内に展開できれば十分だったため
  10. 2020年 Jsonnet導入による設定ファイル運用の改善 ECSのタスク定義はそこそこ複雑な(大きな)JSON構造 生のJSONで書くのはちょっと辛い 末尾 , やクォートまわり コンテナや環境変数などの定義を共通化したい JSONではincludeや共通化が表現できない 「Jsonnet

    を外部コマンドとして使えば良いのでは?」 1. Jsonnetで設定ファイルを記述しておく 2. jsonnet コマンドでJSONファイルを作成 3. ecspressoはJSONを読み込む 2020年ごろからこの運用を始めた(自分もecspressoユーザーなので)
  11. Jsonnet + テンプレート記法 local envs = import 'envs.libsonnet'; // 共通の値の読み込み

    { containerDefinitions: [{ image: "myapp:{{ must_env `IMAGE_TAG` }}", // テンプレートはそのまま使える environment: envs, // 読み込んだ値をここに展開 ↓ jsonnet ecs-task-def.jsonnet > ecs-task-def.json { "containerDefinitions": [{ "image": "myapp:{{ must_env `IMAGE_TAG` }}", "environment": [ {"name": "ENV1", "value": "value1"}, {"name": "ENV2", "value": "value2"}, Jsonnetで共通化や記述の簡略化ができるようになった ただしテンプレート記法はそのまま (Jsonnetには環境変数やtfstateの読み込み機能はない)
  12. 2021年 go-jsonnetをecspressoに組み込み (v1.7) google/go-jsonnet をecspressoに組み込んで外部コマンド不要に *.jsonnet はecspresso自身がJsonnetとして読み込んでJSONに変換 → テンプレート展開 →

    JSONパース という処理順 外部コマンドが不要になって便利! しかし 「JSONに文字列以外の値を注入する」際の課題は解決していない
  13. 解決しないどころかJsonnet化で制約が増えた // これはJSONとしてもJsonnetとしてもinvalid { desiredCount: {{ must_env `DESIRED_COUNT` }} }

    最初に Jsonnet として処理されるため、そもそもパースできない=使えない (JSONならテンプレート展開後にパースするので一応動いていたのに) 「文字列として埋めた後に std.parseInt で数値に変換すれば?」 { desiredCount: std.parseInt('{{ must_env `DESIRED_COUNT` }}') } テンプレート展開はJsonnetの「後」なので、最初に std.parseInt でエラー
  14. 処理順の比較 — なぜ制約が増えたのか v0 (JSON + text/template) .json → template展開

    → JSONパース {{ }} がinvalidでも展開後はvalid → 一応動く(エディタは壊れる) { "desiredCount": {{ must_env `DESIRED_COUNT` }} } ↓ テンプレート処理 { "desiredCount": 3 } v1.7 (Jsonnet組み込み) .jsonnet → Jsonnet評価 → template展開 → JSONパース {{ }} がJsonnet構文を壊す → パースできない Jsonnet評価が先に来るため、テンプレート記法の制約が悪化した しかし処理順は互換性のため変えられない
  15. 2024年 — Jsonnet native functionによる完全解決 (v2.4) go-jsonnetのnative function機能を使い、 テンプレート関数(=Goの関数)をすべてJsonnetの関数としても提供 local

    must_env = std.native('must_env'); // Goの関数をJsonnetから呼び出せる { // must_envは環境変数を文字列で返す // std.parseIntは文字列を数値に変換する desiredCount: std.parseInt( must_env('DESIRED_COUNT') ), } Jsonnetの文法で完結するのでテンプレート記法が衝突しない 文字列外への値展開も自然にできるようになった 2017年(v0)→2024年(v2.4) 制約を完全に解消するまでに気がつけば7年
  16. そもそもなぜこの制約が生まれたのか 「 『JSONに値を埋めるだけ』 — Go の text/template で十分だね」←←← 設定ファイルを構築する、JSONに値を埋めるという課題を軽く見ていた はっきりいえば「舐めていた」

    世の中には設定記述言語(Jsonnet, Cue, HCL…)が多数存在している 餅は餅屋、JSONに値を埋めるのはそこまで単純な問題ではない テンプレートで自作という選択が小さな制約を生み、完全解決までに7年かかった
  17. 選択はトレードオフ 不完全な「JSON + text/template」と完全な「Jsonnet + native function」 しかし最初の選択も、そこまで悪いものでもなかった(と思う) ほとんどのケースではうまく動く 実際今でも

    JSON で十分便利に使っている利用者も多い 「JSON + テンプレート記法」は馴染みやすい構文だった リリース当時の ecspresso はとてもマイナーなツール マイナーなツールに学習コストを割いてくれるユーザーは少ない go-jsonnetは2016年〜(初めてタグが打たれたv0.10.0は2018年10月) 2017年のリリース当初はそもそも自分は知らなかった…
  18. batcha github.com/kyosu-1/batcha ecspressoと同じ構造でAWS Batchの ジョブ定義を管理するツール テンプレートエンジンに kayac/go- config、Terraform state の参照に

    fujiwara/tfstate-lookup を利用しており、 ecspresso と同じエコシステムの上に構築 しています。 ecspresso と同様に、変更頻度の高い Job Definition の管理に特化し、ジョブキュー やコンピュート環境、ネットワークとい った頻繁には変わらないリソースは Terraform などの IaC ツールに任せる設計 です。
  19. ecspresso (ECS) batcha (AWS Batch) タスク定義を JSON テンプレートで管理 Job Definition

    を JSON テンプレートで管理 ecspresso register でタスク定義登録 batcha register で Job Definition 登録 ecspresso diff で差分検出 batcha diff で差分検出 ecspresso verify でローカル検証 batcha verify でローカル検証 ecspresso init で既存リソースから生成 batcha init で既存定義から生成 ecspresso run でタスク実行 batcha run でジョブ実行 tfstate プラグインで Terraform 連携 同様に tfstate プラグインで連携 https://kyosu.dev/#/blog/introducing-batcha より引用
  20. batchaはClaude Codeが(ほぼ)実装した batcha のコードはほぼ Claude Code が書いたもので、自分は設計の方向付けと調 整に徹しました。ecspresso という優れた手本があったのも大きいですが、こう いうツールがサクッと作れてしまう、いい時代になりましたね。

    https://kyosu.dev/#/blog/introducing-batcha より引用 fujiwara自身もこのパターンでいくつもOSSを作っている AWS Lambda用の fujiwara/lambroll さくらのAppRun用の fujiwara/apprun-cli この設計パターンが有用な事は実証されている(と思う) 「良い設計パターンを踏襲すれば同様のツールはAIに作らせることができる」時代