for Startups Tech Blog

フォースタ社員のエンジニアたちが思い思いのことを書き綴ります。

Reusable WorkflowsとComposite Actionsを使い分けてGitHub Actionsの記述を削減する

目次

はじめに

こんにちは。SREの高場です。

テクノロジーグループで、主に社内プロダクトのCI/CDの整備やインフラの安定化を担当しています。

今回は、GitHub Actionsの2種類の機能を利用してワークフローファイルの記述量を削減した取り組みをご紹介します。

きっかけ

テクノロジーグループの社内向けプロダクトでは、CI/CDにGitHub Actionsを利用しています。

またビルドパイプラインにはGitHub Actionsを使っており、AWS環境のECSにデプロイしています。

最近、開発者用のステージング環境を新たに増やしました。

それに合わせてビルドパイプラインを増やす際に、既存のワークフローを参考に作成しました。

しかしパイプラインを記述したymlファイルの内容は、一部サービス名等が異なるだけでほとんど同じ内容でした。

そこで、GitHub Actionsの2つの仕組みを使って記述の重複を可能な限り排除してみました。

GitHub Actionsの2つの仕組み

GitHub Actionsには以下の2つの異なる仕組みがあります。

  • Reusable Workflows
  • Composite Actions

それぞれについて簡単に説明します。

Reusable Workflows

docs.github.com

参考:Reuse Workflows

Reusable Workflowsは、ワークフロー全体を再利用することを可能にします。

  • ユースケース: 複数のリポジトリや異なる環境で共通のワークフローロジックを再利用したい場合に最適です。
  • 特徴:
    • 他のリポジトリから参照可能: 組織内で共通のワークフローを定義し、それを複数のリポジトリから利用できます。
    • トリガー設定: 再利用されるワークフロー自体にトリガーを設定することができます。
    • 複数階層のネスト: ワークフロー内でさらに別のワークフローを呼び出す、といった複数階層の構造を構築できます。

利用例

社内プロダクトの開発では、ECSで複数のステージング環境を利用しています。

それぞれbacon,avocadoという名前をつけていて、ECSサービス名やECRイメージ名に使っています。

ステージング環境でのビルド・デプロイを行うために、それぞれの環境用にファイルを用意しています。

今回、共通した部分をReusable Workflowsに切り出し、ビルド・デプロイ部分を共通のファイルに集約しました。

結果として、stg環境名 (bacon, avocado)のようなパラメータを与えるだけで、複数環境へのビルド・デプロイが可能になりました。

ファイル構成の変更

変更前はdeploy_stg_avocado.ymldeploy_stg_bacon.ymlが独立して存在していましたが、Reusable Workflowsを導入することで、これらはdeploy_stg.ymlという共通のワークフローを参照する形になります。

記述量の削減: この変更により、全体の記述量が80行、約27%削減されました。

  • 変更前 (合計300行):
    • deploy_stg_bacon.yml: 150行
    • deploy_stg_avocado.yml: 150行
  • 変更後 (合計220行):
    • deploy_stg_bacon.yml: 25行
    • deploy_stg_avocado.yml: 25行
    • deploy_stg.yml: 170行

将来的にproduction環境を実装する際にも、環境ごとの分岐を追加して、この共通ワークフローを使い回すことを想定しています。

実際のファイル

以下は、Reusable Workflowsを利用してdeploy_stg.ymlを呼び出すGitHub Actionsのジョブの記述例です。実際のファイルを元にしています。

jobs:
  deploy-staging:
    uses: ./.github/workflows/deploy_stg.yml
    with:
      environment_name: 'bacon'
      ecr_repository: 'product-name-ecr'
      ecs_service: 'product-name-service'
      app_env: 'staging01'
      nginx_hostname: 'bacon.stg.example.com'
      need_ecs_service_start: false
    secrets:
      NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
      SF_CRT_KEY_STG: ${{ secrets.SF_CRT_KEY_STG }}

Composite Actions

docs.github.com

参考:Composite Actions

Composite Actionsは、複数のステップを一つのアクションとしてまとめることができる機能です。

  • ユースケース: 同じジョブ内で繰り返し実行される一連のステップを共通化したい場合に特に有用です。
  • 特徴:
    • 同一ジョブ内で実行: Composite Actionsは、呼び出し元のジョブと同じランナー(OS)内で実行されます。
    • secretsの受け渡し不可: secretsをComposite Actionに渡すことはできません(❌) 。ただし、withを使って明示的に渡すことは可能です。
    • 単独でのトリガー実行不可: Composite Actions単体でトリガーすることはできません (❌)。

ジョブとステップの違い

Composite Actionsの利点を説明するために、GitHub Actionsにおける「ジョブ」と「ステップ」の違いを明確にしておきます。

  • ジョブ (job): 同一ランナー(OS)内で行われる処理のまとまりの単位です。
  • ステップ (step): ジョブ内で実行される個別の処理です。

Composite Actionsの利点

ジョブを分割すると、各ジョブでcheckoutnpm installなどの共通の前処理をその都度行う必要があり、記述量が増えてしまいます。

この前処理はランナーを分けるごとに必須となる処理であるため、別ランナーで実行されるReusable Workflowsでは解決できない問題です。

Composite Actionsは、このような「異なるジョブに何度も現れる記述の重複」を解決します。

利用例

各種Linterやビルドチェックのために、ジョブを分割しています。

それぞれのテストは独立しており、並列で実行することが可能です。

上述したとおり、並列化したジョブのすべてにNode.jsのセットアップを実行する必要がありました。

Composite Actionsを使用することで、これらの同一の処理の記述をまとめることが可能になります。

例えば、.github/actions/setup-node/action.ymlのようなファイルでNode.jsのセットアップ処理を定義し、CIで実行されるeslintstylelintBiometestdocker buildなどのチェックにおいて、このsetup-nodeアクションをuses:で指定して利用します。

実際のファイル

Node.jsのセットアップとnpmrc認証を行うComposite Actionsの記述例です。実際のファイルを元にしています。

---
  name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version-file: ./node-version
    cache: 'yarn'
    registry-url: https://npm.pkg.github.com/
    scope: '@myOrganization'
- name: Setup .npmrc authentication
  if: inputs.setup-npmrc == 'true'
  shell: bash
  run: |
    sed -i "s/\${NODE_AUTH_TOKEN}/${{ inputs.node-auth-token }}/" ~/work/_temp/.npmrc
    cp ~/work/_temp/.npmrc ./.npmrc

注意点としては、記述した処理は同じジョブ内で実行されるので、実行時間は変わらないという点です。

あくまで効果としては記述上の短縮のみになります。

CIでの呼び出し例:

eslintジョブ内で上記Composite Actionを呼び出す例です。secretsは直接渡せないため、Nodeのトークンはwithで引き渡す必要があることに注意してください。

eslint:
  name: runner / eslint
  runs-on: ubuntu-latest
  steps:
    - name: Checkout
      uses: actions/checkout@v4
    - name: Setup Node.js and Dependencies
      uses: ./.github/actions/setup-node
      with:
        node-auth-token: ${{ secrets.NODE_AUTH_TOKEN }}
    - name: Run eslint
      uses: reviewdog/action-eslint@v1
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        eslint_flags: './src/**/*.js,{jsx,ts,tsx}'
        reporter: 'github-pr-review'
        filter_mode: 'diff_context'
        fail_level: 'error'

記述量の削減: この変更により、CI関連の記述量も削減されました。

削減量は69行で、約23%です。

  • 変更前 (合計300行):
    • ci.yml: 242行
    • run-knip.yml: 58行
  • 変更後 (合計231行):
    • ci.yml: 139行
    • run-knip.yml: 41行
    • actions.yml: 51行

Reusable WorkflowsとComposite Actionsの比較

個人的な感覚としては、Reusable Workflowsはワークフローの中からワークフローを呼び出すものですが、

Composite Actionsは純粋に処理をまとめたコンポーネントを呼び出すような印象を受けました。

複雑な分岐や共通化をしたいときはReusable Workflows、重複した記述をまとめたいときはComposite Actionsのような使い分けをすると良さそうです。

項目 Reusable Workflows Composite Actions
リポジトリからの参照 リポジトリを跨げる 同一リポジトリ
Secrets 渡せる 渡せない(withを使えば可能)
if文 使える 使えない
トリガー 使える 使えない

まとめ

全体の記述量としては、約25%の削減になりました。

今後ビルド手順に変更箇所があっても、複数のファイルを変更せずに済むため保守性も向上しています。

しかし、あまり使いすぎると不必要な複雑さを呼び込んだり、メンバーの認知負荷が上がるリスクはあります。

闇雲に使うのではなく、適切に使う場面を選択していきたいです。

ここまでお読みいただき、ありがとうございました。

TSKaigi2025スタッフ参加レポート

サムネイル

こんにちは!フォースタートアップス株式会社のエンジニア、山崎です。

今回は、2025年5月23日・24日に開催された TSKaigi2025 に、スタッフとして初参加した体験をレポートします。
普段は参加者として技術イベントに参加していた私ですが、今回は「運営側」の視点からイベントに関わることで、これまでにない学びと刺激を得ることができました。

「技術イベントを支える側に回ってみたい」
「スタッフって何するの?」
「本業との両立はできるの?」

そんな疑問を持っている方にとって、この記事が少しでも参考になれば嬉しいです。

なお、セッション内容の詳細については他の参加者の素晴らしい記事や、公式YouTubeチャンネルにアーカイブが公開されているので、ぜひチェックしてみてください!

📺 TSKaigi公式YouTubeチャンネル


目次


TSKaigi2025とは?

TSKaigiは、TypeScriptに特化した日本最大級の技術カンファレンスです。
2025年の今回は、東京・ベルサール神田にて2日間・3トラックで開催され、約2,500名以上が参加する大規模なイベントとなりました。

セッションはもちろん、スポンサー企業のブース展示や豪華な懇親会、限定ノベルティなど、TypeScriptに関心のあるエンジニアにとってはまさにお祭りのような場。
昨年は1日の開催だったとのことですが、今年はボリュームも熱量も倍増し、全体を通して熱気に満ちた2日間でした。

集合写真


スタッフとしてどんなことをしていたか?

TSKaigiでは、およそ70名ほどのスタッフが各チームに分かれて活動していました。
主なチーム構成は以下の通りです:

  • コア
  • 会場・チケット
  • プログラム
  • 参加者体験企画
  • PR
  • Webサイト
  • 配信
  • クリエイティブ
  • スポンサー

私はこの中の「会場・チケット」チームに所属し、快適な会場運営を担うミッションのもと活動していました。

私の担当業務:ランチ手配

なかでも私はランチ手配の主担当として動きました。
担当した主なタスクは以下の通りです:

  • 業者選定
  • 弁当の種類・数量の決定
  • ハラールヴィーガン対応弁当の検討
  • スタッフへの事前アンケート実施
  • 注文・配布・片付けのタイムテーブル設計

大規模イベントでは弁当の注文数が多く、種類ごとの配分をどう決めるかは悩ましいポイントでした。 実際、私も注文数の決定には頭を悩ませました。 そこで今回は、事前にスタッフ向けのアンケートを実施し、希望する弁当の種類を確認した上で発注数を調整しました。 結果として、人気の偏りによる偏在や、大量のロスといった事態を避けることができ、とてもスムーズに配布できました。 今後ランチ手配を担当される方には、事前アンケートの活用をおすすめします!

また、会場チームではイベント当日1ヶ月前から週1ペースで夜にオンラインMTGを実施していました。
それ以前は2週に1回ほどで、本業に支障が出ないよう、「無理のない範囲での参加」をスタッフ間で共通認識としていました。
そのおかげで安心して取り組めたのも、非常にありがたかったです。


印象に残ったセッション

当日はスタッフ業務が中心だったため、セッションを腰を据えて聴く時間は多く取れませんでしたが、特に印象に残っているのが、2日目の主催者講演「TypeScriptネイティブ移植観察レポート TSKaigi 2025」です。

この講演では、イベント前日の深夜(5/23)にMicrosoftから発表された tsgo のプレビュー版に早速触れ、その体験を翌日の登壇で即座に共有されていたのがとても印象的でした。

発表された方は、TSKaigiの運営スタッフでもあるとのことで、直前まで準備で多忙だったはず。
それにも関わらず、新技術への好奇心とアウトプット力に圧倒されました
まさに「技術者としての熱量」を体現している姿で、私自身も強く刺激を受けました。

tsgo に関しては名前だけ知っている程度だった私ですが、この講演を通じて一気に理解が深まりました。
興味のある方は、ぜひ講演資料をチェックしてみてください!

参考資料:登壇スライド(Speaker Deck)


まとめ 〜運営の裏側に触れるという体験〜

TSKaigi2025への初めてのスタッフ参加は、エンジニアとしても人としても、大きな学びと成長が得られる貴重な体験でした。

初めてのことで戸惑う場面も多かったものの、周囲のスタッフの皆さんが本当に親切で、 未経験者でも安心して参加できる環境が整っていました。

ちなみに、私はconnpassでスタッフ募集の告知を見つけて応募しましたが、知人の紹介で参加されている方も多く、参加のハードルは思っているよりも低いと感じました。

懇親会では「来年も開催予定」とのアナウンスがあり、今から楽しみにしています。
TSKaigiのような素晴らしいイベントを作り上げてくださった運営の皆さま、参加者の皆さま、本当にありがとうございました!

そして、この記事を読んで「来年は自分も運営側で参加してみようかな」と思っていただけたら、とても嬉しいです!

ちなみに、私が所属するフォースタートアップス株式会社では、TypeScriptを用いたフロントエンド開発にも積極的に取り組んでいます。
TSKaigiに関心のある方や、フロントエンド開発に興味のある方がいらっしゃれば、お気軽にご連絡ください!

開発者体験が良くなると何がいいのか?1年半検証してみた(完結編)

はじめに

こんにちは、エンジニアリングマネージャーの八巻(@hachimaki37)です。今年の3月にファインディさん主催のイベントにて、DevExについて登壇しました。

note.com

登壇内容は、「DevExを推進する前に上手くいかなかったこと」や「具体的な取り組み」についてです。

speakerdeck.com

本記事は、約1年半ほどDevExに向き合ってきたチームの「変化」と「その成果」について書いています。DevExが変化することには、どのような意味があるのでしょうか。そして、どのような効果があったのでしょうか。検証してみた結果を深掘りします。

注記:本記事は、以下3つの完結編です。合わせてお読み頂けると本記事の解像度が高くなるかと存じます。

目次

チーム変遷

入社当初は6名、そこから4名、3名と変遷をたどり、現在は8名のチームです。

取り組みの振り返り

本記事に至るまでの取り組みを簡単に振り返ります。

DevEx取り組みのきっかけと背景

  • 開発者の開発者体験を良くしたいと思っていた際に、DevEx: What Actually Drives Productivity に出会う。
  • 開発生産性向上を目的としたプロジェクト(DevExプロジェクト)を立ち上げ、フォースタートアップスの開発組織(以下、開発組織と呼ぶ)を横断して推進する。

DevExプロジェクトでの具体的な取り組み

  • DevEx in Action を基に、開発者体験に関するアンケート調査(以下、サーベイと呼ぶ)を設計する。
  • 開発組織でサーベイを実施する。
  • サーベイの結果を分析し、グループごとに課題の定義、改善を実施する。
  • 今日までに計3回のサーベイを実施する。
    • 一回目:2024年06月
    • 二回目:2024年09月
    • 三回目:2025年04月

サーベイ結果の変化

開発組織

一回目のサーベイ結果から考えると、各種指標は数値改善が徐々に見られました。今日までに行ってきたグループごとの取り組みが、しっかり開発者体験の改善に貢献してきたと考えています。

所属グループ

三回目のサーベイ結果では、各種指標は4ポイント以上(ややあてはまる)の結果となりました。私が所属するグループ(以下、チームと呼ぶ)は、新しくジョインされたメンバーが多い中でしたが、ポジティブな結果となりました。また、各種指標も一回目のサーベイ結果から改善傾向にあり、課題への改善の取り組みが、開発者体験を向上する要因になったと考えています。特にフロー状態は、大幅な改善が見られました。

強みが続いた指標
課題とした指標と設問
  • フロー状態
    • 設問:途切れることなく日々継続的に開発に集中できる
  • 認知負荷:
    • 設問:プロジェクトのソースコードを理解するためのドキュメントは十分である
    • 設問:プロジェクトのソースコードは、明確且つシンプルで理解しやすい
結果比較

一回目と三回目のサーベイ結果を比較しました。どの設問がどのくらい改善・改悪したのかです(取り組みは一例です)。

3ポイント未満から改善された設問

  • 途切れることなく日々継続的に開発に集中できる:2.7 → 4.2(+1.6)

    • 取り組み
      • カレンダー調整(MTGの時間を意図的にまとめる)
      • オンボーディングのプロセス効率化
  • 当初予期していなかった予定外のタスクや要求はほとんど受けない:2.8 → 3.7(+0.9)

    • 取り組み
      • スプリントゴールのissueに集中する(期待値の調整を行い、やらないことを決める)
      • issueの粒度改善(PRが肥大化し過ぎないように、レビュアーにも優しい取り組みやすい粒度にする)
  • プロジェクトのソースコードは、明確且つシンプルで理解しやすい:2.8 → 3.5(+0.7)

    • 取り組み
      • モブレビューやリファクタリング、コードレビューの観点ナレッジ化など、品質向上を目的に実施
  • プロジェクトのソースコードを理解するためのドキュメントは十分である:2.2 → 3.2(+1.0)

    • 取り組み
      • 地道なドキュメントの整備や充実化

減少した設問

  • チームで協力して対処する必要があるインシデントの頻度は低い:3.8 → 3.5(-0.3)

  • 開発環境にはよく整備された自動テストがあり、結果を迅速に確認できる:3.8 → 3.7(-0.1)

  • テスト環境としてのCIは十分に整えられており、テスト結果を迅速に確認できる:3.8 → 3.7(-0.1)

  • プロジェクトのソースコードの品質は優れており、よくメンテナンスされている:3.4 → 3.2(-0.2)

  • チーム内およびチーム間でコードや作業を理解できるような取り組みが行われている:4.4 → 4.3(-0.1)

+1ポイント以上改善された設問

  • 途切れることなく日々継続的に開発に集中できる:2.7 → 4.2(+1.6)

  • 会議や中断がなく、まとまった時間を開発に充てることができる:3.0 → 4.2(+1.2)

  • 内部の技術的な質問(たとえば、コードやシステム、または作業している領域について)をすると、10分以内に回答を得ることができる:3.0 → 4.2(+1.2)

  • 変更したソースコードは、短いリードタイムで本番環境にリリースされる:3.0 → 4.2(+1.2)

  • プロジェクトやタスクの目標は明確で理解しやすい:3.0 → 4.0(+1.0)

  • プロジェクトのソースコードを理解するためのドキュメントは十分である:2.2 → 3.2(+1.0)

  • プロジェクトの開発環境の設定手順はよく整備されており、すぐに開発を開始できる:3.4 → 4.5(+1.1)

取り組みの成果

これら取り組みの結果、どのような成果をチームで生み出すことができたのでしょうか。以下 Findy Team+ を用いて、2024年度上期と2024年度下期(期間:2024年4月1日〜2025年3月31日)の対比を出してみました。あくまで参考値として紹介いたします。

「オープンからレビューまでの平均時間」と「レビューからアプルーブまでの平均時間」のスタッツの推移:減少傾向にある

プルリク作成数の推移:増加傾向にある

アウトプットの総量:+146%

※計算式:2024年度下期PR数 / 2024年度上期PR数 * 100

一人当たり生産性:+122%

※計算式:((2024年度下期PR数 / 平均稼動人数)/(2024年度上期PR数 / 平均稼動人数))* 100

期待付加価値の生産性:+138%

※計算式:(2024年度の大きなリリース / 2023年度の大きなリリース)* 100

※Findy Team+の集計ではなく、あくまで個人的に集計した数値

まとめ

以下、DevEx in Action から抜粋した研究結果です。

Outcomes

When considering the outcomes of development work or the developer experience, many researchers and people think about productivity.8,21 In our years of experience, however, we have seen that the improvements in developers' work go far beyond personal productivity for individual contributors,16 to include team and organizational outcomes.7,11 This investigation considers outcomes at the developer, team, and organizational levels, which is supported by WDT.23

Developer outcomes

Developer outcomes are those that benefit an individual developer. In particular, prior WDT research shows that improved work design positively influences job performance, creativity,22 and learning5—three outcomes investigated in this study.

Team outcomes

Team outcomes are those that can benefit an individual developer but more likely accrue at the team level of work and are therefore operationalized and studied at this level. WDT also shows that outcomes such as quality benefit teams.22 In the context of DevEx, we want to capture how work design can impact the quality of the system the team can work in, and therefore capture this as code quality and technical debt.

Organization outcomes

Organization outcomes benefit a worker's employer.improvements in developer work positively affect an organization's profitability and its ability to achieve goals.

今回、Organization outcomes(開発生産性レベル3)までは追うことができておりませんが、 取り組みの成果から考える開発者体験と開発生産性の相関が少し見えてきた気がします。

  • 開発者体験が向上することは、開発生産性の「変更のリードタイム(example:オープンからレビューまでの平均時間 → -6.1h、レビューからアプルーブまでの平均時間 → -2.2h)」の改善に繋がる。
  • さらに、変更のリードタイムが減少することは、チームとしての「デプロイ頻度(開発生産性レベル1) → 平均+0.8件」の改善に繋がる。
  • デプロイ頻度の増加により、「価値提供の量/頻度(開発生産性レベル2) → 結果+138%」が向上する可能性が高まる。
  • ただし、「変更障害率 → 平均+1.1%」と「平均修復時間 → +22.6h」は結果的に悪化したため、結果だけ見ると相関は見えてこなかった。

さいごに

約1年半ほどDevExに向き合ってきて感じることは、顧客への価値提供の「量や頻度」は高めることができたと考える一方で、システムとしての品質や可用性という根幹部分には課題が残る結果となったなと感じています。この1年半ほどで得た学びや経験を活かしながら、本来のあるべき姿やチームとしての成長を考え続け、最適解を探していきたいと思います。今後の取り組みもまた発信していきます。

DuckDB + GrafanaでELBのログをSQLライクに集計する

目次

はじめに

こんにちは。SREの高場です。

フォースタートアップス株式会社のプロダクトは、一部を除きAWSで稼働しています。

外部からのアクセスはELB(Elastic Load Balancing)で処理していますが、先日アクセスログを集計する必要が生じました。

ELBログの集計といえばAWS Athenaですが、他にも方法はあります。 aws.amazon.com

今回は、様々な形式のデータをスマートに表示できるDuckDB + Grafanaの組み合わせでELBログの集計をしてみたいと思います。

DuckDBとは

DuckDBは、データ分析に特化した列志向データベースで、SQL標準に準拠しています。

ローカルで軽量に動作するDBという点では比較対象としてSQLiteが挙げられますが、SQLite等のDBに比べてデータ分析等の用途に向いているという特徴があります。

個人的にはS3やローカルのファイルを直接読み込んでテーブル化できるところが使いやすいです。またGzipファイルも読み込み可能で、非常に高機能です。

duckdb.org

Grafanaとは

データの分析や可視化に特化したオープンソースダッシュボードツールです。

様々な形式のビジュアライゼーションに対応しており、データソースの豊富さとプラグインによる拡張性の高さが特徴です。

Grafanaを選んだ理由はDuckDBのプラグインが存在すること、AWSのサービスが存在することです。

AWSのマネージドサービスが存在することで、運用面での選択肢が増えるのは純粋に嬉しい点です。

grafana.com

aws.amazon.com

きっかけ

この記事を書こうと思ったきっかけは、弊社プロダクトのUserAgentの調査でした。

弊社プロダクトではECS Fargateを使用しており、ELBのアクセスログをS3に保存しています。

最近、1年分のアクセスログを調査しUserAgentごとに集計するという機会があったのですが、700万件のログを正確に調査する必要があり、頭を悩ませました。

当初CloudWatch Insightsで分析を行っていたのですが、コストの観点からローカルにログファイルをダウンロードすることにしました。

この時は自作ツールを作って対応したのですが、そもそもELBのログを分析する際にもっと楽な方法がないかと思ったことがきっかけです。

構成

今回は本格的に導入する前の検証段階のため、ローカル環境にDuckDB + Grafanaの環境を構築していきます。 構成は下記のシンプルなものです。

DuckDBを試してみる

今回の構成ではGrafanaのDuckDBプラグインを使うことになります。

そこでまずは本家のDuckDBを触ってみて、使い勝手を見てみます。

ローカル「duckdb」ディレクトリを作成し、brewでDuckdbをインストールします。

ログファイルを配置したディレクトリでコンソールからDuckdbに入ります。

read_csv

まず検証として、ALBログをダウンロードしてローカルで読み込ませてみます。

ログファイルを確認して、カラムを追加しました。

SELECT * FROM read_csv(
   '01/*.gz',
   columns={
       'timestamp': 'VARCHAR',
       'client_ip': 'VARCHAR',
       'client_port': 'VARCHAR',
       'user_id': 'VARCHAR',
       'request_time': 'VARCHAR',
       'path': 'VARCHAR',
       'status': 'INTEGER',
       'bytes_sent': 'BIGINT',
       'user_agent': 'VARCHAR',
   },
   union_by_name=true,
   delim=' ',
   escape='"',
   strict_mode=false,
   ignore_errors=true,
   null_padding=true,
   max_line_size=10000000
);

これはうまくいきませんでした。ELBのログはところどころで不規則なパターンが出現するため、空の項目であってもカラムを設定しないとうまく解釈できないようです。

read_csv_autoを使ってみる

自動でカラムを検出してくれるというread_csv_auto関数を使ってみました。

COPY(SELECT * FROM read_csv_auto(
      'csv_path/*.log.gz',
      union_by_name=true,
      null_padding=true,
      quote='"',
      strict_mode=false,
      ignore_errors=true, delim=' ',
      max_line_size=10000000, escape='"'
  )) TO 'output.csv' (FORMAT CSV, HEADER true);

こちらも、ある程度は自動で検出してくれますが、上記の方法でこれらをすべてカバーするのは難しそうです。

Athenaのパーティションスキームを参考にする

AWSの用意したアクセスログ形式のドキュメントがありました。

こちらを参考にして、きれいなテーブル形式にすることができました。

検証には、こちらの記事を大いに参考にさせていただきました。

road288.hatenablog.com

Grafanaプラグインで表示する

コンソールではクエリの編集や複数ページにまたがる結果の確認がしづらいため、GrafanaからGUIで編集できるようにします。

Grafanaは多様なビジュアライゼーションに対応していますが、今回は単純なテーブル形式で表示します。

Grafanaをローカルで動かしてみる

下記のようなファイルを用意します。

ポイントは下記です。

  • ローカルのログディレクトリをマウントする
  • GF_DEFAULT_APP_MODE=developmentを指定する
  • grafana.iniに下記を指定する
    • allow_loading_unsigned_plugins = motherduck-duckdb-datasource

これらを踏まえて、Dockerfileを作成します。

FROM grafana/grafana-enterprise:main-ubuntu

COPY grafana.ini /etc/grafana/grafana.ini

EXPOSE 3000
services:
 grafana_duckdb:
   build: ./
   container_name: grafana_duckdb
   user: root
   ports:
     - "3010:3000"
   volumes:
     - grafana_data:/var/lib/grafana
     - ./grafana.ini:/etc/grafana/grafana.ini
     - ./plugins/:/var/lib/grafana/plugins/
     - ./logs/:/var/lib/grafana/csv/
   environment:
     - GF_DEFAULT_APP_MODE=development

volumes:
 grafana_data:
  • 上記の構成にあたっては下記の記事を大いに参考にさせていただきました。

heraction.hatenablog.com

今回、大量のログを分析するため一度ローカルにダウンロードしてから検証する必要があります。

そのため、Grafanaのコンテナからローカルのログディレクトリを参照できるようにマウントします。

実際のクエリは下記のようになります。

CREATE TABLE alb_log_2025_01 AS SELECT * FROM read_csv_auto(
   '/var/lib/grafana/csv/01/*.gz',
…
);
Grafanaの初期セットアップ

GrafanaのGUIを立ち上げていきます。

初期ログインは下記のような画面で、固定のID/Passwordです。

DataSourceを追加します。

Grafanaから読み込めるDataSourceは多岐に渡りますが、下記のようなSaaSに対応しています。

Duckdb-Datasourceを選択します。

grafana.iniに設定を追加していれば、DataSource一覧にDuckdb-Datasourceが表示されるようになっています。

ダッシュボードを作成します。

テーブルを作成します。

クエリ編集画面で、DuckDBのクエリを入力します。

UA別にカウントする

作成したテーブルに対してSELECTします。

下記のような結果になりました。

検証のため一部のログのみを読み込ませていますが、しっかりUserAgentごとに分類できていることがわかります。

今後検討したいこと

今回はローカルでの検証でしたが、本格的に運用する場合には色々と検討すべき点があります。

例えばECSでGrafanaをホスティングするか、前述のAWSマネージドGrafanaを使うという選択肢があります。

またサーバレス構成にするなら、Lambda LayerでDuckDBを利用し、S3からログファイルを直接読み込んでGrafanaで表示させる構成も試してみたいです。

実際に運用に持っていくには、これらの構成とAthenaの場合でコストや運用面を比較することになりそうです。

これらの検討は今後の課題にしたいとおもいます。

まとめ

DuckDB + Grafanaで、ELBログをSQLライクに集計することができました。

一度ローカルに環境を作ってしまえば、ちょっとしたログの加工や集計が簡単にできるようになります。

既存の環境にETL基盤がなかったり、作成するまでもない場合には、このような方法で確認することでログ集計の手間を軽減できそうです。

ここまでお読みいただきありがとうございました!