TECH PLAY

サイオステクノロジー(Tech.Lab)

サイオステクノロジー(Tech.Lab) の技術ブログ

614

こんな方へ特におすすめ セキュリティ対応において、何から手をつければいいか迷っている方 HSTSなどのセキュリティヘッダ導入が、他の環境(検証環境など)に与える影響を知りたい方 概要 こんにちは。サイオステクノロジーのはらちゃんです! 今回はSL代表としてセキュリティスキャン対策のコミュニケーションチームに入りました。 そこで得た「現場でのリアルな立ち回り」「セキュリティ対応の基本となる考え方」「HSTS導入時の思わぬ落とし穴(副作用)」といった、実務に直結する知見をまとめていきます。 背景 ある日、内部的なセキュリティスキャンが実施され、レポートが提供されました。 結果は「外部から容易に利用されるサイトで、改善が必要な項目が多々ある」という厳しいものでした。 最終的に、各SLで担当者を立てて全社的に修正対応を図ることになりました。 対応前のマインドセット 実際の技術的な修正に入る前に、チーム全体で以下の「考え方のポイント」を共有しました。無駄な作業を減らすための非常に重要なステップです。 「廃止・退役できるリソースか?」を最初に問う これが最もコストがかからず、最も安全な究極のセキュリティ対策。 使っていない検証環境は、修正するのではなく消すのが正解。 「対応しない」なら客観的な根拠を システム上の都合でどうしても対応できない項目もある。 しかし、放置するのではなく「なぜ対応できないか(客観的に妥当な根拠)」を言語化し、ディスカッションのテーブルに上げることが求められる。 オープンなコミュニケーション 専用のSlackチャンネルを用意し、不明点や気づきはすぐに共有する体制を作成。 3つの対応ポイント 指摘事項は多岐にわたりましたが、大きく以下の3点に分類して対応を進めました。 1. OS・パッケージの脆弱性対応 対応 サポートされている最新版に遅滞なく更新する。 ツール Amazon Inspector + AWS Security Hub CSPM(EC2やECRのDockerイメージの脆弱性を可視化)。 2. 伝送経路(SSL / TLS)の修正 対応 TLS1.0 / 1.1などの古いプロトコル、脆弱な暗号スイート(cipher)を無効化する。 IE11対応などの特殊な要件がない限り、2026年現在では TLS 1.2 / TLS 1.3 中心 で問題なし。 ツール Mozilla SSL Configuration Generator で安全なconfigを作成し、 SSL Labs で評価を確認。 3. セキュリティヘッダの指定 対応 Webサーバー(またはCDN)にCSPやHSTSなどのヘッダを追加する。 ツール securityheaders.com でのチェック。 ここが一番の難所でした。特にCSP(Content Security Policy)は、設定を間違えると必要なJSやCSSが読み込まれずサイトが壊れるため、技術開発センターの管理下サイトで先行検証・適用を進めるという慎重なアプローチをとりました。 そして、私のSLに最も影響を与えたのが HSTS です。 課題 HSTSポリシーと「自己署名証明書」の衝突 HSTS(HTTP Strict Transport Security)は、ブラウザに対して「今後は絶対にHTTPSで接続しろ」と強制する強力なセキュリティヘッダです。 ベストプラクティスとしては、以下の指定が推奨されます。 max-age で1〜2年以上の長期間を指定 preload 指定 includeSubDomains 指定(サブドメインも全てHTTPS強制) ここで問題発生です。依頼事項として「 [社内検証用サブドメイン配下] でHTTP接続しているリソースはないか?」という確認がありました。最終的にSSL接続が必須になります。 私のSLでは、以下の社内検証環境が稼働していました。 OpenShift検証環境 自動構築で「自己署名証明書(オレオレ証明書)」を利用。 社内検証Rancher/GitLab環境 内部DNS設定で、 [社内検証用サブドメイン配下] を利用。 HSTSが引き起こす「副作用」 本番ドメインにHSTSの includeSubDomains が付与されると、それにアクセスしたブラウザはポリシーを記憶します。 その後、同じブラウザで社内検証環境にアクセスすると、ブラウザは「HTTPSでの接続」と「正当な証明書の提示」を厳格に要求します。 そうすると、検証環境で使っている自己署名証明書がブラウザに強固にブロックされ、アクセス不可になるという懸念が浮上したのです。 検討案 HSTSの副作用をどう回避するか? 検証環境のために本番のセキュリティレベルを下げるわけにはいきません。私たちは以下の回避策を検討しました。 正規の証明書を自動発行する Let’s Encrypt (DNS-01認証): Route53と連携して証明書の取得・更新を全自動化する。 AWS Certificate Manager (ACM): AWS環境であれば、ACMで発行した証明書をALB等に割り当てるのが最もスマート。 社内検証・開発用ドメインを物理的に分ける 本番ドメインのサブドメインを使わず、検証専用の別ドメインを取得する。 GoogleやFacebookなどの大手テック企業も採用している王道のアプローチ。 再構築のタイミングでドメインを変更する(期間区切り) 「10月までは今の環境を使い、それ以降は新規作成する」という場合、次回の再構築時にドメイン構成を見直すというリスク受容の判断もアリ。 プライベートCAを構築する 社内用の認証局を立てる案だが、構築・維持のコストリスクが高いため優先度は低め。 まずは「影響を小さく検証」する 様々な理想論を挙げましたが、机上の空論で終わらせず、まずは「実際のブラウザ挙動」を確かめるスモールスタートを切ることにしました。 【決定したネクストアクション】 技術管理担当者が、 sios.jp に HSTS ポリシー( includeSubDomains あり)を適用する。 Slackで連絡を受けたら、インフラ担当者がブラウザで sios.jp にアクセスし、HSTSポリシーを学習させる。 そのまま検証環境へアクセスし、HTTPS強制ブロックの挙動を確認する。 回避策の検証 HSTSポリシーを共有しない「シークレットモード」や「別プロファイルのブラウザ」を利用することで、自己署名証明書の検証環境へアクセス可能か(業務への影響を回避できるか)をテストする。 まとめ セキュリティスキャンからの指摘は、一見すると「ただの面倒な作業」に思えるかもしれません。 しかし、今回のように「なぜHSTSのサブドメイン指定が検証環境を壊すのか?」「どうやって回避するのか?」をチームで議論することで、インフラ設計の解像度がグッと上がります。 不要なリソースは捨てるのが最強のセキュリティ。 HSTS導入時は、サブドメインで動いている検証環境(自己署名証明書)の死に直結しないか注意する。 理想のアーキテクチャ(ドメイン分離やACM活用)を描きつつも、まずはプライベートブラウザ等の運用回避で業務を止めない立ち回りも重要。 今後も、こうした全社的な取り組みを通して得られた知見を、皆さんに共有していきたいと思います! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post セキュリティスキャン指摘対応 | HSTS導入の影響と回避策 first appeared on SIOS Tech Lab .
概要 こんにちは。サイオステクノロジーのはらちゃんです! 今回は、私が 第4回 JAZUG Shizuoka に参加してきた体験をレポートします。ローカル会場の雰囲気や私が登壇した内容の紹介などIT イベント参加の後押しになる情報をお届けします! こんな方へ特におすすめ JAZUG について知らない方 オフラインのIT イベントに興味があるけれど、参加をためらっている方 はじめに: JAZUG(ジャズユージー)とは? Japan Azure Users Group は、Microsoft Azure を学び、楽しみ、活かす、日本のユーザーグループで、2010/8/26 に結成したコミュニティです。 オフィシャルサイト から引用しました。 会場の様子: 常連参加者が多い印象 私が今回参加した静岡会場は、とても親しげな雰囲気でした。会場は 浜松市市民協働センター(第3研修室) に長テーブルが用意されており、参加者は隣同士で交流ができる構図になっていました。 15分前には到着される方がほとんどで、普段扱っている技術について議論する時間を楽しむ間に開始時刻となりました。 懇談会は参加必須 サブイベントとして、本イベント参加者を対象に懇談会を行いました。 登壇前は着席していたこともあり、なんとなく話しかけに行きにくいと感じていたため、道すがらや懇談会の向かい合った座席という場面で親交を深めることができました。 ここで技術初心者の質問をしても、いやな顔をする方などおらず、むしろ素直に分からない事を聞くことで理解を深められました。IT技術を知っているかではなく、興味があるかどうかが参加に大切だと思います。 懇談会への参加としてはもちろん強制ではありません。しかし、私としては懇談会こそ本命のように感じました。 登壇紹介 私が注目した登壇内容をご紹介します。 Work IQ × GitHub Copilot しろくま(Hiorki, Nomura)さんが紹介された「Work IQ」は、Microsoft 365 Copilot やAI エージェントにコンテキストを付与するインテリジェンスレイヤーです。 つまり、データを意味ベースで検索・解釈して、それをエージェント等に提供してくれます。 このメリットは主に以下の3点です。 業務データに自然言語でアクセスできる M365を横断検索する 調べた情報をもとに実行できる 私はGoogle ドライブから資料をダウンロードしてリポジトリに投げるという手間のかかる作業をしていたので、さっそく試してみたいと感じました。 — 登壇資料が こちら にアップロードされているので合わせてご一読ください。 マルチエージェント ×  Azure AI Video Indexer Noriyuki Takeiさんが紹介されたマルチエージェントは、それぞれ役割を持つエージェントが別視点で分析し、それらを総括するエージェントが最終的な結果を表示していました。 ロジックの詳細については配信されるそうなので、視聴しようと思います笑 自分の登壇 実は、YoutubeのLive配信などでライトニングトークをした経験しかなく、オフラインでの登壇は初めての経験でした。 内容としては、これまでの業務で経験したAzureのアーキテクチャ選定に苦労した経験から、公式ツールを使った意識するポイント紹介をしました。 他の方の登壇を聞いていると、導入が引き込まれる話ばかりで、共感を得ることの重要性が分かった気がします。 また、周りを見渡すなどアクションをすることに慣れていないのでオフラインを想定した練習も必要だなと思いました。 まとめ 今回は静岡へ行ってきた感想と気になった登壇内容をまとめました。 JAZUG 参加に必要なのは技術への好奇心だけ。 Work IQ × GitHub Copilot で収集から実行まで一元管理できる。 そう遠くないうちに、またイベントへ参加したいと考えています。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 第4回 JAZUG Shizuoka 参加レポート|気になる雰囲気と登壇紹介 first appeared on SIOS Tech Lab .
今号では、Linux における「拡張子」について、ちょっと深掘りした内容をご説明します! 前号でご紹介した「 知っておくとちょっと便利!Linux の拡張子について 」もぜひご参照くださいね。 Linux がファイルの種類を判断する仕組み 前号で、Linux は ファイルヘッダ というファイルの情報を読み取り、どのようなファイルであるかを判断していると書きましたが、この部分をもう少し具体的に見ていきます。 ファイルの種類を調べたい場合は file コマンドを使用しますが、その際 マジックファイルと呼ばれるファイル (/usr/share/file/magic) の情報を参照した上でファイルの種類を判断します。 なお、/usr/share/file/magic の内容は下記の様になっています (一部抜粋)。file コマンドのプログラムが、どのようにこれらの値を参照して最終的なファイルの種類を決定するか、その仕組みはかなり難解なので、別の機会にお話しできればと思います。 # RISC OS Chunk File Format # From RISC OS Programmer's Reference Manual, Appendix D # We guess the file type from the type of the first chunk. 0 lelong 0xc3cbc6c5 RISC OS Chunk data >12 string OBJ_ \b, AOF object >12 string LIB_ \b, ALF library # RISC OS AIF, contains "SWI OS_Exit" at offset 16. 16 lelong 0xef000011 RISC OS AIF executable # RISC OS Draw files # From RISC OS Programmer's Reference Manual, Appendix E 0 string Draw RISC OS Draw file data # RISC OS new format font files # From RISC OS Programmer's Reference Manual, Appendix E 0 string FONT\0 RISC OS outline font data, >5 byte x version %d 0 string FONT\1 RISC OS 1bpp font data, >5 byte x version %d 0 string FONT\4 RISC OS 4bpp font data >5 byte x version %d # RISC OS Music files # From RISC OS Programmer's Reference Manual, Appendix E 0 string Maestro\r RISC OS music file >8 byte x version %d >8 byte x type %d # Digital Symphony data files # From: Bernard Jungen (bern8817@euphonynet.be) 0 string \x02\x01\x13\x13\x13\x01\x0d\x10 Digital Symphony sound sample (RISC OS), >8 byte x version %d, >9 pstring x named "%s", >(9.b+19) byte =0 8-bit logarithmic >(9.b+19) byte =1 LZW-compressed linear >(9.b+19) byte =2 8-bit linear signed >(9.b+19) byte =3 16-bit linear signed >(9.b+19) byte =4 SigmaDelta-compressed linear >(9.b+19) byte =5 SigmaDelta-compressed logarithmic >(9.b+19) byte >5 unknown format 0 string \x02\x01\x13\x13\x14\x12\x01\x0b Digital Symphony song (RISC OS), >8 byte x version %d, >9 byte =1 1 voice, >9 byte !1 %d voices, >10 leshort =1 1 track, >10 leshort !1 %d tracks, >12 leshort =1 1 pattern >12 leshort !1 %d patterns 0 string \x02\x01\x13\x13\x10\x14\x12\x0e >9 byte =0 Digital Symphony sequence (RISC OS), >>8 byte x version %d, >>10 byte =1 1 line, >>10 byte !1 %d lines, >>11 leshort =1 1 position >>11 leshort !1 %d positions >9 byte =1 Digital Symphony pattern data (RISC OS), >>8 byte x version %d, >>10 leshort =1 1 pattern >>10 leshort !1 %d patterns … Linux でよく使われる二重拡張子 (tar.gz 等) tar.gz や conf.bak 等、Linux ではドットが連続する 二重拡張子 を使用することがあります。 これは、そのファイルにおける 処理の履歴 を表します。 例えば、下記の様な二重拡張子を持つファイルを見てみると… .tar.gz: 複数のファイルを 1つにまとめた (.tar) ものを、圧縮した (.gz)。 .conf.bak: 設定ファイル (.conf) をバックアップとして残した (.bak)。 .log.1.gz: ログファイル (.log) の 1世代前をローテーション (.1) し、さらに圧縮した (.gz)。 それぞれこのような意味になります。 シバン (Shebang) の有無でファイルの種類が変わる!? シバンとは、Linux においてスクリプトファイルの1行目に記述する #! で始まる特別な文字列です。 実はこれが、ファイル実行時における拡張子の代わりとなります。 例えば、下記の様なテキストファイル test.txt があるとします。file コマンドでファイルの種類を確認すると、当然テキストファイル (ASCII text) と判断されます。 # cat test.txt echo "Hello" # file test.txt test.txt: ASCII text ところが、test.txt に #!/bin/bash という文字列を追加してみると、拡張子を含めたファイル名はそのままに、ファイルの種類が変わっていることが分かります。 # cat test.txt #!/bin/bash echo "Hello" # file test.txt test.txt: Bourne-Again shell script, ASCII text executable これは、マジックファイルの定義に基づき、ファイルの先頭に #!/bin/bash が追加された際にスクリプト (Bourne-Again shell script) と判断するルールがあるため、このような結果となります。 プログラムの 1行目に記載する #! は単なるコメントではなく、ファイルの種類を識別するためにも使用される重要な文字列であることが分かりますね。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 知っておくとちょっと便利!Linux の拡張子について2 (ちょっと深掘り) first appeared on SIOS Tech Lab .
こんにちは! 今月も「OSSのサポートエンジニアが気になった!OSSの最新ニュース」をお届けします。 アドビ (Adobe) は、Windows および macOS で Adobe Acrobat、Adobe Reader を使用しているユーザに影響を与える脆弱性「CVE-2026-34621」が、攻撃者に悪用されていることを正式に認めました。 これを受けて、セキュリティアップデートを 72時間以内にインストールするよう呼びかけています。 72時間以内の更新を推奨──「PDFを開くだけで乗っ取られる」アドビリーダーのゼロデイ攻撃が進行 https://forbesjapan.com/articles/detail/95601 攻撃者が送信したフィッシングメールに埋め込まれた番号に電話をかけると、オペレータが悪意のあるソフトウェアのインストール手順を案内したり、認証情報を取得しようとする、ATHR という攻撃プラットフォームが公開されています。 AI Meets Voice Phishing: How ATHR Automates the Full TOAD Attack Chain https://abnormal.ai/blog/athr-ai-voice-phishing-toad-attacks フランス政府は、政府用コンピュータを Windows から Linux へ移行する計画を発表しました。 ヨーロッパ域外へのデジタル依存度を低減するという目標を設定したためです。 フランス政府がWindows搭載の政府用コンピュータをLinuxに移行する計画を発表、アメリカ製技術への依存をさらに軽減するため https://gigazine.net/news/20260414-france-windows-linux/ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【2026年4月】OSSサポートエンジニアが気になった!OSS最新ニュース first appeared on SIOS Tech Lab .
Webサービスやアプリの開発現場で、「ここにハンバーガーメニューを置こう」といった会話を耳にしたことはないでしょうか。 「ハンバーガー」「ミートボール」「ケバブ」「ワッフル」「弁当箱」。おいしそうなランチのような名前が並んでいますが、これらは今のUIデザインに欠かせない「メニューアイコン」の俗称です。 スマートフォンの狭い画面にパソコンに準じた多くの情報を盛り込むために、これらのアイコンは「要素を隠すための魔法の箱」として多用されています。しかし、これらは便利であると同時に、使い方を間違えるとプロダクトの使い勝手を下げる「劇薬」でもあります。 今回は、それぞれのアイコンの暗黙的ルールと、メニューを隠すことの代償について紐解いていきます。 4つのメニューアイコンと意味 これらのアイコンは、どれも「クリック(タップ)すると何かメニューが開く」という点では同じですが、使われる文脈が異なります。 1. 三本線 名称、呼称: menu、ハンバーガーメニュー、… 役割: グローバルナビゲーション(アプリケーション全体に関わる主要なメニュー) 配置: 主に画面の左上(または右上) どの画面にいてもアクセスできる、プロダクトの根幹となるナビゲーションを隠すために使われます。 アイコンが三本線ではなく、二本線や、四本線の場合もあります。同様の役割で、三本線ではなくサイドバーを表すアイコンが配置されている場合もあります。 例: Google Material Designサイト 例: Anthropic Claude (Web) 2. 横の三点リーダー 名称、呼称: ellipsis horizon、more horizon、overflow menu、水平の三点リーダー、ミートボールメニュー、… 役割: 画面全体や、要素に対する追加のアクション 配置: 画面の右上や、要素の末尾など 文章の最後の「……(続く、省略されている)」と意味合いも形状も同じで、「この行(アイテム)に対して、編集・削除・共有などの操作」(コンテキストメニュー)や、「この画面に関するその他の操作」を示します。 「削除」操作は、誤操作を防ぐために、あえてコンテキストメニューに入れて隠す場合もあります。 例: OpenAI ChatGPT(Web) 例: Apple iOS Safari(Webブラウザー) 3. 縦の三点リーダー 名称、呼称: ellipsis vertical、more vertical、overflow menu、垂直の三点リーダー、ケバブメニュー、… 役割: 「横の三点リーダー」と同様 配置: 「横の三点リーダー」と同様 「縦」に並んだ点が、展開するアクションメニュー(メニューリスト)を想像させる形状です。 Googleのプロダクトにて標準的に利用されています。 「横の三点リーダー」と「縦の三点リーダー」は同様の役割で、同じサービス内で共存、使い分けるケースは少ないです。 例: Google Gemini(Web) 例: Google Chrome(Webブラウザー) 4. 九つの点 名称、呼称: Apps、launcher、launchpad、ワッフルメニュー、弁当箱メニュー、… 役割: 別のアプリケーションや、独立したモジュールへの切り替え 配置: 画面の左上、右上など ハンバーガーメニューがアプリケーション内のグローバルメニューなのに対して、ワッフルメニューは、現在のアプリケーション以外に切り替える(起動する)メニューに使われます。 現在のコンテキストを離れ、全く別の機能群にジャンプするための「ハブ」として機能します。 例: Microsoft 365(Web) 現時点では、これらの「暗黙のルール」が主流となっており、メニューアイコンを選ぶ際には、これらを踏まえるのがユーザーのためにも無難です。 メニューを隠すことの代償 これらのアイコンを使えば、どんなに複雑な機能でも小さなボタンの中に押し込むことができます。画面はスッキリと美しくなり、一見するとデザインが洗練されたように感じます。 しかし、UIデザインにおいて 「隠す」ことには代償 が伴います。 認知心理学の分野やUXデザインにおいては 「発見可能性(Discoverability)」 という言葉が使われます。英語のことわざに “Out of sight, out of mind”(見えなければ、忘れ去られる)とあるように、ユーザーは「画面に見えていない機能」はないものとして扱います。 メニューアイコンの中に機能を隠すということは、以下の2つの認知的負荷をユーザーに強いることになります。 推測の負荷: 「このアイコンを押せば、自分が求めている機能があるはずだ」とユーザー自身に推測させる必要がある。 操作の負荷: 目的の機能にたどり着くまでに、必ず「メニューを開く」という余分な1クリック(タップ)が発生する。 「画面をスッキリさせたい」という開発者側の都合で作られたハンバーガーメニューの奥底に、プロダクトにとって重要な機能(リンク)を隠してしまった結果、ユーザビリティや、ビジネス的な成果の悪化を招くこともあります。 見えるメニューも試す 「隠すことの代償」を軽減する具体策としては、例えばモバイルアプリでは、ボトムナビゲーション(タブバー)(見えるメニュー)に主たる項目を配置しハンバーガーメニュー内(見えないメニュー)にそれ以外の項目を並べることを試すとよいでしょう。 同様に、三点リーダーの場合も何を隠すのか、隠さないのか、よく検討することが大切です。 例: バーガーキング(App) まとめ:アイコンを「ガラクタ箱」にしないために ハンバーガーメニュー、三点リーダーメニュー。これらは限られた画面領域を有効活用するための素晴らしい発明です。しかし、これらを 「画面に収まりきらなかった機能をとりあえず放り込んでおくガラクタ箱」 として使ってはいけません。モバイル用、デスクトップ用ともに。 UIを設計する際、アイコンで隠す前に、まず 「情報設計(IA:Information Architecture)」 から見直す必要があります。 メニューアイコンは「デザインの魔法」ではなく、単に「整理のための引き出し」と捉えたほうがよいでしょう。内容、役割、頻度、数、ラベルなど、項目を論理的に整理することが、使いやすいプロダクトを生み出すための第一歩となります。 余談:「食べ物の呼び名(スラング)」の歴史 「三本線」や「横の三点リーダー」のアイコン自体は古くからありましたが、それらが「食べ物の名前」で呼ばれるようになったのは、スマートフォンが普及して画面の省略化が進んだ2010年代以降のことです。デザイナーたちの「連想ゲーム」で以下のように名付けられていったようです。 ハンバーガー iPhoneの登場後、Facebookなどの人気アプリが画面領域を節約するために「三本線」のアイコンでメニューを隠すUIを採用しました。この時、アイコンが爆発的に普及し、デザイナーや開発者の間で「ハンバーガー」と呼ばれ始め、UIデザインにおける最初にして最大の食べ物スラングとして定着しました。 ワッフル / 弁当箱 Googleが「9つの点」のランチャーを大々的に導入。すでに「ハンバーガー」という食べ物スラングが定着していた界隈で、「じゃあ、あの四角い格子状のやつはワッフルだ」「いや、おかずが詰まったBento Box(弁当箱)だ」と呼ばれるように。 ケバブ AndroidのMaterial Designにて標準化された「縦の3つの点」。「ハンバーガーがあるなら、肉が縦に並んでいるのは『ケバブ』だろう」というジョークが生まれました。 ミートボール ケバブ(縦)という呼び名に対して、昔から使われていた「横の3つの点」を呼び分ける必要が出てきました。「縦が串焼き(ケバブ)なら、お皿に横に転がっている肉団子だから『ミートボール』にしよう」という、後追いの連想ゲーム。 … UI界隈でも、どこまでこれらの俗称が通じるかは不明ですが …。 Photo by Jean-claude Attipoe , Olivier Amyot  , Victoria Shes , Najmah Faisal on Unsplash   ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 1人がこの投稿は役に立ったと言っています。 The post 「隠すUI」の功罪:それ、ハンバーガーで大丈夫? first appeared on SIOS Tech Lab .
こんにちは、サイオステクノロジー武井です。今回は、イケてるOSSであるDaprについて一筆したためました。 Daprとは? Daprとは、Distributed Application Runtimeの略であり、本当にざっくり一言で言えば、分散アプリケーションサービスを開発する際のインフラレイヤーを抽象化するためのものです。 ここは説明が非常に難しいので、この章では、ざっくりとした理解で構いません。順を追ってDaprの本質に迫っていきたいと思います。 ここでDaprを理解するために一つの例を上げてみましょう。 例えば、とあるアプリケーションで、ユーザーの情報を保存するために、データベースを使用しているとします。当然従来のアプリケーションでは、当然MySQLにはMySQL用の実装を、PostgreSQLにはPostgreSQL用の実装をする必要があります。これをDaprを使用することで、データベースの種類に関係なく、同じコードでデータベースにアクセスすることができます。つまり、Daprは、データベースの種類を抽象化してくれるのです。 他にも、Daprは、Publish and Subscribe、State Management、Bindingsなど分散アプリケーションを開発する際に必要な機能を提供しているのですが、これを実現するアーキテクチャとして最も重要なのが、Daprのサイドカーです。 このサイドカーの仕組みをわかりやすく伝えるために、データベースにアクセスする際の例を用いて、「Daprを使わない場合」と「Daprを使う場合」の違いを見てみましょう。 Daprを使わない場合 従来のアプリケーションでは、データベースにアクセスするためのコードは、アプリケーションの中に直接書かれています。例えば、MySQLにアクセスするためのコードは、MySQL用のライブラリを使用して書かれています。その構成は以下の通りとなります。 例えば、ここでMySQLをPostgreSQLに変更したい場合、MySQL用のコードを削除して、PostgreSQL用のコードを書き直す必要があります。書き直した後の構成は以下の通りとなります。 先ほどとは違い、アプリ内の「MySQL用のコード」が「PostgreSQL用のコード」に変わっていますね。このように、Daprを使わない場合、データベースの種類を変更するたびに、アプリケーションのコードを変更する必要があります。これが、Daprを使う場合と比べて、非常に面倒であることがわかります。 Daprを使う場合 Daprを使う場合、アプリケーションのコードは、DaprのAPIを使用してデータベースにアクセスします。Daprは、データベースの種類を抽象化してくれるため、アプリケーションのコードは、データベースの種類に関係なく同じコードでアクセスすることができます。 MySQLにアクセスするためのDaprを使う場合の構成は以下の通りとなります。 「Daprを使わない場合」と大きく異なるのは、サイドカーが存在していることです。アプリケーションは、DaprのAPIを使用してサイドカーにアクセスし、サイドカーがデータベースにアクセスするという構成になっています。この構成のメリットは、データベースの種類を変更する際に、アプリケーションのコードを変更する必要がないことです。例えば、MySQLからPostgreSQLに変更したい場合、サイドカーの設定を変更するだけで、アプリケーションのコードはそのままで済みます。変更後の構成は以下の通りとなります。 アプリ側は全く変更してないのがわかりますでしょうか?変更したのは、サイドカーの設定だけです。この設定では、サイドカーにてPostgreSQLにアクセスするように変更しています。 このように、Daprを使う場合、データベースの種類を変更する際に、アプリケーションのコードを変更する必要がないため、非常に便利であることがわかります。 一方で、このサイドカーが対応していないデータベースを使用したい場合は、サイドカーの設定を変更するだけでは対応できないため、アプリケーションのコードを変更する必要があります。 この例では、データベースへのアクセスを例に挙げましたが、Daprは、Publish and Subscribe、State Management、Bindingsなど、分散アプリケーションを開発する際に必要な機能を提供しているため、これらの機能も同様に抽象化されていることがわかります。つまり、Daprを使うことで、分散アプリケーションの開発が非常に楽になるということです。 サイドカーの実行形態について Daprのサイドカーの実行形態は大きく分けると、以下の2つがあります。 サイドカーコンテナ サイドカープロセス サイドカーコンテナ サイドカーコンテナは、Daprのサイドカーがコンテナとして実行される形態です。これは、Kubernetesなどのコンテナオーケストレーションツールを使用している場合に一般的に使用されます。Daprのサイドカーは、アプリケーションと同じPod内のコンテナとして実行されます。アプリケーションとDaprはlocalhostで通信するため、低レイテンシで連携できます。図中の「Scheduler」「Placement」については、Daprのコントロールプレーンコンポーネントであり、サイドカーコンテナとは別に実行されます。本記事では、サイドカーコンテナの説明に焦点を当てるため、これらのコントロールプレーンコンポーネントに関する説明は省略しています。 サイドカープロセス Daprのサイドカーは、VMやローカル環境ではアプリケーションと同じホスト上の別プロセスとして実行されます。アプリケーションは、HTTPまたはgRPCを通じてローカルのdaprdプロセスに接続します。この形態は、Kubernetes以外の環境でDaprを利用する場合によく使われる実行方法です。 以下の図は、開発環境でDaprを利用する際のサイドカープロセスの構成例です。dapr CLIというDapr専用のコマンドラインツールを使用すると、アプリケーションとサイドカープロセスを同時に起動できます。 RedisとZipkinは、Daprのサイドカーが利用する外部コンポーネントの例です。Redisは、DaprのState ManagementやPublish and Subscribeなどの機能でよく利用されるデータストアです。またZipkinは、Daprの分散トレーシング機能で使用されるトレーシングシステムです。これらのコンポーネントは、dapr CLIによって起動されます。 本記事で紹介するサンプルアプリケーションは、ここで説明したように、dapr CLIによって起動されるサイドカープロセスの構成で動作することを前提としています。ただし、実行形態が異なる場合でも基本的な考え方は同じであり、他の実行形態にも応用できます。そのため、本記事ではサイドカープロセスの形態を前提として説明していきます。 Daprを使うための環境準備 先程説明しましたように、Daprを使うための環境は様々なです。例えば、Kubernetes環境でDaprを利用する場合は、Kubernetesクラスターを用意し、Daprをインストールする必要があります。一方で、ローカル環境でDaprを利用する場合は、dapr CLIをインストールするだけで済みます。 本記事の中で説明するサンプルアプリケーションは、ローカル環境でDaprを利用することを前提としています。そのため、dapr CLIをインストールする必要があります。dapr CLIとは、Daprをローカル環境で利用するためのコマンドラインツールです。dapr CLIを使用することで、Daprのサイドカープロセスを簡単に起動したり、Daprのコンポーネントを管理したりすることができます。 そのインストール手順は以下のURLに記載されていますので、それを見てさっくりとインストールしてみてください。 https://docs.dapr.io/getting-started/install-dapr-cli/ そのインストール方法はめっちゃ簡単です。一例としてLinuxにDapr CLIをインストールする手順は以下の通りです。 $ wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash そして、次に、初期化が必要になります。これは、ローカル環境でDaprを使うための様々なコンポーネントを起動するためのコマンドです。初期化のコマンドは以下の通りです。 $ dapr init このコマンドにより、Daprのサイドカープロセスであるdaprdがインストールされ、そしてDaprを動作させるために必要な基本コンポーネントであるRedisやZipkinなども起動されます。試しに以下のコマンドを実行してみてください。 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6822763b7ba6 redis:6 "docker-entrypoint.s…" 2 weeks ago Up 13 days 0.0 .0.0:6379- > 6379 /tcp, [ :: ] :6379- > 6379 /tcp dapr_redis d5bf3b79e44d daprio/dapr:1.17.0 "./scheduler --etcd-…" 2 weeks ago Up 13 days 0.0 .0.0:2379- > 2379 /tcp, [ :: ] :2379- > 2379 /tcp, 0.0 .0.0:50006- > 50006 /tcp, [ :: ] :50006- > 50006 /tcp, 0.0 .0.0:58081- > 8080 /tcp, [ :: ] :58081- > 8080 /tcp, 0.0 .0.0:59091- > 9090 /tcp, [ :: ] :59091- > 9090 /tcp dapr_scheduler c79f84b94e2f daprio/dapr:1.17.0 "./placement" 2 weeks ago Up 13 days 0.0 .0.0:50005- > 50005 /tcp, [ :: ] :50005- > 50005 /tcp, 0.0 .0.0:58080- > 8080 /tcp, [ :: ] :58080- > 8080 /tcp, 0.0 .0.0:59090- > 9090 /tcp, [ :: ] :59090- > 9090 /tcp dapr_placement dbf0fab9540b openzipkin/zipkin "start-zipkin" 2 weeks ago Up 13 days ( healthy ) 0.0 .0.0:9411- > 9411 /tcp, [ :: ] :9411- > 9411 /tcp dapr_zipkin 様々なコンテナが起動していることがわかりますね。これらのコンテナは、DaprのコントロールプレーンコンポーネントであるSchedulerやPlacement、そしてDaprの分散トレーシング機能で使用されるZipkinなどです。それぞれのコンテナの説明は割愛します。ここでは、 これで、ローカル環境でDaprを利用するための環境が整いました。 Daprの様々なコンポーネント Daprにはさまざまなコンポーネントが用意されています。これらのコンポーネントは、Daprの機能を実現するためのものであり、アプリケーションはDaprを介してそれらを利用できます。 例えば、Daprには状態を保存するための State Management コンポーネントがあります。このコンポーネントを利用するには、設定ファイルで対象のコンポーネントを定義します。すると、Daprサイドカー(daprd)がそのコンポーネントを読み込み、対応するインフラへ接続できるようになります。 その結果、アプリケーションがサイドカーに対して状態を保存するAPIを呼び出すと、サイドカーはState Managementコンポーネントを通じて、RedisやAzure Cosmos DBなどのバックエンドストレージに状態を保存できるようになります。 本章では、Daprの代表的なコンポーネントであるState Management、Publish and Subscribe、Service Invocation、Bindingsについて、簡単に説明していきます。これらのコンポーネントは、Daprを使用する際に非常に重要な役割を果たすため、理解しておくことが重要です。 そして、以降では、これらのコンポーネントを実際に利用するためのサンプルコードも紹介しながら、Daprの機能を説明していきます。サンプルコードはGitHubのリポジトリに公開していますので、ぜひ参考にしてみてください。 https://github.com/noriyukitakei/dapr-sample State Management まずは、State Managementコンポーネントについて説明します。State Managementコンポーネントは、Key/Value形式の状態を保存するためのコンポーネントです。Daprは、State Managementコンポーネントを通じて、RedisやAzure Cosmos DBなどのバックエンドストレージに状態を保存できるようになります。 Key/Value形式のデータの代表例で言えば、ユーザーのセッション情報や、IoTデバイスの状態(例: 温度センサーの最新の温度値)などが挙げられます。これらのデータは、アプリケーションの状態を管理するために頻繁に使用されます。 システム構成 State Managementコンポーネントを利用する際のシステム構成は以下の通りとなります。 サイドカーは、Redis用の設定ファイルを読み込むことで、Redisに接続できるようになります。アプリケーションは、DaprのAPIを使用してサイドカーに状態を保存するリクエストを送ります。サイドカーは、そのリクエストを受け取ると、State Managementコンポーネントを通じて、Redisに状態を保存します。 ファイル構成 State Managementコンポーネントを利用するためのファイル構成は以下の通りとなります。これらのファイルは先程紹介したGitHubリポジトリ( https://github.com/noriyukitakei/dapr-sample )の中の、State Managementディレクトリの中に配置されています。 StateManagement ├── app.py ├── Infrastructure │ └── components │ ├── redis │ │ └── statestore_redis.yaml │ └── sqlite │ └── statestore_sqlite.yaml ├── README.md └── requirements.txt ではこれらのファイルを一つずつ紐解くことで、State Managementコンポーネントを利用するための構成を理解していきましょう。 ソースコードの説明 ■ Infrastructure/components/redis/statestore_redis.yaml Infrastructure/components/redis/statestore_redis.yamlは、DaprのState ManagementコンポーネントをRedisに接続するための設定ファイルです。このファイルには、DaprがRedisに接続するための情報が記載されています。 apiVersion : dapr.io/v1alpha1 # Daprのリソース定義のバージョン kind : Component # 設定ファイルの種別 metadata : name : statestore # アプリから参照するコンポーネント名 spec : type : state.redis # コンポーネントの種別 version : v1 # コンポーネントのバージョン metadata : - name : redisHost value : localhost : 6379 # Redisの接続先 - name : redisPassword value : "" # パスワード(未設定) このファイルは一言で言うと、「DaprのState ManagementでRedisを使う」ことを定義しているものです。アプリケーションはRedisを直接操作するのではなく、DaprのAPIを通じて状態を保存します。 例えばアプリケーションから次のように呼び出すと、 client . save_state ( "statestore" , "user1" , data ) Daprは内部的に先程のコンポーネント定義を参照して、Redisに接続し、状態を保存します。つまり以下のような流れです。 アプリケーションがDaprのAPIを呼び出す。そのとき、コンポーネント名として「statestore」を指定する。 Daprがコンポーネント定義の中から、metadata.nameが「statestore」であるコンポーネント定義を探す。 Daprがコンポーネント定義を参照して、Redisに接続するための情報(ここでは接続先がlocalhost:6379、パスワードが空)を取得する。 Daprが取得した情報を基に、Redisに接続する。 Daprがアプリケーションから受け取った状態をRedisに保存する。 Daprが保存の結果をアプリケーションに返す。 ■ app.py app.pyは、DaprのState Managementコンポーネントを利用するためのアプリケーションコードです。このコードは、DaprのPython SDKを使用して、状態を保存するためのAPIを呼び出しています。 では、コードの内容を見てみましょう。 for i in range ( 1 , 10 ) : json_data = [ { "key" : str ( i ) , "value" : { "orderId" : i } } ] requests . post ( "http://localhost:3611/v1.0/state/statestore" , json = json_data ) time . sleep ( 1 ) このコードは、1から9までの数字をキーとし、その値としてorderIdを持つJSONオブジェクト(以下参照)を作成し、DaprのState Management APIにPOSTリクエストを送っています。 [ { "key" : "1" , "value" : { "orderId" : 1 } } , { "key" : "2" , "value" : { "orderId" : 2 } } , ⋯以下略⋯ ] リクエストのURLには、先程のコンポーネント定義で指定した「statestore」が含まれています。これにより、Daprは「statestore」という名前のState Managementコンポーネントを参照して、Redisに状態を保存します。 リクエストのURLの構成は以下の通りです。 http://localhost:3611: Daprサイドカーのエンドポイント ※ このポート番号については、Dapr CLIを使用してサイドカープロセスを起動する際に指定したポート番号になります。後ほど説明しますが、Dapr CLIを使用してサイドカープロセスを起動する際に、–app-portオプションでアプリケーションのポート番号を指定することができます。例えば、アプリケーションのポート番号を3611に指定した場合、Daprサイドカーはlocalhost:3611で待ち受けるようになります。 /v1.0: Dapr APIのバージョン /state: State Management APIを呼び出すことを示すパス /statestore: 先程のコンポーネント定義で指定した「statestore」という名前のState Managementコンポーネントを参照するためのパス result = requests . get ( f"http://localhost:3611/v1.0/state/statestore/ { i } " ) print ( result . json ( ) ) time . sleep ( 1 ) このコードは、先程保存した状態をDaprのState Management APIにGETリクエストを送って取得しています。リクエストのURLには、先程のコンポーネント定義で指定した「statestore」が含まれていることに加えて、最後に/{i}が追加されています。これにより、Daprは「statestore」という名前のState Managementコンポーネントを参照して、Redisからキーが{i}である状態を取得します。リクエストのURLの構成は以下の通りです。 http://localhost:3611: Daprサイドカーのエンドポイント /v1.0: Dapr APIのバージョン /state: State Management APIを呼び出すことを示すパス /statestore: 先程のコンポーネント定義で指定した「statestore」という名前のState Managementコンポーネントを参照するためのパス /{i}: 取得したい状態のキーを指定するためのパス requests . delete ( f"http://localhost:3611/v1.0/state/statestore/ { i } " ) time . sleep ( 1 ) このコードは、先程保存した状態をDaprのState Management APIにDELETEリクエストを送って削除しています。リクエストのURLの構成は、先程のGETリクエストと同様であり、最後に/{i}が追加されていることがわかります。これにより、Daprは「statestore」という名前のState Managementコンポーネントを参照して、Redisからキーが{i}である状態を削除します。リクエストのURLの構成は以下の通りです。 http://localhost:3611: Daprサイドカーのエンドポイント /v1.0: Dapr APIのバージョン /state: State Management APIを呼び出すことを示すパス /statestore: 先程のコンポーネント定義で指定した「statestore」という名前のState Managementコンポーネントを参照するためのパス /{i}: 削除したい状態のキーを指定するためのパス 実行方法 では次に、State Managementコンポーネントを利用するためのアプリケーションコードを実行してみましょう。実行する前に、Dapr CLIを使用してサイドカープロセスを起動する必要があります。ここではその手順を説明します。 $ git clone https://github.com/noriyukitakei/dapr-sample $ cd dapr-sample/StateManegement まずは、先程紹介したGitHubリポジトリからサンプルコードをクローンして、StateManagementディレクトリに移動します。 $ pip install -r requirements.txt 次に、Pythonの依存関係をインストールします。requirements.txtには、このアプリケーションコードを実行するために必要なPythonパッケージが記載されています。 ちなみにRedisについては、dapr initコマンドを実行した際に、DaprのState ManagementやPublish and Subscribeなどの機能でよく利用されるデータストアであるRedisも起動されます。ですので、特にRedisを起動するためのコマンドを実行する必要はありません。 $ dapr run --app-id statemanagement --dapr-http-port 3611 --components-path Infrastructure/components/redis -- python app.py dapr runコマンドを使用して、アプリケーションコードを実行します。 –app-idオプションでアプリケーションのIDを指定します。ここでは「statemanagement」というIDを指定しています。このIDは、Daprサイドカーがアプリケーションを識別するために使用されます。State Managementでは利用しませんが、Service Invocationなどの機能を利用する際に、このIDが重要になります。 –dapr-http-portオプションでDaprサイドカーのHTTPポートを指定します。ここでは3611を指定しています。これにより、Daprサイドカーはlocalhost:3611で待ち受けるようになります。 –components-pathオプションで、Daprのコンポーネント定義ファイルが配置されているディレクトリを指定します。ここではInfrastructure/components/redisを指定しています。これにより、Daprサイドカーはこのディレクトリの中にあるコンポーネント定義ファイルを読み込むようになります。 図解すると以下のような対応関係になります。 このコマンドを実行すると、Daprサイドカーが起動し、そしてアプリケーションコードも実行されます。アプリケーションコードは、DaprのState Management APIを呼び出して、状態を保存、取得、削除するリクエストを送ります。Daprサイドカーは、そのリクエストを受け取ると、State Managementコンポーネントを通じて、Redisに状態を保存、取得、削除します。 保存先をSQLiteに変更してみる では、Daprのメリットを体感して頂くために、状態の保存先をRedisからSQLiteに変更してみましょう。Daprを使わない場合は、アプリケーションコードを変更する必要がありますが、Daprを使う場合は、コンポーネント定義ファイルを変更するだけで済みます。 つまりこんな感じです。 変更後のコンポーネント定義ファイルは以下の通りとなります。 apiVersion : dapr.io/v1alpha1 kind : Component metadata : name : statestore spec : type : state.sqlite version : v1 metadata : - name : connectionString value : "sqlite/data.db" まず、Redis用のコンポーネント定義ファイルと比較すると、typeがstate.sqliteに変わっています。これにより、DaprはState ManagementコンポーネントとしてSQLiteを使用するようになります。そして、spec.metadataの内容も変わっています。SQLiteに接続するための情報を記載する必要があるため、connectionStringという名前のメタデータを追加しています。これにより、DaprはSQLiteに接続するための情報を取得できるようになります。 このファイルは、Infrastructure/components/sqlite/statestore_sqlite.yamlに配置されています。 先程のdapr runコマンドの–components-pathオプションで、このファイルが配置されているディレクトリを指定することで、Daprサイドカーはこのファイルを読み込むようになります。 $ dapr run --app-id statemanagement --dapr-http-port 3611 --components-path Infrastructure/components/sqlite -- python app.py いかがでしょうか?アプリケーションコードを一切変更することなく、状態の保存先をRedisからSQLiteに変更することができましたね。これがDaprのメリットの一つである、インフラレイヤーの抽象化による柔軟性の高さです。 Publish and Subscribe 次は、Publish and Subscribeコンポーネントについて説明します。DaprのPublish and Subscribeコンポーネントについて説明する前に、まずはPublish and Subscribeの概念について簡単に説明します。 例えば、あるアプリケーションで、ユーザーが新しい注文を作成したとします。そのとき、注文が作成されたことを他のサービスに通知したい場合があります。例えば、在庫管理サービスや配送サービスなどです。このような場合に、Publish and Subscribeの仕組みが役立ちます。 Publish and Subscribeを使わない場合で考えてみましょう。ユーザーが新しい注文を作成したとき、注文サービスは在庫管理サービスや配送サービスに対して、HTTPリクエストを送って通知します。注文サービスは、配送サービス、在庫管理サービスの両方から正常にレスポンスが返ってきたら、注文が正常に処理されたと判断します。 もし、ここで配送サービスがダウンしている場合、注文サービスは配送サービスに通知することができません。そのため、注文サービスは失敗して、ユーザーは注文を作成することができません。 そこでPublish and Subscribeの仕組みを使うと、注文サービスは、注文が作成されたことを「メッセージブローカー」に対して通知します。そして、配送サービスや在庫管理サービスは、そのメッセージブローカーから注文が作成されたことを受け取ります。これにより、注文サービスは配送サービスや在庫管理サービスの状態に関係なく、注文が作成されたことを通知することができます。そして、配送サービスが仮にダウンしていたとしても、注文サービスは注文が作成されたことをメッセージブローカーに通知することができるため、ユーザーは注文を作成することができ、かつ在庫管理サービスは注文が作成されたことを受け取ることができます。配送サービスが復旧したときに、配送サービスも注文が作成されたことを受け取ることができます。 このように、Publish and Subscribeの仕組みを使うことで、サービス間の疎結合な連携が可能になります。DaprのPublish and Subscribeコンポーネントは、このようなPublish and Subscribeの仕組みを提供するためのコンポーネントです。Daprを使用することで、アプリケーションは、DaprのAPIを通じて、メッセージブローカーに対してメッセージを公開したり、メッセージブローカーからメッセージを受け取ったりすることができます。 システム構成 DaprでPublish and Subscribeコンポーネントを利用する際のシステム構成は以下の通りとなります。メッセージブローカーとしてRedisを使用する場合の構成例を示しています。 ① まず、Publisherが、DaprのHTTP/gRPC APIを通じて、サイドカーに対してメッセージを公開するリクエストを送ります。 ② サイドカーは、そのリクエストを受け取ると、Publish and Subscribeコンポーネントを通じて、Redisなどのメッセージブローカーに対して、メッセージを送信します。 ③ サイドカーは、Redisに接続し、Redisからメッセージを受け取ります。 ④ サイドカーは、そのリクエストを受け取ると、事前にコンポーネントで指定したSubscriberのエンドポイントに対して、メッセージを送信します。 もちろん、PublisherとSubscriberは、メッセージを格納するためのデータストアに何を使っているのかを知る必要はありません。RedisだろうがRabbitMQだろうが、Publisherは、DaprのAPIを通じてサイドカーに対してメッセージを公開するだけで済みますし、Subscriberは、DaprのAPIを通じてサイドカーからメッセージを受け取るだけで済みます。これが、DaprのPublish and Subscribeコンポーネントのメリットの一つである、インフラレイヤーの抽象化による柔軟性の高さです。 ファイル構成 Publish and Subscribeコンポーネントを利用するためのファイル構成は以下の通りとなります。これらのファイルは先程紹介したGitHubリポジトリ( https://github.com/noriyukitakei/dapr-sample )の中の、PubSubディレクトリの中に配置されています。 PubSub ├── Infrastructure │ └── components │ └── pubsub.yaml ├── publisher │ ├── app.py │ └── requirements.txt └── subscriber ├── app.py └── requirements.txt ソースコードの説明 ■ Infrastructure/components/pubsub.yaml Infrastructure/components/pubsub.yamlは、DaprのPublish and SubscribeコンポーネントをRedisに接続するための設定ファイルです。このファイルには、DaprがRedisに接続するための情報が記載されています。 apiVersion : dapr.io/v1alpha1 # Daprのリソース定義のバージョン kind : Component # 設定ファイルの種別 metadata : name : orderpubsub # アプリから参照するコンポーネント名 spec : type : pubsub.redis # コンポーネントの種別 version : v1 # コンポーネントのバージョン metadata : - name : redisHost # Redisのホスト名とポート value : localhost : 6379 - name : redisPassword # Redisのパスワード value : "" StateManagementと同じところは説明を省略します。ここで新たに説明する必要があるのは、spec.typeがpubsub.redisになっていることです。これにより、DaprはPublish and SubscribeコンポーネントとしてRedisを使用するようになります。 順番が前後しますが、metadata.nameが「orderpubsub」であることも重要です。これにより、アプリケーションは「orderpubsub」という名前のPublish and Subscribeコンポーネントを参照して、メッセージを公開したり、メッセージを受け取ったりすることができます。 ■ publisher/app.py publisher/app.pyは、DaprのPublish and Subscribeコンポーネントを利用してメッセージを公開するためのアプリケーションコードです。このコードは、 トピックにメッセージを発行する for i in range ( 1 , 10 ) : order = { "orderId" : i } requests . post ( "http://localhost:3613/v1.0/publish/orderpubsub/orders" , json = order ) logging . info ( "送信データ: " + json . dumps ( order ) ) time . sleep ( 1 ) このコードは、1から9までの数字をorderIdとするJSONオブジェクト(以下参照)を作成し、DaprのPublish and Subscribe APIにPOSTリクエストを送っています。 { "orderId" : 1 } リクエストのURLには、先程のコンポーネント定義で指定した「orderpubsub」が含まれています。これにより、Daprは「orderpubsub」という名前のPublish and Subscribeコンポーネントを参照して、Redisにメッセージを公開します。リクエストのURLの構成は以下の通りです。 http://localhost:3613: Daprサイドカーのエンドポイント /v1.0: Dapr APIのバージョン /publish: Publish and Subscribe APIを呼び出すことを示すパス /orderpubsub: 先程のコンポーネント定義で指定した「orderpubsub」という名前のPublish and Subscribeコンポーネントを参照するためのパス /orders: メッセージのトピックを指定するためのパス トピックとは、メッセージを分類するための名前のことです。Publisherは、メッセージを公開するときに、どのトピックに公開するかを指定します。そして、Subscriberは、どのトピックからメッセージを受け取るかを指定します。これにより、PublisherとSubscriberは、特定のトピックに対してメッセージを公開したり、受け取ったりすることができます。 ■ subscriber/app.py subscriber/app.pyは、DaprのPublish and Subscribeコンポーネントを利用してメッセージを受け取るためのアプリケーションコードです。 ソースコードの説明に入る前に、Subscriberがメッセージを受信するまでの流れを説明します。 まず、Subscriberのサイドカーは、/dapr/subscribeエンドポイントを通じて、Subscriberがどのトピックからメッセージを受け取るかをDaprに通知します。 Subscriberのサイドカーは、Redisからメッセージを受け取ると、Subscriberのエンドポイントである「/トピック名」(この例では/orders)に対して、メッセージを送信します。 では、上記を踏まえてソースコードの主要な部分を説明します。 from flask import Flask , request , jsonify import json app = Flask ( __name__ ) 最初にFlaskをインポートして、Flaskアプリケーションのインスタンスを作成しています。Flaskは、PythonでWebアプリケーションを作成するためのフレームワークです。 先ほど説明したように、Subscriberのサイドカーは、Subscriberの特定のHTTPエンドポイントに対してアクセスしてくるので、SubscriberはHTTPサーバーを立てる必要があります。Flaskは、そのHTTPサーバーを簡単に立てることができるため、ここではFlaskを使用しています。Flask以外のHTTPサーバーフレームワークを使用しても問題ありません。 @app . route ( "/dapr/subscribe" , methods = [ "GET" ] ) def subscribe ( ) : subscriptions = [ { "pubsubname" : "orderpubsub" , "topic" : "orders" , "route" : "orders" } ] return jsonify ( subscriptions ) このコードは、Subscriberのサイドカーが/dapr/subscribeエンドポイントにアクセスしたときに呼び出される関数を定義しています。この関数は、Subscriberがどのトピックからメッセージを受け取るかをDaprに通知するためのものです。 つまり、この関数は、Daprに対して、Subscriberが「orderpubsub」という名前のPublish and Subscribeコンポーネントの「orders」というトピックからメッセージを受け取ることを通知しています。そして、Daprは、Subscriberのサイドカーが「orders」というエンドポイントに対して、メッセージを送信するようになります。 @app . route ( "/orders" , methods = [ "POST" ] ) def orders_subscriber ( ) : event_orderid = request . json [ "data" ] [ "orderId" ] print ( "受信データ: " + json . dumps ( event_orderid ) , flush = True ) return json . dumps ( { "success" : True } ) , 200 , { "ContentType" : "application/json" } このコードは、Subscriber側のサイドカーから「/orders」エンドポイントにリクエストが送られたときに実行される関数を定義しています。 このエンドポイントは、/dapr/subscribe エンドポイントでDaprに登録した「orders」トピックに対応しており、そのトピックにメッセージが発行されると、Daprのサイドカーによってこの関数が呼び出されます。 つまり、この関数は「orders」トピックに発行されたメッセージを受信し、処理するためのエンドポイントとして動作します。 この関数の中では、リクエストのJSONボディからorderIdを抽出し、それをコンソールに出力しています。そして、最後に、HTTPレスポンスとして、成功を示すJSONオブジェクトを返しています。 app.run(port=6104) このコードは、Flaskアプリケーションをポート6104で起動しています。これにより、Subscriberはポート番号6104でHTTPリクエストを受け付けるようになります。Publisherがメッセージを公開すると、Daprのサイドカーはこのポートに対してリクエストを送るため、Subscriberはこのポートでリクエストを受け取る必要があります。 実行方法 では次に、Publish and Subscribeコンポーネントを利用するためのアプリケーションコードを実行してみましょう。 まずは必要なライブラリをインストールします。 $ pip install -r publisher/requirements.txt $ pip install -r subscriber/requirements.txt 次に、Dapr CLIを使用してSubscriber及びSubscriberのサイドカープロセスを起動します。 $ dapr run --app-id pubsub --app-port 6104 --dapr-http-port 3614 --components-path Infrastructure/components -- python subscriber/app.py 上記のコマンドの詳細を説明します。 –app-idオプションでアプリケーションのIDを指定します。ここでは「pubsub」というIDを指定しています。このIDは、Publish and Subscribeでは特に利用しませんが、Service Invocationなどの機能を利用する際に、このIDが重要になります。 –app-portオプションでSubscriberのアプリケーションのポート番号を指定します。ここでは6104を指定しています。これにより、Subscriberはlocalhost:6104で待ち受けるようになります。 –dapr-http-portオプションでSubscriberのDaprサイドカーのHTTPポートを指定します。ここでは3614を指定しています。これにより、Daprサイドカーはlocalhost:3614で待ち受けるようになります。 –components-pathオプションで、Daprのコンポーネント定義ファイルが配置されているディレクトリを指定します。ここではInfrastructure/componentsを指定しています。これにより、Daprサイドカーはこのディレクトリの中にあるコンポーネント定義ファイルを読み込むようになります。 — python subscriber/app.pyは、SubscriberのDaprサイドカーが起動した後に実行するコマンドを指定しています。ここでは、subscriber/app.pyを実行するように指定しています。つまり、Subscriberのアプリケーションを実行するように指定しています。 次に、Publisher及びPublisherのサイドカープロセスを起動します。 $ dapr run --app-id pubsub --dapr-http-port 3613 --components-path Infrastructure/components -- python publisher/app.py 上記のコマンドの詳細を説明します。 –app-idオプションでアプリケーションのIDを指定します。ここでは「pubsub」というIDを指定しています。このIDは、Publish and Subscribeでは特に利用しませんが、Service Invocationなどの機能を利用する際に、このIDが重要になります。 –dapr-http-portオプションでPublisherのDaprサイドカーのHTTPポートを指定します。ここでは3613を指定しています。これにより、Daprサイドカーはlocalhost:3613で待ち受けるようになります。 –components-pathオプションで、Daprのコンポーネント定義ファイルが配置されているディレクトリを指定します。ここではInfrastructure/componentsを指定しています。これにより、Daprサイドカーはこのディレクトリの中にあるコンポーネント定義ファイルを読み込むようになります。 — python publisher/app.pyは、PublisherのDaprサイドカーが起動した後に実行するコマンドを指定しています。ここでは、publisher/app.pyを実行するように指定しています。つまり、Publisherのアプリケーションを実行するように指定しています。 このコマンドを実行すると、Publisherがメッセージを公開し、Subscriberがそのメッセージを受信する様子を確認することができます。Subscriberのコンソールには、Publisherが公開したメッセージが以下のように表示されるはずです。 == APP == 受信データ: 1 == APP == 127.0 .0.1 - - [ 28 /Mar/2026 01:52:45 ] "POST /orders HTTP/1.1" 200 - == APP == 受信データ: 2 == APP == 127.0 .0.1 - - [ 28 /Mar/2026 01:52:45 ] "POST /orders HTTP/1.1" 200 - == APP == 受信データ: 3 == APP == 127.0 .0.1 - - [ 28 /Mar/2026 01:52:45 ] "POST /orders HTTP/1.1" 200 - == APP == 受信データ: 4 == APP == 127.0 .0.1 - - [ 28 /Mar/2026 01:52:45 ] "POST /orders HTTP/1.1" 200 - == APP == 受信データ: 5 == APP == 127.0 .0.1 - - [ 28 /Mar/2026 01:52:45 ] "POST /orders HTTP/1.1" 200 - == APP == 受信データ: 6 == APP == 127.0 .0.1 - - [ 28 /Mar/2026 01:52:45 ] "POST /orders HTTP/1.1" 200 - == APP == 受信データ: 7 == APP == 127.0 .0.1 - - [ 28 /Mar/2026 01:52:45 ] "POST /orders HTTP/1.1" 200 - == APP == 受信データ: 8 == APP == 127.0 .0.1 - - [ 28 /Mar/2026 01:52:45 ] "POST /orders HTTP/1.1" 200 - == APP == 受信データ: 9 == APP == 127.0 .0.1 - - [ 28 /Mar/2026 01:52:45 ] "POST /orders HTTP/1.1" 200 - Bindings Bindingsコンポーネントとは、アプリに入ってくるデータと、アプリから出ていくデータを処理するためのコンポーネントです。 システム構成 例えば、RabbitMQにデータが入ってきたときに、そのデータを処理して、処理した結果をPostgreSQLに保存したいとします。このような場合に、Bindingsコンポーネントが役立ちます。 前のコンポーネントと同じように、Bindingsコンポーネントも、アプリ側はRabbtiMQやPostgreSQLのことを知らなくても、DaprのAPIを通じて、RabbitMQからデータを受け取ったり、PostgreSQLにデータを保存したりすることができます。これも、Daprのメリットの一つである、インフラレイヤーの抽象化による柔軟性の高さです。 ① Publisher(キューにメッセージを登録する側)が、RabbitMQに対して、メッセージを登録します。 ② RabbitMQは、サイドカーにメッセージを登録します。 ③ サイドカーは、RabbitMQからメッセージを受け取ると、アプリが待ち受けている特定のHTTPエンドポイントに対して、メッセージを送信します。 ④ アプリは、そのHTTPエンドポイントでリクエストを受け取ると、処理を実行します。処理が完了したら、アプリは、DaprのAPIを通じて、サイドカーに対して、処理した結果を保存するリクエストを送ります。 ⑤ サイドカーは、コンポーネント定義ファイルを読み取り、PostgreSQLに対して、処理した結果を保存するリクエストを送ります。 サイドカーにデータが入っていくほうをInput Binding、サイドカーからデータが出ていくほうをOutput Bindingと呼びます。 Input Bindingは、ここで紹介したRabbitMQを始め、Azure Event HubsやAWS Kinesisなどのメッセージングサービス、HTTPやgRPCなどのプロトコル、ファイルシステムやFTPなどのストレージサービスなど、様々なものが用意されています。Output Bindingも、ここで紹介したPostgreSQLを始め、Azure Cosmos DBやAWS DynamoDBなどのデータベースサービス、HTTPやgRPCなどのプロトコル、ファイルシステムやFTPなどのストレージサービスなど、様々なものが用意されています。詳細は以下の公式ドキュメントを参照してください。 https://docs.dapr.io/reference/components-reference/supported-bindings/ ファイル構成 Bindingsコンポーネントを利用するためのファイル構成は以下の通りとなります。これらのファイルは先程紹介したGitHubリポジトリ( https://github.com/noriyukitakei/dapr-sample )の中の、Bindingsディレクトリの中に配置されています。 Bindings ├── app.py ├── Infrastructure │ ├── components │ │ ├── binding-mq.yaml │ │ └── binding-sqldb.yaml │ └── db │ ├── docker-compose.yml │ ├── Dockerfile │ └── temperatures.sql ├── publish_temperatures.py └── requirements.txt ソースコードの説明 ■ Infrastructure/components/binding-mq.yaml apiVersion : dapr.io/v1alpha1 # Daprのリソース定義のバージョン kind : Component # 設定ファイルの種別 metadata : name : mq # アプリから参照するコンポーネント名 spec : type : bindings.rabbitmq # コンポーネントの種別 metadata : - name : host # RabbitMQのホスト名とポート value : "amqp://guest:guest@localhost:5672" - name : queueName # RabbitMQのキュー名 value : "dapr-queue" - name : direction # データの流れを指定するためのメタデータ value : "input" 他のコンポーネントと同じところは説明を省略します。ここで新たに説明する必要があるのは、spec.typeがbindings.rabbitmqになっていることです。これにより、DaprはBindingsコンポーネントとしてRabbitMQを使用するようになります。 順番が前後しますが、metadata.nameが「mq」であることも重要です。これにより、アプリケーションは「mq」という名前のBindingsコンポーネントを参照して、メッセージを受信したり、メッセージを送信したりすることができます。 spec.metadataは、RabbitMQに接続するための情報を記載しています。RabbitMQのホスト名とポートを指定するためのhost、RabbitMQのキュー名を指定するためのqueueName、データの流れを指定するためのdirectionという3つのメタデータを定義しています。directionは、データの流れを指定するためのメタデータであり、inputを指定すると、サイドカーはRabbitMQからメッセージを受信するためのBindingsコンポーネントとして動作します。 ■ Infrastructure/components/binding-sqldb.yaml apiVersion : dapr.io/v1alpha1 # Daprのリソース定義のバージョン kind : Component # 設定ファイルの種別 metadata : name : sqldb # アプリから参照するコンポーネント名 spec : type : bindings.postgres # コンポーネントの種別 version : v1 metadata : - name : url # PostgreSQLの接続情報 value : "user=postgres password=docker host=localhost port=5432 dbname=temperatures" - name : direction # データの流れを指定するためのメタデータ value : "output" このファイルは、DaprのBindingsコンポーネントをPostgreSQLに接続するための設定ファイルです。このファイルには、DaprがPostgreSQLに接続するための情報が記載されています。 spec.typeがbindings.postgresになっていることにより、DaprはBindingsコンポーネントとしてPostgreSQLを使用するようになります。 metadata.nameが「sqldb」であることも重要です。これにより、アプリケーションは「sqldb」という名前のBindingsコンポーネントを参照して、メッセージを受信したり、メッセージを送信したりすることができます。 spec.metadataは、PostgreSQLに接続するための情報を記載しています。PostgreSQLの接続情報を指定するためのurl、データの流れを指定するためのdirectionという2つのメタデータを定義しています。directionは、データの流れを指定するためのメタデータであり、outputを指定すると、サイドカーはPostgreSQLにメッセージを送信するためのBindingsコンポーネントとして動作します。 ■ db/docker-compose.yml このファイルは、PostgreSQLをDockerコンテナで起動するためのdocker-composeファイルです。このファイルを使用して、PostgreSQLを簡単に起動することができます。 ■ db/Dockerfile このファイルは、PostgreSQLのDockerイメージを作成するためのDockerfileです。このファイルを使用して、PostgreSQLのDockerイメージを作成することができます。 FROM postgres COPY temperatures.sql /docker-entrypoint-initdb.d/ このDockerfileは、公式のPostgreSQLイメージをベースにしています。そして、temperatures.sqlというSQLファイルを、PostgreSQLの初期化スクリプトが配置されるディレクトリである/docker-entrypoint-initdb.d/にコピーしています。これにより、PostgreSQLが起動するときに、このSQLファイルが実行されて、temperaturesテーブルが作成されます。 ■ db/temperatures.sql このファイルは、PostgreSQLの初期化スクリプトです。このファイルには、PostgreSQLが起動するときに実行されるSQL文が記載されています。ここでは、temperaturesテーブルを作成するSQL文が記載されています \c temperatures ; create table temperatures ( sensorid text , timestamp timestamptz , temperature float ) ; select * from temperatures ; このSQL文は、まずtemperaturesデータベースに接続するための\c temperatures;というコマンドを実行しています。そして、temperaturesテーブルを作成するためのcreate table文を実行しています。最後に、temperaturesテーブルの中身を確認するためのselect文を実行しています。 ■ publish_temperatures.py このファイルは、RabbitMQに対して、温度センサーのデータを送信するためのアプリケーションコードです。 def publish ( ) - > None : data = { "sensorid" : "sensor-1" , "timestamp" : "2026-03-07T10:00:00Z" , "temperature" : 22.5 , } まず、関数publishを定義しています。この関数は、RabbitMQに対して、温度センサーのデータを送信するためのものです。 このコードは、温度センサーのデータを表すJSONオブジェクトを作成しています。このJSONオブジェクトには、sensorid、timestamp、temperatureという3つのフィールドが含まれています。sensoridは、センサーのIDを表す文字列です。timestampは、センサーのデータが記録された日時を表す文字列です。temperatureは、センサーのデータである温度を表す数値です。 params = pika . URLParameters ( "amqp://guest:guest@localhost:5672" ) connection = pika . BlockingConnection ( params ) channel = connection . channel ( ) このコードは、pikaライブラリを使用して、RabbitMQに接続しています。pika.URLParametersを使用して、RabbitMQの接続情報を指定しています。そして、pika.BlockingConnectionを使用して、RabbitMQに接続しています。最後に、connection.channel()を使用して、RabbitMQのチャネルを作成しています。 channel . queue_declare ( queue = "dapr-queue" ) このコードは、RabbitMQのチャネルを使用して、dapr-queueという名前のキューを宣言しています。これにより、dapr-queueという名前のキューがRabbitMQに作成されます。 body = json . dumps ( data ) channel . basic_publish ( exchange = "" , routing_key = "dapr-queue" , body = body , properties = pika . BasicProperties ( delivery_mode = 2 ) , ) このコードは、RabbitMQのチャネルを使用して、dapr-queueという名前のキューに対して、温度センサーのデータを送信しています。exchangeには空文字列を指定することで、デフォルトのエクスチェンジを使用しています。routing_keyには、dapr-queueという名前のキューを指定しています。bodyには、温度センサーのデータをJSON形式で表した文字列を指定しています。そして、propertiesには、メッセージのプロパティを指定しています。ここでは、delivery_mode=2を指定することで、メッセージが永続化されるようにしています。 connection . close ( ) このコードは、RabbitMQへの接続を閉じています。 ■ app.py このファイルは、DaprのBindingsコンポーネントを利用して、RabbitMQからデータを受け取って、PostgreSQLにデータを保存するためのアプリケーションコードです。 app = Flask ( __name__ ) Triggered by Dapr input binding @app . route ( "/mq" , methods = [ "POST" ] ) def process_batch ( ) : このコードは、Flaskを使用して、HTTPサーバーを立てています。そして、/mqというエンドポイントに対してPOSTリクエストが送られたときに呼び出される関数process_batchを定義しています。このエンドポイントの/mqというパスは、先程のコンポーネント定義で指定した「mq」という名前のBindingsコンポーネントを参照するためのパスです。これにより、Daprは「mq」という名前のBindingsコンポーネントを参照して、RabbitMQからメッセージを受信すると、このエンドポイントに対してリクエストを送るようになります。 data = request . get_json ( silent = True ) sql_output ( data ) print ( "Finished processing batch" , flush = True ) return json . dumps ( { "success" : True } ) , 200 , { "ContentType" : "application/json" } 関数process_batchの中身です。まず、リクエストのJSONボディを取得しています。そして、sql_output関数を呼び出して、RabbitMQから受け取ったデータをPostgreSQLに保存しています。最後に、HTTPレスポンスとして、成功を示すJSONオブジェクトを返しています。 def sql_output ( reading ) : # expected keys: sensorid, timestamp, temperature sensorid = reading . get ( "sensorid" ) timestamp = reading . get ( "timestamp" ) temperature = reading . get ( "temperature" ) 先程の関数process_batchの中で呼び出されているsql_output関数です。この関数は、RabbitMQから受け取ったデータをPostgreSQLに保存するためのものです。まず、関数sql_outputを定義しています。この関数は、RabbitMQから受け取ったデータを表すJSONオブジェクトを引数として受け取ります。そして、そのJSONオブジェクトから、sensorid、timestamp、temperatureという3つのフィールドを取得しています。 sqlCmd = ( "insert into temperatures (sensorid, timestamp, temperature) values " + "('%s', '%s', %s)" % ( sensorid , timestamp , temperature ) ) このコードは、PostgreSQLに対して実行するSQL文を作成しています。ここでは、temperaturesテーブルに対して、sensorid、timestamp、temperatureの値を挿入するためのinsert文を作成しています。 payload = { "operation" : "exec" , "metadata" : { "sql" : sqlCmd } } このコードは、DaprのAPIに送るリクエストを定義しています。リクエストの形式は、利用するコンポーネントによって異なるのですが、PostgreSQLは以下の形式になります。 { "operation" : "exec" , "metadata" : { "sql" : "INSERT INTO foo (id, c1, ts) VALUES ($1, $2, $3)" , "params" : "[1, \"demo\", \"2020-09-24T11:45:05Z07:00\"]" } } このリクエストは、operationにexecを指定することで、SQL文を実行することを示しています。そして、metadataの中に、sqlというフィールドを定義して、その中に実行するSQL文を指定しています。ここでは、先程作成したsqlCmdという変数に格納されているSQL文を指定しています。 paramsというフィールドも定義されているのですが、今回は使用しません。paramsは、SQL文の中でプレースホルダを使用する場合に、そのプレースホルダに対応する値を指定するためのフィールドです。今回は、SQL文の中でプレースホルダを使用していないため、paramsは必要ありません。 よって今回送付するリクエストは以下のとおりとなります。 { "operation" : "exec" , "metadata" : { "sql" : "INSERT INTO temperatures (sensorid, timestamp, temperature) VALUES ('sensor-1', '2026-03-07T10:00:00Z', 22.5)" } } resp = requests . post ( "http://localhost:3617/v1.0/bindings/sqldb" , json = payload ) return resp requestsライブラリを使用して、DaprのAPIに対してリクエストを送っています。リクエストのURLには、先程のコンポーネント定義で指定した「sqldb」が含まれています。これにより、Daprは「sqldb」という名前のBindingsコンポーネントを参照して、PostgreSQLに対してSQL文を実行するようになります。リクエストのURLの構成は以下の通りです。リクエストボディには、先程作成したpayloadという変数に格納されているJSONオブジェクトを指定しています。 http://localhost:3617: Daprサイドカーのエンドポイント /v1.0: Dapr APIのバージョン /bindings: Bindings APIを呼び出すことを示すパス /sqldb: 先程のコンポーネント定義で指定した「sqldb」という名前のBindingsコンポーネントを参照するためのパス app.run(port=6107) Flaskアプリケーションをポート6107で起動しています。これにより、アプリはlocalhost:6107でHTTPリクエストを受け付けるようになります。RabbitMQからメッセージが送られてくると、Daprのサイドカーはこのポートに対してリクエストを送るため、アプリはこのポートでリクエストを受け取る必要があります。 実行方法 まずは必要なライブラリをインストールします。 $ pip install -r requirements.txt 次に、PostgreSQLとRabbbitMQのコンテナを起動します。 $ cd Infrastructure/db $ docker-compose up -d Dapr CLIを使用してアプリケーションコードとサイドカープロセスを起動します。 $ dapr run --app-id bindings --app-port 6107 --dapr-http-port 3617 --components-path Infrastructure/components -- python app.py 上記のコマンドの詳細を説明します。 –app-idオプションでアプリケーションのIDを指定します。ここでは「bindings」というIDを指定しています。このIDは、Bindingsコンポーネントでは特に利用しませんが、Service Invocationなどの機能を利用する際に、このIDが重要になります。 –app-portオプションでアプリケーションのポート番号を指定します。ここでは6107を指定しています。これにより、アプリはlocalhost:6107で待ち受けるようになります。 –dapr-http-portオプションでDaprサイドカーのHTTPポートを指定します。ここでは3617を指定しています。これにより、Daprサイドカーはlocalhost:3617でHTTPリクエストを受け付けるようになります。 –components-pathオプションでDaprコンポーネントの定義ファイルが格納されているディレクトリを指定します。ここではInfrastructure/componentsを指定しています。これにより、Daprサイドカーはこのディレクトリの中にあるコンポーネント定義ファイルを読み込むようになります。 — python app.pyは、Daprサイドカーが起動した後に実行するコマンドを指定しています。ここでは、app.pyを実行するように指定しています。つまり、アプリケーションコードを実行するように指定しています。 このコマンドを実行した状態で、publish_temperatures.pyを実行してみましょう。 $ python publish_temperatures.py このコードを実行すると、RabbitMQに温度センサーのデータが送信されます。そして、Daprのサイドカーがそのデータを受け取って、アプリケーションコードに送ります。アプリケーションコードは、そのデータをPostgreSQLに保存します。 本当にデータがPostgreSQLに保存されたかを確認してみましょう。以下のコマンドを実行して、PostgreSQLのコンテナに接続します。 $ docker exec -i postgres psql --username postgres --dbname temperatures -c "select * from temperatures;" sensorid | timestamp | temperature ----------+------------------------+------------- sensor-1 | 2026 -03-07 10 :00:00+00 | 22.5 ( 1 row ) すると、temperaturesテーブルの中に、先程publish_temperatures.pyを実行したときに送信したデータが保存されていることが確認できます。 Service Invocation Service Invocationは、Daprの機能の一つであり、アプリケーションが他のアプリケーションに対してリクエストを送るときに、便利な機能がたくさんあります。 アプリケーションは、他のアプリケーションのこと(IPアドレスなど)を知らなくても、DaprのAPIを通じて、他のアプリケーションに対してリクエストを送ることができます。 アプリケーション同士の通信が失敗しても、サイドカーが自動的にリトライしてくれるため、通信の信頼性が高まります。 サイドカーが、アプリケーション同士の通信をmTLSによって暗号化してくれるため、通信のセキュリティが高まります。 サイドカーが、アプリケーション同士の通信をモニターして、ログやメトリクスなどを収集してくれるため、通信の可観測性が高まります。 つまり、サイドカーがいろんなことをしてくれるので、アプリケーション側では何も考えずに、DaprのAPIを通じて、他のアプリケーションに対してリクエストを送ることができます。これが、Service Invocationの大きなメリットの一つです。 様々な利便性があるService Invocationですが、1の機能に焦点をあてて、具体的な事例(センサーデータを送るアプリケーションから、センサーのデータを受け取って処理するアプリケーションに対してリクエストを送る)を通じて、Service Invocationの使い方を説明していきます。 システム構成 ということで、センサーデータを送るアプリケーションから、センサーのデータを受け取って処理するアプリケーションに対してリクエストを送るシステムを例に挙げて説明します。構成図は以下のとおりです。 ① アプリケーションIDが「telemetry-sender」のアプリケーションは、DaprのAPIを使って、アプリケーションIDが「telemetry-collector」のアプリケーションにリクエストを送ります。 このときのURLは   http://localhost:3615/v1.0/invoke/telemetry-collector/method/telemetry   です。 ここで「3615」は telemetry-sender 側のDaprサイドカーのHTTPポートを表しています。 また、このURLのパスは「telemetry-collector」というアプリケーションの「telemetry」というエンドポイントを呼び出すことを意味します。 ② サイドカーはこのリクエストを受け取ると、mDNS(multicast DNS)を使って、アプリケーションID「telemetry-collector」がどこで動いているか(IPアドレス)を調べます。 ③ すると、telemetry-collector 側のサイドカー内のmDNSリゾルバーが応答し、「自分が telemetry-collector である」と名乗って、自身のIPアドレスなどの情報を返します。 ④ telemetry-sender 側のサイドカーは、その情報をもとに telemetry-collector のサイドカーへリクエストを送ります。 ⑤ telemetry-collector 側のサイドカーは、受け取ったリクエストを実際のアプリケーション(telemetry-collector)に転送します。 このように、アプリケーションID「telemetry-sender」のアプリケーションは、アプリケーションID「telemetry-collector」のアプリケーションのIPアドレスやポート番号を知らなくても、DaprのAPIを通じて、アプリケーションID「telemetry-collector」のアプリケーションに対してリクエストを送ることができます。これが、Service Invocationの大きなメリットの一つです。 ちなみに本記事で紹介している構成(Self-hosted)では、mDNSを使っていますが、AWSのECSやAzureのAKSなどのコンテナオーケストレーションサービスを使用している場合は、mDNSの代わりに、高度なDNSリゾルバーが使用されます。 ファイル構成 Service Invocationコンポーネントを利用するためのファイル構成は以下の通りとなります。これらのファイルは先程紹介したGitHubリポジトリ( https://github.com/noriyukitakei/dapr-sample )の中の、ServiceInvocationディレクトリの中に配置されています。 ServiceInvocation ├── telemetry-collector │ ├── app.py │ └── requirements.txt └── telemetry-sender ├── app.py └── requirements.txt ソースコードの説明 ■ telemetry-collector/app.py app = Flask ( __name__ ) このコードは、Flaskを使用して、HTTPサーバーを立てています。 @app . route ( "/telemetry" , methods = [ "POST" ] ) def telemetry ( ) : data = request . json print ( "Collector received:" , data , flush = True ) return ( json . dumps ( { "ok" : True } ) , 200 , { "ContentType" : "application/json" } , ) このコードは、/telemetryというエンドポイントに対してPOSTリクエストが送られたときに呼び出される関数を定義しています。このエンドポイントは、先程の構成図の中で、アプリケーションID「telemetry-sender」のアプリケーションが呼び出すエンドポイントになります。 アプリケーションID「telemetry-sender」が呼び出すエンドポイントは、先ほど説明した通り、 http://localhost:3615/v1.0/invoke/telemetry-collector/method/telemetry   になります。このURLのパスの最後の部分である「telemetry」が、telemetry-collector 側のアプリケーションのエンドポイントになります。なので、telemetry-collector 側のアプリケーションは、このエンドポイントを定義する必要があります。 telemetry関数の中では、リクエストのJSONボディを取得して、コンソールに出力しています。そして、HTTPレスポンスとして、成功を示すJSONオブジェクトを返すという非常にシンプルな処理をしています。 app . run ( port = 6106 ) Flaskアプリケーションをポート6106で起動しています。これにより、アプリはlocalhost:6106でHTTPリクエストを受け付けるようになります。telemetry-senderからリクエストが送られてくると、Daprのサイドカーはこのポートに対してリクエストを送るため、アプリはこのポートでリクエストを受け取る必要があります。 ■ telemetry-sender/app.py payload = { "sensorId" : "sensor-1" , "temperatureC" : 23 } このコードは、telemetry-collectorに送るデータを表すJSONオブジェクトを作成しています。このJSONオブジェクトには、sensorIdとtemperatureCという2つのフィールドが含まれています。センサーのIDを表すsensorIdと、センサーのデータである温度を表すtemperatureCです。 response = requests . post ( url = "http://127.0.0.1:3615/v1.0/invoke/telemetry-collector/method/telemetry" , data = json . dumps ( payload ) , headers = { "content-type" : "application/json" , } , ) このコードは、requestsライブラリを使用して、DaprのAPIに対してリクエストを送っています。リクエストのURLには、先程の構成図の中で、アプリケーションID「telemetry-sender」のアプリケーションが呼び出すURLである   http://localhost:3615/v1.0/invoke/telemetry-collector/method/telemetry   が指定されています。リクエストボディには、先程作成したpayloadという変数に格納されているJSONオブジェクトを指定しています。 実行方法 まずは必要なライブラリをインストールします。 $ pip install -r telemetry-sender/requirements.txt $ pip install -r telemetry-collector/requirements.txt 次に、telemetry-collectorのアプリケーションコードとサイドカープロセスを起動します。 $ dapr run --app-id telemetry-collector --app-port 6106 --dapr-http-port 3616 -- python telemetry-collector/app.py 上記のコマンドの詳細を説明します。 –app-idオプションでアプリケーションのIDを指定します。ここでは「telemetry-collector」というIDを指定しています。このIDは、Service Invocationでは非常に重要になります。なぜなら、アプリケーションID「telemetry-sender」のアプリケーションが、アプリケーションID「telemetry-collector」のアプリケーションにリクエストを送るときに、このIDを使用して、どのアプリケーションにリクエストを送るかを指定するからです。このアプリケーションIDを元にして、Daprのサイドカーは、mDNSを使って、アプリケーションID「telemetry-collector」がどこで動いているか(IPアドレス)を調べます。 –app-portオプションでアプリケーションのポート番号を指定します。ここでは6106を指定しています。これにより、アプリはlocalhost:6106で待ち受けるようになります。 –dapr-http-portオプションでDaprサイドカーのHTTPポートを指定します。ここでは3616を指定しています。これにより、Daprサイドカーはlocalhost:3616でHTTPリクエストを受け付けるようになります。 — python telemetry-collector/app.pyは、Daprサイドカーが起動した後に実行するコマンドを指定しています。ここでは、app.pyを実行するように指定しています。つまり、アプリケーションコードを実行するように指定しています。 次に、telemetry-senderのアプリケーションコードとサイドカープロセスを起動します。 $ dapr run --app-id telemetry-sender --dapr-http-port 3615 -- python telemetry-sender/app.py 上記のコマンドの詳細を説明します。 –app-idオプションでアプリケーションのIDを指定します。ここでは「telemetry-sender」というIDを指定しています。 –dapr-http-portオプションでDaprサイドカーのHTTPポートを指定します。ここでは3615を指定しています。これにより、Daprサイドカーはlocalhost:3615でHTTPリクエストを受け付けるようになります。 — python telemetry-sender/app.pyは、Daprサイドカーが起動した後に実行するコマンドを指定しています。ここでは、app.pyを実行するように指定しています。つまり、アプリケーションコードを実行するように指定しています。 このコマンドを実行すると、telemetry-sender側のコンソールに以下のような出力がされます。 == APP == Sender sent: {'sensorId': 'sensor-1', 'temperatureC': 23} == APP == Response status: 200 そして、telemetry-collector側のコンソールには以下のような出力がされます。 == APP == Collector received: {'sensorId': 'sensor-1', 'temperatureC': 23} == APP == 127.0.0.1 - - [03/Apr/2026 13:53:42] "POST /telemetry HTTP/1.1" 200 - 上記のようなものが出力されていれば、telemetry-senderのアプリケーションが、telemetry-collectorのアプリケーションに対してリクエストを送ることができていることが確認できます。 Secrets Management Secrets Managementは、Daprの機能の一つであり、アプリケーションがシークレットを安全に管理するための機能です。クラウドのサービスで、シークレットを扱うサービスはたくさんあります。AWSのSecrets ManagerやAzureのKey Vaultなどが有名です。これらのサービスを利用することで、シークレットを安全に管理することができます。 ただし、これらのサービスを使うためには、それぞれの専用のAPIを呼び出したり、SDKを使用したりする必要があります。これらのサービスを利用するためのコードを書くのは、面倒なことが多いです。DaprのSecrets Managementを利用することで、これらのサービスを利用するためのコードを書く必要がなくなります。DaprのAPIを通じて、シークレットを取得することができるようになります。 アプリからはDaprのサイドカーに、「シークレットを取得したい」というリクエストを送ります。すると、サイドカーは、あらかじめ設定されているシークレットストアに対して、シークレットを取得するためのリクエストを送ります。そして、シークレットストアからシークレットが返ってくると、サイドカーは、そのシークレットをアプリに返します。 システム構成 では、シークレットを取得するアプリケーションの例を通じて、Secrets Managementの使い方を説明していきます。構成図は以下のとおりです。 今回ご紹介する例では、AWS Secrets ManagerやAzure Key Vaultではなく、ローカルに配置したJSONファイルをシークレットストアとして使用します。Daprは、ローカルに配置したJSONファイルをシークレットストアとして使用することができます。これにより、AWS Secrets ManagerやAzure Key Vaultなどのクラウドのサービスを利用しなくても、Secrets Managementの機能を試すことができます。 ローカルに配置したJSONファイルをシークレットストアとして使用する方式は、Daprの公式ドキュメントにおいては本番環境では推奨されていません。今回の説明のために、ローカルに配置したJSONファイルをシークレットストアとして使用する方式を紹介していますが、実際のプロジェクトでSecrets Managementの機能を利用する際には、AWS Secrets ManagerやAzure Key Vaultなどのクラウドのサービスを利用することをおすすめします。 ということで先ほどの構成図に基づいて、シークレットを取得するアプリケーションの例を通じて、Secrets Managementの使い方を説明していきます。 ① アプリケーションは、DaprのAPIを使って、シークレットを取得するためのリクエストをサイドカーに送ります。 このときのURLは   http://127.0.0.1:3618/v1.0/secrets/local-secretstore/my-secret   です。このURLの構成を説明します。 http://127.0.0.1:3618 : DaprサイドカーのHTTPポート /v1.0/secrets: シークレット管理のAPIエンドポイント /local-secretstore: 使用するシークレットストアの名前( コンポーネント定義で指定したmetadata.name ) /my-secret: 取得したいシークレットの名前 ② サイドカーはこのリクエストを受け取ると、あらかじめ設定されているシークレットストアに対して、シークレットを取得するためのリクエストを送ります。 ファイル構成 Secrets Managementコンポーネントを利用するためのファイル構成は以下の通りとなります。これらのファイルは先程紹介したGitHubリポジトリ( https://github.com/noriyukitakei/dapr-sample )の中の、SecretsManagementディレクトリの中に配置されています。 SecretsManagement ├── app.py ├── Infrastructure │ └── components │ ├── local-secretstore.yaml │ └── secrets.json ├── README.md └── requirements.txt ソースコードの説明 では、Secrets Managementの機能を利用するためのソースコードを説明していきます。 ■ Infrastructure/components/local-secretstore.yaml apiVersion : dapr.io/v1alpha1 # DaprのAPIバージョン kind : Component # コンポーネントの種類 metadata : name : local - secretstore # コンポーネントの名前 spec : type : secretstores.local.file # コンポーネントの種類を指定 version : v1 metadata : - name : secretsFile # シークレットストアとして使用するJSONファイルのパスを指定するためのメタデータ value : "./Infrastructure/components/secrets.json" # シークレットストアとして使用するJSONファイルのパス このファイルは、DaprのSecrets ManagementコンポーネントをローカルのJSONファイルをシークレットストアとして使用するように設定するためのコンポーネント定義ファイルです。 metadata.nameは、アプリケーションがこのコンポーネントを参照するための名前になります。先ほどご紹介したシステム構成の中で、アプリケーションが送るリクエストのURLは   http://127.0.0.1:3618/v1.0/secrets/local-secretstore/my-secret   でした。このURLの中の「local-secretstore」という部分は、metadata.nameで指定した名前になります。つまり、metadata.nameが「local-secretstore」であることによって、アプリケーションは「local-secretstore」という名前のコンポーネント定義ファイルを参照して、シークレットストアとして使用するJSONファイルのパスを知ることができるようになります。 spec.typeは、コンポーネントの種類を指定するためのフィールドです。ここでは、Daprが提供しているローカルのJSONファイルをシークレットストアとして使用するためのコンポーネントであるsecretstores.local.fileを指定しています。Azure Key Vaultの場合はsecretstores.azure.keyvault、AWS Secrets Managerの場合はsecretstores.aws.secretsmanagerを指定します。 metadataの中には、spec.typeで指定したコンポーネントの種類に応じたメタデータを定義します。ローカルのJSONファイルをシークレットストアとして使用するためのコンポーネントであるsecretstores.local.fileの場合は、secretsFileという名前のメタデータを定義して、そのvalueにシークレットストアとして使用するJSONファイルのパスを指定します。 ■ Infrastructure/components/secrets.json { "my-secret" : "hello-from-file" } このファイルは、ローカルに配置したJSONファイルをシークレットストアとして使用するためのJSONファイルです。このファイルには、シークレットの名前と値のペアが定義されています。ここでは、my-secretという名前のシークレットに対して、hello-from-fileという値が定義されています。 ■ app.py URL = "http://127.0.0.1:3618/v1.0/secrets/local-secretstore/my-secret" print ( requests . get ( URL ) . text ) このコードは、DaprのAPIを使って、シークレットを取得するためのリクエストをサイドカーに送っています。リクエストのURLには、先程の構成図の中で、アプリケーションが送るリクエストのURLである   http://127.0.0.1:3618/v1.0/secrets/local-secretstore/my-secret   が指定されています。リクエストを送ると、サイドカーは、あらかじめ設定されているシークレットストアに対して、シークレットを取得するためのリクエストを送ります。そして、シークレットストアからシークレットが返ってくると、サイドカーは、そのシークレットをアプリに返します。最後に、取得したシークレットをコンソールに出力しています。 実行方法 まずは必要なライブラリをインストールします。 $ pip install -r requirements.txt 次に、Dapr CLIを使用してアプリケーションコードとサイドカープロセスを起動します。 $ dapr run --app-id secretsmanagement --dapr-http-port 3618 --components-path Infrastructure/components -- python app.py .. .略 .. . == APP == { "my-secret" : "hello-from-file" } 上記のコマンドの詳細を説明します。 –app-idオプションでアプリケーションのIDを指定します。ここでは「secretsmanagement」というIDを指定しています。 –dapr-http-portオプションでDaprサイドカーのHTTPポートを指定します。ここでは3618を指定しています。これにより、Daprサイドカーはlocalhost:3618でHTTPリクエストを受け付けるようになります。 –components-pathオプションでDaprコンポーネントの定義ファイルが格納されているディレクトリを指定します。ここではInfrastructure/componentsを指定しています。これにより、Daprサイドカーはこのディレクトリの中にあるコンポーネント定義ファイルを読み込むようになります。 — python app.pyは、Daprサイドカーが起動した後に実行するコマンドを指定しています。ここでは、app.pyを実行するように指定しています。つまり、アプリケーションコードを実行するように指定しています。 このコマンドを実行すると、コンソールに{“my-secret”:”hello-from-file”}と出力されます。これは、DaprのAPIを通じて、シークレットを取得するためのリクエストをサイドカーに送った結果、サイドカーがあらかじめ設定されているシークレットストアに対して、シークレットを取得するためのリクエストを送って、そのシークレットをアプリに返した結果になります。つまり、DaprのSecrets Managementの機能が正常に動作していることが確認できます。 デモアプリ ひと通り、Daprの基本的な機能であるPub/Sub、Bindings、Service Invocation、Secrets Managementについて説明してきました。これらの機能を組み合わせることで、ちょっぴり本格的なマイクロサービスを作ってみましょう。 このアプリのソースコードは以下のリポジトリで公開しております。 https://github.com/noriyukitakei/dapr-demo 機能概要 このデモアプリは、センサーから送られてくるデータをリアルタイムに処理し、可視化やアラート通知を行うシステムです。 まず、センサーのシミュレーターが温度データを生成します。このデータは一度メッセージング基盤に送られ、そこからシステム全体に配信されます。 データを受け取ったゲートウェイの役割を持つサービスは、その内容を保存するとともに、異常値かどうかを判断します。 もし異常な値であれば、別のサービスに通知され、メールなどでアラートが送信されます。 一方で、通常のデータは蓄積され、ダッシュボードサービスから参照できるようになります。ユーザーはブラウザなどからセンサーの状態を確認でき、現在の状況をリアルタイムに把握することができます。 つまりこのシステムは、 データの収集 データの保存 異常検知 通知 可視化 といった一連の処理を、複数のサービスに分けて実現しています。 システム構成 デモアプリのシステム構成は以下のとおりです。 このシステムは3つのマイクロサービスで構成されています。デバイスからセンサーを受信するSensor Gateway Service、異常値を検知してアラートを送るAlert Service、センサーの状態を可視化するDashboard Serviceです。 このデモアプリでは、センサーのシミュレーターは、Sensor Gateway Serviceに対してセンサーデータを送ります。Sensor Gateway Serviceは、そのデータを保存するとともに、異常値かどうかを判断します。もし異常な値であれば、Alert Serviceに通知され、メールなどでアラートが送信されます。一方で、通常のデータは蓄積され、Dashboard Serviceから参照できるようになります。 では、システム構成図の中の①~⑰の流れに沿って、システム全体の動きを説明していきます。 ① センサーのシミュレーターであるSensor Simulatorが、センサーデータを生成する。アラートが上がるようなある一定温度以上のデータを一定確率で生成して、MQTTに送信する。 ② MQTTに送られたデータをSensor Gateway Serviceが受信する。Sensor Gateway Serviceは、DaprのInput Bindings機能を使って、MQTTからデータを受け取るように設定されている。 ③ Sensor Gateway Serviceのサイドカーは、MQTTからデータを受け取ると、そのデータをSensor Gateway Serviceに送る。受け取るためのエンドポイントはInput Bindingsのコンポーネント定義で指定されている。 ④ Sensor Gateway Serviceのサイドカーは、Secrets Managementの機能を使って、機密情報を格納しているデータストア(今回はローカルのJSONファイル)から、アラートのしきい値などを取得する。 ⑤ Sensor Gateway Serviceは、④で受け取ったしきい値を元に、受け取ったセンサーデータが異常値かどうかを判断する。 ⑥ Sensor Gateway Serviceは、受け取ったセンサーデータをRedisに保存する。これはState Managementの機能を使って実現している。 ⑦ Sensor Gateway Serviceは、受け取ったセンサーデータが正常値であればtelemetryトピックに、異常値であればalertトピックに送信する。これはPub/Subの機能を使って実現している。 ⑧ RabbitMQは、alertトピックに送られた異常値のデータをAlert Serviceに配信する。Alert Serviceは、DaprのPub/Sub機能を使って、alertトピックからデータを受け取るように設定されている。 ⑨ Alert Serviceのサイドカーは、alertトピックからデータを受け取ると、そのデータをAlert Serviceに送る。/alertsというエンドポイントに対してPOSTリクエストを送るように設定されている。このエンドポイントは、Alert Serviceのアプリケーションコードの中で定義されている。 ⑩ Alert Serviceは、受け取ったデータを元に、メールなどでアラートを送る。Output Bindingsの機能を使って、メールを送るように設定されている。 ⑪ Alert Serviceのサイドカーは、Output Bindingsの機能を使って、指定されたSMTPサーバーに対してメールを送信する。このメールサーバーは、maildev( https://github.com/maildev/maildev )というローカルで動かすことができるメールサーバーを使用している。 ⑫ 管理者は、DashBoard Serviceが提供しているエンドポイントに対して、センサーの状態を確認するためのリクエストを送る。 ⑬ Dashboard Serviceは、Service Invocationの機能を使って、Sensor Gateway Serviceに対してリクエストを送る。リクエストの内容は、センサーの状態を確認するためのものである。リクエストのURLは、センサーのIDがsensor-001とした場合、   http://localhost:3610/v1.0/invoke/sensorgateway/method/state/sensor-001   である。このURLのパスのsensorgatewayという部分は、Sensor Gateway ServiceのアプリケーションIDである。つまり、Dashboard Serviceは、Service Invocationの機能を使って、Sensor Gateway Serviceに対してリクエストを送るときに、Sensor Gateway ServiceのアプリケーションIDを指定している。state/sensor-001という部分は、Sensor Gateway Serviceのアプリケーションコードの中で定義されているエンドポイントになる。つまり、Dashboard Serviceは、Service Invocationの機能を使って、Sensor Gateway Serviceに対してリクエストを送るときに、Sensor Gateway Serviceのアプリケーションコードの中で定義されているエンドポイントを指定している。 ⑭ DashBoard Serviceのサイドカーは、mDNSを使って、Sensor Gateway Serviceのサイドカーがどこで動いているか(IPアドレス)を調べて、Sensor Gateway Serviceのサイドカーにリクエストを送る。 ⑮ Sensor Gateway Serviceのサイドカーは、受け取ったリクエストをSensor Gateway Serviceの/state/{sensor_id}というエンドポイントに転送する。 ⑯ Sensor Gateway Serviceは、受け取ったリクエストを元に、Sate Gateway ServiceのState Managementの機能を使って、Redisに保存されているセンサーの状態を取得する。 ⑰ Sensor Gateway Serviceのサイドカーは、Redisにアクセスしてセンサーの状態を取得した後、その状態をDashboard Serviceに返す。 ファイルの構成 デモアプリのファイル構成は以下のとおりです。 . ├── AlertService │   ├── alertservice │   │   └── app.py │   └── requirements.txt ├── DashboardService │   ├── dashboard │   │   └── app.py │   └── requirements.txt ├── Infrastructure │   ├── components │   │   ├── mqtt-binding.yaml │   │   ├── notify-binding.yaml │   │   ├── pubsub.yaml │   │   ├── secretstore.yaml │   │   └── statestore.yaml │   ├── docker-compose.yml │   ├── mosquitto.conf │   └── secrets.json ├── SensorGatewayService │   ├── requirements.txt │   └── sensorgateway │   ├── app.py │   └── settings.py ├── Simulation │   ├── requirements.txt │   └── simulate.py └── common    ├── pyproject.toml    └── smartfarm_common    └── models.py それぞれのファイルの内容をサービスごとに説明します。 Sensor Gateway Service ■ sensorgateway/app.py センサーのデータを受信して処理するサービスです。センサーデータを受信して、異常値かどうかを判断し、データを保存したり、異常値であればアラートを送ったりします。 Input Bindingsを使ってMQTTからデータ受信 State Managementを使ってデータ保存(Redis) Pub/Subでイベント発行(RabbitMQに正常データ、異常データをそれぞれ別のトピックに送る) DashBoard Serviceからのリクエストに応じて、センサーの状態を返すエンドポイントを提供 ■ sensorgateway/settings.py アラートのしきい値など各種設定を管理するファイルです。DaprのSecrets Management機能を使って、ローカルのJSONファイルから設定値を取得するようになっています。 Alert Service ■ alertservice/app.py アラート通知を担当するサービスです。異常なセンサーデータを検知した際に、通知処理を行います。 メッセージキュー(RabbitMQ)から異常データ(alertトピックに送られたデータ)を受信 異常時にOutput Bindingsの機能を使って、メールで通知 Dashboard Service ■ dashboard/app.py ユーザー向けにデータを表示するサービスです。以下の機能を提供します。 /sensors/{sensor_id}というAPIエンドポイントを提供して、センサーの状態を返却 Infrastructure ■ components/mqtt-binding.yaml MQTTからデータを受信するためのInput Bindingsのコンポーネント定義ファイルです。 ■ components/notify-binding.yaml メールを送るためのOutput Bindingsのコンポーネント定義ファイルです。maildevというローカルで動かすことができるメールサーバーに対して、SMTPプロトコルを使ってメールを送るように設定されています。 ■ components/pubsub.yaml RabbitMQをPub/Subのコンポーネントとして使用するためのコンポーネント定義ファイルです。 ■ components/secretstore.yaml ローカルのJSONファイルをSecrets Managementのシークレットストアとして使用するためのコンポーネント定義ファイルです。 ■ components/statestore.yaml RedisをState Managementのステートストアとして使用するためのコンポーネント定義ファイルです。 ■ docker-compose.yml RabbitMQ、mosquitto(MQTTブローカー)、maildev(SMTPサーバー)をDockerコンテナで起動するためのdocker-composeファイルです。 ■ mosquitto.conf mosquitto(MQTTブローカー)の設定ファイルです。MQTTのポート番号や、認証の設定などが記述されています。 ■ secrets.json DaprのSecrets Managementの機能を使って、ローカルのJSONファイルをシークレットストアとして使用するためのJSONファイルです。アラートのしきい値などの設定値が定義されています。 Simulation ■ simulate.py センサーのシミュレーターです。センサーデータを生成して、MQTTに送る役割を持っています。 common ■ smartfarm_common/models.py このファイルは、Sensor Gateway Service、Alert Service、Dashboard Serviceの3つのサービスで共通して使用するモデルクラスを定義しています。センサーデータの構造などを定義しています。 実行方法 このデモアプリを実行するための方法を説明します。以下の手順に従ってください。 事前準備 既にDapr CLIをインストールしている場合は、Dapr CLIのインストールはスキップしていただいて構いません。まだインストールしていない場合は、以下のコマンドを実行して、Dapr CLIをインストールしてください。 $ wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash $ dapr init リポジトリのクローンとディレクトリ移動 このデモアプリが公開されているGitHubリポジトリをクローンして、プロジェクトのディレクトリに移動します。 $ git clone https://github.com/noriyukitakei/dapr-demo.git $ cd dapr-demo 必要なDockerコンテナの起動 デモアプリの稼働に必要な3つのコンテナ(RabbitMQ、mosquitto、maildev)をDocker Composeを使って起動します。 $ cd Infrastructure $ docker-compose up -d Sensor Gateway Serviceの起動 Pythonの仮想環境を作成して有効にします。 $ cd .. /SensorGatewayService $ python -m venv .venv $ source .venv/bin/activate 必要なライブラリをインストールします。 $ pip install -r requirements.txt Sensor Gateway Serviceのアプリケーションとサイドカープロセスを起動します。 $ dapr run --app-id sensorgateway --app-port 6100 --dapr-http-port 3610 --components-path .. /Infrastructure/components -- python -m uvicorn sensorgateway.app:app --port 6100 Alert Serviceの起動 Sensor Gateway Serviceはフォアグラウンドで起動したままにしておいてください。新しいターミナルを開いて、Alert Serviceを起動します。 Pythonの仮想環境を作成して有効にします。 $ cd [ GitHubリポジトリのクローン先ディレクトリ ] /AlertService $ python -m venv .venv $ source .venv/bin/activate 必要なライブラリをインストールします。 $ pip install -r requirements.txt Alert Serviceのアプリケーションとサイドカープロセスを起動します。 $ dapr run --app-id alertservice --app-port 6101 --dapr-http-port 3611 --components-path .. /Infrastructure/components -- python -m uvicorn alertservice.app:app --port 6101 Dashboard Serviceの起動 Sensor Gateway ServiceとAlert Serviceはフォアグラウンドで起動したままにしておいてください。新しいターミナルを開いて、Dashboard Serviceを起動します。 Pythonの仮想環境を作成して有効にします。 $ cd [ GitHubリポジトリのクローン先ディレクトリ ] /DashboardService $ python -m venv .venv $ source .venv/bin/activate 必要なライブラリをインストールします。 $ pip install -r requirements.txt Dashboard Serviceのアプリケーションとサイドカープロセスを起動します。 $ dapr run --app-id dashboardservice --app-port 6102 --dapr-http-port 3612 --components-path .. /Infrastructure/components -- python -m uvicorn dashboard.app:app --port 6102 シミュレーターの起動と動作確認 Sensor Gateway ServiceとAlert ServiceとDashboard Serviceはフォアグラウンドで起動したままにしておいてください。新しいターミナルを開いて、センサーのシミュレーターを起動します。 Pythonの仮想環境を作成して有効にします。 $ cd [ GitHubリポジトリのクローン先ディレクトリ ] /Simulation $ python -m venv .venv $ source .venv/bin/activate 必要なライブラリをインストールします。 $ pip install -r requirements.txt シミュレーターを起動します。すると、定期的にセンサーデータが生成されてMQTTに送られます。 $ python simulate.py Publishing telemetry to mqtt://localhost:1883 topic = farm/telemetry sent: { 'sensorId' : 'sensor-003' , 'temperature' : 29.89 , 'humidity' : 60.44 , 'timestamp' : '2026-04-07T00:44:04.142516+00:00' } sent: { 'sensorId' : 'sensor-002' , 'temperature' : 24.09 , 'humidity' : 60.69 , 'timestamp' : '2026-04-07T00:44:05.145853+00:00' } sent: { 'sensorId' : 'sensor-002' , 'temperature' : 33.83 , 'humidity' : 41.07 , 'timestamp' : '2026-04-07T00:44:06.147417+00:00' } sent: { 'sensorId' : 'sensor-003' , 'temperature' : 22.22 , 'humidity' : 35.16 , 'timestamp' : '2026-04-07T00:44:07.149945+00:00' } Alert Serviceのコンソールには、異常値が検知されたときに以下のような出力がされます。 == APP == WARNING:alertservice:ALERT received: Temperature 42 .27C exceeds threshold 40 .0C == APP == INFO: 127.0 .0.1:53585 - "POST /alerts HTTP/1.1" 200 OK == APP == WARNING:alertservice:ALERT received: Temperature 44 .84C exceeds threshold 40 .0C == APP == INFO: 127.0 .0.1:53660 - "POST /alerts HTTP/1.1" 200 OK メールが送付されているはずですので、見てみましょう。maildevは、http://localhost:1080でWeb UIが提供されているので、ブラウザでアクセスしてみてください。すると、Alert Serviceから送られたメールが届いていることが確認できます。 DashBoard Serviceにアクセスして、センサーの状態を確認してみましょう。センサーのIDがsensor-001とした場合、以下のURLにアクセスしてみてください。 $ curl http://localhost:6102/sensors/sensor-001 { "found" :true, "state" : { "sensorId" : "sensor-001" , "temperature" :31.88, "humidity" :45.29, "timestamp" : "2026-04-07T00:51:40.901649Z" , "updatedAt" : "2026-04-07T00:51:40.906840Z" } } 上記のようなレスポンスが返ってくれば、Dashboard ServiceがSensor Gateway Serviceに対してリクエストを送って、センサーの状態を取得することができていることが確認できます。 まとめ 今回は、Daprの基本的な機能であるPub/Sub、Bindings、Service Invocation、Secrets Managementについて、わかりみ深く説明しました。これらの機能を組み合わせることで、ちょっぴり本格的なマイクロサービスを作ることができることも紹介しました。 Daprは、マイクロサービスを開発するための便利な機能を提供しているので、ぜひ活用してみてください。Daprを使うことで、マイクロサービスの開発がより簡単になり、開発者はビジネスロジックの実装に集中することができるようになります。Daprは、マイクロサービスの開発を加速させるための強力なツールであると言えるでしょう。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 世界一わかりみの深いDapr first appeared on SIOS Tech Lab .
こんにちは、伊藤です。 Exchange Online経由でメールを送信するサーバを構築しましたので、今回はその手順をまとめました。 Exchange Online経由とする利点 Exchange Onlineを経由することで、以下の利点があります。 スパム対策などのExchange Onlineのメールフィルタ機能(Exchange Online Protection)を活用可能 Exchange管理センターのメール追跡機能でメール送信を確認できるようになり、メール送信の管理が簡単 メール送信時のDKIM署名をExchange Onlineに集約可能 前提条件 メールサーバに固定のパブリックIPアドレスが割り当てられている必要があります。 送信元のメールドメインは、事前にMicrosoft 365管理センターでセットアップを完了させ、Exchange OnlineとDKIMを有効にしておく必要があります。 検証環境 パブリックIPアドレスを持つRHEL10サーバ(OS:Red Hat Enterprise Linux release 10.1) Exchange Onlineが利用可能なMicrosoft Entra IDテナント Exchange管理センターの設定 Exchange管理センターでは、メールサーバからメールを受信するコネクタを以下の手順で設定します。 1. Exchange管理センター( https://admin.exchange.microsoft.com/ )にログインし、「メールフロー」>「コネクタ」>「コネクタの追加」を選択します。 2. 接続先は「組織のメール サーバー」を選択します。接続先は自動的に「Office 365」が選択されます。設定完了後に「次」を選択します。 3. コネクタ名と機能を設定します。設定完了後に「次」を選択します。 コネクタの保存後の設定のうち、「オンにする」のチェックボックスで、コネクタの有効・無効を設定します。 また、「内部Exchangeメールヘッダーを保持する」は無効(チェックなし)とします。この設定項目は、主に送信元がExchange(Exchange Serverや別のExchange Onlineなど)の場合に利用される項目です。電子メールアドオンサービスに送信されるメッセージの内部ヘッダーを保持することで、該当のメッセージが信頼された内部メッセージとして扱われるようになります。 4. 送信メールの認証を設定します。「送信側サーバーのIPアドレスが、あなたの組織にのみ属している次のIPアドレスのいずれかと一致すること」を設定した上で、メールサーバのパブリックIPアドレスを追加します。追加完了後に「次」を選択します。 5. 設定内容を確認の上、「コネクタを作成」を選択します。 6. コネクタ一覧に表示されることを確認して完了です。 参考: https://learn.microsoft.com/ja-jp/exchange/mail-flow-best-practices/use-connectors-to-configure-mail-flow/set-up-connectors-to-route-mail メールサーバの構築 メールサーバを以下の手順で設定します。 1. メールサーバにログインし、Postfixをインストールします。 $ sudo dnf install postfix 2. /etc/postfix/main.cf を設定します。 # デフォルトから以下の設定を追加または変更 # 外部からの通信を受け付ける inet_interfaces = all # ローカル配送を無効 mydestination = # メール送信元許可IPアドレス帯を設定(※1) mynetworks = 127.0.0.0/8, [::1]/128 # Exchange Onlineへリレー(※2) relayhost = [mail-example-com0i1b.mail.protection.outlook.com]:25 smtpd_relay_restrictions = permit_mynetworks, reject_unauth_destination ※1 mynetworksは、環境に合わせて、メールクライアントのIPアドレス帯を設定してください。 ※2 relayhostは、メール送信元ドメインのMXレコードの値(.mail.protection.outlook.comで終わる値)を設定します。メール送信元ドメインが複数想定される場合は、最もよく使用するドメインのMXレコードの値を設定します。 また、メールクライアントからメールサーバへの通信でIPv6を使用しない場合は inet_protocols=ipv4 を設定し、IPv6を無効にしても問題ありません。 3. メールクライアントからメールサーバへの通信でIPv6を使用する場合は、メールサーバから外部への送信時のみIPv6を無効とします。今回の手順ではExchange Online側の受信コネクタでIPv4アドレスによる送信元認証を行っており、IPv6で接続されると認証に失敗するためです。 /etc/postfix/master.cf を以下のように設定します。 # デフォルトから以下の設定を追加 smtp unix - - n - - smtp -o inet_protocols=ipv4 4. Postfixを起動し、自動起動を有効にします。 $ sudo systemctl start postfix $ sudo systemctl enable postfix 必要な通信要件について メールサーバからExchange Onlineへメール送信する際に必要な通信要件は以下の通りです。 通信元 通信先 ポート メールサーバ *.mail.protection.outlook.com, *.mx.microsoft 40.92.0.0/15, 40.107.0.0/16, 52.100.0.0/14, 104.47.0.0/17, 2a01:111:f400::/48, 2a01:111:f403::/48 TCP: 25 通信先は2026年4月時点の内容です。正式情報は、以下URLの内容をご確認ください。 参考: https://learn.microsoft.com/ja-jp/microsoft-365/enterprise/urls-and-ip-address-ranges?view=o365-worldwide メールサーバのSPFの設定について 原則として、すべてのメールがExchange Onlineを経由して送信されるのであれば、外部の受信サーバから見た「送信元IPアドレス」はExchange Onlineのものになるため、 include:spf.protection.outlook.com だけでSPFチェックはパスします。それでもなお、メールサーバのIPアドレスをSPFレコードに追加しておくべき理由は以下の2点です。 1. Exchange Online自身のスパム判定をクリアするため メールサーバからExchange Onlineへメールをリレー(中継)する際、Exchange Onlineが、SPFチェックを行う場合があります。受信コネクタが適切に設定されていれば通常は問題ありませんが、SPFレコードにIPを記載しておくことで、「なりすまし」や「スパム」の誤検知リスクを確実に減らすことができます。 2. 万が一の「直接送信」への保険として 何らかの設定変更やルーティングのトラブルで、メールサーバがExchange Onlineを経由できず、直接インターネット上の宛先へメールを送信してしまった場合(フォールバック)、IPアドレスがSPFレコードにないと、外部の受信側で確実にスパム扱い(SPF Fail)されてしまいます。IPを記載しておくことで、このリスクを回避できます。 SPFレコードを以下のように設定します。 v=spf1 ip4:<メールサーバのIPアドレス> include:spf.protection.outlook.com -all 参考: https://learn.microsoft.com/ja-jp/defender-office-365/email-authentication-spf-configure#scenario-microsoft-365-email-with-on-premises-email-and-a-non-microsoft-email-service メール送信確認 構築したメールサーバ上でtelnetコマンドを使用し、メールの送信テストを行います。 1. telnetコマンドを使用できるようにインストールします。 $ sudo dnf install telnet 2. telnetコマンドで以下のメールを送信します。 $ telnet localhost 25 Trying ::1... Connected to localhost. Escape character is '^]'. 220 testmailserver.localdomain ESMTP Postfix MAIL FROM:<送信元メールアドレス> 250 2.1.0 Ok RCPT TO:<送信先メールアドレス> 250 2.1.5 Ok DATA 354 End data with <CR><LF>.<CR><LF> Subject: test mail From: <送信元メールアドレス> To: <送信先メールアドレス> test mail contents . 250 2.0.0 Ok: queued as E3F1818054B9 quit 221 2.0.0 Bye Connection closed by foreign host. 3. 送信先メールアドレスで受信できることを確認します。 4. Exchange管理センターのメッセージ追跡機能にて、メールの送信を確認します。メールの詳細では、Office 365(Exchange Online)経由で送信されていることを確認できます。 まとめ 今回は、Exchange Onlineを経由してメールを送信するサーバの構築手順と、実際の送信テストについて解説しました。Exchange Onlineを含むメールシステムの構築の参考にしていただければ幸いです。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Exchange Online経由でメールを送信するサーバを構築してみた first appeared on SIOS Tech Lab .
概要 インターネットセキュリティの重要性が日に日に増していく中、 SSL/TLS サーバー証明書の最大有効期間が 2029 年までに段階的に短縮されることが決定しました。 この短命化の波において、今回は「Certbot」というツールに注目し、証明書取得・更新の自動化について記事にさせていただこうと思います。 Certbotは、ACME(Automated Certificate Management Environment)プロトコルを利用して証明書の自動取得・更新を行うためのクライアントツールです。 また、取得した証明書を指定のサーバーへ配置、そしてサービスの再起動(Reload)までも自動化することが可能です。 本記事では、このCertbotサーバを構築する手順を記載していきます。   構成 本記事で構築する環境および証明書発行・設定反映までの流れは図の通りです。 本記事において、Certbotサーバーは図の通り、WEBサーバーなどの証明書を使わせたいサーバーとは別に構築します。 Certbotは証明書を自身のサーバーで取得し、その後各サーバーへ配置させる構成となります。 Certbotサーバの構築手順 構築手順を大きく分けると下記の4ステップとなります。 Certbotのインストール ACME-DNSのインストール・設定 認証スクリプトの用意 デプロイスクリプトの用意 ACME-DNSなど初出のワードが出てきていますが、各ステップの手順にて説明させていただきます。   Certbotのインストール まずは、Certbotをインストールするための基盤(Snap環境)をセットアップ。 # EPELリポジトリが必要な場合(RHEL/Alma/Rockyなど) sudo dnf install epel-release -y # Snapdのインストール sudo dnf install snapd -y # Systemdソケットの有効化と起動 sudo systemctl enable --now snapd.socket # シンボリックリンク作成 sudo ln -s /var/lib/snapd/snap /snap # Snap環境自体のコアパッケージを最新にします。 sudo snap install core sudo snap refresh core 続いてsnap版のCertbotをインストール。 # Snap版Certbotのインストール sudo snap install --classic certbot # certbotコマンドのシンボリックリンクを作成 sudo ln -s /snap/bin/certbot /usr/bin/certbot # バージョン確認 certbot --version なお、snap版のCertbotを取得している理由としては、 Certbotの開発元であるEFF(Electronic Frontier Foundation)が、多くのLinuxディストリビューションにおいてsnap版を利用したインストールを公式に推奨しているからです。 https://eff-certbot.readthedocs.io/en/latest/install.html     続いて、「2. ACME-DNSのインストール・設定」のステップとなりますが、 次回の記事にて解説させていただきます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post SSL/TLS証明書の有効期限短縮に備えて脱・手動更新① first appeared on SIOS Tech Lab .
概要 前回の記事「 SSL/TLS証明書の有効期限短縮に備えて脱・手動更新① 」の続きとなります。 本記事では、Certbotサーバがどのように認証局(CA)から証明書を取得するのか、 そのための手段としてACME-DNSというソフトウェアがどのように関わるのかを説明します。 CertBOTを用いた証明書発行と設定の流れ Certbotが認証局(CA)から証明書を取得しする過程はこちらの図の通りですが、 これは大まかな流れとなります。 CAがCertBOTサーバに対して行うドメイン所有確認の手法には、 HTTP-01チャレンジとDNS-01チャレンジがあります。 HTTP-01 チャレンジは、 Web サーバーを使ってドメインの所有を証明する方式です。 Let’s Encryptなど認証局 は、「このドメインの管理者であるなら、Web サーバー上の特定の場所に指定した内容のファイルを置けるはずだ」という考え方で確認を行います。 ただし、認証局が対象ドメインに対して HTTP(ポート80)でアクセスするため、インターネットから対象ドメインの80番ポートへアクセスできる必要があります。 Webサーバーを公開していない環境や、80番ポートを外部公開できない環境では利用できないという制約があります。 本記事ではそのような制約を受けないDNS-01チャレンジを採用します。 DNS-01チャレンジ DNS-01チャレンジでは、下記のような流れで証明書取得が行われていきます。 HTTP-01チャレンジが「Web サーバーに置いたファイル(トークン)を見に来る」方式なのに対し、DNS チャレンジは「DNS に登録された情報を見に来る」方式です。 HTTP-01チャレンジとDNS-01チャレンジ、それぞれの特徴を比較すると表のようになります。 DNS-01チャレンジは80 番ポートの公開が不要であるため、メールサーバーや LDAPサーバーなど80 番ポートを使わないサーバーを対象にドメインの所有確認ができ、証明書自動取得を行えます。 一方で、DNS-01チャレンジには注意点もあります。一部のDNSサービスでは、そもそもAPIが公開されておらず、Certbotから自動で書き換える手段がありません。 すべての DNS プロバイダがAPI を提供しているわけではないため、APIがない場合はDNS-01 チャレンジの自動化はできず、更新のたびに手作業が必要になります。 その課題に対する対処法として、acme-dns というオープンソースソフトウェアが挙げられます。 ACME-DNSとは ACME-DNSは、DNS-01 チャレンジ認証を実施するための簡易 DNS サーバ、Web API サーバ機能を有するソフトウェアです。 https://github.com/joohoi/acme-dns acme-dns を使う場合、実際の DNS には最初に一度だけ設定を行います。 まず、ドメインを管理するDNS側で、サブドメイン(_acme-challenge)を、CNAMEを使ってACME-DNSサーバーへ向くように設定(委任)します。 これにより、認証局からのドメイン所有確認のクエリは、自動的にACME-DNSサーバーへと転送されるようになります。 また、ACME-DNSサーバーへの問い合わせを委任するための情報として、Aレコード、acme.example.comのNSレコードもメインのDNSに登録しています。 委任に必要なUUIDとはACME-DNSがFQDNごとに発行するもので、 ACME-DNSサーバーのエンドポイントを叩いた際に発行される情報ですが、 それを取得するコマンドはこの後説明させていただきます。 CertbotとACME-DNSを用いて証明書取得を行う具体的な流れは6つのステップです。 (なお、今回構築する環境では、CertbotとACME-DNSの機能が1つのサーバに同居している構成としています) まず、CertbotがACMEサーバに証明書を要求し、それに対してACMEサーバがドメイン所有を確認しようとDNSチャレンジを要求します。 次に、CertbotはACME-DNSのAPIを叩き、取得する証明書のFQDNに対応するUUIDのレコードに検証用トークンを書き込みます。 ACMEサーバがDNS検証を開始します。 メインDNSはCNAME設定に従い、問い合わせをACME-DNSサーバへ転送します。 ACME-DNSがUUIDに基づいた適切なトークンを回答することで検証が成功し、 証明書が発行されます。 結果、DNSのプロバイダにAPI がなくても、DNS チャレンジの自動更新が可能になります。CNAMEレコードさえ設定すればDNSのレコードを手でいじる手間がなくなり自動更新されるものとなります。 ACME-DNSの構築手順 まずは、ACME-DNSのソースコードを取得します。 # ACME-DNSのソースをGithubから取得 $ git clone https://github.com/joohoi/acme-dns # 設定ファイルやデータベース用のディレクトリを作成 $ cd acme-dns $ mkdir data db 次に、オリジナルをコピーした上で、設定ファイルを編集します。 $ cp config.cfg config.org $ vim config.cfg 下記のような内容に編集します。 [general] # DNS interface. Note that systemd-resolved may reserve port 53 on 127.0.0.53 # In this case acme-dns will error out and you will need to define the listening interface # for example: listen = "127.0.0.1:53" #listen = "127.0.0.1:53" listen = "0.0.0.0:53" # protocol, "both", "both4", "both6", "udp", "udp4", "udp6" or "tcp", "tcp4", "tcp6" protocol = "both" # domain name to serve the requests off of domain = "auth.example.org" # acme-dns が管理する DNS ゾーン名 # zone name server nsname = "auth.example.org" # domain で指定したゾーンのネームサーバ名 # admin email address, where @ is substituted with . nsadmin = "admin.example.org" # SOAレコード内の管理者メールアドレス # predefined records served in addition to the TXT records = [ # acme-dns サーバが固定で返す DNS レコードを定義 # domain pointing to the public IP of your acme-dns server auth.example.org. A 198.51.100.1, # specify that auth.example.org will resolve any *.auth.example.org records auth.example.org. NS auth.example.org., ] # debug messages from CORS etc debug = true # デバッグ用の詳細ログを出力するかどうかを指定する項目 [database] # Database engine to use, sqlite3 or postgres engine = "sqlite3" # acme-dns が TXT レコードや登録情報を保存するデータベースの種類を指定 # Connection string, filename for sqlite3 and postgres://$username:$password@$host/$db_name for postgres # Please note that the default Docker image uses path /var/lib/acme-dns/acme-dns.db for sqlite3 connection = "/var/lib/acme-dns/acme-dns.db" # データベースへの接続情報(保存先)を指定 # connection = "postgres://user:password@localhost/acmedns_db" [api] # listen ip eg. 127.0.0.1 #ip = "0.0.0.0" ip = "127.0.0.1" # API サーバが待ち受ける IP アドレスを指定。ACME-DNSサーバー内でAPI叩く構成であれば 127.0.0.1 を指定。 # disable registration endpoint disable_registration = false # /register エンドポイントを無効化するか指定する項目。false を設定することで、新しいサブドメインを ACME-DNS に登録することが可能。 # listen port, eg. 443 for default HTTPS port = "8080" # APIサーバの待受ポート # possible values: "letsencrypt", "letsencryptstaging", "cert", "none" tls = "none" # APIとの通信を HTTPS (TLS) で暗号化するかどうかを指定 # only used if tls = "cert" # tls = "cert" を選んだ際に使用する項目。ACME-DNS の API との通信を暗号化する際に使用する SSL/TLS 証明書のパスを指定。 tls_cert_privkey = "/etc/tls/example.org/privkey.pem" tls_cert_fullchain = "/etc/tls/example.org/fullchain.pem" # only used if tls = "letsencrypt" acme_cache_dir = "api-certs" # tls = "letsencrypt" を選んだ際、API との通信に使用する証明書を保存するディレクトリ # optional e-mail address to which Let's Encrypt will send expiration notices for the API's cert notification_email = "" # tls = "letsencrypt" に設定している場合に使用。ACME-DNS の API との通信を暗号化する際に使用する証明書の期限を通知する先のメールアドレスを指定。 # CORS AllowOrigins, wildcards can be used corsorigins = [ # ブラウザから API を呼び出せる Web ページのドメインを指定する設定。 * ] # use HTTP header to get the client ip use_header = false # 接続してきたクライアントのIPアドレスを特定する際に、HTTPヘッダーの情報を使用するかどうかを指定する項目。 # header name to pull the ip address / list of ip addresses from header_name = "X-Forwarded-For" # use_header = true に設定した場合に、どの名前のHTTPヘッダにIPアドレスが入っているかを指定。 [logconfig] # logging level: "error", "warning", "info" or "debug" loglevel = "debug" # ログの出力レベルを指定 # possible values: stdout, TODO file & integrations logtype = "stdout" # ログの出力先を指定 # file path for logfile TODO # logfile = "./acme-dns.log" # format, either "json" or "text" logformat = "text" # 記録されるログのフォーマットを指定 設定ファイルを編集したら、諸々の準備を行います。 # Go言語の開発環境(コンパイラ)をインストール $ sudo dnf install -y golang # ソースコードから実行可能なプログラムを生成 $ go build # ビルドしたプログラムを、システム全体の実行用フォルダへ配置 $ sudo cp acme-dns /usr/local/bin/ # 配置されたことを確認 $ which acme-dns # 設定ファイル用のディレクトリを root 権限で作成 $ sudo install -d -m 755 -o root -g root /etc/acme-dns # 専用ユーザーの作成(この手順ではacmednsというユーザでACME-DNSを実行させることとしています) getent passwd acmedns || sudo useradd -r -s /sbin/nologin -d /var/lib/acme-dns -M acmedns # 設定ファイルをコピーしつつ、所有者を root、グループを acmedns に設定 # /path/to は git clone を実行ユーザーの home ディレクトリに置き換えます $ sudo rsync -a --chown=root:acmedns /path/to/acme-dns/config.cfg /etc/acme-dns/ # データディレクトリの作成と権限設定 sudo install -d -m 700 -o acmedns -g acmedns /var/lib/acme-dns # データベースファイルの空作成 sudo test -f /var/lib/acme-dns/acme-dns.db || sudo touch /var/lib/acme-dns/acme-dns.db # ファイルの所有権変更 sudo chown acmedns:acmedns /var/lib/acme-dns/acme-dns.db # ファイルのアクセス権制限 sudo chmod 600 /var/lib/acme-dns/acme-dns.db # 一般ユーザー(acmedns)に、特権が必要なポート(53番)を使うための権限を付与 $ sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/acme-dns 次に、ACME-DNSをSystemd化させて常駐化します。 $ sudo vim /etc/systemd/system/acme-dns.service こちらのファイルを下記のように編集します。 [Unit] Description=acme-dns authoritative DNS server for ACME DNS-01 Wants=network-online.target After=network-online.target [Service] User=acmedns Group=acmedns ExecStart=/usr/local/bin/acme-dns -c /etc/acme-dns/config.cfg WorkingDirectory=/var/lib/acme-dns Restart=on-failure RestartSec=2s # 低権限でも 53 を開ける(setcapコマンドを実行しているので不要ですが予備措置として記載) AmbientCapabilities=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BIND_SERVICE NoNewPrivileges=true # ログは journal に流す StandardOutput=append:/var/log/acme-dns/acme-dns.log [Install] WantedBy=multi-user.target 編集後、サービスを読み込んで起動。 $ sudo systemctl daemon-reload $ systemctl restart acme-dns ここまで実行すれば、ACME-DNSの初期設定・構築は完了です。 次に、「3. 認証スクリプトの用意」のステップへ進みますが、次回の記事にて解説させていただきます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post SSL/TLS証明書の有効期限短縮に備えて脱・手動更新② first appeared on SIOS Tech Lab .
WebサイトやアプリケーションのUIをデザインする際、テキストの色や背景色にどのような「黒」を使っていますか? CSSでとりあえず color: #000000; や color: black; と指定しているエンジニアの方も多いかもしれません。しかし、多くのサービスでは、テキストや背景に「完全な黒(#000000)」が使われていません。黒に近いグレーが使われています。 一見すると些細な違いに思えますが、実はこの「黒の選び方」には、人間の視覚や画面の特性に基づいたロジックが存在します。 今回は、なぜUIデザインにおいて「真っ黒」を避けるのか、その論理的な理由と、「良い黒」の作り方、そして例外的な使われ方を解説します。 理由1:コントラストが強すぎると目が疲れる 紙に印刷された黒い文字を読むのとは異なり、私たちが普段見ているPCやスマートフォンのディスプレイは「発光体」です。 真っ白な背景(#FFFFFF)に真っ黒なテキスト(#000000)を配置すると、画面の輝度の最大値(100%の光)と最小値(0%の光)が隣り合うことになります。この極端なコントラストは、目に対して強い刺激を与えます。 アクセシビリティの観点から「コントラスト比は高い方が良い」とされていますが、高ければ高いほど良いというわけではありません。視覚過敏を持つユーザーや、乱視のユーザーにとっても、100%のコントラストは逆に可読性を下げてしまうのです。 理由2:現実世界に「純粋な黒」はほとんど存在しない 色彩心理学や物理学の観点からも、#000000 は特異な色です。 現実世界を見渡してみてください。アスファルト、カラスの羽、黒いスマートフォンケース……私たちが日常で「黒」と認識しているもののほとんどは、光をわずかに反射しており、環境光の影響を受けてかすかに色味を持っています。光の反射率が完全に0%の「純粋な黒」は、光吸収素材のような特殊な状況にしか存在しません。 そのため、人間の脳は画面上の #000000 を見たとき、無意識に「不自然さ」や「重苦しさ」、あるいは画面に穴が空いているような「圧迫感」を感じ取ります。 UIデザインが目指すのは、現実世界の物理法則に寄り添った自然な認知です。自然界に存在しない完全な黒を避けることで、UI全体から人工的な違和感を取り除くことができます。 理由3:ダークモードで「奥行き」が表現できなくなる 近年標準となった「ダークモード」の設計において、背景を #000000 にすることはUIに制約が発生する恐れがあります。 UIデザインは、ボタンやカードなどの要素に「ドロップシャドウ(影)」をつけることで、要素が重なり合っている「Z軸(奥行き)」を表現します。手前にある要素ほど影が大きく、背景から浮き上がって見えます。 しかし、背景色が #000000(これ以上暗くならない色)の場合、その上に要素を置いて影をつけても、背景と同化してしまい影が見えません。結果として、どの要素が手前で、どれがクリック可能なのかといった階層構造(エレベーション)を視覚的に伝えることが難しくなります。 UIの「黒」の作り方 では、実際にコーディングやデザインをする際、どのような「黒」を使えばよいのでしょうか。 1. 無彩色のダークグレー 最もシンプルで失敗がないのは、明度を10パーセント程度上げたダークグレーです。 2. ブランドカラーを混ぜた「リッチブラック」 純粋なグレー(RGBの値がすべて同じ色)ではなく、ブランドのメインカラー(プライマリーカラー)をわずかに混ぜた黒、通称「リッチブラック」を使用する方法です。 例えば、上記の「Jira」のようにプライマリーカラーが「青」のサービスなら、黒にも少しだけ青味を足します。 これにより、画面全体の色調が馴染み、統一感と高級感が生まれます。 あえて「完全な黒(#000000)」を利用するケース ここまで「使うべきではない」と解説してきましたが、 あえて #000000 を利用するケース も存在します。 OLED(有機EL)ディスプレイ機器でのバッテリー節約 有機ELディスプレイは、黒を表示する際に「そのピクセルの発光を完全にオフにする」という仕組みを持っています。例えばスマートフォンアプリにて背景色を#000000とすれば、該当の端末でバッテリー消費を抑えることができます。 ハイコントラストで世界観を伝える ブランドのコンセプトとして、意図的にエッジを効かせ、強烈なコントラストで強い印象を与えたい際に、あえて純粋な黒と白をぶつける場合があります。 アクセシビリティ要件(ハイコントラストモード) 視覚障害を持つユーザー向けにOSレベルで提供される「ハイコントラストモード」では、視認性を最優先するため、#000000 と #FFFFFF の組み合わせが意図的に使用されます。 まとめ:色の設計は、まず「ロジック」 「なぜその『黒』にするのか」を意識するだけで、UIのクオリティは向上します。 目の疲れを防ぐため(コントラストの調整) 自然な認知を促すため(現実世界とのリンク) 奥行きを表現するため(ダークモードの設計) UIデザインはサービスの固有性を表現する要素もありますが、前提として人間の特性やデバイスの仕様に基づいた「ロジック」があります。これにより、適切な配色が可能になります。 Photo by Ricardo Avelar , Kyaw Zay Ya , Wander Fleur , Michael Maasen Eva Wilcock , Jordi Vich Navarro , Vivek Doshi , Aaron Burden on Unsplash ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 配色ロジック:なぜ、UIの「黒」は黒ではないのか first appeared on SIOS Tech Lab .
はじめに PSSLの佐々木です。 AIコーディングアシスタントの進化により、テストコードの自動生成が身近になりました。しかし、ここに大きな落とし穴があります。 AIが実装コードを見ながらテストを書くと、実装のロジックをそのままテストにコピーしてしまう ので仕様の不具合に気づくことができません。 これを「トートロジカルテスト(同義反復テスト)」と呼びます。 // 実装コード function calculateTax(price: number): number { return Math.floor(price * 0.1); } // AIが生成した"テスト"(実装を見て書いた場合) it('税額を計算する', () => { const price = 1000; const expected = Math.floor(price * 0.1); // 実装と同じロジック! expect(calculateTax(price)).toBe(expected); }); このテストは 常に成功 します。なぜなら、実装が間違っていてもテストも同じ間違いをするからです。カバレッジは100%でも、バグは検出できません。 解決策:AIに実装コードを「見せない」 この問題を解決するため、 仕様書のみを参照してテストを生成する ワークフローを構築しました。 Claude Codeのエージェント機能を使い、 テスト生成エージェントには Read ツールを与えない という技術的制約を設けます。 # .claude/agents/test-generator.md --- name: test-generator description: 仕様書のみに基づいてテストを生成する tools: Write, Bash # Readがない = 実装コードを読めない --- これにより、AIは仕様書( spec.md )だけを頼りにテストを書くしかなくなります。 アーキテクチャ全体像 specs/features/{feature}/ ├── spec.md # 仕様書(Given/When/Then形式) └── plan.md # 実装計画 ↓ 並列実行 ┌─────────────────┐ ┌─────────────────┐ │ implementer │ │ test-generator │ │ (tools: 全て) │ │ (tools: Write, │ │ │ │ Bash) │ │ 実装コードを │ │ 仕様書のみ参照 │ │ 自由に読み書き │ │ src/は読めない │ └────────┬────────┘ └────────┬────────┘ │ │ ▼ ▼ src/lib/*.ts src/lib/*.test.ts src/app/**/* e2e/*.spec.ts 仕様書のフォーマット テスト生成の精度を上げるため、仕様書は Given/When/Then形式 で記述します。 ## 受け入れ条件 ### AC-01: 定額割引の適用 - Given: 価格1000円の商品がある - When: クーポンコード「SAVE100」を適用する - Then: 割引後価格は900円になる ### AC-02: パーセント割引の適用 - Given: 価格2000円の商品がある - When: クーポンコード「OFF10」(10%割引)を適用する - Then: 割引後価格は1800円になる この形式により、AIはテストケースを正確に生成できます。 核心となるルール:仕様整合ルール テストが失敗した場合、 どちらを修正するか という判断が重要です。 私たちは「 仕様整合ルール 」を採用しました: テストが失敗し、かつテストが仕様書と整合している場合、 実装を修正する (テストを修正しない) # .claude/rules/src-lib.md ## 仕様整合ルール(Spec-aligned Rule) テスト失敗時の対応: 1. テストが仕様書(spec.md)と整合しているか確認 2. 整合している → 実装を修正 3. 整合していない → テストを修正 これにより、仕様書が「信頼できる唯一の情報源(Single Source of Truth)」として機能します。 実際のワークフロー Step 1: 仕様書を作成 # specs/features/discount-calculator/spec.md ## 概要 クーポンコードを適用して割引価格を計算する機能 ## 受け入れ条件 ### AC-01: 定額割引 - Given: 価格1000円 - When: SAVE100を適用 - Then: 割引後900円 ### AC-04: 無効なクーポン - Given: 価格1000円 - When: INVALIDを適用 - Then: エラー「無効なクーポンコードです」 Step 2: 並列エージェントを実行 # 2つのエージェントを同時に起動 claude --agent implementer "specs/features/discount-calculator/plan.mdに基づいて実装" claude --agent test-generator "specs/features/discount-calculator/spec.mdに基づいてテスト生成" Step 3: テストを実行して結果を確認 npm run test:run npx playwright test Step 4: 失敗があれば仕様整合ルールに従って修正 実際のプロジェクトでは、以下のような失敗がありました: FAIL src/lib/discount.test.ts ✕ SAVE100で100円割引が適用される Expected: 100 Received: 0 テストは仕様書通り「100円割引」を期待しています。実装側のバグなので、実装を修正しました。 GitHub Actions での自動化 CI/CDパイプラインで品質を担保します: # .github/workflows/ci.yml jobs: test: steps: - name: Type check run: npx tsc --noEmit - name: Run unit tests run: npm run test:run - name: Run unit tests with coverage run: npm run test:coverage e2e: steps: - name: Run E2E tests run: npx playwright test さらに、ミューテーションテストで「テストの質」も検証します: # .github/workflows/mutation.yml - name: Run mutation tests run: npm run test:mutation # Stryker 技術スタック 用途 ツール フレームワーク Next.js 15 (App Router) 単体テスト Vitest E2Eテスト Playwright ミューテーションテスト Stryker AIエージェント Claude Code 得られた効果 1. テストが仕様の検証になる 実装を見ずに書かれたテストは、「仕様通りに動くか」を純粋に検証します。 2. バグが確実に検出される 実際に3件の実装バグがテストで検出されました: 割引額の計算ミス クーポン情報の返却漏れ 0円時の割引処理 3. リファクタリングが安全になる テストが仕様に基づいているため、内部実装を変更しても「振る舞いが変わっていないこと」を確認できます。 注意点とトレードオフ 仕様書のメンテナンスコスト 仕様書が古くなると、テストと実装が乖離します。仕様変更時は必ず spec.md を更新する運用が必要です。 E2Eテストのセレクター問題 仕様書だけではDOM構造がわからないため、E2Eテストでは data-testid の命名規則を事前に決めておく必要があります。 # 仕様書に記載 ## UI要素のTestID - 価格入力: `data-testid="price-input"` - 計算ボタン: `data-testid="calculate-button"` - 結果表示: `data-testid="discounted-price"` 初期設定の手間 エージェント設定、ルール定義など初期構築には時間がかかります。ただし、一度作れば再利用可能なテンプレートになります。 まとめ AIによるテスト生成の最大の罠は、「実装を見てテストを書く」ことです。 これを防ぐため: テスト生成エージェントから Read ツールを剥奪 仕様書(Given/When/Then)を信頼できる情報源に 仕様整合ルールでテスト失敗時の判断を明確化 このワークフローにより、AIが生成するテストは「実装の複製」ではなく「仕様の検証」になります。 テンプレートはGitHubで公開しています。ぜひ活用してください。 https://github.com/atomic-kanta-sasaki/-claude-test-driven-template ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AIがテストを書くと「実装をコピーしたテスト」になる問題を解決するテンプレート公開! first appeared on SIOS Tech Lab .
こんにちは、サイオステクノロジーの佐藤 陽です。 今回は、Azure OpenAI Serviceを複数部門や複数プロジェクトで利用する際に、「誰がどれだけ使ったのか」を見える化するための仕組みを作ってみた。という話を書いていこうと思います。 生成 AI の業務利用が広がるにつれて、Azure OpenAI Serviceを複数部門や複数プロジェクトで共用するケースは珍しくないかと思います。 一方で、運用を始めるとすぐに出てくるのが、誰がどれだけ使ったのかをどう見える化するか、という課題です。 全体の利用額だけではなく、部門別にも見たい 案件やプロジェクト単位でも利用料金を把握したい といった方には、是非最後までご覧ください! (記事の最後にはサンプルコードを記載しています) はじめに LLM のコストの考え方は、従来の Web アプリケーションとは少し異なります。 従量課金であることに加えて、利用されるトークン数を事前に見積もりづらいため、想定外の利用料金が発生することがあります。 こうした状況に備えて、LLM のコストを意識しておくことは非常に大切です。 ただし、全体の利用額だけが分かっても、そのままでは使いづらいことがあります。 Azure Portal のコスト管理ツールでも全体傾向は確認できますが、基本的にはリソース単位での集計になるため、1 つの Azure OpenAI リソースを複数部門や複数プロジェクトで共用している場合には、その内訳までは見えません。 そこで今回は、Azure OpenAI Serviceの利用料を部門別・プロジェクト別に可視化する仕組みを作りました。 本記事では、その設計意図、実装のポイント、実際に得られた知見をまとめます。 背景と課題 単一のAzure OpenAI Serviceを複数のアプリケーションや組織単位で共用すると、インフラとしてはシンプルですが、利用責任の所在が見えにくくなります。 たとえば、ある月に利用額が増えたとしても、次のような内容にはたどり着くことが難しいです。 どの部門が多く使っていたのか その部門の中でどのプロジェクトが増えていたのか どのモデル利用がコストを押し上げていたのか そこで、このあたりを見える化するため、まず次の 2 点で整理することにしました。 部門(department_id) プロジェクト(project_id) 例えば人事総務部(department_id)といった部門や、社内DX推進プロジェクト(project_id)といったような分類となります。 この 2 つを Azure OpenAI Service の利用料と紐付けられれば、部門別にも、プロジェクト別にも、比較的シンプルにコスト可視化ができます。 なお今回はこのような分け方を行いましたが、もちろんユーザー単位などでも分割することも可能で、そのあたりは定義・設計次第で自由に設定可能です。 何を作ったのか 今回作ったのは、Azure OpenAI Service を背後に持つチャットバックエンド兼テレメトリ BFF です。 アーキテクチャ 以下の流れで実装します。 クライアントがチャットリクエストを送る リクエストには department_id と project_id を含める Azure Functions が Azure OpenAI Service に接続する Azure OpenAI Service のストリーミング最終チャンクから usage(利用料) を取り出す Functions が usage_telemetry という custom event を Application Insights に送る Log Analytics で KQL 集計する Workbook でグラフ表示する usage をどう回収したか Azure OpenAI のチャット補完をストリーミングで使う場合、usage(利用料) は最終チャンクに含まれます。 そのため、単にレスポンスを受け取るだけではなく、最後までストリームを読み切る必要があります。 公式ドキュメント において、 chatCompletionStreamOptionsのinclude_usageの値を Trueにすると、 `data: [DONE]` の直前に usage を持つ追加チャンクが返されると説明されています。 今回の実装でもこのオプションを有効にし、最終チャンクから usage を回収しました。 usage有効化 async def stream_chat_with_usage(     create_stream: StreamFactory,     **request: Any, ) -> AsyncIterator[Chunk]:     stream_options = dict(request.get("stream_options") or {})     stream_options["include_usage"] = True     request["stream_options"] = stream_options     request["stream"] = True     stream_or_awaitable = create_stream(**request)     if inspect.isawaitable(stream_or_awaitable):         stream = await stream_or_awaitable     else:         stream = stream_or_awaitable     async for chunk in stream:         yield chunk レスポンスの usage には主に次のような項目があります。 prompt_tokens: プロンプト内のトークンの数。 completion_tokens: 生成された入力候補内のトークンの数。 total_tokens: 要求内で使われたトークンの合計数 (プロンプトとcompletionの和)。 completion_tokens_details: 出力トークンの詳細。モデルによっては reasoning_tokens  のような詳細項目が返ります。 reasoning_tokens 推論のためにモデルによって生成されたトークン。 prompt_tokens_details: 入力トークンの詳細。プロンプトキャッシュが有効なモデルでは、この下に cached_tokens  が含まれます。 cached_tokens: キャッシュされたプロンプト トークンの数。 レスポンスのイメージは次のようになります。 "usage": {   "prompt_tokens": 1566,   "completion_tokens": 1518,   "total_tokens": 3084,   "completion_tokens_details": {     "reasoning_tokens": 576   },   "prompt_tokens_details": {     "cached_tokens": 1408   } } 特に今回のコスト可視化で重要だったのは、次の 3 つです。 prompt_tokens completion_tokens cached_tokens この 3 つが取れれば、少なくとも推定コストの算出に必要な基本情報は揃います。 特に  cached_tokens  は「入力トークンのうち、プロンプトキャッシュから再利用できたトークン数」を表します。 公式ドキュメント における prompt caching の説明では、キャッシュヒット時はこの値が prompt_tokens_details.cached_tokens   に現れ、キャッシュミス時は 0 になるとあります。 最終チャンクからのトークン数取得 def extract_usage_from_final_chunk(final_chunk: Chunk) -> UsageTokens:     usage = final_chunk.get("usage")     if not isinstance(usage, dict):         raise ValidationError("usage payload missing in final chunk")     prompt_tokens = usage.get("prompt_tokens")     completion_tokens = usage.get("completion_tokens")     prompt_tokens_details = usage.get("prompt_tokens_details") or {}     cached_tokens = prompt_tokens_details.get("cached_tokens", 0)     result = UsageTokens(         prompt_tokens=prompt_tokens,         completion_tokens=completion_tokens,         cached_tokens=cached_tokens,     )     result.validate()     return result なお、prompt caching が効く条件についても公式ドキュメントに記載があり、少なくとも次の条件を満たす必要があります。 リクエスト全体が 1,024 トークン以上であること 先頭 1,024 トークンが同一であること そのため cached_tokens    は常に返るとは限らず、モデル種別や API バージョン、入力内容によっては 0 あるいは詳細項目自体が無いことがあります。 KQL でコストをどう計算したか アプリケーション側では usage までを送るだけにして、金額換算は KQL で行うようにしました。 アプリ側で金額を計算してしまうと、モデル単価の変更や料金体系の更新があるたびにコード修正が必要になります。 一方で KQL に価格表を持っておけば、可視化ロジックの変更だけで追従できます。 KQL では、customEvents の usage_telemetry を対象にして、次のような流れで集計します。 customDimensions から department_id、project_id、model_name、deployment_name を取り出す customMeasurements から prompt_tokens、completion_tokens、cached_tokens を取り出す モデルごとの価格表と join する 入力トークン、キャッシュ入力トークン、出力トークンを分けて推定コストを計算する 部門別に summarize する この責務分離によって、アプリケーションコードはシンプルなまま、分析だけを柔軟に変えられるようになりました。 let pricing = datatable(model_name:string, input_per_1m:real, cached_input_per_1m:real, output_per_1m:real) [ 'gpt-5-mini', 39.10, 3.91, 312.80 ]; customEvents | where name == 'usage_telemetry' | extend department_id = tostring(customDimensions.department_id) | extend model_name = tostring(customDimensions.model_name) | extend prompt_tokens = tolong(customMeasurements.prompt_tokens) | extend cached_tokens = tolong(customMeasurements.cached_tokens) | extend completion_tokens = tolong(customMeasurements.completion_tokens) | join kind=leftouter pricing on model_name | extend normal_prompt_tokens = prompt_tokens - cached_tokens | extend input_cost = normal_prompt_tokens / 1000000.0 * input_per_1m | extend cached_input_cost = cached_tokens / 1000000.0 * cached_input_per_1m | extend output_cost = completion_tokens / 1000000.0 * output_per_1m | extend total_cost_jpy = round(input_cost + cached_input_cost + output_cost, 2) | summarize total_cost_jpy = round(sum(total_cost_jpy), 2) by department_id | order by total_cost_jpy desc Workbook ではどう見せたか 可視化としては、部門ごとの推定コストを棒グラフで並べてみました。 このようにすることでビジネスサイドのユーザーにとっても一目で各部署の利用状況などが把握可能です。 余談:Azure Monitor の標準メトリックだけでは難しい理由 Azure OpenAI Service には、利用状況を把握するための標準メトリックも用意されています。 その中には、プロンプトキャッシュの効き具合を把握するうえで参考になる「Prompt Token Cache Match Rate」のようなメトリックもあります。 ただし、このメトリックは現時点ではLog Analytics にデータを転送して、KQL で継続的に分析することができません。(恐らく) メトリックとしてポータル上で確認できたとしても、他のログや業務コンテキストと組み合わせて分析しづらいとなると、やや使いにくい場面があります。 今回 customEvents ベースの仕組みにしたのは、まさにこの点も理由の 1 つです。 Application 側で usage を回収し、department_id や project_id と一緒に送っておけば、必要な切り口で後から KQL 集計しやすくなります。 サンプルコード 今回作成したアプリケーションのコードは以下に置いてあります。 ただし、こちらは生成AIによって出力されたものであり、細部までの動作確認や、セキュリティ面での確認を行っておりません。 あくまで参考レベルとしてご確認いただけると幸いです。 https://github.com/satodayo/azure-openai-usage-telemetry おわりに Azure OpenAI Service の利用コストは、総額だけを追っていても運用に活かしにくい現実があります。 部門やプロジェクトのような業務単位と結び付けて初めて、予算管理や利用改善の議論に使える情報になります。 今回の PoC では、Azure Functions をチャットバックエンド兼テレメトリ BFF として使い、Azure OpenAI Service の usage を回収し、Application Insights の customEvents に送って、KQL と Workbook で可視化する流れを構築しました。 結果として、比較的シンプルな構成でも、部門別コスト可視化の最初の一歩として十分実用的であることを確認できました。 是非導入を検討してみてください! ではまた! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 1人がこの投稿は役に立ったと言っています。 The post Azure OpenAI Service の利用料金を部門別・プロジェクト別に追うためのテレメトリ基盤を作った話 first appeared on SIOS Tech Lab .
Microsoft AI Tour Tokyo参加レポート 3月24日に東京ビッグサイトで開催されたMicrosoft AI Tour Tokyoに参加してきました! 本記事は、本イベントの参加レポートになります。 Connect with Community 午前中は、Microsoft MVPとしてConnect with Communityのブースに立ち、コミュニティに関する説明をしていました。 朝7時半オープンにもかかわらず、朝から多くの方が参加されていて、イベントの盛り上がりを感じました。 会場ではおにぎりやコーヒーなども配布されており、朝からエネルギーをチャージできる体制も整っていました。 ただ、基調講演の時間と重なっていたことや、ビジネス寄りの目的で来場している方が多かったこともあり、残念ながらコミュニティーブースに足を止めてもらえる場面はそれほど多くありませんでした。 が、それでも何人かの方には立ち止まっていただき、さまざまなコミュニティを紹介することができました。 また、ブースには世界のコミュニティマップを示すモニターも展示されていました。 ただ、少し残念だったのは、日本のコミュニティの掲載が少なかったことです。 この点についてはMicrosoftの社員の方にもフィードバックを行ったので、来年は日本のコミュニティがもっと多く掲載されることを期待しています。 Connection Hub 午後からは参加者として、いろいろなブースを回ってきました。 Connection Hubでは、さまざまなMicrosoftのサービスごとにブースが立っており、Microsoftの社員の方や、Microsoft MVPのスペシャリストの方から直接話を聞くことができました。 自分が聞きたいことをその場で社員の方に直接聞ける機会はあまりないので、非常によかったです。 そのこともあって各ブースは大盛況で、質問するにも順番待ちになっていました。 あまり時間がなかったこともあり、すべてを回ることはできませんでしたが、特に気になっていた「Purview」と「Foundry IQ」について話を聞いてきました。 Purview Purviewについては、名前を聞いたことがある程度で、概要すら把握できていなかったので、いろいろ質問してきました。 Purviewは外部からの攻撃を防ぐための製品というより、情報保護やコンプライアンス、内部リスクへの対策を担う製品ということでした。 このPurviewの柱としては、主に次の3つがあるとのことでした。 DLP(データ損失防止) 秘密度ラベル インサイダー リスク マネジメント DLP DLPは、個人情報や機密情報の持ち出し行動を検出・制御するための仕組みです。 ユーザーが機密性の高いアイテムに対して行う操作を監視し、あらかじめ定めたポリシー条件を満たした場合に保護措置が適用されます。 例えば、次のようなポリシーを定めておくことで、情報漏えいにつながる行為を防止できます。 USBへのダウンロードを禁止する コピーを禁止する また、AI時代に合わせて、これを拡張したDLP for Microsoft 365 Copilotのような考え方も出てきており、実際のユーザー操作だけでなく、 ユーザーがM365 Copilotなどを経由してファイル操作を行う場合にもDLPの考え方を適用できるとのことでした。 たしかに、M365 Copilotのようなアプリケーションがエージェントライクに動くのであれば、そうしたデータアクセスも厳格に管理する必要があります。 秘密度ラベル 秘密度ラベルは、「誰がその情報を見られるか」を制御するための仕組みです。 ファイルを作成するタイミングなどで作成者がラベルを付与することで、必要な人だけが見られる状態を作れます。 これにより、社内での閲覧権限を適切に管理できますし、誤って社外にデータを送ってしまった場合でも、権限が不足していれば閲覧できないため、情報漏えいの防止にもつながります。 このラベルは、メール、会議出席依頼、ドキュメントなど、さまざまなコンテンツに適用できるとのことでした。 インサイダー リスク マネジメント インサイダー リスク マネジメントは、たとえば退職予定者がデータを持ち出そうとしているようなケースで、情報漏えいのリスクが高まる行動を監視するための仕組みです。 もちろん、明確な持ち出し意図がある場合だけでなく、意図せずリスクのある行動を取ってしまうケースにも対応できるとのことでした。 Foundry IQ こちらは有益な情報を多く得られたので、別記事であらためてまとめたいと思います。 公開されている情報だけではつかみにくかった実装寄りの話も、直接Microsoftの社員の方から聞くことができ、とても参考になりました。 ワークショップ 会場では非常に多くのワークショップが開催されていました。 今回、私が参加したのは以下の2つです。 GitHub Copilot Agent Mode による実践的なコード移行 AI Toolkit と Model Context Protocol (MCP) を使ったエージェント開発プロトタイピング GitHub Copilot Agent Mode による実践的なコード移行のセッションでは、GitHub Copilotを使って、Pythonで書かれたソースコードをC#に変換する体験を行いました。 とはいえ、主眼はコード変換ではなく、GitHub Copilotをどう使いこなしていくかという点に置かれていました。 講師の寺田さんの実体験を交えたCopilot活用術を聞けたのは、非常に魅力的でした。 寺田さんが何度も繰り返していた「AIの最初の回答は信じないこと。再度聞き直すこと」という話は、自分でCopilotを使っていても確かにそうだよなと思う部分がありました。 また、作業レポートをmdなどで書き出しておくのは非常に有用なTipsだと感じました。 チャット履歴だけを追うのは大変ですし、次回のチャットのコンテキストとしても活用できるので、ぜひ取り入れていきたいです。 2つ目のセッションでは、AI Toolkitを活用した開発を体験しました。 恥ずかしながらAI Toolkitの存在を知らず、このセッションで初めて知りました。 これまではMicrosoft Foundry上でモデルのデプロイなどを都度実施していましたが、VS Code上でこうした作業が行えるようになると、開発体験はかなり良くなると感じました。 今後の開発では積極的に使っていきたいと思います。 ただ、2つともワークショップの内容自体は非常に良かった一方で、作業環境が重かったのか、Wi-Fiが貧弱だったのか、1つの作業に1分前後かかることもしばしばあり、最後まで進めるのが難しかったです。 来年はこのあたりが改善されていることに期待したいです。 まとめ Microsoft AI Tour Tokyoに参加してきました。 午前中は、Microsoft MVPとしての初仕事としてCommunityブースでいろいろお話しさせていただきました。 ブースに遊びに来ていただいた方、ありがとうございました。 午後は参加者として、さまざまなブースやワークショップに参加しました。 Microsoftの方に直接質問できたのは、非常に良い機会でした。 参加者も非常に多く、会場の盛り上がりからもAIに対する注目度の高さを感じるイベントでした。 基調講演は YouTube でも見られるので、ぜひご覧ください!  ではまた! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Microsoft AI Tour Tokyo 2026参加レポート first appeared on SIOS Tech Lab .
はじめに こんにちは、サイオステクノロジーの小野です。 Kubernetesを利用する中で、yamlファイルの差分を確認することがよくあります。 差分確認コマンドと言えばdiffコマンドが一般的ですが、yamlファイルは行単位ではなく設定単位で比較を行いたいケースが多いので、diffコマンドを使うのが難しいです。 そんな時に役立つdyffというツールをご紹介します。 dyffとは dyffはテキストの「行」ではなく「データ構造(意味)」に基づいてYAMLやJSONファイルを比較するコマンドラインツールです。 dyffは以下の特徴があります。 キーの順序を無視して比較 人間が読みやすい出力形式 ツール連携 dyffインストール方法 dyffのインストール方法について詳しくは 公式のリポジトリ を参照してください。 バイナリファイルを用いてインストールする場合、以下のように実行します。 $ curl -LO https://github.com/homeport/dyff/releases/download/<バージョン>/dyff_<インストール環境>.tar.gz $ tar -xvzf dyff_<インストール環境>.tar.gz $ sudo chmod +x dyff $ sudo mv dyff /usr/local/bin/ Macをお使いの場合はHomebrewでインストールが可能です。 $ brew install homeport/tap/dyff LinuxやMacにdyffの最新バージョンを手軽にインストールしたい場合は以下のスクリプトを使うことが可能です。 $ curl --silent --location https://git.io/JYfAY | bash dyff使い方 例として以下の2つのyamlファイルを比較します。 # deployment-A.yaml apiVersion: apps/v1 kind: Deployment metadata: name: frontend-app namespace: production labels: app: frontend spec: replicas: 2 selector: matchLabels: app: frontend template: metadata: labels: app: frontend spec: containers: - name: nginx-container image: nginx:1.24.0 ports: - containerPort: 80 env: - name: ENVIRONMENT value: "prod" # deployment-B.yaml apiVersion: apps/v1 kind: Deployment metadata: name: frontend-app namespace: production labels: app: frontend team: ui-dev # ラベルが追加されている spec: replicas: 3 # レプリカ数が増えている selector: matchLabels: app: frontend template: metadata: labels: app: frontend team: ui-dev # ラベルが追加されている spec: containers: - name: nginx-container image: nginx:1.25.0 # イメージがバージョンアップしている env: # 環境変数設定とポート設定の記載が入れ替わっている - name: ENVIRONMENT value: "prod" ports: - containerPort: 80 ちなみにdiffコマンドを使って上記のyamlを比較すると以下のようになります。diffでは、ぱっと見でどんな設定差分があるのかわかりません。また、環境変数設定とポート設定の記載が入れ替わっただけだと設定的には差分がないですが、diffは行単位で差分比較するので、差分として出力されてしまいます。 $ diff deployment-A.yaml deployment-B.yaml 7a8 > team: ui-dev 9c10 < replicas: 2 --- > replicas: 3 16a18 > team: ui-dev 20,23c22,23 < image: nginx:1.24.0 < ports: < - containerPort: 80 < env: --- > image: nginx:1.25.0 > env: 25a26,27 > ports: > - containerPort: 80 使い方① yamlファイルの比較 dyffは以下のコマンドを実行することで、yamlファイルAからyamlファイルBはどのように差分があるのかを出力します。dyffはyamlファイルのどこが変更されたか、設定単位で確認することが可能です。したがって、一目で差分を確認することができます。また、記載が変更されても設定が変更されていなければ出力されないようになっています。 $ dyff between deployment-A.yaml deployment-B.yaml _ __ __ _| |_ _ / _|/ _| between deployment-A.yaml / _' | | | | |_| |_ and deployment-B.yaml | (_| | |_| | _| _| \__,_|\__, |_| |_| returned four differences |___/ metadata.labels + one map entry added: team: ui-dev spec.replicas ± value change - 2 + 3 spec.template.metadata.labels + one map entry added: team: ui-dev spec.template.spec.containers.nginx-container.image ± value change - nginx:1.24.0 + nginx:1.25.0 dyffのロゴが鬱陶しい場合はオプションで消すことも可能です。 $ dyff between -b deployment-A.yaml deployment-B.yaml metadata.labels + one map entry added: team: ui-dev spec.replicas ± value change - 2 + 3 spec.template.metadata.labels + one map entry added: team: ui-dev spec.template.spec.containers.nginx-container.image ± value change - nginx:1.24.0 + nginx:1.25.0 使い方②Kubernetesクラスターにデプロイされているリソースと yamlファイルの比較 Kubernetesにデプロイされているリソースとyamlファイルを比較する際に、以下のコマンドを実行することが良くあります。しかし、こちらもdiffコマンドと同様に行単位での比較を行っているため、どこの設定が変更されたのかわかりづらいです。 # deployment-A.yamlがデプロイされているKubernetesクラスターに対して、以下のコマンドを実行 $ kubectl diff -f deployment-B.yaml diff -u -N /tmp/LIVE-2041829305/apps.v1.Deployment.production.frontend-app /tmp/MERGED-4236462828/apps.v1.Deployment.production.frontend-app --- /tmp/LIVE-2041829305/apps.v1.Deployment.production.frontend-app 2026-03-26 18:24:38.226501887 +0900 +++ /tmp/MERGED-4236462828/apps.v1.Deployment.production.frontend-app 2026-03-26 18:24:38.227501925 +0900 @@ -6,16 +6,17 @@ kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"frontend"},"name":"frontend-app","namespace":"production"},"spec":{"replicas":2,"selector":{"matchLabels":{"app":"frontend"}},"template":{"metadata":{"labels":{"app":"frontend"}},"spec":{"containers":[{"env":[{"name":"ENVIRONMENT","value":"prod"}],"image":"nginx:1.24.0","name":"nginx-container","ports":[{"containerPort":80}]}]}}}} creationTimestamp: "2026-03-26T09:22:34Z" - generation: 1 + generation: 2 labels: app: frontend + team: ui-dev name: frontend-app namespace: production resourceVersion: "3430046" uid: 9602c096-6931-4278-ba8d-8dbaf9fd2faa spec: progressDeadlineSeconds: 600 - replicas: 2 + replicas: 3 revisionHistoryLimit: 10 selector: matchLabels: @@ -30,12 +31,13 @@ creationTimestamp: null labels: app: frontend + team: ui-dev spec: containers: - env: - name: ENVIRONMENT value: prod - image: nginx:1.24.0 + image: nginx:1.25.0 imagePullPolicy: IfNotPresent name: nginx-container ports: kubectl v1.20以降ではdyffを組み込んで、差分比較することが可能です。デフォルトのkubectl diffよりかなり見やすく設定差分を確認することができます。 $ export KUBECTL_EXTERNAL_DIFF="dyff between --omit-header --set-exit-code" $ kubectl diff -f deployment-B.yaml metadata.generation ± value change - 1 + 2 metadata.labels + one map entry added: team: ui-dev spec.replicas ± value change - 2 + 3 spec.template.metadata.labels + one map entry added: team: ui-dev spec.template.spec.containers.nginx-container.image ± value change - nginx:1.24.0 + nginx:1.25.0 –omit-headerのオプションは上記の-bオプションと同じでロゴを表示しないようにします。–set-exit-codeのオプションは差分が存在した場合は終了コード 1、差分がない場合は 0 を返すようにします。これによって、CI/CDなどの差分確認スクリプトの中に組み込んでも、差分があるかどうかを判定できるようになります。 使い方③Gitリポジトリ内の比較 Gitの過去のコミットを比較する際に、以下のコマンドを使います。Gitでよく見るコミット差分比較の出力です。 $ git log -u commit *** Author: *** Date: *** yamlの更新 diff -git a/deployment.yaml b/deployment.yaml *** @@ -5,8 +5,9 @@ namespace: production labels: app: frontend + team: ui-dev spec: - replicas: 2 + replicas: 3 selector: matchLabels: app: frontend @@ -14,12 +15,13 @@ metadata: labels: app: frontend + team: ui-dev spec: containers: - name: nginx-container - image: nginx:1.24.0 - ports: - - containerPort: 80 - env: + image: nginx:1.25.0 + env: - name: ENVIRONMENT value: "prod" + ports: + - containerPort: 80 Gitのコミット差分比較機能に対してもdyffを組み込むことが可能です。yamlのみですが、見やすく設定差分を確認することができます。 $ git config --local diff.dyff.command 'dyff_between() { dyff --color on between --omit-header "$2" "$5"; }; dyff_between' echo '*.yaml diff=dyff' >> .gitattributes $ git log --ext-diff -u commit *** Author: *** Date: *** yamlの更新 diff -git a/deployment.yaml b/deployment.yaml *** metadata.labels + one map entry added: team: ui-dev spec.replicas ± value change - 2 + 3 spec.template.metadata.labels + one map entry added: team: ui-dev spec.template.spec.containers.nginx-container.image ± value change - nginx:1.24.0 + nginx:1.25.0 –ext-diffオプションをつけることで、dyffの差分が確認できます。オプションを外せば従来の差分比較もできるので、使い分けたい方は上記の設定にして、オプションをつけるのがめんどくさい方はGitのエイリアスに登録してもよいかもしれません。 おわりに yamlの比較の確認がしやすいdyffの紹介をしました。Kubernetesの運用の中で、yamlの設定差分の見落としは危険です。しかし、ServiceやDeploymentといった単純な単体リソースの比較であればdiffでもなんとなく差分比較できますが、HelmアプリケーションのValuesといった数百、数千行あるような設定ファイルをdiffで比較するのはかなりの苦行だと思います。ぜひともdyffをインストールして、その苦行から解放されてください。 参考 dyff公式リポジトリ: https://github.com/homeport/dyff ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post YAMLの変更点を見落とさない!diffより強力なYAML差分確認ツール『dyff』のすすめ first appeared on SIOS Tech Lab .
こんにちは、伊藤です。 佐賀大学では2000年より認証情報の統合を進めており、その成果を踏まえて2007年度から毎年統合認証に関するシンポジウムが開催されています。 統合認証シンポジウムURL: https://www.cc.saga-u.ac.jp/center/workshop/ias 2026年3月6日には「第18回 統合認証シンポジウム」が実施され、弊社エンジニアの服部が登壇しました。 昨年プレスリリースした、「SIOS Shibboleth IdP サービス」( https://sios.jp/products/it/auth/sios-ldp/ )の紹介とデモを行い、ご参加いただいた方々から多くのご質問をいただき、大きなご関心をお寄せいただきました。 ご登壇者 「紙の証明から、検証できる証明へ ― 日本におけるデジタル証明書の可能性」  鈴木彦文 様(国立情報学研究所) 紙の証明書を今後電子証明書に変換していく(デジタル化)際に、現状IdPが持ってる情報は少なく、別途提供手段が必要である課題があります。 NIIで標準のソリューションやIHV共通基盤を提供することで、各大学での導入ハードルを下げられるのではないかといった意見が出されました。 また、鈴木様は、デジタル化の推進にはDX(デジタルトランスフォーメーション)の観点が不可欠であり、関係者にその重要性を深く理解してほしいと訴えられていました。弊社としても、既存の認証基盤と新しいデジタル証明の橋渡しが今後の重要なテーマになると再認識しました。 「統合認証のクラウド化に向けた基礎情報整理のすすめ」  三島和宏 様(大阪教育大学) 大学の停電等でシステムを停止する際に統合認証は稼働させ続けたいという背景から、統合認証のクラウド化を進めたいというニーズが高まっています。今回の講演では、クラウド化を進める前に、まずは「学務・人事システム(情報源)→情報連結→ID管理→各連携システム」の流れの整理が必要であると主張されていました。 特に情報源の整理が重要であり、場所・自動化の可否・信頼性・手を加える必要性・いつ入力され入手できるかを整理した上で、ID管理へ連携する必要性を三島様ご自身の経験や学内環境の現状を踏まえて強調されていました。 各大学でクラウド化が進む中、クラウド化を単なるシステム移行ではなく、情報源の再評価の機会と捉える視点は、弊社のインテグレーション業務においても非常に共感できるポイントでした。 「マイナンバーカードを活用した証明書発行システムの認証について」  大林正人 様(NTT西日本株式会社ビジネス営業本部エンタープライズビジネス営業部文教営業部) マイナンバーカード認証を用いて大学の証明書を発行するソリューションの紹介と、その利点について述べられていました。 デジタル庁提供のデジタル認証アプリ(既存機能の活用)でマイナンバーを使用して認証することで、卒業後のアカウント申請が不要になり、在学中から卒業後まで同一人物であることを継続して保証できる点が強みとして挙げられていました。 マイナンバーカードの活用によるライフサイクルを通じた本人保証は、これからの大学のアイデンティティ管理における新しいスタンダードになる可能性を感じます。 「学生生活とマイナンバーカード: 様々な利用シナリオ」   佐藤周行 様(国立情報学研究所) National IDとしてのマイナンバーカードの民間利用促進に対し、大学としてどう向き合うべきかのプランが示されました。例として、東京大学では認証器のre-binding(再紐付け)時の当人確認にマイナンバーカードを利用するケースが紹介されました。 National IDは利便性に優れる一方で、現状ではプライバシーリスクの評価や信頼性の担保が必要といった意見も出ていました。 また、現状のマイナンバーは日本国内でしか通用しないため、海外でも通用するような枠組みも検討していく必要があると問題提起され、利用者が選択でき、各サービスでNational IDを受け入れられる形が理想であるという意見も出ておりました。 利便性とプライバシーのバランス、そして国際的な相互運用の課題は、IdPサービスを提供する弊社にとっても常に注視していくべき領域です。 「AXIOLEパスキー版の実装について」  上田秋成 様(株式会社ネットスプリング) ユーザ管理も行える認証アプライアンス「AXIOLE」が、今年で20周年を迎えたとのことです。昨年リリースされた同製品の「パスキー認証機能」とその特長についてご紹介いただきました。 パスキー認証機能により、ログインIDやパスワードの入力が不要になります。パスワードレス認証の普及はユーザビリティとセキュリティの両立において急務であり、アプライアンス製品におけるパスキー対応の広がりは、認証業界全体にとって大変喜ばしい潮流だと感じました。 「SIOS Shibboleth IdPサービスが実現する、持続可能な認証基盤の最適解」  服部祥大(サイオステクノロジー株式会社) 弊社で昨年プレスリリースした、「SIOS Shibboleth IdPサービス」を紹介しました。Azure基盤を活用し、Shibboleth IdPの運用管理を大幅に簡略化したソリューションとなっております。本ソリューションの詳細につきましては、後日別の記事やYouTubeでの紹介を予定しております。 まとめ 今回の統合認証シンポジウムでは、学認からマイナンバーカードの最新の活用事例まで幅広い題材の講演があり、終始学ぶことが多く大変有意義な時間となりました。 また講演終了後には情報交換会が行われました。大学のシステムご担当者様や統合認証ソリューションの開発企業など、多くの方々と直接交流することができ、貴重な意見交換の場となりました。 今回の経験でさらに広がった知見を、今後の弊社の製品開発やサポート活動にしっかりと活かしていきたいと感じています。 関係者の皆様、ご参加いただいた皆様、誠にありがとうございました。 関連記事 参考 第17回 統合認証シンポジウムで登壇しました SIOS Tech Lab 参考 第12回 統合認証シンポジウムで登壇しました SIOS Tech Lab ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 第18回 統合認証シンポジウムに参加しました first appeared on SIOS Tech Lab .
ChatGPTの登場以降、多くのWebサービスやアプリに「AI機能」が搭載されるようになりました。現在、生成AIをアプリケーションのUIに組み込むアプローチには、次のような例があります。 対話型(Chatbot) プロンプトビルダー型(Parametric UI) インライン補完型(Ghost Text) コンテキストメニュー型(Contextual Actions) キャンバス型(Artifacts / Workspace) ジェネレーティブUI型(Generative UI) 本記事では、これら6つのUIパターンの特徴を整理し、ユーザーの作業を文脈から支援するAIインターフェース設計の勘所について解説します。 UIパターン 1. 対話型(Chatbot) 画面の右下などにアイコンを置き、クリックするとチャットウィンドウが開く、現在最も一般的な実装形式です。 メリット: チャットボットの表示・非表示をユーザーが簡単に切り替えられるため、既存のアプリケーションのUIを大きく変更せずに導入できるのが特徴です。AIによる補助が不要なユーザーの作業を邪魔しないため、ユーザー数の多い既存サービスでも抵抗感を持たれにくいという大きなメリットがあります。 対話形式は万能で強力なインターフェースであり、幅広い質問やゼロからのアイデア出しに向いています。 例: Nulab Backlog AI アシスタント 限界: 一方で、ユーザーはいちいち作業の手を止め、チャットを開き、「〇〇をして」とプロンプトを入力し、出力された結果をコピーして元の作業画面に貼り付ける……という 「コンテキストスイッチ(文脈の切り替え)」 が発生します。 また、最大の課題となるのが 「言語化のコスト(プロンプトの壁)」 です。 例えば、画像編集ソフトで「右上の画像の明るさを10%上げて、背景を少しぼかして」と文字で指示するのは、スライダーを直接操作するよりも遥かに手間がかかります。人間はやりたいこと全てを簡単に言語化できるわけではありません。そのため、ここから紹介するような、よりワークフローに溶け込んだUIパターンとの使い分けが重要になります。 2. プロンプトビルダー型(Parametric UI) 自由記述のチャット欄にすべてを委ねるのではなく、ドロップダウン、スライダー、タグ選択といった 従来のGUIパーツを使って、AIへの指示(プロンプト)を組み立てさせるUI です。 文章作成ツールで「トーン:丁寧 / カジュアル」「長さ:短め / 長め」をボタンで選ばせたり、画像生成ツールで「アスペクト比:16:9」「スタイル:水彩画」をメニューから選ばせたりする形式です。 メリット: ユーザーは「上手なプロンプトの書き方」を知らなくても、意図した結果を得られます(言語化のコストを下げる)。 システムの裏側で、GUIの選択状態を良質なプロンプト文字列に変換してAPIに渡すため、出力の精度が安定します。 例: Canvaのマジック生成 3. インライン補完型(Ghost Text / Inline Completion) 現在、テキスト入力において最も成功しているAI支援のUIです。 GitHub Copilot (コード補完)などで採用されています。 ユーザーが文字を入力している最中に、AIが予測した続きを薄いグレーの文字(ゴーストテキスト)で表示します。ユーザーは Tab キーを押すだけでそれを採用(Accept)でき、気に入らなければそのまま入力を続けて無視(Reject)できます。 メリット: ユーザーの思考を中断させません。 プロンプトを入力する必要がありません(現在のカーソル位置までのテキストがそのまま文脈になる)。 採用・不採用の判断が極めて高速に行えます。 実装のポイント: レイテンシ(反応速度)が命です。ユーザーのタイピング速度に追いつくためには、数百ミリ秒以内の応答が求められます。 例: Visual Studio Core 上の GitHub Copilot 4. コンテキストメニュー型(Contextual Actions) ユーザーが選択したオブジェクト(テキスト、画像、表など)に対して、AIが特定の処理を提案するパターンです。 Notion AI などのエディタで見られます。 テキストをハイライト選択すると、通常の「コピー」「貼り付け」の隣に、「要約する」「翻訳する」「短くする」といったAIアクションがポップアップ表示されます。 メリット: 「ここでAIに何を頼めるか」をユーザーに視覚的に提示できます。 選択範囲という明確な「対象(Subject)」があるため、AIがコンテキストを誤解せず、精度が高まります。 例: Notion AI 5. キャンバス型(Artifacts / Workspace) チャット画面とは別に、プレビュー・編集専用の画面(キャンバス)が左右や上下に分割して用意され、 「AIとの対話」と「生成物の直接編集」をシームレスに行えるUI です。 Claudeの「Artifacts」や、ChatGPT、Geminiの「Canvas」機能が代表例です。AIにコードや長文を書かせると、専用の領域にそれがレンダリングされ、人間がそこを直接手直ししたり、さらにAIに「ここだけ修正して」と指示を出しできます。 メリット: 対話型(Chatbot)の弱点だった「結果をコピペして手元に持ってくる手間」を完全に排除します。 コーディング、Webデザイン、企画書の作成など、何度も推敲を重ねる「反復的な作業(イテレーション)」に最適です。 例: Google GeminiのCanvas 6. ジェネレーティブUI型(Generative UI) AIの出力結果を単なるテキスト(Markdown)として表示するのではなく、 目的に応じた「操作可能なUIコンポーネント」そのものを動的に生成して表示する 手法です。 例えば、「来週の東京の天気は?」と聞いたときに、「晴れです」というテキストを返すのではなく、 「天気ウィジェット(グラフやアイコン)」 をチャット欄の中にレンダリングします。「フライトを予約したい」と言えば、日付選択カレンダーと便のリストUIが表示されます。 メリット: テキストを読むよりも直感的に情報を把握できます。 表示されたUIを使って、さらに詳細な操作(ボタンを押す、選択するなど)がチャット欄から離れることなく可能になります。 例: Google Gemini UX設計の要:Human-in-the-loop これら多様なUIパターンを取り入れる上で、最も重要な哲学があります。それは、 「最終決定権は常に人間にある(Human-in-the-loop)」 ということです。 AIは確率的に次の言葉や操作を予測しているに過ぎず、平気で嘘をついたり(ハルシネーション)、的外れな提案をしたりします。したがって、AIの提案は常に 「暫定的」 であり、ユーザーが簡単に修正・破棄できなければなりません。 Undo/Redo の保証 AIによる自動編集が行われた後、Ctrl + Z (Undo) で即座に元の状態に戻せることは必須要件です。「AIが勝手に書き換えて、元に戻せなくなった」という体験は、ツールへの信頼を損ないます。 差分の可視化 (Diff View) AIがコードや文章を自動で修正した場合、どこがどう変わったのかをハイライト表示(Diff表示)することで、ユーザーは安心して変更を受け入れることができます。 まとめ:AIはユーザーの能力を拡張する「道具」 業務アプリやプロフェッショナルツールにおけるAIは、人間と会話するだけの存在から、ユーザーの能力を拡張する高度な「道具(コパイロット)」へと進化しています。 AIを道具として捉えたとき、プロンプトを入力させる対話型だけでなく、GUIパーツで指示を組み立てる「プロンプトビルダー型」や、カーソルの先で文脈を汲み取る「インライン補完型」、そして共同作業空間である「キャンバス型」など、多様なアプローチが有効です。 それぞれの特性を理解し、ユーザーのワークフローに最も適したUIを選択・組み合わせることが、これからの生成AI時代の標準的なインターフェース設計となっていくでしょう。 Illustration by Google DeepMind on Unsplash ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 生成AIツールを便利にする6つのUIアプローチ first appeared on SIOS Tech Lab .
こんにちは。香西です。 今回は、生成AI(Claude Code や GitHub Copilot など)と MCP(Model Context Protocol)を組み合わせて、AIに自由度が高く人間にもわかりやすいシステム構成図を .drawio.svg 形式で描かせるためのツール「 mcp-drawio-svg 」を作成し、OSSとして公開しました。 この記事では、開発に至った背景や、既存の作図ツール(Mermaidなど)が抱えていた課題、そして本ツールを使った実際の作図フローについて解説します。 コードは GitHub で公開しているので、ぜひ試してみてください。 https://github.com/Takashi-KOZAI-sti/mcp-drawio-svg なぜ作ったのか? — 「AIにdraw.ioを書かせる」の落とし穴 生成AIが開発スタイルの主流になってくる中で、「AIにMarkdown形式でドキュメントを記述させる」というフローはすでに当たり前になりつつあります。それに伴い、インフラ構成図やシステム概要図などもAIに作図させたいというニーズが急速に高まっています。 「AIで図を描く」となれば、Mermaid や PlantUML が第一候補に挙がりますが、上流設計においては表現力不足(自由な配置、色味、公式アイコンの利用など)が否めません。そこで昨今トレンドになっているのが、**「生成AIに draw.io のファイルを作らせる」**というアプローチです。実際、ネット上でもこの手法を紹介する記事を多く見かけるようになりました。 しかし、真に業務でのドキュメント運用を考えたとき、既存のアプローチには 重大な課題 があると私は考えています。 それは**「エクスポートの手間と、ファイルの二重管理」**です。 通常、AIが出力した .drawio ファイルはそのままでは Markdown に埋め込めません。そのため、一度 draw.io でファイルを開き、PNG や SVG 形式でエクスポートして Markdown から参照する、という作業が必ず発生します。これを繰り返すと「図を更新したのにエクスポートし忘れて、設計書の画像が古いまま」という惨事が必ず起きます。 本当に私たちが欲しいのは、**「AIが作図してくれて、そのままMarkdownでプレビューでき、かつ後から人間が直接手直しできる」**という、シームレスな体験のはずです。 解決策:最初から「.drawio.svg」を出力させ、ローカルで完結させる この課題を根本から解決するために作成したのが、AIが構成図を .drawio.svg 形式 で直接生成するための MCP サーバー「 mcp-drawio-svg 」です。 本ツールの最大の強みであり、既存の「生成AI × draw.io」手法との明確な差別化ポイントは以下の2点です。 ① 「表示」と「編集」を1ファイルで両立するデュアルフォーマット 本ツールは .drawio ではなく、最初から .drawio.svg 形式を出力します。 これは draw.io が定義する特殊なSVGファイルで、画像データの中に編集用のソースデータが内包されています。 Markdownにそのまま埋め込める : ![](diagram.drawio.svg) と書くだけで、設計書に画像として表示されます。エクスポート作業は一切不要です。 1ファイルでそのまま編集可能 :生成されたファイルを VS Code の draw.io 拡張などで開けば、通常の draw.io と同様にノードの移動やスタイル変更が可能です。 ② 機密情報を守る「完全ローカル動作」 社内システムのインフラ図やアーキテクチャ図には、外部に漏らしたくない機密情報が多く含まれます。外部の作図SaaSやAPIに構成データを投げて図をレンダリングさせる手法は、セキュリティの観点からエンタープライズでの導入ハードルが高くなります。 mcp-drawio-svg は、図のレイアウト計算やファイル生成の処理が すべてローカルマシンのMCPサーバー内で完結 します。構成データが外部に漏洩する心配がなく、セキュアな業務環境でも安心して利用できます。 mcp-drawio-svg ができること そこで作成したのが、AIが構成図・インフラ図・システム概要図を .drawio.svg 形式で生成するための MCP サーバー「 mcp-drawio-svg 」です。 AIに対して「こんな構成の図を作って」とお願いすると、AIがこのMCPサーバー( create_drawio_svg ツール)を呼び出します。MCPサーバー側では、図の構成要素(ノード・エッジ・グループ)を JSON で受け取り、以下の処理を全自動で行います。 自動レイアウト計算 : 座標の指定は不要です。フロー方向やレイヤーを考慮して自動で配置を決定します。 アイコン解決 : simple-icons などから適切なアイコンを自動検索して適用します。(もちろん指定することもできます) ファイル生成 : 最終的な .drawio.svg ファイルを出力します。 実装・使い方 — AIに構成図を生成させる ここからは実際の設定と使い方です。今回は Claude Code などに組み込む例を紹介します。 1. セットアップ まずはリポジトリをクローンしてビルドします。 Bash git clone <https://github.com/Takashi-KOZAI-sti/mcp-drawio-svg.git> cd mcp-drawio-svg npm install npm run build 2. MCPサーバーとして登録 お使いの AI クライアントの MCP 設定に本ツールを追加します。 Claude Code の場合 ( ~/.claude/settings.json ) JSON { "mcpServers": { "drawio-svg": { "command": "node", "args": ["/path/to/mcp-drawio-svg/dist/index.js"] } } } 3. AI に「ざっくり」プロンプトを投げる ここが生成AI×MCPの便利なところです。人間がいちいちノードやエッジを細かく指定する必要はありません。 例えば、Claude Code であれば、以下のように 非常にざっくりとした依頼 をplan mode で投げると、MCPのインプットに合わせて構成等を考えてくれます。 あなた(ユーザー): azure 上で構築するwebアプリの、典型的なインフラ構成図を記述してください。 アイコンについては、./icons ファオルダに使えそうなものを配置してあるので、適当に使ってください。 アイコンについては、あらかじめしてしておけばそれを使って図を作成してくれますし、なければsimple-icons からそれっぽいものをMCP側で当て込んでくれます。それでも該当するものがなければ、矩形での表示となります。 Claude Code(AIによる構成案の提案): Azure Web App 典型インフラ構成図の作成プラン ノード & アイコンパス ノード アイコンパス Users general/10783-icon-service-Browser.svg Front Door & CDN web/10073-icon-service-Front-Door-and-CDN-Profiles.svg Application Gateway (WAF) networking/10076-icon-service-Application-Gateways.svg … グループ構成 Azure Subscription  (全体) Virtual Network  グループ Application Gateway, App Service, Function Apps Data Services  グループ SQL Database, Cache for Redis, Blob Storage Security & Identity  グループ Key Vault, Entra ID エッジ(接続) Users → Front Door Front Door → App Gateway App Gateway → App Service … レイアウト algorithm: layered, direction: RIGHT spacing: 70 あなた(ユーザー): 「それでOK!」 これで、あとは、Claude Code がノードとエッジの情報を組み立てて、 create_drawio_svg ツールを実行し、アイコン付きの立派な .drawio.svg ファイルを出力してくれます。例えば、こんな感じ(中身のシステムの妥当性は今は気にしないでくださいね)。      4. 手修正と追加 自動生成らしく、一部エッジが交錯していたり、見にくかったりするので、ここは残念ながら手修正をおねがいします。ある程度調整しているつもりですが、残念ながら完璧な配置はなかなか難しいです。お手数ですが、例えば、こんな感じに修正してみてください。 これに対して、追加/削除を行っていくことも問題なくできます。   あなた(ユーザー): 作成した drawio.svg をベースに、新しいdrawio.svg を作って欲しいです。 構成要素として、github を追加して、cicd をこの構成図に増やしてください。 今の構成図のレイアウトはそのままにしてください。 アイコンはやはり./icons フォルダから適当に探してください。 そうすると、AIは対象となる drawio.svg の構成を読み取るため read_drawio_svg を実行し、現在の図の構造を読み取ります。そして、 edit_drawio_svg を用いて手修正で変更したものを壊すことなく、新しいノード(画像右端のCI/CD Pipeline)を追加することができました。 デフォルトでは以前のレイアウトを残したまま新しいノードを追加する動作ですが、 layout_mode: "recompute” を指定すれば(AIにレイアウトを再計算しろと指示すれば)、すべてのノード位置を改めて再計算し、配置し直すことも可能です。ただし、自分で加えた微修正(ノード位置の修正やエッジの接続点の変更など)は再計算でもとに戻ってしまいますし、手作業で作成した図などは場合によっては原型を全く留めない場合もあるので、図を完全に新規で作り直したいときなどに限定して使用してください。 あとはこれの繰り返しで、期待する図を作成していくことができます。 人間とAIの理想的な役割分担(設計思想) このツールの根底にある設計思想は、**「人間が主導権を持ちつつ、面倒な作業をAIに任せる」**という理想的な役割分担です。 私が目指したのは、以下のような人間とAIの協働プロセスです。 【人間】大枠の指示を出す 「AzureでよくあるWebアプリの構成にして」「セキュリティ要件としてPrivate Endpointは必ず入れて」など、大まかなプランや絶対に外せないポイントを指示します。 【AI】詳細化と提案(たたき台の作成) 人間の指示を受けて、AIが必要な構成要素(各サービス、ネットワーク、接続関係)を具体的に洗い出し、構成案を提案します。 【人間】レビューと承認 提案内容を確認してOKを出します。これにより、**「論理的には100点、見栄え的には70点」**の構成図( .drawio.svg )が全自動で出力されます。 【人間】見栄えの最終仕上げ 出力された図を draw.io で開き、ノードの配置バランスや色使いなど、人間の感性が問われる「見栄え」の部分を100点に仕上げます。 つまり、 「大きな方向性の決定」と「感性が必要なデザインの最終調整」は人間が担当し、その間にある「構成要素の洗い出しから初期レイアウト(たたき台)の作成」をAIが担う 、という形です。 最初から真っ白なキャンバスに向かってアイコンを一つずつ配置していくのは、結構な手間ですよね。すでに論理的な接続が完了している70点の図を100点に引き上げる作業から始められるため、設計ドキュメント作成の負担が圧倒的に軽くなるというところです。 実運用に向けた環境構築とTips 第2章で触れた通り、 mcp-drawio-svg は完全にローカル環境で動作しますが、実際に業務等のプロジェクトに組み込む際には以下の点にご注意ください。 1. AIクライアントとMCPサーバー間のファイルアクセス 本ツールはローカルで .drawio.svg ファイルを直接生成・上書き保存します。そのため、Claude Code などの生成AIクライアントと、このMCPサーバーのプロセスは、 出力先のディレクトリに対して共通の読み書き権限 を持っている必要があります。 もしAIクライアントを Docker などのコンテナ内で実行している場合は、ホスト側のディレクトリを適切にマウントし、両者が同じファイルパスを正しく参照・編集できるように設定してください。 2. プロンプト(LLM)側のデータ管理 MCPサーバーの処理(レイアウト計算やファイル生成)において、構成データが外部の作図APIなどに送信されることは一切ありません。ただし、ユーザーがAIクライアントに入力する指示(「こういうシステム構成を作って」というプロンプト)自体は、当然ながらバックエンドのLLM(AnthropicやOpenAIなど)へ送信されます。 業務利用の際は、作図の処理自体はローカルで安全に行われていることを理解しつつ、利用するLLMプロバイダのデータ規約(学習への利用オプトアウト設定など)を自社のポリシーに合わせて適切に管理した上でご活用ください。 まとめと今後の展望 今回は、生成AI時代の新しい作図アプローチとして、 mcp-drawio-svg を紹介しました。 Mermaid のような「コード駆動の手軽さ」と、 draw.io の「表現力・直感的な編集のしやすさ」を両立させることで、開発現場のドキュメント作成・設計体験が劇的に向上するはずです。 コードは GitHub で公開していますし、ここでは紹介していない機能も実装していますので、ぜひ手元の環境で動かしていただき、日々のドキュメント作成に役立ててみてください! https://github.com/Takashi-KOZAI-sti/mcp-drawio-svg おわりに(余談):マネージャー職が「片手間」でOSSを作れる時代 最後に少しだけ、個人的な所感を書かせてください。 実は私、現在の会社ではマネージャー的ポジションにおります。旧来の日本企業の考え方からすれば、「管理職が自らコードを書いてツールなんか作っていないで、組織の利益最大化のためのマネジメント業務に専念しなさい」とお叱りを受けかねない立場です(笑)。 しかし、私は**「業務改善のヒントや組織の本当の強さの源泉は、常に現場にある」**と強く信じています。現場のペイン(今回の例で言えば、ドキュメント作図の煩わしさ)を解消する細やかな改善こそが、最終的に組織全体の大きな生産性向上に繋がるからです。 これまでは、こうした現場向けのニッチなツールを管理職が自ら作るのは、時間的にもリソース的にも困難でした。しかし、生成AIの登場で状況は一変しました。 AIに対して「こんな課題があるから、こういうアプローチで解決したい」とイメージを伝え、少しのやり取り(壁打ち)を行うだけで、 日々の業務の隙間時間に「ペロッと」具体的な動くものが作れてしまう のです。今回の mcp-drawio-svg も、季節柄の胃の痛くなる業務を行う傍らで、少し息抜きしながらAIという優秀なアシスタントの力を借りてサクッと形にしたものです。 さらに白状すると…… 実はこのブログ記事自体も、私がゼロから執筆したわけではありません。 私が「こういう構成で、こういうアピールポイントを入れて」と要点を箇条書きで投げただけで、あとは生成AIがいい感じに文脈を汲み取って、この原稿を書き上げてくれました。この余談くらいでしょうか、自分で書いたのは。(^_^;) アイデアと現場への課題意識さえあれば、ポジションに関わらず誰でも素早く形にできる。本当に素晴らしい時代になりましたね。 皆さんもぜひ、生成AIという強力なツールを使って、日々のちょっとした業務改善を形にしてみてください! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【ローカルで完結】AIエージェントに”いい感じの”構成図を描かせるMCP「mcp-drawio-svg」を作りました first appeared on SIOS Tech Lab .
はじめに こんにちは!サイオステクノロジーのなーがです。前回はGoogle CloudのVertex AIをAzureから使用するための手順ということで主にインフラ関連の内容を書きましたが、今回はAIコーディングエージェントの開発プロセスを強化するフレームワーク「obra/superpowers」について書こうと思います。 AIエージェントは本当に便利なのですが、使い込んでいると「あれ、テスト書かずにいきなり実装してる…」「原因調査なしにとりあえずパッチを当てようとしてる…」という場面に気づくことがあります。エージェントは高性能ですが、プロセスを省略するクセがあるんですよね。 そんな悩みを解決してくれるのが、今回紹介する obra/superpowers です。92K以上のGitHubスターを獲得している注目のフレームワークで、AIコーディングエージェントに規律ある開発プロセスを注入してくれます。 今回は、superpowersの概要からインストール方法、実際の活用シナリオまでを詳しく紹介します。 obra/superpowers とは obra/superpowers は、AIコーディングエージェント向けの アジャイルスキルフレームワーク です。 「スキル」と呼ばれるMarkdownベースの指示ファイルをエージェントに読み込ませることで、テスト駆動開発・体系的なデバッグ・コードレビューといった規律ある開発プロセスを強制します。 “coding agents produce better results when they follow systematic processes rather than ad-hoc approaches” この思想が、superpowersの根幹にあります。エージェントの能力を引き出すのではなく、 プロセスをガイドする というアプローチです。 誕生の背景 開発者の Jesse Vincent 氏(Prime Radiant)が、AIエージェントが以下のような「近道」を取ろうとする問題に直面したことがきっかけです。 テストを後回しにして実装を先に書く 根本原因を調査せずにパッチを当てる 完了確認なしに「できました」と報告する superpowers はこれらのアンチパターンを明示的にドキュメント化し、エージェントが自分でそれを認識・拒否できるように設計されています。 参照: obra/superpowers – GitHub 対応プラットフォーム superpowers は主要なAIコーディングプラットフォームすべてに対応しています。 プラットフォーム 対応状況 Claude Code 公式マーケットプレイス対応 Cursor プラグインマーケットプレイス対応 Gemini CLI Extension対応 Codex .codex/ ディレクトリ方式 OpenCode .opencode/ ディレクトリ方式 スキル一覧とアーキテクチャ 14種類のスキル一覧 superpowers には14種類のスキルが含まれており、4つのカテゴリに分類されています。 カテゴリ スキル名 目的 テスト・品質 test-driven-development テスト先行の RED-GREEN-REFACTOR サイクルを強制 systematic-debugging 4フェーズの根本原因分析を強制 verification-before-completion 証拠なき完了宣言を禁止 計画・実行 brainstorming 実装前の設計対話・仕様レビューを実施 writing-plans 2〜5分タスク単位の実装計画を作成 executing-plans 実装計画を順序通りに実行 subagent-driven-development サブエージェント×2段階レビューで高品質実装 dispatching-parallel-agents 独立タスクを並列サブエージェントへ委譲 Git・コラボレーション using-git-worktrees 独立したGit worktreeで安全に並列開発 finishing-a-development-branch テスト確認→マージ/PR/破棄の構造化フロー requesting-code-review 適切なタイミングでコードレビューを依頼 receiving-code-review レビューフィードバックを技術的に評価 メタ using-superpowers スキルの発見・呼び出しのメタプロトコル writing-skills 新しいスキルをTDDで作成 コアスキル12種に加え、フレームワーク自体を拡張するメタスキル2種( using-superpowers ・ writing-skills )が含まれています。 ディレクトリ構成 リポジトリのトップレベル構成は以下の通りです。 superpowers/ ├── skills/ # 14種類のスキルモジュール ├── agents/ # エージェント設定・振る舞い定義 ├── commands/ # CLIコマンド実装 ├── hooks/ # プラットフォーム統合フック ├── docs/ # プラットフォーム別ドキュメント ├── tests/ # テストスイート ├── .claude-plugin/ # Claude Code 統合 ├── .cursor-plugin/ # Cursor 統合 ├── .codex/ # Codex 統合 └── .opencode/ # OpenCode 統合 各スキルは skills/<スキル名>/ ディレクトリに格納されており、必須ファイル SKILL.md の他に必要に応じてテンプレートやスクリプトが含まれます。 アーキテクチャ図 superpowers の全体アーキテクチャを図で示します。AIコーディングプラットフォームからスキルレイヤーを通じ、フック・Git統合へと流れる構造になっています。 スキルはプロジェクトのコンテキストに応じて **自動的に発火** します。特別な構文や手動呼び出しは不要です。 次に、スキルのカテゴリ構成をマップで示します。 SKILL.md のフォーマット 各スキルは SKILL.md ファイル一枚で定義されます。構造は以下の通りです。 --- name: skill-name description: "Use when ... (最大1024文字、トリガー条件を記述)" --- ## Overview ## When to Use ## Core Pattern ## Quick Reference ## Implementation ## Common Mistakes フロントマターの description フィールドが特に重要で、エージェントがスキルを自動選択する際の判断基準になります。「Use when…」という形式でトリガー条件を記述することがベストプラクティスです。 Token効率も設計の考慮事項で、頻繁に読み込まれるスキルは200語以内に収めることが推奨されています。 インストール方法 Claude Code 公式マーケットプレイス経由(推奨): /plugin install superpowers@claude-plugins-official カスタムマーケットプレイス経由: # ステップ1: マーケットプレイスを追加 /plugin marketplace add obra/superpowers-marketplace # ステップ2: インストール /plugin install superpowers@superpowers-marketplace インストール後、新しいセッションを開始して「この機能を計画したい」などと話しかけると、 brainstorming スキルが自動的に発火します。 アップデート: /plugin update superpowers Cursor Cursor のプラグインマーケットプレイスから検索してインストールするか、以下のコマンドを使用します。 /add-plugin superpowers Gemini CLI gemini extensions install https://github.com/obra/superpowers 参照: obra/superpowers – Installation 活用シナリオ 活用シナリオは無限に考えられますが、今回は下記の6つのシナリオを紹介します。 TDD を強制する test-driven-development スキルを使うと、エージェントはテストなしに実装コードを一切書かなくなります。 鉄則: “NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST.” テストより先にコードを書いた場合は、そのコードをすべて削除して最初からやり直す。 サイクルは以下の通りです。 RED — 失敗するテストを書く RED確認 (必須)— テストが実際に失敗することを確認する GREEN — テストが通る最小限のコードを書く GREEN確認 (必須)— テストが通ることを確認する REFACTOR — コードを整理する 手動テスト済みであることを理由にテストを省略しようとすると、スキルがそのアンチパターンを認識して拒否します。 長時間の自律開発セッション executing-plans スキルと subagent-driven-development スキルを組み合わせると、2時間以上の自律開発セッションが可能になります。 各タスクは2〜5分単位に分割され、サブエージェントが1タスクずつ実装・レビューを繰り返します。人間が介入しなくても、スキルがプロセスの品質を担保します。 複数機能の並列開発 dispatching-parallel-agents スキルを使うと、互いに独立した複数のタスクを並列で処理できます。 適用条件: 3つ以上の独立した失敗ファイル・サブシステムがある タスク間で共有状態がない それぞれ異なる根本原因を持つ using-git-worktrees と組み合わせることで、各エージェントが独立したGit worktreeで安全に作業できます。 公式ドキュメントによると、6件の失敗を3つの並列エージェントで解決したケースでは、コンフリクトゼロで解決できたとのことです。 根本原因を特定してからデバッグする systematic-debugging スキルは、エージェントが「とりあえずパッチを当てる」近道を防ぎ、4フェーズの体系的なデバッグを強制します。 4フェーズ: Root Cause Investigation — エラーを読み込み、再現手順を確認し、直近の変更を調査する Pattern Analysis — 動いている似た箇所と比較して差分を特定する Hypothesis and Testing — 仮説を1つ立て、最小限の変更で検証する Implementation — 根本原因のみを修正し、再発防止のテストを追加する 原因が特定できていない段階で修正コードを書き始めようとすると、スキルの Iron Law「NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST」に引っかかり、Phase 1 に差し戻されます。「直ったと思ったらまた壊れた」という状況を根本から防げます。 実装前に設計を固める brainstorming スキルを使うと、エージェントはコードを書く前に設計対話フェーズを実施します。 対話の流れ: 要件の確認と曖昧な点の洗い出し 複数の実装アプローチを提示して比較 トレードオフ(速度・保守性・複雑度)を明示 合意した方針をドキュメント化 このスキルは writing-plans と連携しており、対話が終わると自動的に実装計画の作成に移行します。「とりあえず実装してみて後で考える」というエージェントの傾向を、設計ファーストに切り替えます。 1問ずつ丁寧に要件を深掘りし、合意が取れた段階で設計書を作成→ writing-plans スキルへ引き渡します。 コードレビューのサイクルを回す requesting-code-review と receiving-code-review の2スキルを使うと、エージェントがレビューの依頼から反映まで一貫して対応します。 レビュー依頼時( requesting-code-review ): PR の目的・変更の概要・テスト状況を整理してレビュアーに提示 レビューしやすい粒度に変更を分割 フィードバック受け取り時( receiving-code-review ): 指摘内容を「必須修正」「提案」「議論」に分類 技術的な根拠をもとに修正の要否を判断 反映した変更を明示してレビュアーに返答 感情的に反応したり、すべての指摘を無条件に受け入れたりするのではなく、技術的な評価に基づいて対応します。 技術的に不要な変更はプッシュバックし、根拠のある修正のみを行います。 さいごに 今回は obra/superpowers について紹介しました。 AIエージェントは非常に高性能ですが、プロセスを省略しがちという弱点があります。superpowers はそこに「規律」を注入することで、エージェントを本来の実力で動かし続けるための仕組みです。 特に test-driven-development スキルと systematic-debugging スキルは、エージェントが近道を取ろうとする典型的な場面で真価を発揮します。まずはこの2つからインストールして試してみることをおすすめします。 興味を持った方は、ぜひ公式リポジトリや writing-skills スキルを使って、自分だけのスキルを作ってみてください! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AIコーディングエージェントの弱点を補う「obra/superpowers」 first appeared on SIOS Tech Lab .
こんにちは! 今月も「OSSのサポートエンジニアが気になった!OSSの最新ニュース」をお届けします。 3/2、高市早苗首相が自身の名前入りの仮想通貨「SANAE TOKEN」が発行・取引されていると X に投稿し、自身や事務所と関係はなく誤解しないよう注意を呼びかけました。 高市首相名の仮想通貨に注意を 「全く存じ上げず」と投稿 https://news.yahoo.co.jp/articles/36178752c01fbee50efe23bd46332d36f34ee8b0 3/17、Linux Foundation はオープンソースソフトウェアの持続的なセキュリティ強化を目的として Anthropic、AWS、GitHub、Google、Google DeepMind、Microsoft、OpenAI が総額 1250 万ドル(約 19億 8300万円)を助成したと発表しました。 米テック大手7社、オープンソースセキュリティに1250万ドルを助成 https://japan.zdnet.com/article/35245203/ 3/17、LayerX Security は Web サイトに表示される悪意のあるテキストを AI から隠蔽する手法を発見したと発表しました。 フォント改ざんでAI検出を回避、主要ツールがすべて失敗 https://news.mynavi.jp/techplus/article/20260321-4237821/ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【2026年3月】OSSサポートエンジニアが気になった!OSS最新ニュース first appeared on SIOS Tech Lab .
今号では、Linux における「拡張子」の取り扱いについて説明します! Linux での「拡張子」の役割とは 「拡張子」とは、 ファイルの種類を分かりやすく判断するための仕組み であり、 ファイルの末尾に付けられる文字 のことです。 例えば Windows の場合、.exe は実行ファイル、.txt はテキストファイル、となっています。この、あらかじめ決められている拡張子を他のものに変えてしまうと、そのファイルを開いたり実行できなくなるなどの問題が発生します。 しかし、Linux の場合は「ファイルヘッダ」というファイルの情報を読み取り、どのようなファイルであるかを判断しています。そのため、 拡張子がなくてもどのようなファイルかを判別できます。 とはいえ…システム的にはすぐにファイルを判別できても、人間はそうはいきませんよね。そこで、Linux でも Windows と同様にファイルに拡張子を付けることで、どのようなファイルかを判別しやすくしているのです。 Linux で使われる拡張子 Linux でよく使用される拡張子と、その内容についてご紹介します。 スクリプト系 .sh シェルスクリプト。Linux のコマンドや、if文、for文などの制御構文から構成される。 .py Python で実装されたスクリプト。 他にも様々なスクリプト言語があり、それによって拡張子も様々です。 設定ファイル系 .conf アプリケーション、システムの設定ファイル。 .cfg GRUB (ブートローダー) などに使用される、独自の書式を持つ設定ファイル。 .yaml、.yml コンテナ関連や Ansible で使用される設定ファイル。 設定ファイルは、主に /etc ディレクトリ配下に配置されます。 パッケージ、リポジトリ系 .rpm RHEL 系 OS で使用される、ソフトウェアのパッケージファイル。 .repo RHEL 系 OS で使用される、リポジトリの設定ファイル。 .rpm ファイルは、yum / dnf コマンドでダウンロードした場合 /var/cache/yum や /var/cache/dnf ディレクトリ配下に一時的に保存されます。 .repo ファイルは、通常 /etc/yum.repos.d ディレクトリ配下に配置されます。 システム管理系 .service サービス (インストール済みのプログラム) の起動、停止などを定義するファイル。 .timer プログラムの定期実行を定義するファイル。 .target 「マルチユーザーモード」や「グラフィカルモード」など、システムの起動状態をグループ化するためのファイル。 鍵・証明書系 .pem 秘密鍵や証明書の情報を格納する、テキストベースのファイル。 .crt SSL/TLS の証明書ファイル。 .key SSL/TLS の秘密鍵ファイル。 .pub SSH の公開鍵ファイル。 その他 .log ログファイル。 .tar アーカイブファイル。 .tar.gz 圧縮アーカイブファイル。 補足事項 file コマンドでファイルの形式を表示する file コマンド を使用すると、指定したファイルの形式を表示します。 私は、このファイルはテキスト?バイナリ?どちらなのか?を確認するのに使ったりします。 # file text.txt # テキストファイル text: ASCII text # file archivefile.gz # アーカイブファイル archivefile: gzip compressed data, was "test.txt", last modified: Mon Feb 9 00:40:33 2026, from Unix, truncated # file /var/log/messages # ログファイル /var/log/messages: UTF-8 Unicode text, with very long lines ※file コマンドは、あくまで ファイルの形式 を表示するためのものです。 例えば、.conf ファイルや .service ファイルはいずれもテキスト形式で保存されているため、file コマンドで確認すると ASCII text という結果が返ってきます。 「これは設定ファイル」「これはサービスファイル」という、 具体的なファイルの種類については判別できません ので注意してください。 拡張子を変更した場合、各ファイルは認識されるのか 結論から言いますと、 ちゃんと認識されます。 例えば .sh ファイルを .txt に変えても、シェルスクリプトとして実行できます (ただし実行権限は必要)。 ただし、GUI (デスクトップ環境) を利用している場合は Windows と同様に拡張子とアプリケーションが紐付けられている場合があるため、注意が必要です。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 知っておくとちょっと便利!Linux の拡張子について first appeared on SIOS Tech Lab .