TECH PLAY

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

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

610

こんにちは。サイオステクノロジーの和田です。アドベントカレンダー17日目です。今回はアウトボックスパターンという設計パターンを学んだので、紹介したいと思います。まず初めにアウトボックスパターンの説明をしてから、その設計を使った具体的なサービス例、そして冪等性の考慮について書いていきます。それではいきましょう。 アウトボックスパターンとは アウトボックスパターンとはマイクロサービスアーキテクチャにおいて、データベースの更新と「イベント発行(メッセージ送信要求)」を矛盾なく行うための設計パターンです。マイクロサービスでは、あるサービスが自身のデータベースを更新し、その変更をイベントとして他のサービスに通知するという処理がよくあります。 アウトボックスパターンでは、送信したいイベント(= メッセージの内容)を Outbox テーブル に書き込み、ユーザーデータの更新と同一トランザクションでコミットします。実際のメッセージブローカーへの送信は後段のリレーが行うため、「DB に書けたのにイベントが失われる / DB に書けていないのにイベントだけ流れる」といった不整合を避けられます。 例:ユーザー登録サービス ここでは具体例として、Web サービスの 新規ユーザー登録 を例に考えます。 ユーザーが登録ボタンを押した際に、システムでは以下の 2 つを行う必要があります。 「ユーザーデータベース」にユーザー情報(ID、メールアドレス、パスワード等)を書き込む。 「メール配信サービス」に対して、「ユーザー登録が完了した」というメッセージをメッセージブローカーに送信する(登録完了メールや認証メールを送るため)。 この 2 つの処理を、アプリケーションが順番に実行しようとすると、以下のような問題が発生する可能性があります。 ケース 1:DB 書き込み成功、メッセージ送信失敗 ユーザーデータベースへの保存は成功したが、その直後にメッセージブローカーがダウンしてしまった。 結果 ユーザーのアカウントは作成されたが、登録完了メール(または認証メール)が届かない。ユーザーはログインの手順を完了できず、サービスを利用開始できない。 ケース 2:DB 書き込み失敗、メッセージ送信成功 何らかの理由(メールアドレスの重複など)でデータベースへの書き込みが失敗(ロールバック)したが、そのあとにメッセージだけ送信されてしまった。 結果 データベースにユーザーは存在しない(登録できていない)のに、「登録ありがとうございます」というメールだけがユーザーに届いてしまう。ユーザーがメール内のリンクをクリックしても、アカウントが存在しないためエラーとなる。 このように、どちらのケースの場合でも結果に不整合が生じてしまいます。 解決策:アウトボックスパターン このような問題に対する解決策として使えるのがアウトボックスパターンです。アウトボックスパターンでは「外部への送信そのもの」ではなく「送信したい内容を Outbox に永続化するところまで」を、データベースへの書き込みと同じトランザクションで処理することで問題を解決します。具体的には以下の流れで実現します。 1. ユーザー登録サービスの処理 ユーザー登録サービスではユーザーテーブルへの書き込みと同時に、送信したいメッセージ(メール送信依頼)を Outbox テーブル と呼ばれる特別なテーブルに書き込みます。 ここで重要なのは、ユーザーテーブルへの書き込みと Outbox テーブルへの書き込みを単一のデータベーストランザクションで実行することです。 トランザクションの結果 トランザクションが成功した場合 ユーザーデータと送信すべきメッセージの両方がデータベースにコミットされます。 トランザクションが失敗した場合 何らかのエラー(DB エラーやバリデーションエラーなど)があれば、両方の書き込みがロールバックされます。 この結果、データベースへのユーザー登録が成功したならば、送信すべきメッセージも必ず DB 内に保存されている状態が保証され、データの整合性を担保することができます。 一方で、メッセージブローカーへの反映はリレーのタイミングに依存するため、通知は(多くの場合)最終的整合になります。つまり、登録完了直後に必ずメール送信が開始されるとは限らず、多少の遅延が起こり得ます。 2. メッセージのリレー 実際の送信ではメッセージリレーと呼ばれるアプリケーションとは別の独立したプロセスがメッセージの送信を行います。 このリレーが Outbox テーブルを定期的に監視(ポーリング)し、未送信のメッセージを見つけた場合はメッセージブローカーへ送信します。送信が完了したら Outbox テーブルの該当のレコードを削除(または送信済みステータスに変更)します。 監視の方法はポーリング以外にも、CDC(Change Data Capture)などで変更を検知する構成もあります。 3. メールの配信 最後にメールを送信するプロセスがメッセージブローカーからメッセージを受け取り、実際にメールの送信を行います。 冪等性の考慮 アウトボックスパターンを実装する際には、 冪等性 についても考慮する必要があります。冪等性とは、 同じ操作を何度実行しても、結果が 1 回実行した場合と同じになる性質 のことです。 メッセージの重複送信の可能性 メッセージリレーが Outbox テーブルからメッセージを読み取り、メッセージブローカーに送信した後、送信済みのマークを付ける前にクラッシュした場合、同じメッセージが再度送信される可能性があります。 例えば、ユーザー登録のケースでは以下のような問題が発生する可能性があります メッセージリレーがメッセージブローカーにメール送信依頼を送信 メッセージブローカーへの送信は成功 Outbox テーブルの更新前にメッセージリレーがクラッシュ リレーが再起動し、同じメッセージを再送信 ユーザーに登録完了メールが 2 通届いてしまう 今回の例のようなメール送信の場合は、2 通届いても致命的ではないケースもあります。しかし、他のストレージにデータを保存する処理などを行う場合は冪等性を確実に実装する必要があります。 典型的な対策としては、各メッセージに一意な eventId (メッセージ ID)を付与し、受信側で「処理済み eventId」を保存して重複を検知・無視する方法があります。 まとめ 今回はアウトボックスパターンという設計パターンについて紹介しました。アウトボックスパターンを使うことで、マイクロサービス間でのデータの整合性を担保しやすくなり、「データは保存されたのに通知が届かない」といった事象を避けられます。その結果、より堅牢なシステムを構築できます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post アウトボックスパターンとはなにか first appeared on SIOS Tech Lab .
こんにちは、サイオステクノロジーの遠藤です。 最近はClaude Codeを使った開発にどっぷりハマっているのですが、「とりあえず作ってみて」でAIに丸投げすると、思ってたのと違うものができあがることってありませんか? そんな課題を解決してくれるのが、GitHubがオープンソースで公開した Spec Kit です。今回は実際にTodoアプリの仕様をSpec Kitで生成してみたので、その体験をシェアします。 この記事の内容 項目 内容 やったこと GitHub Spec Kitを使ってTodoアプリの仕様書を生成 得られるもの Spec Kitの導入方法と4フェーズワークフローの理解 対象読者 AIコーディングエージェントでの開発精度を上げたい人 こんな人に読んでほしい ✅ Claude Code、GitHub Copilot、Cursor などのAIコーディングエージェントを使っている ✅ AIに「とりあえず作って」で丸投げして失敗したことがある ✅ 仕様書を書くのが面倒だけど、品質は上げたい ✅ チーム開発でAIの使い方を標準化したい はじめに AIコーディングエージェントの課題 AIコーディングエージェントは便利ですが、こんな経験ありませんか? 「ログイン機能作って」→ 想定と全然違う認証方式で実装された 「修正して」→ 関係ないところまで変えられた 「シンプルに」→ 必要な機能まで削られた これらの問題の多くは、 仕様が曖昧なまま実装を始めてしまう ことが原因です。 Spec Kitとは Spec Kit は、GitHubが2025年9月にオープンソースで公開した「仕様駆動開発(Spec-Driven Development)」のためのツールキットです。 特徴 : 4フェーズワークフロー : Specify → Plan → Tasks → Implement ゲート付き進行 : 各フェーズが完了するまで次に進めない マルチエージェント対応 : Claude Code、GitHub Copilot、Cursor、Gemini CLIなど15以上のAIエージェントに対応 仕様書を先に作ることで、AIへの指示が明確になり、実装のブレを防げるという考え方ですね。 Spec Kitのインストール 前提条件 Python 3.11以上 Git uv(パッケージ管理) AIコーディングエージェント(Claude Code、GitHub Copilot、Cursorなど) インストール手順 uvを使って永続的にインストールします: uv tool install specify-cli --from git+https://github.com/github/spec-kit.git インストール確認: specify version プロジェクトの初期化 specify init コマンド 新規プロジェクトを作成します。今回はClaude Codeを使うので --ai claude を指定: mkdir todo-app-speckit cd todo-app-speckit specify init . --ai claude 出力: ╭──────────────────────────────────────────────────────────────────────────────╮ │ │ │ Specify Project Setup │ │ │ │ Project todo-app-speckit │ │ Working Path /path/to/todo-app-speckit │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯ Selected AI assistant: claude Selected script type: sh Initialize Specify Project ├── ● Check required tools (ok) ├── ● Select AI assistant (claude) ├── ● Select script type (sh) ├── ● Fetch latest release (release v0.0.90) ├── ● Download template ├── ● Extract template ├── ● Initialize git repository └── ● Finalize (project ready) Project ready. 生成されるファイル構成 .claude/ └── commands/ ├── speckit.constitution.md # プロジェクト原則 ├── speckit.specify.md # 仕様作成 ├── speckit.plan.md # 技術計画 ├── speckit.tasks.md # タスク分解 ├── speckit.implement.md # 実装実行 ├── speckit.clarify.md # 曖昧性解消(オプション) ├── speckit.analyze.md # 一貫性分析(オプション) └── speckit.checklist.md # 品質チェック(オプション) .specify/ ├── memory/ │ └── constitution.md # 憲法(プロジェクトルール) ├── scripts/ │ └── bash/ │ ├── create-new-feature.sh │ └── ... └── templates/ ├── spec-template.md ├── plan-template.md └── tasks-template.md Claude Codeの .claude/commands/ にスラッシュコマンドが配置されます。 Spec Kitの4フェーズワークフロー Spec Kitは4つのフェーズで構成されています: Phase 1: Specify(仕様作成) /speckit.specify コマンドで仕様書を生成します。 入力例 : /speckit.specify シンプルなTodoアプリ - タスクの作成・完了・削除ができるWebアプリケーション 生成される spec.md の構成 : # Feature Specification: Simple Todo Application **Feature Branch**: `001-todo-app` **Created**: 2025-12-07 **Status**: Draft ## User Scenarios & Testing *(mandatory)* ### User Story 1 - Create New Task (Priority: P1) [ユーザーストーリーの詳細] **Acceptance Scenarios**: 1. **Given** [初期状態], **When** [アクション], **Then** [期待結果] ### Edge Cases [エッジケースの列挙] ## Requirements *(mandatory)* ### Functional Requirements - **FR-001**: System MUST allow users to create a new task with a title - **FR-002**: System MUST validate that task title is not empty ... ## Success Criteria *(mandatory)* - **SC-001**: Users can create a new task in under 5 seconds ... ポイント : ユーザーストーリーは優先度(P1, P2, P3…)付き 各ストーリーは 独立してテスト可能 (MVP単位でリリース可能) 技術スタックには言及しない(WHATとWHYに集中) Phase 2: Plan(技術計画) /speckit.plan コマンドで技術的な実装計画を作成します。 生成される plan.md の構成 : # Implementation Plan: Simple Todo Application ## Summary [仕様から抽出した要件 + 技術的アプローチ] ## Technical Context **Language/Version**: TypeScript 5.x **Primary Dependencies**: React 18.x, Vite **Storage**: LocalStorage **Testing**: Vitest + React Testing Library **Target Platform**: Modern Web Browsers ## Project Structure ### Source Code todo-app/ ├── src/ │ ├── components/ │ ├── hooks/ │ ├── types/ │ └── utils/ └── tests/ ## Architecture Decisions [データフロー、コンポーネント階層、エンティティ定義] ## Implementation Phases [Phase 1: Core Data Model → Phase 2: UI → Phase 3: Styling → Phase 4: Testing] このフェーズで初めて技術スタックが決まります。 Phase 3: Tasks(タスク分解) /speckit.tasks コマンドでタスクリストを生成します。 生成される tasks.md の構成 : # Tasks: Simple Todo Application ## Format: `[ID] [P?] [Story] Description` - **[P]**: 並列実行可能(ファイルが異なる、依存なし) - **[Story]**: どのユーザーストーリーに属するか ## Phase 1: Setup - [ ] T001 Create project structure with `npm create vite@latest` - [ ] T002 Install dependencies - [ ] T003 [P] Configure Vitest ## Phase 2: Foundational - [ ] T005 Define Task type in `src/types/task.ts` - [ ] T006 [P] Implement LocalStorage utilities - [ ] T007 Create `useTasks` custom hook ## Phase 3: User Story 1 - Create New Task (P1) - [ ] T010 [US1] Create TaskInput component - [ ] T011 [US1] Add validation logic - [ ] T012 [US1] Style TaskInput with error state - [ ] T013 [P] [US1] Write TaskInput component tests ## Dependencies & Execution Order [フェーズ間の依存関係、並列実行の機会] ポイント : ユーザーストーリー単位でグループ化 [P] マークで並列実行可能なタスクを識別 MVPファーストの実装戦略 Phase 4: Implement(実装) /speckit.implement コマンドでタスクを実行します。 実装フロー : タスクリストから1つずつ(または並列で)実装 各タスク完了後にテスト実行 チェックポイントで動作確認 次のユーザーストーリーへ 実際に生成された仕様書を見てみる 今回、Todoアプリ用に生成した仕様書の一部を紹介します。 spec.md(仕様書) ## User Scenarios & Testing ### User Story 1 - Create New Task (Priority: P1) ユーザーがやるべきことを思いついたとき、すぐにタスクとして記録したい。 タスクにはタイトルを入力して保存できる。 **Why this priority**: タスク作成はTodoアプリの最も基本的な機能であり、 これがなければアプリとして成立しない。 **Acceptance Scenarios**: 1. **Given** タスク入力画面が表示されている **When** タスク名「買い物に行く」を入力して保存する **Then** タスクリストに「買い物に行く」が追加される 2. **Given** タスク入力欄が空の状態 **When** 保存ボタンを押す **Then** エラーメッセージが表示され、タスクは追加されない ### Edge Cases - 非常に長いタスク名(1000文字以上)→ 最大255文字で制限 - 同じタスク名を複数回入力 → 許可(別タスクとして扱う) - 特殊文字やHTMLタグ → エスケープして安全に表示 ## Requirements ### Functional Requirements - **FR-001**: System MUST allow users to create a new task with a title - **FR-002**: System MUST validate that task title is not empty or whitespace-only - **FR-003**: System MUST limit task title to maximum 255 characters ... ## Success Criteria - **SC-001**: Users can create a new task in under 5 seconds - **SC-002**: Users can mark a task as complete with a single click - **SC-004**: Task data persists across browser sessions with 100% reliability ## Assumptions - ユーザー認証は不要(ローカルストレージのみで動作) - 複数デバイス間の同期は不要 - タスクの編集機能は初期スコープ外 tasks.md(タスクリスト) ## Phase 3: User Story 1 - Create New Task (Priority: P1) **Goal**: Users can create new tasks with validation ### Implementation for User Story 1 - [ ] T010 [US1] Create TaskInput component in `src/components/TaskInput.tsx` - [ ] T011 [US1] Add validation logic (empty check, max length 255) - [ ] T012 [US1] Style TaskInput with error state - [ ] T013 [P] [US1] Write TaskInput component tests **Checkpoint**: Users can create tasks with proper validation --- ## Phase 5: User Story 2 - Complete Task (Priority: P1) **Goal**: Users can mark tasks as complete/incomplete with visual feedback ### Implementation for User Story 2 - [ ] T018 [US2] Add completion toggle to TaskItem component - [ ] T019 [US2] Style completed tasks with strikethrough - [ ] T020 [US2] Write completion toggle tests **Checkpoint**: Tasks can be toggled between complete/incomplete states Spec Kitを使ってみた感想 良かった点 ✅ 仕様が明確になる 「タスクを作成する」という曖昧な要件が、具体的なAcceptance Scenariosに落とし込まれる エッジケースを事前に洗い出せる ✅ AIへの指示が楽になる 「T010を実装して」で済む(仕様は参照すればいい) 実装の判断に迷ったときに仕様書に戻れる ✅ MVPファーストの開発 ユーザーストーリー単位で優先度が明確 P1だけ実装→テスト→リリースが可能 注意点 ❌ 最初の仕様入力は人間が考える必要がある 「シンプルなTodoアプリ」だけでは曖昧すぎる ある程度の要件整理は事前に必要 ❌ オーバーヘッドがある 小規模な修正には向かない ドキュメント生成に時間がかかる ❌ テンプレートがやや重厚 全セクションを埋めようとすると大変 必要に応じてカスタマイズ推奨 まとめ 今回はGitHub Spec Kitを使ってTodoアプリの仕様を生成してみました。 この記事で学んだこと ✅ Spec Kitは「仕様→計画→タスク→実装」の4フェーズワークフロー ✅ 各フェーズにゲートがあり、品質を担保しながら進める ✅ ユーザーストーリー単位でMVPファーストの開発が可能 ✅ Claude Code、GitHub Copilot、Cursorなど15以上のAIエージェントに対応 AIコーディングエージェントで「思ってたのと違う」を減らしたい方、ぜひSpec Kitを試してみてください! 参考リンク 公式リソース GitHub Spec Kit リポジトリ Spec-driven development with AI(GitHub Blog) ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GitHub Spec Kit入門|AIコーディングエージェントで仕様駆動開発を実践する first appeared on SIOS Tech Lab .
はじめに サイオステクノロジーのひろです。アドベントカレンダー16日目です。 この記事ではAzure OpenAI入門と題して、まずAzure OpenAIモデルのデプロイ方法、Pythonを使ってAPIを叩くまでの手順をまとめていきたいと思います。 この記事のゴール Azure OpenAIモデルのデプロイ + pythonでAPIを叩く Azure OpenAIリソースの作成 まず、Azureポータルにログインし、リソース作成画面へ移動します。 検索窓でAzure OpanAIと検索し、表示されたAzure OpenAIリソースを選択して「作成」を押下しましょう。 作成を押下すると、リソース作成画面へ移動します。ここで必須項目を入力します。 ・サブスクリプション:お使いのサブスクリプションを選択 ・リソースグループ:任意のリソースグループを選択 ・リージョン:利用したいリージョン(例:East US 2等) ・名前:リソース名を入力 ・価格レベル:Standard S0を選択 名前の入力 名前はテナント内だけでなく、Azure全体で一意である必要があります。 他のユーザがすでに使用している場合その名前は使用できないので注意が必要です。 必要な項目を入力したら、「作成」ボタンを押下します。 しばらく待つとリソース作成完了です。 Azure OpenAIモデルのデプロイ リソース作成が完了したら、作成したリソース画面へ遷移します。 検索欄の右横にある「Go to Foundry portal」を押下してMicrosoft Foundry portalへ移動します。 Microsoft Foundry portal(旧Azure AI Foundry portal)が開いたら、左メニューの「デプロイ」を選択し、「モデルデプロイ」を選択します。 今回私は「基本モデルのデプロイ」を選択し、gpt-4o-miniを選択しました。 モデル毎に金額は異なりますので注意しましょう。 参考: https://azure.microsoft.com/ja-jp/pricing/details/cognitive-services/openai-service/ 各項目について解説していきます。 1.デプロイの種別 デプロイの種別は「データ処理の地理的範囲」と「データ処理手法」の組み合わせで定義されています。 地理的範囲には、以下の3つの種類があります。 地理的範囲 説明 グローバル Azureのグローバルインフラを活用し、ユーザのリクエストを世界中のデータセンターから最適な場所へルーティングし、データ処理。 データゾーン リージョンより広域な指定されたデータゾーンでデータ処理。グローバルとリージョンの中間の選択肢。 リージョン リソースを作成したリージョンでデータ処理。 地理的範囲は可用性と応答性に関わります。地理的範囲をグローバルにしておけば、特定のリージョンに不具合が起きてもそのほかのリージョンでデータが処理されるため、可用性が向上します。リージョンにすると、特定のリージョン内のみでデータが処理されるため、データの移動が制限される場合などに適しています。 データ処理手法には、標準(standard)、バッチ、プロビジョニング済みスループットの3つ種類があります。 データ処理手法 説明 標準(standard) 従量課金制。リクエスト毎にトークン単位で課金されます。 リアルタイムでデータ処理を行う。 バッチ 非同期でデータを処理する割引モデル。 受け付けたデータを24時間以内に処理。 プロビジョニング済みスループット 事前に支払いを行い、リソースを事前に確保できる。 スループット(処理能力)が保証される。 バッチではStandardより50%低コストで使用できるため、リアルタイム性が重視されない場合に使用したいですね。 今回は地理的範囲がリージョンのStandardを選択しました。 編集不能な項目 デプロイ名とデプロイの種類は後から編集することができないため慎重に設定しましょう。 2.1分あたりのトークン数レート制限 この項目ではAPI利用量の上限を定めることができます。 TPM (Tokens Per Minute) 1分あたりに処理できるトークン数の上限 RPM (Requests Per Minute) 1分あたりに送信できるリクエスト数の上限 トークンとは生成AIモデルがテキストを処理する最小単位で、プロンプトや回答の文字数等によって消費量が変動します。 モデルによっても異なりますが、日本語だと1文字1~2トークン程と言われています。 3.コンテンツフィルター コンテンツフィルターは有害なコンテンツ(暴力、嫌悪、性的、自傷行為の4つのカテゴリに属するもの)を検出及びフィルタリングする機能です。デフォルトの設定を使用します。 4.動的クォータ クォータとは割り当てという意味があり、今回の場合は先ほど設定したTPMの上限値のことです。 有効にすると、azure側に余剰リソースがある場合、設定したTPM上限を超えてリクエストを処理できるようになります。 TPM上限以上のトークン数を消費する場面で、動的クォータがオフであれば429エラーが返されます。 動的クォータがオンであれば、一時的に上限が増え、レスポンスが返る可能性があります。 上限を超えた追加のクォータ分は課金対象なので注意が必要です。 必要な項目をすべて選択して右下の青い「デプロイ」ボタンを押下するとモデルデプロイ完了です。 作成後、チャットプレイグラウンドへ移動し、チャットを送信して回答が返ってくることを確認してみましょう。 「こんにちは」と入力すると自然な文章を返してくれることが確認できました。 PythonでAPIを叩いてみよう プログラムからこのモデルを利用してみましょう。 Microsoft Foundry portalのデプロイタブに移動すると、サンプルコードが用意されているので、今回はこちらを使用してAPIの呼び出しを行います。 言語はpython、Javascript、C#等が用意されています。今回はpythonを使用して実行します。デプロイ画面に記述されていますが、こちらにもサンプルコードを載せておきます。 まずOpenAIの公式ライブラリをインストールします。 pip install openai 以下がサンプルコードです。endpoint、model_name、deployment、apiキーといった項目はデプロイタブで確認できますのでご自身の環境に合わせて変更してください。 なお、api_versionについては、今回はサンプルコードに記載があった「2024-12-01-preview」をそのまま使用しています。 APIバージョンの選び方について APIバージョンには安定版とPreviewが存在します。 安定版は文字通り安定したバージョンで、将来的な仕様の変更が起こらないため本番環境での使用が推奨されます。 Preview版は最新の機能を使うことができるため、新機能を試すのに最適です。 APIバージョンによっては、構造化出力等の新しい機能が使えない場合があるので、バージョン選択には注意が必要です。 構造化出力については次回以降解説します。 今回はサンプルコードを実行するだけなのでこのまま2024-12-01-previewを使用します。 安定版を使用したい場合、2024-10-21などに変更して実行してみてください。別のAPIバージョンを試したいという方は、以下のURL(Microsoft AzureのREST API仕様(GitHub))で確認できます。 参考:APIバージョン プレビュー版の一覧(Microsoft AzureのREST API仕様) 安定版の一覧(Microsoft AzureのREST API仕様) import os from openai import AzureOpenAI endpoint = "<your-endpoint>" model_name = "<your-model_name>" deployment = "<your-deployment>" subscription_key = "<your-api-key>" api_version = "2024-12-01-preview" client = AzureOpenAI( api_version=api_version, azure_endpoint=endpoint, api_key=subscription_key, ) response = client.chat.completions.create( messages=[ { "role": "system", "content": "You are a helpful assistant.", }, { "role": "user", "content": "I am going to Paris, what should I see?", } ], max_tokens=4096, temperature=1.0, top_p=1.0, model=deployment ) print(response.choices[0].message.content) コードを実行するとプロンプトとして送信されたメッセージの内容についてモデルから返答が行われます。 contentを日本語に翻訳して実行したところ以下のような回答が得られました。 まとめ この記事ではAzure OpenAIモデルのデプロイ方法、Pythonを使ってAPIを叩くまでの手順をまとめました。 次回以降はAzure OpenAIを使用した構造化データ出力やfunctionCallingといった、より実践的な機能について解説していきます。 参考文献 https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/deployment-types#deployment-types https://azure.microsoft.com/ja-jp/pricing/details/cognitive-services/openai-service/ https://learn.microsoft.com/ja-jp/azure/ai-foundry/what-is-azure-ai-foundry https://learn.microsoft.com/ja-jp/azure/ai-foundry/openai/concepts/content-filter https://learn.microsoft.com/ja-jp/azure/ai-foundry/openai/how-to/dynamic-quota?view=foundry-classic ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure OpenAI入門:モデルのデプロイとpythonからAPIを実行 first appeared on SIOS Tech Lab .
こんにちは。サイオステクノロジー武井です。今回は、AIエージェント/MCPサーバー実装ガイドを作成しましたので、そのご案内をしたいと思います。 AIエージェント/MCPサーバー実装ガイドとは? AIをより便利に活用するための  AIエージェント  や、その拡張技術である  MCP(Model Context Protocol)  を体系的に学べる教科書となっております。以下で実施するイベントにて、ダウンロードURLを公開し、皆様にダウンロードいただけるようになります。是非ともご参加ください。 AIエージェント/MCPサーバー実装ガイド 解説セミナー https://tech-lab.connpass.com/event/378331/ ガイドの総ページ数は 201ページ となっており、かなりのボリュームとなります。7章の構成となっており、読了頂くことにはもうMCPサーバーについては怖いもんなしという感じになっているはずです。章の構成は以下のとおりです。 第1章: AI エージェントとは → AIエージェントの基本的な概念をじっくり説明します。新旧の技術(AIエージェントある場合/ない場合)を比較して、AIエージェントのつよみをわかりみ深く説明します。 第2章: AI エージェントを実現する ReAct  → AIエージェントを実現する技術の一つであるプロンプトエンジニアリング「ReAct」を取り上げることで、AIエージェントの基本的な動作を知ることができます。 第3章: Function Calling による AI エージェント の実装  → Function Callingによる実用的なAIエージェントの実装を学ぶことができます。 第4章:   MCP とは → MCPの基本的な理論からプロトコルの構造やデータの流れまで理解することができます。 第5章: MCP サーバーを作ってみよう  → とてもシンプルなMCPサーバーの実装を通して、MCPサーバーの仕組みを理解することができます。 第6章:   MCPの認可 → エンタープライズ向けのMCPサーバーに必須の機能である認可について詳しく解説します。 第7章: MCP 対応 AI エージェントを作ろう → 今までの集大成としてMCPサーバーを用いた本格的なAIエージェントの実装方法を解説します。 対象 本ガイドの対象読者は以下を想定しております。 これからAIエージェント/MCPを学ぶ開発者 AIエージェント/MCP開発プロジェクトのPL/PM AIエージェント開発者/MCP開発プロジェクトをリードするプロジェクトマネージャーやプロジェクトリーダーの方にもお読みいただける内容となっております。なぜならば、もちろん「実装ガイド」という名前なので、実装の方法やソースコードを余すことなく紹介しているのですが、それだけではなく、AIエージェント/MCPサーバーの基礎理論も図解たっぷりで説明しています。 よって、 実装の細部に踏み込む前段階として全体像や考え方を把握したい方 や、 技術選定やアーキテクチャ設計の判断が求められる立場の方 にも、無理なく読み進めていただけます。 AIエージェントやMCPに初めて触れる方でも、「なぜこの仕組みが必要なのか」「どのような役割分担で構成されているのか」といった背景から理解できる構成としており、開発者とPM/PLの共通言語をつくるための資料としても活用いただける内容です。 そのため、本ガイドは単なる実装手順書ではなく、 AIエージェント/MCPをプロジェクトとして成功させるための土台となる知識を体系的に整理したガイド としてご利用いただけます。 特徴 では、本ガイドの3つの大きな特徴を説明させていただきます。 特徴その1: 図をふんだんに使っている 図や画面ショットなどをふんだんに用いて、基本的な概念をイメージでわかりやすく伝えております。やっぱり直感的な理解を助けるのは、テキストよりも図!!ということで、わかりみの深い図解を多用しており、きっとAIエージェントやMCPの複雑な概念もスッと入ってくることでしょう。 特徴その2: 基礎のキから説明している AI エージェントや MCP の仕組みを基礎から解説しているため、単なるツールの操作説明にとどまらず、実際にどのように活用できるのかまでしっかり理解できる内容になっています。 特徴その3: すぐ動くソースコードを公開している AIエージェント/MCPの理論だけではなく、すぐ動くソースコードを本書にて公開しています。また、ソースコードの詳しい説明も載せております。 ぜひダウンロードを!! 本ガイドにて、皆様のAIエージェント/MCPサーバー開発の一助になりましたら幸いです。 繰り返しになりますが、以下で 実施するイベントにて、ダウンロードURLを公開し、皆様にダウンロードいただけるようになります。是非ともご参加ください。 AIエージェント/MCPサーバー実装ガイド 解説セミナー https://tech-lab.connpass.com/event/378331/ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AIエージェント/MCPサーバー実装ガイドを作成しました first appeared on SIOS Tech Lab .
はじめに 皆さん、こんにちは!PS-SLの織田です。今回は、『 Java言語で学ぶデザインパターン入門 』の第2章を読んだ感想をまとめていきたいと思います。第1章ではIteratorパターンを扱いましたが、第2章ではAdapterパターンについて書かれています。第1章のブログは コチラ からご覧ください。それでは詳しく見ていきましょう! Adapterパターンとは Adapterパターンとは「すでに提供されているもの」と「本当に必要なもの」のズレを埋めるためのパターンです。文字で説明しても何のこっちゃという感じなので、具体的なコードとともに説明していきます。 すでに提供されているもの:Banner.java public class Banner { private String string; public Banner(String string) { this.string = string; } public void showWithParen() { System.out.println("(" + string + ")"); } public void showWithAster() { System.out.println("*" + string + "*"); } } このクラスは「すでに提供されているもの」を表現します。文字列を受け取り、その文字列を括弧で囲んで表示するshowWithParen()メソッドと、アスタリスクで囲んで表示するshowWithAster()メソッドを提供します。前提として、このクラスには若干修正したい内容(言語ごとに表示内容を変えたい)があるのですが、下手に触るとバグが発生する恐れがあるため、変更することなくそのまま使用したい既存の資産という位置づけです。 本当に必要なもの Print.java public interface Print { public abstract void printWeak(); public abstract void printStrong(); } このインターフェースは「こういう形で使いたい」という理想的な仕様を定義します。弱い表示を行うprintWeak()メソッドと、強い表示を行うprintStrong()メソッドという抽象メソッドを宣言しています。これはあくまで仕様の定義にすぎないため、具体的な実装は書かれていません。実装は次のクラスで表現されます。 橋渡し役:PrintBanner.java public class PrintBanner extends Banner implements Print { public PrintBanner(String string) { super(string); } @Override public void printWeak() { // 言語設定に応じて表示を変更 if (isJapanese()) { System.out.println("《"); showWithParen(); System.out.println("》"); } else { showWithParen(); // 日本語以外では既存処理 } } @Override public void printStrong() { // 言語設定に応じて表示を変更 if (isJapanese()) { System.out.println("《"); showWithAster(); System.out.println("》"); } else { showWithAster(); // 日本語以外では既存処理 } } このクラスがAdapterパターンの核心部分です。BannerクラスとPrintインターフェースを橋渡しする役割を担います。「日本語とその他の言語で表示方法を切り替えたい!」というニーズに応えつつ、もとの関数は一切修正していません。 printWeak()メソッドの実装では継承したshowWithParen()メソッドを、printStrong()メソッドの実装では継承したshowWithAster()メソッドを呼び出します。これにより、既存のBannerクラスの機能をPrintインターフェースの形で利用できるように変換しています。 元のshowWithParen()やshowWithAster()のコードを修正しなくても、ニーズに合わせた微調整を行うことができています。 利用例:Main.java public class Main { public static void main(String[] args) { Print p = new PrintBanner("Hello"); p.printWeak(); p.printStrong(); } } このクラスはAdapterパターンを利用するクライアント側の実装例です。重要なポイントは、Print型の変数を宣言し、PrintBannerのインスタンスを代入している点です。 クライアントコードからはBannerクラスの存在は完全に隠蔽されており、Printインターフェースのメソッドのみを使用してBannerクラスの機能を利用しています。この設計により、将来的にBannerクラス以外の実装に変更したい場合でも、クライアントコードは全く変更する必要がありません。 Adapterパターンで嬉しいこと 「元のコードを修正しないで良いのは分かったけど、インターフェースを作る必要はないんじゃない?」と思う人がいるかもしれません。しかし、このパターンを使うことで受けられる恩恵があります。例えば、現在は単純な文字列を出力していますが、将来的に「HTMLで出力するクラス」を使いたくなったとします。こうなると、「複数のファイルにまたがって修正を実施しなきゃいけないのか…」と思うかもしれませんが、実は新しいクラスとアダプターを追加すれば大丈夫で、Mainを修正する必要はほぼありません。 新しいクラス:HtmlDisplay.java public class HtmlDisplay { private String content; public HtmlDisplay(String content) { this.content = content; } public void showAsEmphasis() { System.out.println("<em>" + content + "</em>"); } public void showAsStrong() { System.out.println("<strong>" + content + "</strong>"); } } 新しいアダプター:HtmlPrintBanner.java public class HtmlPrintBanner extends HtmlDisplay implements Print { public HtmlPrintBanner(String string) { super(string); } @Override public void printWeak() { showAsEmphasis(); } @Override public void printStrong() { showAsStrong(); } } AdapterパターンなしのMain.java public class Main { public static void main(String[] args) { // 変更前:Bannerを直接使用 // Banner banner = new Banner("Hello"); // banner.showWithParen(); // banner.showWithAster(); // 変更後:HtmlDisplayに変更 HtmlDisplay html = new HtmlDisplay("Hello"); html.showAsEmphasis(); // メソッド名も変更が必要 html.showAsStrong(); // メソッド名も変更が必要 } } AdapterパターンありのMain.java public class Main { public static void main(String[] args) { // 変更前 Print p = new PrintBanner("Hello"); // 変更後:この1行だけ変更すれば済む Print p = new HtmlPrintBanner("Hello"); // 以下のコードは全く変更不要 p.printWeak(); p.printStrong(); } } Adapterパターンあり:クライアントコード上の修正を最小限に抑えられる Adapterパターンなし:より多くの変更を実施する必要がある また、もっと複雑な処理をする際にはより重宝します。例えば表示方法を動的に切り替える際を考えてみましょう。 AdapterパターンありのMain.java public class Main { public static void main(String[] args) { // 設定ファイルや環境変数で切り替え可能 String outputType = System.getProperty("output.type", "banner"); Print p; switch(outputType) { case "html": p = new HtmlPrintBanner("Hello"); break; case "xml": p = new XmlPrintBanner("Hello"); break; default: p = new PrintBanner("Hello"); } // どの実装でも同じコードで動作 p.printWeak(); p.printStrong(); } } AdapterパターンなしのMain.java public class Main { public static void main(String[] args) { String outputType = System.getProperty("output.type", "banner"); // 各クラスのインスタンスを別々に管理する必要がある Banner banner = null; HtmlDisplay htmlDisplay = null; XmlDisplay xmlDisplay = null; switch(outputType) { case "html": htmlDisplay = new HtmlDisplay("Hello"); break; case "xml": xmlDisplay = new XmlDisplay("Hello"); break; default: banner = new Banner("Hello"); } // 弱い表示の処理 - 全パターン分岐が必要(インターフェースでメソッド名を統一していない弊害) switch(outputType) { case "html": htmlDisplay.showAsEmphasis(); break; case "xml": xmlDisplay.displayAsItalic(); break; default: banner.showWithParen(); } // 強い表示の処理 - また全パターン分岐が必要 switch(outputType) { case "html": htmlDisplay.showAsStrong(); break; case "xml": xmlDisplay.displayAsBold(); break; default: banner.showWithAster(); } } } Adapterパターンなしの場合には、インターフェースによるメソッド名の統一がなされていないため、呼び出すクラスに応じてメソッドの名前を別々に表記する必要があります。そのため、クライアントコードは複雑かつ長大になってしまっています。 まとめ Adapterパターンの本質は、「変更に強いシステム」の実現にあります。既存のコードを活かしながら、新しい要求に柔軟に対応できる設計を提供します。特に重要なのは、インターフェースによるメソッド名の統一です。これにより、開発者は複雑な実装詳細を意識することなく、一貫した方法でシステムを操作できるようになります。 実際の開発では、「既存のコードは変更したくないが、新しい方法で使いたい」という場面が発生するかと思います。そんな時、Adapterパターンは既存システムの価値を最大限に活かしながら、将来の拡張性も確保する理想的な解決策となるのです。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post デザインパターンのすゝめ ~Adapterパターン編~ first appeared on SIOS Tech Lab .
はじめに PS-SLの佐々木です。 アドベントカレンダー14日目になります。 今回はRAGシステムを構築している際にデータの一覧や統計データの取得、集計をしたい場合のTipsを紹介します セマンティック検索が苦手な質問 RAG(Retrieval-Augmented Generation)システムを構築したことがある方なら、こんな経験はないでしょうか。 ユーザー: 「完了率を教えてください」 RAG: 「完了に関する情報が見つかりました。タスクAは完了しています。タスクBも完了しています...」 ユーザー: 「いや、パーセンテージで知りたいんだけど...」 セマンティック検索(ベクトル検索)は「意味的に関連する情報を取得する」ことは得意ですが、「集計」「統計」「条件フィルタリング」は苦手です。 本記事では、この限界を突破するために LangChainのSQL Agentを組み合わせる アプローチを紹介します。 セマンティック検索の得意・不得意 得意なこと クエリ例 なぜ得意か 「冷却システムの問題点は?」 意味的に関連するドキュメントを取得 「バッテリーに関する懸念事項」 「バッテリー」「電池」「蓄電池」など類似概念も取得 「納期遅延のリスクについて」 文脈を理解して関連情報を取得 不得意なこと クエリ例 なぜ不得意か 「完了率は何%?」 集計計算ができない 「担当者が田中のタスク一覧」 完全一致フィルタリングが苦手 「期限が来週までのものは何件?」 日付比較・カウントができない 「担当者ごとの件数は?」 GROUP BY相当の処理ができない セマンティック検索は「類似度」で検索するため、「田中」で検索すると「田中」だけでなく「山田」「中田」など”なんとなく似ている”ものも返してしまう可能性があります。 解決策:SQL Agentとのハイブリッドアーキテクチャ 全体像 ┌─────────────────────────────────────────────────────────────┐ │ ユーザーの質問 │ │ 「冷却システムの問題点は?」「完了率は?」「田中の担当分は?」 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ LLM(ルーター) │ │ 質問の意図を分析し、適切なツールを選択 │ └─────────────────────────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ セマンティック検索 │ │ SQL Agent │ │ その他ツール │ │ (Azure AI Search │ │ (SQLite) │ │ │ │ / Pinecone等) │ │ │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ ▼ ▼ 意味的に関連する 正確なフィルタリング ドキュメント取得 集計・統計計算 データの二重管理 同じデータを2つの形式で保持します: ベクトルDB : セマンティック検索用(埋め込みベクトル + メタデータ) RDB(SQLite等) : 構造化クエリ用(正規化されたテーブル) 元データ(JSON/Excel等) │ ├──→ ベクトルDB(Azure AI Search / Pinecone) │ - content: テキスト全文 │ - embedding: ベクトル │ - metadata: 付随情報 │ └──→ SQLite - record_no: INT - assignee: TEXT - status: TEXT - deadline: DATE - ... 実装:LangChainのSQL Agent LangChainには公式の SQLDatabaseToolkit と create_sql_agent が用意されており、簡単にSQL Agentを構築できます。 公式のSQL Agentを使う方法 from langchain_community.utilities import SQLDatabase from langchain_community.agent_toolkits import SQLDatabaseToolkit from langchain.agents import create_agent from langchain_openai import ChatOpenAI # 1. データベース接続 db = SQLDatabase.from_uri( "sqlite:///mydata.db" ) print ( f"利用可能なテーブル: {db.get_usable_table_names()} " ) # 2. LLMの準備 llm = ChatOpenAI(model= "gpt-4" , temperature= 0 ) # 3. SQLToolkitの作成 toolkit = SQLDatabaseToolkit(db=db, llm=llm) tools = toolkit.get_tools() # 4. エージェントの作成 system_prompt = """あなたはSQLデータベースと対話するエージェントです。 質問に対して、正しいSQLクエリを作成し、結果を確認して回答してください。 """ agent = create_agent(llm, tools, system_prompt=system_prompt) # 5. 実行 result = agent.invoke({ "messages" : [{ "role" : "user" , "content" : "完了率を教えて" }]}) SQLDatabaseToolkitが提供する4つのツール ツール名 用途 sql_db_query SQLクエリを実行し、結果またはエラーを返す sql_db_schema 指定テーブルのスキーマとサンプル行を取得 sql_db_list_tables 利用可能なテーブル一覧を表示 sql_db_query_checker 実行前にクエリの構文をチェック エージェントの動作フロー: sql_db_list_tables   でテーブル一覧を確認 sql_db_schema   で関連テーブルの構造を確認 sql_db_query_checker   でクエリを検証 sql_db_query   で実行 エラーが発生した場合、エラーメッセージがLLMに返され、自動的にクエリを修正して再実行します。 セキュリティ対策(重要)   警告 : SQL Q&Aシステムの構築には、モデルが生成したSQLクエリを実行する必要があります。これには固有のリスクがあります。 公式ドキュメントの推奨事項: 権限を最小限に : Read-Only接続を使用 テーブルを制限 : 必要なテーブルのみアクセス許可 Human-in-the-Loop : 重要なクエリは人間が承認 # Read-Only接続の例(SQLite) uri = "sqlite:///file:mydata.db?mode=ro&uri=true" db = SQLDatabase.from_uri( uri, include_tables=[ "allowed_table" ], # 許可テーブルを制限 sample_rows_in_table_info= 3 , ) Human-in-the-Loop(人間による承認) LangChain 2025では、クエリ実行前に人間の承認を求める機能が組み込まれています。 from langchain.agents.middleware import HumanInTheLoopMiddleware from langgraph.checkpoint.memory import InMemorySaver agent = create_agent( llm, tools, system_prompt=system_prompt, middleware=[HumanInTheLoopMiddleware( interrupt_on={ "sql_db_query" : True } # クエリ実行時に一時停止 )], checkpointer=InMemorySaver() ) SQL生成用プロンプトの例 prompt = ChatPromptTemplate.from_messages([ ( "system" , """あなたはSQLの専門家です。 ## ルール 1. SELECT文のみ生成 2. SQLのみ返答(説明不要) ## テーブル情報 {table_info} ## クエリ例 完了率: SELECT COUNT(*) as total, SUM(CASE WHEN status = '完了' THEN 1 ELSE 0 END) as completed, ROUND(SUM(CASE WHEN status = '完了' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 1) as rate FROM records 担当者別件数: SELECT assignee, COUNT(*) as count FROM records GROUP BY assignee ORDER BY count DESC """ ), ( "human" , "{question}" ) ]) --- ## LLMによるツール選択の実装 ### ツールの定義 ```python from langchain_core.tools import tool @tool async def semantic_search ( query: str , top_k: int = 5 ) -> str : """意味的に関連するドキュメントを検索します。 「〜について」「〜に関する」「〜の問題点」などの質問に使用。 """ # ベクトル検索の実装 results = await vector_store.search(query, top_k) return format_results(results) @tool async def sql_query ( question: str ) -> str : """データの集計・フィルタリング・統計を取得します。 「何件?」「完了率は?」「担当者が〜」「一覧」などの質問に使用。 """ result = await sql_agent.execute(question) return json.dumps(result, ensure_ascii= False ) システムプロンプトで使い分けを指示 ポイント:LLMが適切なツールを選べるよう、判断基準を明示します。 ## ツール選択の判断基準 | ユーザーの意図 | 使うツール | キーワード例 | |--------------|-----------|-------------| | 意味的に関連する情報 | semantic _search | 「〜について」「〜に関する」 | | 件数・統計 | sql_ query | 「何件」「完了率」「平均」 | | 条件フィルタ | sql _query | 「担当者が〜」「ステータスが〜」 | | 一覧取得 | sql_ query | 「〜の一覧」「すべての〜」 | ### 具体例 ✅ semantic _search を使う: - 「冷却システムの問題点は?」→ 意味的に関連する情報を取得 ✅ sql_ query を使う: - 「担当者が田中のタスク」→ WHERE assignee = '田中' - 「完了率は?」→ COUNT + CASE WHEN - 「来週期限のものは?」→ WHERE deadline <= '2025-01-17' 実際のクエリ例と結果 例1: セマンティック検索が適切なケース ユーザー: 「バッテリー関連のリスクについて教えて」 → semantic_search("バッテリー リスク") 結果: - バッテリー劣化による航続距離低下のリスク - 充電インフラ不足による利便性低下 - 電池廃棄時の環境負荷 (意味的に関連する情報を幅広く取得) 例2: SQLが適切なケース ユーザー: 「担当者ごとの未完了件数を教えて」 → sql_query("担当者ごとの未完了件数") 生成されたSQL: SELECT assignee, COUNT(*) as count FROM records WHERE status != '完了' GROUP BY assignee ORDER BY count DESC 結果: | assignee | count | |----------|-------| | 田中 | 12 | | 佐藤 | 8 | | 鈴木 | 5 | 例3: 組み合わせが必要なケース ユーザー: 「田中さんの担当分の詳細を教えて」 → 1. sql_query("担当者が田中のrecord_no一覧") → [1, 5, 12, 23, ...] → 2. semantic_search("record_no:1 OR record_no:5 OR ...") → 各レコードの詳細情報 メリットと考慮点 メリット 項目 セマンティック検索のみ + SQL Agent 意味検索 完全一致フィルタ △(不正確) 集計・統計 日付範囲検索 △ GROUP BY 考慮点 データの二重管理 : ベクトルDBとRDB両方にデータを投入・同期する必要がある スキーマ設計 : SQLで検索したい項目は正規化してテーブル設計する セキュリティ : SQL Injection対策、Read-Only接続、クエリバリデーション まとめ セマンティック検索は強力ですが、万能ではありません。 「〜について教えて」→ セマンティック検索 「何件?」「完了率は?」「担当者が〜」→ SQL LangChainのSQL Agentを組み合わせることで、RAGシステムの回答可能な範囲が大幅に広がります。 特に業務システムでは「統計」「集計」「正確なフィルタリング」の要求が多いため、この組み合わせは非常に効果的です。 ぜひ試してみてください。 参考 LangChain SQL Agent LangGraph Documentation Azure AI Search ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【LangChain】SQL AgentでRAGに集計・統計機能を追加する方法 first appeared on SIOS Tech Lab .
PS-SLの佐々木です。 アドベントカレンダー13日目になります この記事では、Next.js 14プロジェクトにStorybookとPlaywright E2Eテストを導入し、Chromaticを使ってGitHub Actionsで自動ビジュアルテストを実現するまでの過程を解説します。 Chromaticとは Chromatic は、Storybookチームが提供するビジュアルテスト・UIレビュープラットフォームです。 なぜChromaticを使うのか 課題 Chromaticでの解決 CSSの変更が他のコンポーネントに影響していないか不安 全Storiesのスナップショットを自動比較 PRレビューでUIを確認するのが面倒 プレビューURLが自動でPRにコメントされる デザインシステムのドキュメントが古くなる 常に最新のStorybookがホスティングされる E2Eテストの画面キャプチャを管理したい Playwright連携でE2Eもビジュアルテスト化 今回のプロジェクト構成 project/ ├── frontend/ │ ├── src/ │ │ ├── components/ │ │ │ ├── ui/ # shadcn/ui ベースの共通コンポーネント │ │ │ │ ├── button.tsx │ │ │ │ ├── button.stories.tsx │ │ │ │ ├── card.tsx │ │ │ │ ├── card.stories.tsx │ │ │ │ ├── input.tsx │ │ │ │ ├── input.stories.tsx │ │ │ │ └── textarea.tsx │ │ │ ├── chat/ # チャット機能コンポーネント │ │ │ │ ├── MessageList.tsx │ │ │ │ ├── MessageList.stories.tsx │ │ │ │ ├── MessageInput.tsx │ │ │ │ └── ModeSelector.tsx │ │ │ └── import/ # インポート機能コンポーネント │ │ │ ├── FileUploader.tsx │ │ │ └── ImportProgress.tsx │ │ └── app/ │ ├── e2e/ # Playwright E2Eテスト │ │ └── home.spec.ts │ ├── .storybook/ │ │ ├── main.ts │ │ └── preview.ts │ └── package.json ├── backend/ └── .github/ └── workflows/ └── ci.yml 技術スタック Next.js 14 (App Router) shadcn/ui + Tailwind CSS Storybook 8.4 Playwright @chromatic-com/playwright Storybookのセットアップ 1. Storybookの初期化 cd frontend npx storybook@latest init 2. 設定ファイル .storybook/main.ts : import type { StorybookConfig } from '@storybook/nextjs' ; const config : StorybookConfig = { stories : [ '../src/**/*.mdx' , '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)' ] , addons : [ '@storybook/addon-essentials' , '@storybook/addon-interactions' , '@storybook/addon-links' , ] , framework : { name : '@storybook/nextjs' , options : { } , } , docs : { autodocs : 'tag' , } , staticDirs : [ '../public' ] , } ; export default config ; .storybook/preview.ts : import type { Preview } from '@storybook/react' ; import '../src/app/globals.css' ; // Tailwind CSSを読み込む const preview : Preview = { parameters : { controls : { matchers : { color : / (background|color)$ / i , date : / Date$ / i , } , } , } , } ; export default preview ; 3. package.jsonのスクリプト { "scripts" : { "storybook" : "storybook dev -p 6006" , "build-storybook" : "storybook build" , "chromatic" : "chromatic --exit-zero-on-changes" } } コンポーネントのStories作成 実際に作成したStoriesの例を紹介します。 UIコンポーネント(Button) src/components/ui/button.stories.tsx : import type { Meta , StoryObj } from '@storybook/react' ; import { Button } from './button' ; const meta : Meta < typeof Button > = { title : 'UI/Button' , component : Button , parameters : { layout : 'centered' , } , tags : [ 'autodocs' ] , argTypes : { variant : { control : 'select' , options : [ 'default' , 'destructive' , 'outline' , 'secondary' , 'ghost' , 'link' ] , } , size : { control : 'select' , options : [ 'default' , 'sm' , 'lg' , 'icon' ] , } , } , } ; export default meta ; type Story = StoryObj < typeof meta > ; export const Default : Story = { args : { children : 'Button' , variant : 'default' , } , } ; export const Secondary : Story = { args : { children : 'Secondary' , variant : 'secondary' , } , } ; export const Outline : Story = { args : { children : 'Outline' , variant : 'outline' , } , } ; export const Destructive : Story = { args : { children : 'Destructive' , variant : 'destructive' , } , } ; export const Small : Story = { args : { children : 'Small' , size : 'sm' , } , } ; export const Large : Story = { args : { children : 'Large' , size : 'lg' , } , } ; 機能コンポーネント(MessageList) チャット機能のメッセージ一覧コンポーネント。様々な状態をStoriesで表現します。 src/components/chat/MessageList.stories.tsx : import type { Meta , StoryObj } from '@storybook/react' ; import { MessageList } from './MessageList' ; import type { Message } from '@/types' ; const meta : Meta < typeof MessageList > = { title : 'Chat/MessageList' , component : MessageList , parameters : { layout : 'fullscreen' , } , tags : [ 'autodocs' ] , decorators : [ ( Story ) => ( < div className = "h-[500px] bg-gray-50" > < Story / > < / div > ) , ] , } ; export default meta ; type Story = StoryObj < typeof meta > ; // モックデータ const mockMessages : Message [ ] = [ { id : '1' , role : 'user' , content : '冷却システムの変更点を教えてください' , timestamp : new Date ( '2024-01-15T10:00:00' ) , } , { id : '2' , role : 'assistant' , content : '冷却システムには以下の変更点があります:\n\n1. 冷却ファンの形状を変更\n2. ヒートシンクの素材を変更' , sources : [ { id : 'src-1' , content : 'No.15: 冷却システム' , excel_row : 15 } , ] , timestamp : new Date ( '2024-01-15T10:00:05' ) , } , ] ; // 空の状態 export const Empty : Story = { args : { messages : [ ] , isLoading : false , onExport : ( ) => { } , } , } ; // メッセージがある状態 export const WithMessages : Story = { args : { messages : mockMessages , isLoading : false , onExport : ( ) => { } , } , } ; // ローディング状態 export const Loading : Story = { args : { messages : [ mockMessages [ 0 ] ] , isLoading : true , onExport : ( ) => { } , } , } ; // エクスポートボタン付き export const WithExportButton : Story = { args : { messages : [ ... mockMessages , { ... mockMessages [ 1 ] , id : '3' , exportReady : true , // エクスポート可能フラグ } , ] , isLoading : false , onExport : ( ) => alert ( 'Export clicked' ) , } , } ; インポート機能(ImportProgress) ファイルインポートの進捗表示コンポーネント。各状態をStoriesで網羅します。 src/components/import/ImportProgress.stories.tsx : import type { Meta , StoryObj } from '@storybook/react' ; import { ImportProgress } from './ImportProgress' ; const meta : Meta < typeof ImportProgress > = { title : 'Import/ImportProgress' , component : ImportProgress , parameters : { layout : 'centered' , } , tags : [ 'autodocs' ] , decorators : [ ( Story ) => ( < div className = "w-[400px]" > < Story / > < / div > ) , ] , } ; export default meta ; type Story = StoryObj < typeof meta > ; export const Idle : Story = { args : { status : 'idle' , result : null , } , } ; export const Uploading : Story = { args : { status : 'uploading' , result : null , } , } ; export const Success : Story = { args : { status : 'success' , result : { success : true , message : 'Excelファイルのインポートが完了しました' , documentCount : 25 , } , } , } ; export const Error : Story = { args : { status : 'error' , result : { success : false , message : 'ファイル形式が不正です。xlsx または xls ファイルをアップロードしてください。' , } , } , } ; Playwright E2EテストのChromatic対応 1. パッケージのインストール npm install --save-dev @chromatic-com/playwright 2. E2Eテストの修正 通常の @playwright/test の代わりに、 @chromatic-com/playwright からインポートします。 e2e/home.spec.ts : // Before: import { test, expect } from '@playwright/test'; // After: import { test , expect } from '@chromatic-com/playwright' ; test . describe ( 'Home Page' , ( ) => { test ( 'should display the header' , async ( { page } ) => { await page . goto ( '/' ) ; await expect ( page . locator ( 'h1' ) ) . toContainText ( 'Excel RAG' ) ; // テスト終了時に自動的にスナップショットが取得される } ) ; test ( 'should have navigation links' , async ( { page } ) => { await page . goto ( '/' ) ; await expect ( page . getByRole ( 'link' , { name : 'QA' } ) ) . toBeVisible ( ) ; await expect ( page . getByRole ( 'link' , { name : 'Import' } ) ) . toBeVisible ( ) ; } ) ; test ( 'should display mode selector' , async ( { page } ) => { await page . goto ( '/' ) ; await expect ( page . getByRole ( 'button' , { name : 'QA' } ) ) . toBeVisible ( ) ; await expect ( page . getByRole ( 'button' , { name : 'Export' } ) ) . toBeVisible ( ) ; } ) ; test ( 'should have message input' , async ( { page } ) => { await page . goto ( '/' ) ; await expect ( page . getByPlaceholder ( '質問を入力してください' ) ) . toBeVisible ( ) ; } ) ; } ) ; test . describe ( 'Import Page' , ( ) => { test ( 'should display file uploader' , async ( { page } ) => { await page . goto ( '/import' ) ; await expect ( page . locator ( 'h2' ) ) . toContainText ( 'Excel帳票インポート' ) ; } ) ; } ) ; 3. Playwright設定 playwright.config.ts は通常通りでOK: import { defineConfig , devices } from '@playwright/test' ; export default defineConfig ( { testDir : './e2e' , fullyParallel : true , forbidOnly : ! ! process . env . CI , retries : process . env . CI ? 2 : 0 , workers : process . env . CI ? 1 : undefined , reporter : 'html' , use : { baseURL : 'http://localhost:3000' , trace : 'on-first-retry' , } , projects : [ { name : 'chromium' , use : { ... devices [ 'Desktop Chrome' ] } , } , ] , webServer : { command : 'npm run dev' , url : 'http://localhost:3000' , reuseExistingServer : ! process . env . CI , } , } ) ; GitHub Actionsでの自動化 StorybookとE2Eで別々のChromaticプロジェクトを使用する設定です。 完全なワークフロー設定 .github/workflows/ci.yml : name : CI on : push : branches : [ master , develop ] pull_request : branches : [ master , develop ] concurrency : group : $ { { github.workflow } } - $ { { github.ref } } cancel-in-progress : true jobs : # =========================== # Chromatic - Storybook # =========================== chromatic-storybook : name : Chromatic - Storybook runs-on : ubuntu - latest defaults : run : working-directory : frontend steps : - uses : actions/checkout@v4 with : fetch-depth : 0 # 全履歴取得(差分比較に必要) - name : Setup Node.js uses : actions/setup - node@v4 with : node-version : '20' cache : 'npm' cache-dependency-path : frontend/package - lock.json - name : Install dependencies run : npm ci - name : Build Storybook run : npm run build - storybook - name : Publish Storybook to Chromatic id : chromatic uses : chromaui/action@latest with : projectToken : $ { { secrets.CHROMATIC_STORYBOOK_TOKEN } } workingDir : frontend storybookBuildDir : storybook - static exitZeroOnChanges : true autoAcceptChanges : master onlyChanged : true - name : Comment Storybook URL on PR if : github.event_name == 'pull_request' uses : actions/github - script@v7 with : script : | const storybookUrl = '${{ steps.chromatic.outputs.storybookUrl }}'; const buildUrl = '${{ steps.chromatic.outputs.buildUrl }}'; if (storybookUrl) { github.rest.issues.createComment( { issue_number : context.issue.number , owner : context.repo.owner , repo : context.repo.repo , body : ` ## Storybook Preview\n\n- [View Storybook](${storybookUrl})\n- [View Chromatic Build](${buildUrl})` } ); } # =========================== # Chromatic - E2E (Playwright) # =========================== chromatic-e2e : name : Chromatic - E2E runs-on : ubuntu - latest defaults : run : working-directory : frontend steps : - uses : actions/checkout@v4 with : fetch-depth : 0 - name : Setup Node.js uses : actions/setup - node@v4 with : node-version : '20' cache : 'npm' cache-dependency-path : frontend/package - lock.json - name : Install dependencies run : npm ci - name : Install Playwright browsers run : npx playwright install - - with - deps chromium - name : Run Playwright tests run : npx playwright test - name : Publish E2E to Chromatic id : chromatic - e2e uses : chromaui/action@latest with : projectToken : $ { { secrets.CHROMATIC_E2E_TOKEN } } workingDir : frontend playwright : true exitZeroOnChanges : true 必要なGitHub Secrets GitHubリポジトリの Settings > Secrets and variables > Actions で設定: Secret名 用途 取得方法 CHROMATIC_STORYBOOK_TOKEN Storybook用 Chromaticで「Storybook」プロジェクトを作成 CHROMATIC_E2E_TOKEN E2E用 Chromaticで「E2E」プロジェクトを作成 なぜ分けるのか? StorybookとE2Eは性質が異なるため、別プロジェクトで管理する方が見やすくなります: Storybook: コンポーネント単位のスナップショット E2E: ページ全体のスナップショット Chromaticの画面を見てみる 実際にPRを作成したりtargeブランチにPRがマージされると以下のように確認することができます。 Buildごとにどのコンポーネントがどのように変更されているかがスナップショットで確認できるようになっており、これを承認したりコメントを残したりすることができます。 UIはソースコードを眺めているだけではなかなか差分がわかりにくいため実際のビジュアルを手軽にできるのはとても良い機能だと思います。 ハマったポイントと解決策 1.   chromatic --playwright でアーカイブが見つからないエラー Chromatic archives directory cannot be found: /path/to/frontend/test-results/chromatic-archives 原因 : Playwrightテストを実行する前に chromatic --playwright を実行していた 解決策 : 先にPlaywrightテストを実行してからChromaticにアップロード - name : Run Playwright tests run : npx playwright test - name : Publish E2E to Chromatic uses : chromaui/action@latest with : playwright : true 2.   takeArchive が見つからないエラー Module '"@chromatic-com/playwright"' has no exported member 'takeArchive'. 原因 : 古いドキュメントを参照していた 解決策 :   @chromatic-com/playwright からは test と expect をインポートするだけでOK。テスト終了時に自動的にスナップショットが取得される。 // ❌ 間違い import { takeArchive } from '@chromatic-com/playwright' ; // ✅ 正しい import { test , expect } from '@chromatic-com/playwright' ; 3. Storybookビルドエラー(webpack関連) Module not found: TypeError: Cannot read properties of undefined (reading 'tap') 原因 :   @chromatic-com/playwright が古いStorybookバージョンを要求し、バージョン競合が発生 解決策 : Storybookのバージョンを8.4.2に統一 npm install @storybook/nextjs@8.4.2 @storybook/addon-essentials@8.4.2 \ @storybook/addon-interactions@8.4.2 @storybook/addon-links@8.4.2 \ @storybook/blocks@8.4.2 @storybook/react@8.4.2 @storybook/test@8.4.2 \ storybook@8.4.2 4. Chromaticの storybookBuildDir が必要 CIでStorybookをビルドしてからアップロードする場合、ビルド済みディレクトリを指定する必要がある。 - name : Build Storybook run : npm run build - storybook - name : Publish to Chromatic uses : chromaui/action@latest with : storybookBuildDir : storybook - static # これを指定 まとめ 導入後のワークフロー 開発者がPRを作成 GitHub ActionsがStorybookをビルド → Chromaticにアップロード GitHub ActionsがE2Eテスト実行 → Chromaticにアップロード PRにStorybookプレビューURLが自動コメント レビュアーがChromaticで差分を確認 問題なければマージ → masterブランチは自動承認 作成したファイル一覧 frontend/ ├── src/components/ │ ├── ui/ │ │ ├── button.stories.tsx │ │ ├── card.stories.tsx │ │ ├── input.stories.tsx │ │ └── textarea.stories.tsx │ ├── chat/ │ │ ├── MessageList.stories.tsx │ │ ├── MessageInput.stories.tsx │ │ └── ModeSelector.stories.tsx │ ├── import/ │ │ ├── FileUploader.stories.tsx │ │ └── ImportProgress.stories.tsx │ └── Introduction.mdx # デザインシステムドキュメント ├── e2e/ │ └── home.spec.ts # Chromatic対応済み ├── .storybook/ │ ├── main.ts │ └── preview.ts └── package.json .github/workflows/ └── ci.yml # Chromatic自動化設定 得られた効果 Before After UIレビューは手動で各画面を確認 PRにプレビューURLが自動投稿 CSS変更の影響範囲が不明 全コンポーネントのスナップショットで差分検出 E2Eテストは結果のみ確認 画面キャプチャも自動で比較・管理 デザインシステムのドキュメントが陳腐化 常に最新のStorybookがホスティング Chromaticを導入することで、UIの品質を担保しながら開発スピードを落とさないワークフローが実現できました。 参考リンク Chromatic公式ドキュメント chromaui/action (GitHub Action) @chromatic-com/playwright Storybook公式サイト ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Next.js + Storybook + PlaywrightをChromaticでビジュアルテスト自動化する first appeared on SIOS Tech Lab .
はじめに こんにちは、サイオステクノロジーの小野です。SIOS Tech Labアドベントカレンダー12日目の投稿です。 「SIOS社員が一年で学んだこと」がアドベントカレンダーのテーマですが、私はこの一年間〇〇Opsといった様々な開発・運用の効率を上げる仕組みを学んできました。 例えば、 OpenShift AIによってAIモデル開発の効率化を行うMLOps OpenShift AIのデータサイエンスパイプラインについて GitLabのCI/CDパイプラインとGateKeeperを組み合わせてセキュリティが高い状態で自動デプロイを行うDevSecOps DevSecOps実践ガイド:CI/CD環境の構築編 そして現在はRancher FleetのHelmOpsについて学んでいます。 今回はKubernetesクラスター環境へのアプリケーション自動デプロイ(CD)手段として、HelmOpsについて解説します。 ちなみにDevOpsから派生したこれらの〇〇Opsという名前が付く仕組みをまとめてxOpsと呼ぶそうです。 GitOps HelmOpsの説明をする前にアプリケーションの自動デプロイ手段として一般的なGitOpsについて解説します。 GitOpsとはGitリポジトリを唯一の信頼できる情報源(Single Source of Truth)として扱います。要するに、Gitリポジトリ内のソースコードの状態とデプロイされているアプリケーションの状態を自動的に同期する仕組みです。 したがって、Gitリポジトリの更新をするだけで、アプリケーションとそれがデプロイされているインフラ環境を自動的に更新することができます。 さらにGitでインフラ環境を管理できるので、インフラ環境のバージョン管理やロールバックがしやすいです。 例としてKubernetesクラスターにデプロイされているアプリケーションをGitOpsによって自動的に更新する流れについて解説します。 GitOpsフローの流れ(アプリケーションの更新の例) CIプロセス アプリ開発者がソースコードを更新し、アプリ用リポジトリにPushして、ブランチをマージする。 CIツールがアプリ用リポジトリの更新を検出すると、アプリケーションのコンテナイメージをビルドして、コンテナレジストリに保存する。 CIツールが新しいコンテナイメージを参照するようにHelmチャートをパッケージ化する。その後、このHelmチャートをHelmレジストリに保存する。 CDプロセス アプリ開発者が新しいHelmチャートを利用するようにアプリケーションのマニフェストファイルを更新し、設定用リポジトリにPushして、ブランチをマージする。 CDツールがGitリポジトリの更新を検出すると、設定用Gitリポジトリ内の設定ファイルを参照して、アプリケーションとそれがデプロイされているKubernetesクラスター環境を設定ファイルと同期させる Kubernetesクラスターが新しいHelmチャートと新しいコンテナイメージを利用してアプリケーションを更新する この説明ではCIとCDの区別をわかりやすくするために、あえてCDプロセスの設定用リポジトリの更新を開発者が行うようにしましたが、CIツールがコンテナイメージとHelmチャートの更新をした後、自動的に設定用リポジトリを更新することで、CIからCDまで自動化する場合もあります。 HelmOps HelmOpsとはHelmレジストリを唯一の信頼できる情報源(Single Source of Truth)として扱います。要するに、Helmレジストリ内のチャートファイルの状態とデプロイされているアプリケーションの状態を自動的に同期する仕組みです。 GitOpsとの差異はGitレジストリと同期するかHelmレジストリと同期するかの違いです。 HelmOpsも同様に、例としてKubernetesクラスターにデプロイされているアプリケーションをHelmOpsによって自動的に更新する流れについて解説します。 HelmOpsフローの流れ(アプリケーションの更新の例) CIプロセス アプリ開発者がソースコードを更新し、アプリ用リポジトリにPushして、ブランチをマージする。 CIツールがアプリ用リポジトリの更新を検出すると、アプリケーションのコンテナイメージをビルドして、コンテナレジストリに保存する。 CIツールが新しいコンテナイメージを参照するようにHelmチャートをパッケージ化する。その後、このHelmチャートをHelmレジストリに保存する。 CDプロセス HelmOpsツールがHelmレジストリの更新を検出すると、Helmレジストリ内のHelmチャートを参照してアプリケーションとそれがデプロイされているKubernetesクラスター環境を設定ファイルとHelmチャートと同期させる Kubernetesクラスターが新しいHelmチャートと新しいコンテナイメージを利用してアプリケーションを更新する HelmOpsはCDプロセスでGitを介さずにアプリケーションの更新を行うことができるので、シンプルな構成にできます。ただし、設定用Gitリポジトリを利用しない都合上、環境ごとにカスタマイズしてデプロイすることが難しくなります。 終わりに HelmOpsの仕組みについて解説しました。カスタマイズとその管理がしやすいGitOpsとシンプルな構成のHelmOpsについてはうまく使い分けることで、さらなる運用効率を上げることができるのではないかと思います。 参考 https://ndigi.tech/all_post/24547 https://about.gitlab.com/ja-jp/topics/gitops/ https://fleet.rancher.io/helm-ops https://www.docswell.com/s/yassan/5PGJG6-rancherjp-online-meetup-07#p1 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GitOpsだけじゃない!新たな選択肢「HelmOps」とは? first appeared on SIOS Tech Lab .
サイオステクノロジー武井です。今回は自宅にデジタルサイネージを導入した話をします。仕事とはまったく関係ないです。 デジタルサイネージとは? デジタルサイネージとは、電子的な看板のことです。店舗や公共の場で情報を表示するために使用されます。テレビやモニターを使って、広告や案内、ニュースなどを表示します。 モチベーション 私が自宅に導入したデジタルサイネージはこんな感じです。 全景はこんな感じです。 普通の人は自宅にデジタルサイネージ導入しないと思いますが、私は多分普通じゃないので。。。   で、きっかけとしては、自宅のデスクを飛行機のコックピットみたいにかっこよくしてみたいと言う理由だけで導入したのですが、実際に入れてみるとこれが意外にすごい便利でした。 デジタルサイネージの画面 デジタルサイネージの画面の構成はこんな感じです。 左上に時計と祝日のカレンダー、右上に現在の天気と予報、真ん中に直近(12時間先まで)の雨予報、その下にニュースヘッドライン、その下にニュースのYouTubeのライブ動画を垂れ流しています。 これ、すごい便利なんですよね。まぁ、全部普通にスマホで見れるでしょって感じなんですけど、やっぱ、視線をぱっと動かしただけで、必要な情報が全部見れるってのはすごい便利です。雨予報とかニュースのYouTubeとか個別にスマホで見るのは面倒くさいですからね。 直近の雨予報表示しているのは実は自作している部分なのですが、これ、洗濯物をしまうかどうかの判断にとても役立っておりまして、、洗濯物を外に干しているときに、あと何時間後に雨が降るかがぱっとわかるので、洗濯物をしまうタイミングが非常にわかりやすいです。 構成 デジタルサイネージを実現するソフトウェアはオープンソースのMagicMirror2というものを使用しています。これは一言でいえば 「Web技術で作られた、超拡張可能なスマートミラー/デジタルサイネージ用フレームワーク(OSS)」 であるといえます。 https://magicmirror.builders/ MagicMirror2はnode.jsで動作するWebアプリケーションであり、そのWebアプリケーションに対して、Electrtonという技術を使ってデスクトップアプリケーションとして動作させています。つまりクラサバ構成ということになっています。 先程説明した時計や祝日カレンダー、天気予報、ニュースヘッドラインなどはすべてMagicMirror2のモジュールとして提供されているものを使用しています。YouTubeのライブ動画表示もモジュールとして提供されているものを使用しています。つまり各機能は全てモジュールという形で提供されており、そのモジュールを画面上の好きな位置に配置することで、デジタルサイネージの画面を構成しています。以下のようなイメージです。 このMagicMirror2を導入するハードウェアはnode.jsが動作するものであれば何でも良いのですが、Raspberry Piを使っている例が多いです。私はそのへんに転がっていたRaspberry Pi 3を使用しています。スペック的には十分です。 ディスプレイは何でもいいのですが、解像度よりも画面の大きさが重要です。とはいいつつも私はそんな大きなディスプレイを持っていなかったので、余っていた16インチのモバイルディスプレイを使っています。 導入手順 では、実際にMagicMirror2を導入する手順を説明します。 MagicMirror2のインストール Raspberry Piでもなんでもいいのですが、node.jsが動作するLinuxマシン(多分Windowsでも大丈夫)を用意します。Raspberry Piの場合はRaspberry Pi OSをインストールしておきます。 次に以下のコマンドを実行して、MagicMirror2をインストールします。pm2というnode.jsのプロセスマネージャも一緒にインストールします。これはつまり、マシンの電源を入れたときに自動的にMagicMirror2が起動するようにしたり、MagicMirror2を簡単に再起動したりするために使用します。 $ bash -c "$(curl -sL https://raw.githubusercontent.com/sdetweil/MagicMirror_scripts/master/raspberry.sh)" Do you want use pm2 (node process manager) for auto starting of your MagicMirror (y/N)? y Do you want to update the PM2 process name? (Default is MagicMirror) (y/N) N インストール自体はこれで終わりで、ディスプレイにMagicMirror2の画面が表示されると思います。 YouTubeモジュールのインストール 標準で備わっているモジュールもいくつかあるのですが、YouTubeのライブ動画を表示するモジュールは標準では備わっていないので、別途インストールします。以下のコマンドを実行します。 $ cd ~/MagicMirror/modules $ git clone https://github.com/nitpum/MMM-EmbedYoutube.git $ cd MMM-EmbedYoutube $ npm install スライドショーモジュールのインストール 背景の画像をスライドショーで表示するモジュールも標準では備わっていないので、別途インストールします。以下のコマンドを実行します。 $ cd ~/MagicMirror/modules $ git clone https://github.com/darickc/MMM-BackgroundSlideshow.git $ cd ~/MagicMirror/modules/MMM-BackgroundSlideshow $ npm install 直近の雨予報の自作モジュールのインストール これは私が作成したモジュールです。洗濯物を外に干しているときに、あと何時間後に雨が降るかがぱっとわかるようにするためのモジュールです。以下のコマンドを実行します。 $ cd ~/MagicMirror/modules $ git clone https://github.com/noriyukitakei/MMM-RainForecast.git $ cd MMM-RainForecast ちなみにVibe Codingで作成しました。コードは1行も書いていません。 https://github.com/noriyukitakei/MMM-RainForecast 設定ファイルの編集 MagicMirror2の設定ファイルは ~/MagicMirror/config/config.js にあります。このファイルを編集して、各モジュールの設定を行います。以下に私の設定ファイルの例を示します。 日本語表示 表示を日本語にするために、以下のように設定を変更します。 - language: "en", - locale: "en-US", // this variable is provided as a consistent location + language: "ja", + locale: "ja_JP.UTF-8", // this variable is provided as a consistent location カレンダーの設定 日本の休日カレンダーを表示するために、以下のように設定を変更します。 { module: "calendar", - header: "US Holidays", + header: "JP Holidays", position: "top_left", config: { calendars: [ { fetchInterval: 7 * 24 * 60 * 60 * 1000, symbol: "calendar-check", - url: "https://ics.calendarlabs.com/76/mm3137/US_Holidays.ics" + url: "https://calendar.webcal.jp/JapanHolidays.ics" } ] } }, YouTubeモジュールの設定 YouTubeのライブ動画を表示するために、以下のように設定を追加します。「video_id」には表示したいYouTube動画のIDを指定します。YouTubeのURLが https://www.youtube.com/watch?v=t9kwjZBLI-A の場合、video_idは t9kwjZBLI-A となります。 + { + module: 'MMM-EmbedYoutube', + position: 'bottom_bar', + config: { + video_id: 't9kwjZBLI-A', + autoplay:true, + loop: true, + width: 700, + height: 394 + }, + }, 直近の雨予報のモジュール設定 私自作の直近の雨予報モジュールを使用する場合は、以下のように設定を追加します。緯度(latitude)と経度(longitude)は自分の住んでいる場所に合わせて変更してください。 + { + module: "MMM-RainForecast", + position: "middle_center", // 好きな位置に + config: { + latitude: 35.681236, // 東京駅あたり + longitude: 139.767125, + maxHoursAhead: 12, // 12時間先までチェック + rainThreshold: 0.1, // 0.1mm/h 以上を「雨」とみなす + timezone: "Asia/Tokyo", + updateInterval: 10 * 60 * 1000, // 10分ごとに更新 + showDebug: false // trueにすると取得した生データも表示 + } + }, 他の部分は以下のGitHubリポジトリのREADMEを参考にしてください。 https://github.com/noriyukitakei/MMM-RainForecast MagicMirror2の再起動 以下のコマンドを実行して、MagicMirror2を再起動します。 $ pm2 restart all これで以下のように表示されるはずです。 まとめ 自宅にデジタルサイネージを導入するのいいです。もし自宅に使ってないRaspberry Piとかモニタが転がっている方はぜひ試してみてください。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 自宅にデジタルサイネージを導入しました first appeared on SIOS Tech Lab .
はじめに こんにちはSIOS Tech Labアドベントカレンダー11日目になります。私は現在KubeBlocksの調査検証をしており、それについての解説ブログをシリーズとして投稿しています。 第1回 はKubeBlocksについて解説を行い、 第2回 ではKubeBlocksでサポートされているDBソリューションについて解説を行いました。 今回からは実際にKubernetes環境にKubeBlocksを導入し、データベースの運用管理を自動化するサービスであるDBaaSを構築していきます。DBaaSについては こちらの記事 で詳しく解説をしているので興味のある方はぜひご覧ください。 また、今後の記事では、構築したDBaaS上でデータベースの作成やスケーリングなどの具体的な操作の解説も行っていく予定です。 今回は、KubeBlocksの導入手順について解説しますので、ぜひ最後までご覧ください。 導入環境構成図 以下の図は今回KubeBlocksを導入する環境の構成図です。今回の記事では赤い丸でマーキングされた範囲を対象としています。 今回の対象範囲は「KubeBlocksオペレーター」です。KubeBlocksオペレーターはKubernetesクラスタ内で、データベースのデプロイ・スケーリング・自己修復などの運用作業を自動的に実行するカスタムオペレーターです。 導入環境構成図 KubeBlocksの導入方法 KubernetesクラスタにKubeBlocksを導入する方法について、公式サイトでは以下の2つの方法が紹介されています Helm :本番環境に推奨されるインストール方法 kbcli  :検証や学習環境向きのインストール方法 今回は、この2つの導入方法をそれぞれ実行し、その手順を紹介します。 前提条件 Kubernetesクラスタがセットアップ済みであること kubectl コマンドが使用可能であること Helmがインストールされていること ( インストールガイド ) スナップショットコントローラがインストールされていること( インストールガイド ) kbcliインストール KubeBlocksのバージョンと一致させたバージョンのkbcliをインストールします。 今回はKubeBlocksv1.0.1をインストールするためkbcliも同様にv1.0.1をインストールします。 curl -fsSL https://kubeblocks.io/installer/install_cli.sh | bash -s v1.0.1 HelmによるKubeBlocks導入 HelmによるKubeBlocks導入を実施します。 CRDを追加する KubeBlocksが使用するカスタムリソース定義 (CRD) をKubernetesクラスタに登録します。これを行うことでKubeBlocksで提供される機能を利用することが出来るようになります。 kubectl create -f https://github.com/apecloud/kubeblocks/releases/download/v1.0.1/kubeblocks_crds.yaml # 出力例 customresourcedefinition.apiextensions.k8s.io/clusterdefinitions.apps.kubeblocks.io created customresourcedefinition.apiextensions.k8s.io/clusters.apps.kubeblocks.io created customresourcedefinition.apiextensions.k8s.io/componentdefinitions.apps.kubeblocks.io created # ~以下省略~ Helmリポジトリを追加する KubeBlocksのHelmチャートが格納されている外部リポジトリを、ローカルのHelm環境に登録しチャートを取得可能にするコマンドです。 helm repo add kubeblocks https://apecloud.github.io/helm-charts helm repo update # 出力例 "kubeblocks" already exists with the same configuration, skipping Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "piraeus-charts" chart repository ...Successfully got an update from the "kubeblocks" chart repository Update Complete. ⎈Happy Helming!⎈ KubeBlocksインストール KubeBlocksオペレーターをKubernetesクラスタにデプロイします。 helm install kubeblocks kubeblocks/kubeblocks --namespace kb-system --create-namespace --version=v1.0.1 # 出力例 NAME: kubeblocks LAST DEPLOYED: Mon Dec 8 13:46:15 2025 NAMESPACE: kb-system STATUS: deployed REVISION: 1 TEST SUITE: None インストール確認 kubeblocks-xxxxxxxxxx-xxxxx kubeblocks-dataprotection-xxxxxxxxxx-xxxxx の二つのPodがRunningになっていることを確認します。 kubectl get pods -n kb-system # 出力例 NAME READY STATUS RESTARTS AGE kubeblocks-777d976794-pfwlg 1/1 Running 0 5m16s kubeblocks-dataprotection-84db8c4c9-dmqx2 1/1 Running 0 5m16s snapshot-controller-58455ddb79-6nw4c 1/1 Running 0 9m1s kbcliによるKubeBlocks導入 kbcliによるKubeBlocks導入を実施します。 KubeBlocksインストール 以下のコマンドは、KubeBlocksの導入プロセス全体を自動化する単一のコマンドです。helmでは別コマンドで実行したCRDの適用、Helmリポジトリの追加、オペレーターのデプロイなどをkbcliではまとめて実行します。 kbcli kubeblocks install --version=1.0.1 --create-namespace # 出力例 KubeBlocks will be installed to namespace "kb-system" Kubernetes version 1.33.1 kbcli version 1.0.1 Collecting data from cluster OK Kubernetes cluster preflight OK Create CRDs OK Add and update repo kubeblocks OK Install KubeBlocks 1.0.1 OK Wait for addons to be enabled apecloud-mysql OK etcd OK kafka OK mongodb OK mysql OK postgresql OK redis OK KubeBlocks 1.0.1 installed to namespace kb-system SUCCESSFULLY! -> Basic commands for cluster: kbcli cluster create -h # help information about creating a database cluster kbcli cluster list # list all database clusters kbcli cluster describe <cluster name> # get cluster information -> Uninstall KubeBlocks: kbcli kubeblocks uninstall インストール確認 kubeblocks-xxxxxxxxxx-xxxxx kubeblocks-dataprotection-xxxxxxxxxx-xxxxx の二つのPodがRunningになっていることを確認します。 kubectl get pods -n kb-system # 出力例 NAME READY STATUS RESTARTS AGE kubeblocks-777d976794-vbl24 1/1 Running 0 4m40s kubeblocks-dataprotection-84db8c4c9-ppkcs 1/1 Running 0 4m40s snapshot-controller-58455ddb79-sks7q 1/1 Running 0 9m59s おわりに 今回はhelmとkbcliを利用してKubernetes上にKubeBlocksを導入する方法を紹介しました。 次回からは実際にKubeBlocksにデータベースを導入していきます。 参考文献 https://kubeblocks.io/docs/preview/user_docs/references/install-kbcli https://kubeblocks.io/docs/release-1_0/user_docs/overview/install-kubeblocks ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post KubeBlocks導入ガイド:Kubernetes上にDBaaSを構築する手順 first appeared on SIOS Tech Lab .
こんにちは、チンパンジー配属の永田です。 みなさんは「自分って、チンパンジーなんだ」と思ったことがありますか? 僕はあります。   新卒1年目のころ、「 サルでもわかるgit 」を読みました。 わかりませんでした。   「サルでもわかるgit」がわからないということは、サル以下ということになります。 すみません、見栄を張ってチンパンジーと偽っていましたが、実際はサル以下です。 当時は、自分がサル以下ということが受け入れられませんでした。 だから、数年前の自分を救うべく、今ここに「サル”以下”でもわかるgit」を記したいと思います。   ざっくり言うと グループで何かを作るとき、「誰がいつ何を変えたか」を全部記録してくれる仕組みです。 間違えても前の状態に戻せるし、メンバーが別々に作業しても後からきれいに合体できます。   例えば: 実際のグループ授業で起きがちなこと グループで協力し、一つのレポートを書いて提出すると想定してください。 よくある悲劇 田中「レポート最新版送るわ」 鈴木「え、俺も今書いてたんだけど」 山田「あれ、俺が昨日書いた部分消えてない?」 田中「ごめん、古いファイルに上書きしたかも…」 全員「どれが本当の最新版??」 「最新版.docx」「最新版(2).docx」「これが本当の最新.docx」が乱立している こうなってたら良かった レポートは一箇所で管理されてて、全員がそこにアクセスする 編集するときはオリジナルを編集せず、それぞれ自分用のコピーを作ってから作業する 作業が終わったら「これでOK?」とみんなに確認してから合体 もし何かミスっても、「昨日の状態に戻して」が一発でできる 誰かが編集したら「田中が第2章を追加しました」と記録が残る レポートでさえこの混乱なのに、プログラミングの仕事ではこれを何十人で、何万行のコードでやります。 毎日何十回も更新があって、しかも一文字間違えるだけでシステムが動かなくなるかもしれない。 だから「誰がいつ何を変えたか」を完璧に記録する仕組みが必須です。 これを実現するのがGit/GitHubです。   用語の説明 各用語の意味はざっくりこんな感じです。 用語 意味 リポジトリ 共有フォルダ。レポート本体や資料が全部入ってる場所 クローン 共有フォルダを自分のパソコンにダウンロードすること ブランチ 自分用のコピーを作って別々に作業する仕組み コミット 「ここまでやった」という保存記録。メモ付きで残せる プッシュ 自分の作業を共有フォルダにアップすること プルリクエスト 「これで合ってる?」と確認してから合体をお願いすること マージ 別々に作業した内容を一つに合体させること プル 最新版を自分のパソコンに取り込むこと コンフリクト 同じ場所を別々に編集しちゃって、どっちを採用するか決める必要がある状態   さっきの例を用語で言い換えると 理想の流れ 共有フォルダで原本レポートを一箇所で管理する 各メンバーが原本をダウンロード=手元にコピーを持つ 田中は念のため、その原本コピーをさらにコピー。「第2章作業用」という名前に変更し、編集を始める 作業が終わったらどこを誰が何のために編集したか、記録を残す 共有フォルダにアップする リーダーに「確認お願いします」と申請する OKが出たら本体に合体 他のメンバーは最新版をダウンロードしなおして、元の原本コピーに上書きする もし同じ場所を編集してしまっていたら、どっちを採用するか決める 用語で言い換えると リポジトリ で原本レポートを一箇所で管理する 各メンバーが クローン して手元にコピーを持つ 田中は「第2章作業」という ブランチ を新たに切って作業をはじめる 作業が終わったら コミット して記録を残す プッシュ して共有フォルダにアップする プルリクエスト でリーダーに確認をお願いする OKが出たら マージ して本体に合体 他のメンバーは プル して最新版を取り込む もし同じ場所を編集してたら コンフリクト を解決する   gitとgithub ややこしいのでここまでは一緒くたにしていましたが、gitとgithubは厳密には違います。 Git は自分のパソコンで動く「ファイルの変更履歴を記録するツール」です。 編集記録はもちろん、元の状態に戻したり、コピーを作って作業をはじめたりができます。 GitHub はその記録をネット上に保存して、みんなで共有できる「Webサービス」です。 原本と、その変更履歴を保存しているほか、みんながどういう変更を加えたか?とか、オリジナルへの合体の申請とかができます。   まとめ Git は、変更履歴を記録してくれる仕組み。自分のパソコンで動く。 GitHub は、それをネット上で共有できるサービス。みんなでアクセスできる。 要するに「誰が何を変えたか全部わかる」「前の状態に戻せる」「別々に作業しても大丈夫」という、グループ作業の悲劇を防ぐ道具です。 ちなみに「GoogleドキュメントやNotionと何が違う?」と思うかもしれませんが、それらは同時編集、Gitは「各自で作業して完成したら合体」という点で違います。 作りかけの状態で他の人を巻き込まないために、プログラミングではこっちが使われています。 以上、かなりざっくりとしたgit/githubの説明でした。   あとがき 僕が学んでいた当時に一番つまづいたのは、話のピンとこなさと用語の多さでした。 もう本当に、「アプリ開発の現場では、オフチョベットしたテフをマブガッドしてリットしています。」くらいわけがわかりませんでした。 なので今回は、大学生であればイメージしやすいグループ課題を具体例として、gitでやったらこんな感じ、を書いてみました。 このイメージがあった上でもう少し詳しくgitを学ぶと、わかりやすいんじゃないかと思います。   ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post チンパンジーでもわかるGit/Github【初心者向け】 first appeared on SIOS Tech Lab .
久々のブログです。菊地啓哉です。 Dev Container は便利ですよね。開発を始めようとした時に、まずは Dev Container で環境をつくるのが習慣になってきました。 今回は(気持ちは)自力で Hardhat 3 で Solidity の開発をできる Dev Container の開発環境をつくっていこうと思います。つくり方の流れを説明しているので、これをベースにカスタマイズしていただければと思います。 ついでに、Hardhat 3 で SmartContract を動かすところまでやってみます。 前提 WSL上で Docker が使えること VS Code をインストール済みで、 Remote Development の拡張機能をインストール済みであること (パブリックネットワークにデプロイする場合)ガス代を払う為のネイティブトークンを持っている Wallet Address とその秘密鍵、RPC Endpoint 最終的にできあがる devcontainer.json ほとんど自動でつくられるもののままでとってもシンプルですが、できあがる devcontainer.json は以下の通りです。 // For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node { "name": "Node.js & TypeScript", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile "image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm", // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "yarn install", // Configure tool-specific properties. "customizations": { "vscode": { "extensions": [ "NomicFoundation.hardhat-solidity" ], "settings": { "[solidity]": { "editor.formatOnSave": true } } } }, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. "remoteUser": "node" } Dev Container で Solidity 開発環境をつくっていく まずは、WSL上で空の作業ディレクトリを作成し、VS Code のリモートエクスプローラーから、WSL に接続し、作業ディレクトリを開きます。 左下の背景色がある 「 >< WSL: ~」 と書かれているところをクリックして出てくるメニューから「コンテナーで再度開く」選びます。コマンドパレットから探して実行することもできます。 この先、環境によっては出てくるダイアログが違うかもしれないので参考程度に見ていただければと思いますが、 適当な starting point を選びます。Hardhat は Node.js が必要になり、TypeScript にも対応しているのでベースに「Node.js & TypeScript」を選びました。 Docker Image を選択します。特に理由が無ければ既定のもので良いと思います。 追加機能は必要なものがあれば選んで Enter または OKをクリックします。ここでは特に選択せずに OK にしました。 オプションファイルも必要であれば選んで進みます。ここも選択しませんでした。 これで、設定ファイルの devcontainer.json を .devcontainerディレクトリに作ってくれて、Dev Container で開発環境が開かれます。 Dev Container ができたということで、まず最初にターミナルで id を実行してユーザや UID/GID を確認しておきます。 UID/GID がホストの UID/GID と一致していれば、コンテナ内で作成したファイルをホストの WSL側で開けないなどの権限問題は起こらないのですが、 remoteUser に、このユーザを指定しておきます。 remoteUser を設定することで、Dev Container内で使用するユーザにホストの UID/GID が設定され、権限問題が起こらないようにしてくれるかと思います。 今の状態でログインしているユーザが root のような特権ユーザの場合には別のユーザを指定するなど、別の方法が必要になります。 Dev Container の設定を変更して、反映したいタイミングで左下の背景色がついている今度は「>< 開発コンテナー: ~」などになっているところをクリックして、「コンテナーのリビルド」を選び、リビルドします。適宜リビルドしてください。 もしくは、 devcontainer.json を変更した場合には右下などにポップアップが出てくると思いますので、そこからでもリビルドできます。 続いて、拡張機能を入れていきます。 通常、 VS Code でやるように、拡張機能のメニューから選んで追加したいものをインストールすれば使えますが、コンテナがリビルドされると入れ直しが必要になってしまうので、Dev Container の設定にも追加します。 ここでは Hardhat を作っている Nomic Foundation の Solidity 拡張を入れます。 拡張機能を見つけたらインストールして、歯車 から「拡張機能 ID のコピー」を選びます。 ※画面からインストールするのは、コンテナのリビルドを待つのが面倒だからです。 そして、devcontainer.json で customizations.vscode.extensions の配列の中に拡張機能 ID を追加します。補完機能を使って書いていけるので、IDはダブルクォーテーションで囲むことさえ気を付ければ、特に迷わず設定できると思います。 これでコンテナーのリビルドがあっても拡張機能が入った状態になります。上記の通り手動でインストールもしていればコンテナのリビルドは不要です。 続いて、Solidity ファイルを保存した時に、フォーマットされるように設定します。 こちらも、devcontainer.json で設定することで、コンテナ内にだけ設定が適用され、コンテナがリビルドされても設定が残るようにします。 具体的には、devcontainer.json で customizations.vscode.settings["[solidity]"]["editor.formatOnSave"] を true にします。 私が少し Solidity のコードをいじった感触としてはデフォルトの設定のままで特に問題無さそうだったので、スタイルの設定は特に設定しません。 拡張機能と自動フォーマットの設定を入れたものが以下のようになります。devcontainer.json のコードはこのページの最初に記載したものです。 これで簡易的ではありますが、Solidity の開発環境ができました。 Hardhat 3 で開発する 続いて、 Hardhat のチュートリアル に従って、Solidity の開発準備を進めていきます。 Hardhat 3 初期化 Dev Container で Node.js を使えるようにしているはずなので、以下のコマンドで初期化します。 チュートリアルには npm, pnpm, Yarn での実行方法が書かれているので、お好きなもので進めてください。ここでは、 npm で進めます。 npx hardhat --init 実行時点では Hardhat 3 はβ版となっていましたが、気にせず進んでいきます。幾つか質問されますが、適宜変更しつつ、進めていきます。(ここでは全てデフォルトでインストールしました。) 以下のようなディレクトリ構成となりました。 hardhat.comfig.ts :プロジェクトの設定。 Solidity のコンパイラのバージョン、ネットワーク設定、プラグインやプロジェクトで使用するタスクなどが設定されています。 contracts :Solidity の SmartContract を保存します。 .t.sol という拡張子で Solidity で書かれたテストファイルを保存することもできます。 test :TypeScript で書かれたテストを保存します。Solidity で書かれたテストファイルをここに保存することもできます。 ignition : SmartContract をどのようにデプロイするかを定義する Hardhat Ignition を保存します。また、デプロイ情報が保存されます。 scripts : ワークフローを自動化するスクリプトを保存します。スクリプトは Hardhat runtime にフルアクセスでき、プラグインの使用、ネットワークへの接続、コントラクトのデプロイなどができます。 Hardhat 3 の基本的な使い方 build npx hardhat build テスト Solidity で書かれているテスト(拡張子: .t.sol)と TypeScript で書かれたテストの全てが実行されます。 npx hardhat test # Solidity のテストだけ実行する場合は npx hardhat test solidity # TypeScript のテストだけ実行する場合は npx hardhat test nodejs ローカルで実行 まずはローカルの Hardhat node を起動します。 npx hardhat node Hardhat node が起動し、ログが出力されるようになります。続いて、Dev Container で別のターミナルを開き、初期化時に自動で作成された ignition/modules/Counter.ts を使って localhost のネットワークに Contract をデプロイします。 npx hardhat ignition deploy ignition/modules/Counter.ts --network localhost hardhat console プログラムから localhost ネットワーク上の Contract を操作しても良いのですが、ここでは対話型で Contract を操作してみます。 npx hardhat console --network localhost このコマンドを実行すると、対話型で Node.js のコマンドを実行できるようになります。ということで以下のようにいろいろと操作してみます。 // connection const connection = await hre.network.connect() // viem const viem = connection.viem // Contract Address // ignition/deployments/chain-31337/deployed_addresses.json か npx hardhat node のログからコピーする const address = "0x5fbdb2315678afecb367f032d93f642f64180aa3" // 例 // wallet const wallet = await viem.getWalletClient() // 先にデプロイしたContract const counter = await viem.getContractAt("Counter", address) // publicなプロパティの x を読み取る // public property に対応する view関数を勝手に作ってくれる await counter.read.x() // 5n // increment : x がインクリメントされる await counter.write.inc() // 再度読み取る await counter.read.x() // 6n Sepolia へのデプロイ デフォルトで作られている hardhat.config.ts では、ネットワーク設定の sepolia のところで、 configVariable というものが使われています。これは、Hardhat 3 が提供する、設定変数を扱うことのできる仕組みなので、これを使ってみましょう。 Sepolia の RPC Endpoint を設定します。 npx hardhat keystore set SEPOLIA_RPC_URL 初回の実行では設定するパスワードの入力が求められ、続けて打ち間違いが無いように再入力が求められます。その後、 RPC Endpoint として設定する値を入力します。 続いて、 Sepolia で処理を実行するための Wallet の秘密鍵を設定します。 npx hardhat keystore set SEPOLIA_PRIVATE_KEY パスワードを聞かれ、先ほどと同じ値を入力し、設定する値を入力します。 これで準備ができましたので、Sepolia にデプロイします。 npx hardhat ignition deploy ignition/modules/Counter.ts --network sepolia これで ignition のプログラムが実行され、Sepolia にデプロイされたかと思います。 まとめ Solidity 開発のために Dev Container 構築と Hardhat 3 の準備と簡単な使い方をご紹介しました。 Dev Container の設定についてはポチポチするのが中心で、あまり覚えたりコピペする必要が無いようになっているかと思います。 Hardhat は久しぶりに触ったら新しいものがいろいろありましたが、少し触った感覚だと使いやすい印象でした。ローカルのネットワークで簡単に SmartContract を動かせるので開発や検証に便利だと思います。 またかきます またね ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Hardhat 3 と Dev Container で Solidity の開発 first appeared on SIOS Tech Lab .
はじめに 前回はKubernetesのバックアップは、etcdとPV/PVCの二軸で、セットで行うことが重要であることを解説しました。しかし、ステートフルなアプリケーションのバージョンアップでは、データの互換性と整合性が課題となります。 本記事では、これらの複雑な課題を解消し、安全なリソースとデータのバックアップ・リストアを実現するソリューションである「Velero」について解説します。 Veleroの基本コンセプトとアーキテクチャ Velero(旧称 Heptio Ark)は、Kubernetesクラスターリソース(マニフェストファイル)と永続ボリューム(PV)のデータの両方をまとめてバックアップ/リストアを実現するツールです。 Kubernetesのバックアップでは、アプリケーションを構成するすべてのクラスターリソースをバックアップするだけでは不十分で、データ(永続ボリューム)も同時に保存する必要があります。これは、Veleroを使うことで実現可能です。 さらに、クラウドプロバイダーの変更やオンプレミスからクラウドへの移行、Kubernetesのメジャーバージョンアップに伴う新しいクラスターへのデータ移行といった複雑で時間がかかる作業もVeleroでは対応可能になります。 Veleroは以下の用途で利用できます。 クラスターのバックアップを取得し、データ損失が発生した場合にリストアを行う クラスターリソースを他のクラスターへ移行する 本番クラスターを開発/テストクラスターへ複製する Kubernetesのアップグレードなどのシステム操作を実行する前に、アプリケーションの状態をスナップショットする Veleroの主な特徴 クラスター内のすべてのオブジェクトをバックアップまたはリストアできる。オブジェクトタイプ、ネームスペース、ラベルによるフィルタリングも可能 バックアップ、リストア、スケジュールといったすべての操作の定義がKubernetesのカスタムリソースとして定義され、etcdに保存される クラウドプロバイダーまたはオンプレミスのKubernetes環境で動作可能 独自のカスタム機能をVeleroに追加できるプラグインシステムが提供されている PVのバックアップ方式 Veleroは、永続ボリューム(PV)のデータを保護するために、主に以下の3つの方式をサポートしています。ファイルシステムバックアップ(FSB)とスナップショットは、同一ボリュームに対して同時に実行されず、相互に排他的な関係になります。 方式 概要 主な用途 ボリュームスナップショット クラウドプロバイダー(AWS EBS, Azure Disk等)やCSIドライバーが提供するネイティブなスナップショット機能を利用する。 主要なクラウドプロバイダーやCSI準拠のストレージを利用している場合の標準的なデータ保護。 ファイルシステムバックアップ (FSB) Node Agent (Kopia Uploader) と呼ばれるDaemonSetが各ノードで稼働し、ボリュームのファイルシステムを直接読み取り、オブジェクトストレージにデータを保存する。 NFSのような共有ファイルシステムや、CSIドライバーが提供するスナップショット機能を持たないストレージのデータ保護。プロバイダー間の移行に利用可能。 CSIスナップショットデータムーバー CSIスナップショットで作成されたデータを、Node Agent経由でオブジェクトストレージに移動・コピーする。 クラウドプロバイダ間のデータ移行、オンプレミスからクラウドへのデータ移行、スナップショットデータの長期アーカイブ。 Veleroアーキテクチャ Veleroの主要リソースは以下の通りです。 バックアップ、リストア、スケジュールといった操作はクライアントのVelero CLIから実行します。 主要リソース 役割 実行環境 Velero サーバー メインコントロールプレーン。バックアップ/リストアのロジック、リソースの収集、オブジェクトストレージへの連携を担当する。 Kubernetesクラスター内のDeployment。 Node Agent ファイルシステムバックアップ(FSB)やCSIスナップショットデータムーバーなどのデータ転送を担う。 Kubernetesクラスター内のDaemonSet。 BackupStorageLocation (BSL) KubernetesリソースのメタデータやPVデータ(FSB/データムーバー利用時)の保存場所(オブジェクトストレージ)を定義する。 オブジェクトストレージ(AWS S3、Azure Blob、MinIOなど)。 VolumeSnapshotLocation (VSL) PVスナップショットの保存場所を定義。 CSIドライバー対応のスナップショットシステム。 バックアップのワークフロー VeleroによるKubernetesリソースと永続ボリュームのオンデマンドバックアップのワークフローは以下になります。 Image Source: https://velero.io/docs/v1.17/how-velero-works/ VeleroクライアントがKubernetes APIサーバーを呼び出してBackupオブジェクトを作成する BackupControllerが新しいBackupオブジェクトを認識して、検証を行う BackupControllerがバックアッププロセスを開始する。APIサーバーにリソースを照会してバックアップするデータを収集する 収集したオブジェクトをtar形式にアーカイブし、BackupControllerがオブジェクトストレージサービス(AWS S3など)を呼び出して、バックアップファイルをアップロードする 永続ボリューム(PV)が存在する場合、連携するストレージプロバイダのAPIを呼び出し、ボリュームのスナップショットを作成する フック(Backup Hooks)が設定されている場合、カスタムアクション処理の前(pre hook)または、すべてのカスタムアクション完了および追加アイテムのバックアップ完了後(post hook)に、Pod内のコンテナでコマンドが実行される スケジュールされたバックアップは、Cron式で指定された間隔で自動的に実行される Veleroのフック(Hook)機能とは? データベースのようなアプリケーションは、DBのバックアップ中にデータが書き換わると整合性が取れなくなる可能性があります。Veleroのフック(Hook)機能は、この問題を解決するために、バックアップ処理に合わせてコンテナ内で任意のコマンドを実行する機能です。例として、Veleroのフック機能は以下のようなことが可能です。 Pre-hook(直前):データの書き込みをロックし、静止点を作成 Post-hook(直後): バックアップ完了後にロックを解除し、通常稼働に復帰 Veleroでのリストア VeleroによるKubernetesリソースと永続ボリュームのリストアのワークフローは以下になります。 VeleroクライアントがKubernetes APIサーバーを呼び出してRestoreオブジェクトを作成する RestoreControllerが新しいRestoreオブジェクトを認識して、検証を行う RestoreControllerがオブジェクトストレージサービスからバックアップ情報を取得する。次に、バックアップされたリソースに対して前処理を行い、リソースが新しいクラスターで動作することを確認する RestoreControllerがリストアプロセスを開始する。依存関係を考慮した適切な順序で対象となるリソースが1つずつ復元される フック(Restore Hooks)が設定されている場合、以下のタイミングでリストアされるPod内のコンテナに対して実行される InitContainer:リストアされるPodにInitコンテナを追加して、アプリケーションコンテナが開始される前に必要なセットアップを実行する。PVと紐づく場合は、PVのリストア後に実行される Exec:リストアされたPodのコンテナが起動した後、コンテナ内でカスタムコマンドまたはスクリプトが実行される デフォルトではリストア時にターゲットクラスタ上のデータは削除されません。バックアップ内の同名リソースがターゲットクラスタ内に既に存在する場合は、そのリソースをスキップします。「update」フラグがある場合、リソースを更新します。 他のバックアップ手法との比較 VeleroはKubernetesネイティブなOSSバックアップソリューションです。他のKubernetesのバックアップソリューションとしては、Cohesity、Commvault、Rubrikが挙げられますが、いずれもエンタープライズ向け包括バックアップ製品の一機能としてKubernetesのバックアップ機能を提供しています。 Cohesity 初回以降は変更分のみをバックアップする永久増分バックアップを採用しており、バックアップ時間の短縮とネットワーク・ストレージ負荷の低減が強みです。 Commvault AKS/EKS/Openshiftなどのマルチクラウド、マルチディストリビューションのクラスターを1つの画面で統合管理できます。アプリ単体だけでなく、クラスタ全体を保護できる広範なバックアップが特徴です。 Rubrik 書き換え不可能な(不変)バックアップとゼロトラストアーキテクチャを採用し、ランサムウェア対策と整合性を提供しています。アプリとデータ状態の整合性を厳密に保ったままバックアップできる点が強みです。 Kubernetesだけを低コストでバックアップしたい場合はVeleroが第一候補に挙がります。既にCohesity、Commvault、Rubrikを使用している、またはKubernetes以外もバックアップ対象に含めたい場合は既存・導入予定のプラットフォームに寄せる方が現実的です。 まとめ 本記事では、Kubernetesのリソースと永続ボリュームを包括的に保護できるVeleroについて解説しました。Veleroは、Hook機能を利用してデータベースの整合性を確保できる点や、環境に応じてスナップショットとファイルシステムバックアップを使い分けられる柔軟性が強みです。 次回は、実際にVeleroをKubernetesへ導入するための構築手順と設定方法について詳しく解説します。 参考文献 https://velero.io/docs/v1.17/ https://github.com/vmware-tanzu/velero https://www.cohesity.com/ja-jp/solutions/kubernetes/ https://www.commvault.com/platform/kubernetes-backup https://www.rubrik.com/ja/solutions/kubernetes ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Kubernetesバックアップツール「Velero」の概要 first appeared on SIOS Tech Lab .
1. 今回の投稿について こんにちは、サイオステクノロジーの安藤 浩です。SIOS Tech Labアドベントカレンダー10日目の投稿です。 技術よりというよりマネジメントよりの内容で投稿します。私は開発も行いますが、プロジェクト管理を担当することがあり、プロジェクト管理で気を付けたいポイントを記載してみます。 ソフトウェア開発のプロジェクトでは、適切なプロジェクト管理が欠かせません。スケジュール、コスト、リスク、コミュニケーション、スコープ管理など様々な考慮をする必要があります。 特にWBS(Work Breakdown Structure)の作成はプロジェクト全体像の工数、タスクを把握するために重要な基盤です。 今回は、WBSの作成方法、スケジュール作成、JiraやNotionの比較をご紹介します。 2. WBS(Work Breakdown Structure)とは WBSはプロジェクトのタスクを階層的に分解し、管理可能な単位に整理する手法です。プロジェクトの成果物やタスクを「見える化」することで、以下のメリットが得られます: タスクの漏れ防止 :全体を分解することで、見落としがちなタスクを発見 正確な工数見積もり :小さな単位に分解することで精度向上 進捗管理の容易化 :各タスクの完了状況を明確に把握 責任の明確化 :誰が何を担当するかを明示(出来る限りタスクに対して1名のみを推奨) 3. WBS作成の基本ステップ 3.1. 成果物の洗い出し プロジェクトで作成すべき成果物を最初に洗い出します。 成果物の例 : 要件定義書 基本設計書 詳細設計書 ソースコード 単体テスト仕様書兼成績書 結合テスト仕様書兼成績書 リリース手順書 マニュアル・ドキュメント 3.2. タスクの分解 成果物の洗い出しと並行して、進めてもよいですが、成果物に対して必要なタスクを詳細に分解します。 分解の原則 : 1タスクは1~5人日程度の粒度にする → タスクが大きすぎると漏れや管理が困難になります。 具体的なタスクの完了条件を明確にする。→ タスクによっては完了条件を決めることが難しい場合がありますが、タスクの完了条件を決めることで対応内容を明確にします。 「○○の作成」「○○のレビュー」など動詞で終わる形にする。 → タスク名から何をするタスクか判別できるようにします。 担当者を1名で対応できるタスクに分割します。 → 複数担当者にわたる場合は、タスク分割が必要です。 WBS構造の例 : 担当やプロジェクト、会社によって異なると思いますが、3階層くらいまでにすることがWBS構造の把握をするためのポイントかと思います。 あまりに階層が深すぎたり、階層が浅すぎると工程やタスクがわかりにくくなります。 1. 要件定義フェーズ 1.1 要件ヒアリング 1.1.1 ヒアリングシート作成 1.1.2 ユーザーヒアリング実施 1.1.3 要件整理 1.2 要件定義書作成 1.2.1 機能要件定義 1.2.2 非機能要件定義 1.2.3 レビュー・承認 2. 設計フェーズ 2.1 基本設計 2.1.1 システム構成設計 2.1.2 画面設計 2.1.3 DB設計 2.2 詳細設計 ... 3.3. タスクの前後関係(依存関係)の定義 タスク間の依存関係を明確にすることで、どのタスクが完了していなければ着手できないのかの明確化や正確なスケジュール作成を行うことができます。 依存関係の種類 : プロジェクト管理 依存関係  を見ると 「PMBOKでは依存関係という用語は定義されていない」とあり、PMBOKでは依存関係という定義がないことを知りましたが、タスクの依存関係を意識するとスケジュールが立てやすいです。 種類 説明 例 FS(Finish to Start) 先行タスク完了後に後続タスク開始 設計完了後に実装開始 SS(Start to Start) 先行タスク開始と同時に後続タスク開始 設計書作成とレビュー準備を同時開始 FF(Finish to Finish) 先行タスク完了と同時に後続タスク完了 テスト実施とテスト報告書作成が同時完了 SF(Start to Finish) 先行タスク開始時に後続タスク完了 新システム稼働開始時に旧システム停止 依存関係の定義例 : 以下タスクがざっくりしていますが、こんな感じです。 タスクA(要件定義)→ タスクB(基本設計)[FS] タスクB(基本設計)→ タスクC(詳細設計)[FS] タスクC(詳細設計)→ タスクD(実装)[FS] タスクD(実装)→ タスクE(単体テスト)[FS] 3.4. タスクのバッファ設定 技術的に不確実性を抱えることは通常ですが、リスクに備えて各タスクにバッファ(余裕時間)を設定したり、各タスクではバッファを考慮せずに、全体の工数に対してバッファを積むことがあります。 ここでのタスクのバッファは工数見積もり時に考慮するもので、スケジュール上のバッファとは区別します。 タスクバッファの指針 : 各タスクに例: 5~20%の余裕を持たせる(プロジェクトによってどのくらいかも異なります。バッファを積まずに楽観的な工数とする場合もあります) 不確実性が高いタスクほど多めにバッファを確保 経験の浅いメンバーが担当するタスクは余裕を持たせる 例:実装タスクの工数見積もり ├── 機能A実装: 2人日 + バッファ0.5人日 = 2.5人日 ├── 機能B実装: 3人日 + バッファ0.5人日 = 3.5人日 └── 機能C実装: 4人日 + バッファ1人日 = 5人日 4. スケジュールの作成 WBSで洗い出したタスクをもとに、具体的なスケジュールを作成します。 月から金まであって「よし、5日だから5人日のタスクができるな!」となって失敗するケースがありますが、考慮すべき要素はいくつかあります。 スケジュール作成で考慮すべき要素 営業日(土日祝日、長期休暇など) スキル・経験 タスク優先度 バッファ 前工程、後工程の状況 稼働率 など 4.1. 稼働日を考慮したスケジュール作成 少なくとも営業日(稼働日)はどの会社にもあると思うので、スケジュールに織り込むことは必須です。 また、有休や稼働率の考慮なども必要なので、考慮すべき要素は以下かと思います。 考慮すべき要素 : 項目 内容 土日祝日 カレンダー上の休日を除外 年末年始・お盆 会社の休業期間 有給取得予定 メンバーの休暇予定 他プロジェクトとの兼務 稼働率の考慮(50%稼働など) 会議・定例 開発タスク以外の時間 スケジュール作成の際に毎回営業日いつだったか確認してSpreadsheet で営業日外(休日・祝日)一覧を書き出すのが面倒なので、直近やった方法を紹介しておきます。 前提 前提として、WBSには各工程のタスクの分割が行われ、タスクに対して工数が振られているとします。 No 工程 タスク 工数(人日) 1 設計 基本設計書作成 3 2 設計 詳細設計書作成 5 3 実装 機能A実装 2.5 4 実装 機能B実装 3.5 5 実装 機能C実装 5 6 テスト 単体テスト 3 7 テスト 結合テスト 4 手順 営業日の情報を入手する。YYYY-MM-dd の形式で一覧でスプレッドシート上で扱いやすければよいですが、カレンダー形式になっていたり、画像やPDFの場合があるので、 1の画像やPDFをGemini 3 Proに読み込ませて「祝日と休日すべてをYYYY-MM-dd の形式で一覧に出力して」とプロンプトを入力します。※弊社には有休奨励日が設定されている(休日と休日の間の営業日が多い)ので念のためその日は休む人がいそうなので考慮に入れておきます。 以下のような感じで出力してくれました。 **12月 (11日間)** 2025-12-06 2025-12-07 2025-12-13 2025-12-14 2025-12-20 2025-12-21 2025-12-27 2025-12-28 2025-12-29 2025-12-30 2025-12-31 ざっとあっていそうか確認してスプレッドシートに「休日」のシートを作成します。 3の結果を張り付けます。WBSの工数や納期によっていつまでの期間の情報が必要か異なります。 例えば2025/12/01がプロジェクト開始だとして、以下のようにC2のセルの開始日を「 =WORKDAY(WBS!F1 - 1, 1, '休日'!$B$4:$B$140) 」のようにすると休日でを避けて開始日を設定することができます。 7. D2セルの終了日は「 =WORKDAY(C2-1,E2+F2,'休日'!$B$4:$B$140) 」のようにして開始日からE2: 対応工数+F2: スケジュールバッファの期間での休日を除いた終了日が設定されます。 ※スケジュールバッファは以下で検討。 8. プロジェクトメンバーが複数いて並列対応が可能であれば、「並列対応」の列を用意してタスクを並列で進める考慮も行います。 9. 2026年1月中旬に終わりそうだとなります。 細かい作業ですが、休日一覧文字認識やフォーマットを変更したりするなども手間なので生成AIを活用して楽になりました。 4.2. スケジュールバッファの設定 プロジェクト全体やフェーズ単位で、期間的なバッファを設定します。タスクのバッファとは異なり、スケジュール上の調整日として確保します。結合テストの期間やリスクが見込まれるマイルストーンでは期間的なバッファを持たせることで例えばリリースに間に合わないということがないようにします。 スケジュールバッファの指針(例) : プロジェクトバッファ :全体期間の10~15%をプロジェクト終盤に確保(プロジェクトによってどのくらいかも異なります) マイルストーンバッファ :フェーズ終了時に調整日を設定 例:実装フェーズのスケジュール ├── 機能A実装: 1/6 ~ 1/14(6営業日) ├── 機能B実装: 1/15 ~ 1/18(4営業日) ├── 機能C実装: 1/19 ~ 1/25(5営業日) └── フェーズバッファ: 1/26 ~ 1/28(2営業日) ----------------------------------- 合計: 17営業日 4.3. ガントチャートによる可視化 ガントチャートは、作成したスケジュールを視覚的に表現するツールですが、Excel やスプレッドシートで書くと色づけするなど変更に耐えられないので、Backlog, Jira, Notion などのツールを使った方が良いと思います。 ガントチャートに含める情報 : タスク名 担当者 開始日・終了日 進捗率 見積工数(人日) 依存関係(矢印で表示) マイルストーン など ガントチャート作成ツールの例 : Microsoft Project Jira(タイムラインビュー) Notion(タイムラインビュー) Backlog プロジェクトメンバー全員で共通認識が容易にできることが重要だと思うので、Microsoft Projectはライセンスが高かったり、プロジェクトメンバー全員がMicrosoft Projectをもっていないとファイルが開けなかったりと導入には注意が必要です。 5. Jira と Notion の比較 直近だとJira(無料版) と Notionを利用しましたが、小~中規模ではNotionのほうが操作性が良く学習コストが低いと思いました。 Notionはやや操作が重くなることがありましたが、ステータスの変更によって進捗率の算出など数式を利用したいときはJiraの無料枠ではAutomation の月の上限がすぐに達してしまうのでうまくいきませんでした。 今回でだいぶJiraになれたので、次回機会があれば有料版も利用したいところです。 観点 Jira Notion 価格 有料(無料枠あり) 無料プランあり 得意な領域 アジャイル開発 ドキュメント管理、柔軟な運用 学習コスト やや高い 比較的低い カスタマイズ性 設定項目が豊富 自由度が非常に高い 開発ツール連携 GitHub, Bitbucket等と連携 API経由 チーム規模 中~大規模 小~中規模 WBSやガントチャートをJiraやNotionに落とし込む方法も記載しようと思いましたが、時間がないので次回にしたいと思います。 6. まとめ 本記事では、プロジェクト管理の基本であるWBS作成からスケジュール作成のポイント、Jira・Notionの比較についてご紹介しました。 1. WBS作成について : 成果物を洗い出し、タスクを適切な粒度(1〜5人日)に分解する タスクの依存関係を明確にする 不確実性に備えてタスクバッファを設定する 2. スケジュール作成について : 営業日(土日祝日、有給、稼働率)を正確に考慮する プロジェクト全体やフェーズ単位でスケジュールバッファを確保する ガントチャートで可視化し、進捗管理を容易にする 3. ツール選定について : Jiraはソフトウェア開発・アジャイルに強く、大規模チーム向け Notionは柔軟性が高く、ドキュメント管理も一元化したい小〜中規模チーム向け 7. 参考リンク プロジェクトマネジメント知識体系ガイド(PMBOK) プロジェクトマネジメント プロジェクト管理 依存関係 Jira公式ドキュメント Notion公式ドキュメント ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post プロジェクト管理入門 – WBS・スケジュール作成 first appeared on SIOS Tech Lab .
「SIOS社員が今年一年で学んだこと」アドベントカレンダー9日目です。 最近少し苦戦した、Kongの小ネタを紹介させていただきます。 想定状況 旧サービスから新サービスに移行するにあたって、まずは一部のユーザーにのみ公開し動作を確認してから全体公開したい…という状況を考えます。 旧サービス: http://httpbin.org/xml 新サービス: http://httpbin.org/json こういった作業を本番で作業を行う場合はdecKコマンドを叩くと思いますが、今回は分かりやすさ重視でKong Manager上からポチポチ設定していく手順を紹介します。 また、canary release pluginはenterprise onlyのため、順当な手段で試そうとするとハードルが高いです。幸い Kong academy 内で実際のKongの操作を試せるvirtual labではenterprise onlyなpluginでも使うことができるので、そちらを使わせていただきましょう。 操作手順 workspace作成 適当なworkspaceを作ります。複数のworkspace作成もenterprise機能だった気が… serviceの作成 Gateway Servicesから、旧サービスとなるhttp://httpbin.org/xmlを割り当てたcanary-api-serviceを作成します。 Name: canary-api-service Full URL: http://httpbin.org/xml routeの作成 上記のserviceに紐づいたrouteを作成します。 Name: canary-api-route Service: canary-api-service Path: /api/canary 疎通確認 curl -i http://localhost:8000/api/canary HTTP/1.1 200 OK Content-Type: application/xml Content-Length: 522 Connection: keep-alive Date: Sat, 06 Dec 2025 11:55:35 GMT Server: gunicorn/19.9.0 Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true X-Kong-Upstream-Status: 200 X-Kong-Upstream-Latency: 2009 X-Kong-Proxy-Latency: 2 Via: 1.1 kong/3.10.0.6-enterprise-edition <?xml version='1.0' encoding='us-ascii'?> <!-- A SAMPLE set of slides --> <slideshow title="Sample Slide Show" date="Date of publication" author="Yours Truly" > <!-- TITLE SLIDE --> <slide type="all"> <title>Wake up to WonderWidgets!</title> </slide> <!-- OVERVIEW --> <slide type="all"> <title>Overview</title> <item>Why <em>WonderWidgets</em> are great</item> <item/> <item>Who <em>buys</em> WonderWidgets</item> </slide> </slideshow> key auth pluginを設定 routeに対してpluginを適応します。 Scoped Route: canary-api-route consumerを作成 旧サービスを見せるか新サービスを見せるか分類するため、2つのconsumerを作成します。 Username: general-consumer 同様に、Username: vip-consumerでvip-consumerを作成します。 key auth credential付与 それぞれのconsumerにcredentialを付与します。 general-consumerはgeneral-apiを割り当てます 同様に、vip-consumerにはvip-apiを割り当てます。 疎通確認 apikeyがない場合、401で弾かれます。 curl -i http://localhost:8000/api/canary HTTP/1.1 401 Unauthorized Date: Thu, 06 Nov 2025 02:13:07 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive WWW-Authenticate: Key realm="kong" Content-Length: 96 X-Kong-Response-Latency: 5 Server: kong/3.4.3.21-enterprise-edition { "message":"No API key found in request", "request_id":"e9f5080d632bba08e2ba023597c3d006" } 先ほど設定したapikeyがあれば、アクセスが可能です。 curl -i http://localhost:8000/api/canary?apikey=general-api curl -i http://localhost:8000/api/canary?apikey=vip-api HTTP/1.1 200 OK (以下略) acl pluginを設定 Scoped Route: canary-api-route Allow: general-acl, vip-acl acl credentialを付与 各consumerにacl credentialを付与します vip-consumerに対しても同様に、vip-aclを設定します。 canary pluginを設定 以下の通り設定します。 Scoped Route: canary-api-route UpstreamHost : httpbin.org UpstreamPort : 80 UpstreamUri : /json Groups : vip-acl Hash: allow 疎通確認 これで、general-consumerは旧サービス(/xml)が見え、vip-consumerは新サービス(/json)が見えるようになりました。 curl -i http://localhost:8000/api/canary?apikey=general-api HTTP/1.1 200 OK Content-Type: application/xml Content-Length: 522 Connection: keep-alive Date: Sat, 06 Dec 2025 11:55:35 GMT Server: gunicorn/19.9.0 Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true X-Kong-Upstream-Status: 200 X-Kong-Upstream-Latency: 2009 X-Kong-Proxy-Latency: 2 Via: 1.1 kong/3.10.0.6-enterprise-edition <?xml version='1.0' encoding='us-ascii'?> <!-- A SAMPLE set of slides --> <slideshow title="Sample Slide Show" date="Date of publication" author="Yours Truly" > <!-- TITLE SLIDE --> <slide type="all"> <title>Wake up to WonderWidgets!</title> </slide> <!-- OVERVIEW --> <slide type="all"> <title>Overview</title> <item>Why <em>WonderWidgets</em> are great</item> <item/> <item>Who <em>buys</em> WonderWidgets</item> </slide> </slideshow> curl -i http://localhost:8000/api/canary?apikey=vip-api HTTP/1.1 200 OK Content-Type: application/json Content-Length: 429 Connection: keep-alive Date: Sat, 06 Dec 2025 12:02:41 GMT Server: gunicorn/19.9.0 Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true X-Kong-Upstream-Status: 200 X-Kong-Upstream-Latency: 1530 X-Kong-Proxy-Latency: 3 Via: 1.1 kong/3.10.0.6-enterprise-edition { "slideshow": { "author": "Yours Truly", "date": "date of publication", "slides": [ { "title": "Wake up to WonderWidgets!", "type": "all" }, { "items": [ "Why <em>WonderWidgets</em> are great", "Who <em>buys</em> WonderWidgets" ], "title": "Overview", "type": "all" } ], "title": "Sample Slide Show" } } 本リリース vip-consumerに対してカナリアリリースを行い、十分に動作検証ができたとします。canary release状態から通常リリースに変更するには、serviceに登録しているendpointを新サービスのものに変更し、canary release pluginを削除すればOKです いかがでしたか? 長々とした拙筆にお付き合いいただきありがとうございました。本記事が皆様の、何かしらの助けになれば幸いです。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【Kong初心者向け】Canary Release Pluginの使い方 first appeared on SIOS Tech Lab .
はじめに 前回はダウンタイムを最小限に抑えるBlue/Greenデプロイ戦略について紹介しました。これは旧環境(Blue)と新環境(Green)を完全に分離して用意し、外部からのトラフィックを一瞬でGreenに切り替える手法です。これにより、ダウンタイムを抑制しつつバージョンアップを完了させ、かつ問題発生時の切り戻し(ロールバック)も瞬時に行えるという、安全性の高い方法です。 本記事では、安全なバージョンアップに不可欠なバックアップの必要性と、その際に直面するデータ管理の課題について深堀して解説します。 なぜKubernetesのリソースバックアップが必要なのか バージョンアップ中の操作ミスやエラーにより、Kubernetesでは以下の二つの要素が失われる可能性があります。 クラスターリソースの設計図:Deployment、Serviceなどクラスター全体を構成する設定情報 アプリケーションデータ:データベースに保存されたアプリケーションデータなど サービスを元の状態に完全に復元するには、アプリケーションデータに加え、クラスターの設計図も完全に復元できる状態が必要です。 ステートレスとステートフルアプリケーションの違い デプロイメント戦略を考えるうえで、データの有無が大きな違いとなります。 ステートレス:データを持たず、リソースの入れ替えが容易です。バージョンアップ時にデータ移行の問題は発生しません。 ステートフル:永続的なデータ(例:DBのデータ)を保持し、その状態に依存して動作します。データの損失を防ぎつつ、新旧バージョン間でデータの互換性をどう担保するかが大きな壁となります。 Kubernetesのデータ構成要素 Kubernetesが「データ」をどのように管理しているか、バックアップとリストアに必要な要素を解説します。 etcd Kubernetesクラスターの全てのメタデータと状態を保持しています。 etcdのバックアップこそがクラスターの設計図のバックアップであり、これがなければPodやServiceの定義は全て失われてしまいます。etcdのバックアップは最も重要です。 PV、PVC(アプリケーションの永続データの実体) PVC(Persistent Volume Claim): アプリケーションが「ストレージを使いたい」と要求するリソース定義です。 PV(Persistent Volume): 実際のストレージ(ディスクなど)の実体です。 アプリケーションデータを復元するには、PVの実データだけでなく、そのデータを使うためのPVCというKubernetesリソース定義もセットでリストアしなければなりません。この「リソースとデータの実体をまとめて管理する」点が、従来のバックアップとの大きな違いです。 Blue/Greenにおけるデータ移行の課題 ステートフルなアプリケーションの安全なバージョンアップにおいて、バックアップ・リストアが課題になる箇所を説明します。 課題1:ロールバック時のデータ損失 Blue/GreenデプロイでデータをコピーしてGreen環境を構築した場合、ロールバックでBlue環境に戻ると、Green環境で発生した新規データは全て失われます。 技術的には「コピーした時点に戻す」という期待通りの動作ですが、これはデータ損失を意味します。 最も問題なのは、ロールバック後に改めてバージョンアップを試みる際、失われたデータは二度と取り戻せないという点です。 安全なロールバックを実現するには、このデータ損失を防ぎつつ、リソースとデータを同時に、任意の時点に復元する必要があります。 課題2:新旧バージョンのデータ互換性 Blue/Greenデプロイでデータストアを共有した場合、新バージョン(Green)がDBスキーマを変更すると、即座に旧バージョン(Blue)のアプリケーションが動かなくなります。 バックアップツールは「データの互換性」そのものを解決できません。データ移行処理(DBマイグレーション)を、デプロイプロセスの中に組み込む必要があり、この統合が複雑になります。 安全なバージョンアップを実現するには、これらの課題を解消するために、リソースとデータを同時に扱える専門的なツールが必要になります。 まとめ 今回の記事では、Kuberntesのバックアップは、etcdとPV/PVCの二軸で、セットで行うことが重要であることを解説しました。しかし、ステートフルなアプリケーションのバージョンアップでは、データの互換性と整合性が課題となります。 これらの複雑な課題を解消し、安全なリソースとデータのバックアップ・リストアを実現するにはKubernetes専門のバックアップツールが不可欠です。次回は、そのソリューションである「Velero」について解説します。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 初めてのKubernetesバージョンアップ:Kubernetesにおけるバックアップの必要性とデータ管理の課題 first appeared on SIOS Tech Lab .
はじめに 皆さん、こんにちは!PS-SLの織田です。 SIOS Tech Labアドベントカレンダー8日目になります!今回は、『 Java言語で学ぶデザインパターン入門 』の第1章を読んだ感想をまとめていきたいと思います。購入してからそこそこ時間が経ってしまったのですが、なんとか第1章を読むことができたので内容をまとめていきたいと思います。 Iteratorパターンとは 第1章ではIteratorパターンに関する内容が記されていました。Iteratorパターンとは何かしらの集合があったときに、それらを順に指していき 処理を繰り返し実行 することです。文字で説明しても何のこっちゃという感じなので、具体的なコードとともに説明していきます。 基本要素:Book.java public class Book { private String name; public Book(String name) { this.name = name; } public String getName() { return name; } } このクラスは単純なデータホルダーです。本の名前を保持するprivateフィールドと、それを取得するgetName()メソッドを提供します。コレクションに格納される個々の要素を表現する役割を担います。Iteratorパターンにおいては「要素」の役割を果たし、パターン全体の基盤となるシンプルなクラスです。 集約オブジェクト:BookShelf.java import java.util.Iterator; public class BookShelf implements Iterable<Book> { private Book[] books; private int last = 0; public BookShelf(int maxsize) { this.books = new Book[maxsize]; } public Book getBookAt(int index) { return books[index]; } public void appendBook(Book book) { this.books[last] = book; last++; } public int getLength() { return last; } @Override public Iterator<Book> iterator() { return new BookShelfIterator(this); } } 本棚を表現するクラスで、Iteratorパターンの中核となる「集約」の役割を担います。内部では固定サイズの配列を使って本を管理し、本の追加(appendBook)、指定位置の本の取得(getBookAt)、現在の本の数を取得(getLength)といった基本機能を提供します。 反復子:BookShelfIterator.java import java.util.Iterator; import java.util.NoSuchElementException; public class BookShelfIterator implements Iterator<Book> { private BookShelf bookShelf; private int index; public BookShelfIterator(BookShelf bookShelf) { this.bookShelf = bookShelf; this.index = 0; } @Override public boolean hasNext() { if (index < bookShelf.getLength()) { return true; } else { return false; } } @Override public Book next() { if (!hasNext()) { throw new NoSuchElementException(); } Book book = bookShelf.getBookAt(index); index++; return book; } } 実際の反復処理を担当するクラスです。BookShelfへの参照と現在の位置を示すindexフィールドを持ち、hasNext()で次の要素の存在を判定し、next()で要素を順次返します。 このクラスの重要な点は、BookShelfの内部構造を知らずに反復処理を行えることです。getLength()とgetBookAt()メソッドを通じてのみBookShelfにアクセスし、直接配列を操作することはありません。 利用例:Main.java import java.util.Iterator; public class Main { public static void main(String[] args) { BookShelf bookShelf = new BookShelf(4); bookShelf.appendBook(new Book("Around the World in 80 Days")); bookShelf.appendBook(new Book("Bible")); bookShelf.appendBook(new Book("Cinderella")); bookShelf.appendBook(new Book("Daddy-Long-Legs")); // 明示的にIteratorを使う方法 Iterator<Book> it = bookShelf.iterator(); while (it.hasNext()) { Book book = it.next(); System.out.println(book.getName()); } System.out.println(); // 拡張for文を使う方法 for (Book book: bookShelf) { System.out.println(book.getName()); } System.out.println(); } } Iteratorパターンの使用方法を実演するクライアントクラスです。BookShelfインスタンスを作成し、複数の本を追加した後、二つの異なる方法で反復処理を行います。 一つ目は明示的にiterator()メソッドを呼び出してIteratorを取得し、while文でhasNext()とnext()を使った伝統的な方法です。二つ目はJava 5以降で導入された拡張for文を使った方法で、Iterableインターフェースの実装により自動的に内部でIteratorが使用されます。 いずれにしても繰り返し処理の中ではIteratorのメソッドのみ呼び出されています。ここは伏線なので覚えておいてください。 Iteratorパターンで嬉しいこと 一見すると、冗長な書き方に見えるかもしれませんが、このパターンを使うことで受けられる恩恵があります。例えば、現在のBookShelfクラスは配列で管理をしているため、最初に指定した本棚の大きさ以上のデータは入れられません。そこで、配列ではなくjava.util.ArrayListを使うよう修正するとします。こうなると、「複数のファイルにまたがって修正を実施しなきゃいけないのか…」と思うかもしれませんが、実はBookShelfクラスだけ修正すれば大丈夫です。 BookShelf.java(修正後) import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class BookShelf implements Iterable<Book> { private List<Book> books; public BookShelf(int initialsize) { this.books = new ArrayList<>(initialsize); } public Book getBookAt(int index) { return books.get(index); } public void appendBook(Book book) { books.add(book); } public int getLength() { return books.size(); } @Override public Iterator<Book> iterator() { return new BookShelfIterator(this); } } 先述の通り、Mainの繰り返し処理の中ではIteratorのメソッドのみ呼び出されています。つまりBookShelfクラスに依存していないため修正も不要ということです。 もしiteratorパターンを使っていない状況で、配列からjava.util.Listへの修正を行った場合、main関数の繰り返し処理は以下のように変更する必要があります。 Main.java(配列版) public class Main { public static void main(String[] args) { BookShelf bookShelf = new BookShelf(4); bookShelf.appendBook(new Book("本A")); bookShelf.appendBook(new Book("本B")); // 配列を取得して処理 Book[] books = bookShelf.getBooks(); for (int i = 0; i < bookShelf.getLength(); i++) { System.out.println(books[i].getName()); } } } Main.java(List版) import java.util.List; public class Main { public static void main(String[] args) { BookShelf bookShelf = new BookShelf(4); bookShelf.appendBook(new Book("本A")); bookShelf.appendBook(new Book("本B")); // Listを取得して処理 List<Book> books = bookShelf.getBooks(); // Book[]からList<Book>に変更 for (int i = 0; i < books.size(); i++) { // getLength()からsize()に変更 System.out.println(books.get(i).getName()); // books[i]からget(i)に変更 } } } 上記のコードの通りMain.javaで複数箇所の修正が必要になり、手間がかかるだけでなく、コンパイルエラーが発生する可能性もあります。 import文の追加 変数型の変更 メソッド呼び出しの変更 まとめ 第1章を読んだ感想としては、「デザインパターンを意識しなくてもコード を書くのはなんとかなりそう。ただ、可読性や保守性はめっちゃ低いなぁ」という感じでした。例えるなら、きれいに整えられた回路(下図①)と、ただ闇雲にジャンク品を使いながら無計画に作られた回路(下図②)のような具合です。デザインパターンを理解していなくても動くものは作れるでしょう。しかし、作りたいものが複雑になればなるほど、デザインパターンなしでは保守・運用の難易度が跳ね上がります。まだまだ読み始めたばかりですが、今後もデザインパターンを学び、保守・運用のしやすいコードを実際の業務でも書けるように頑張りたいと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post デザインパターンのすゝめ ~Iteratorパターン編~ first appeared on SIOS Tech Lab .
こんにちは、伊藤です。 この記事は、アドベントカレンダー7日目の記事になります。 今回は、Exchange Onlineで、メールの自動転送を制限する方法を紹介します。 Exchange Onlineでは特定のユーザー・グループ・ドメインに対して自動転送可能なメールドメインを制限することが可能です。 今回はその中でも 特定のドメインメールのユーザーに対してメール自動転送を不可にする方法 、 特定のドメインメールのユーザーに対してメール自動転送可能ドメインを制限する方法 を紹介します。 メールの自動転送を制限する目的 メールの自動転送を制限する主な目的は、メールに含まれる情報の外部漏洩を軽減することです。メールでは、ファイルやパスワードのやり取りを行うことがありますが、自動転送を制限しない場合、全てのドメインのメール宛てに転送可能になるため、情報漏洩のリスクが高まります。 設定に必要なMicrosoft Entra IDのロールについて 今回紹介する設定を行うためには、「Exchange管理者」、「セキュリティ管理者」ロールを持つMicrosoft Entra IDのアカウントが必要です。 特定のドメインメールのユーザーに対してメール自動転送を不可にする 例として検証ドメイン(soito001.mail.onmicrosoft.com)のユーザに対してメール自動転送を不可にします。 アウトバウンド スパム フィルター ポリシーの設定 1. Microsoft 365 Defender ポータル(https://security.microsoft.com/)に管理者アカウントでサインインします。 2. [メールとコラボレーション] > [ポリシーとルール] > [脅威ポリシー] > [スパム対策] の順に選択します。 3. [スパム対策ポリシー] ページで、メール自動転送を制限したいドメイン(例:soito001.mail.onmicrosoft.com) 専用のカスタムポリシーを作成します。[+ ポリシーの作成] > [Outbound] の順に選択します。 4. ポリシーの名前を設定します(例: soito001.mail.onmicrosoft.com Auto-forward Policy)。 5. [ユーザー、グループ、およびドメイン] で、このポリシーを適用する対象として、[ドメイン] に”soito001.mail.onmicrosoft.com”を指定します。特定のユーザー、グループに対して適用したい場合は、それぞれ[ユーザー] 、[グループ]で指定してください。 6. [送信の保護設定]で、[自動転送ルール] に「オフ – 転送が無効になっています」を設定します。 7. 確認画面にて適用するポリシーを確認し、[作成]を選択します。 自動転送の制限を確認する 1. テストアカウント(exchangetest001@soito001.mail.onmicrosoft.com)の自動転送を有効にして、テストアカウントにメールを送信します。 2. Exchange 管理センター (EAC)(https://admin.exchange.microsoft.com/)に管理者アカウントでサインインします。 3. [メールフロー] > [メッセージ追跡] > [+ 追跡を開始] の順に選択します。 4. [新しいメッセージ追跡]で、[受信者]にテストアカウントを設定して[検索]を選択します。時間の範囲等の条件で絞り込むことも可能です。 5. テストアカウントへのメール送信に該当する追跡結果を確認し、[メッセージイベント]で外部転送がブロックされている内容を確認します。 特定のドメインメールのユーザに対してメール自動転送可能ドメインを制限する 例として検証ドメイン(soito001.mail.onmicrosoft.com)のユーザに対して社内ドメインのみへのメール自動転送を許可します。 アウトバウンド スパム フィルター ポリシーの設定 1. Microsoft 365 Defender ポータル(https://security.microsoft.com/)に管理者アカウントでサインインします。 2. [メールとコラボレーション] > [ポリシーとルール] > [脅威ポリシー] > [スパム対策] の順に選択します。 3. [スパム対策ポリシー] ページで、メール自動転送を制限したいドメイン(例:soito001.mail.onmicrosoft.com) 専用のカスタムポリシーを作成します。[+ ポリシーの作成] > [Outbound] の順に選択します。 4. ポリシーの名前を設定します(例: soito001.mail.onmicrosoft.com Auto-forward Policy)。 5. [ユーザー、グループ、およびドメイン] で、このポリシーを適用する対象として、[ドメイン] に”soito001.mail.onmicrosoft.com”を指定します。特定のユーザー、グループに対して適用したい場合は、それぞれ[ユーザー] 、[グループ]で指定してください。 6. [送信の保護設定]で、[自動転送ルール] に「オン – 転送が有効になっています」を設定します。 7. 確認画面にて適用するポリシーを確認し、[作成]を選択します。 リモートドメインの設定 1. Exchange 管理センター (EAC)(https://admin.exchange.microsoft.com/)に管理者アカウントでサインインします。 2. [メール フロー] > [リモート ドメイン] の順に選択します。 3. ここで、許可するドメイン と 既定(その他すべて) の設定を行います。     A. 許可する外部ドメインの設定     [+ リモート ドメインを追加] をクリックします。     [ドメイン名を指定]で、[リモートドメイン]に社内ドメインを入力します。     [メールの返信の種類] で、「自動転送を許可する」 (Allow automatic forwarding) を有効にします。    確認画面にて適用するリモートドメインの設定を確認し、[保存]を選択します。     B. 既定ドメインの設定 (許可されていないその他すべてのドメイン)     リモート ドメインの一覧から、「Default」または * (アスタリスク) という名前の既定のリモート ドメインを見つけ、[返信の種類を編集]を選択します。     「自動転送を許可する」 (Allow automatic forwarding) を無効にして保存します。 自動転送の制限を確認する 1. テストアカウント(exchangetest001@soito001.mail.onmicrosoft.com)の自動転送を有効にして、[メールの転送先]に社内ドメインのメールアドレスを指定し、テストアカウントにメールを送信します。 2. 社内ドメインのメールアドレスへの転送は許可されているため、社内ドメインのメールアドレスで転送メールを受信することができます。 3. Exchange 管理センター (EAC)(https://admin.exchange.microsoft.com/)のメッセージ追跡機能にて、テストアカウントへのメール送信に該当する追跡結果を確認し、[メッセージイベント]で外部転送が完了した内容を確認します。 4. テストアカウント(exchangetest001@soito001.mail.onmicrosoft.com)の[メールの転送先]に社内ドメイン以外のメールアドレスを指定し、テストアカウントにメールを送信します。 5. Exchange 管理センター (EAC)(https://admin.exchange.microsoft.com/)のメッセージ追跡機能にて、テストアカウントへのメール送信に該当する追跡結果を確認し、[メッセージイベント]で外部転送がブロックされている内容を確認します。 特定のドメインメールのユーザーに対してメール自動転送を不可にする方法 で確認したメッセージの内容とは異なりますが、イベント: Dropはメールの配送がここで 破棄(Drop) されたことを示しており、”handled AutoForward addressed to external recipient”は、外部受信者宛の自動転送(AutoForward)として処理されたことを示しております。外部への自動転送の設定で禁止されているからここで破棄するという挙動になります。 まとめ 今回は、Exchange Onlineで、 特定のドメインメールのユーザーに対してメール自動転送を不可にする方法 、および 特定のドメインメールのユーザーに対してメール自動転送可能ドメインを制限する方法 を紹介しました。 Exchange Onlineの設定の参考にしていただければ幸いです。 参考 参考 Microsoft 365 での外部メール転送の構成と制御 – Microsoft Defender for Office 365 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Exchange Onlineのメール自動転送を制限する first appeared on SIOS Tech Lab .
アドベントカレンダー6日目の記事です。 今回はClaude Codeを使っていて「毎回同じ説明をするのが面倒」「プロジェクト固有のルールを覚えさせたい」という問題を解決する方法を紹介します。 また今回Web開発に使えそうな汎用Skillsのテンプレートを作成し、Githubで公開しましたのでぜひ本記事を読む際に参考にしていただき、よりよいスキルセットがあればPRお待ちしています。 https://github.com/atomic-kanta-sasaki/claude-code-general-skills/tree/main/.claude/skills Skillsとは何か? Skillsは、Claudeに特定のタスクの実行方法を教えるためのナレッジパッケージです。 公式ドキュメントでは以下のように説明されています: Skillsはフォルダ内の指示、スクリプト、リソースで、Claudeが特定のタスクを繰り返し実行する方法を教えます。 簡単に言えば、 新しいチームメンバーに渡すオンボーディング資料 のようなものです。プロジェクトの規約、ツールの使い方、ワークフローを文書化しておけば、Claudeがそれを参照して作業してくれます。 Skillsの特徴 自動呼び出し : ユーザーが明示的に指定しなくても、タスクに応じてClaudeが自動で適切なスキルを選択 プログレッシブ・ディスクロージャー : 必要な情報だけを段階的に読み込むため、コンテキストを圧迫しない コード実行可能 : 指示だけでなく、Pythonスクリプトなども含められる Skillsとサブエージェント・CLAUDE.mdの違い Claude Codeには似たような機能がいくつかあります。違いを整理しておきましょう。 機能 目的 トリガー コンテキスト Skills 知識・手順の提供 自動(description基準) メインと共有 Subagents タスクの委任・並列処理 自動/手動 独立 CLAUDE.md プロジェクト全体のコンテキスト 常時読み込み メインと共有 Commands 定型プロンプト実行 手動(/command) メインと共有 使い分けの指針 Skills : 特定タスク(レビュー、テスト作成など)の専門知識 Subagents : 独立したコンテキストで並列作業させたい場合 CLAUDE.md : プロジェクト全体で常に参照すべき情報 Commands : よく使うプロンプトのショートカット Skillsのディレクトリ構造 Skillsは以下の場所に配置します: .claude/skills/ └── your-skill-name/ ├── SKILL.md # 必須:メインの指示ファイル ├── reference.md # 任意:参照ドキュメント ├── examples.md # 任意:使用例 ├── scripts/ # 任意:ヘルパースクリプト │ └── helper.py └── templates/ # 任意:テンプレートファイル └── template.txt 配置場所による違い 場所 スコープ ~/.claude/skills/ 全プロジェクトで使用可能(個人用) .claude/skills/ プロジェクト固有(チーム共有可能) SKILL.md の書き方 基本構造 --- name: your-skill-name description: | このスキルが何をするか、いつ使うべきかの説明。 具体的なトリガーワードや使用シーンを含める。 version: 1.0.0 --- # Your Skill Name ## Overview このスキルの概要説明 ## Instructions ステップバイステップの指示 ## Examples 具体的な使用例 フィールドの説明 フィールド 必須 制限 説明 name 64文字、小文字・数字・ハイフンのみ スキルの識別子 description 1024文字 最重要 : いつ呼び出すかの判断基準 version 任意 – バージョン管理用 descriptionの書き方(最重要ポイント) Skillsが正しく呼び出されるかどうかは、 descriptionの書き方で9割決まります 。 Claudeはdescriptionを見て「このスキルを使うべきか」を判断するため、曖昧な記述では呼び出されません。  悪い例 description: コードを手伝う これでは「いつ」「何を」手伝うのかわかりません。 良い例 description: | Pythonコードのセキュリティレビューを実施。 OWASP Top 10に基づく脆弱性チェック、認証・認可の検証、入力バリデーション確認。 セキュリティチェック、脆弱性診断、コードのセキュリティ評価時に使用。 良いdescriptionのポイント: 具体的な動作 : 何をするスキルか明確に トリガーワード : どんな言葉で呼び出されるべきか 使用シーン : どんな状況で使うか 境界線 : 何に使わないか(オプション) Skillsの呼び出しの仕組み プログレッシブ・ディスクロージャー Skillsは段階的に情報を読み込む設計になっています: 1. 起動時 └─ 全スキルの name と description だけを読み込み 2. ユーザーリクエスト受信 └─ description を参照してマッチするスキルを判断 3. スキル選択時 └─ SKILL.md の本文を読み込み 4. 必要に応じて └─ scripts/ や references/ を読み込み この設計により、多数のスキルをインストールしても、実際に使うスキルの情報だけがコンテキストを消費します。 スクリプトの活用 Skillsの強力な機能の1つが、 Pythonスクリプトの実行 です。 指示だけでは不確実な処理(フォーマット、バリデーションなど)をスクリプトで確実に実行できます。 例:フォーマッタースキル --- name: python-formatter description: | Pythonコードをプロジェクト標準にフォーマット。 Black, isort, ruffを適用。Pythonファイルの整形時に使用。 version: 1.0.0 --- # Python Formatter ## Workflow ### Step 1: フォーマット実行 Run: `python .claude/skills/python-formatter/scripts/format.py <file>` ### Step 2: 結果確認 フォーマット結果を確認し、問題があれば報告。 # scripts/format.py import subprocess import sys def main(): file = sys.argv[1] subprocess.run(["black", file]) subprocess.run(["isort", file]) print(f" Formatted: {file}") if __name__ == "__main__": main() スクリプトのメリット 確実性 : LLMの揺らぎなく、決まった処理を実行 コンテキスト節約 : スクリプトの内容ではなく、出力だけがトークンを消費 再利用性 : 同じスクリプトを複数のスキルで共有可能 Skillsが有効なケース・有効でないケース  有効なケース 定型的なレビュー作業 : セキュリティチェック、コード品質チェック ドキュメント生成 : API仕様書、README、ADRなど フォーマット・バリデーション : スクリプトと組み合わせて確実に実行 プロジェクト固有のルール適用 : ブランドガイドライン、コーディング規約  有効でないケース 創造的なタスク : アイデア出し、自由な設計 対話的な作業 : フィードバックを受けながら進める作業 コンテキスト依存の判断 : プロジェクト全体を俯瞰した判断 単純な質問応答 : スキルを使うまでもないタスク ベストプラクティス 1. スキルはフォーカスを絞る 1つのスキルに複数の機能を詰め込まず、目的別に分割しましょう。 ❌ code-helper(何でもやる) ✅ security-review(セキュリティ特化) ✅ test-generator(テスト生成特化) ✅ api-design(API設計特化) 2. SKILL.mdは500行以下に 長すぎるスキルはコンテキストを圧迫します。詳細は別ファイルに分割し、SKILL.mdはメニューとして機能させましょう。 3. 具体例を含める Claudeが「成功とは何か」を理解できるよう、入出力の例を含めましょう。 4. 制限事項を明記する スキルができないことを明示すると、誤用を防げます。 セキュリティ上の注意 Skillsは強力な機能ですが、注意点もあります: 信頼できるソースのみ使用 : 自作またはAnthropicの公式スキルを推奨 APIキーをハードコードしない : 環境変数を使用 ダウンロードしたスキルは監査 : 実行前にスクリプトの内容を確認 まとめ Claude Code Skillsは、AIコーディングを「汎用アシスタント」から「プロジェクト専門家」に進化させる機能です。 ポイントをまとめると: descriptionが命 : 適切に書かないと呼び出されない 段階的読み込み : 多数のスキルを入れてもパフォーマンスに影響しにくい スクリプト活用 : 確実に実行したい処理はコード化 フォーカスを絞る : 1スキル1目的で設計 まずはシンプルなスキルから始めて、徐々に拡張していくのがおすすめです。 次の記事では、Web開発で使い回せる汎用スキル集を紹介します。 この記事はClaude Code公式ドキュメントおよび実際の検証に基づいて作成しています。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Claude Code Skillsの使い方と汎用テンプレート公開 first appeared on SIOS Tech Lab .
こんにちは、PS-SLの織田です。SIOS Tech Labアドベントカレンダー5日目になります。 今回のブログは資格取得に関するものになります。今年の7月に Azure Administrator Associate(AZ-104) に合格しました。この試験は、Azureの広範な知識と実践的な理解が求められるため、闇雲に学習を進めてもなかなか成果が出にくいものです。 私が実際に行った学習方法の中で特に効果的だった方法と、これから受験される方への具体的なアドバイスを、私の失敗談も交えながら、詳しくご紹介します。 最も合格に直結した学習リソース 私の学習において、最も合格に直結し、知識の定着に役立ったのはUdemyで提供されている高品質な予想問題集でした。学習を始めた当初は、「試験問題はAzureリソースのことをよく理解してから解いたほうが良いかな…」と考えていたのですが、むしろこの問題集を中心に学習を進めた方がよいと後になって気づきました。 本番さながらのシミュレーション : 予想問題集は、実際の試験と同じように、複数の選択肢、ドラッグアンドドロップ、ケーススタディなど、多様な問題形式で構成されています。時間を計って模擬試験として取り組むことで、 2時間の制限時間内に問題を解ききるペース配分 を身につけることができました。 出題傾向の把握と知識の定着 : 実際に本番と非常に似た、あるいは全く同じトピックの問題が出題されることが多々あります。これにより、試験で問われる核となる概念や、細かい設定項目についての理解が深まります。 正解の選択肢だけでなく、不正解の選択肢がなぜ間違っているのか を理解することで、知識をより確固たるものにすることができました。実際に本番では、練習問題とほぼ同じ問題が多数出題されました。練習問題をやりこむことで大きく加点を増やすことができ合格率を高めることができます。 本番環境を意識したMSLearn活用 AZ-104試験の最大の特徴は、試験中に公式ドキュメントであるMS Learnを参照することが許可されている点です。自分は高校や大学の試験のように「全てを暗記する」必要があると勘違いしていたのですが、実は「調べる力」も求められる試験でした。 MSLearnで「調べる力」を鍛える : 合格の鍵は、「どのトピックについて問われているか」を瞬時に判断し、「MS Learnのどのページを見れば確実な情報が得られるか」の当たりをつける能力です。最初のうちは欲しいページを探すのに時間がかかってしまいますが、繰り返し行っていくうちに素早く欲しいページを見つけられるようになります。 練習中からMS Learnを確認する習慣 : 予想問題集を解く際も、最初から「MS Learnを見ても良い」という本番ルールを適用し、常に「この問題はMS Learnのどのページを見れば解決できるか」という視点で検索する癖をつけましょう。 ドキュメント構造に慣れる : 練習を通じて、各Azureサービス(例:Virtual Machines、VNet、Storage Account)の概要ページ、料金ページ、具体的なデプロイ手順やトラブルシューティングに関するドキュメントが、MS Learnのどこに、どのようなキーワードで存在しているか、おおよその「あたり」を頭の中に作り上げておくことが、本番での貴重な時間短縮につながります。 Azure Storage アカウントの種類をまとめた表。このような同一サービスにおける種類・プランによる差異を比較できるページにあたりをつけておくと試験本番で重宝する。 私の失敗談:モチベーションの回復から推奨学習サイクルへ 実は、私の学習の初期段階では大きな失敗がありました。 最初は自分の実力を測ろうと、MS Learnを全く見ずに予想問題集に挑戦しました。結果は散々で、ほとんどの問題が分からず、スコアも非常に低く、 「こんなに難しいのか」とモチベーションが一気に低下 してしまいました。 しかし、合格した方からのアドバイスや冷静に試験の特性を考え直した結果、学習方法を根本的に変更しました。 推奨する効率的な学習サイクル 私が最も効果的だと思った学習サイクルは以下の通りです。 MS Learnを参照しながら練習問題を解く : 最初は正解率を気にせず、「どのドキュメントが使えるか」を探しながら解きます。問題を解くための手がかりを検索する能力を養うことに集中します。 間違った問題・自信のない問題の徹底的な深掘り(深掘り学習) : 単に正解の解説を読むだけでなく、間違えた理由や、偶然正解したものの自信がないトピックについては、徹底的に時間をかけて深く理解します。 「深掘り学習」の具体的なアプローチ 深掘りとは、単なる知識の丸暗記ではなく、その知識を多角的に理解し、本番で応用できる状態にすることです。 多角的な情報の整理 : 表や図の活用 : 例えば、ストレージアカウントの異なる冗長オプション(LRS, GRS, ZRS, GZRS)の特性や、VMの異なるサイズ(SKU)の機能や価格の違いなど、 比較が必要な情報を自分なりに表や図に整理 します。 実践的な比較 : 似た機能を持つAzureリソース(例:Azure FirewallとNetwork Security Group)について、それぞれの ユースケース、メリット・デメリット を対比して整理します。 公式ドキュメントとの紐づけ : 「この問題はMS Learnのどのドキュメントに該当する内容か」を再確認し、 いつでもそのページにすぐにたどり着けるように、キーワードや目次構造を脳内にインデックス化 します。 特に、PowerShellやAzure CLIのコマンド例が問われる問題については、MS Learnで実際のコマンドを確認し、なぜその引数が必要なのかを理解します。 実際に私が作成した冗長オプションをまとめた図。文字だけでは分かりずらい内容も図表にすることで視覚的に理解することができる。 まとめ 実は今回初めてテストセンターを利用して少し緊張していましたが、問題集をやりこんだおかげで、落ち着いていつも通り回答することができたと思います。練習問題をやりこんでおいてよかったです。 ただし、今回ご紹介した方法はあくまで一例ですので、参考程度に見てもらえればと思います。実際に学習を進める過程でご自身に合った学習方法で進めていくと良いかなという感じです。皆さんの学習が効率的に進むことを願っています! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AZ-104 合格体験記:効果的な学習法と予想問題集の活用戦略 first appeared on SIOS Tech Lab .