サイオステクノロジー(Tech.Lab)のブログ - TECH PLAY

TECH PLAY

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

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

全739件

本書は、2025年8月に公開した マイクロサービス化の課題とその解決策 に関するホワイトペーパーを紹介するものです。 「ビジネスの変化に柔軟かつスピーディーに対応するためのマイクロサービス化だったはずが、なぜか開発は遅くなり、運用は複雑怪奇、分散モノリスのようになってしまった…」 もし、読者の皆様が企業の技術リーダーやアーキテクトとして、少しでも心当たりのあるのであれば、本書をお勧めいたします。さらに、AI時代のマイクロサービス化においては、別の問題も待ち構えていることを意識する必要があります。 AIが生み出すマイクロサービス化の新しい課題 ご存知の通り、生成AIは、驚異的なスピードでコードを生成します。しかし、一方ではビジネスの意図を汲み取ることが苦手です。 その結果、AIが高速で生成したコードが、気づかぬうちにサービスをまたがるデータの整合性を破壊し、ビジネスに致命傷をもたらしかねません。ここは人間による介入が必要な部分なのです。従来のSagaパターンのような複雑な手法のみでは、AIが大量に生み出すコードの整合性を人間が検証し続けるには、もはや限界に近づいています。 課題の全体像と解決へのアプローチ この度公開したホワイトペーパー「マイクロサービス化の課題とその解決策」では、マイクロサービス化に関する旧来からの課題に対しては、単なる技術論のみではなく、技術論から一歩離れた組織論から検討する体系的なアプローチでの解決策を提案します。更に、AI時代のマイクロサービス固有の課題であるデータ整合性担保に対する解決策として、強力な選択肢の一つとなり得る ScalarDB を紹介します。 ホワイトペーパーの構成(11ページ) 第1部:問題提起 – なぜマイクロサービス化は失敗するのか 第2部:SIOS APIエコシステムとScalarDBによる包括的解決策 第3部:実践的トレードオフと典型的な設計パターン 第4部:結論   経験豊富なアーキテクトの皆様へ 第3部では、本書の記載に対して経験豊富なアーキテクトであればこそ抱くであろう妥当な疑問に対して、正面から向き合います。 疎結合と「時間的カップリング」の板挟み 2PCの性能オーバーヘッドとTCO(総所有コスト) アプリケーション層からインフラ層への複雑性の移動は問題解決になるか? ストラングラーフィグパターンへの応用   ▼ホワイトペーパーのダウンロードはこちらから https://api-ecosystem.sios.jp/scalar/download/scalarsiosapi01.html ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post マイクロサービス化の新旧課題の解決策を提案するホワイトペーパーの紹介 first appeared on SIOS Tech. Lab .
はじめに こんにちは!珍しくブログを連投しているなーがです。今回はClaude Code UIをインストールした際につまづいたことについて書こうと思います。 最近、AIを活用した開発ツールが次々と登場していますが、その中でもClaude Code UIは特に注目を集めています。しかし、Windows環境でのインストールには意外な落とし穴があることがわかりました。同じ問題で悩む方のお役に立てればと思い、解決方法をまとめてみました。 時間がない方のために Claude Code UIをWindows環境でインストールする際は、Visual Studio Installerから以下の2つをインストールする必要があります: ワークロード「C++によるデスクトップ開発」 個別のコンポーネント「最新のSpectre 軽減のライブラリ」 これらがないと、 npm install 時にビルドエラーが発生します。 Claude Code UIとは Claude Code UIは、Anthropic社のClaude Code CLIやCursor CLIをWebブラウザ経由で操作できるようにするWebアプリケーションです。これにより、ローカル環境だけでなく、スマートフォンやタブレットなどのモバイルデバイスからもAIを活用したコーディングが可能になります。 主な特徴 ブラウザベースのターミナルインターフェース Claude CodeやCursorの全機能をWeb UIから利用可能 モバイルフレンドリーなレスポンシブデザイン Cloudflare Tunnelなどと組み合わせて、どこからでもアクセス可能 GitHubリポジトリ:https://github.com/siteboon/claudecodeui 関連するいくつかのブログが投稿されています。 スマホでClaude Code!?外出先でClaude Codeを使ってみた!! (こちらは弊社の細川君の記事なのでぜひ読んでください!) 【徹底解説】Claude Code UI と Cloudflare Tunnelでスマホから快適にAIコーディング Claude Code UIレビュー:スマホからでもAIコーディングが可能に!開発効率が劇的向上 インストール手順と遭遇した問題 前提条件の確認 READMEによると、以下の前提条件が必要です: Prerequisites Node.js v20 or higher Claude Code CLI installed and configured, and/or Cursor CLI installed and configured 私のPCにはすでにv20以上のNode.jsとClaude Code CLIがインストール済みだったので、さっそくインストールを開始しました。 インストール開始 まずはリポジトリをCloneします。 git clone https://github.com/siteboon/claudecodeui.git Cloneされたら、ディレクトリを移動します。 cd claudecodeui ライブラリをインストールします。 npm install 最初のエラー:Visual Studio C++ツールセットが見つからない $ npm install npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory... (中略) npm error gyp ERR! find VS - found "Visual Studio C++ core features" npm error gyp ERR! find VS - missing any VC++ toolset npm error gyp ERR! find VS could not find a version of Visual Studio 2017 or newer to use npm error gyp ERR! find VS npm error gyp ERR! find VS ************************************************************** npm error gyp ERR! find VS You need to install the latest version of Visual Studio npm error gyp ERR! find VS including the "Desktop development with C++" workload. npm error gyp ERR! find VS For more information consult the documentation at: npm error gyp ERR! find VS https://github.com/nodejs/node-gyp#on-windows npm error gyp ERR! find VS ************************************************************** すっごく長いエラーが発生して最初は驚きましたが、よく見ると意味は明確でした。 エラーの原因: node-pty というNode.jsのネイティブモジュールのビルドでエラーが発生しています。 node-pty は疑似ターミナル(PTY)を作成するためのモジュールで、Claude Code UIのターミナル機能に必要不可欠です。 Windowsでこのようなネイティブモジュールをビルドするには、C++のコンパイラツールチェーンが必要となります。エラーメッセージは「Visual StudioのC++開発ツールが見つからないので、最新のVisual Studioをインストールして『C++によるデスクトップ開発』ワークロードをインストールしてください」と教えてくれています。 解決策1:Visual Studio C++開発環境のインストール Visual Studio Installerを開きます。Visual Studioをインストールされていない方は、 こちら からダウンロードしてください。 「変更」をクリックします。 「ワークロード」タブで「C++によるデスクトップ開発」を選択してインストールをクリックします。(筆者はインストール済みのため、ボタンが「閉じる」になっています) インストールが終わったら、再度ライブラリをインストールするコマンドを実行します。 npm install 2回目のエラー:Spectre軽減ライブラリが必要 $ npm install npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory... (中略) npm error C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.CppBuild.targets(511,5): error MSB8040: Spectre 軽減のライブラリは、このプロジェクトに必要です。使用されているツールセットとアーキテクチャについては、Visual Studio インストーラー (個々のコンポーネント タブ) からインストールします。詳細情報: https://aka.ms/Ofhn4c 今度は「Spectre 軽減のライブラリ」が必要というエラーが発生しました。 Spectreとは? 2018年に発見されたCPUの脆弱性で、投機的実行という高速化技術の副作用を悪用して、本来アクセスできないメモリ領域の情報を盗み見ることができる攻撃手法です。MicrosoftはこれらのセキュリティリスクからWindowsアプリケーションを保護するため、Visual Studioに特別な軽減ライブラリを提供しています。 最近のプロジェクトでは、セキュリティ上の理由から、これらの軽減ライブラリを使用することがデフォルトで要求されることが多くなっています。 解決策2:Spectre軽減ライブラリのインストール 診断コードMSB8040の解決策は Microsoft公式リファレンス に記載されている通り、Visual Studio Installerの「個別のコンポーネント」タブから最新のSpectre軽減ライブラリを選択してインストールします。 インストールが終わったら、三度目の正直でライブラリをインストールするコマンドを実行します。 npm install インストール成功! $ npm install npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory... (警告は続きますが、これらは依存関係の警告なので問題ありません) added 613 packages, and audited 614 packages in 2m 141 packages are looking for funding run `npm fund` for details 1 low severity vulnerability To address all issues, run: npm audit fix Run `npm audit` for details. やっとライブラリのインストールが成功しました!警告はいくつか表示されていますが、これらは依存パッケージの非推奨に関するものなので、動作には影響しません。 あとは README.md に書いてある通りに実行するだけです! トラブルシューティング もし同様の問題に遭遇した場合は、以下の点を確認してください: 1. Visual Studioのバージョン Visual Studio 2017以降が必要です Community版で問題ありません 2. 必要なコンポーネント 必須 : C++によるデスクトップ開発(ワークロード) 必須 : 最新のSpectre軽減のライブラリ(個別のコンポーネント) 3. Node.jsのバージョン Node.js v20以上が必要です node --version で確認できます 4. 権限の問題 管理者権限でコマンドプロンプトを実行してみてください node_modules フォルダを削除してから再度 npm install を試してください まとめ 今回はClaude Code UIをWindows環境でインストールする際につまづいたことについて書きました。一見すると単純なnpmパッケージのインストールですが、ネイティブモジュールを含む場合は、Visual StudioのC++ビルドツールが必要になることがあります。 特にWindows環境では、以下の2つが必要です: Visual StudioのC++によるデスクトップ開発ワークロード Spectre軽減のライブラリ これらを事前にインストールしておけば、スムーズにセットアップできるはずです。 昨今のAIツールの発展はめまぐるしいものがありますが、こうしたツールを活用することで、開発効率は飛躍的に向上します。インストールで少し苦労するかもしれませんが、その価値は十分にあると思います。 AIツールの進化に負けずに、楽しく開発していきましょう! 参考リンク Claude Code UI GitHub Repository Node.js Visual Studio Downloads MSB8040エラーの解決方法(Microsoft公式) node-gyp on Windows(GitHub) ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Claude Code UIをインストールする際につまづいたこと first appeared on SIOS Tech. Lab .
はじめに 前回 は、CI/CDの基本から、GitLab Runnerの導入、そしてコンテナイメージのビルド・プッシュ・デプロイまで、一連のワークフローを解説しました。 今回は、マージリクエスト(MR)の重要な役割であるコードレビューに焦点を当てます。コードレビューは、バグの早期発見やコード品質の向上、さらにはチームの知識共有を促す、開発プロセスに欠かせない作業です。 本記事では、GitLabのMR機能を活用し、レビューをスムーズに進めるための具体的な操作方法を、レビュアー(レビューする人)とレビューイ(レビューしてもらう人)の両方の視点から解説します。 コードレビューの概要 コードレビューとは、他の開発者が書いたコードを読み、フィードバックや改善点を議論するプロセスです。これは、単にバグを見つけるだけでなく、チーム全体のコード品質を高め、知識を共有し、開発の一貫性を保つために重要です。 GitLabでは、このコードレビューのプロセスがマージリクエスト(MR)に統合されています。MR上でコードレビューを行う流れは以下のようになります。 レビューイ(レビューしてもらう人):構文チェック後にMRを作成し、パイプラインの自動テストが完了していることを確認する。 レビューイ:レビュアーを指定してレビュー依頼を出す。 レビュアー(レビューする人):レビュー依頼を受けたらMR画面を確認し、MRの概要を把握する。 レビュアー:コードの変更点へのコメントをする レビューイ:MRのレビューを確認し、コードの変更が必要な場合は修正する。 レビュアー:コードの変更点に問題がなければMRを承認する。 レビューイ:MRが承認されたらマージする。 今回はレビューイが承認をもらったら、レビューイ自身でマージする流れで紹介します。 承認に複数名必須な場合もあるので、すべての承認者が承認してからマージするためにレビューイ自身でマージする流れを紹介しましたが、誰がマージするかはチーム文化に依存します。 マージリクエストのレビュー機能紹介 MRの設定 マージリクエスト(MR)を作成する際、タイトルや説明、担当者といった様々な項目を設定できます。 タイトル MRの目的を示します。一目で内容がわかるように「feat: 新機能追加」や「fix: バグ修正」といったルール(コミット規約)を適用するのが一般的です。 説明 以下の点を意識して記載すると、レビュアーは内容を素早く理解できます。 なぜこの変更が必要かといった背景や課題 何を変更したかといった技術的な詳細 何を確認してほしいかといったレビューのポイント 関連するイシュー番号 担当者 このMRの責任者をアサインします。通常は作成者(レビューイ)自身をアサインします。 レビュアー コードレビューを依頼する人を指定します。 マイルストーン プロジェクトの特定のフェーズやリリース目標にMRを関連付けます。次のバージョンのリリースに向けたタスクをまとめること等ができます。 ラベル MRを分類するためのタグ付け機能です。「バグ」「ドキュメント」「緊急修正」などのラベルをつけて分類します。 マージ開始日時 マージを特定の日時にスケジュールするための機能です。特定の時間に自動でマージを実行したい場合に利用します。例えば、深夜に自動デプロイを行う際などに便利です。マージを開始するには、CI/CDのテストが成功している状態などのチェックに合格している必要があります。 マージオプション MRが承認されたときにソースブランチを削除します。 マージ完了後に、不要になった作業用ブランチを自動的に削除します。マージ後にブランチを削除し忘れることがなくなり、リポジトリを整理できます。 MRが承認されたときにコミットをスカッシュします。  マージする際に、複数のコミットを1つの大きなコミットにまとめる機能です。マージ時には1つのコミットとして履歴に記録できます。これにより、mainブランチの履歴を見やすく保てます。 ブランチ保護 以前の記事 で解説したように、mainのような重要なブランチは保護設定をすることが推奨されます。これにより、MRを経由しない直接のプッシュを防ぎ、レビューのプロセスを強制します。 Approve必須化 プロジェクトの承認ルールを設定することで、特定のチームメンバーやコードオーナーからの承認をマージに必須できます。例えば、「2人以上のレビューアの承認が必要」といったルールを設定することで、コード品質のチェック体制を強化します。この設定は、Premium, Ultimateのプランで行えます。Free版では1人の承認必須設定が可能です。 参考: https://docs.gitlab.com/user/project/merge_requests/approvals/rules/ レビュアー(レビューする人)の視点 レビュアーとしてMRをアサインされたら、以下のステップでレビューを進めましょう。 MR画面の確認 まず、MRの概要を把握します。 MRの目的、関連するイシュー、変更の概要を確認します。 失敗したCI/CDパイプラインのエラーが未解決の場合は、そこでレビューを中断し、修正を依頼します。 変更されたコードを詳細に確認します。以下の点を意識して確認するとよいと思います。 コードの品質 境界値や大規模データでの計算量が考慮されているか プロジェクトのコーディング規約に準拠しているか 変更点へのコメント 特定の変更点にコメントを付けることで、レビューイにフィードバックを伝えます。 変更された特定の行にカーソルを合わせると、コメントアイコンが表示されます。クリックすると、コメント入力欄が現れます。 修正案をコメントで提案できます。 「レビューを開始」 複数の箇所にわたるフィードバックを、まとめて一度に投稿したい場合に使うボタンです。このボタンをクリックすると、コメントはすぐに投稿されず、「保留中のコメント」として保存されます。すべてのレビューを終えた後、MRの画面の「Your review」から「レビューを送信」ボタンをクリックすると、保留中のすべてのコメントがまとめて投稿されます。 「今すぐコメントを追加」 特定のコード行に対して、すぐに投稿したい単発のコメントに使うボタンです。 明確な正解はありませんが、基本的には「レビューを開始」でフィードバックをまとめ、「今すぐコメントを追加」で緊急性の高いシンプルなコメントを送るのが良いと思います。例えば、大きめのリファクタリングでは「レビューを開始」、小さな誤字では「今すぐコメントを追加」といった使い分けができます。 「提案」 コメント入力欄にある「候補を挿入する」ボタンをクリックし、修正後のコードを記述します。これにより、ワンクリックで修正を適用できるようになります。 記述したら、その他コメントと同様にコメントを追加します。 承認(Approve) レビューが完了したら、MRを承認します。コードに修正が必要な場合は、レビューイに修正依頼を出し、修正完了の連絡をもらったら再度変更を確認して承認します。 変更が適切であると判断したら、MR画面の「承認」ボタンをクリックします。これは、コードを承認したという意思表示になります。 レビューイ(レビューしてもらう人)の視点 レビューイは、MRを作成してビューを依頼した後、レビュアーからのフィードバックに対応し、変更を完了させます。レビューが完了したらマージを行います。 MRの作成 マージするブランチのコードを確認して、レビュアーに見てもらえる状態にします。以下の点を意識して確認するとよいと思います。 パイプラインのテスト結果が成功しているか 不要なデバッグコードやコメント、一時ファイルが残っていないか レビュアーを指定してMRを作成します。MRの作成手順は 第5回 を参考にしてみてください。 コメントへの返信 質問のコメントに対しては意図を説明する返信を行います。議論が必要な場合は、返信で対話を続けます。 感謝の意を常に忘れないようにしましょう。 変更の適用 レビュアーから「提案」を受け取った場合、GitLabの機能を使って簡単に変更を適用できます。しかし、提案をそのまま適用すると1コミット単位で履歴が増えるため、コミットをまとめたい場合はローカルでの修正が必要です。「提案」機能ではコミットの粒度を制御しにくいため、ローカルでの修正との使い分けが必要です。 MRの「変更」タブで、コメントに付いている「変更を適用」ボタンをクリックします。コミットメッセージを入力して「適用」をクリックします。これで、提案された変更をコミットとして追加できます。 表示するコミットを最新バージョンに変更します。 提案された変更が適用されていることが確認できます。これで、簡単に変更を適用できました。 追加コミットと再プッシュ 複雑な修正が必要な場合や、提案をまとめて適用したい場合は、ローカルで変更を行い、再度コミットしてプッシュします。コメントに対してすべて対応が完了したら、再度レビュアーに連絡して、承認をもらいます。 マージ レビューが完了したら、MRを承認してマージします。 承認ルールが満たされ、すべてのチェックが完了したら、MR画面の「マージ」ボタンをクリックして、ブランチをマージします。 まとめ 今回は、GitLabのマージリクエストを単なるマージの申請ではなく、コードレビューの場として活用する方法を解説しました。 レビュアーは、MR画面を確認し、具体的なコメントや提案を使ってフィードバックを伝えます。一方、レビューイは、コメントに適切に対応し、修正を迅速に反映させることが重要です。MRでレビューを行うことで、レビューの履歴が残り、開発の透明性も向上します。 MRの機能を使いこなすことで、チーム全体のコード品質が向上し、効率的な開発が可能になります。 参考文献 https://docs.gitlab.com/user/project/merge_requests/reviews/ https://docs.gitlab.com/user/project/merge_requests/approvals/rules/ https://qiita.com/C_HERO/items/c5cfbdbb269efb72fb8f https://bake0937.hatenablog.com/entry/2019/10/24/145241   ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Git & GitLab 入門 (9) ~Git マスターへの道~「コードレビューの進め方」 first appeared on SIOS Tech. Lab .
はじめに 前回の記事 では、単一のジョブをステップごとに実行するシンプルなパイプラインを題材に、GitLab CI/CDの基本的な仕組みを確認しました。しかし、実際の開発現場では、アプリケーションのビルド、テスト、デプロイなど複数の処理を組み合わせ、段階的に実行することが求められます。今回はその実践編として、複数のジョブをつなぎ合わせてステージごとに実行するマルチステージパイプラインの設計方法を紹介します。さらに、作成したパイプラインを効率的に管理・運用するための実行管理のポイントについても解説します。 前提条件 本記事では、あらかじめ以下の環境が構築されていることを前提とします。環境の詳細な構築手順については、 環境構築編の記事 をご参照ください。 GitLab(Self-Managed版) LinuxサーバーにOmnibusパッケージでインストール済み Community Edition(無償版)を利用 自己署名証明書を利用し、内部DNSによる名前解決が可能 Gitlab Runner OpenShiftクラスター上にデプロイ済み Kubernetes Executorを利用し、CI/CDジョブをPodとして実行可能 レジストリ認証情報をリンクしたServiceAccountを利用可能 ex. oc secrets link gitlab-runner-sa gitlab-registry-secret –for=pull OpenShiftクラスター 閉域環境(インターネット非接続)に構築 踏み台サーバーからocコマンドで操作可能 GitLab Container Registry コンテナイメージのpush / pullに利用 自己署名証明書のため、CA証明書をRunner / ノードに登録済み 踏み台サーバー 内部DNSサーバー、NFSサーバーを兼任 外部インターネットへの接続が可能 ocコマンドでOpenShiftを操作可能 podmanでコンテナイメージの push / pull が可能 これらの環境を利用してGitLab CI/CDパイプラインを実行し、ビルドからデプロイまでの流れを検証できます。 また、本記事では以下のようにテスト用のプロジェクトとブランチを用意して検証します。 テスト用プロジェクトの作成 任意の名称で新規作成(例: test-project)。このプロジェクトでジョブを検証します。 テスト用ブランチの作成 本記事ではmulti-stage-testブランチを作成して使用していますが、mainなど任意のブランチでも検証可能です。 マルチステージパイプラインの作成方法 ここでは、実際にGitLab上でマルチステージパイプラインを作成し、アプリケーションを OpenShiftクラスターにデプロイする一連の流れを確認します。今回のサンプルでは、「イメージのビルド」と「Kubernetes へのデプロイ」をそれぞれ独立したステージとして定義します。 ディレクトリ構成 まず、プロジェクトのディレクトリ構成は以下のとおりです。 ソースコードや設定ファイルに加え、CI/CDの定義ファイル(.gitlab-ci.yml)とKubernetes のマニフェスト(deploy.yaml)を配置しています。 . ├── .gitlab-ci.yml ├── Dockerfile ├── README.md ├── deploy.yaml ├── nginx.conf └── web     └── index.html 各ファイルの役割 ファイル名 概要 Dockerfile ベースとなるNginxイメージにアプリの静的ファイルとNginx設定を組み込み、ポート8080で待ち受けるコンテナを作成します。 nginx.conf ヘルスチェック用の/healthzエンドポイントとトップページでindex.htmlを返す設定、使用ポートを定義しています。 web/index.html デプロイ成功を確認するための簡単なHTMLページです。 deploy.yaml OpenShift上にDeploymentを作成するためのマニフェストです。後述のジョブで${IMAGE}:${TAG}の部分を置換し、実際にビルドしたコンテナイメージを指定します。 .gitlab-ci.yml CI/CD パイプラインの定義です。buildステージでイメージのビルドとプッシュを行い、deployステージでKubernetesへ適用します。 Dockerfile このアプリケーションでは、ベースイメージに nginx:1.29-alpine を利用しています。閉域環境のため、本記事では事前にこのイメージをGitLab Container Registryにpushし、そのレジストリを参照する形で FROM を指定しています。 FROM registry.gitlab.local.example.com/root/test-project/nginx:1.29-alpine # アプリ静的ファイル COPY web/ /usr/share/nginx/html/ # Nginx 設定(ポート8080で待受) COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 8080 CMD ["nginx", "-g", "daemon off;"] このDockerfileでは、web/index.htmlを/usr/share/nginx/html/に配置し、Nginxをポート8080で起動することで、ブラウザやcurlからアクセスするとindex.htmlが返されるシンプルな構成になっています。 nginx.conf このnginx.confはポート8080で待ち受け、/healthzに200を返すヘルスチェック用エンドポイントを定義しています。 ドキュメントルート/usr/share/nginx/htmlに配置したindex.htmlをトップページとして返し、存在しないパスもindex.htmlにフォールバックするシンプルな設定です。 server {   listen 8080 default_server;   server_name _;   # ヘルスチェック用(200/ok)   location = /healthz {     add_header Content-Type text/plain;     return 200 "ok\n";   }   root  /usr/share/nginx/html;   index index.html;   location / {     try_files $uri $uri/ /index.html;   } } web/index.html デプロイの動作確認用に用意したシンプルな静的ファイルです。 Nginxのドキュメントルートに配置され、ブラウザやcurlでアクセスするとトップページとして返されます。パイプラインの挙動を検証したい場合は、このファイルを編集してpushすることで、新しいイメージがビルドされ、再デプロイ後に変更内容を確認できます。 <!doctype html> <html lang="ja">   <head><meta charset="utf-8"><title>Demo App</title></head>   <body>     <h1>Deploy Success v1.0.0.</h1>   </body> </html> deploy.yaml アプリケーションをOpenShiftクラスター上にデプロイするためのDeploymentマニフェストです。spec.template.spec.containers[0].imageには ${IMAGE}:${TAG}を指定しており、CI/CDパイプライン実行時にビルドしたイメージ名へ置換されます。 コンテナはポート8080を公開し、/healthzへのHTTP応答をreadinessProbeとして利用することで、Pod が正常に起動しているかを判定します。 apiVersion: apps/v1 kind: Deployment metadata:   name: multi-stage-demo-deploy   namespace: gitlab-runner-test spec:   replicas: 1   selector:     matchLabels:       app: multi-stage-demo   template:     metadata:       labels:         app: multi-stage-demo     spec:       serviceAccountName: gitlab-runner-sa       imagePullSecrets:         - name: gitlab-registry-secret       containers:         - name: multi-stage-demo-app           image: ${IMAGE}:${TAG}           imagePullPolicy: Always           ports:             - containerPort: 8080           readinessProbe:             httpGet:               path: /healthz               port: 8080             initialDelaySeconds: 3             periodSeconds: 5 .gitlab-ci.yml この.gitlab-ci.ymlは 2ステージ(build → deploy)構成でdindを使ってイメージをビルド→レジストリへpush →OpenShiftへのデプロイまでを自動化します。まず定義全体を示し、その後に実行フローを解説します。 stages:   - build   - deploy default:   tags: [devsecops-runner] build_image:   stage: build   image: registry.gitlab.local.example.com/root/registry-project/docker:28.3.3   services:     - name: registry.gitlab.local.example.com/root/registry-project/docker:28.3.3-dind       alias: docker       entrypoint: ["/bin/sh","-lc"]       command:         - >           cp /etc/gitlab-runner/certs/gitlab.local.example.com.crt /usr/local/share/ca-certificates/ca.crt &&           update-ca-certificates ;           exec dockerd-entrypoint.sh           --tls=false           --host=unix:///var/run/docker.sock           --host=tcp://0.0.0.0:2375   variables:     DOCKER_HOST: tcp://docker:2375     DOCKER_TLS_CERTDIR: ""   before_script:     - cp /etc/gitlab-runner/certs/gitlab.local.example.com.crt /usr/local/share/ca-certificates/ca.crt && update-ca-certificates     - docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"     # IMAGE_VERSION が定義されていればそれを利用、なければ CI_COMMIT_SHORT_SHA     - export IMAGE_TAG="${IMAGE_VERSION:-$CI_COMMIT_SHORT_SHA}"     - export IMAGE_FULL="${CI_REGISTRY_IMAGE}/demo:${IMAGE_TAG}"   script:     - docker build -t "$IMAGE_FULL" .     - docker push "$IMAGE_FULL"     - printf "IMAGE_FULL=%s\n" "$IMAGE_FULL" > build.env   artifacts:     reports:       dotenv: build.env deploy_manifest:   stage: deploy   image: registry.gitlab.local.example.com/root/test-project/oc:4.18   needs:     - job: build_image       artifacts: true   variables:     K8S_NAMESPACE: gitlab-runner-test   before_script:     - oc whoami   script:     - |       : "${IMAGE_FULL:?IMAGE_FULL missing from build.env}"       # IMAGE と TAG を分離       IMAGE="${IMAGE_FULL%:*}"; TAG="${IMAGE_FULL##*:}"       # ${IMAGE}:${TAG} を合成       sed "s|\${IMAGE}:\${TAG}|${IMAGE_FULL}|g" deploy.yaml > deploy.rendered.yaml       echo "---- rendered ----"       cat deploy.rendered.yaml       oc apply -f deploy.rendered.yaml ※IMAGE_VERSIONが未定義の場合はCI_COMMIT_SHORT_SHAがタグに使われます。この場合、短いコミットIDごとに新しいイメージが作成されるため、レジストリの容量管理や不要イメージの削除に注意してください。レジストリの容量管理については GitLab Container Registry: 応用編(可視性設定とガベージコレクション) をご参照ください。 補足: 閉域環境向けの準備 ベースイメージ登録 通常はDocker Hubから直接nginx:1.29-alpineを取得できますが、インターネット接続ができない環境では以下のように 事前にレジストリにpushしておきます。 $ podman login registry.gitlab.local.example.com $ podman pull docker.io/library/nginx:1.29.1-alpine $ podman tag docker.io/library/nginx:1.29.1-alpine \ registry.gitlab.local.example.com/root/test-project/nginx:1.29.1-alpine $ podman push \ registry.gitlab.local.example.com/root/test-project/nginx:1.29.1-alpine ※Docker 利用環境ではpodman → dockerに置き換えてください。 ocコマンド用イメージ登録 通常はregistry.redhat.ioから直接ose-cli-rhel9:v4.18を取得できますが、インターネット接続ができない環境では以下のように 事前にレジストリにpushしておきます。 $ podman login registry.redhat.io $ podman pull registry.redhat.io/openshift4/ose-cli-rhel9:v4.18 $ podman login registry.gitlab.local.example.com $ podman tag registry.redhat.io/openshift4/ose-cli-rhel9:v4.18 \   registry.gitlab.local.example.com/root/test-project/oc:4.18 $ podman push registry.gitlab.local.example.com/root/test-project/oc:4.18 ※Docker利用環境ではpodman → dockerに置き換えてください。 ※デプロイ先のコンテナ基盤がKubernetesである場合はoc → kubectlに置き換えてください。OpenShiftの場合もkubectlコマンドで基本的な操作は可能ですが、ocにはOpenShift特有の拡張機能が含まれています。 パイプラインの仕組み(実行フロー解説) パイプラインは 2ステージ構成 になっています。 1. build ステージ(イメージのビルドとプッシュ) ジョブbuild_imageが実行されます。 Docker-in-Docker(dind)環境を利用してdocker buildを実行し、GitLab Container Registryにイメージをpushします。 pushしたイメージのフルパス(IMAGE_FULL)をbuild.envとしてアーティファクトに保存します。 → この情報が次のステージに引き渡されます。 2. deploy ステージ(マニフェストの適用) ジョブdeploy_manifestが実行されます。 先ほど保存したIMAGE_FULLを利用し、deploy.yaml内の${IMAGE}:${TAG}を実際のイメージ名に置換します。 oc apply -f deploy.rendered.yamlを実行してOpenShiftクラスターにDeploymentを作成します。 Podが立ち上がり、readiness probeによって/healthzが成功すればデプロイ完了です。 パイプラインの実行 作成したパイプラインを実際に動かしてみましょう。 まず、検証用プロジェクトに移動し、管理画面からCI/CD変数を設定します。[変数を追加]をクリックし、キーにIMAGE_VERSION、値にv1.0.0を入力します。なお、mainブランチなどの保護ブランチ以外で試す場合は、保存時に[変数の保護]のチェックを外してください。チェックが付いたままだと、featureブランチなど非保護ブランチでパイプライン実行時に環境変数を参照できなくなります。 次に、ソースコードをリポジトリにpushします。GitLabのプロジェクト画面から検証対象のブランチに切り替え、[編集] → [Web IDE]を選択して内部エディタを開きます。前述のファイル(.gitlab-ci.ymlやdeploy.yamlなど)を作成し、右側の[Source Control]アイコンからコミットメッセージを入力して[Commit and push]をクリックすれば、コードがブランチにpushされます。 ソースコードがpushされると、自動的にパイプラインが起動します。プロジェクトのサイドバーから [ビルド] > [パイプライン] を選択すると、実行中のパイプラインを確認できます。さらに、パイプラインIDをクリックすれば、各ジョブの実行状況やログを確認できます。 プロジェクトのサイドバーから[デプロイ] > [コンテナレジストリ]を選択すると、プロジェクト内レジストリのリポジトリ一覧が表示されます。今回ビルドしたイメージはdemoリポジトリに格納されています。 この例では、build_imageジョブでイメージをビルドしてレジストリにpushし、その結果生成されたIMAGE_FULL変数がbuild.envに保存されます。続くdeploy_manifestジョブでは、その変数を利用してマニフェスト内のプレースホルダーを置換し、oc applyコマンドでOpenShiftにデプロイできていることが確認できます。 デプロイ成功の確認 デプロイジョブの成功を確認した後、アプリのPodがRunning状態で起動していることを確認します。 $ oc get pod -n gitlab-runner-test -l app=multi-stage-demo NAME                                       READY   STATUS    RESTARTS   AGE multi-stage-demo-deploy-6874887f67-w7br8   1/1     Running   0          69s Podが正常に起動すると、Pod内のNginxでweb/index.htmlが配信されます。以下のようにアクセスして動作を確認できます。 # Pod名を取得 $ POD=$(oc -n gitlab-runner-test get pod -l app=multi-stage-demo -o jsonpath='{.items[0].metadata.name}') # ローカル18080 → Pod 8080 へ転送 $ oc -n gitlab-runner-test port-forward pod/$POD 18080:8080 # ブラウザからlocalhost:18080にアクセスし、index.htmlの内容が表示されることを確認 Deploy Success v1.0.0. このメッセージが表示されれば、マルチステージパイプラインを通じて「ビルド→レジストリへのpush→OpenShiftへのデプロイ」が自動化できていることを確認できます。 これで、コードのpushをトリガーにビルドからデプロイまでが一連の流れで実行されることを、実際のパイプライン実行を通して確かめられます。 なお、すべての処理を1つのステージにまとめることも可能ですが、ビルドとデプロイを分けることで以下のメリットがあります。 責務の分離: ビルドとデプロイを明確に分けることで、どの段階で失敗したのかを特定しやすくなります。 効率的なリトライ: ビルドが成功していれば、デプロイだけを再実行できるため、再試行の効率が向上します。 並列・拡張性の確保: 将来的にテストステージを挟んだり、複数環境へのデプロイを分岐させたりといった拡張が容易になります。 このように、ステージを適切に分割することで、パイプラインの信頼性と運用効率を高めることができます。 まとめ 本記事では、複数のステージを組み合わせたパイプラインの設定方法を解説しました。 サンプルアプリケーションを題材に、ビルドとデプロイを別ステージに分けたマルチステージパイプラインを構築し、実際にコードのpushをトリガーにして一連の処理が自動で実行される流れを確認しました。 単一ステージに処理をまとめることも可能ですが、ステージを分割することで「どこで失敗したのかが明確になる」「ビルドが成功していればデプロイのみを再実行できる」といった運用上のメリットが得られることも示しました。これにより、パイプラインの信頼性や管理性が大きく向上します。 次回は、さらに複雑化したパイプライン定義ファイルを include / extends 機能 を活用して整理する方法を解説します。複数のジョブやステージが増えて管理が煩雑になったときに役立つ実践的なテクニックを取り上げ、よりスケーラブルなCI/CD運用に向けた一歩を紹介します。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GitLab CI/CD 実践[マルチステージ編]:複数ジョブをつなぐパイプライン設計 first appeared on SIOS Tech. Lab .
初めに 前回のブログでは「 Claude Code革命!3フェーズ開発で効率的な開発:計画→実装→検証術 」について紹介しましたが、今回は実際にClaude Codeで仕様書ベース開発運用する際に「これは絶対に押さえておかないとハマる」というポイントについてお話しします。 実際に検証していて失敗したことや、気をつけないと時間を無駄にしてしまうポイントを中心にまとめました。 1. ドメイン知識の明示的なドキュメント化の重要性 この方法で検証していて失敗したこともあります。例えば、 プロジェクトの基礎的な知識(ドメイン知識)は、明示的にドキュメント化しない限りAIは理解してくれません 。 実際あった問題として、ネイティブアプリとWebアプリのバックエンドリンクの構成があります。ローカル環境ではSWCでエミュレートしていて、「/api」に自動でプロキシされる仕組みでしたが、AIはそれを知らずにバックエンドURLを直接埋め込もうとして、 APIパスの参照エラーが多発 しました。 これは「プロジェクトコア」のようなドキュメントを作成して共通知識を提供することで解決しました。 AIは書かれていないことは認識できず、セッション内で与えていない情報は理解しません 。 プロジェクトコアドキュメントの具体例 プロジェクトコアには、実際にデプロイする環境のプロキシの設計やプロジェクトの根幹に関わる部分をちゃんと定義しておくべきです。 今回私が作った環境では、Azure Static Web Appsをバックエンドリンクで構築していて、スラッシュAPIというのが自動でプレフィックスで飛んでいくという仕組みがあります。そのため、プレフィックス「/api」でアクセスするということを明記したり、プロジェクト構成みたいなのを書いておいて、そういった情報をAIに与えています。 当たり前のように思える環境設定や開発ルールも、AIにとっては「初めて聞く話」です。人間の開発者なら暗黙知として共有されている部分も、AIには明示的に教える必要があります。 2. AIがドキュメントを無視する問題への対処 計画フェーズでは「計画.md」などのファイルに指示を書いていますが、 AIは時々ドキュメントを無視することがあります 。人間と同じで、指示や定義を見ないこともあるんです。読んだうえで無視することもあるので、 絶対守ってほしいことはドキュメント化し、プロンプトでも注意するのが重要 です。 これは本当にイライラするポイントで、せっかく時間をかけて詳細な仕様書を作っても「それ、無視しちゃダメでしょ!」ということが頻繁に起こります。 無視されやすいパターンと対策 プロンプトに期待してない情報は割と無視しますね。そのパターン的なミスの傾向みたいなのがあったら、マークダウンファイルに情報反映させるというやり取りをよくやっています。 仕様書を書いてもらうフェーズと開発フェーズで、仕様書を書いている最中にコードの編集をしたりしていたんですよ。そこで、仕様要件フェーズと開発フェーズの三つに分けますという指示をつけて、計画フェーズではファイルの編集は加えないでくださいという指示を追加しています。開発フェーズは、作成した仕様のドキュメントを読んで、それに対してステップバイステップで開発を進めてくださいということを、マークダウンファイルで定義しています。 対策としては: 最重要な制約事項はドキュメントに書く さらにプロンプトでも再度強調する 「この部分だけは絶対に守ってください」という明示的な指示を含める 二重、三重の安全策を講じることが大切です。 3. 仕様書の適切な分量バランス 仕様書の分量については、 長すぎるとAIが無視する確率が上がります 。「無視しました」という返答が返ってくることもあり、それを見たときは正直 ブチ切れました (笑)。 短ければ理想的ですが、短すぎると時間がかかるので、AIが無視する可能性も考慮しながら適切な分量を見極める必要があります。 どの程度の分量が適切かはプロジェクト依存 です。ドメイン知識が多ければ、その分仕様書に書く情報は少なくて済みます。 具体的な分量の目安 私の環境で言うと、 エンドポイント2つと、それに対応する画面1つ という感じだと、体感的に上手くいったという印象です。これは本当にプロジェクトによるというか、プロジェクトコアによる感じの内容というか、元のコンテキスト量が影響してきます。 この辺りは経験則になってしまいますが、AIが「長いから読みたくない」と判断するラインを見極めることが重要です。人間と同じで、あまりにも長い仕様書は最後まで読んでもらえません。 4. 新機能開発とバグレポートの使い分け 私は新機能開発とバグレポートをこの二つの方法で分けて対応しています。 新機能開発 : 必ず3フェーズ(計画→実装→検証)を踏む 小さな変更 : でもドキュメント→実装という段階を踏む 本当に小さな変更 : 人間がやったほうが早い 人間がやったほうが早い変更の見極め プロジェクト構成的な部分の変更ですね。コード規約みたいなところで、ちょっと上の階層にファイルを移し替えたりとか、ファイル構成で、ちょっとこっちに移行させるほうがいいよねみたいな話とか、ファイルの単純な移動は、VS Codeの補完機能が優秀なので、そういった作業とか、あとは関数名が気に入らないので、関数名のリネームするという作業ですよね。 人間が手を動かして作業して、セッションが継続しているのであれば、ちゃんと変更を加えましたよということをAI側に伝えてあげる必要はあります。そうじゃないと、人間が編集したファイルとAIが編集しようとしているファイルでコンフリクト起きてしまうので、再度ファイルを作ろうとするんですよ。なので、そういったところで調整を取っていく必要はあるかなと思います。 この使い分けが重要で、何でもかんでもAIに頼めばいいというものではありません。1行2行の修正をAIに説明するより、自分でやってしまったほうが早いケースも多々あります。 プロセスを踏んでも意図したものができない場合は、やはり人間のドキュメントが不十分だということです。AIは書かれていないことを自由に判断するので、ドキュメントの質を高める必要があります。 5. 中規模開発での威力と現在の限界 現在はプライベートリポジトリで検証しているため、 大規模プロジェクトへの導入経験はまだありません が、中規模程度の開発であれば強力なツールになると思います。特に プロトタイピングでは大きな力を発揮しそう です。 個人的に学びになった点としては、仕様書作成が苦手な私でも、AIが返してきた仕様書を見て「分かりやすい」と感じることがあります。 学習的な側面でも価値がある と思います。 完璧な仕様書があれば理想的ですが、そういうものは実際には存在せず、どこかに不備があるものです。だからこそ、機能ごとに段階的に仕様書を作っていくアプローチが有効だと思います。 6. よくある失敗パターンとその対策 型定義の不整合問題 本当によくあるのが、フロントエンドとバックエンドで型定義が共通化されてなくて、バックエンドにアクセスできない、APIアクセスは成功してるんだけども、画面に表示がされないということがありました。 対策としては、 OpenAPI スペックを作らせて、そのOpenAPIスペックから型定義を作成する 。 そして、フロントエンドはその型定義を絶対に使用するようにする、という感じで回避しています。 デプロイ環境の差異による問題 デプロイ環境の差異みたいなところで問題が起きることもあります。今回Azure のバックエンドリンクで、スラッシュAPIのコンテンツを自動でプロキシしてくれるんですけど、そういったことを伝えてなくて、なんか別のエンドポイントベースURLみたいなのを付けて、そこにアクセスするようにしましょうみたいな提案をされました。 バックエンドが現状、localhost の3001番で動いてるので、ローカル3001番に直接アクセスするように変えちゃいましょう、みたいなことを言われることがあります。これはプロジェクトコアドキュメントでしっかりと環境構成を明記しておくことで回避できます。 まとめ Claude Codeは確実に開発効率を向上させてくれるツールですが、これらのポイントを押さえておかないと、余計な時間を費やしてしまいます。特にドメイン知識の明示化と仕様書の分量バランスは、成功の鍵となる重要な要素です。 まぁ暴走するのは人間と一緒って感じですね。人間よりも暴走しがちですが、言いやすい相手ですよねw 皆さんも、ぜひこれらのポイントを参考にClaude Codeを活用してみてください! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Claude Code仕様書ベースでハマる6つの落とし穴!失敗回避の備忘録 first appeared on SIOS Tech. Lab .
はじめに ども!AIとの開発をがっつり初めて2か月で毎日Claude Codeとお話している龍ちゃんです。本業とは別で開発を進めているのですが、今まで1週間で頑張って1件の検証開発しかできなかったんですが、2日で検証したいことをサンプル付きで作成できるので最高ですね。これまでの開発経験で技術記事も数多く書いてきましたが、今回は特に効果を実感したAIとの協働について書いていこうと思います。 ちなみに「 Claude Code革命!3フェーズ開発で効率的な開発:計画→実装→検証術 」Claudeに実装させるフェーズを分割して開発をする手法を紹介しています。今回はそこの計画書執筆フェーズのお話です。 皆さんは、仕様書を書くことに対してどんな印象をお持ちですか?正直に言うと、入社して4年ですが、いまだに仕様書を書くという作業に対して苦手意識があります。仕様書を書くのが好きというエンジニアに個人的にあったことがありません。「完璧な仕様書を書くぞ~!」というモチベーションを持ったとしてもアレルギーといいますか、「完璧な仕様書」というものを書くのはハードルが高いですね。 背景・課題説明 仕様書が必要な理由 その一方でAIと共同で開発を進めていくためには「仕様書」があったほうが良いです。コード規約やLinter/Formatterが整備されているプロジェクトであれば、コードが仕様書の機能を持つことがありますが、プロジェクト初期の段階であれば仕様書を読み込ませて開発をするほうが圧倒的に開発効率が良いです。Vibe Codingで単純なプロンプトで100-300文字のプロンプトで作成するのは不可能です。 機能開発中によくあるのが、検討不足や考慮不足によって開発中にSlackでの相談やミーティングを挟んだり、手が止まって考え込んだりすることではないでしょうか。コードの書き方で悩むのは問題ありませんが、事前に決めておくべき情報が未確定なために発生する手戻りは避けたいものです。 仕様書アレルギーの原因分析 仕様書を書くことに対してアレルギーがある原因を個人的に分析をしてみると、仕様書を読み込んだ経験があまりないことがあります。より良い仕様書を書くためには、良い仕様書のお手本が必要です。じゃあそのより良い仕様書ってどこにあるのって話ですよね。完璧超人が執筆した仕様書があれば理想ですね。(レビューなしで完璧な仕様書を書ける人がいるならあってみたい…) そんな可能性はないので、プロジェクトではレビューを重ねてより良い仕様書を目指していくのが、普通の流れになると思います。ということを踏まえて、皆さんの周りに仕様書ってありますか?仕様書を読み込む経験ってあまりないんじゃないでしょうか? 技術選択の理由:なぜAIと協力するのか そこで提案です!小さく仕様書を書いてみませんか?いきなり完璧な仕様書なんて不可能ですし、レビューアーを確保するのも大変ですよね。そこでAIを活用します。 AIを活用した仕様書作成フロー AIに仕様書を書いてもらい、それをレビューする。私たちは意図を伝えて仕様書を作成してもらい、人間がレビュアーとして参加し、AIと協力する形で仕様書を作成していくフローです。 実装詳細 CLAUDE.mdの具体的な記述例 前提としてAIはコードを書きたがります。設計段階では、目的や禁止事項をCLAUDE.mdに定義しています。 実際に私が使っているCLAUDE.mdの中身を見てみましょう: # 設計フェーズのルール ## 目的 AIに人間の意図を適切に伝えて効率的な開発を行う ## 設計フェーズで行うこと - 型定義の設計 - データベース構造の設計 - API 仕様の定義 - アーキテクチャ設計 - 機能要件の明確化 - 非機能要件の定義 ## 設計フェーズで禁止すること - ❌ 実装コードの記述禁止 - ❌ 具体的なロジックの記述禁止 - ❌ ライブラリの選定禁止 - ❌ 具体的な関数実装禁止 ## レビューポイント - 要件の抜け漏れはないか - 設計の整合性は取れているか - スケーラビリティは考慮されているか - セキュリティ要件は満たしているか このように明確にルールを定義することで、AIが設計に集中してくれます。 仕様書作成の具体的な手順 要件の整理 :何を作りたいかを箇条書きで整理 AIとの対話 :CLAUDE.mdのルールに基づいて仕様書を依頼 レビューと修正 :作成された仕様書を人間の視点でレビュー 反復改善 :不足部分や曖昧な部分を繰り返し修正 仕様書のテンプレート例 # 機能仕様書:[機能名] ## 概要 [機能の目的と概要] ## 機能要件 - [要件1] - [要件2] - [要件3] ## 非機能要件 - パフォーマンス要件 - セキュリティ要件 - 可用性要件 ## データ構造 [必要なデータ構造の定義] ## API仕様 [エンドポイントの定義] ## 制約事項 [技術的制約や業務的制約] 動作確認・検証 実際の効果を検証してみた 従来の開発フローと比較してみました: 項目 Before(従来の人間だけでの開発) After(AIと協力した仕様書駆動) 仕様検討 開発中に随時(割り込み多数) 事前に1-2時間 開発時間 1週間 2日 手戻り回数 平均3-4回 平均1回 特に 開発時間の短縮 と手戻り回数が顕著に現れました! まだまだ検証中の段階なので、小規模な検証なことは留意しておいてください。 AIと協力する副次的効果 仕様書を書くことの効果は、開発前に作成したい機能の整理ができることです。 AIと一緒に仕様書を作成する副次的効果として、AIが作った仕様書をレビューする経験が得られます。仕様書の段階で、こちらの意図が適切に伝わっていなければレビューして修正します。これにより開発前に検討不足や考慮不足を抑えることができます。完璧なレビューは不可能ですが、それは経験を重ねることで改善していくものですね。 成功事例:実際のプロジェクトから 最近の検証プロジェクトでは、以下のような成果が得られました: ユーザー認証システム :仕様書作成1時間 → 実装1.5日 データ分析ダッシュボード :仕様書作成2時間 → 実装2日 API統合機能 :仕様書作成1.5時間 → 実装1日 いずれも従来の3-4倍の速度で完成させることができました。 課題と今後の展望 現在の制限事項と改善案 まだ完璧ではありません。AIの理解度に依存する部分があり、複雑な業務ロジックは人間の補完が必要です。また、チーム内での標準化やレビュー観点の統一も課題ですね。 短期的には仕様書テンプレートの標準化とAIプロンプトの最適化を進めています。 いっそのこと 、将来的にはAIが要件定義から運用まで一貫してサポートしてくれるような環境を構築したいですね。 まとめ 今回は、AIと協力した仕様書作成フローについて詳しく見てきました。 「仕様書アレルギー」を克服し、効率的な開発を実現する方法として: 小さく始める仕様書作成 :完璧を目指さず、まずは基本的な要件から AIとの協働レビュープロセス :AIに作成してもらい、人間がレビューする 設計フェーズでの明確なルール設定 :CLAUDE.mdでの禁止事項と目的の明示 段階的な開発プロセス :設計→開発→ブラッシュアップの3段階 これらの知識を活かして、皆さんもAIと一緒に仕様書作成にチャレンジしてみてください!最初は慣れないかもしれませんが、開発効率の向上を実感できると思います。 実際のプロジェクトでどちらを選ぶべきか、迷うところですよね。でも、一度この方法を試してみれば、その効果を実感していただけるはずです。 次回は、実際の開発フェーズでのAI活用術について書く予定です。お楽しみに! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AI協働で仕様書アレルギー克服!開発時間を1週間→2日に短縮する実践法 first appeared on SIOS Tech. Lab .
初めに AIと一緒に開発をするようになってから、フロントエンドとバックエンド両方を爆速で開発することができるようになって、検証が爆速で進むようになった龍ちゃんです。 AIが混乱しないようにプロジェクト自体を整備する方法についてお話しします。AIと一緒に開発を進める開発手法に関しては、以下の記事で解説をしています。 Claude Code革命!3フェーズ開発で効率的な開発:計画→実装→検証術 AI協働で仕様書アレルギー克服!開発時間を1週間→2日に短縮する実践法 Claude Code仕様書ベースでハマる6つの落とし穴!失敗回避の備忘録 AIに触らせないファイルを作ることで過剰な生成を禁止したり、自動生成パイプラインを構築して人為的なミスを防ぐなど、プロジェクト構造レベルでの対策が重要です。 実際に発生した問題事例:型定義の齟齬 まず、実際に私が遭遇した問題から紹介します。 環境構成 フロントエンド : Next.js バックエンド : Nest.js 発生した問題 フロントエンドとバックエンドで 型定義の齟齬が発生 していました。バックエンドで定義した型をフロントエンド側で再定義して、変更が反映されておらず 参照エラーが発生 する状況でした。 この問題の根本原因は、AIが各環境で独立して型定義を生成してしまい、バックエンドの型定義変更がフロントエンドに反映されないことでした。人間の開発者なら「バックエンドで型が変わったから、フロントエンドも更新しなきゃ」と気づけますが、AIは各ファイルを独立して見てしまいます。 解決策:自動生成パイプラインの構築 この問題を解決するため、以下の自動生成パイプラインを構築しました: 1. OpenAPI Spec自動生成 Nest.jsからOpenAPI Spec を自動生成する仕組みを導入しました。これにより、バックエンドのAPI仕様が変更されると、自動的にスペックファイルも更新されます。 公式で提供されているので、コードレベルでOpenAPI Specを生成することができます。 2. フロントエンド側の自動生成 Orval を使用して、Next.jsに SWR + Axiosのリクエスト・型定義を自動生成 する仕組み を構築しました。OpenAPI Specから自動的にフロントエンド用のコードが生成されるため、バックエンドとフロントエンドの型定義が必ず一致します。 このシステムでは、SWRとAxiosを使ってAPIリクエストを行っており、データフェッチャーやカスタムフックが自動で提供されるため、非常に便利な仕組みになっています。 3. Claude への通知 最も重要なのは、 Claudeに変更不可と明確に通知 することです。自動生成されるファイルには、コメントやCLAUDE.mdファイルで「このファイルは自動生成されるため変更禁止」と明記し、Claudeが勝手に編集しないように徹底しました。 自動生成するためのコマンドと、どのファイルが変更不可なのかを明確に指定する必要があります。 プロジェクト構成の最適化 AIが迷わないよう、プロジェクト構成も工夫しています: / ├── CLAUDE.md # プロジェクト全体のガイドライン ├── docs/ # 計画・設計フェーズ │ ├── CLAUDE.md # 計画フェーズ専用ルール │ └── api/ # OpenAPI Spec └── application/ # 実装フェーズ ├── backend/ │ └── CLAUDE.md # バックエンド実装ルール └── frontend/ └── CLAUDE.md # フロントエンド実装ルール 各フェーズ用のCLAUDE.mdファイル配置 各ディレクトリに CLAUDE.md ファイルを配置し、そのディレクトリで作業する際の固有ルールを記載しています。例えば: application/frontend/CLAUDE.md : 自動生成ファイルの変更禁止、使用可能なライブラリの制限など application/backend/CLAUDE.md : API仕様変更時の手順、データベーススキーマ変更の注意点など docs/CLAUDE.md : ドキュメント作成時のフォーマット、必須項目など AIに触らせないファイルの明確化 過剰な生成を防ぐファイル管理戦略 として、以下のような対策を実施しています: 自動生成ファイルには必ず「DO NOT EDIT – Auto Generated」のコメントを付与 パッケージファイル(package.json等)の変更制限 設定ファイル類の変更制限 各CLAUDE.mdで変更不可ファイルのリスト明記 ただし、ここまで書いていたとしても、AIは結構無視することがあります。実際に、自動生成して編集禁止にしている設定ファイルを編集しに行って動くようにしたりとか、ダイナミックな手法をやってきたりするので、いくら書いていたとしても無視をする可能性があることは念頭においた方が良いと思います。 大規模プロジェクトでの課題と対策 フロントエンド・バックエンド開発者が異なる場合の同期 大規模プロジェクトでは、フロントエンドの開発者とバックエンドの開発者が違う場合に、いかに同期を取っていくかが重要な課題となります。 開発順序の調整 現在のシステムだと、バックエンドの開発をしてからフロントエンドの開発をするという順番になるため、この順序をどう合わせていくかが必要です。 同時開発のためのアプローチ 同時に開発を進めていくためには、以下のような手法が有効です: モック を使用した並行開発 型定義を先に作成 してOpenAPI仕様書だけを先に渡す Orvalで生成 したフロントエンドコードを使ってバックエンドと協調開発 このアプローチにより、フロントエンドとバックエンドの同時進行での開発が可能になります。 この手法の効果 自動生成パイプラインとプロジェクト整備により: 型定義の齟齬が完全に解消 AIが余計なファイルを触ることによる意図しない変更の防止 人間が後から「なぜこの変更をしたのか分からない」状況の回避 コードレビュー時の確認ポイントの削減 バックエンドを定義したら、そこからフロントエンドを並行して開発できますし、バックエンドとフロントエンドで型定義が異なるという問題がほとんどなくなりました。もちろん、バックエンドの定義自体が間違っていれば動作しませんが、フロントエンド側での型の不一致はかなり抑制できたと感じています。 これまでは自動生成の仕組みを使わずに開発していたため、コードが若干汚くなる傾向があり、フロント側で型定義の再定義が行われて連携が取れていないという問題や、型ファイルが増えていくという課題がありました。この仕組みにより、プロジェクト全体がより整理されたファイル構造になり、可読性が向上したと実感しています。 特に型定義の自動同期により、フロントエンドとバックエンドの開発を並行して進められるようになりました。 まとめ 正直、これは導入してみて一番良かったなと思える点ですね。ルールをあまり追加させずにAIに生成させるコードが圧倒的にきれいになりました。 ルールをガッチガチにしてコードをきれいにするというのも今後の検証として取り込んでいきます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AIと爆速開発!Next.js×Nest.js型定義同期の自動生成パイプライン構築術 first appeared on SIOS Tech. Lab .
はじめに ども!毎日毎日Claude Codeとお話して日課となっている龍ちゃんです。直近ではClaude Codeに関するお話を執筆していこうと思います。 最初は「これは革命的だ!」と思って飛び込んだClaude Codeでしたが、実際に使い込んでいくと、思った以上に奥が深くて、適当にプロンプトを投げているだけでは思うような成果が出ないことがわかってきました。 今回は、私が実際にClaude Codeを使い倒してきた中で見つけた、「Vibe Coding」から卒業するための実践的なTipsをまとめてみました。同じような課題を感じている方の参考になれば幸いです。 今回紹介する問題点・課題を解消するために検証した内容をまとめています。こちらも併せて読んでもらえると楽しいかもしれません。 Claude Code革命!3フェーズ開発で効率的な開発:計画→実装→検証術 AI協働で仕様書アレルギー克服!開発時間を1週間→2日に短縮する実践法 Claude Code仕様書ベースでハマる6つの落とし穴!失敗回避の備忘録 AIと爆速開発!Next.js×Nest.js型定義同期の自動生成パイプライン構築術 Vibe Codingの落とし穴 雑なプロンプトでは成果が出ない現実 「とりあえずやってみる」テンション感で始めがちなClaude Codeですが、これが最初の落とし穴でした。数行のプロンプトで開発に十分な意図が伝わっていないんですよね。 例えば、「4輪駆動で走る車を作って」と言っているようなもので、具体的な仕様や制約が伝わっていない状況になってしまいます。これでは、Claude Codeも困ってしまいますよね。 ドキュメント化が成功の鍵 解決策として見えてきたのが、 Claude Codingの前にドキュメント化しておく ことの重要性です。 具体的には、以下のような準備が必要になります: 既存のコードがある場合 /initはとりあえず打つ! 既存コードを読み取らせてドキュメントを作っておく Azureなどの構成などもある場合はドキュメント化しておく コーディングの前にタスク専用の設計書を作成する 設計書→Codingの流れを徹底する まずは設計書を作成して内容が人間の意図しているものと一致しているか確認しておく Claude Codeはコードを書きたがるから、設計書のみに中止させる Vibe Debugがつらすぎる問題 よくあるデバッグ地獄 Vibe Codingしていて、動かないことがそれなりにあります。エラー内容を入力して修正してもらうこともできますが、治らない場合は大胆な戦略をとることがあるんです。 例えば: フロントの改修をしているのに、バックエンドのコードを直そうとする 新規ファイルを作成してバージョンアップしたりする Bicepファイルを編集するときに、いったんリソースグループを削除してしまう 効果的なデバッグ戦略 エラー→Vibe Codingがつらくなる場合は、該当箇所のコードを読み込んで、 人間の観点+エラーメッセージ+調査指示 で取り組むことで精度が上がることがわかりました。 セッション管理の重要性 メモリ制限を理解する ドキュメント化していない知識は伝わっていないと思ったほうが良い です。Claude Codeはセッション型でメモリに記憶を保持していないので、セッションを閉じると記憶が失われると思ったほうが良いですね。 そのため、定期的なドキュメント管理とプロジェクトのコア部分の情報をドキュメント化して読み込ませておくことが重要になります。 適切なタスクサイズ 1セッション1タスク ぐらいの分量で収まるように進めることを意識しています。ドキュメント→タスクを完了させる動きをさせておくのがコツです。 セッションを閉じるとタスクの進捗が確認することができないので、セッションを閉じるときはタスクが完了したタイミングであることを意識しておきます。 巨大なタスクでなく、 2~3hで完了させる程度 でタスクのサイズを分割して進めるのが良いですね。タスクが大きすぎると、Claude Code側がフェーズを分割したりするので、フェーズ進行中はセッションを切らないようにしています。 実践的なTips集 不要ファイル・デバッグコンソールの管理 Vibe Debugの結果、不要なコードや不要なファイルが生成されていることがあります。これは、最終的にコミットやPRに含まないことが大切です。 最後に「参照していないコードを削除しておいてください。不要なファイルは削除してください」と指示することで、きれいな状態を保てます。 ディレクトリ単位で不要になったファイルは削除しておくことも重要です(過去のプロジェクトなどのファイルがある場合は削除しておきましょう)。 フロントとバックの型定義の不整合対策 バックエンドとフロントエンドでAPIの型が不整合が発生していたことがありました。 対応策: フロントのコードを作成する前にバックエンドのソースを読ませる バックエンドのAPIへアクセスする部分は自動生成させてそれを使用するように変更する(検討中) CLAUDE.mdの活用と注意点 特にセッションが長くなった場合は CLAUDE.mdを無視しがち になります。AIは昔のコンテキストほど優先度が下がるというのはよくある話ですね。 対応策: セッションが長くなった場合はいったん切る セッションの開始時には必ずCLAUDE.mdを読み込まれるので、セッション内で編集するファイルの領域を決定する バック→フロントの連続ではなく、バック作成→OpenAPI Spec→フロントという連携をする ただし、 セッション開始時からCLAUDE.mdを無視することもあります 。そういった場合は、追加のプロンプトで指示を与えることが効果的です。 具体的には: いったん処理を止めて、追加のプロンプトで再指示 「無視されているCLAUDE.mdを再度読み込んでください」といった明示的な指示 重要なルールを#コマンドで再度強調する これらの方法で、CLAUDE.mdの内容を確実に反映させることができます。 ファイルスコープの明確化 編集するファイルと触らないファイルを決定する ことが重要です。例えば: ビルド後のファイルを直接変更しようとした フロントの改修中にバックエンドのコードを変更しようとした このような問題を避けるために、事前にスコープを明確にしておきます。 プログラミングファイルによる違い Bicepファイルは難しい Bicepファイルの依存関係周りの設定が難しいことがわかりました。 回避策: 依存関係やリソースが完成した後で連携する処理はShell ScriptでAzure CLI経由で処理を行う Bicepの処理はリソースの作成のみに注力させておく GitHub Actionsも要注意 GitHub Actionsについても課題があります: 提供されているActionsを読み込んで使うことが難しい 割と大胆なActionsを書いてくる(Azure CLIとかGitHub CLIを使うような) 回避策: GitHub Actionsはプログラム言語レベルで多岐にわたらないため、Vibe Codingではなく、人力コーディングで行ったほうが良いという結論になりました。 まとめ 今回は、Claude Code使用時の「Vibe Coding」から脱却するための実践的なTipsをまとめてみました。 重要なポイントをまとめると: 事前のドキュメント化と設計書作成を徹底する セッション管理とタスクサイズの適正化 ファイル種別による特性を理解して使い分ける 型の不整合やスコープの明確化に注意する Claude Codeは非常に強力なツールですが、適切な使い方を身につけることで、その真価を発揮できるようになります。皆さんも、ぜひこれらのTipsを参考に、より効率的な開発フローを構築してみてください! LinterとFormatterの設定など、基本的な開発環境の整備も忘れずに。ここは人間と一緒ですからね。 Claude Codeでの開発、一緒に極めていきましょう! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 「適当にプロンプト投げるだけ」を卒業!Vibe Coding脱却術:【Claude Code】 first appeared on SIOS Tech. Lab .
はじめに みなさん、こんばんは。最近はClaude CodeでAIにコードを書かせる検証をしている龍ちゃんです。 先月はあまりブログを書けなくて、僕もビックリするぐらい三本しか書いていませんでした。今月は多少時間を取ってでもブログを書いていこうと思っています。 まず Vibe Codingをやってみて感じた課題感 に関してはこちらでまとめています。 AIが悪いのではなく、人間が悪い 色々検証して思ったことですが、 だいたい人間の入力が悪い というか、人間のせいなんですよね。AIの仕組みでそれを解消しようという試みもありますが、業務でAIを使うなら、人間の入力が悪いと考えて、入力の方を改善していく方が、AIの精度向上を待つよりも効果的だと思います。人間の入力をいかに変えていくかということにフォーカスを当てて検証を進めています。つまり、 Claude Codeが悪いんじゃなく、人間が悪い んです。 人間の入力をAIが理解しやすいように作っていくという前提を押さえてもらえればと思います。 従来手法の限界:ペアコーディング 最初の1〜2ヶ月間、Claude Codeを使い始めた頃は、Vibe Coding(ペアコーディング)という手法で開発していました。プロンプトとしては100から300文字ぐらいのシンプルなものを投げていました。 これだと小規模な改善や機能追加には非常に優秀でした。リファクタリングも効果的でした。しかし、大規模な開発(例えば、一つのページ全体を組むような)をしようとすると、意図したとおりのものを作ってくれないことがありました。AIが自由に解釈しすぎて、変なものを作ってくることもありました。 ペアコーディングの具体的な問題点 実際にペアコーディング手法で開発していて気づいた課題があります。雑なプロンプトを投げて作ったものだと、やっぱり意図したものが作られないということが頻繁にありました。また、ペアプログラミング的な使い方だと人間の手が入る分、作業が遅くなるという印象もありますね。 特に既存プロジェクトで、すでにコード規約やディレクトリ構成が決まっている状態なら、それを学習させてからエンドポイントを追加する作業は割と有効だと思います。しかし、 300文字程度で自分が作りたいものを説明するのは文豪でも難しいでしょう 。文章が苦手な人にとっては尚更です。 ここで思い出してほしいのが「人間が悪い」ということです。そういう意味では、「意図したものができない」と言っている人は、当然のことを言っているだけなのです。 このような背景があり、リファクタリングは得意分野だと言えます。既存コードのパフォーマンス問題を改善するといった単純な指示は分かりやすいです。しかし新機能開発となると話が違います。Claude Codeで自分の意図したとおりのものを作る手法についてお話ししようと思います。 ディレクトリ構成とファイル管理 ディレクトリ構成としては、計画・検証用のドキュメント部分と実装コード部分を分けています。 / ├── CLAUDE.md # プロジェクト全体のガイドライン ├── docs/ # 計画・設計フェーズ │ ├── CLAUDE.md # 計画フェーズ専用ルール │ ├── features/ # 新機能計画 │ ├── bugs/ # バグ調査・修正計画 │ └── research/ # 検証結果・知見 └── application/ # 実装フェーズ ├── backend/ │ └── CLAUDE.md # バックエンド実装ルール └── frontend/   └── CLAUDE.md # フロントエンド実装ルール 新手法:3フェーズ開発 最近実践している方法では、開発フェーズを「 計画 」「 実装 」「 検証 」の3つに分けています。 計画フェーズ まず「計画」フェーズでは、やりたいことの仕様書をAIに説明し、文書化してもらいます。 このフェーズではコードを一切書かせません (型定義などの簡単なものは除く)。 AIが返してきたドキュメントを確認し、自分の意図しているものかを確認します。これによってAIが自由に解釈する余地を減らします。 計画フェーズで効果を発揮するCLAUDE.mdや具体的な手法・効果については「 AI協働で仕様書アレルギー克服!開発時間を1週間→2日に短縮する実践法 」で紹介しています。 実装フェーズ 次に「実装」フェーズでは、作成した仕様書を読み込ませ、それに基づいて実装してもらいます。説明は全て計画フェーズで作った仕様書に含まれているので、それを入力として与えるだけです。 人間は監視役に徹し、AIが暴走した場合は適宜小さなプロンプトで修正します。 龍ちゃん 実装時に陥りそうな課題に関しては、「 Claude Code仕様書ベースでハマる6つの落とし穴!失敗回避の備忘録 」でも書いています。 検証フェーズ 最後の「検証」フェーズでは、仕様書の不備がどこにあったか、その結果どのような変更が加えられたかを分析します。これにより実装と仕様を照らし合わせ、根拠のない編集を防止できます。実装後は人間の手で確認作業を行います。 計画と実装を比較することで、レビューした仕様書の中で不足があった分などを特定することができます。 計画ドキュメントと実装の内容を確認して、計画と実装の差異がある点をまとめてください。 ここで不測の指摘があるってことは、仕様書や計画が不足しているということになります。ここで学びができますね。 実際の開発効果:驚異的な時間短縮 この3フェーズ手法を実践してみて、開発期間は圧倒的に短縮されました。具体的な事例をご紹介しますと: 小規模システムの例 バックエンドのエンドポイント2つ フロントエンドの画面1つ 開発時間:約30分で完了 作った画面は結構シンプルなシステムでしたが、エンドポイントの仕様としては単純なものでした。この規模なら、仕様書作成から実装まで含めて30分という短時間で完成します。 中規模システムでの限界 一方で、バックエンドのエンドポイントが4つで、フロントエンドの画面でそれら4つのエンドポイントをいい感じに使うような中規模なものを作ろうとすると、セッションが限界になったりということがありました。この辺はタスクを分割するべきだったなと考えています。 この手法の効果と学び、そして課題 現在はプライベートリポジトリで検証しているため、大規模プロジェクトへの導入経験はまだありませんが、中規模程度の開発であれば強力なツールになると思います。特に プロトタイピングでは大きな力を発揮しそう です。 バグ発生率と仕様漏れの実態 開発時の課題として、バグというよりも 仕様漏れ の方が圧倒的に多いです。仕様書を元に開発するので、仕様から漏れている内容が実装している最中やテストしている最中に気づいたりします。これは大体バグというより仕様漏れかなと思います。 ビルドが失敗するようなケースは結構あるんですが、PrettierやESLintなどのフォーマッターやリンターをちゃんと定義しておいて、その定義の下で処理してもらうようにするとある程度コードとしては綺麗になります。 ただし、ディレクトリ構成やプロジェクトのコード設計思想みたいなところを伝えていないと、fat controllerのように1ファイルに対して過剰にコードを書いてしまうみたいなことは割とありがちですね。 学習効果の発見 個人的に学びになった点としては、仕様書作成が苦手な私でも、AIが返してきた仕様書を見て「分かりやすい」と感じることがあります。学習的な側面でも価値があると思います。 完璧な仕様書があれば理想的ですが、そういうものは実際には存在せず、どこかに不備があるものです。だからこそ、 機能ごとに段階的に仕様書を作っていくアプローチが有効 だと思います。 また、検証フェーズで計画の不備を見つけるプロセスを踏むことでレビュー観点を育てることができます。レビューをする体験そのものが貴重です。 実践での注意点とコツ ここにまとまり切っていない部分に関しては別途「 Claude Code仕様書ベースでハマる6つの落とし穴!失敗回避の備忘録 」でまとめています。 この手法が向いていない作業 実際に検証してみて、 この3フェーズ手法でも上手くいかない作業 があることが分かりました。 プロジェクト構造の大幅変更 プロジェクトのディレクトリ構成を変えるような作業は、Claude Codeだとパスの解決なども自動でやってくれるのですが、使っている・参照しているファイルを再帰的に検索してということになるので、実際時間がかかってしまうという印象があります。 簡単なリネーム作業 関数のリネームのような作業に関しては、人間の手作業の方が圧倒的に早いです。VS Codeのリファクタリング機能のようなサポート機能を使うのがいいんじゃないかなと思っています。 フロントエンドデザインの特殊性 フロントエンドのデザインに関しては、仕様書を書いて作るというのはあまり向いていない かなと思ってます。 理由としては: フロントエンドで最初に想定していた仕様が全てを満たすことって無い UIを文章で説明するのって結構な分量が必要 分量が増えるとAIが混乱する確率も上がる 実際に画面を見て「何か違うな」という瞬間が割とある 自分の書いている仕様と自分の頭の中にあるイメージが合ってることがあまりない コンテキストが膨大に増えていく そのため、フロントエンドに関しては別のアプローチが必要だと考えており、別途検証を進めています。 Claude Codeの”忘れっぽさ”との付き合い方 実際に運用していて気づいたのですが、 CLAUDE.mdにいくら設定を書いても、コンテキストを無視することが結構あります 。特に長時間の作業や複雑なタスクになると、最初に設定した原則を忘れてしまうことがありますね。 そういう時の対処法として、私は以下のようなアプローチを取っています: リアルタイム修正パターン 動作を確認しながら進めて、「あ、意図した挙動をしてないな」と感じた瞬間に処理を止めます。そして、プロンプトに守ってほしい原則を改めて追加して再度実行する、という感じですね。 事後確認パターン 処理が完了した後に、「あれ?原則守ってないですよね?」とClaude Codeに詰める作業も行っています。これによって、なぜその判断をしたのかを確認し、次回の改善につなげています。 この経験から学んだのは、 AIも完璧ではない ということです。「人間が悪い」と言いましたが、同時にAI側の限界も理解して、適切にガードレールを設ける必要がありますね。完璧な仕様書があれば理想的ですが、そういうものは実際には存在しないのと同じように、完璧なAIも存在しません。だからこそ、人間とAIの協働において、お互いの特性を理解することが重要だと感じています。 適用に向いているプロジェクト 逆に、この手法が効果的なのは: バックエンドのAPI開発 :仕様が明確に定義しやすい データ処理ロジック :入力と出力が明確 既存プロジェクトへの機能追加 :コード規約やディレクトリ構成が既に決まっている プロトタイピング :完璧性よりもスピードを重視 まとめ 私は新機能開発とバグレポートをこの二つの方法で分けて対応しています。計画段階で仕様書を挟むことで、意図したものを作れるようになります。 ただし、万能ではありません。プロジェクトの性質や作業内容によって、従来のペアコーディング手法や人間による手作業の方が効率的な場合もあります。 適材適所で使い分けることが重要 ですね。 皆さんも、ぜひこの3フェーズ開発手法を試してみて、自分のプロジェクトに合うかどうか検証してみてください!何か質問や改善点があれば、コメントでお聞かせください。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Claude Code革命!3フェーズ開発で効率的な開発:計画→実装→検証術 first appeared on SIOS Tech. Lab .
はじめに ども!最近はGitHubをCMSとして活用する検証にハマっている龍ちゃんです。社内システムでGitHubをCMSとして使ったデータ管理を2パターン試してみたんですが、それぞれ全然違う特性で面白かったんですよね。 「GitHubをCMSとして使いたいけど、どうやってデータを取得するのがベストなんだろう?」って悩んでいる方、結構多いんじゃないでしょうか? 今回は、私が実際に検証した2つのアプローチを比較します: ランタイムAPIアクセス (NestJS + GitHub API) ビルドタイム同期 (GitHub Actions外部リポジトリ統合) どちらも実際にプロダクション環境で運用してみたので、リアルな運用データと合わせて比較していきますね! 2つのアプローチ概要 ランタイムAPIアクセス(NestJS実装) 特徴: リクエスト時にリアルタイムでGitHub APIを呼び出し 常に最新のデータを取得 サーバーサイドでAPI呼び出しを実行 ビルドタイム同期(GitHub Actions) 特徴: 事前にデータをビルド時に同期 静的ファイルとして配信 定期実行やトリガー実行での更新 技術的詳細比較 アーキテクチャの違い 項目 ランタイムAPIアクセス ビルドタイム同期 データ取得タイミング ユーザーリクエスト時 ビルド/デプロイ時 データ鮮度 リアルタイム 同期タイミング次第 サーバー負荷 API呼び出し毎回 静的配信 障害耐性 GitHub API依存 静的ファイル レスポンス速度 API待機時間あり 即座にレスポンス 運用面での比較 ランタイムAPIアクセス メリット デメリット デバッグが容易(ログでAPI呼び出しを追跡可能) レート制限対策が必要 即座にデータ反映 GitHub API障害の影響を直接受ける シンプルなエラーハンドリング サーバー監視が必須 API呼び出しによる遅延が発生 継続的なサーバーリソース消費 ビルドタイム同期 メリット デメリット 本番環境が安定(静的配信) データ反映にタイムラグあり GitHub API障害の影響を受けにくい GitHub Actions失敗時の対処が必要 運用コストが低い デバッグが間接的 高速なレスポンス 同期処理の複雑性 CDN活用による配信最適化 データ整合性の管理が困難 用途別推奨パターン ランタイムAPIアクセスを選ぶべきケース 推奨条件: - データ更新頻度: 高頻度(1日数回以上) - ユーザー数: 小〜中規模(同時接続 < 100) - 予算: サーバー運用費用が確保可能 - リアルタイム性: 重要 具体例: - 社内ドキュメントシステム - 個人ブログ(更新頻度高) - プロトタイプ・MVP開発 - チーム内情報共有ツール ビルドタイム同期を選ぶべきケース 推奨条件: - データ更新頻度: 低〜中頻度(1日数回以下) - ユーザー数: 大規模対応が必要 - 予算: 運用コスト削減重視 - パフォーマンス: 高速レスポンス必須 具体例: - 企業サイト・LP - 技術ブログ - ドキュメントサイト - ニュースサイト(定期更新) ビルドタイム同期のポイント: セキュアなPAT管理とアクセス制御 データ整合性チェックとバリデーション エラー時のフォールバック機構 ハイブリッド構成という第3の選択肢 実際の運用では、2つのアプローチを使い分ける選択肢もあります: 適用パターン 静的コンテンツ : ビルド時同期(ドキュメント、記事等) 動的コンテンツ : ランタイムAPI(ユーザー投稿、リアルタイム情報等) 頻度別管理 : 更新頻度に応じた自動振り分け ハイブリッド構成の特性比較 メリット デメリット 各コンテンツの特性に最適化 システム複雑性の増加 コストとパフォーマンスの両立 運用・監視コストの増大 段階的な移行が可能 デバッグ・障害対応の難易度上昇 リスク分散効果 技術スタックの多様化によるスキル要求 推奨判断基準 ハイブリッド構成を検討すべきケース: ├─ コンテンツ種別が明確に分かれている ├─ 段階的移行でリスク軽減したい ├─ 運用チームに十分なスキルがある └─ システム複雑性を許容できる予算・体制 セキュリティ面での考慮 項目 ランタイムAPI ビルドタイム PAT管理 本番サーバーに保存 CI/CD環境のみ アクセス制御 サーバーサイド制御 静的ファイル権限 ログ監査 詳細なアクセスログ ビルドログのみ 障害影響範囲 即座にサービス影響 既存データで継続 まとめ:最適解の選び方 決定フローチャート データ更新頻度は? ├─ 高頻度(1日数回以上) │ └─ ユーザー数は? │ ├─ 小規模(<100) → ランタイムAPI │ └─ 大規模(>100) → ハイブリッド構成 │ └─ 低頻度(1日数回以下) └─ コスト重視? ├─ Yes → ビルドタイム同期 └─ No → どちらでも可 技術選択の判断基準(検証結果) ランタイムAPI適用ケース: 小規模チームでの情報共有システム プロトタイプ開発(迅速なデバッグ重視) リアルタイム性が重要な管理画面 ビルドタイム同期適用ケース: 技術ブログやドキュメントサイト アクセス数が多い企業サイト 安定性を重視するシステム 今後の展望 GitHub APIエコシステムは進化し続けています: GitHub Apps認証 の活用でセキュリティ強化 GraphQL API による効率的なデータ取得 Webhook との連携でリアルタイム同期 どちらのアプローチを選んでも、これらの新機能を活用することで、さらに強力なシステムが構築できるでしょう。 次のステップ この記事を読んで「実際に試してみたい!」と思った方は、まずは小さなプロトタイプから始めることをおすすめします。GitHubの無料枠とActions無料枠を使えば、コストをかけずに両方のアプローチを検証できますよ! 皆さんも、ぜひ自分のプロジェクトに合った方法でGitHubをCMSとして活用してみてください。質問やご相談があれば、いつでもお声がけくださいね! 参考資料 GitHub REST API Documentation GitHub Actions Documentation NestJS GitHub Integration Best Practices Personal Access Token Security Guidelines ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GitHub CMS実装:ランタイム vs ビルドタイム比較!用途別プラクティス first appeared on SIOS Tech. Lab .
初めに 最近は社内の活動を少しでもやりやすくするための社内システム開発を龍ちゃんです。合わせて、AIと協業して開発する検証も進めています。AIを開発に入れてから、少ない人数で開発を回せているので素晴らしいですね。今回は、AIとはちょっと関係ない領域でのコンテンツですけど… 今回は、GitHubの優れた編集・レビュー機能をそのまま活用しながら、APIで簡単に自分のシステムに組み込む方法を紹介します。 なぜGitHubをCMSとして使うのか? 最大の魅力:GitHubの機能をフル活用できる 編集 : VS Codeライクなエディタで快適に編集 レビュー : Pull Requestで複数人でのレビュー 履歴管理 : すべての変更履歴が自動保存 検索 : リポジトリ内の強力な検索機能 権限管理 : チームでの細かいアクセス制御 つまり、 コンテンツの作成・編集・管理はGitHubに任せて、自分のシステムは「読み取り」に専念できる のです。 こんな使い方ができる GitHubリポジトリ構成例: /posts ├── 2024-01-15-github-api-guide.md ├── 2024-01-20-nestjs-tips.md └── 2024-01-25-typescript-tricks.md → これをAPIで取得して、ブログや社内ドキュメントサイトに表示! こちらのシステムと統合して使用する ことで、GitHub上で作成した成果物ををシステムに統合することができます。 Personal Access Token (PAT) の設定 GitHub APIを使うためには、PATの設定が必要です。 基本設定(検証で使いまわししたい場合) GitHub Settings → Developer settings → Personal access tokens → Tokens (classic) Generate new token をクリック 権限は repo を選択(プライベートリポジトリアクセス用) セキュリティ重視の場合 Fine-grained PAT の使用を推奨します: 対象リポジトリを限定可能 必要な権限: Contents: Read のみでOK 有効期限の強制設定でより安全 レート制限が劇的に改善されるんです! PAT認証の違いは劇的 なんですよ。 認証方式 1時間あたりの制限 1分あたり換算 認証なし 60回 1回/分 PAT認証 5,000回 83回/分 *つまり、PATを使うだけで83倍のリクエストが可能になるんです!**これは使わない手はないですよね。 コピペで動く!NestJSコントローラー実装 環境変数を設定 GITHUB_OWNER=your-username GITHUB_REPO=your-repo-name GITHUB_TARGET_DIRECTORY=/posts GITHUB_PAT=ghp_xxxxxxxxxxxxxxxxxxxx コントローラーにコピペするだけ! import { Controller, Get, Param, HttpException, HttpStatus } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'; @ApiTags('GitHub CMS') @Controller('api/github-cms') export class GitHubCmsController { private readonly baseUrl = 'https://api.github.com'; private readonly githubPat = '取得したPATを埋め込んでね!'; private readonly githubOwner = 'リポジトリOwnerを入れてね'; private readonly githubRepo = 'リポジトリ名を入れてね!'; private readonly githubTargetDirectory = '/posts'; private async githubRequest(path: string): Promise<any> { const response = await fetch(`${this.baseUrl}${path}`, { headers: { 'Authorization': `Bearer ${this.githubPat}`, 'Accept': 'application/vnd.github+json', 'User-Agent': 'github-cms-client/1.0' } }); if (!response.ok) { const errorMessages = { 401: 'GitHub認証エラー: PATを確認してください', 403: 'アクセス権限がありません', 404: 'リソースが見つかりません', 429: 'レート制限に達しました' }; throw new HttpException( errorMessages[response.status] || `GitHub API Error: ${response.status}`, response.status ); } return response.json(); } @Get('files') @ApiOperation({ summary: 'Markdownファイル一覧を取得' }) @ApiResponse({ status: 200, description: 'ファイル一覧' }) async getFiles() { const path = `/repos/${this.githubOwner}/${this.githubRepo}/contents${this.githubTargetDirectory}`; const files = await this.githubRequest(path); const markdownFiles = files.filter((file: any) => file.type === 'file' && file.name.endsWith('.md') ); return markdownFiles.map((file: any) => ({ name: file.name, path: file.path, sha: file.sha, size: file.size, downloadUrl: file.download_url })); } @Get('files/:filename') @ApiOperation({ summary: '特定のMarkdownファイルの内容を取得' }) @ApiResponse({ status: 200, description: 'ファイル内容' }) @ApiResponse({ status: 404, description: 'ファイルが見つかりません' }) async getFileContent(@Param('filename') filename: string) { if (!filename.endsWith('.md')) { filename += '.md'; } const path = `/repos/${this.githubOwner}/${this.githubRepo}/contents${this.githubTargetDirectory}/${filename}`; try { const file = await this.githubRequest(path); const content = Buffer.from(file.content, 'base64').toString('utf-8'); return { name: file.name, path: file.path, content: content, sha: file.sha, size: file.size, downloadUrl: file.download_url, htmlUrl: file.html_url }; } catch (error) { if (error.status === 404) { throw new HttpException('ファイルが見つかりません', HttpStatus.NOT_FOUND); } throw error; } } @Get('rate-limit') @ApiOperation({ summary: 'GitHub APIレート制限情報を取得' }) @ApiResponse({ status: 200, description: 'レート制限情報' }) async getRateLimit() { const data = await this.githubRequest('/rate_limit'); return { limit: data.rate.limit, remaining: data.rate.remaining, resetAt: new Date(data.rate.reset * 1000), used: data.rate.limit - data.rate.remaining }; } } 使い方はこんな感じ! APIエンドポイント # ファイル一覧を取得 GET /api/github-cms/files # 特定のファイル内容を取得 GET /api/github-cms/files/2024-01-15-github-api-guide.md # レート制限を確認 GET /api/github-cms/rate-limit レスポンス例 // GET /api/github-cms/files [ { "name": "2024-01-15-github-api-guide.md", "path": "posts/2024-01-15-github-api-guide.md", "sha": "abc123...", "size": 2048, "downloadUrl": "https://raw.githubusercontent.com/..." }, // ... ] // GET /api/github-cms/files/2024-01-15-github-api-guide.md { "name": "2024-01-15-github-api-guide.md", "path": "posts/2024-01-15-github-api-guide.md", "content": "# GitHub API活用ガイド\\n\\n本文...", "sha": "abc123...", "size": 2048, "downloadUrl": "https://raw.githubusercontent.com/...", "htmlUrl": "https://github.com/..." } フロントエンドでの使用例 // React/Next.jsでの実装 import { useEffect, useState } from 'react'; export default function BlogList() { const [posts, setPosts] = useState([]); useEffect(() => { fetch('/api/github-cms/files') .then(res => res.json()) .then(data => setPosts(data)); }, []); return ( <div> <h1>ブログ記事一覧</h1> {posts.map(post => ( <div key={post.sha}> <Link href={`/blog/${post.name.replace('.md', '')}`}> {post.name.replace('.md', '')} </Link> </div> ))} </div> ); } まとめ GitHubをCMSとして使うメリット: 編集・レビュー機能が無料で使える – GitHubの優れたUIをそのまま活用 APIアクセスが簡単 – シンプルな実装で十分実用的 レート制限もPATで解決 – 83倍のリクエストが可能に バージョン管理付き – すべての変更履歴が自動保存 つまり、 コンテンツ管理の面倒な部分はGitHubに任せて、自分はAPIで読み取るだけ という理想的な構成が実現できます。 こんなプロジェクトにおすすめ 個人ブログ・技術ブログ 社内ドキュメントサイト 静的サイトのコンテンツ管理 チームで編集する設定ファイル管理 GitHubの編集・レビュー機能を活用しながら、APIで簡単に自分のシステムに組み込める。これが「GitHub as a CMS」の魅力です! 参考資料 GitHub公式ドキュメント GitHub REST API Documentation – APIの完全なリファレンス Rate limits for the REST API – レート制限の詳細(認証なし60回/時 vs PAT認証5,000回/時の比較表あり) Contents API – ファイル取得APIの仕様 Personal Access Token (PAT) について Creating a personal access token – PATの作成手順 Scopes for OAuth apps – 必要な権限スコープの説明 実装に役立つリソース Octokit.js – GitHub公式のJavaScript SDKもあります(今回は使わずにfetch APIで実装) gray-matter – Markdown Frontmatterのパース用ライブラリ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GitHubをCMSに!NestJSでAPI実装する実践方法【コード付き】 first appeared on SIOS Tech. Lab .
はじめに ども!最近はXの自動投稿システムにハマっている龍ちゃんです。開発の方はかなりいい感じで進んでほくほく顔ですね。 皆さんも複数のリポジトリでデータ連携する時って、「どうやって安全に取り込もうか?」って悩むことありませんか?今回は、そんな課題を解決するGitHub Actionsを使った外部リポジトリ統合手法について、実際のプロダクション環境で検証した内容をシェアします。 活用シナリオ この手法が特に威力を発揮するのは、以下のようなケースです: コンテンツ管理の分離 :ブログポストを別リポジトリで管理し、開発者とコンテンツ作成者で作業領域を分ける 設定ファイルの共有 :複数のアプリケーション間で設定やデータを一元管理 権限管理 :コンテンツとアプリケーションで異なるアクセス権限を設定 私の具体的なケースでは、 Xの投稿文を自動で作成してプルリクエストを生成するシステム を別途構築し、そのデータをメインアプリケーションに取り込む必要がありました。 Personal Access Token (PAT) の設定 GitHub APIを使うためには、PATの設定が必要です。 基本設定(検証で使いまわししたい場合) GitHub Settings → Developer settings → Personal access tokens → Tokens (classic) Generate new token をクリック 権限は repo を選択(プライベートリポジトリアクセス用) セキュリティ重視の場合 Fine-grained PAT の使用を推奨します: 対象リポジトリを限定可能 必要な権限: Contents: Read のみでOK 有効期限の強制設定でより安全 リポジトリシークレットと環境変数の設定 作成したPATをGitHubリポジトリのSecretsに登録します: リポジトリの Settings > Secrets and variables > Actions New repository secret をクリック 名前: GHCR_TOKEN 、値:作成したPATを入力 続いて、リポジトリの Variables タブで以下を設定: EXTERNAL_REPO_OWNER : 外部リポジトリの所有者 EXTERNAL_REPO_NAME : 外部リポジトリ名 重要な注意点 :シークレット名に GITHUB_TOKEN は使用できません。これはGitHubが予約している名前のため、エラーになります。今回は GHCR_TOKEN としましたが、 EXTERNAL_REPO_TOKEN など分かりやすい名前を付けましょう。 環境変数として分けることで設定の柔軟性とメンテナンス性が大幅に向上します。後からリポジトリ名が変わっても、ワークフローファイル自体を変更する必要がありません。 実装詳細 ワークフロー全体構成 まずは全体の構成から見てみましょう: name: Sync External Data on: workflow_dispatch: permissions: contents: read actions: read env: EXTERNAL_REPO_OWNER: ${{ vars.EXTERNAL_REPO_OWNER || '' }} EXTERNAL_REPO_NAME: ${{ vars.EXTERNAL_REPO_NAME || '' }} 実装のポイント解説: workflow_dispatch で手動実行を可能に 環境変数で外部リポジトリ情報を管理 contents: read のみでOK:ファイル操作はローカルで完結し、リポジトリへのcommitは行わないため 私の検証環境では、最初permissions設定を忘れて「なんで動かないんだ?」ってハマりました。あと、会社側のリポジトリへのアクセス権を振ってないなんてこともありましたね。皆さんはこの辺り、気をつけてくださいね。 外部リポジトリの取得 - name: Step 1 - Fetch external repository if: ${{ env.EXTERNAL_REPO_OWNER != '' && env.EXTERNAL_REPO_NAME != '' }} uses: actions/checkout@v4 with: repository: ${{ env.EXTERNAL_REPO_OWNER }}/${{ env.EXTERNAL_REPO_NAME }} token: ${{ secrets.GHCR_TOKEN }} path: external-repo fetch-depth: 1 # 最新コミットのみ取得してパフォーマンス向上 重要な設定解説: token: ${{ secrets.GHCR_TOKEN }} : PAT認証を使用 path: external-repo : 取得先ディレクトリを指定 fetch-depth: 1 : 履歴を取得せず最新のみでパフォーマンス向上 if 条件で環境変数の存在をチェック ダミーデータのクリーンアップと差し替え - name: Step 2 - Clear existing markdown files run: | echo "Removing existing markdown files..." rm -f ./application/frontend/markdown/*.md echo "Existing markdown files removed" - name: Step 3 - Copy posts directory if: ${{ env.EXTERNAL_REPO_OWNER != '' && env.EXTERNAL_REPO_NAME != '' }} run: | echo "Copying posts directory from external repository..." if [ -d "./external-repo/posts" ]; then cp -r ./external-repo/posts/* ./application/frontend/markdown/ echo "Posts directory copied successfully" echo "Final markdown directory contents:" ls -la ./application/frontend/markdown/ else echo "Posts directory not found in external repository" exit 1 fi この処理では、開発中はダミーデータでアプリ側の挙動を確認し、デプロイする際に本番データに差し替えています。これによって、ダミーデータで動作確認しながら開発ができて、本番時は本当のデータでアプリを動かすことができるんです。 セキュリティと運用上の注意点 トークンの適切な管理 セキュリティ面での注意点について、実際の運用で学んだポイントをシェアします: 最小権限の原則 :必要最小限のスコープのみを付与 定期的なトークンのローテーション :90日推奨 環境別の設定 :本番環境とテスト環境でトークンを分離 更新管理の対策 この構成における運用上の重要な注意点として、 GitHub Actionsを実行した時にのみデータが更新される 点があります。外部リポジトリに変更を加えた場合、以下のような対策が有効です: スケジュール実行による定期的な同期 on: schedule: - cron: '0 6 * * *' # 毎日6時に実行 CI/CDパイプラインの一部として組み込む デプロイ前の必須ステップとして設定 本番データ同期の自動化 実際に運用してみると、「あれ?データが古いままだ」ってことがよくあります。この辺りの仕組みをしっかり作っておくと、運用が格段に楽になりますよ。 よくあるトラブルと対処法 実際に運用していて遭遇したトラブルと対処法をまとめました: PAT権限不足エラー(403 Forbidden) :PAT作成時に必要な権限スコープを再確認 ファイルが見つからないエラー : ls -la コマンドでディレクトリ構造を確認 環境変数未設定エラー :Repository Variablesの設定を確認 まとめ PATを使用したGitHub Actions外部リポジトリ統合により、以下を実現できます: セキュアなアクセス制御 :適切な権限管理で安全性を確保 柔軟な運用 :環境変数による設定の外部化でメンテナンス性向上 自動化されたワークフロー :手動作業を削減し、ヒューマンエラーを防止 データ整合性の保証 :バリデーションと復旧機能で信頼性向上 実装時は、セキュリティ、パフォーマンス、保守性の観点から十分な検討を行い、チームの開発フローに適合した形で導入することが重要です。特にPATの管理については、定期的なローテーションと最小権限の原則を徹底しましょう。 皆さんも、ぜひこの手法を使って効率的なデータ連携にチャレンジしてみてください!質問やご相談があれば、いつでもお声がけくださいね。 参考リンク GitHub Actions Checkout Action GitHub Personal Access Tokens 管理ガイド GitHub Actions Secrets 使用ガイド ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GitHub Actions×PATで実現!セキュアな外部リポジトリ統合の実践手法 first appeared on SIOS Tech. Lab .
はじめに ども!最近はClaude Codeにべったりな龍ちゃんです。皆さんは技術ブログから SNS 投稿まで、コンテンツマーケティングの自動化に取り組んでいますか?エンジニアやっている傍らで、情報発信やXの運用なども行っています。 私たちのチームでも以前、 Claude のプロジェクト機能でブログから X 投稿文を自動生成するシステム を構築しました。これはこれで便利だったのですが、運用していくうちに以下の課題が明らかになったんですね。 Claude プロジェクトの運用課題 Claude の契約者しか利用できず、チーム内での共有や協業に制限がある システムプロンプトの更新が上書き保存となり、改善履歴や変更効果の追跡ができない 成果物が Claude 上や他ツールに分散し、管理とレビューワークフローが属人化しやすい 「これは何とかしないと…」ということで、Claude API × GitHub Actions による完全自動化システムを構築することにしました。今回は設計思想から実装のポイント、コスト最適化まで詳しく解説していきますね。 成果物 今回の作成物は、.envファイルにClaude APIのAPI Keyを環境変数として入れることで動作するようになります。興味がある方はリポジトリを読んでみてください。こちらのリポジトリではPythonを書く環境をDevContainerで構築しています。 参考 GitHub – Ryunosuke-Tanaka-sti/github-x-generate-demo GitHub GitHub × Claude API で実現する解決アプローチ 基本コンセプト 「GitHub だけでブログ URL から X 投稿文の生成・レビュー・公開まで完結」を目指しました。具体的には以下を実現しています: ワンクリック起動 : URL 入力で自動実行 完全なバージョン管理 : Git でプロンプト・成果物を管理 チーム協業 : 誰でもレビュー・承認可能 コスト最適化 : API 利用料を最小化 システム全体の流れ 自動化フローは以下のようになります: GitHub Issue でトリガー : 記事 URL をタイトルに入力してイシューを作成 HTML 自動取得 : Python スクリプトで記事本文を抽出・クリーニング Claude API で分析 : プロンプトキャッシュを活用して 3 パターンの投稿文を生成 PR 自動作成 : 生成結果を含む Pull Request を自動作成 レビュー・マージ : チームメンバーが内容を確認して承認 このフローにより、Issue 作成から最終的な投稿文完成まで、すべて GitHub 上で完結できるようになりました。 コスト最適化の技術的工夫:API 利用料を 60% 削減 Claude API の利用において、最も大きなコスト要因はトークン数です。私たちは以下の 2 つのアプローチでコストを大幅に削減しました。 1. HTML 前処理によるトークン削減(約 50% 削減) 当初はブログページの HTML を丸ごと送信していましたが、よく考えてみれば記事本文のみが必要なんですよね。Python スクリプトで以下の前処理を実装しました: # 不要な HTML 要素の除去例 - ヘッダー・フッター・ナビゲーション - サイドバー・広告・関連記事 - JavaScript・CSS・meta タグ - コメント欄・SNS ボタン この前処理により入力トークン数を約 50% 削減、コストが半分になりました。 2. プロンプトキャッシュの活用(約 37% 削減) Claude API のプロンプトキャッシュ機能 を活用することで、システムプロンプト部分のトークン利用料を大幅に削減しました。 # プロンプトキャッシュの実装例 response = self.client.messages.create( model=self.model, max_tokens=4000, system=[ { "type": "text", "text": self.system_prompt_content, "cache_control": {"type": "ephemeral"} # キャッシュ設定 } ], messages=[{"role": "user", "content": user_prompt}] ) システムプロンプトをキャッシュすることで、2 回目以降は約 37% のコスト削減を実現しています。 実際のコスト削減効果 両方の最適化を組み合わせることで、実行コスト $0.10 → $0.04 程度に削減できました。年間で考えるとかなりの削減効果ですね。 システム実装の詳細 プロジェクト構成 github-x-generate/ ├── scripts/ # 実行スクリプト群 │ ├── generate_posts_with_cache.py # Claude API統合 │ ├── fetch_html_from_techlab.py # HTML取得・前処理 │ └── simple_claude_api.py # API クライアント ├── prompts/ │ └── system_prompt.md # システムプロンプト定義 ├── .github/workflows/ │ └── generate_PR_from_issue.yaml # メイン自動化ワークフロー ├── posts/ # 生成結果保存ディレクトリ └── requirements.txt # Python 依存関係定義 4 段階の投稿文生成プロセス システムプロンプトでは、以下の 4 段階プロセスで高品質な投稿文を生成します: Phase 1: 記事分析・品質評価 技術的正確性の 5 段階評価 実装レベルの判定(プロトタイプ〜企業レベル) 対象読者レベルの特定 Phase 2: ハッシュタグ効果分析 Web 検索による最新トレンドの調査 エンジニア向け効果的ハッシュタグの選定 Phase 3: 3 パターンの投稿文作成 A パターン: 効果重視・数値訴求型 B パターン: 課題共感・解決提案型 C パターン: 技術トレンド・学習促進型 Phase 4: 誇張表現検証・修正 記事内容との照合による事実確認 根拠のない数値・効果の除去 使用しているプロンプトはこちらのブログで解説をしています。 実際の運用フロー 1. GitHub Issue での起動 https://tech-lab.sios.jp/archives/48173 記事 URL をタイトルに入力して Issue を作成するだけで、GitHub Actions が自動起動します。これだけです! 2. 自動処理の実行 HTML 記事の取得と本文抽出(約 30 秒) Claude API による投稿文生成(約 45 秒) PR 作成とメタデータ保存(約 15 秒) トータル 1 分半程度で完了します。 3. レビュー・承認プロセス 生成された PR には以下が含まれます: 3 パターンの投稿文 : 異なるアプローチの投稿候補 品質評価データ : 技術的正確性や対象読者の分析結果 コスト情報 : 実際の API 利用料とトークン使用量 レビュアーは内容を確認し、必要に応じて微調整を加えてからマージするだけです。 4. 運用実績 23 件の技術記事で運用実績があり、Azure・インフラ、AI・Claude、Git・CI/CD、Docker・コンテナなど多様な技術分野で安定動作を確認しています。 効果とメリット 定量的な効果 コスト削減効果 HTML 前処理: 50% トークン削減 プロンプトキャッシュ: 37% トークン削減 総合的なコスト削減: 約 60% 工数削減効果 投稿文作成時間: 30 分 → 3 分(90% 削減) レビュー・修正工程: 20 分 → 5 分(75% 削減) 全体工程: 1 時間 → 10 分(83% 削減) 定性的なメリット チーム協業の向上 誰でも投稿文生成を実行可能 GitHub ベースのレビューワークフロー 知見の蓄積とバージョン管理 品質の向上 誇張表現の自動検出・修正 記事内容との整合性保証 3 パターンによるA/Bテスト可能 運用の効率化 Issue 作成からマージまで自動化 エラー時の詳細な診断情報 成果物の一元管理 これは想像以上に効果的でした。特に複数人でのコンテンツマーケティングには必須のツールになりそうですね。 まとめと今後の展望 Claude API × GitHub Actions により、個人プロジェクトでは困難だった「チーム協業」「バージョン管理」「コスト最適化」を同時実現することができました。約 60% のコスト削減と GitHub 完結型ワークフローにより、効率的な運用が可能になります。 今後の発展可能性 他プラットフォーム展開 : LinkedIn、Facebook 対応 多言語対応 : 英語版投稿文の自動生成 効果分析 : エンジニアリング指標の追加連携 統計分析活用 : 蓄積データによるプロンプト最適化 技術ブログのコンテンツマーケティング自動化にお悩みの方は、ぜひ参考にしてください。もし実装で困りごとがあれば連絡いただければお手伝いしますよ!! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Claude API×GitHub Actions完全自動化でコスト60%削減!ブログ投稿システム構築術 first appeared on SIOS Tech. Lab .
はじめに 前回 は、GitLabのプロジェクトについて解説しました。GitLabのプロジェクトは、ソースコード管理を中心に、イシュー管理、CI/CD、Wiki、セキュリティまでをまとめて扱うことができます。これにより、開発プロセス全体を効率化し、ツール間の切り替えや管理の手間をなくすことができます。 今回は、その中でもGitLabのCI/CD(継続的インテグレーション・継続的デリバリー)に焦点を当てます。CI/CDは、開発プロセスを自動化し、生産性を大きく向上させる方法です。 まずは、CI/CDの基本を解説し、GitLab Runnerのインストールとセットアップを行います。そしてnginxのコンテナイメージを自動でビルド・プッシュ・デプロイする簡単なパイプラインを構築するまでを解説します。 Kubernetesクラスターとの連携をするため、以下を前提としています。本記事では minikube を利用します。 Dockerやコンテナの基本操作を理解していること Kubernetesの基本的な概念(Pod, Deployment, Namespaceなど)を知っていること kubectl コマンドを実行できる環境があること GitLab CI/CDの概要 CI/CD(Continuous Integration / Continuous Delivery & Deployment)は、継続的インテグレーション(CI)と継続的デリバリー(CD)の2つの概念から成り立っています。 継続的インテグレーション (CI):開発者がコードをリポジトリにマージするプロセスです。マージされるたびに自動的にビルドとテストが実行され、バグを早期に発見できます。これにより、コードの品質を常に高いレベルに保つことができます。 継続的デリバリー (CD):テスト済みのコード変更を、いつでも環境にデプロイできる状態に保つプロセスです。手動でデプロイすることもできますが、継続的デプロイメントでは、すべてのテストがパスしたら自動的にデプロイが行われます。 GitLabは、このCI/CDワークフローを標準機能としてサポートしています。「.gitlab-ci.yml」という設定ファイルを作成すれば、ビルド、テスト、デプロイなどのパイプラインを簡単に定義できます。 .gitlab-ci.ymlによって自動化されたタスク(ジョブ)は、GitLab Runnerというエージェントによって実行されます。GitLab Runnerは、パイプラインの指示を受け取り、実際の作業を行う実行役です。GitLab Runnerは、GitLabとは別にインストール・登録する必要があります。 KubernetesとGitLabの連携 GitLab Runnerは、Dockerや仮想マシンなど様々な実行環境(Executor)でジョブを実行できますが、今回はKubernetesとの連携をとりあげます。Kubernetes Executortは、ジョブをKubernetesのPodとして実行する仕組みのことです。さらに詳しく知りたい人は以下のURLを参考にしてみてください。 https://docs.gitlab.com/runner/executors/kubernetes/ GitLabとKubernetesを連携させるには、Kubernetesクラスター上にGitLab Runnerをデプロイします。GitLab、GitLab Runner、Job Pod、Kubernetesクラスターの関係性は以下のようになっています。 GitLabのパイプライン実行:ユーザーがGitLab上でコミットやマージを行うと、CI/CDパイプラインが自動的にトリガーされます。 GitLab Runnerの起動:パイプライン内のジョブが、Kubernetesクラスター上で動作しているGitLab Runnerに割り当てられます。 Job Podの生成:GitLab RunnerはKubernetes Executorを使用し、ジョブを実行するために一時的なJob PodをKubernetesクラスター内に自動生成します。Job Podはジョブ完了後に削除されます。 コマンドの実行:このJob Pod内で、パイプラインのジョブに定義されたコマンド(例:kubectl apply)が実行されます。 コンテナのデプロイ:コマンドの実行により、アプリケーションのコンテナがKubernetesクラスター内にデプロイされます。 この連携により、kubectlコマンドなどを手動で実行することなく、GitLab上でデプロイを自動化できます。GitLabとKubernetesを連携させるために、GitLab RunnerをKubernetesに導入していきます。 環境情報 GitLab:SaaS版 minikube:v1.36.0 Kubernetes Server Version: v1.33.1 kubectl:v1.33.1 helm:v3.18.6 GitLab Runnerを導入してみる それでは、ローカル環境に簡単にKubernetesクラスターを構築できるminikubeを使って、GitLab Runnerを導入してみましょう。minikubeはインストール済みであることを前提とします。 GitLabで今回使用するプロジェクトを作成します。プロジェクトの作成方法は 第4回 、 第7回 を参考にしてみてください。今回はブランチの考慮は行わないのでmainブランチのみで検証していきます。 GitLabでプロジェクトのCI/CD設定からRunnerの登録トークンを取得します。 メニューの 設定>CI/CD から[プロジェクトRunnerを作成]をクリックします。 タグを入力して、あとはデフォルトで[Runnerを作成]をクリックします。 タグは.gitlab-ci.ymlでGitLab CI/CDでジョブを実行するRunnerを選択するために使用します。 表示された Runner認証トークン(glt-xxxxxxx)をメモします。 GItLabはこのページのままで大丈夫です。 それでは、GItLab Runnerを導入してみましょう。   ローカル端末で操作します。 minikubeを起動します。 $ minikube start Kubernetesにアプリケーションをデプロイする標準的なツールであるHelmをインストールします。 $ sudo snap install helm --classic インストールが完了したら、Helmが正しくインストールされたか確認します。 バージョン情報が表示されれば、インストールは成功です。 $ helm version version.BuildInfo{Version:"v3.18.6", GitCommit:"b76a950f6835474e0906b96c9ec68a2eff3a6430", GitTreeState:"clean", GoVersion:"go1.24.6"} GItLab Runnerをデプロイするネームスペースを作成します。 $ kubectl create namespace gitlab GItLab Runnerをデプロイします。 $ helm install gitlab-runner gitlab/gitlab-runner -f values.yaml -n gitlab values.yaml gitlabUrl: https://gitlab.com runnerToken: "<取得したRunner認証トークン>" rbac:   create: true   clusterWideAccess: true   rules:     - resources: ["configmaps", "pods", "pods/attach", "secrets", "services"]       verbs: ["get", "list", "watch", "create", "patch", "delete"]     - resources: ["secrets"]       verbs: ["get", "list", "watch", "create", "patch", "update", "delete"]     - resources: ["serviceAccounts"]       verbs: ["get"]     - apiGroups: [""]       resources: ["pods/exec"]       verbs: ["create", "patch", "delete"] serviceAccount:   create: true   name: "gitlab-runner"    runners:   config: |     [[runners]]       name = "Kubernetes GitLab Runner"       executor = "kubernetes"       shell = "bash"       [runners.kubernetes]         terminationGracePeriodSeconds = 5         privileged = true         allow_privilege_escalation = true         image = "alpine" 少ししてから、GItLab Runner Podがデプロイされているか確認します。 $ kubectl get pod -n gitlab NAME                             READY   STATUS    RESTARTS   AGE gitlab-runner-85d8fbcdc4-94ph2   1/1     Running   0          94s GitLab Runnerがローカルのminikubeに接続するためのkubeconfigの設定をします。 $ kubectl config view --minify --flatten --raw > kubeconfig-inline $ sed -i 's|https://127.0.0.1:32771|https://kubernetes.default.svc|' kubeconfig-inline 以下で表示されるbase64エンコードしたkubeconfigの文字列は後ほど使用するので、記録しておきます。 $ cat kubeconfig-inline | base64 -w 0 GitLabの画面に戻ります。 [Runnerを表示する]をクリックし、「アサインされたプロジェクト」のRunnerが図のように緑のオンラインになっていたら連携完了です。 Ci/CDの環境変数設定をします。ここで先ほど記録したbase64エンコードしたkubeconfigの文字列を環境変数に設定します。 メニューの 設定>CI/CD から変数を開きます。CI/CD変数の[変数を追加]をクリックします。 キーと値を入力して、[変数を追加]をクリックします。 kubeconfigは機密情報なのでマスク(非可視化)しています。 キー:KUBECONFIG_BASE64 値:<先ほど記録したbase64エンコードしたkubeconfigの文字列> 追加したCI/CD変数が表示されていたら完了です。 .gitlab-ci.ymlについて .gitlab-ci.ymlは、GitLab CI/CDパイプラインを定義するためのYAMLファイルです。プロジェクトのルートディレクトリに配置します。 このファイルでは、さまざまなキーワードや要素を使って柔軟なパイプライン定義が可能です。 主な基本構造と記述方法 stages:パイプラインの実行順序を定義します。例えば、build、test、deployといったステージを設定できます。各ジョブはstageでそれぞれ自身が所属するステージを指定します。 job:具体的に何を実行するかを定義します。script要素でコマンド記述が必須です。トップレベルのキー名がそのままジョブ名になります。例えば、build_jobの場合はジョブ名がbuild_jobになります。 stagesで順序を定義し、ジョブごとに所属するステージを指定することで一連の処理フローを構築します。stagesで定義した順番に、同一ステージのジョブは並列、次のステージのジョブは順次実行されます。 代表的な機能 tags:使用するGitLab Runnerを指定します。 image:ジョブを実行するためのDockerイメージを指定します。Docker / Kubernetes Executorに限ります。 services:テストやビルド環境に必要な外部サービス(例:DB)用の補助コンテナを追加します。Docker / Kubernetes Executorに限ります。 script:ジョブで実行するコマンドを記述します。 before_script / after_script:全ジョブや特定ジョブ実行前後の共通コマンドを定義できます。 variables:環境変数やパイプライン用の変数を定義できます。 rules:ブランチ・タグ・パイプラインなどの条件分岐でジョブの起動制御が可能です。mainブランチへpushされた時だけデプロイを実行する場合などに使用します。 参考: https://docs.gitlab.com/ci/yaml/ コンテナイメージのビルド・プッシュ・デプロイをしてみる CI/CDパイプラインを構築し、コンテナイメージのビルドからデプロイまでを自動化します。このプロセスは通常、複数のステージに分かれます。 build-and-push ステージ:ここでは、アプリケーションのコンテナイメージをDockerでビルドします。ビルドしたイメージは、GitLabのコンテナレジストリにタグを付けてプッシュします。これにより、作成したイメージがCI/CDパイプラインで利用可能になります。 deploy ステージ:build-and-push ステージでビルド・プッシュされたコンテナイメージを使用して、ローカル環境のminikubeにアプリケーションをデプロイします。このステージでは、kubectlを使って、デプロイメントの更新やサービスの公開を行います。 はじめに、Gitリポジトリをローカルにクローンします。 $ git clone <GitリポジトリのURL> 以下のディレクトリ構造でファイルを作成していきます。 . ├── .gitlab-ci.yml ├── Dockerfile ├── README.md ├── kubernetes │   └── deployment.yaml.tpl └── src     ├── html     │   └── index.html     └── nginx.conf gitlab-ci.ymlにDockerビルドのステップを追加します。 この例では、docker:dind(Docker in Docker)サービスを使ってDockerコマンドを実行し、コンテナイメージのビルドとレジストリへのプッシュを行っています。実運用では権限やネットワーク設定に注意が必要ですが、ここでは動作確認のための簡易構成として使います。 .gitlab-ci.yml stages:   - build-and-push   - deploy variables:   DOCKER_HOST: tcp://docker:2375   DOCKER_TLS_CERTDIR: ""   K8S_NAMESPACE: gitlab  build-and-push:   tags:     - git-tutorial-8   stage: build-and-push   image: docker:28.4.0-alpine3.22   services:     - docker:28.4.0-dind-alpine3.22   before_script:     - until docker info; do sleep 1; done;   rules:     - if: '$CI_COMMIT_BRANCH == "main"'   script:     - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY     - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG .     - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG deploy-to-minikube:   tags:     - git-tutorial-8   stage: deploy   image:      name: bitnami/kubectl:1.33.1     entrypoint: [""]   before_script:     - echo "$KUBECONFIG_BASE64" | base64 -d > kubeconfig     - export KUBECONFIG=$(pwd)/kubeconfig   rules:     - if: '$CI_COMMIT_BRANCH == "main"'   script:     - kubectl config set-context --current --namespace=$K8S_NAMESPACE     - kubectl create secret docker-registry regcred --docker-server=https://registry.gitlab.com --docker-username=$CI_REGISTRY_USER --docker-password=$CI_REGISTRY_PASSWORD -n $K8S_NAMESPACE || true     - envsubst < kubernetes/deployment.yaml.tpl > deployment.yaml     - kubectl apply -f deployment.yaml Dockerfile FROM nginx:alpine COPY src/nginx.conf /etc/nginx/nginx.conf COPY src/html/index.html /usr/share/nginx/html/index.html CMD ["nginx", "-g", "daemon off;", "-p", "8080"] src/html/index.html <!DOCTYPE html> <html> <head>   <meta charset="UTF-8" />   <title>GitLab CI/CD Demo</title>   <style>     body {       font-family: sans-serif;       background-color: #f0f0f0;       text-align: center;       padding-top: 50px;     }   </style> </head> <body>   <h1>Deployment Successful!</h1>   <p>This is an application deployed via the GitLab CI/CD pipeline.</p> </body> </html> src/nginx.conf user  nginx; worker_processes  auto; error_log  /var/log/nginx/error.log notice; pid        /run/nginx.pid; events {     worker_connections  1024; } http {     include       /etc/nginx/mime.types;     default_type  application/octet-stream;     charset       utf-8;     log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '                       '$status $body_bytes_sent "$http_referer" '                       '"$http_user_agent" "$http_x_forwarded_for"';     access_log  /var/log/nginx/access.log  main;     sendfile        on;     #tcp_nopush     on;     keepalive_timeout  65;     #gzip  on;     server {         listen 8080;         location / {             alias /usr/share/nginx/html;             index index.html index.htm;             default_type text/html;             charset utf-8;         }     }     include /etc/nginx/conf.d/*.conf; } kubernetes/deployment.yaml.tpl apiVersion: apps/v1 kind: Deployment metadata:   name: nginx-deployment   labels:     app: nginx spec:   replicas: 1   selector:     matchLabels:       app: nginx   template:     metadata:       labels:         app: nginx     spec:       containers:       - name: nginx-container         image: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_SLUG} # GitLab CI/CD の環境変数         ports:         - containerPort: 8080       imagePullSecrets:       - name: regcred --- apiVersion: v1 kind: Service metadata:   name: nginx-service spec:   type: LoadBalancer   ports:   - port: 8080     targetPort: 80   selector:     app: nginx ファイルを作成したら、リモートリポジトリにプッシュします。 $ git add . $ git commit -m "git-tutorial-8" $ git push origin main プッシュしたらメニューの ビルド>パイプライン から、リポジトリへのプッシュをトリガーにパイプラインが実行されていることを確認します。 ステータスが「成功」になっていたら完了です。 各ステージのステータスも確認しておきましょう。 パイプラインの下に表示されているコミットメッセージの下の数列をクリックします。 .gitlab-ci.ymlで定義した2つのステージが完了していることが確認できます。 それでは、ビルドしたコンテナイメージがプロジェクトのコンテナレジストリにプッシュされていることを確認しましょう。 メニューの デプロイ>コンテナレジストリ から、プロジェクトのコンテナレジストリを確認します。 コンテナイメージが表示されたら完了です。 次に、アプリケーションがデプロイされていることを確認しましょう。 ローカル端末で操作します。 デプロイしたネームスペースのリソースを確認します。 nginxのservice、deployment、podが作成されていることが確認できます。 $ kubectl get all -n gitlab NAME                                    READY   STATUS    RESTARTS   AGE pod/gitlab-runner-767f96f489-f2qsb      1/1     Running   0          13m pod/nginx-deployment-7cdb8748c6-bbwlm   1/1     Running   0          12m NAME                    TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE service/nginx-service   LoadBalancer   10.98.81.158   <pending>     8080:32740/TCP   12m NAME                               READY   UP-TO-DATE   AVAILABLE   AGE deployment.apps/gitlab-runner      1/1     1            1           13m deployment.apps/nginx-deployment   1/1     1            1           12m NAME                                          DESIRED   CURRENT   READY   AGE replicaset.apps/gitlab-runner-767f96f489      1         1         1       13m replicaset.apps/nginx-deployment-7cdb8748c6   1         1         1       12m 外部からminikube内のサービスにアクセスできるようにします。 $ minikube tunnel ターミナルはそのままにして、ローカル端末のブラウザで http://127.0.0.1:8080/ にアクセスします。 アプリケーションの画面が表示されたら完了です。 これで、Gitリポジトリにプッシュするだけで次の一連のワークフローを自動化することができました。 コンテナイメージをビルド ビルドしたコンテナイメージをGitLabのプロジェクトのコンテナレジストリにプッシュ プッシュしたコンテナイメージを使用して、Kubernetesクラスターにアプリケーションをデプロイ まとめ 今回は、CI/CDの基本から、GitLab Runnerの導入、そしてコンテナイメージのビルド・プッシュ・デプロイまで、一連のワークフローを解説しました。 この自動化されたプロセスにより、開発者はより頻繁にコードをリリースでき、サービスの改善サイクルを加速させることができます。 参考文献 http://xn--docs-u83c.gitlab.com/ci/ https://docs.gitlab.com/runner/executors/kubernetes/ https://docs.gitlab.com/ci/yaml/ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Git & GitLab 入門 (8) ~Git マスターへの道~「GitLabのCICD設定」 first appeared on SIOS Tech. Lab .
こんな方へ特におすすめ Azure Database for MySQLを利用している方 SSL/TLS証明書に関する基本的な知識を持つ方 はじめに こんにちは。サイオステクノロジーのはらちゃんです!今回3本目のブログ執筆です。 Azure Database for MySQLを利用していたら証明書エラーになったので調べた結果をお話したいと思います。 概要 ある日突然、Azure Database for MySQLへの接続が「証明書エラー」でできなくなった──。 DigiCertGlobalRootCA.crt.pem を利用しており、こんな経験をした方はいませんか? もしかしたら、「証明書が予期せず切れた?」と感じたかもしれません。しかし、その原因は、Azure側がセキュリティ強化のために 計画的にルート証明書を変更した ためです。 今回は、このAzure Database for MySQLで発生したルート証明書変更の背景、なぜ変更が必要だったのか、どのように対応すべきかを詳しく解説します。 なぜ証明書の変更が必要? これまでAzure Database for MySQLで利用されていたルート証明書 DigiCertGlobalRootCA.crt.pem は、「 SHA-1 」というハッシュアルゴリズムで署名されていました。 しかし、このSHA-1には 深刻な脆弱性 が発見されており、現在では安全とは言えません。多くのWebブラウザやセキュリティ機関が、SHA-1証明書の使用を非推奨としています。 そこでMicrosoftは、ユーザーのデータセキュリティとコンプライアンスの基準を維持するため、より強固な「 SHA-256 」で署名された新しいルート証明書 DigiCertGlobalRootG2.crt.pem への計画的な移行を進めていました。 利用していた環境で突然接続が無効になったように感じられたのは、この移行期間において、MySQLサーバー側が新しい証明書を要求するように更新されたためと考えられます。 セキュリティは常に進化しており、古い技術は新しい脅威に対応できなくなります。今回の変更は、Azureがユーザーに最新のセキュリティを提供するための重要なステップだったのです。   SHA-1 SHA-256 概要 任意の長さのデータを160ビットの固定長ハッシュ値に変換 固定長の256ビットのハッシュ値を生成 特徴 衝突耐性が低下しており、セキュリティリスク SHA-1よりも高いセキュリティと衝突耐性 大きな違いは「生成されるハッシュ値の長さ」と「セキュリティの強度」です。 証明書の有効期限切れが原因ではない 「証明書エラー」と聞くと、まず「有効期限が切れたのでは?」と思うかもしれません。 確かに、サーバー証明書の有効期間はセキュリティ上の理由から年々短くなる傾向にあります。 しかし、今回のAzure MySQLの接続問題は、 証明書の有効期限切れが直接の原因ではありません 。あくまで、より安全な証明書への切り替えという、Azureの計画的なメンテナンスの一環でした。 2025 年 9 月 1 日以降、Azure Database for MySQL フレキシブル サーバーのルート証明書の変更を開始しています。 この違いを理解することは、今後のトラブルシューティングにおいても非常に重要です。 今後の対応 このルート証明書の変更については、Microsoftの 公式ドキュメント で詳しく説明されています。今回のメンテナンスにより、以下の対応が求められます。 新しいルート証明書のダウンロード Microsoft Learnのドキュメントから、最新のルート証明書(例: DigiCertGlobalRootG2.crt.pem )をダウンロードします。 アプリケーションへの適用 お使いのアプリケーションや接続ライブラリ(JDBC, ODBC, .NET, MySQL Connector/Pythonなど)の設定で、 ダウンロードした新しいルート証明書を信頼するようパスを指定 します。多くの場合、 ssl_ca や cafile といったパラメータで設定します。 既存の証明書との併用(推奨) 一時的な期間は、古い証明書と新しい証明書の両方を信頼するように設定できる「 CAバンドルファイル 」を作成・利用することが推奨されます。これにより、環境全体が新しい証明書に対応するまでの移行期間中の接続断を防ぐことができます。 また、 メンテナンスの通知設定 なども紹介されているので合わせて確認しておくと役立つと思います。 まとめ 今回は、Azure Database for MySQLで発生したルート証明書変更の背景と、その対応策について解説しました。 「証明書エラー」の裏には、サービスのセキュリティ強化という重要な目的がありました。突然の接続断に驚かれたかもしれませんが、 DigiCertGlobalRootG2.crt.pem のような新しい安全な証明書への更新は、現代のクラウドサービス運用において不可欠なプロセスです。 今後もAzureからの重要なお知らせには注意を払い、公式ドキュメントを参照しながら早めに対応することが、安定したサービス運用の鍵となります。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure MySQLの使い方|突然の接続エラー? ルート証明書変更の原因と対応策 first appeared on SIOS Tech. Lab .
こんにちは。サイオステクノロジー OSS サポート担当 山本 です。 今回は 2025年5月末にリリースされた RHEL10 へのアップグレードに関するお話です。 RHEL9 から RHEL10 に移行する場合の、RHEL 同梱版 squid の変更点について確認したいと思います。 ご存じのとおり、squid は必須のソフトウェアというわけではないので、使っている場合にのみ気にする必要がある、程度のものですが、何かの参考になればと思います。 ■メジャーバージョンの更新 ベースとなるバージョンが squid 6.10 になりました 。(RHEL9 では squid 5.2 がベース) Red Hat 社から公表されている情報 は、確認時点では (Red Hat 社が用意している squid 導入マニュアル等も含めて) このベースバージョンについての情報以外に RHEL9 のものから特に変更は確認できません。 しかし、メジャーバージョンが変わっているので、機能追加や動作改善などを含め変更点は多数存在しているでしょう。 実際に squid 公式 github ページの Releases で squid 6 系の一番最初のリリース情報を確認してみると、以下のとおり非常に多数の項目が確認できます。 (末尾の方には “… and ~” と一まとめにされた内容もあるので、細かく見れば変更箇所は相当な数になるでしょう…)  ・ Release v6.0.1 今回はそんな変更点の一部を確認していきます。 ■一部機能の削除 一部の機能やツールなどが削除されました。 ただしその殆どは以下のページで説明されているとおり、削除されたものの大半は (過去に使われてはいたけれど) 既に完全に使用されておらず、消されすらせずに放置されていたもの や 別の機能に置き換えられて使われなくなったもの です。  ・ Schedule for Feature Removals 他、squid の内部管理に使用する “Cache Manager” へのアクセス手段の一つ である “cache_object://” という URL スキームが削除されていますが、こちらは使っているとしても別のアクセス手段を使えば問題ないでしょう。  ・ cache_object:// URI Scheme  ・ The Cache Manager その他、一部環境への対応が打ち切られたりしていますが、 これらの機能削除によって影響を受けることはまずない と言ってよさそうです。 ■ヘッダの変更 レスポンス時に使用されていた http ヘッダ “X-Cache” と “X-Cache-Lookup” が、2022年に標準化された http ヘッダ “Cache-Status” に置き換えられました。  ・ RFC 9211: The Cache-Status HTTP Response Header Field squid のレスポンスを利用しており、かつこれらのヘッダを使用していた場合には確認と変更が必要になります。 ■ログ機能の強化 squid が確立した TLS 接続 (SSLBump なども含む) に関する秘密鍵や暗号化などの情報 を、wireshark などのツールで確認できるような ログとして記録する ことができる機能と、この機能を利用するための設定項目 “ tls_key_log ” が追加されました。 ※ このログの情報を利用すれば通信内容を復号できてしまう 可能性があるので、扱いには細心の注意が必要です。  ・ tls_key_log また、設定項目 “ logformat ” に、例えば接続の内部 ID を記録する “transport::>connection_id” など、いくつかの設定が追加されています。  ・ logformat (v6)  ・ logformat (v5) これらのように、ログに関連する機能が強化されています。 ■その他設定項目の変更点 ログ関連以外の設定項目の変更点としては、 キャッシュエントリのヒットごとに整合性の検証をする設定 である “paranoid_hit_validation” という項目が追加されています。  ・ paranoid_hit_validation 他には追加・削除された設定項目はなく 、各設定項目について 設定を行わなかった場合のデフォルト値 も変更はないようです。 ただし、例えばデフォルト値は “設定なし” で変更はありませんが、 設定ファイルの設定がデフォルト値の “設定なし” の場合の 実際の内部的なデフォルト値が変更 されている “sslcrtvalidator_program” や、機能の使用に必要となるインストールオプションが変わっている “loadable_modules” などのように中身に変更が入っているものもあります。  ・ sslcrtvalidator_program (v6)  ・ sslcrtvalidator_program (v5)  ・ loadable_modules (v6)  ・ loadable_modules (v5) このため、バージョンアップという節目を機に、一度現在の設定の状態や意味・意図を見直しておくとよりよいでしょう。 ■最後に 今回は RHEL9 → RHEL10 へのアップグレードに係る、同梱版 squid の変更点を確認しました。 確認してきたとおり、レスポンスヘッダの変更など若干の変更点はありますが基本的な動作へ大きく影響を与える変更はほぼなく、ほとんどの場合は 特に問題なく移行できるはず です。 これは squid に限った話ではありませんが、 バージョンが変わったからと言ってそのソフトウェアの基本的な役割や基本的な動作ががらりと変わることは稀 であり、大体の場合の変更内容は  ・コードの改善 (効率化・高速化や安全性の強化など)  ・不具合や問題点の修正  ・より便利にするための機能追加 (今回の話で言えばログ機能の強化など)  ・標準仕様への準拠 (今回の話で言えばヘッダ変更など)   ・廃止された標準仕様や危険のある仕様などの排除 (例えば、古いバージョンの TLS など) など、利便性の向上か必要に迫られてのものが多い……はずです。 もちろんバージョンアップにより変更が加えられている以上、例えば「安全性の強化により動作がブロックされるようになってしまった」「コード改善により偶然影響を受けてしまった」「廃止された標準仕様を使っていた」……などなど、 バージョンアップ後に想定通りの動作をしてくれなくなってしまうケースが発生する可能性は否定できません 。 このため、バージョンアップの際にはきちんと検証を行なって動作に問題ないかを確認し、問題があれば対応を行なってからバージョンアップを実施するようにしましょう。 (システムの安全・効率的な運用のためには、バージョンアップは必ず必要になってくるものです。対応の手間を嫌わず、きちんと実施するようにしましょう。) ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post RHEL10 移行に際する squid の変更点のお話 first appeared on SIOS Tech. Lab .
はじめに 前回の記事 では、GitLabのコンテナレジストリについて2回にわたり解説しました。リポジトリに加えてレジストリを利用することで、ソースコードとコンテナイメージを一元管理できる点を確認しました。 今回からは、いよいよGitLabのCI/CD機能に焦点を当てます。CI/CDは、開発からデプロイまでの流れを自動化するための基本となる仕組みです。特にGitLabでは、リポジトリ・レジストリ・Runnerを組み合わせることで、シンプルながら強力なパイプラインを構築できます。 本記事では基本編として、ビルド、push、デプロイといったCI/CDにおける基本的な操作をジョブ単位で検証します。まずはシンプルなジョブを一つずつ動かし、パイプラインの仕組みに慣れることを目的とします。次回はこの内容を発展させ、複数のステージを組み合わせたマルチステージパイプラインを解説します。 ゴール GitLab Runnerを利用して簡単なCI/CDパイプラインを実行できるようにする ビルド→push→デプロイの一連の流れを体験する パイプラインの仕組みを理解する 前提条件 本記事では、あらかじめ以下の環境が構築されていることを前提とします。環境の詳細な構築手順については、 環境構築編の記事 をご参照ください。 GitLab(Self-Managed版) Omnibusパッケージを利用してLinuxサーバーにインストールしたGitLabを利用します 無償版であるCommunity Editionを利用します Gitlab Runner OpenShiftクラスター上にデプロイ済みのRunnerを利用して、ジョブを実行します OpenShiftクラスター 閉域環境(インターネット非接続環境)に構築されています GitLab Container Registry コンテナイメージのpush / pullに利用します これらの環境を利用して、GitLab CI/CDパイプラインを実行し、ビルドからデプロイまでの流れを検証できます。また、本記事では以下のようにテスト用のプロジェクトとブランチを用意して検証します。 テスト用プロジェクトの作成 任意の名称の動作確認用プロジェクトを新規作成します。例:test-project このプロジェクト配下で以降のジョブ検証を行います。 ブランチ構成 本記事では、各ジョブを個別のブランチで検証します。 基本の流れはbuild→push→deployですが、deployについては2つの方法(マニフェスト、Helm)をそれぞれ検証する構成にしています。実際の運用ではHelmを利用するケースが多いですが、まずはマニフェスト適用から試すとより理解が深まります。 single-job-build 目的:Dockerイメージのビルド 主要ファイル:.gitlab-ci.yml、Dockerfile single-job-push 目的:既存イメージのタグ名リネーム&レジストリへのpush 主要ファイル:.gitlab-ci.yml single-job-deploy-manifest 目的:oc applyでマニフェストを適用 主要ファイル:.gitlab-ci.yml、deploy.yaml ※deployの検証パターン1 single-job-deploy-helm 目的:Helmチャートを利用したデプロイ 主要ファイル:.gitlab-ci.yml、templates/ディレクトリ、values.yaml、Chart.yaml ※deployの検証パターン2 各ブランチのファイル配置 single-job-build test-project/ ├─ .gitlab-ci.yml ├─ Dockerfile └─ README.md single-job-push test-project/ ├─ .gitlab-ci.yml └─ README.md single-job-deploy-manifest test-project/ ├─ .gitlab-ci.yml ├─ deploy.yaml └─ README.md single-job-deploy-helm test-project/ ├── Chart.yaml ├── templates │   └── deployment.yaml ├── values.yaml └─ README.md 用語説明 本記事で紹介するパイプラインを理解するために、CI/CDに関連する基本用語を簡単におさらいしておきます。 Pipeline(パイプライン) GitLab CI/CDにおける一連の自動化処理の流れを指します。コードのビルド、テスト、デプロイといった複数の処理を順番にまとめたものです。 Stage(ステージ) パイプラインを構成する処理のグループです。たとえば、「build」「test」「deploy」といった大まかな段階をステージとして定義します。ステージは順番に実行されます。 Job(ジョブ) 各ステージの中で実行される具体的な処理の単位です。ビルドのジョブ、デプロイのジョブといった形で定義されます。ジョブは.gitlab-ci.ymlに記述します。 Runner(ランナー) ジョブを実際に実行する役割を担うコンポーネントです。GitLab本体とは別に用意され、指定した環境(Docker、Kubernetes、VMなど)でジョブを動かします。本記事ではOpenShift上にデプロイしたRunnerを利用します。 これらの用語を理解しておくことで、以降のサンプルコードや解説がスムーズに読み進められます。 サンプルコード全体像 今回の検証では、以下の基本的な処理をそれぞれ独立したジョブとして実行し、CI/CDの流れを確認します。 Build:Dockerイメージのビルド Push:GitLab Container Registryへのイメージpush Deploy(マニフェスト):Kubernetesマニフェストを適用してデプロイ Deploy(Helm):Helmチャートを利用してデプロイ まずは全体像をイメージしやすいよう、シンプルな.gitlab-ci.ymlの例を示します。 stages:   - build   - push   - deploy build_job:   stage: build   script:     - echo "Build Docker image" push_job:   stage: push   script:     - echo "Push image to GitLab Registry" deploy_job:   stage: deploy   script:     - echo "Deploy application" 上記は最小限の例であり、実際には各ジョブに詳細な処理(docker build / push、kubectl apply、helm installなど)を記述します。以降のセクションでは、各ジョブを個別に取り上げ、実際のコード例とともに動作を確認していきます。 ジョブ別解説 ここからは、実際に.gitlab-ci.yml にジョブを定義し、CI/CDの流れを確認していきます。 基本の流れはBuild → Push → Deployです。まずはビルドとpushを行い、その後のデプロイ方法として「マニフェスト適用」と「Helm」の2種類を検証します。 Buildジョブ 最初に実行するのはBuildジョブです。ソースコードからDockerイメージを作成し、次のステップで利用できる状態にします。 以下の例では、docker buildを実行してnginxのイメージを作成し、GitLabのレジストリに登録できるようにリポジトリ名をtestに変更したタグ(test:v1.0)を付与しています。 なお、本記事の環境ではOpenShiftクラスターはインターネットと接続できない閉域環境に配置しているため、ベースイメージとなるnginxのイメージはあらかじめGitLabのプロジェクト内のレジストリに格納してあります。 最小のnginxベースのサンプルです。 Dockerfile FROM registry.gitlab.local.example.com/root/test-project/nginx:v1.0 ここでは Docker-in-Docker(dind)でビルドする例を示します。GitLab既定のレジストリ変数(CI_REGISTRY / CI_REGISTRY_IMAGE / CI_REGISTRY_USER / CI_REGISTRY_PASSWORD)を利用します。 このジョブを実行することで、アプリケーションのDockerイメージが生成されます。 .gitlab-ci.yml stages:   - build build_job:   stage: build   tags:     - devsecops-runner   image: registry.gitlab.local.example.com/root/registry-project/docker:28.3.3   services:     - name: registry.gitlab.local.example.com/root/registry-project/docker:28.3.3-dind       alias: docker       entrypoint: ["/bin/sh","-lc"]       command:         - >           cp /etc/gitlab-runner/certs/gitlab.local.example.com.crt /usr/local/share/ca-certificates/ca.crt &&           update-ca-certificates &&           exec dockerd-entrypoint.sh           --tls=false           --host=unix:///var/run/docker.sock           --host=tcp://0.0.0.0:2375   variables:     DOCKER_HOST: tcp://docker:2375     DOCKER_TLS_CERTDIR: ""   before_script:     - cp /etc/gitlab-runner/certs/gitlab.local.example.com.crt /usr/local/share/ca-certificates/ca.crt     - update-ca-certificates || true   script:     - |       echo "Login to $CI_REGISTRY"       docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"       docker build . --tag="${CI_REGISTRY_IMAGE}/test:v1.0" 上記のサンプルコードをGitLabのプロジェクトにpushすると、pushをトリガーとしてパイプラインが起動します。 プロジェクト画面のサイドメニューから[ビルド] > [パイプライン]を選択して移動し、パイプラインの実行結果を確認します。 パイプラインの実行に成功したことを確認できました。 ジョブのログを見ると、.gitlab-ci.yml内で指定した通り、イメージタグのリネームが行われています。 Pushジョブ 次に実行するのはPushジョブです。このジョブでは、あらかじめ検証用プロジェクトのレジストリにpushしておいた既存のnginx:v1.0イメージのリポジトリ名をtest:v1.0に付け替えて、GitLab Container Registryへpushします。 stages:   - push push_only:   stage: push   tags: [devsecops-runner]   image: registry.gitlab.local.example.com/root/registry-project/docker:28.3.3   services:     - name: registry.gitlab.local.example.com/root/registry-project/docker:28.3.3-dind       alias: docker       entrypoint: ["/bin/sh","-lc"]       command:         - >           cp /etc/gitlab-runner/certs/gitlab.local.example.com.crt /usr/local/share/ca-certificates/ca.crt &&           update-ca-certificates &&           exec dockerd-entrypoint.sh           --tls=false           --host=unix:///var/run/docker.sock           --host=tcp://0.0.0.0:2375   variables:     DOCKER_HOST: tcp://docker:2375     DOCKER_TLS_CERTDIR: ""     SOURCE_IMAGE: registry.gitlab.local.example.com/root/test-project/nginx:v1.0   before_script:     - cp /etc/gitlab-runner/certs/gitlab.local.example.com.crt /usr/local/share/ca-certificates/ca.crt     - update-ca-certificates || true   script: |     echo "Login to $CI_REGISTRY"     docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"     echo "Pull source image and push re-tagged"     docker pull "$SOURCE_IMAGE"     docker tag "$SOURCE_IMAGE" "${CI_REGISTRY_IMAGE}/test:v1.0"     docker push "${CI_REGISTRY_IMAGE}/test:v1.0" このジョブでレジストリにpushしたイメージは、後続のデプロイジョブで利用できるようになります。 上記のサンプルコードをGitLabのプロジェクトにpushすると、pushをトリガーとしてパイプラインが起動します。 プロジェクト画面のサイドメニューから[ビルド] > [パイプライン]を選択して移動し、パイプラインの実行結果を確認します。 パイプラインは正常に実行されていることを確認できました。 プロジェクト画面のサイドメニューから[デプロイ] > [コンテナレジストリ]を選択して移動し、レジストリにpushしたイメージが格納されていることを確認します。 ジョブ内で指定した通り、test:v1.0というタグのイメージが保存されていることを確認できました。 Deployジョブ(マニフェスト) ここからはデプロイの検証です。まずはKubernetesのマニフェストを直接oc applyで適用する方法を試します。 ※本記事の環境ではコンテナ基盤としてOpenShiftを利用しているため、クラスター操作のCLIツールとしてocを利用しています。Kubernetes環境を利用している場合は、imageでkubectlを利用可能なイメージを指定し、scriptのデプロイコマンドはkubectl applyを使用してください。 まずはocコマンドを実行できるコンテナイメージをローカルでビルドして、GitLabのレジストリにpushします。 $ podman login registry.redhat.io $ podman pull registry.redhat.io/openshift4/ose-cli-rhel9:v4.18 $ podman login registry.gitlab.local.example.com $ podman tag registry.redhat.io/openshift4/ose-cli-rhel9:v4.18 \   registry.gitlab.local.example.com/root/test-project/oc:4.18 $ podman push registry.gitlab.local.example.com/root/test-project/oc:4.18 デプロイテスト用のマニフェストファイルです。このファイルを作成したデプロイテスト用のブランチにpushしておきます。 deploy.yaml apiVersion: apps/v1 kind: Deployment metadata:   name: myapp   namespace: gitlab-runner-test spec:   replicas: 1   selector:     matchLabels:       app: myapp   template:     metadata:       labels:         app: myapp     spec:       serviceAccountName: gitlab-runner-sa       containers:         - name: nginx           image: registry.gitlab.local.example.com/root/test-project/test:v1.0           imagePullPolicy: IfNotPresent .gitlab-ci.yml stages:   - deploy deploy_manifest:   stage: deploy   tags: [devsecops-runner]   image: registry.gitlab.local.example.com/root/test-project/oc:4.18   before_script:     - oc whoami   script: |     set -euo pipefail     oc apply -f deploy.yaml このジョブを実行すると、deploy.yamlに記載されたリソースがOpenShiftクラスター上に作成されます。 上記のサンプルコードをGitLabのプロジェクトにpushすると、pushをトリガーとしてパイプラインが起動します。 プロジェクト画面のサイドメニューから[ビルド] > [パイプライン]を選択して移動し、パイプラインの実行結果を確認します。 ジョブの実行が成功していることを確認できました。 続いて、クラスターにPodがデプロイされていることを確認します。 deploy.yamlで指定した通り、myappという名前のPodが起動していることを確認できました。 $ oc get pod -n gitlab-runner-test -l app=myapp NAME                    READY   STATUS    RESTARTS   AGE myapp-7d45468b7-24mwn   1/1     Running   0          69m Deployジョブ(Helm) 最後にHelmを使ったデプロイ方法です。Helmを利用することで、複数のマニフェストをまとめて管理することができ、環境ごとの設定差分を柔軟に扱うことができます。 今回の検証では、以下のテスト用のHelmチャートを利用します。 先ほどと同様に、プロジェクト上にHelmテスト用のブランチを作成してHelmチャートを作成したブランチにテスト用のチャートをpushしておきます。 Podが参照するイメージは、Pushジョブでpushしたイメージを利用します。 Chart.yaml apiVersion: v2 name: single-job-deploy-helm-chart description: A Helm chart for a single deployment job type: application version: 0.1.0 appVersion: "1.0" templates/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:   name: single-job-helm-deploy   labels:     app: helm-test-app spec:   replicas: {{ .Values.replicaCount }}   selector:     matchLabels:       app: helm-test-app   template:     metadata:       labels:         app: helm-test-app     spec:       {{- with .Values.imagePullSecrets }}       imagePullSecrets:         {{- toYaml . | nindent 8 }}       {{- end}}       containers:         - name: nginx           image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"           imagePullPolicy: {{ .Values.image.pullPolicy }} values.yaml replicaCount: 1 image:   repository: "registry.gitlab.local.example.com/root/test-project/test"   pullPolicy: IfNotPresent   tag: "v1.0" imagePullSecrets:   - name: gitlab-registry-secret Helmチャートをデプロイできるようにするため、Helm CLIが利用できるジョブPod用のイメージをプロジェクト内のレジストリに格納します。 $ podman pull docker.io/alpine/helm:3 $ podman tag docker.io/alpine/helm:3 \   registry.gitlab.local.example.com/root/test-project/helm:3 $ podman push registry.gitlab.local.example.com/root/test-project/helm:3 .gitlab-ci.yml stages: [deploy] deploy_helm:   stage: deploy   tags: [devsecops-runner]   image: registry.gitlab.local.example.com/root/test-project/helm:3   variables:     RELEASE_NAME: mynginx     NAMESPACE: gitlab-runner-test   before_script:     - |       if [ -n "${KUBECONFIG_DATA:-}" ]; then         mkdir -p ~/.kube         echo "$KUBECONFIG_DATA" | base64 -d > ~/.kube/config         export KUBECONFIG="$HOME/.kube/config"       fi     - helm version   script: |     set -euo pipefail     helm upgrade --install "$RELEASE_NAME" ./chart \       --namespace "$NAMESPACE" \       -f ./values.yaml \       --wait --timeout 180s     helm -n "$NAMESPACE" status "$RELEASE_NAME" Helmを利用したデプロイでは、再実行時もupgrade –installにより差分更新されるため、継続的デプロイ(CD)の仕組みとして有効です。 上記のサンプルコードをGitLabのプロジェクトにpushすると、pushをトリガーとしてパイプラインが起動します。 プロジェクト画面のサイドメニューから[ビルド] > [パイプライン]を選択して移動し、パイプラインの実行結果を確認します。 ジョブの実行に成功していることを確認できました。 続いて、クラスターにPodがデプロイされていることを確認します。 templates/deployment.yamlで指定した通り、helm-test-appという名前のPodが起動していることを確認できました。 $ oc get pod -n gitlab-runner-test -l app=helm-test-app NAME                                      READY   STATUS    RESTARTS   AGE single-job-helm-deploy-6bdbc56bbf-58nv8   1/1     Running   0          68m まとめ 本記事では、GitLab CI/CDの基本的な使い方を確認するために、ビルド→push→デプロイという一連の流れをジョブ単位で検証しました。 BuildジョブでDockerイメージを作成 PushジョブでGitLabコンテナレジストリへ登録 DeployジョブでOpenShiftクラスターにデプロイ(マニフェスト / Helm) ジョブを分けて検証することで、CI/CDの各ステップを個別に把握でき、トラブル発生時の切り分けや理解の定着に役立ちます。これは、後に複数のステージを組み合わせたパイプラインを設計する際の基盤となります。 次回は、今回学んだ各ジョブを組み合わせてマルチステージパイプラインを構築したり、パイプラインの実行管理をする方法について解説します。これにより、より実践的なCI/CDパイプラインを設計できるようになります。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GitLab CI/CD 実践[基本編]:パイプライン作成の基本 first appeared on SIOS Tech. Lab .
はじめに 前回 は、GitLabの画面と主要な機能について解説しました。今回は、GitLabの最も基本的な単位であるプロジェクトに焦点を当てます。GitLabのプロジェクトが提供するユニークな価値と、それを最大限に活用する方法を解説します。 グループとプロジェクトの関係 GitLabのプロジェクトを最大限に活用するには、まずグループの概念を理解することが重要です。グループは、複数のプロジェクトをひとまとめにして管理するためのコンテナです。例えば、「Web開発チーム」というグループを作り、その中に「フロントエンド」「バックエンド」「API」といった複数のプロジェクトを配置することができます。 この階層構造には、次のような大きなメリットがあります。 グループに設定したメンバーのアクセスレベルは、そのグループ内のすべてのプロジェクトに自動的に適用されます。これにより、プロジェクトごとに個別の権限設定を行う手間が省けます。 グループ単位でイシューボードやCI/CD変数、セキュリティポリシーなどを設定できます。複数のプロジェクトにまたがるタスクや、共通の設定を効率的に管理できます。 プロジェクトの機能 GitLabにおけるプロジェクトの役割は開発の最初から最後まで、チームの作業全体を統合管理する場所になります。GitLabのプロジェクトは、以下のすべてを一つの場所に集約しています。 リポジトリ:プロジェクトのすべてのコードを保管する場所です。チームメンバーは、ブランチを切ってコードを編集し、変更履歴をコミットすることで、コードベースの変更を管理します。 マージリクエスト:ブランチの変更を、mainなどの共有ブランチに統合するための機能です。コード統合だけでなく、チームメンバーによるコードレビューや議論、そしてCI/CDパイプラインの実行結果を確認する場所としても機能します。 イシュー管理:タスク、バグ、新機能などの作業を追跡・管理する場所です。イシューに担当者を割り当てたり、進捗状況をボードで可視化したりすることで、プロジェクトの作業を一元的に把握し、効率的な開発をサポートします。 CI/CD:CI/CD(継続的インテグレーション・継続的デリバリー)は、コードのテスト、ビルド、デプロイを自動化する仕組みです。gitlab-ci.ymlという設定ファイルに記述されたパイプラインが自動で実行され、開発プロセスを効率化します。 Wiki:プロジェクトのドキュメントやナレッジを保管する場所です。Gitリポジトリとは別に管理されるため、API仕様書や環境構築手順など、チームで共有すべき情報を整理し、常に最新の状態を保つのに役立ちます。 セキュリティ:コードの脆弱性や機密情報を自動で検知する仕組みです。CI/CDパイプラインに組み込まれたスキャンが、開発の初期段階から潜在的なセキュリティリスクを発見し、安全なコードベースを維持するのに貢献します。 これは、単一のプラットフォームで開発ライフサイクル全体を管理するというGitLabの哲学を体現しています。プロジェクトを作成するだけでコード管理からテスト・デプロイまでを効率的に進めることができます。   プロジェクトを最大限に活用する実践的な方法 GitLabのプロジェクトは、ただコードを置くだけでなく、以下の機能を活用することで、その真価を発揮できます。 ユーザー招待 プロジェクトの「メンバー」から、チームメンバーを招待し、それぞれの役割を設定します。これにより、適切な権限で共同作業を始めることができます。ユーザー招待時には 期限付きのアクセス権 を設定することも可能です。外部ベンダーや短期間だけ関わるメンバーに対して有効で、不要になったあと自動的に権限を失効させることでセキュリティリスクを低減できます。 グループ単位での招待を活用すれば、同じチームメンバーを複数プロジェクトにまとめて参加させることもできます。これにより、効率的かつ統一的にメンバー管理が行えます。 権限設定 GitLabでは、ユーザーに付与する権限(ロール)によってプロジェクトやグループ内での操作範囲を細かくコントロールできます。これにより「誰が何をできるか」を明確にし、誤操作やセキュリティリスクを防ぐことができます。 以下はよく使われる代表的な権限例の一例です。 プロジェクトレベルの権限 各プロジェクトごとに付与される権限で、主に日々の開発作業に関わる役割を定義します。 Owner:プロジェクトの最上位権限を持ち、全ての操作が可能です。プロジェクトの削除や移動も含まれ、最大の権限を持ちます。基本的にはプロジェクトやグループの責任者やシステム管理者に付与されます。 Maintainer:主にプロジェクト管理者として設定変更、保護ブランチ管理、メンバー招待・管理などが可能です。Ownerほどの権限はなく、プロジェクトの設定を統括します。プロジェクトの削除や移動などはできません。 Developer:日常的な開発者権限で、ブランチ作成やコードのプッシュ、マージリクエストの作成・承認などができますが、プロジェクトの設定変更やメンバー管理はできません。 グループレベルの権限 複数のプロジェクトを束ねるグループ単位でも権限を設定できます。グループに属する全プロジェクトに影響するため、組織全体での権限設計に有効です。例えば、あるユーザーをグループでDeveloperとして登録すれば、そのグループ配下のすべてのプロジェクトで開発者権限を持つことになります。逆に、個別のプロジェクト単位で追加の権限を付与することも可能です。 このように、グループとプロジェクト双方の権限設定を適切に使い分けることで、 日常の開発作業はプロジェクト権限で細かくコントロールしつつ、グループ権限で全体の統制を効かせるといった柔軟な運用が可能になります。 公式ドキュメント: https://docs.gitlab.com/user/permissions/ 保護ブランチ main ブランチなど、特定の重要なブランチに対して直接の変更を制限する機能です。通常、開発者は自分のブランチで作業し、レビューを経てからmainブランチに統合します。しかし、誤って直接mainにプッシュしてしまうと、予期せぬバグが本番環境にデプロイされ、システム全体が停止するような大事故につながる可能性があります。 保護ブランチ機能は、誤ってコードがブランチにプッシュされることを防ぎ、開発の安定性を確保できます。 設定 >リポジトリ から設定できます。 wiki コードとは別に、プロジェクト固有のドキュメントを管理できる仕組みです。API仕様書、開発環境のセットアップ手順、リリースノートなど、チームで繰り返し参照する情報を整理するのに最適です。ドキュメントがプロジェクトと一緒に管理されるため、常に最新の情報を共有できます。   開発を始めるまでの準備 開発を始めるとき、まずは以下の準備を行いましょう。 GitLabにログイン済みであることを前提とします。 グループの作成 複数のプロジェクトを管理する予定がある場合は、まずグループを作成します。グループの作成は、GitLabのグループ画面から「新しいグループ」をクリックします。 「グループを作成」をクリックします。 グループ名、表示レベルを設定して「グループの作成」をクリックします。 これでグループの作成は完了です。 グループメンバーの招待 グループの管理>メンバーから、チームメンバーを招待し、適切な権限を付与します。 「メンバーを招待」をクリックします。 今回はメンテナーと開発者を追加したいと思います。 ユーザーとロール、アクセスの有効期限を設定して、「招待」をクリックします。 プロジェクトの作成 作成したグループ内で、新しいプロジェクトを作成します。グループのホーム画面から「新しいプロジェクト」をクリックします。 「空のプロジェクトの作成」をクリックします。 プロジェクト名、表示レベルなどを設定して、「プロジェクトを作成」をクリックします。 今回は、デフォルト設定のままプロジェクトを作成することを前提とします。 表示レベルは、誰がプロジェクトにアクセスできるかを定義します。 非公開 (Private):プロジェクトメンバーだけがアクセスできます。 GitLabで最も推奨される設定です。コードやイシュー、CI/CDパイプラインなど、すべての情報がチーム内のみで共有されます。 内部 (Internal):ログインしているすべてのユーザーがアクセスできます。 GitLabインスタンスの全ユーザー(例:社内の全従業員)がプロジェクトを閲覧できます。社内向けの共通ライブラリやドキュメントを共有するのに便利です。 公開 (Public):ログインしていないユーザーも含め、誰でもアクセスできます。 オープンソースプロジェクトなど、広く一般に公開したい場合に選択します。機密情報や社内情報を含むプロジェクトでは使用しないでください。 通常、機密情報が含まれるプロジェクトでは非公開を、ログイン済みユーザーなら誰でも参照可能なプロジェクトでは内部を選択することを強く推奨します。 プロジェクトの設定には、リポジトリの初期状態を決める追加オプションがあります。 デフォルトでは、「READMEを作成する」にのみチェックが入っています。これは、プロジェクト作成と同時に、簡単な説明が記載されたREADME.mdファイルが自動で作成される設定です。 プロジェクトの画面が表示されたら作成完了です。 ブランチ保護の設定 プロジェクトの初期設定をしていきます。まずはブランチ保護の設定です。 ブランチ戦略にもよりますが、ここではmainブランチへの直接のプッシュを制限し、マージの権限設定をします。 設定>リポジトリ から、保護ブランチを開きます。 mainブランチに対してのマージを許可するロールとプッシュを許可するロールを選択します。強制プッシュを許可するかも選択します。 developブランチなどにも保護ブランチ設定をしたい場合は、「保護ブランチを追加する」をクリックします。 Wikiの作成 続いて、Wikiを作成しておきます。こちらは必須ではないですが、プロジェクトの概要やルールなどをWikiに記載し、チームで共有することで円滑な開発が可能になります。環境構築の手順、API仕様書などがあればWikiに追加しておきましょう。 計画>Wikiから、「最初のページを作成」をクリックします。 タイトルや内容を記述し、「ページを作成」をクリックします。 ページが表示されたら完了です。 これでプロジェクトの準備は完了です。 あとは、リモートプロジェクトをローカル環境に取得したら、ローカルでの開発作業を開始できます。リモートプロジェクトのクローンは第4回のブログを参考にしてみてください。 まとめ 今回の記事では、GitLabのプロジェクトについて深く掘り下げました。GitLabのプロジェクトは、コード、イシュー、CI/CD、Wiki、セキュリティといったすべてを統合していました。 さらに、グループを活用することで、複数のプロジェクトを効率的に管理し、メンバーの権限設定を簡素化できることを解説しました。そして、実際にプロジェクトの作成から、保護ブランチやWikiの設定といった初期準備までを実践しました。 次回は、GitLabのCI/CDに焦点を当てます。今回設定したプロジェクト上で、コードのビルド、テスト、デプロイといった一連のプロセスを自動化する方法を、簡単に解説します。 参考文献 https://docs.gitlab.com/user/group/ https://docs.gitlab.com/user/project/members/sharing_projects_groups/ https://docs.gitlab.com/user/permissions/ https://docs.gitlab.com/user/project/ https://docs.gitlab.com/user/project/working_with_projects/ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Git & GitLab 入門 (7) ~Git マスターへの道~「GitLabのプロジェクトについて」 first appeared on SIOS Tech. Lab .
はじめに 「 構築編 」では、非接続環境のOpenShiftクラスターにOPA/Gatekeeperを導入する基本的な手順を解説しました。しかし、Gatekeeperは、適切なポリシーを定義して適用することで初めて動作します。 この記事では、実際にポリシーの作成と適用を行い、OpenShiftの運用ルールを自動化する方法を解説します。具体的には、リソースの妥当性を検証するバリデーションと、リソースを自動的に変更するミューテーションという、Gatekeeperの二つの主要な動作について深く掘り下げていきます。 前提条件 バージョン:Gatekeeper v3.20.0 Gatekeeperが非接続環境のOpenShiftクラスターに導入済みであること 参考: OPA/Gatekeeperで始める安心OpenShift運用:構築編 ポリシーの適用範囲 Gatekeeperのポリシーは、ConstraintTemplateとConstraintsという2つのカスタムリソース(CRD)で構成されます。 ConstraintTemplate:ポリシーのひな形を定義します。どのようなリソースに、どのような条件でポリシーを適用するかを定義します。これはクラスター全体に適用されるクラスタースコープのリソースです。 Constraints: ConstraintTemplateで定義されたひな形を使い、実際の適用ルールを定義します。例えば、「k8srequiredlabels」というテンプレートを使って、「すべてのPodにappラベルを必須とする」という具体的なルールを作成します。デフォルトではクラスター内のすべてのリソースにポリシーを適用しますが、特定のNamespaceにのみ適用することも可能です。 ポリシーの適用動作 GatekeeperのConstraintリソースでは、enforcementActionというフィールドを使用して、ポリシーに違反したリソースに対してどのようなアクションを実行するかを制御できます。これにより、単にデプロイを拒否するだけでなく、より柔軟な運用が可能になります。 enforcementActionでは、3つのアクションを選択できます。 deny (デフォルト):ポリシーに違反したリソースの作成や更新を拒否し、デプロイを停止します。 dryrun:ポリシー違反を記録するだけで、リソースのデプロイはそのまま許可します。 warn:ポリシー違反をログに警告として出力しますが、デプロイはブロックしません。 これらのアクションを使い分けることで、ポリシーをより柔軟に運用できます。 dryrunはポリシーのテストで有効です。新しいポリシーを適用する前にdryrunモードで試すことで、既存のリソースや今後のデプロイでどのような違反が発生するかを事前に把握できます。これにより、環境に予期せぬ影響を与えることなく、段階的にポリシーを導入することが可能になります。 warnは段階的なルール適用に役立ちます。例えば、「latestタグの使用は非推奨です」といった警告を出しつつ、Podのデプロイ自体は許可するといった運用が可能です。このようにして、運用を徐々に改善していくことができます。 バリデーションポリシー リソースがポリシーに適合しているか検証し、違反していればリクエストを拒否します。 これは、ポリシーに違反するリソースがクラスターにデプロイされるのを未然に防ぐ動作です。 有効なシーン 特権コンテナやホストのネットワークへのアクセスを許可するPodのデプロイを拒否し、セキュリティリスクを排除します。 すべてのリソースに特定のラベルやアノテーションを必須とすることで、監査や管理を容易にします。 ここでは、よく使われるバリデーションの具体例として、必須ラベルの強制とルートファイルシステムの書き込み禁止を紹介します。 必須ラベルの強制 以下のポリシーは、stagingネームスペースのPodにenvのラベルを必須とします。 ConstraintTemplateの作成 apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata:   name: k8srequiredlabels spec:   crd:     spec:       names:         kind: K8sRequiredLabels       validation:         openAPIV3Schema:           type: object           properties:             labels:               type: array               items:                 type: string   targets:     - target: admission.k8s.gatekeeper.sh       rego: |         package k8srequiredlabels         violation[{"msg": msg, "details": {"missing_labels": missing}}] {           provided := {label | input.review.object.metadata.labels[label]}           required := {label | label := input.parameters.labels[_]}           missing := required - provided           count(missing) > 0           msg := sprintf("you must provide labels: %v", [missing])         } Constraintsの作成 apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sRequiredLabels metadata:   name: must-have-required-labels spec:   match:     namespaces: ["staging"]     kinds:       - apiGroups: [""]         kinds: ["Pod"]   parameters:     labels: [“env”] ポリシーをゼロから書くのは大変な作業ですが、Gatekeeperには公式に提供されている便利なポリシーライブラリがあります。 公式ウェブサイト: https://open-policy-agent.github.io/gatekeeper-library/website/ このライブラリを活用することで、目的に合ったポリシーが存在する場合はポリシーの作成にかかる時間を大幅に短縮でき、より早く安全なOpenShift運用を始めることができます。 作成したConstraintTemplateとConstraintsのマニフェストファイルを適用します。 $ oc project gatekeeper-system $ oc apply -f ConstraintTemplate_k8srequiredlabels.yaml $ oc apply -f Constraints_k8srequiredlabels.yaml 作成したポリシーを確認します。 $ oc get constrainttemplate NAME                         AGE k8srequiredlabels            65s ポリシー違反が見つかった場合は、TOTAL-VIOLATIONSのカウントが増えていきます。 $ oc get constraints NAME                        ENFORCEMENT-ACTION   TOTAL-VIOLATIONS must-have-required-labels   deny                 0 検証用のネームスペースを作成します。 $ oc create namespace staging 以下の指定したラベルを持たないDeploymentのバリデーションを確認してみます。 apiVersion: apps/v1 kind: Deployment metadata:   name: gatekeeper-test   namespace: staging spec:   replicas: 1   selector:     matchLabels:       name: gatekeeper-test   template:     metadata:       name: gatekeeper-test       labels:         name: gatekeeper-test     spec:       nodeSelector:         "kubernetes.io/os": linux       containers:         - name: ubi-container           image: registry.redhat.io/ubi8/ubi:latest           command:             - "/bin/sh"             - "-c"             - "echo 'Pod is running with UBI...' && sleep infinity" $ oc apply -f Deployment_k8srequiredlabels.yaml ポリシーの適用スコープはPodのみなので、Deploymentオブジェクト自体は作成されるが、その下位のPodがAdmissionWebhookに拒否されるため、Podは起動できません。 Podがデプロイされているか確認してみます。 $ oc get all -n staging Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+ NAME                              READY   UP-TO-DATE   AVAILABLE   AGE deployment.apps/gatekeeper-test   0/1     0            0           7m5s NAME                                         DESIRED   CURRENT   READY   AGE replicaset.apps/gatekeeper-test-766b7bdb6b   1         0         0       7m5s デプロイされていなかったので、replicasetのdescribeを確認してみます。 $ oc describe replicaset gatekeeper-test-766b7bdb6b -n staging … Events:   Type     Reason        Age                   From                   Message   ----     ------        ----                  ----                   -------   Warning  FailedCreate  5m34s (x18 over 11m)  replicaset-controller  Error creating: admission webhook "validation.gatekeeper.sh" denied the request: [must-have-required-labels] you must provide labels: {"env"} デプロイが許可されなかったことがわかります。 次に、指定したラベルを付与してデプロイしてみます。 apiVersion: apps/v1 kind: Deployment metadata:   name: gatekeeper-test   namespace: staging spec:   replicas: 1   selector:     matchLabels:       name: gatekeeper-test   template:     metadata:       name: gatekeeper-test       labels:         name: gatekeeper-test         env: staging     spec:       nodeSelector:         "kubernetes.io/os": linux       containers:         - name: ubi-container           image: registry.redhat.io/ubi8/ubi:latest           command:             - "/bin/sh"             - "-c"             - "echo 'Pod is running with UBI...' && sleep infinity" $ oc apply -f Deployment_k8srequiredlabels.yaml Podがデプロイされているか確認してみます。 $ oc get all -n staging Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+ NAME                                   READY   STATUS    RESTARTS   AGE pod/gatekeeper-test-6b5f4f8d74-5cngp   1/1     Running   0          3s NAME                              READY   UP-TO-DATE   AVAILABLE   AGE deployment.apps/gatekeeper-test   0/1     0            0           7m5s NAME                                         DESIRED   CURRENT   READY   AGE replicaset.apps/gatekeeper-test-6b5f4f8d74   1         1         1       3s replicaset.apps/gatekeeper-test-766b7bdb6b   1         0         0       7m5s これでデプロイが成功しました! 次の検証のためにConstraints、Deploymentは削除しておきます。 $ oc delete -f Deployment_k8srequiredlabels.yaml $ oc apply -f Constraints_k8srequiredlabels.yaml ルートファイルシステムの書き込み禁止 セキュリティ上の理由から、PodのsecurityContext.readOnlyRootFilesystem を true に設定することを強制します。 ConstraintTemplateの作成 apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata:   name: k8spspreadonlyrootfilesystem   annotations:     metadata.gatekeeper.sh/title: "Read Only Root Filesystem"     metadata.gatekeeper.sh/version: 1.1.1     description: >-       Requires the use of a read-only root file system by pod containers.       Corresponds to the `readOnlyRootFilesystem` field in a       PodSecurityPolicy. For more information, see       https://kubernetes.io/docs/concepts/policy/pod-security-policy/#volumes-and-file-systems spec:   crd:     spec:       names:         kind: K8sPSPReadOnlyRootFilesystem       validation:         # Schema for the `parameters` field         openAPIV3Schema:           type: object           description: >-             Requires the use of a read-only root file system by pod containers.             Corresponds to the `readOnlyRootFilesystem` field in a             PodSecurityPolicy. For more information, see             https://kubernetes.io/docs/concepts/policy/pod-security-policy/#volumes-and-file-systems           properties:             exemptImages:               description: >-                 Any container that uses an image that matches an entry in this list will be excluded                 from enforcement. Prefix-matching can be signified with `*`. For example: `my-image-*`.                It is recommended that users use the fully-qualified Docker image name (e.g. start with a domain name)                 in order to avoid unexpectedly exempting images from an untrusted repository.               type: array               items:                 type: string   targets:     - target: admission.k8s.gatekeeper.sh       code:       - engine: K8sNativeValidation         source:           variables:           - name: containers             expression: 'has(variables.anyObject.spec.containers) ? variables.anyObject.spec.containers : []'           - name: initContainers             expression: 'has(variables.anyObject.spec.initContainers) ? variables.anyObject.spec.initContainers : []'           - name: ephemeralContainers             expression: 'has(variables.anyObject.spec.ephemeralContainers) ? variables.anyObject.spec.ephemeralContainers : []'           - name: exemptImagePrefixes             expression: |               !has(variables.params.exemptImages) ? [] :                 variables.params.exemptImages.filter(image, image.endsWith("*")).map(image, string(image).replace("*", ""))           - name: exemptImageExplicit             expression: |               !has(variables.params.exemptImages) ? [] :                  variables.params.exemptImages.filter(image, !image.endsWith("*"))           - name: exemptImages             expression: |               (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container,                 container.image in variables.exemptImageExplicit ||                 variables.exemptImagePrefixes.exists(exemption, string(container.image).startsWith(exemption))).map(container, container.image)           - name: badContainers             expression: |               (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container,                 !(container.image in variables.exemptImages) &&                  (!has(container.securityContext) ||                 !has(container.securityContext.readOnlyRootFilesystem) ||                 container.securityContext.readOnlyRootFilesystem != true)               ).map(container, container.name)           validations:           - expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.badContainers) == 0'             messageExpression: '"only read-only root filesystem container is allowed: " + variables.badContainers.join(", ")'                   - engine: Rego         source:           rego: |             package k8spspreadonlyrootfilesystem             import data.lib.exclude_update.is_update             import data.lib.exempt_container.is_exempt             violation[{"msg": msg, "details": {}}] {                 # spec.containers.readOnlyRootFilesystem field is immutable.                 not is_update(input.review)                 c := input_containers[_]                 not is_exempt(c)                 input_read_only_root_fs(c)                 msg := sprintf("only read-only root filesystem container is allowed: %v", )             }             input_read_only_root_fs(c) {                 not has_field(c, "securityContext")             }             input_read_only_root_fs(c) {                 not c.securityContext.readOnlyRootFilesystem == true             }             input_containers {                 c := input.review.object.spec.containers[_]             }             input_containers {                 c := input.review.object.spec.initContainers[_]             }             input_containers {                 c := input.review.object.spec.ephemeralContainers[_]             }             # has_field returns whether an object has a field             has_field(object, field) = true {                 object[field]             }           libs:             - |               package lib.exclude_update               is_update(review) {                   review.operation == "UPDATE"               }             - |               package lib.exempt_container               is_exempt(container) {                   exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", [])                   img := container.image                   exemption := exempt_images[_]                   _matches_exemption(img, exemption)               }               _matches_exemption(img, exemption) {                   not endswith(exemption, "*")                   exemption == img               }               _matches_exemption(img, exemption) {                   endswith(exemption, "*")                   prefix := trim_suffix(exemption, "*")                   startswith(img, prefix)               } Constraintsの作成 apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sPSPReadOnlyRootFilesystem metadata:   name: psp-readonlyrootfilesystem spec:   match:     namespaces: ["staging"]     kinds:       - apiGroups: [""]         kinds: ["Pod"] 作成したConstraintTemplateとConstraintsのマニフェストファイルを適用します。 $ oc apply -f ConstraintTemplate_readonlyrootfilesystem.yaml $ oc apply -f Constraints_readonlyrootfilesystem.yaml 作成したポリシーを確認します。 $ oc get constrainttemplate NAME                         AGE k8spspreadonlyrootfilesystem   19s $ oc get constraints NAME                                                                                ENFORCEMENT-ACTION   TOTAL-VIOLATIONS k8spspreadonlyrootfilesystem.constraints.gatekeeper.sh/psp-readonlyrootfilesystem   deny                 0 以下のルートファイルシステムを読み取り専用にする設定を入れていないDeploymentのバリデーションを確認してみます。 apiVersion: apps/v1 kind: Deployment metadata:   name: gatekeeper-test   namespace: staging spec:   replicas: 1   selector:     matchLabels:       name: gatekeeper-test   template:     metadata:       name: gatekeeper-test       labels:         name: gatekeeper-test     spec:       nodeSelector:         "kubernetes.io/os": linux       containers:         - name: ubi-container           image: registry.redhat.io/ubi8/ubi:latest           command:             - "/bin/sh"             - "-c"             - "echo 'Pod is running with UBI...' && sleep infinity" $ oc apply -f Deployment_readonlyrootfilesystem.yaml ポリシーの適用スコープはPodのみなので、Deploymentのデプロイは成功します。 Podがデプロイされているか確認してみます。 $ oc get all -n staging Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+ NAME                              READY   UP-TO-DATE   AVAILABLE   AGE deployment.apps/gatekeeper-test   0/1     0            0           5s NAME                                        DESIRED   CURRENT   READY   AGE replicaset.apps/gatekeeper-test-58f8db6f8   1         0         0       5s デプロイされていなかったので、replicasetのdescribeを確認してみます。 $ oc describe replicaset gatekeeper-test-58f8db6f8 -n staging … Events:   Type     Reason        Age                 From                   Message   ----     ------        ----                ----                   -------   Warning  FailedCreate  14s (x15 over 96s)  replicaset-controller  Error creating: admission webhook "validation.gatekeeper.sh" denied the request: [psp-readonlyrootfilesystem] only read-only root filesystem container is allowed: ubi-container デプロイが許可されなかったことがわかります。 次に、ルートファイルシステムを読み取り専用にする設定を入れてデプロイしてみます。 apiVersion: apps/v1 kind: Deployment metadata:   name: gatekeeper-test   namespace: staging spec:   replicas: 1   selector:     matchLabels:       name: gatekeeper-test   template:     metadata:       name: gatekeeper-test       labels:         name: gatekeeper-test         env: staging     spec:       nodeSelector:         "kubernetes.io/os": linux       containers:         - name: ubi-container           image: registry.redhat.io/ubi8/ubi:latest           command:             - "/bin/sh"             - "-c"             - "echo 'Pod is running with UBI...' && sleep infinity"           securityContext:             readOnlyRootFilesystem: true $ oc apply -f Deployment_readonlyrootfilesystem.yaml $ oc get all -n staging Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+ NAME                                   READY   STATUS    RESTARTS   AGE pod/gatekeeper-test-5d6ff97778-nkrns   1/1     Running   0          8s NAME                              READY   UP-TO-DATE   AVAILABLE   AGE deployment.apps/gatekeeper-test   1/1     1            1           4m5s NAME                                         DESIRED   CURRENT   READY   AGE replicaset.apps/gatekeeper-test-58f8db6f8    0         0         0       4m5s replicaset.apps/gatekeeper-test-5d6ff97778   1         1         1       8s これでデプロイが成功しました! 次の検証のためにConstraints、Deploymentは削除しておきます。 $ oc delete -f Deployment_readonlyrootfilesystem.yaml $ oc delete -f Constraints_readonlyrootfilesystem.yaml ミューテーションポリシー リソースのリクエストを、ポリシーに基づいて自動的に変更します。 これは、ユーザーが明示的に設定しなくても、必要な設定を自動で追加する動作です。 ミューテーションは以下の4種のCRDを使用して定義されます。今回は、ssign(メタデータ以外を変更する)を使います。 AssignMetadata:リソースのmetadataセクション(例:ラベルやアノテーション)を変更する際に使います。例えば、新しいPodがデプロイされる際に、自動でapp: my-appというラベルを追加することができます。 Assign:metadataセクション以外のリソースの変更を定義します。例えば、コンテナに環境変数を追加したり、リソース制限(limitsやrequests)のデフォルト値を設定したりする場合に利用します。 ModifySet:リスト形式のデータ(argumentsやvolumesなど)に対して、要素の追加や削除を行う際に使います。例えば、すべてのコンテナに特定のコマンド引数を自動で追加することができます。 AssignImage:コンテナイメージの文字列を自動で変更する際に使います。これは、イメージのタグを自動的に最新バージョンに更新したり、内部レジストリのドメイン名に書き換えたりする場合に便利です。 有効なシーン: リソース管理 PodにCPUやメモリのリソース制限が設定されていない場合、自動的にデフォルト値を追加し、クラスターのリソース枯渇を防ぎます。 しかし、この機能には注意が必要です。ミューテーションはマニフェストを自動的に変更するため、Gitリポジトリ(IaC)と実際のクラスター間で差分が発生します。例えば、ArgoCDのようなGitOpsツールでは、この差分が原因で「Out of Sync」と表示され、予期せぬ競合が起こる可能性があります。 このため、開発環境では利便性を優先し、自動変更を適用し、本番環境では監査や透明性を確保するため、マニフェストに設定を明記するなど、環境に合わせて適切に利用することが重要です。 運用効率化 すべてのPodに特定のサービスメッシュ用のサイドカーコンテナを自動で注入する、といった運用を効率化できます。 ただし、本番環境におけるサイドカー注入は、Istioをはじめとするサービスメッシュでは専用のAdmission Controllerによって行われることが一般的です。具体的には、Istioの公式ドキュメントにあるように、特定のNamespaceにラベルを付与することで自動注入が有効になり、APIサーバーのAdmission WebhookがPod作成時にサイドカーを挿入します。 https://istio.io/latest/docs/setup/additional-setup/sidecar-injection/ 一方で、GatekeeperのMutation機能によるサイドカー注入は、主にポリシー検証の目的で用いられています。Gatekeeper公式ドキュメントによると、このMutationはIstioのサイドカー注入後のPodに似せたモックPodを作成してポリシーを検証するために利用されるため、本番環境で直接IstioのサイドカーをMutationで注入するケースは想定されていません。 https://open-policy-agent.github.io/gatekeeper/website/docs/expansion/ したがって、サービスメッシュのサイドカー注入をGatekeeperのMutationで自動化するユースケースは主に検証用途であり、本番運用ではIstioなどの専用Admission Controllerを利用するのが一般的です。 リソース制限の自動追加 以下のYAMLは、Podにリソース制限(limits.cpuとlimits.memory)が設定されていない場合、自動的に値を挿入します。 Mutationsの作成 apiVersion: mutations.gatekeeper.sh/v1 kind: Assign metadata:   name: pod-resource-limits-default spec:   applyTo:   - groups: [""]     versions: ["v1"]     kinds: ["Pod"]   match:     scope: Namespaced     namespaces: ["staging"]   location: "spec.containers[name:*].resources"    parameters:     assign:       value:         limits:           cpu: "500m"           memory: "256Mi"         requests:           cpu: "250m"           memory: "128Mi" 作成したMutationsのマニフェストファイルを適用します。 $ oc apply -f Mutations_pod-resource-limits-default.yaml 作成したミューテーションを確認します。 $ oc get assign NAME                          AGE pod-resource-limits-default   33s 以下のリソース制限を設定していないDeploymentのミューテーションを確認してみます。 apiVersion: apps/v1 kind: Deployment metadata:   name: gatekeeper-test   namespace: staging spec:   replicas: 1   selector:     matchLabels:       name: gatekeeper-test   template:     metadata:       name: gatekeeper-test       labels:         name: gatekeeper-test     spec:       nodeSelector:         "kubernetes.io/os": linux       containers:         - name: ubi-container           image: registry.redhat.io/ubi8/ubi:latest           command:             - "/bin/sh"             - "-c"             - "echo 'Pod is running with UBI...' && sleep infinity" $ oc apply -f Deployment_mutation.yaml 作成したPodを確認します。 $ oc get pod -n staging NAME                              READY   STATUS    RESTARTS   AGE gatekeeper-test-58f8db6f8-96ghh   1/1     Running   0          16s Podにリソース制限が設定されていることを確認します。 $ oc describe pod gatekeeper-test-58f8db6f8-96ghh -n staging … Containers:   ubi-container:   …   Limits:     cpu:     500m     memory:  256Mi   Requests:     cpu:        250m     memory:     128Mi … このように、マニフェストファイルに記載していなくてもリソース制限が設定されます。 継続的なポリシーチェックについて Gatekeeperは、Admission Webhookによるリアルタイムなチェックだけでなく、既存のリソースに対する継続的な監査も行います。 Gatekeeperのコントローラーは、定期的にクラスター内のすべてのリソースをスキャンし、既存のリソースがポリシーに適合しているかをチェックします。監査の結果、ポリシー違反が発見された場合、Constraintsリソースのstatusフィールドにその情報が記録されます。以下のコマンドで確認することができます。 $ oc describe <ConstraintTemplateで定義したCRD名> <constrains名> 例:$ oc describe k8srequiredlabels must-have-required-labels これにより、ポリシーを適用する前にデプロイされたリソースや、ポリシーが変更された後に違反状態になったリソースを特定し、修正することができます。 まとめ この記事では、OPA/Gatekeeperのバリデーションとミューテーションという二つの主要なポリシー適用動作について解説しました。 バリデーションは、運用ルールの自動化・強制適用に役立ちます。例えば、デプロイ前にPodに特定のラベルが付いているか、セキュリティ設定が適切かなどを自動でチェックし、違反を未然に防ぎます。これにより、手作業による確認ミスをなくし、クラスターの健全性を保つことができます。 ミューテーションは、必要な設定を自動で修正・追加し、開発者の負担を軽減に貢献します。例えば、リソース制限が設定されていないPodに自動でデフォルト値を設定したり、アプリケーションのPodに監視用のサイドカーコンテナを自動で注入したりすることができます。 これらのポリシーを導入することで、以下のような利用シーンでの課題を解決できます。 セキュリティ: ルートファイルシステムの読み取り専用の設定を強制し、コンテナイメージが実行時に書き込まれるのを防ぎます。 コンプライアンス: すべてのアプリケーションに、セキュリティ監査やコスト管理に必要なラベルを強制します。 運用: 開発チームが設定を忘れても、Podに適切なリソース制限が自動で適用されるため、リソース枯渇によるサービス停止を防げます。 参考文献 https://open-policy-agent.github.io/gatekeeper/website/docs/ https://open-policy-agent.github.io/gatekeeper-library/website/ https://istio.io/latest/docs/setup/additional-setup/sidecar-injection/ https://open-policy-agent.github.io/gatekeeper/website/docs/expansion/ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OPA/Gatekeeperで始める安心OpenShift運用:設定編 first appeared on SIOS Tech. Lab .
はじめに 前回の記事 ではGitLab Container Registryの基本的な設定と利用方法を解説しました。 今回は、GitLabに保存したイメージの公開範囲を制御したり、ガベージコレクションでストレージ利用を最適化するなど、運用で役立つ実践的な設定方法を紹介します。 可視性の設定 GitLabにおける「可視性(Visibility)」とは、プロジェクトや関連リソースに誰がアクセスできるかを制御する仕組みを指します。例えば、「公開」に設定すればGitLabのアカウントがないユーザーも含めて誰でも閲覧可能になり、「非公開」に設定すれば招待されたメンバーしか利用できません。 GitLabではこの可視性をプロジェクト単位とレジストリ単位の両方で設定することができます。つまり、ソースコードとコンテナイメージで異なる公開範囲を持たせることが可能です。両者を組み合わせることで「コードは公開するが、イメージは限定公開」といった柔軟な運用が可能となります。 以下ではまずプロジェクトの可視性について整理し、その後にContainer Registry特有の可視性設定を紹介します。 プロジェクトの可視性 プロジェクト全体の可視性は [設定] > [一般] > [可視性、プロジェクトの機能、権限] で変更可能です。 可視性レベル アクセス可能ユーザー 利用用途 公開 全てのユーザー(非ログインユーザーを含む) OSS公開、社外への配布 内部 GitLabにログイン済みのユーザー全員 社内全体で共有 非公開 プロジェクトのメンバーのみ 機密性の高いプロジェクト レジストリの可視性 各プロジェクトに紐づくコンテナレジストリも独自に公開範囲を設定可能です。 選択肢は以下の2種類です: アクセスできる人すべて(Everyone With Access、デフォルト): プロジェクトの可視性に従い、アクセス権を持つすべてのユーザーが利用可能 プロジェクトメンバーのみ(Only Project Members): 招待されたメンバーのみ利用可能 イメージの可視性はプロジェクト設定画面の[一般]を選択し、[コンテナレジストリ]の欄で設定することができます。 このプロジェクト可視性とレジストリ可視性を組み合わせることで、コンテナイメージに対する実際のアクセス範囲が決まります。 可視性の組み合わせとユースケース例 プロジェクト可視性 レジストリ可視性 イメージ取得可能ユーザー 主なユースケース 公開 アクセスできる人すべて 誰でも(非ログインユーザー含む) OSSライブラリの公式コンテナイメージ、学習教材用のサンプルアプリイメージ 公開 プロジェクトメンバーのみ プロジェクトメンバーのみ ソースコードはGitLab上で公開しつつ、商用サービス向けのビルド済み実行イメージは顧客限定で配布 内部 アクセスできる人すべて 社内ユーザー全員(Guest 以上) 社内共通のベースイメージ配布(Python/Java ランタイムなど) 内部 プロジェクトメンバーのみ プロジェクトメンバーのみ 特定部門やチーム限定のアプリイメージ 非公開 アクセスできる人すべて プロジェクトメンバー(Reporter 以上) 社外非公開の業務システム、クライアント案件専用イメージ 非公開 プロジェクトメンバーのみ プロジェクトメンバー(Reporter 以上) 高度なセキュリティが求められる分野のイメージ(金融・医療・政府系など) ※公開設定を選ぶ場合は、情報漏洩が起きないように権限やイメージの内容を十分確認してください。 ガベージコレクション GitLabのレジストリでイメージタグを削除しても、ストレージ容量はすぐには解放されません。タグが外れたイメージは「未タグ付き(Untagged)」として残るためです。 この問題を解決するのが ガベージコレクション(GC) です。 ガベージコレクションはGitLabの管理者がサーバー上でCLIコマンド(gitlab-ctl registry-garbage-collect)を実行することで動作します。 CI/CDで頻繁にイメージをビルド・pushする環境では、レジストリに不要なイメージが蓄積しやすいため、定期的にガベージコレクションを実施することでストレージを効率的に利用できます。 前提 gitlab-ctl registry-garbage-collectはメタデータをオブジェクトストレージに保存している場合のみ有効。 メタデータデータベース方式 (GitLabの新しいレジストリ方式)を有効にしている場合、このコマンドは使えず、代わりにオンラインGCが利用可能。 この機能はSelf-Managed版(Omnibus / Helm)限定 であり、GitLab.com(SaaS)では利用不可。 未参照レイヤーと未タグ付きマニフェストの違い ガベージコレクションが扱う対象は2種類あります。 未参照レイヤー(unreferenced layers) どのイメージマニフェストからも参照されていないレイヤー デフォルトのガベージコレクションで削除対象になる pullできず、純粋に不要なデータ 未タグ付きマニフェスト(untagged manifests) タグは削除されたが、ダイジェスト(@sha256:…)を指定すればpull可能 再タグ付けも可能で、再びGitLab UIやAPIに表示される デフォルトでは削除されず、ガベージコレクション実行コマンド(gitlab-ctl registry-garbage-collect)に-m オプションを付けた場合のみ削除される このため -m を付けると「未タグ付きイメージ」も消えるため、過去のdigest pullや再利用ができなくなります。実行前に十分注意してください。 基本の実行方法 ガベージコレクションは、GitLabサーバー上でgitlab-ctl registry-garbage-collectコマンドを実行して行います。 # 未参照レイヤーを削除 $ sudo gitlab-ctl registry-garbage-collect デフォルトでは未参照レイヤーのみ削除され、未タグ付きのマニフェストは残ります。 未タグ付きイメージも含めて完全に削除したい場合は、同じコマンドに -m オプションを指定して実行します。 # 未タグ付きイメージも含め削除 $ sudo gitlab-ctl registry-garbage-collect -m ※-mは破壊的な操作で元に戻せません。実行前に必ずバックアップを取得することを推奨します。 レジストリのダウンタイムと回避方法 gitlab-ctl registry-garbage-collectコマンドでガベージコレクションを行うと、処理前にレジストリが停止し、終了後に再起動されます。つまり、基本的にダウンタイムが発生することとなります。 停止を避けたい場合は、レジストリを「読み取り専用モード」に切り替え、GitLabに含まれるregistryバイナリ(通常は/opt/gitlab/embedded/bin/registry)を直接実行してガベージコレクションを行います。 /etc/gitlab/gitlab.rbに以下を設定し gitlab-ctl reconfigureを実行 registry['storage'] = {   'filesystem' => { 'rootdirectory' => "<レジストリのストレージパス>" },   'maintenance' => { 'readonly' => { 'enabled' => true } } } ガベージコレクションを実行 # 未参照レイヤーのみ削除 $ sudo /opt/gitlab/embedded/bin/registry garbage-collect /var/opt/gitlab/registry/config.yml # 未タグ付きも削除 $ sudo /opt/gitlab/embedded/bin/registry garbage-collect -m /var/opt/gitlab/registry/config.yml 読み取り専用モードをfalseに戻して再度 gitlab-ctl reconfigureを実行 registry['storage'] = {   'filesystem' => { 'rootdirectory' => "<レジストリのストレージパス>" },   'maintenance' => { 'readonly' => { 'enabled' => false } } } ガベージコレクション実行中でもイメージのpullは可能ですがpushは不可となります。 まとめ 今回は、GitLab Container Registryを実務で活用するための応用設定として「可視性の制御」と「ガベージコレクション」を紹介しました。特にガベージコレクションはストレージ効率を維持する上で重要ですが、Self-Managed版限定の管理者作業であり、ダウンタイムやオプション指定に注意が必要です。 2回にわたり解説したとおり、GitLabはソースコード管理だけでなく、コンテナイメージの管理機能も備えています。コードとイメージを一元管理することで、CI/CD環境をシンプルかつ効率的に構成できます。 次回からはGitLab CI/CD編として、レジストリと連携したパイプライン構築について解説していきます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GitLab Container Registry: 応用編(可視性設定とガベージコレクション) first appeared on SIOS Tech. Lab .