0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ClickHouse + Cloudflare Workers + Hono と Docker + nfcpy+ PaSoRi を利用して、合宿参加者の状態を把握できるシステムを作ってみた

0
Last updated at Posted at 2026-07-13

はじめに

kazu_iroiroと申します!

1年以上ぶりにQiitaへ戻ってきました。

大学生になって、これまで触れてこなかった言語を学んだり、趣味だった電子工作にお金をつぎ込めるようになったりと、日々を満喫しています。

今年の4月からは2年生になり、そろそろアウトプットをしたいなと感じていたので、記事投稿キャンペーンを機に、アウトプットの回数を増やしていこうと思います。

この記事では、バックエンドにClickHouse + Cloudflare Workers + Honoを、フロントエンドにDocker + nfcpy + PaSoRiを利用した、合宿参加者の状況把握システムについて紹介します!

この記事は Qiita Tech Festa Sprint 記事投稿キャンペーン 『「AI時代のデータベース、何が変わる?」について語ろう!』の投稿記事です

経緯

私は大学でデジタル創作系の団体に所属しています。

この団体で今年度の合宿担当をしているのですが、この夏の合宿参加者数が80人規模となる見込みで、大変不安に思っています。

この人数を目視でカウントすることは非現実的なので、今部員がどこにいるのか、状況をリアルタイムで確認できたらいいよねということになり、開発を始めました。

リアルタイム把握系のプロダクトは、プライバシーと利便性の間でバランスを取るのが難しかったり、誰かが操作を忘れたりして、破綻しがちです

できるだけノンストレス、かつプライバシーに配慮した形として、所属している全部員が持っている「学生証」に目をつけました。

「改札」を宿泊先の各所に設け、部員が通行する際に「学生証」で認証してもらえば、リアルタイムでの位置確認を実現できそうです。

仕様の検討

学生証について

弊学の学生証はFeliCaをベースとしており、これをPaSoRiにかざすことで学籍番号とIDmを取得できます。

FeliCaなら読み取り速度も速く、改札として運用するのに最適です。

また、部員のみが使う前提のため、IDm(各FeliCaカードに固有のID)を利用したシステムでもセキュリティ上の心配はありません。

利便性向上のため、部員が持っている任意のFeliCaを学生証と紐づける仕組みを導入し、任意のFeliCaをかざしても反応させることとしました。
(自分がモバイルSuicaで通りたかっただけ)

ネットワーク環境について

合宿先では安定したインターネット環境が手に入りません。

毎年、複数の通信回線を束ねた冗長化回線を構築していますが、どうしてもレイテンシが大きくなってしまったり、接続が途切れてしまうことがあります。

そんな時でもログを保持できるような仕組みが必要です。

パソコンの台数について

デジタル創作サークルのため、もちろん部員から募ることでパソコンは沢山手に入ります。

しかし改札用に「放置してよいパソコン」というのは少なく、できる限りクライアントになる端末数は削減したいです。

したがって、2台のPaSoRiを1台のパソコンで管理することを要件としました。
これにより、「改札」を出るとき、入るときで別々のPaSoRiに学生証をタッチすることができるので、通過時の利便性を向上できます。

技術検討と失敗

最終的な構成に至るまでに、いくつかの試作を行ったので紹介します。

クライアント側

  • Go (github.com/ebfe/scard)
    ライブラリの仕様上モバイルSuicaの読み取りができなかった
  • Python (nfcpy)
    Windowsだとドライバーの入れ替えが必要だった
  • .NET (felicalib)
    ライブラリの仕様上、複数台のPaSoRiを同時に扱えなかった

サーバー側

  • GAS + Googleスプレッドシート
    認証APIとして動かすにはレスポンスが遅く、スプレッドシート側の情報表示も遅い
  • Cloudflare Workers (Hono)
    認証APIとしては優秀だが、Workersに重たいDB処理をさせるわけにいかない(CPUの実行時間に制限がある)ので、単体では使いにくい

最終的に

どうしようか悩んでいたところで、ちょうどQiitaの記事投稿キャンペーンからClickHouseを知り、Cloudflare Workersと組み合わせることにしました。

Windowsでnfcpyを使う際にドライバの入れ替えが必要な問題については、usbipd-winを通してWSL上のDockerコンテナにPaSoRiをアタッチしてあげることで解決できることが分かりました。

ClickHouseって?

列でデータを管理する、オープンソースのデータベースです。
PostgreSQLなどは、行単位でデータを管理するので、データの持ち方が異なります。

列ごとにデータを処理できるため、分析に向いているらしく、今回のような蓄積したログを管理するのにも向いていそうです。
逆に、Cloudflare D1のような、Key-Value型のDBが得意とする「認証」には向いてないわけですね。

だからこそ、Cloudflare Workers と ClickHouseの組み合わせがちょうど良さそうです。

実際に作ってみた

構成

以下のような構成で作ってみました。

Slide 16_9 - 1.png

【Reader: 検知部】
 Windows + usbipd-win + WSL2 + Docker + nfcpy + PaSoRi RC-S380

【認証】
 Cloudflare Workers + Hono + Cloudflare D1

【DB】
 ClickHouse Cloud

【Viewer: Webダッシュボード】
 Cloudflare Workers + Hono + Cloudflare Pages

の組み合わせです。

他の記事で楽だと言われていたので、ClickHouseはセルフホストではなく、Cloud版の無料トライアルで試してみました。

ClickHouse Cloudの登録から認証情報の取得まで

登録手順
  1. トップページにアクセス
    image.png

  2. アカウント登録
    image.png

  3. メール認証をして
    image.png

  4. ログイン
    image.png

  5. ダッシュボード画面が開けました
    image.png

  6. コストが安そうなのでGCPを選択
    image.png

  7. 作成したサービスのダッシュボードを確認できました
    image.png

  8. DBの構造を指定してあげて
    image.png

  9. 認証情報をコピーして取得します
    image.png

この認証情報を、Cloudflare Workersに登録してあげれば、準備完了です。

利用規約がシンプルで読みやすく、簡単に登録できて便利でした。

ランニングコストの観点から、セルフホスト版のClickHouseも試してみたいです。
クラウド系のサービスを展開しつつも、オープンソースかつ自前の環境ですぐ動かせる、というのは珍しく、大変ありがたいです。

動いた!!

パソコンに2台のPaSoRiを繋いだ状態で、実際にWebダッシュボードがリアルタイムに更新されることを確認できました。

感想

GAS + Googleスプレッドシートでバックエンドを構成していた時よりも、圧倒的に速くなって感動しました。

検討していた仕様も満たせたので、大変満足です。

ClickHouse側の更新がとても速いため、Webダッシュボードのデータ取得間隔が、表示の更新頻度に大きく影響を与えるようになってしまいました。

今後の展望

実運用を始めると、ClickHouseに蓄積されるデータ量も膨大になると思うので、これからレスポンス速度がどうなるのかが楽しみです。

合宿まではもう少し時間があるので、Cloudflare Workersの改修をして、少しでもレイテンシが短くなるようにしようと思います。
ダッシュボードのUI・UX向上もしていきたいです。

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

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?