TECH PLAY

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

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

546

こんにちは、OSSよろず相談室の神崎です。 今回は、 Apache のエラー AH00288 と logrotate の関係性に焦点を当てて、原因や対策を解説していきます。 AH00288: scoreboard is full, not at MaxRequestWorkers このメッセージが、logrotate 実行時の Apache HTTP Server のエラーログに出力されることがあります。 以下の流れでこのエラーを深堀りしていきます。 AH00288 メッセージについて 対象のMPM まず、このエラーは MPM が worker MPMの場合に出力されます。event MPMの場合は、AH003490 エラーが出力されます。 エラーメッセージの解説 次にエラーメッセージに着目します。 scoreboard is full はスコアボードに登録できるスレッドの枠が限界に達したことを意味します。 ※スコアボードとは、スレッドの接続数と各スレッドの処理状態をリアルタイムに記録・共有するためのメモリ構造です。 not at MaxRequestWorkers は MaxRequestWorkers に達していないことを意味します。 MaxRequestWorkers は、Apache が同時に処理できるリクエストの最大数です。 スコアボードのスレッドの接続数と MaxRequestWorkers が対応しています。 つまり、Apache が同時に処理できるリクエストの最大ではないのにも関わらず、スコアボードの接続数が埋まっているというエラーになります。 原因の一つとしては終了した古いプロセスやスレッドがスコアボード上に残ってしまっているということが考えられます。 logrotate とAH00248 エラーについて logrotate 時になぜ AH00248 エラーが発生するのかについて解説していきます。 logrotate とは Linux がサポートするログローテーションのツールです。logrotate の設定内容にもよりますが、ログローテーションの際に Apache の再起動させることが一般的です。通常は、サービスの停止を最小限にするために、graceful restart をプロセス数の少ない深夜に実行することで運用されています。 logrotate と graceful restart logrotate が実行されると、graceful restart が行われます。 graceful restart とは、Apache サーバの再起動時に、プロセスが行っている現在の処理を完了してから終了し、新しいログファイルを再オープンする動作のことを指します。これはプロセス単位で行われるため、プロセス内でのスレッドがすべての処理が完了後にそのプロセスが再起動します。 この処理の目的は、サーバが新しいリクエストを受け付けることができない時間を最小限に抑えるとともに、設定ファイルに記載されたパラメータを尊重することです。 しかし、上記で述べたように稼働中の古いプロセスはリクエストが終了するまでプロセスとスレッドは graceful restart 後のリクエストを受け付けることができない状態です。それに加えて、graceful restart 後に確保するアイドルスレッドや新しく受け付けたリクエストでスコアボードがうまってしまい、AH00288 エラーが出力されます。 ※古いプロセス内のスレッドは新しいリクエストを受け付けない状態なので MaxRequestWorkers と処理できるリクエスト数が一致しない Apacheのドキュメントにてこのエラーについての説明があります。 ※event MPM の説明ですが、worker MPM においても同様の現象だと考えます。 ※worker MPM には event MPM の “From 2.4.24 onward,” に記載の機能改善は行われていません。 Apache MPM event / Graceful process termination and Scoreboard usage 基本的には、古いプロセスは終了し次第プロセス内の全スレッドが解放されていくので、処理自体に問題はありません。 しかし、高負荷のシステムであればリクエストが処理しきれなくなるため、問題になる可能性があります。 AH00248 エラーの対策 解決策としては、2つの方法が考えられます。 MPM のパラメータの調整 rotatelogs の使用 MPM のパラメータの調整 エラーを出力されないように MPM のパラメータを調整します。 MPM のパラメータは、httpd.conf の <IfModule mpm_worker_module> タグ内で以下のように設定することができます。 <ifmodule mpm_worker_module> ServerLimit 16 //子プロセスの最大数 StartServers 2 //起動時に子プロセスの数(リクエスト処理用) MaxRequestWorkers 150 //最大リクエスト処理数 MinSpareThreads 25 //アイドルスレッド数の最小数 MaxSpareThreads 75 //アイドルスレッド数の最大数 ThreadsPerChild 25 //子プロセスが生成するスレッド数 </ifmodule> Apacheドキュメント / Apache MPM worker より設定の典型例を抜粋 graceful restart によって、リクエスト処理中の古い子プロセスのスレッドが解放されないため、スコアボードの上限 (MaxRequestWorkers や ServerLimit) を上げることや起動直後のプロセス数 (StartServers) を制限することにより、スコアボードが埋まるのを対策することができます。 ※graceful restart の仕様として、スレッドが一つでも稼働中だと子プロセスは再起動されないため、ServerLimit を調整が有効です。 ※パラメータを変更した場合、通常時の運用に影響が出る可能性があります。 1つのパラメータを変更すると、他のパラメータの調整が必要になる場合があります。以下を目安に調整してください。 ServerLimit × ThreadsPerChild >= MaxRequestWorkers MinSpareThreads + 想定最大アクティブスレッド数 < MaxRequestWorkers MaxSpareThreads + 想定最小アクティブスレッド数 < MaxRequestWorkers rotatelogs の使用 logrotate では graceful restart が実行されてしまうので、 rotatelogs を代わりに実行するという方法があります。 rotatelogs は Apache がサポートするログローテーションツールで Apache のアクセスログまたはエラーログを graceful restart の必要なくローテーションが可能です。 graceful restart しないのため AH00288 エラーはログローテーションの際には発生しませんが、設定の反映等のため定期的な再起動を必要とします。 参考文献 類似のエラーである AH03490 についてより詳細に解説していますので、ぜひご覧ください サポートエンジニアの現場から!Apacheエラー「AH03490」を徹底解説 StartServers、ServerLimit、ThreadsPerChild、MaxSpareThread等、各MPMの値のパラメータの説明は以下を参照してください。 Apacheドキュメント / Apache MPM Common Directives logrotate についての基本知識です。 知っておくとちょっと便利!logrotate の仕組みについて1 Red Hat社の公式ドキュメントで logrotate と rotatelogs について説明があります。(Red Hat社の有料ポータルログインIDが必要です) logrotate と rotatelogs を使用して Apache HTTPD のログをローテーションするにはどうすればよいですか? ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post [Apache エラー解説] logrotate 時に Apache で AH00288 エラーが出力される原因と対策 first appeared on SIOS Tech. Lab .
アバター
概要 前回は、OCP-Virt, SUSE-Virt, vSphere の3製品の比較についてご紹介しました。 今回は、OCP-VirtでMultus CNI(以下、Multus)を用いてVMに複数NICを付与する方法について記載します。 前回の記事は こちら 本記事では、Multusの基本的な概要、追加ネットワークの構築、VMへの追加NICの設定について解説します。 今回の作業におけるOCP-Virt環境は用意してあることを前提としています。 OCP-Virtの構築についての記事は こちら Multusとは Multusは、Kubernetes環境で複数のCNIを扱うことを可能にするCNIプラグインとなります。通常Podには一つのNICが接続されますが、Multusを用いることによりPodに追加NICを構成して、複数のネットワークに接続することが可能になります。 OCP-Virtでも同様にVMのPodに対して追加NICを構成して対応することができます。 Image Source: https://github.com/k8snetworkplumbingwg/multus-cni net0, net1は追加のNICとなり、他のCNIを使用して複数のネットワークに接続します。 この時、NetworkAttachmentDefinitions という設定により追加のネットワークを用意します。 NetworkAttachmentDefinitions (以下、NAD) Multusで追加のNICの構成を行いますが、NICが接続するネットワーク設定をNADというカスタムリソースで設定します。 NADは、ネットワーク設定をYAMLファイルとして用意しWeb コンソールまたはCLIなどを通してアプライすることで設定が出来るようになります。 使用出来るCNIは主に下記の種類があります。 Linux Bridge ノード上のBridgeと接続するネットワークを定義します クラスタ外部との通信が可能です OVN-Kubernetes OVN Kubernetes secondary localnet network OVN Kubernetes によるオーバーレイネットワークを定義します クラスタ内部の通信と、ノード上のBridgeと接続してクラスタ外部との通信が可能です OVN Kubernetes L2 overlay network OVN Kubernetes によるオーバーレイネットワークを定義します クラスタ内部の通信が可能です。クラスタ外部との通信は不可能です SR-IOV PodにSR-IOV 仮想機能(VF)を割り当てネットワークを構成します クラスタ外部との通信が可能です 事前準備 前回までの記事で紹介したOCP-Virt環境を使用します。 詳細は、 OCP-Virt 構築してみた を参照して下さい。 OCPへのログイン 下記コマンドを用いて、構築済みのOCPにログインしておきます。 $ oc login -u <ユーザー名> https://api.<クラスター名>.<管理ドメイン>:6443 追加ネットワークの構築 NADの作成 NADを作成して、2つ目のネットワークを作成します。 今回の手順では、CNIとしてOVN Kubernetes L2 overlay network を利用します。 NADのマニフェストファイルを作成します。 追加するネットワークのサブネットを172.31.0.0/24で定義します。 $ vi l2_overlay_nad.yaml apiVersion: k8s.cni.cncf.io/v1 kind: NetworkAttachmentDefinition metadata: name: vm-tutorial-l2-overlay-network namespace: vm-tutorial spec: config: |- { "cniVersion": "0.3.1", "name": "vm-tutorial-l2-overlay-network", "type": "ovn-k8s-cni-overlay", "topology": "layer2", "subnets": "172.31.0.0/24", "mtu": 1300, "netAttachDefName": "vm-tutorial/vm-tutorial-l2-overlay-network" } NADのマニフェストファイルを適用します。 $ oc apply -f l2_overlay_nad.yaml networkattachmentdefinition.k8s.cni.cncf.io/vm-tutorial-l2-overlay-network created NADの設定を確認します。 $ oc get net-attach-def -n vm-tutorial -oyaml apiVersion: v1 items: - apiVersion: k8s.cni.cncf.io/v1 kind: NetworkAttachmentDefinition metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"k8s.cni.cncf.io/v1","kind":"NetworkAttachmentDefinition","metadata":{"annotations":{},"name":"vm-tutorial-l2-overlay-network","namespace":"vm-tutorial"},"spec":{"config":"{\n \"cniVersion\": \"0.3.1\",\n \"name\": \"vm-tutorial-l2-overlay-network\",\n \"type\": \"ovn-k8s-cni-overlay\",\n \"topology\": \"layer2\",\n \"subnets\": \"172.31.0.0/24\",\n \"mtu\": 1300,\n \"netAttachDefName\": \"vm-tutorial/vm-tutorial-l2-overlay-network\"\n}"}} creationTimestamp: "2025-04-03T08:37:06Z" generation: 1 name: vm-tutorial-l2-overlay-network namespace: vm-tutorial resourceVersion: "33233267" uid: f26c98a1-9ad2-4713-911d-7afd1618458a spec: config: |- { "cniVersion": "0.3.1", "name": "vm-tutorial-l2-overlay-network", "type": "ovn-k8s-cni-overlay", "topology": "layer2", "subnets": "172.31.0.0/24", "mtu": 1300, "netAttachDefName": "vm-tutorial/vm-tutorial-l2-overlay-network" } kind: List metadata: resourceVersion: "" これで二つ目のネットワークを構成することが出来ました。 VMへの追加NICの設定 VMへ追加のNICを設定していきます。 追加のNICの設定は、OCPのWebコンソールで実施していきます。 追加のNICの設定 OCPのWebコンソールで [Virtualization] > [VirtualMachines]に移動します。 プロジェクトの選択をして、作成済みのVMを選択します。 [Configuration] -> [Network] を選択して、[Add network interface] をクリックします。 Networkに作成したNADを選択出来るようになっています。選択を行いSaveをクリックします。 追加のNICが付与されていることを確認します。 「Pending changes」と表示されるため、追加NICの反映のために画面右上のリスタートボタンを選択して再起動を行います。 数分後、ステータスがRunningであることを確認します。 画面右側のNetworksにnic-xxxと追加のNICの構成、IP(172.31.0.4)が付与されていることを確認します。 内容についての補足事項 今回のケースでは、NADでサブネットを定義しました。 その場合VMにはサブネットの範囲内でDHCPによってIPが割り振られる形になります。 VMで静的IPを使用する場合は、サブネットを定義しない状態でNADを設定する必要があります。 NADを設定後、VMに対して静的IPを定義する必要があることに注意してください。 詳細は、参考URLを参照していただければと思います。 参考URL: https://docs.redhat.com/ja/documentation/openshift_container_platform/4.17/html-single/virtualization/index#virt-connecting-vm-to-ovn-secondary-network https://ovn-kubernetes.io/features/multiple-networks/multi-homing/#network-configuration-reference まとめ Multusの基本的な概要、追加ネットワークの構築、VMへの追加NICの設定について解説しました。 Multus を使用してOCP上でもVMに複数のNICを構成することが確認出来ました。 追加のNICによるマルチネットワーク構成により、OCP上でもVMに対して柔軟性と利便性の高いネットワーク設計の提供が可能になります。 この記事がMultus利用の参考になれば幸いです。 参考文献 https://github.com/k8snetworkplumbingwg/multus-cni https://docs.redhat.com/ja/documentation/openshift_container_platform/4.17/html-single/virtualization/index#virt-connecting-vm-to-ovn-secondary-network https://ovn-kubernetes.io/features/multiple-networks/multi-homing/#network-configuration-reference ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OpenShift VirtualizationでMultusを利用してみた first appeared on SIOS Tech. Lab .
アバター
ども!一週間の業務がDocker関連業務で、悩まされている龍ちゃんです。Docker系の問題は単に使うだけでは出会わないので、調査も時間がかかります。今回は、そんな時に出会った問題です。 今回のテーマはDocker Desktop環境で発生した問題です。内容は「nodeのDocker Imageをビルドした際、 COPY --chown=node:node が動かず、1001や1002になる」です。 こちらの問題ですが、WSL+Docker CLIの構成のみでは起きませんでした。WSL+Docker Desktop環境でのみ発生した問題になっています。 起因については、わかりません。発生パターンと回避方法が分かったのでブログとしてまとめていきます。 発生した問題 以下のDockerfileをDocker Compose環境でビルドした際に発生していました。 COPY --chown=node:node ./next-app . でコンテナーに移したファイルの権限が、環境によっては node ・ 1001~1003 になってしまう問題が発生していました。 ARG NODE_VER="latest" FROM node:${NODE_VER} RUN npm install -g npm USER node WORKDIR /home/node/app RUN mkdir next-app WORKDIR /home/node/app/next-app RUN mkdir node_modules RUN chown node:node -R node_modules COPY --chown=node:node ./next-app . RUN npm install 発生パターン調査 まずは、WSL+Docker CLI環境では発生せずにDocker Desktop環境でのみ発生していました。上記の問題について、人によってはビルドが成功(ファイル権限がnode)していました。ですが、1001~1003までの広い範囲でファイル権限が変更されていました。 1001~1003までのファイル権限で、実行された環境のユーザーUID:GIDを調査したところファイル権限とUID:GIDが一致していました。よって問題としては、以下になります。 「Docker Desktop環境で COPY --chown コマンドを実行した際にWSL実行ユーザーでファイルがマウントされる」 対応法 暫定的な対応方法は以下の2ステップです WSL実行ユーザーのUID:GIDを取得する 修正したDockerfileに実行ユーザーのUID:GIDを指定してビルド まずは、以下のコマンドでWSL実行ユーザーのUIDとGIDを一括で取得します。 id 取得できた情報を以下のDockerfileへ反映させます。 ARG NODE_VER=latest FROM node:${NODE_VER} # 非rootユーザーを作成 ARG USERNAME=vscode # ユーザーのUIDとGIDを指定 # デフォルトは1000:1000 ARG USER_UID=1000 ARG USER_GID=$USER_UID ENV WORKDIR=/home/$USERNAME RUN npm install -g npm # nodeを削除(1000:1000を開ける) RUN userdel -r node RUN groupadd --gid $USER_GID $USERNAME \\ && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \\ && apt update \\ && apt install -y sudo \\ && echo $USERNAME ALL=\\(root\\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \\ && chmod 0440 /etc/sudoers.d/$USERNAME USER $USERNAME WORKDIR $WORKDIR RUN mkdir next-app WORKDIR $WORKDIR/next-app RUN mkdir node_modules RUN chown $USER_UID:$USER_GID -R node_modules COPY --chown=$USER_UID:$USER_GID ./next-app . RUN npm install nodeのDocker Imageでは、nodeユーザーが1000を使用しています。UIDを指定してユーザーを作成する場合は、そのUIDを使用しているユーザーがいる場合は失敗するため、先回りしてnodeユーザーを削除しています。 知りたいこと 本稿で紹介した問題について、以下の点がまだ不明確です。 なぜDocker Desktop環境でのみCOPYコマンドの挙動が異なるのか この問題に対する根本的な解決方法はあるのか これらの点について、さらなる調査や情報提供をいただけると幸いです。ぜひ!根本原因について知っている方、実行ユーザーのUID:GIDを移す方法に関してベストプラクティスを知っている方がいれば、教えてください。 おわり 本記事では、Docker Desktop環境でCOPYコマンドの挙動が異なる問題について解説しました。この問題に対する暫定的な解決策は見つかりましたが、根本的な原因についてはまだ調査が必要です。今後も新しい情報が得られ次第、アップデートしていきたいと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Docker Desktopで起きるCOPY –chownが権限通りにマウントされない問題|調査と対応 first appeared on SIOS Tech. Lab .
アバター
ども!4月に年度が更新され、4年目のエンジニアになった龍ちゃんです。気づけば、また春の季節になっていますね。春といってもまだ寒いですね。 今回は、Azure OpenAI Serviceのお話になります。生成AIを活用したアプリを作成する際に、レスポンスがJSONで返答されるかどうかは重要な要素になります。DifyでもAOAIでも、方法を模索して実装していました。 Difyでの構造化:「 Dify入門ガイド:LLM出力を構造化する!JSONデータ作成の具体的手順 」 AOAIでの構造化:「 AOAI:Gpt-4oでJSON出力に失敗する対症療法 」 AOAIのバージョンアップに併せて「構造化出力」という機能がリリースされていました。こちらを用いてTypescript環境で構造化出力を行うサンプルの実装を行います。 公式のサンプル では、PythonとREST APIの記載があります。 前回は、 「json schema」での構造化出力を検証 しました。Open AIでは、「 Zod 」を使用した構造化出力を検証し実装していきます。 前提条件 公式リファレンス:構造化出力の情報 はこちらにあります。モデルによっては使えない機能なので、サポートされているモデルやバージョンなどを確認して使用してください。アップデートによっては使えなくなる可能性もあるので、公式の情報を確認して実装するのが一番です。 私の環境では、以下のモデルを使用してAPI Key認証で検証しています。 モデル名 モデルバージョン APIバージョン gpt-4o-mini 2024-07-18 2024-08-01-preview Typescriptのライブラリに関する情報ですが、発見できた情報としては以下の二つです。 TypeScript 用 Azure OpenAI ライブラリ – 2.0.0 Azure OpenAI library for TypeScript 実装は、OpenAIの「 Text Generation:Generate JSON data 」を参考に進めていきます。 サンプルでは、「 LINE×生成AI:チャットバトルゲームを作る! 」で作成したAPIのレスポンスオブジェクトを作成します。こちらのサンプルでは、「キャラクター同士を戦わせ戦いの勝者と描写を出力するゲーム」になっています。出力したいJSONは以下になります。 { "winner":"user"|"system", "combatLogs": { "round":number, "combatLog":string }[] } 構造化出力:Zodサンプル AOAIへ問い合わせて、構造化出力を行うサンプルの全文を張ります。 import { zodResponseFormat } from 'openai/helpers/zod'; import { AzureOpenAI } from 'openai'; import { z } from 'zod'; // Zod定義 const PromptResultTypeSchema = z.object({ winner: z .enum(['user', 'system']) .describe('戦いの勝者を記述する。ユーザー側が勝利した場合は「user」、システム側が勝利した場合は「sysytem」を代入'), combatLogs: z.array( z.object({ round: z.number().describe('戦いの記録の順序を記述する。'), combatLog: z.string().describe('戦いの記録の内容を記述する。'), }), ), }); // Zod ObjectからTypeを生成 type PromptResultTypeFromZod = z.infer<typeof PromptResultTypeSchemaZod>; async battlePrompotFormatJSON_Zod(): Promise<PromptResultType> { const AOAIClient = new AzureOpenAI({ endpoint: "xxxxxxxxxxxxxxxxxxxxx", apiKey: "xxxxxxxxxxxxxxxxxxxxx", apiVersion: "xxxxxxxxxxxxxxxxxxxxx", deployment: "xxxxxxxxxxxxxxxxxxxxx", }); const systemPrompt = ` ~~~~~~~~~略~~~~~~~~~ `; const response_format = zodResponseFormat(PromptResultTypeSchemaZod, 'combat_schema'); const res = await AOAIClient.chat.completions.create({ messages: [ { role: 'system', content: systemPrompt, }, { role: 'user', content: '剣士 主に剣で戦う' }, ], model: '', response_format: response_format, }); const math_combat_schema = res.choices[0].message; if (math_combat_schema.refusal) { return { winner: 'user', combatLogs: [ { round: 1, combatLog: 'JSON整形を正しく行うことができませんでした。よって開発者の負けです。', }, ], }; } const tmp = PromptResultTypeSchemaZod.safeParse(JSON.parse(res.choices[0].message.content)); if (!tmp.success) { console.log(tmp.error); return { winner: 'user', combatLogs: [ { round: 1, combatLog: 'JSON整形を正しく行うことができませんでした。よって開発者の負けです。', }, ], }; } return tmp.data; } 手順としては、AOAIとの通信部分にresponse_formatとしてZodから作成したフォーマットの定義を渡します。結果として構造化出力で返答が返されます。 出力サンプル 実行した結果として、以下のような情報が出力されます。 { "winner": "system", "combatLogs": [ { "round": 1, "combatLog": "リングの中央で立ちはだかるボクシングチャンピオン、彼の拳はまるで鉄の塊のようだ。挑戦者である剣士は、激しい視線を向け、自身の剣をしっかりと構える。初めのラウンドが始まると、チャンピオンが力強いジャブを繰り出す。それは剣士の顔面をかすめ、観客から驚嘆の声が上がる。" }, { "round": 2, "combatLog": "剣士は冷静を保ちながら、反撃のチャンスをうかがう。しかし再びチャンピオンが猛攻を仕掛け、フックが剣士の横っ面に見舞う。その強烈な一撃に、剣士は一歩後退し、動揺を隠せない。拳の力に打ちひしがれる剣士の表情が、リングの明るさに映える。" }, { "round": 3, "combatLog": "猛攻を続けるチャンピオンは、剣士のペースを完全に奪い取っている。挑戦者は必死に防御しようとするが、チャンピオンの強烈なアッパーカットが剣士の顎を捉える。剣士はついに膝をつき、観客たちはその劇的な光景に息を呑む。" }, { "round": 4, "combatLog": "剣士はなんとか立ち上がるものの、目の前には打撃の鬼が立ちはだかる。最後の力を振り絞って反撃しようとするが、チャンピオンのパンチが再度放たれ、その拳は剣士の顔を叩きつけてしまう。剣士は再び地面に崩れ落ち、審判のカウントが始まる。" }, { "round": 5, "combatLog": "カウントが進む中、剣士の意識が薄れていく。彼はまだ戦う力が残っているものの、チャンピオンの強力な防御と攻撃を前に完全に出遅れている。カウントが10に達する時、審判は試合を止め、チャンピオンの勝利を宣言する。" } ] } response_format定義方法 import { zodResponseFormat } from 'openai/helpers/zod'; const PromptResultTypeSchema = z.object({ winner: z .enum(['user', 'system']) .describe('戦いの勝者を記述する。ユーザー側が勝利した場合は「user」、システム側が勝利した場合は「system」を代入'), combatLogs: z.array( z.object({ round: z.number().describe('戦いの記録の順序を記述する。'), combatLog: z.string().describe('戦いの記録の内容を記述する。'), }), ), }); const response_format = zodResponseFormat(PromptResultTypeSchema, 'combat_schema'); Zodの形式で出力させたいオブジェクトを定義します。Zodオブジェクトから zodResponseFormat を通してフォーマットを生成します。Zodを学んでいれば、フォーマット定義に関してはスムーズに定義することができます。 zodResponseFormat を通すことでJSON Schemaを作成してくれています。説明( describe )を定義することで各項目の情報が保管されます。説明を詳細にしておくことで、構造化(JSON)の作成をサポートしてくれます。 構造化の検証 const math_combat_schema = res.choices[0].message; // Azure OpenAI Serviceのレスポンスからの確認処理 if (math_combat_schema.refusal) { return { winner: 'user', combatLogs: [ { round: 1, combatLog: 'JSON整形を正しく行うことができませんでした。よって開発者の負けです。', }, ], }; } // Zodのスキーマに対応できているかの確認処理 // Errorがある場合もレスポンスに含まれる const tmp = PromptResultTypeSchemaZod.safeParse(JSON.parse(res.choices[0].message.content)); if (!tmp.success) { console.log(tmp.error); return { winner: 'user', combatLogs: [ { round: 1, combatLog: 'JSON整形を正しく行うことができませんでした。よって開発者の負けです。', }, ], }; } 構造化の検証は、Azure OpenAI Serviceからの返答とZodによるスキーマを用いた二つの方法で検証しています。エラーが発生した場合は、固定メッセージを返答しています。 AOAIからの返答には、 モデルから返答された拒否メッセージ (choice.message.refusal)が含まれる場合があります。拒否メッセージが含まれる場合では、生成を開始したがうまく生成することができなかった場合に含まれます。 Zodのスキーマ判定では、AOAIからの返答をJSON構造化に変換して、 Zodを通じて検証(safeParse) を行っています。検証の結果には、成功した場合はデータが失敗した場合はZodErrorが出力されます。 Zodスキーマ判定:簡易的な例 const stringSchema = z.string(); stringSchema.safeParse(12); // => { success: false; error: ZodError } stringSchema.safeParse("billie"); // => { success: true; data: 'billie' } 構造化出力の精度を高める方法 システムプロンプトで出力形式の詳細な指示を事前に設定する Zodのオブジェクトの命名をシステムプロンプトの内容に合わせて適切に命名する 複雑な出力が必要な場合は、処理を小さな単位に分割する これらの方法を組み合わせることで、より高精度な構造化出力を実現できます。 あなたは決闘の審判です。二つのキャラクターの戦闘を見守り、勝敗までの流れを判定してください。 AI側がチャンピオン、ユーザー側が挑戦者です。 次の内容は必ず守ってください「チャンピオンのキャラクターが勝利した場合はsystem、挑戦者が勝利した場合はuserと明記してください。」 --- ボクシングチャンピオン主にこぶしで戦う --- 以下のType出力を守った内容を最後に付録として記載してください。 --- { "combatLogs": { "round":number, "combatLog":string }[] } --- 例は以下のようになります。combatLogは小説家のように過大に脚色して演出してください。決闘の勝者を明確にしてください。 --- { "combatLogs": [ { "round": 1, "combatLog": "訓練場の教官が鉄の剣で攻撃しました" }, { "round": 2, "combatLog": "訓練場の教官が鉄の盾で防御しました" } ] } --- システムプロンプトでの詳細な指示は、AIモデルが期待される出力形式を正確に理解するために重要です。出力の例(few-shot)を含めることで、より具体的な指示となります。 おわり 以上、Azure OpenAI Serviceの構造化出力(JSON)をZodを用いて検証する方法について解説しました。システムプロンプトの適切な設定と、Zodによる厳密な型チェックをセットで行うことで、より信頼性の高いAIアプリケーションを構築することができます。みなさんも是非試してみてください! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 今すぐ使える!Azure OpenAI × Zodで実現する高精度な構造化出力の実装方法 first appeared on SIOS Tech. Lab .
アバター
こんにちは PS/SLの佐々木です 今回はNext.jsにDIコンテナを導入したいと思います。 TypescriptのDIコンテナライブラリは inversifyJS と Tsyringe の二つが有名なようですが、今回は inversify を採用しました 今回は導入方法の動作の検証がメインのためDI、DIコンテナについては軽くしか触れません。 DIコンテナとは DI(Dependency Injection)コンテナ とは、「依存関係(=使いたい機能やクラス)」を自動で注入(Inject)してくれる仕組みです。 DIコンテナを導入するメリット 依存関係の管理が楽になる どのクラスがどの機能を使っているか、一元管理できる。 テストがしやすくなる モックやスタブの差し替えが簡単になる。 コードの再利用性・拡張性が上がる 実装の入れ替えが柔軟にできる(例えば本番用と開発用で処理を変える、など)。 導入方法 Next.js プロジェクト作成 npx create-next-app Inversify JS インストール npm install inversify@alpha reflect-metadata 今回作成するディレクトリ構成 今回使用するのはNext.js v15になります。 /app/apiを基準のディレクトリとして作業を行っています . ├── (controller) │   └── route.ts // ルーティング ├── instrumentation.ts // ├── interface │   ├── repository.ts │   └── service.ts ├── inversify.config.ts // 識別子と実装クラスの関係付け ├── repository │   └── index.ts // DBにアクセスするリポジトリ層 ├── service │   └── index.ts // ビジネスロジックを実装するservice層 └── type └── symbol └── type.ts // 識別の定義 それぞれのディレクトリとファイルの責務は上記の通りになります。 またDI を行う際に @Injectable() や @Inject() などのデコレーターを使用するためtsconfigに以下の設定を追加します。 { "compilerOptions": { "target": "ES2017", ... "experimentalDecorators": true, // 追加 "emitDecoratorMetadata": true, // 追加 ... } 実装 では早速動かしてみましょう 今回はDIコンテナなしの場合とありの場合でどれぐらい違うのかをみていただくために、DIコンテナを使用しないバージョンも載せておきます。 今回実装するのは簡単なUserの取得と作成の処理になります。 またDBはないのでRepository層はほぼ何も処理はしてません 動作確認(DIコンテナなしの場合) まずはDIコンテナなしの場合以下のようになります。(特に見なくても大丈夫の方はDIコンテナありの場合まで読み飛ばしてください) interface/repository.ts import { User } from "../type"; export interface IUserRepository { getById(id: string): User, create(user: User): void, } interface/service.ts import { User } from "../type"; export interface IUserService { get(id: string): User create(user: User): void } type.ts export type User = { id: string, name: string, email: string, address: string } route.ts import { NextResponse } from "next/server" import { UserService } from "../service" import { User } from "../type" export const GET = () => { const service = new UserService() const user = service.get('user0001') return NextResponse.json(user) } export const POST = () => { const service = new UserService() const body: User = { id: 'user0002', name: 'sasaki2', address: '東京都港区...', email: 'sasaki2@sios.jp' } const user = service.create(body) return NextResponse.json('ok') } service/index.ts import { IUserService } from "../interface/service"; import { UserRepository } from "../repository"; import { User } from "../type"; export class UserService implements IUserService{ private repository: UserRepository constructor(){ this.repository = new UserRepository() } get(userId: string){ return this.repository.getById(userId) } create(user: User) { this.repository.create(user) } } repository/index.ts import { IUserRepository } from "../interface/repository"; import { User } from "../type"; export class UserRepository implements IUserRepository { constructor(){} getById(id: string): User { return { id: id, name: 'kanta sasaki', email: 'sasaki@sios.jp', address: '東京都港区....' } } create(user: User): void { // prisma.create({}) ... } } DIコンテナを使用しない場合にはcontroller, seviceそれぞれで依存しているクラスのインスタンスを作成しているのがわかると思います。 これだと具体的な実装に依存してしまっており、テストの際にダミーのRepositoryと入れ替えが難しくなってしまったり、依存している実装の変更の影響を受けやすくなってしまいます。 では次にDIコンテナありの場合を見てみます。 動作確認(DIコンテナありの場合) 識別子の定義 type/symbol/type.ts const TYPES = { // repository IUserRepository: Symbol.for('IUserRepository'), // service IUserService: Symbol.for('IUserService') }; export default TYPES; 識別子と実装クラスの関係付け inversify.config.ts import { Container } from 'inversify'; import TYPES from './type/symbol/type'; import { IUserRepository } from './interface/repository'; import { UserRepository } from './repository'; import { IUserService } from './interface/service'; import { UserService } from './service'; const diContainer = new Container(); diContainer.bind<IUserRepository>(TYPES.IUserRepository).to(UserRepository); diContainer.bind<IUserService>(TYPES.IUserService).to(UserService); export { diContainer }; Symbolで定義した識別子と具体的な実装クラスは上記のように紐づけます。 DIコンテナを使用してRepositoryとServiceの依存関係を解決する controller/route.ts import { NextResponse } from "next/server" import { User } from "../type" import { diContainer } from "../inversify.config" import { IUserService } from "../interface/service" import TYPES from "../type/symbol/type" export const GET = () => { const service = diContainer.get<IUserService>(TYPES.IUserService) const user = service.get('user0001') return NextResponse.json(user) } export const POST = () => { const service = diContainer.get<IUserService>(TYPES.IUserService) const body: User = { id: 'user0002', name: 'sasaki2', address: '東京都港区...', email: 'sasaki2@sios.jp' } const user = service.create(body) return NextResponse.json('ok') } service/index.ts import { inject, injectable } from "inversify"; import { IUserService } from "../interface/service"; import { UserRepository } from "../repository"; import { User } from "../type"; import TYPES from "../type/symbol/type"; import type { IUserRepository } from "../interface/repository"; @injectable() export class UserService implements IUserService{ constructor( @inject(TYPES.IUserRepository) private readonly repository: IUserRepository ){} get(userId: string){ return this.repository.getById(userId) } create(user: User) { this.repository.create(user) } } repository/index.ts import { injectable } from "inversify"; import { IUserRepository } from "../interface/repository"; import { User } from "../type"; @injectable() export class UserRepository implements IUserRepository { constructor(){} getById(id: string): User { return { id: id, name: 'kanta sasaki', email: 'sasaki@sios.jp', address: '東京都港区....' } } create(user: User): void { // prisma.create({}) ... } } Point 依存される側のclassには @injectable() のデコレーターを付与する コンストラクタでDIコンテナを用いて依存解決する場合には @inject(識別子) private readonly repository: IUserRepository のように依存解決を行うことができ @inject デコレーターの引数に識別子を入れることによって inversify.config.ts で定義した関連付けに応じで解決することができる コンストラクタを使用しない場合には const service = diContainer.get<IUserService>(識別子) のような形で依存解決を行うことができる DIコンテナの初期化 最後にサーバー起動時にDIコンテナを初期化しておきましょう Next.js15より next.config に instrumentationHook:true を追加しなくても instrumentation.ts という名前のファイルを作成すればサーバー起動時に一回だけ読み込んでくれるようなのでこちらを使用します import 'reflect-metadata'; export async function register() { if (process.env['NEXT_RUNTIME'] === 'nodejs') { await import('./inversify.config'); } } こんな感じでAPIからユーザー情報が取得できていると思います。 またDIコンテナで依存関係を解決した後の型を見てみると IUserRepository となっているため抽象に依存していることがわかります。 これによってテストの際に使用する具象クラスの入れ替えも非常に楽になります。 使ってみた感想 inverifyJSを使用してみて非常にシンプルで軽量なライブラリのためキャッチアップも非常に楽で直感的に使えるライブラリだと感じました。 もちろんDIを使わないものと比べたら冗長な感じになりますが、それ以上に受けられる恩恵は大きいと感じています。 付録 各種バージョンの確認にお使いください { "name": "dicontainer", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev --turbopack", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "inversify": "^7.0.0-alpha.5", "next": "15.2.4", "react": "^19.0.0", "react-dom": "^19.0.0", "reflect-metadata": "^0.2.2" }, "devDependencies": { "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", "tailwindcss": "^4", "typescript": "^5" } } ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Next.jsでDIコンテナを導入する方法(inversify編) first appeared on SIOS Tech. Lab .
アバター
こんにちは PS SLの佐々木です。 今回SBOMの導入ガイドをリリースしたので紹介させてください。 SBOM導入ガイドでは私のいるチームで1年間SBOMに関する調査検証をした中で、事前に整理をしておきたかったポイントや観点、導入にあたって悩ましかったポイントなどを整理し、スムーズにSBOMを導入するためのガイドになっています。 是非SBOMの導入を考えている方に読んでいただきたいと思います。 SBOMとは? 近年、ソフトウェアのサプライチェーン管理の重要性がますます高まっています。その中で注目されているのが SBOM(Software Bill of Materials) です。 SBOMは、ソフトウェアを構成するすべてのコンポーネントのリストを明示し、ソフトウェアの透明性を向上させる役割を果たします。特に、セキュリティリスクの特定やライセンス管理の観点で、多くの企業がSBOMの導入を進めています。 なぜSBOMが重要なのか? 脆弱性管理の強化 :使用しているソフトウェアコンポーネントの脆弱性を素早く把握できる。 ライセンスコンプライアンスの向上 :不適切なライセンス使用を防ぐために、すべてのコンポーネントのライセンスを確認可能。 ソフトウェアサプライチェーンの透明性 :外部提供ソフトウェアの構成を把握し、サイバー攻撃対策を強化。 SBOM導入ガイドの概要 今回、SBOMの基礎から導入方法までを詳しく解説した 「SBOM導入ガイド」 を無料でダウンロードできるようにしました! 本書は、以下のような方々に最適な内容となっています。 SBOMの基礎を学びたい方 ソフトウェアサプライチェーンの管理に関心がある方 実際にSBOMを導入しようと考えている開発者やセキュリティ担当者 本ガイドのポイント SBOMの基本概念を解説 SBOMの導入ステップを具体的に紹介 SBOMを運用する際の注意点やアンチパターンを解説 ダウンロードはこちら SBOM導入ガイドを読んで、ソフトウェアサプライチェーンの透明性とセキュリティを向上させましょう! [ダウンロードリンク] (https://mk.sios.jp/sbom-first-guide-form) [プレスリリース] (https://sios.jp/news/press/2025/20250408-sbom.html) ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post SBOM導入ガイド公開しました! first appeared on SIOS Tech. Lab .
アバター
はじめに こんにちはサイオステクノロジーの小野です。今回はOpenShift AIのモデルレジストリ機能について解説します。この機能を利用することで、モデルの各種情報を整理することができ、管理がしやすくなります。 モデルレジストリは2025年4月4日現在テクノロジープレビューです。この機能はサポート対象外であることと、製品版では変更される可能性があることに注意してください。 モデルレジストリ モデルレジストリはMLOpsにおける機械学習モデルの数々のメタデータを保持する機能です。この機能により、モデルのバージョン管理を行うことができるため、実験を再現したり、モデルの追跡を行ったり、簡単にモデルのデプロイを行えたりします。 以下の内容をモデルまたはそのバージョンごとに登録することが可能です モデルのS3ストレージ保管場所 モデルソースのフォーマット モデルのバージョン モデルの所有者 最終変更タイムスタンプ 説明文 ラベル プロパティ モデルレジストリ構築方法 前提条件 導入Operator Red Hat Authorino Operator Red Hat OpenShift Serverless Operator Red Hat OpenShift Servicemesh Operator Red Hat OpenShift AI Operator バージョン 2.14以上 S3ストレージにモデルを保存済み OpenShift AI構築済み OpenShift AIの構築は以前の記事を参考にしてください OpenShift AI を導入してみた DataScienceClusterを作成する際に、modelregistryコンポーネントをManagedにします。 DataScienceClusterのmodelregistryコンポーネントをManagedにする OpenShift AIの管理者権限を持つ OpenShift AI Operatorインストール時に作成されるグループにOpenShift AIにログインするユーザーを追加してください。 rhods-adminsグループに管理者権限を持たせるユーザーを追加する DB作成 モデルレジストリに登録するメタデータはDBに保存されます。そのため、最初にDBを作成します。 以下のyamlでリソースを作成してください。モデルレジストリ用のnamespaceのrhoai-model-registriesにMySQLのコンテナを作成します。MYSQL_ROOT_PASSWORD、MYSQL_DATABASE、MYSQL_USER、MYSQL_PASSWORDの環境変数は各自設定してください。 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc namespace: rhoai-model-registries spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi --- apiVersion: v1 kind: ConfigMap metadata: name: mysql-config namespace: rhoai-model-registries data: my.cnf: | [mysqld] default_authentication_plugin=mysql_native_password --- apiVersion: apps/v1 kind: Deployment metadata: name: mysql namespace: rhoai-model-registries spec: selector: matchLabels: app: mysql replicas: 1 template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:8.0 env: - name: MYSQL_ROOT_PASSWORD value: <MySQLのルートパスワード> - name: MYSQL_DATABASE value: <MySQLのデータベース> - name: MYSQL_USER value: <MySQLのユーザー名> - name: MYSQL_PASSWORD value: <MySQLのパスワード> ports: - containerPort: 3306 name: mysql volumeMounts: - name: mysql-persistent-storage mountPath: /var/lib/mysql - name: mysql-config-volume mountPath: /etc/mysql/my.cnf subPath: my.cnf volumes: - name: mysql-persistent-storage persistentVolumeClaim: claimName: mysql-pvc - name: mysql-config-volume configMap: name: mysql-config --- apiVersion: v1 kind: Service metadata: name: mysql namespace: rhoai-model-registries spec: ports: - port: 3306 targetPort: 3306 selector: app: mysql mysqlのリソースを作成する mysqlというDeploymentsによりpodがrhoai-model-registries内に作成されます。 リソースを作成するとmysqlのpodが作成される モデルレジストリ設定 DBの作成が完了したら、モデルレジストリの設定を行います。OpenShift AIに管理者権限を持っているアカウントでOpenShift AIにログインします。 Setting > Model Registry Settingsを開きます。 Setting > Model Registry Settingsを開く Create model registryを押下すると設定画面が表示されるので以下のように設定します: Name:モデルレジストリ名 Resource Name:モデルレジストリ名と同じ Connect to external MySQL database Host:mysqlのホスト名(yamlと同じ設定の場合mysql) DBが rhoai-model-registries とは異なる namespace で実行されている場合は、ホスト名を <host name>.<namespace>.svc.cluster.local 形式で入力します。 Port:mysqlのポート番号(yamlと同じ設定の場合3306) Username:MYSQL_USERに設定したユーザー名 Password:MYSQL_PASSWORDに設定したパスワード Database:MYSQL_DATABASEに設定したデータベース名 モデルレジストリを作成する モデルレジストリのStatusがAvailableになれば作成完了です。 StatusがAvailableになれば作成完了 モデルの登録 モデルレジストリの作成が完了すると、左のメニューのModel Registryからモデルの登録ができるようになります。 メニューのModel Registryからモデルの登録を行う Registry modelを押下するとモデルの登録ができます。各パラメータは以下のように設定します: Model details:モデル名 Version details:登録するモデルのバージョンとモデルの形式情報 Model location:モデルが保存されているS3オブジェクトストレージの接続情報 モデルの登録設定 登録が完了するとモデル名とVersionsの欄に登録したモデルのバージョンが表示されます。 登録するとモデルのバージョンが表示される 登録したバージョンを開くと登録した際に入力したメタデータが閲覧できます。 モデルのメタデータの閲覧 Description、Labels、Propertiesもここで編集できます。 Description、Labels、Propertiesも編集可能 新しいバージョンの登録 レジストリにモデルの新しいバージョンを登録するには、登録したモデルの画面からRegister new versionを押下します。 Register new versionから新しいバージョンの登録を行える モデルの登録したときと同じように新しいバージョン名と新しいモデルのS3の接続情報を入力します。 新しいバージョンのモデル情報を登録する 入力すると、モデルの新しいバージョンの登録ができます。 新しいバージョンが追加される モデルのデプロイ モデルレジストリを利用することで、登録したモデルのデプロイを行うことができます。 登録したモデルのバージョンの詳細画面のActionsからDeployを選択します。 ActionsのDeployからモデルデプロイを行える モデルをデプロイするデータサイエンスプロジェクトを選択して、デプロイ設定を行います。設定方法は以前の記事を参考にしてください。 OpenShift AIにLLMをデプロイしてみた モデルデプロイ設定 バージョンのDeplymentsの欄にデプロイしたモデル名が表示され、Statusに がつけばデプロイ完了です。 Statusに がつけばデプロイ完了。ここからモデルAPIのエンドポイントを取得できる。 モデルのアーカイブ モデルをアーカイブするには、バージョンの詳細画面のActionsからArchive model versionを選択します。 ActionsのArchive model versionからモデルのアーカイブができる バージョン名を入力すればアーカイブできます。 バージョン名を入力するとモデルをアーカイブできる アーカイブ完了 モデルの復元 アーカイブしたモデルを復元することが可能です。モデルのVersionsの三点メニューを開いて、View archived versionsを選択します。 三点メニューのView archived versionsからアーカイブしたモデルのバージョン一覧を表示できる アーカイブしたモデルバージョンの一覧が表示されるので、復元したいバージョンのモデルを開いて、Restore model versionを選択することでモデルの復元が実行されます。 アーカイブされたモデルのバージョン一覧 アーカイブされたモデルバージョンの詳細。Restore model versionにより復元できる。 モデル復元完了 バージョン単位ではなくモデル自体のアーカイブと復元も同様に行えます。 モデル単位でのアーカイブ、復元も可能 おわりに 以上により、モデルレジストリのDB作成、モデルレジストリの登録、モデルメタデータの登録、モデルのデプロイ、モデルのアーカイブ・復元について解説しました。 MLOpsにおいてAIモデルの更新が行われるため、その管理というのは非常に重要になってきます。ぜひモデルレジストリを活用することで、モデルの管理に役立ててください。 参考 モデルレジストリーの操作: https://docs.redhat.com/ja/documentation/red_hat_openshift_ai_self-managed/2.16/html/working_with_model_registries/index モデルレジストリーの管理: https://docs.redhat.com/ja/documentation/red_hat_openshift_ai_self-managed/2.16/html/managing_model_registries/index ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OpenShift AIのモデルレジストリ機能を使ってみた first appeared on SIOS Tech. Lab .
アバター
  こんにちは、サイオステクノロジーの佐藤 陽です。 相変わらずCosmosDBについての勉強しているので、引き続きアウトプットしていきたいと思います。 今回のテーマはCosmosDBでのベクトル検索についてです。 ベクトル検索って何? CosmosDBでどうベクトル検索を実装するの? RAGにCosmosDBを組み込みたい! といった方は是非最後までご覧ください! はじめに 2024年のMicrosoft IgniteにてCosmosDBのベクトル検索機能がGAとなることが発表されました。 同年のMicrosoft BuildではまだPublic Previewでしたが、これで本番環境でも安心して利用できます。 このベクトル検索ですが、昨今の生成AIのアプリケーションと非常に相性が良いです。 特にRAG(Retrieval Augmented Generation)ではベクトル検索がよく使われており、CosmosDBをRAGに導入するといったパターンが今後主流になってくるのではないかと思います。 そこで本記事では、「ベクトル検索とは?」といった点からCosmosDBでのベクトル検索の実装まで一通りご紹介したいと思います。 余談 本記事は JAZUG Shizuoka での登壇資料をベースとしているため、内容がやや静岡寄りのものとなっています。 そして是非、JAZUG Shizuokaが開催するイベントへも参加いただければと思います! ベクトル検索とは? ではまずベクトル検索についてご紹介します。 生成AIを活用するうえでベクトルは非常に重要な要素です。 高校生の時にベクトルとは「大きさ・向きを持つもの」として習い X,Y平面上に矢印を書いた図をたくさん見たかと思いますが、まさにあれです。 今回の記事においては、文章や画像などをベクトル化します。 例えば「犬」「猫」「花」の3つの単語をベクトル化すると、それぞれ以下のような値で表現されます。 (ベクトル値としては1000を超える次元数であるため一部のみ掲載します) 犬 猫 花 ベクトル値 [-0.019726261, -0.014364496, -0.011311122,…] [-0.013889334, -0.050191004, -0.03891719, …] [0.02033748, 0.027342353, 0.017839076, …] そしてこのベクトル値の特徴として、 似たような使われ方をする単語 は似たような値を取ることが言われています。 例えば 私は毎朝、犬にエサをあげる 私は毎朝、猫にエサをあげる といったような使い方はしますが 私は毎朝、花にエサをあげる といった使い方はしませんよね。 このように、同じような使われ方をする単語は、似たようなベクトル値を持ちます。 今回はこの「似たような値」を評価するためにコサイン類似度を利用します。 犬, 猫, 花の3つの単語のベクトルア値、それぞれの組み合わせでコサイン類似度を算出した値を以下に示します。 犬&猫 犬&花 猫&花 0.6180 0.4022 0.3522 この結果から、「犬」と「猫」のベクトル値の相関が高くなり、近しい意味を持つことを表しています。 人間の感覚からしたら明らかな結果ですが、コンピューターが定量的な値としてこの意味の近さを扱えることは大きなメリットです。 この仕組みを利用したのが ベクトル検索 です。 ベクトル検索は、ユーザーからの質問(クエリ)をベクトル化し、データベースなどから似たようなベクトル値を持つものを算出します。 これにより、キーワード的には一致していないとしても、意味的に近しいものをデータベースから取得することが可能となります。 CosmosDBでのベクトル検索 では実際にCosmosDBにおけるベクトル検索の例を見てみたいと思います。 まず初めに検索対象となるデータをCosmosDBに格納します。 今回は、静岡の観光地をテーマに格納するデータを用意しました。 観光地名 説明 富士山 世界文化遺産に登録された、標高3,776メートルの日本一高い象徴的存在の山。四季を通じて登山や観光で多くの人が訪れ、山頂からの日の出は特に有名。 伊豆半島 地中海のような青い海と豊富な自然が魅力的なリゾート地。温泉や海水浴、ダイビングなどのマリンスポーツが楽しめる。また、伊豆七島を望む絶景ポイントも多数存在する。 三島スカイウォーク 日本最長の歩行者専用吊り橋で、全長400メートル。橋の上からは富士山や駿河湾の壮大な景色を一望でき、特に晴れた日には絶景が広がる。 熱海温泉 江戸時代から続く歴史ある温泉地。海辺に多くの旅館が立ち並び、海を眺めながらの露天風呂が楽しめる。また、芸術作品や熱海の花火大会も観光の大きな魅力の一つ。 日本平 静岡市内からロープウェイでアクセス可能な展望公園。富士山や駿河湾の大パノラマを背景に、季節ごとの花々が美しい。 静岡県立美術館 静岡市内に位置し、地元出身の画家、レオナール・フジタの作品を中心に展示。また、世界的に著名な画家の作品も常設展示され、アートファンには見逃せないスポット。 清水エスパルスドリームプラザ 清水区にある大型のショッピングモールで、レストラン、ショップ、映画館、水族館などが集まる。家族連れで一日中楽しめる。 浜松城公園 浜松市にある城址公園で、徳川家康ゆかりの地。春には桜が咲き誇り、多くの花見客で賑わう。城内には歴史資料館もあり、歴史好きにはたまらない。 久能山東照宮 富士山の麓、久能山の山頂に鎮座する神社で、徳川家康を祀っている。豪華絢爛な彫刻や装飾が施された建物は国宝に指定されており、日本の伝統美を感じさせる。 沼津港深海水族館 沼津港に隣接する水族館で、深海に生息する珍しい生き物たちを展示。普段はなかなか見ることのできない深海生物の神秘的な世界を体験できる。 この項目に対して以下の処理を行います。 ベクトル化 CosomosDBへの登録 ベクトル化 まずはそれぞれのコンテンツをベクトル化します。 今回は 観光地名 と 説明 の2列に分かれているものを、一つの文字列としてベクトル化していきます。 ベクトル化するにあたってはEmbeddingモデルが必要となるので事前にデプロイしておきましょう。 今回はAzure OpenAI Service上で、 text-embedding-3-small のモデルをデプロイしておきました。 そのうえで今回は以下のような実装を行いました。 url = "https://{AOAIのエンドポイント}/openai/deployments/{Embeddingのモデル名}/embeddings?api-version=2023-05-15" content = [ "富士山 - 世界文化遺産に登録された、標高3,776メートルの日本一高い象徴的存在の山。四季を通じて登山や観光で多くの人が訪れ、山頂からの日の出は特に有名。", "伊豆半島 - 地中海のような青い海と豊富な自然が魅力的なリゾート地。温泉や海水浴、ダイビングなどのマリンスポーツが楽しめる。また、伊豆七島を望む絶景ポイントも多数存在する。", "三島スカイウォーク - 日本最長の歩行者専用吊り橋で、全長400メートル。橋の上からは富士山や駿河湾の壮大な景色を一望でき、特に晴れた日には絶景が広がる。", "熱海温泉 - 江戸時代から続く歴史ある温泉地。海辺に多くの旅館が立ち並び、海を眺めながらの露天風呂が楽しめる。また、芸術作品や熱海の花火大会も観光の大きな魅力の一つ。", "日本平 - 静岡市内からロープウェイでアクセス可能な展望公園。富士山や駿河湾の大パノラマを背景に、季節ごとの花々が美しい。", "静岡県立美術館 - 静岡市内に位置し、地元出身の画家、レオナール・フジタの作品を中心に展示。また、世界的に著名な画家の作品も常設展示され、アートファンには見逃せないスポット。", "清水エスパルスドリームプラザ - 清水区にある大型のショッピングモールで、レストラン、ショップ、映画館、水族館などが集まる。家族連れで一日中楽しめる。", "浜松城公園 - 浜松市にある城址公園で、徳川家康ゆかりの地。春には桜が咲き誇り、多くの花見客で賑わう。城内には歴史資料館もあり、歴史好きにはたまらない。", "久能山東照宮 - 富士山の麓、久能山の山頂に鎮座する神社で、徳川家康を祀っている。豪華絢爛な彫刻や装飾が施された建物は国宝に指定されており、日本の伝統美を感じさせる。", "沼津港深海水族館 - 沼津港に隣接する水族館で、深海に生息する珍しい生き物たちを展示。普段はなかなか見ることのできない深海生物の神秘的な世界を体験できる。" ] headers = {   'Content-Type': 'application/json',   'api-key': '{api-key}' } results = [] for spot in content:     payload = json.dumps({       "input": spot     })     response = requests.request("POST", url, headers=headers, data=payload)     response_data = response.json()     embedding = response_data['data'][0]['embedding']     result = {         "content": spot,         "contentVector": embedding     }     results.append(result) ここでresultとして出力された内容を一つ見てみます。 {     "content": "富士山 - 世界文化遺産に登録された、標高3,776メートルの日本一高い象徴的存在の山。四季を通じて登山や観光で多くの人が訪れ、山頂からの日の出は特に有名。",     "contentVector": [         0.015276342,         -0.04262919,         -0.010207734,         0.027943453,         0.033972893,         ...     ] } このように、元の情報(content)と、その値をベクトル化した値(contentVector)が用意できました。 CosmosDBへの登録 事前準備 ではこの用意出来た値をCosomosDBに登録してきます。 CosomosDBにベクトル値を登録するには事前準備が必要となるため、以下に手順を示します。 コンテナ作成時にベクトルポリシーを追加します。 以下が設定項目です。 パラメータ 概要 Path ベクトル値として扱うパラメータのパスです。今回は/contentVectorが対象となります。 Data データのタイプです。今回扱うEmbeddingモデルが float32で出力する ため、これに合わせてfloat32に設定します。 Distance function ベクトル間の距離を測るための関数です。こちらは先程ご紹介したコサイン類似度を利用します Dimensions ベクトルの次元数です。こちらは今回扱うEmbeddingモデルの次元数が1536となっているため、この値を設定します。 Index type インデックス方式です。このあたりは検索精度に関わってきますが、今回は詳細な説明は割愛します。diskANNとします コンテナ作成完了後、Settingsファイルを確認します。 先程設定した内容が正しくポリシーとして登録されていることが確認できます。 データアップロード 準備が出来たところで、こちらのコンテナに先程のデータを格納していきます。 今回は、先程のベクトル化の処理の最後に出力結果をjsonファイルとして出力しておき、その内容を以下のコードで改めてアップロードします。 # CosmosDBの接続情報 endpoint = "{CosnosDBのエンドポイント}" key = "{Key}" database_name = "sios-database" container_name = "vector-container" # CosmosDBクライアントの作成 client = CosmosClient(endpoint, key) database = client.get_database_client(database_name) container = database.get_container_client(container_name) # JSONファイルをアップロードする関数 def upload_json_to_cosmosdb(file_path):     with open(file_path, 'r', encoding='utf-8') as f:         data = json.load(f)         # idフィールドを追加         data['id'] = data['content']         container.upsert_item(data) # 先程出力したベクトル値を含むJSONファイルをアップロード # 今回は簡易的に`result_`から始まるファイルを指定 for file_name in os.listdir('.'):     if file_name.startswith('result_') and file_name.endswith('.json'):         upload_json_to_cosmosdb(file_name)         print(f"Uploaded {file_name} to CosmosDB") print("All files have been uploaded.") このコードを実行した後にCosmosDBのデータエクスプローラを見てみると、無事にデータが登録されていることが確認できました。 ベクトル検索 次に格納されたデータに対して検索を行っていきます。 今回は以下のような検索を行ってみたいと思います。 「景色が良い所は?」 まずは、この質問(クエリ)をベクトル化します。 先程と同様にEmbeddingモデルを利用してベクトル化したところ以下のような結果となりました。 [−0.01532, −0.02434, −0.05363, ...] この値を利用して検索を行います。 検索クエリとしては以下の通りです。 -- データベース内のコレクション "c" から、最も類似した10件のデータを取得するクエリ SELECT TOP 10     c.content, -- 各ドキュメントの "content" フィールドを取得     VectorDistance(        c.contentVector, -- ドキュメント内のベクトルデータ        [0.0015812921, -0.01822265, -0.011891554,...] -- クエリで指定したターゲットベクトル     ) AS SimilarityScore -- ベクトル間の距離を "SimilarityScore" として取得 FROM c -- ベクトル間の距離(類似度)で昇順に並べ替え ORDER BY VectorDistance(c.contentVector, [0.0015812921, -0.01822265, -0.011891554,...] ) これにより、CosmosDB上から最も類似したデータを検索することが可能です。 実行した結果を以下に示します。 順位 コンテンツ 類似度スコア 1 日本平 – 静岡市内からロープウェイでアクセス可能な展望公園。富士山や駿河湾の大パノラマを背景に、季節ごとの花々が美しい。 0.5055713624352297 2 伊豆半島 – 地中海のような青い海と豊富な自然が魅力的なリゾート地。温泉や海水浴、ダイビングなどのマリンスポーツが楽しめる。また、伊豆七島を望む絶景ポイントも多数存在する。 0.3731433864334767 3 久能山東照宮 – 富士山の麓、久能山の山頂に鎮座する神社で、徳川家康を祀っている。豪華絢爛な彫刻や装飾が施された建物は国宝に指定されており、日本の伝統美を感じさせる。 0.3712187302734037 4 三島スカイウォーク – 日本最長の歩行者専用吊り橋で、全長400メートル。橋の上からは富士山や駿河湾の壮大な景色を一望でき、特に晴れた日には絶景が広がる。 0.37086192839082927 5 熱海温泉 – 江戸時代から続く歴史ある温泉地。海辺に多くの旅館が立ち並び、海を眺めながらの露天風呂が楽しめる。また、芸術作品や熱海の花火大会も観光の大きな魅力の一つ。 0.34883694499412393 6 浜松城公園 – 浜松市にある城址公園で、徳川家康ゆかりの地。春には桜が咲き誇り、多くの花見客で賑わう。城内には歴史資料館もあり、歴史好きにはたまらない。 0.3407391717527575 7 富士山 – 世界文化遺産に登録された、標高3,776メートルの日本一高い象徴的存在の山。四季を通じて登山や観光で多くの人が訪れ、山頂からの日の出は特に有名。 0.31416708979179037 8 静岡県立美術館 – 静岡市内に位置し、地元出身の画家、レオナール・フジタの作品を中心に展示。また、世界的に著名な画家の作品も常設展示され、アートファンには見逃せないスポット。 0.30245339778589064 9 清水エスパルスドリームプラザ – 清水区にある大型のショッピングモールで、レストラン、ショップ、映画館、水族館などが集まる。家族連れで一日中楽しめる。 0.2622979141244738 10 沼津港深海水族館 – 沼津港に隣接する水族館で、深海に生息する珍しい生き物たちを展示。普段はなかなか見ることのできない深海生物の神秘的な世界を体験できる。 0.17702736397602103 このように、各観光地の概要の中に、「景色が良い」といった直接的なワードは含まれていないにも関わらず、景色がよさそうな観光地が上位の検索結果に表れました。 これは以下に示すような 「コンテンツに含まれている文章のベクトル値」 と、 「”景色が良い所は?”の文章のベクトル値」 が近い値を取った結果であると予想されます。 富士山や駿河湾の大パノラマを背景に、季節ごとの花々が美しい。 伊豆七島を望む絶景ポイントも多数存在する。 富士山の麓、久能山の山頂に鎮座する神社 これにてベクトル検索が完了です! 何となくイメージはつかめていただけたのではないでしょうか? 注意点 ベクトル検索で扱う ベクトル値 は、膨大な学習データに基づいて算出されています。 そのため、一般的な単語に対するベクトル値はある程度正確に算出することができますが、ユニークな単語などは苦手です。 例えば社内で「SIOS Innovation Program」といったオリジナルの制度があったとし、通称「SIP」と呼ばれているとします。 この場合、SIPの制度に関連した情報についてベクトル検索を行おうとした場合、期待した結果が得られない場合があります。 これは、SIPもしくはSIOS Innovation Programが一般的な単語ではなく、適切にベクトル化が行えないためです。 こういったケースはベクトル検索よりも、キーワード検索が適していると言われています。 このように必ずしもベクトル検索が優れているわけではなく、ケースに応じて検索方法を切り替える必要があります。 そして、このキーワード検索とベクトル検索を組み合わせたハイブリッド検索といった強力な検索手法も活用されているのですが 2025年のBuildにてCosmosDBでもベクトル検索の機能がGAされるとうわさされています。 また実際にGAされたら試してご紹介したいと思うので期待ください! おわり 本日はCosmosDBのベクトル検索の機能をご紹介しました。 CosmosDBでのベクトル検索機能がGAされたことにより、より生成AIソリューションとの親和性が高まり、今後の活用が期待されます。 また、ハイブリッド検索機能のGAも噂されていることにより、より一層の期待ができます。 これを機会にCosmosDBを是非活用してみてください! ではまた! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【Azure】CosmosDBにおけるベクトル検索入門ガイド【初心者向け】 first appeared on SIOS Tech. Lab .
アバター
こんにちは。サイオステクノロジーの橋本です。 皆さんは Ansible で環境構築をしているものの一回の実行時間がちょっと長くて… という事はありませんか 自動化してもやっぱり時間がかかるパッケージインストールをちょっと早くする方法を紹介します。 今回は以下の 4 回 dnf を実行している Playbook に関して、 1 回の dnf 実行に集約してます。 - name: "httpd のインストール"   ansible.builtin.dnf:     name: httpd   when:   - ansible_distribution_major_version == "9" - name: "DNS (bind) のバージョン指定" set_fact: bind_version: 9.16 - name: "DNS (bind) のインストール" ansible.builtin.dnf: name: "bind{{ bind_version }}" when: - ansible_distribution_major_version == "8" - name: "wget のインストール" ansible.builtin.dnf: name: wget - name: "sos のインストール" ansible.builtin.dnf: name: sos 上記 Playbook はわかりやすいというメリットは有るものの 4 回 dnf を実行しているため 都度都度 dnf モジュールを実行し毎回リポジトリのチェックなどが入りその分処理時間が長くなります。 そのため、dnf を可能な限り少ない回数にして、リポジトリチェックなどの回数 を抑えようという発想です。 Step 1 ドキュメント にもあるように dnf モジュールはパッケージ名を配列で渡すことができます。 まずは sos と wget を配列で渡しましょう。 Step 2 で活きてきますが配列は事前に定義します。 - name: "インストールパッケージの定義" ansible.builtin.set_fact: packages: - wget - sos - name: "必要パッケージのインストール" ansible.builtin.dnf: name: "{{ packages }}" ここまではよくある手順ですね。 Step 2 さて、問題は以下です。 OS や サーバの役割によってインストールパッケージが異なる運用はよくあるとおもいます。 そして、それが dnf の複数回実行になることもよくあるケースだと思います。 when 句で条件分岐していても、以下のように set_fact すれば同様の挙動をしてくれます。 - name: "インストールパッケージの定義" ansible.builtin.set_fact: packages: - wget - sos - name: "httpd を配列に追加" ansible.builtin.set_fact: packages: "{{ packages + \ ['httpd'] }}" when: - ansible_distribution_major_version == "9" - name: "DNS (bind) のバージョン指定" set_fact: bind_version: 9.16 - name: "bind を配列に追加" ansible.builtin.set_fact: packages: "{{ packages + \ ['bind{{ bind_version }}'] }}" when: - ansible_distribution_major_version == "8" - name: "必要パッケージのインストール" ansible.builtin.dnf: name: "{{ packages }}" 当初とインストールするパッケージに変わりはないですが、 dnf モジュールを呼び出す回数は 1 回になっているため処理時間も大幅に短くなります。 Ansible の処理速度が遅いとき、上記のようにパッケージのインストールを集約するのを検討してみてはいかがでしょうか。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Ansible をちょっと早く実行する dnf (yum) tips first appeared on SIOS Tech. Lab .
アバター
こんにちは、OSSよろず相談室のSKです。 日々、多くのOSSに関するお問い合わせをいただいているのですが、今回は、そもそもOSS(オープンソースソフトウェア)とはどんなものなのかというお話です。 オープンソースとは まず、座学的な話になりますが、オープンソースとは、 主に オープンソース・イニシアティブ(OSI) によって推進される概念で、実用性や協調開発を重視しています。 重視されることは、透過性(公開されていること)実用性、協調開発されコミュニティがあることです。 逆に見た目やデザイン、UI(使い勝手)、利益などは軽視され、サポートはありません。 OSIに掲載された10個の定義はこちらです。 オープンソースの定義 上記オープンソースの定義は、主に以下の内容について定義しています。 ソースコードの公開 – プログラムが実行ファイルとともにソースコードも提供・公開されること。 改変と配布の許可 – 誰でも改変や配布ができること。 差別の禁止 – 特定の人や団体に対する差別的な制限を設けないこと。 ここで疑問に思うことは、「いわゆるフリーウェアと何が違うのだろう?」だと思います。 フリーウェアはOSSと同様に無償ですが、プログラムの中身は公開されていないことが多いです。 公開されていないので、改変もできません。 OSSとフリーウェアの大きな違いは、この 「ソースコードが公開されている」 ことと、 「改変できる」 という点です。 OSSは皆で使おう!という精神が強くあります。 「世界中皆で使う」ものであるはずなのに、2024年10月にLinuxカーネルのメンテナーからロシア人が削除されたことが物議となりました。 以下は Linuxカーネルのメーリングリストのアーカイブが公開されたもの の一部です。差分情報がdiffとして記録されていますが、ロシアのドメインを示す「.ru」のメンテナー「M」が「-」で削除されていることが分かります。 Linuxの生みの親である Linus Torvalds 氏自ら「 I’m Finnish. Did you think I’d be supporting Russian aggression? (フィンランド人の私がロシアの侵略を支持すると思いますか?)」と言及しています。 これは OSS の定義に反するのではないか?と物議を醸しました。 OSSライセンスの探し方 それでは、OSSライセンスが具体的にどのように表記されているのか、見てみましょう。 例えば、Tomcatを見てみます。 ソースコードはGitHubで公開されています。 Apache Tomcatのソースコード OSSライセンスの記載場所は以下の4か所です。 ソースコード群に同梱されているテキストファイル ソースコードファイルのヘッダ プロジェクト公式Webサイト 製品に組み込みのドキュメント 「1.ソースコード群に同梱されているテキストファイル」を見てみましょう。 以下はソースコード群の中身です。 OSSライセンスは、ソースコード群にテキストファイルで同梱されています。 LICENSEファイル を開くと以下のようになっています。 Apache License Version 2.0 であることが明記されています。 Apache LicenseはOSSライセンスの1つです。 ここに書かれた http://www.apache.org/licenses/ をクリックすると、ライセンスの内容を見ることができます。 OSSライセンスを適用する では、自分のソフトウェアにApacheライセンスを適用する方法を紹介します。 適用イメージ 自分のソフトウェアにApacheライセンス適用する方法 全ソースコードの各ファイルの冒頭に、指定されたライセンスの告知文(license notice)を追加する必要があります。 例えばApacheライセンスの場合、 Apacheライセンス に明記されています。 付録: Apache Licenseの適用の仕方 あなたの製作物にApache Licenseを適用するときは、次の定型文を添付してください。ただし、"[]"で囲まれている部分は、あなた自身の識別情報に置き換えてください(その際、角括弧は取り除きます)。また、この文言を該当するファイル形式に合ったコメント構文で囲んでください。さらに、第三者アーカイブ内での識別を容易にするため、ファイル名またはクラス名ならびに趣旨説明が著作権表示と同じ「印刷ページ」に現れるようにすることをお勧めします。 Copyright [yyyy] [著作権所有者の名前] Apache License Version 2.0(「本ライセンス」)に基づいてライセンスされます。あなたがこのファイルを使用するためには、本ライセンスに従わなければなりません。本ライセンスのコピーは下記の場所から入手できます。 http://www.apache.org/licenses/LICENSE-2.0 例えば、Tomcat も Apache ライセンスを適用したソフトウェアです。 ossライセンスの探し方 で紹介した Tomcat が公開された GitHub の中から、jav aのファイルを選んでみましょう。 全てのjavaのソースコードの先頭にライセンスが記載されていることが確認できます。 OSSライセンスソフトを同梱する方法 次に、自分のソフトウェアにApacheライセンスを適用したTomcatを同梱する方法をご案内します。 さきほどの 自分のソフトウェアにApacheライセンス適用する方法 では、全てのソースコードの先頭にライセンスを表記しないといけませんでしたが、同じことをしなければいけないでしょうか? 答えは No です。 Tomcat内にあるLICENSEファイルとNOTICEファイルを入れ、readmeファイルにTomcatが入っていることを明記すればよいだけです。 今日のオープンソースの話はここまでにします。 次回は コピーレフト オープンソースにまつわるリアルな事例 についてご紹介します。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post オープンソースライセンスとは 1 first appeared on SIOS Tech. Lab .
アバター
はじめに アプリケーションのコンテナ化が一般的になった現在、コンテナイメージをどこに保存し、どうやって配布・管理するかは非常に重要な課題となっています。 本記事では、コンテナプラットフォーム向けのコンテナレジストリ選択肢として Red Hat Quay・Harbor・パブリッククラウドのマネージドイメージレジストリそれぞれの特徴について解説します。 各コンテナレジストリソリューションの概要 Red Hat Quay Red Hat QuayはRed Hat社が提供する セルフホスト型 のオープンソースコンテナレジストリです。 オープンソース版と有償版が存在し、アプリケーションの特性に応じて選択できます。 特徴 GitHubなどソースリポジトリからのプッシュイベントをトリガーにした自動ビルド機能が充実 地理的レプリケーションな機能によりグローバルにイメージを分散させることで多拠点環境下における高可用性と高パフォーマンスを実現 Red Hat社が提供する信頼性ある有償サポート付き RH社製品であるOpenShiftとの統合のしやすさ 開発からコンテナプラットフォームへのデプロイを高度に自動化したい開発チームや 既にOpenShift環境を利用しているチームなどでの利用が特に見込まれます。 Harbor HarborはVMware社が開発した セルフホスト型 のオープンソース コンテナレジストリです。 CNCFプロジェクトとなっており、他ツールとの連携も考慮しながら選択できます。 特徴 Kubernetesとの連携を前提とした設計で、幅広いコンテナプラットフォームに導入しやすい オープンソースなので、コストを抑えて導入することが可能 マルチテナント対応:複数プロジェクトを1つのインスタンス内で分離管理可能 アクセス権限やセキュリティポリシーなどを細かく制御することが可能 プロジェクト単位の設定、プロジェクトごとの分離 コンテナに関する知識(Docker、Kubernetes、Helmなど)が豊富でオープンソースを好む文化を持つチームや 設定のカスタマイズ性の高さを重視するチームなどでの利用が見込まれます。 パブリッククラウドのマネージドイメージレジストリ Amazon Elastic Container Registry(ECR)、Azure Container Registry(ACR)、Google Artifact Registry(旧GCR)など、クラウドベンダーが提供する フルマネージド型 のコンテナレジストリです。 特徴 インフラの構築・運用・スケーリングが不要。高可用性・冗長化もベンダー側で担保 IAMやサービスアカウントでの権限管理が容易 クラウドプロバイダーが提供するCI/CDとの統合が容易 AWS: Code Pipelineなど パブリッククラウドが提供するコンテナプラットフォームを利用しているチームや レジストリ運用をマネージド化させて運用負荷を削減したいチームなどでの利用が見込まれます。 まとめ コンテナレジストリはセキュリティ面での考慮やCI/CDにおいても起点となる重要なコンポーネントです。 各パブリッククラウドでマネージドサービスとして提供される中でOSSや有償ソリューションとしても、セルフホスト型でのソリューションが選択肢として多く存在します。 他のコンポーネントとの連携や運用負荷等を考慮しながらコンテナレジストリについては選定していく事が大きなポイントとなってきます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post コンテナレジストリを選ぶなら?Quay / Harbor / マネージドサービスそれぞれの概要解説 first appeared on SIOS Tech. Lab .
アバター
こんにちは、RAG StarterPackチームです。 日頃より多くの皆さまにご利用いただき、心より感謝申し上げます。 さて、本日2025年4月1日、私たちは重大な決断をいたしました。 「RAG スターターパック、Internet Explorerに完全対応いたしました。」 なぜIE対応なのか? 現代のブラウザは進化を遂げましたが、あの独特の青いeマークに懐かしさを感じる方も多いはず。  「RAG × レトロでエモい体験を!」という熱い思いから、  IE6〜IE11までの完全対応 に踏み切りました。 機能ハイライト Chat UIの“謎のズレ”を忠実に再現 ボタンの角丸が消滅するノスタルジー RAGの回答を3秒遅らせて、ダイヤルアップの気持ちに! ActiveX風の謎ポップアップも出るかも?(仕様です) 開発裏話 私がエンジニアになった5年前 Reactで開発中にChrome, Edgeのテストは通過して最後のIEで正常に動作せずとてもつらかった日々 「DOCTYPEを外したら動いた!」と歓喜したあの日 IEのおかげで進捗が出ず爆詰めされたあの日 実装をしているときには悩みの種でしたがいざいなくなると寂しいものです お問い合わせ方法 ブログを閲覧いただきありがとうございました。 もしRAGに興味のある方がいらっしゃいましたら こちら からご連絡お待ちしています! Happy April Fools! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【重要なお知らせ】RAG スターターパック、Internet Explorerに完全対応しました(IE6もOK) first appeared on SIOS Tech. Lab .
アバター
こんにちは、伊藤です。今回は、Application Gatewayのカスタムエラーページを構成する手順を紹介します。 Application Gatewayではデフォルトのエラーページが用意されていますが、エラー番号とエラー内容が簡易に表示されるにとどまっています。そこで、カスタムエラーページを構成していきます。 Application Gatewayの作成 Application Gatewayの解説は過去記事で紹介されているので、参考にしてみてください。 参考: https://tech-lab.sios.jp/archives/30628 Application Gatewayを作成します。今回は以下の設定とします。 基本 フロントエンド バックエンド 構成 ルーティング規則(リスナー) ルーティング規則(リスナー)ではカスタムエラーページを設定する項目があり、リスナーごとにカスタムエラーページを設定できます。今回は、Application Gateway全体(グローバルレベル)でカスタムエラーページを設定するため、ここでは未設定とします。 ルーティング規則(バックエンドターゲット) Application Gatewayの作成後、概要のフロントエンドパブリックIPアドレスを確認し、アクセスします。バックエンドターゲットが存在しないのでエラーとなり、デフォルトのエラーページが表示されます。 ストレージアカウントの作成 カスタムエラーページを配置するストレージアカウントを作成します。今回は以下の設定とします。 基本情報 詳細 ネットワーク データ保護 暗号化 ストレージアカウントの作成後、コンテナ―にてカスタムエラーページを配置します。 「errorpage」というコンテナーを匿名アクセスレベル「BLOB」で新規作成し、その中にhtmlファイル(502.html)を作成します。カスタムエラーページの条件は以下を参考にしてみてください。 参考: https://learn.microsoft.com/ja-jp/azure/application-gateway/custom-error 502.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>エラー 502 - 不正なゲートウェイ</title> </head> <body> <div class="error-container"> <h1>エラー 502 - 不正なゲートウェイ</h1> <p>申し訳ありませんが、サーバーへの接続に問題が発生しました。</p> </div> </body> </html> htmlファイルのプロパティを開いてURLを確認し、アクセスします。アクセスでき、エラーページが表示されることを確認します。 Application Gatewayのカスタムエラーページ設定 Azure PowerShell を使用して、カスタム エラー ページ(グローバルレベル)を設定します。 $appgw = Get-AzApplicationGateway -Name testgw -ResourceGroupName <resource-group-name> $updatedgateway = Add-AzApplicationGatewayCustomError -ApplicationGateway $appgw -StatusCode HttpStatus502 -CustomErrorPageUrl "https://testgwerrorpage.blob.core.windows.net/errorpage/502.html" Set-AzApplicationGateway -ApplicationGateway $appgw カスタムエラーページの確認は以下のコマンドを実行します。 $appgw = Get-AzApplicationGateway -Name testgw -ResourceGroupName <resource-group-name> $appgw.CustomErrorConfigurations カスタムエラーページがすでに設定されており、設定変更したい場合は以下のコマンドを実行します。 $appgw = Get-AzApplicationGateway -Name testgw -ResourceGroupName <resource-group-name> $updatedgateway = Set-AzApplicationGatewayCustomError -ApplicationGateway $appgw -StatusCode HttpStatus502 -CustomErrorPageUrl "<errorpage-url>" Set-AzApplicationGateway -ApplicationGateway $appgw カスタムエラーページを削除する場合は、以下のコマンドを実行します。 $appgw = Get-AzApplicationGateway -Name testgw -ResourceGroupName <resource-group-name> $updatedgateway = Remove-AzApplicationGatewayCustomError -ApplicationGateway $appgw -StatusCode HttpStatus502 Set-AzApplicationGateway -ApplicationGateway $appgw 参考: https://learn.microsoft.com/ja-jp/powershell/module/az.network/?view=azps-13.3.0#application-gateway カスタムエラーページの設定後に、Application Gatewayにアクセスします。カスタムエラーページが表示されることを確認します。 ストレージアカウントのネットワーク設定 ここまでのストレージアカウントは、全てのパブリックネットワークアドレスからアクセスが許可されています。そのため、Application Gatewayのサブネットとカスタムエラーページを管理するクライアントPCからのみアクセスできるようにします。 ストレージアカウントの「ネットワーク」を選択すると、パブリックアクセスの設定が表示されます。「Manage」を選択して設定を変更します。 「Enable from selected networks」を選択して特定のネットワークからのアクセスを許可し、許可するネットワークとしてApplication GatewayのサブネットとクライアントPCのIPアドレスを設定し、設定保存します。 アクセス許可されていないIPアドレスからカスタムエラーページのURLにアクセスできないことを確認します。 Application Gatewayのカスタムエラーページ設定を変更する場合は、Azure PowerShell からカスタムエラーページのURLにアクセスできず設定変更に失敗するため、再度全てのパブリックネットワークアドレスからのアクセスを許可してから設定する必要があります。そのため、次のカスタムエラーページ設定を変更せずエラーページの内容を変更する方法が望ましいです。 カスタムエラーページ設定を変更せずエラーページの内容を変更したい場合は、ストレージアカウントにあるhtmlファイルのプロパティから編集することができます。 編集後のhtmlを反映するためには、Application Gatewayで任意の構成更新を実行して、ファイルのキャッシュを手動で更新します。 例えば、新しいリスナーを追加します。 まとめ 今回は、Application Gatewayのカスタムエラーページを構成する手順を紹介しました。 Application Gatewayの設定の参考になれば幸いです。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Application Gatewayのカスタムエラーページを構成する first appeared on SIOS Tech. Lab .
アバター
サイオステクノロジー武井です。Microsoftがグローバルで開催しているAIのイベント「Microsoft AI Tour」の東京バージョンに、Microsoft MVPとして、ブース対応とハンズオンのサポートスタッフ対応として参加いたしました。そのレポートになります。 東京ビッグサイトで行われたイベントなのですが、たくさんの来場者がいらっしゃって、熱気に包まれたイベントでした。 バッジのピックアップ 翌日の下見も兼ねて、前日にチェックインしてバッジを受け取りました。前日に受け取ることができるのは非常に助かりますね。 そしてその時に基調講演の優先席チケットもいただきました。これは非常にありがたいですね。 基調講演 そして、基調講演当日、Reservedと書いてある席に座ることができましたが、そこそこ後ろの方でした。最前列はおそらくメディアと、Microsoftのパートナー企業の方々が座っていたのだと思います。 豪華なライティングとスモークが炊かれていました。そんなかっこいいステージで、基調講演が行われました。そして、今回の本丸であるMicrosoftのCEO、Satya Nadella氏の登場です。 基調講演の内容はまさに、 AIエージェント 一色でした。AIエージェントで業務を革新していくぞーみたいな話から始まりました。 そしておなじみCopilot Stackですね。 一番上のCopilotはいわゆるユーザーインターフェースの層です。WordやPower PointなどのビジネスアプリケーションにCopilot(副操縦士)として、業務を補佐するためのUIが組み込まれているぞーみたいなお話。 そしてさらに、Resaercher agentを始めとした賢いエージェントがMicrosoft 365に搭載されるという話です。様々な業務データを分析して、より高度な推論を行うエージェントが業務の補佐を行うという話でした。 AgentsはMicrosoftが持っている様々なデータとシームレスに繋がりますという話です。Agentはもちろんデータなくして動かないので、このように様々なデータと繋がることができるのは非常に強力です。 そして、弊社が最も得意とする「Copilot & AI stack」です。 日本の東西リージョンが強化されるようです。これはよいことですね。Agentの利用に伴い、さらなるコンピューティングリソースの需要が増えることが予想されるため、リージョンの強化は非常に重要です。 そしてAzure AI Foundryです。これは、本イベントでもイチオシでありますし、私もワークショップを担当したAzure AI Agent Serviceの管理には欠かせないものです。今後、Azure AI Foundryの機能が強化されることが予想されます。 JALさんは、ローカルLLMをFine Tuningして、自社の業務に適用しているという話でした。これは非常に興味深いですね。ローコストで、高精度なAIモデルを構築するための手法として、ローカルLLMをFine Tuningするというのは非常に有用です。今後の動向に期待大です。 ということで、まさにAIエージェント一色の基調講演でした。   私が担当したブース 私は、Azure Application Platofromのブースを担当いたしました。Azure上でアプリケーションをどうっするための様々なプラットフォーム(Azure App Service、Azure Functions、Azure Container Appsなど)をご紹介するブースです。 他のブースに比べると、Copilot Stackでいうところの下の方に位置するレイヤーだったので、このブースにいらっしゃたお客様は非常にエンジニア寄りの方が多い印象でした。 やはり皆様、Azure上でAIアプリケーションを動作させるのに、どのようなプラットフォームがあるのか、どのような違いがあるのか、といったことに興味を持っていらっしゃいました。 Azureには、様々なアプリケーションプラットフォームがあるのですが、やはりAzure FunctionsがAIアプリケーションには非常に適しているということをお伝えしましたし、Azure Functionsをご検討されているお客様が多かったですね。私の推しも、Azure Functionsでした。やはり、Azure OpenAI ServiceのAPIのレスポンって非常に軽いリクエストとレスポンスのやり取りで、さらにステートレスなAPIなのでAzure Functionsが非常に適していると思います。 Azure AI Agent Serviceリリースの影響もあって、AIの基盤としてどれを選定したらよいのか、というご相談も多くありました。 AIは、そもそもそれを動かすためのアプリケーション基盤がないと動作しません。よって、AIアプリケーションを動かすためのアプリケーション基盤についての知識は非常に重要です。そのため、Azure Application Platformはこれからも重要な役割を担っていくと思います。   ごはん ウマウマなランチもいただけたので、とても満足でした。朝、昼、夜ともに食事が提供されました。 朝と夜は写真を取り忘れたのですが、昼の写真はあります。数種類のお弁当が提供されていました。 なんとその一つに、今半のすき焼き弁当がありました。これはすごい!!!と思い、早速いただきました。 ワークショップ 私は、以下のワークショップのサポーター(受講者の方のご質問に答える役割)を担当しました。 Azure AI エージェント サービスで最初のエージェントを構築する https://aitour.microsoft.com/ja-JP/sessions/a6cb7b8d-9f92-47e2-94dc-d58a0feb5d17 写真の通りワークショップは2回とも満席でした。 ワークショップではAzure AI Agent Serviceを使って、以下の役割を持つエージェントを構築するというものでした。 データベース(SQLite)からデータを取得するエージェント データベース(SQLite)から取得したデータをCode Interpreterでチャート図を作成するエージェント ベクトルデータベースから必要な情報を検索するエージェント Bingにアクセスして必要な情報を検索するエージェント 様々なユースケースに基づいて、エージェントを構築していき、その凄さを体感するものでした。 Azure AI Agent Serviceは、AIエージェントを開発するうえで非常に便利な以下の機能を提供しています。 スレッドという単位で会話履歴を永続化してくれる Bing SearchやCode Interpreterなどへのツールの利用が超簡単である OpenAPIなどを利用することで既存システムともシームレスに連携できる ワークショップの参加者の方々は、Azure AI Agent Serviceの簡単さに驚かれていました。これからも、Azure AI Agent Serviceは、AIエージェント開発の現場で非常に重要な役割を担っていくと思います。 ワークショップは大成功でした。(講師の本間咲来(さっくる)さん、Microsoft MVP河本さんと)   セッション セッションもたくさん開催されていました。私はブース担当とワークショップサポーターを担当していたので、多くのセッションには参加できませんでしたが、その中でも非常に気になったものを一つピックアップします。それは以下です。 Azure上でDifyを活用したAIアプリケーションの構築 https://aitour.microsoft.com/ja-JP/sessions/24da067e-6a85-437c-835d-8935467ba7cc Microsoft MVPとして、またLINE API Expertとしてご活躍されている平林さん(ひらりんさん)が登壇されていました。Azure上にDifyが稼働するためのアプリケーションインフラを構築するという内容でした。 DifyをAzure上に構築するための方法は、以下の3つがあります。 VM上に構築する Azure Kubernetes Service(AKS)上に構築する Azure Container Apps上に構築する このセッションでは、3の「Azure Container Apps上に構築する」をしていました。実は、Azure Container AppsでDifyを構築するためのTerraformは以下のGitHubリポジトリに公開されておるのですが、バージョンが古かったりして少々手を加えないと動きません。弊社でもそれは確認済みでした。 しかしながら、平林さんそれをBicepにリファクタリングして、Azure Container Apps上にDifyを構築する方法をご紹介されていました。スゴイです。以下のリポジトリでBicepのコードを公開されているとのことでした。 https://github.com/himanago/dify-azure-bicep 素晴らしいです!!   まとめ Microsoft AI Tour Tokyoは、AIエージェント一色のイベントでした。AIエージェントは、業務を効率化するための非常に強力なツールであり、今後ますますその重要性が高まっていくと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Microsoft AI Tourレポート first appeared on SIOS Tech. Lab .
アバター
概要 今までの回で、プラットフォーム上でコンテナとVMを統合管理することが出来るOCP-Virt, SUSE-Virt についてご紹介しました。 紹介の中でも、簡易的にその他仮想化基盤との比較も行ってきました。 今回は、今までにご紹介したOCP-Virt, SUSE-Virt, 加えてvSphereを含めて3製品の比較について記載します。 vSphereについても、今までの回で軽く触れていますが改めて概要と特徴について簡単に触れておきたいと思います。 OCP-Virt, SUSE-Virt の概要については下記の記事をご覧ください。 OCP-Virtの概要は こちら SUSE-Virtの概要は こちら vSphereの概要と特徴 vSphereは、仮想化基盤として提供され複数のVMを実行して管理することができます。 リソース使用率の最適化、スケーラビリティの向上のため企業環境での広範な採用実績を持っています。 主な機能としては、以下があります。 ESXiホスト 物理サーバー上で動作するハイパーバイザーで、仮想マシンの実行環境を提供します vCenter Server 複数のESXiホストを集中管理するためのサーバーで、VMの管理、リソースの最適化などを行います vMotion 実行中のVMをダウンタイムなしで他のESXiホストに移動する機能です。この機能によりシステムのダウンタイムを最小化することができます。 DRS(Distributed Resource Scheduler) VMのリソース需要に応じて、リソースの負荷を自動的に分散させる機能です vSphere HA ESXiホスト障害時にVMを自動的に他のホストに移動させ、可用性を高める機能です vSphere Replication VMのデータを別の場所に複製し、災害復旧を実現するための機能です。この機能により、データセンターの障害に対して迅速に復旧が可能になります 主な特徴は、以下があります。 柔軟性とスケーラビリティ vSphereは、小規模から大規模な環境までスケーリングが可能で、リソースの追加や削減が簡単に行えます また、ハイブリッドクラウド環境にも対応しており、クラウドとオンプレミス環境を柔軟に組み合わせて運用できます 効率的なリソース管理 VMごとにリソースの割り当てや優先順位を設定し、リソースの効率的な利用が可能です 高可用性とフォールトトレランス 自動フェイルオーバー用の vSphere HA や予測障害検出用の Proactive HA など信頼性の高い仮想化基盤環境を提供します 簡単な管理インターフェース 直感的な管理インターフェースを提供し、管理者がvSphere ClientやvCenter Serverなどを通じて、VMやリソースを簡単に管理できるように設計されています 前提情報 本記事では、現時点で以下のバージョンを対象としています。 OpenShift Virtualization OpenShift v 4.17 OpenShift Virtualization v4.17.3 SUSE Virtualization harvester v 1.4.0 VMware vSphere VMware vSphere 8.0 OCP-Virt、SUSE-Virt、vSphere の比較 OCP-Virt、SUSE-Virt、vSphere の主な機能の比較を記載します。 比較区分は以下の通りです。 〇:機能が存在するあるいは、他のソリューションと比較し優れている △:オプションの機能と組み合わせて実現が可能、また機能はあるが他のソリューションと比較すると制限や制約が大きい ✕:機能がない   表1:OCP-Virt、SUSE-Virt、vSphere の比較表 大項目 中項目 小項目 OCP-Virt SUSE-Virt vSphere VM基本機能 VM操作 VMの作成 〇 〇 〇 VMの編集 〇 〇 〇 VMの削除 〇 〇 〇 VMのクローン 〇 〇 〇 リソース拡張(CPU/メモリ/ボリューム) 〇 ボリュームの削減はできない リソース拡張はVMの再起動が伴う 〇 ボリュームの削減はできない リソース拡張はVMの再起動が伴う 〇 ボリュームの削減はできない リソース拡張はVM無停止で可能 VM/ノード管理 VMの可用性 VMの自動復旧機能 〇 runStrategyで、VMの自動復旧機能を実現 〇 runStrategyで、VMの自動復旧機能を実現 〇 VMのライブマイグレーション 〇 〇 〇 クラスターノードの可用性 クラスターノードの自動復旧機能 △ Workload Availability *1でクラスターノードの自動復旧を実現 ✕ 手動で障害ノードを復旧するか、新規ノードを用意 ✕ ホストの自動復旧機能は提供されていない VM管理 テンプレート機能 〇 〇 〇 クォータ(リソース制限)の設定 〇 △ Rancherとの統合でnamespaceのクォータ設定を実現 〇 CPU/メモリ/ストレージ I/Oの上限を指定可能 ホスト名の名前設定の可否 〇 〇 〇 VM内部からホスト名を変更することも可能 認証 認証基盤連携 ユーザー認証基盤との連携機能 〇 〇 Rancherとの統合で、ユーザー認証基盤との連携を実現 〇 ネットワーク 負荷分散 複数VMへのアクセスの負荷分散 〇 Routeを使用してL7の負荷分散を実現 Metal LBを使用して、L2,L3の負荷分散を実現 〇 組み込みのHarvester LBを利用して、L4の負荷分散を実現 △ アドオン、または外部ソリューションとの組み合わせで負荷分散を実現 DNS設定 DNS設定 〇 〇 〇 Pod/VMのIP固定化 Pod/VMの静的IP 〇 VMに付与した2つ目のネットワークで静的IP設定が可能 〇 VMに付与した2つ目のネットワークで静的IP設定が可能 〇 モニタリング&ロギング モニタリング VM/クラスターノードの標準モニタリング(CPU/メモリ/IOPS) 〇 〇 〇 VM / クラスターノードの死活監視 〇 〇 〇 ロギング VM / クラスターノード のロギング 〇 △ 外部のログサーバーとの統合でログデータの保存を実現 〇 データ管理 スナップショット スナップショット 〇 〇 〇 バックアップ バックアップ △ OADP Operatorインストールで実現 〇 △ ホストのバックアップは可能 VMのバックアップは、VADP *2を使用して別バックアップツールと連携することで実現 スケジューリング スナップショットのスケジューリング ✕ 〇 〇 バックアップのスケジューリング △ OADP Operatorインストールで実現 〇 ✕ セキュリティ マルチテナント 異なるテナントごとにリソースを論理的に分離する機能 〇 OCPのプロジェクト機能を使用して テナントごとにリソースの分離、セキュリティの強化を実現 △ Kubernetes標準の機能で簡易的なリソース分離が可能。Rancherと統合することで十分なマルチテナント機能を実現 ✕ アクセス制限 VM間または外部へのアクセス制限 〇 OCPのネットワークポリシーを利用してアクセス制御を実現 〇 〇 RBACでのVM操作・管理の制御 〇 OCPのRBACを利用して、プロジェクトに対するユーザーの権限制御を実現 △ Rancherと統合しRBAC機能を活用してユーザーの権限制御を実現 〇 ストレージ ボリューム 永続ボリューム 〇 〇 〇 分散ストレージ △ ODFをインストールして分散ストレージを実現 〇 △ Storage vMotionに相当する機能 ✕ ✕ 〇 ボリューム形式の種類 – ブロック ファイル – ブロック ファイル – ブロック ファイル ストレージ バックエンドのストレージ対応種類 〇 多くのストレージベンダーのCSIプラグインが提供 〇 多くのストレージベンダーのCSIプラグインが提供 〇 クラスター管理 バージョンアップ バージョンアップのプロセスの容易さ 〇 簡単にバージョンアップすることが可能 バージョンアップ中にVMを停止する必要はない 〇 UIから簡単にバージョンアップすることが可能 バージョンアップ中にVMを停止する必要はない △ 事前準備、バージョンアップ作業が入る 対応プラットフォーム 対応プラットフォーム 対応プラットフォーム – オンプレミス AWS – オンプレミス Equinix – オンプレミス 多くのクラウドベンダー *3 *1 Workload Availability:Workload Availability for Red Hat OpenShift *2 VADP:VMware vStorage API for Data Protection *3 多くのクラウドベンダー:VMware Cloud on AWS、Azure VMware Solution、Google Cloud VMware Engine、Oracle Cloud VMware Solution に対応している   各製品の利用ケース どの仮想化基盤も基本的な機能の共通点が多く、優れた環境を提供しますが、それぞれが特定の利用ケースに適している場合があります。 OCP-Virtの利用ケース OpenShift(OCP) の機能を利用した効率的な運用 OpenShift(OCP) 側のセキュリティ、モニタリング、ロギング、ユーザー管理などの様々な機能をOCP-Virtでも利用できます。既存のコンテナ運用と統合して管理することで効率的な運用を実現できます 基盤となるノードの復旧対応の効率化 Workload Availability for Red Hat OpenShift機能を用いて構成するノードの自動復旧・自動追加機能を活用して柔軟なノード管理ができます RHEL OSの利用 多くのRHEL OSのVMを構成するユーザーは、ライセンス費用低減のメリットを享受できます SUSE-Virt利用ケース 標準のデータ管理機能 Harvester標準に備わっているスナップショット機能、バックアップ機能およびそれらのスケジューリング機能を使用して、安定したデータ管理機能を素早く実現できます Rancherとの統合 既存のKubernetesクラスターがあり、KubernetesクラスターとHarvesterをRancherで統合管理することができます Rancherと統合することにより、ユーザー認証基盤の連携、テナント管理、RBACなどの機能を一元的に管理することができます コストを抑えた仮想化基盤の利用 OSSの性質から基本的に無償で利用でき、大きな投資をせずに堅牢な仮想化基盤を必要とするユーザーに適しています vSphere利用ケース シンプルなネットワーク構成 vCenterでの包括的なvSwitchの管理などを実現できます。また複数のネットワーク構成をする際もシンプルな方法での構成を提供しています より広範なストレージ機器の利用 ストレージ機器の選定の幅が広いため、他の仮想化基盤に対応していないストレージ機器を使用したいユーザーに適しています バージョンアップ運用の負担軽減 Kubernetes環境のリリースの速さに追従する必要がないため、バージョンアップに係る運用面の負担を軽減できます まとめ 今回は、今までご紹介したOCP-Virt, SUSE-Virtと vSphere を含めて3製品の比較をしてみました。 基本的な機能についてはどの製品も提供していますが、利用ケースに応じた選定の一要素として、いくつか比較ポイントが見えました。 仮想化基盤を比較する上でこの記事が比較の参考になれば幸いです。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OpenShift Virtualization, SUSE Virtualization, VMware vSphere の比較 first appeared on SIOS Tech. Lab .
アバター
今号では、Linux におけるパーミッションについて説明します! パーミッションとは パーミッションとは、Linux におけるファイルやディレクトリに対する アクセス権限 のことです。 一般ユーザとして使用している場合、あまりパーミッションについて意識することは少ないかもしれませんが、普段利用しているすべてのファイルやディレクトリにパーミッションが設定されています。 「誰が」「どのような操作を」許可されているかを決める重要な仕組みであり、パーミッションが適切に設定されていないと 不正アクセス や データの改ざん 、 誤ってプログラムを実行してしまう 可能性があります。 なお、パーミッションはファイルを作成するごとに自分で設定する必要はなく、システム側の設定とアプリ側の設定を組み合わせた上で、それぞれデフォルトのパーミッションが割り振られます。 適宜、パーミッションを変更しながら運用していくことになります。 パーミッションの考え方 Linux では、ファイルやディレクトリのパーミッションは下記 3つのカテゴリに分けられます。 Owner (所有者) :ファイルを作成したユーザ Group (グループ) :Owner が所属するグループ Other (その他) :Owner 以外の全ユーザ また、各カテゴリに対して下記 3つの権限を設定できます。 r (読み取り) :ファイルの読み取り権限 w (書き込み) :ファイルの書き込み権限 x (実行) :ファイルの実行権限 (プログラムやスクリプトを実行する場合) パーミッションの確認方法 最も簡単なのは、 ls -l コマンドを実行してファイルの情報を表示する方法です。 $ ls -l test.txt -rw-r--r--. 1 ykaino ykaino-group 0 Mar 24 12:00 test.txt 出力された各内容を、それぞれ説明します。 -rw-r–r– :パーミッションを表す文字列 1 :リンク数。通常は 1となるが、ハードリンクがある場合は増加する ykaino :Owner。ファイルを作成したユーザ名 ykaino-group :group。Owner が所属するグループ名 1234 :ファイルサイズ (byte 単位) Mar 20 10:00 :ファイルの最終更新日時 example.txt :ファイル名 (ディレクトリの場合はディレクトリ名) 続いて、パーミッションを表す文字列について説明します。 上記で出力された文字列 ( -rw-r–r– ) を例にすると、 1文字目 (-):ファイルの種類。- は通常のファイル、d はディレクトリ、l はシンボリックリンクとなる 2~4文字目 (rw-):Owner (所有者) の権限。読み、書きは許可、実行は不可 5~7文字目 (r–):Owner がいるグループに所属する、他のユーザの権限。読みは許可、書き、実行は不可 8~10文字目 (r–):上記に該当しない他のユーザの権限。読みは許可、書き、実行は不可 なお、ls -l コマンドの他に、 getfacl コマンドを実行してファイルの情報を表示する方法もあります。 $ getfacl test.txt # file: test.txt # owner: ykaino # group: ykaino-group user::rw- group::r-- other::r-- デフォルトのパーミッション 上記の test.txt ファイルでは、ユーザに読み、書きの権限があり、グループおよびその他のユーザには読み取り権限のみが与えられていました。 これは、ファイルやディレクトリにデフォルトで設定されるパーミッションがあらかじめ決められているために上記のような結果となっています。 デフォルトのパーミッションは umask というパラメータで設定されます。 ls -l コマンドの実行結果では、各権限を r (読み取り)、w (書き込み)、x (実行) で表現しましたが、 umask では各権限を数値で表現します。 r (読み取り) :4 w (書き込み) :2 x (実行) :1 例えば、読み取りと書き込みの権限がある場合は 4 + 2 = 6 、読み取りと実行の権限がある場合は 4 + 1 = 5 のように考えます。 Linux ファイルシステムでは、ファイルの基本パーミッションが 666 (読み書きのみ、実行権限なし) 、ディレクトリの基本パーミッションが 777 (読み書き実行すべて可能) になっています。そこから umask の値を引き算して、最終的なパーミッションが決定します。 現在の umask の値は umask コマンドで確認できます。 # umask 0022 ファイルの場合は 666 から 022 を引いて 644 (Owner は読み、書き権限あり、グループおよびその他のユーザには読み取り権限あり) 、ディレクトリの場合は 777 から 022 を引いて 755 (Owner は読み、書き、実行権限あり、グループおよびその他のユーザには読み、実行権限あり) になります。 umask の値を一時的に変更する場合は umask コマンドを実行します。 $ umask 027 永続的に変更する場合は ~/.bashrc もしくは ~/.profile などのファイルに、下記の様な umask の設定を追加します。 umask 027 次号では、パーミッションの変更方法や、特殊なパーミッションについて説明します! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 知っておくとちょっと便利!ファイルのパーミッションについて1 first appeared on SIOS Tech. Lab .
アバター
こんにちは! 今月も「OSSのサポートエンジニアが気になった!OSSの最新ニュース」をお届けします。 Google は 3月の Pixel Feature Drop (Pixel 端末向けアップデート) で、Linux のネイティブアプリを導入しました。 グーグル、「Android」向けに「Linux」ターミナルを追加–まずは「Pixel」で導入 https://news.yahoo.co.jp/articles/d3473c2e2a2cd2d4e084b2e4b65d2d9953d0277f Google は、日本企業のサイバーセキュリティ意識の向上と専門人材の育成を支援する「Japan Cybersecurity Initiative」を立ち上げました。 日本のサイバーセキュリティの底上げに向けた産学官連携「Japan Cybersecurity Initiative」を設立 https://blog.google/intl/ja-jp/company-news/technology/cybersecurity-initiative/ 2025/3/19、FinOps Foundation の創始者が執筆した「クラウドFinOps 第2版 ー協調的でリアルタイムなクラウド価値の意思決定」の日本語書籍が出版されました。 国内初、Linux Foundation傘下のFinOps Foundationが公認した『クラウドFinOps 第2版 ー協調的でリアルタイムなクラウド価値の意思決定』の日本語書籍が本日出版 https://prtimes.jp/main/html/rd/p/000000133.000015260.html ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【2025年3月】OSSサポートエンジニアが気になった!OSS最新ニュース first appeared on SIOS Tech. Lab .
アバター
サイオステクノロジーの菊地啓哉です。思ったより間が空いてしまったのですが、 前回 の記事の続きになります。 ECDSA や zk-STARK などでは有限体がベースにあるので、 前回 の体の話から、有限体の話に進んでいこうと思います。 自分が有限体について調べた時には、体の拡大に急に既約多項式が出てきて飲み込みにくいところがあったので、そのあたりに少しだけ間の内容を入れています。 数学に詳しくないエンジニアの体入門 数学に詳しくないエンジニアの有限体 巡回群 前回 も少し触れましたが、 巡回群 についてご紹介したいと思います。この群は、ただ1つの元で生成される群で、 mod n(nで割った余り)の中での足し算もこれの具体例の1つとなります。 例えば、単位元 e、生成元 g、要素数 4 の巡回群 G について考えると、 g ○ g = g 2 のように演算 ○ の繰り返しを累乗のように表現するなら G の元は のように表すことができます。これは整数の足し算の4で割った余りが元の数の4で割った余り同士の足し算(をさらに4で割った余り)と同じです。 13(4で割ると余り1)+ 22(4で割ると余り2) = 35(4で割ると余り3) 6(4で割ると余り2)+ 35(4で割ると余り3) = 41(4で割ると余り1) 有限体 有限体の性質 体のうち、元が有限個のものを 有限体(ゆうげんたい、Finite Field) と呼びます。話がとても難しくなってしまうので導出はしませんが以下のような性質を持ちます。 3点目については、有限群の性質から導けます。1点目と2点目は実力不足で分かりやすくまとめられなさそうなので諦めました(これは3点目のフェルマーの小定理にかけているわけではありません)。 有限体の例 mod 3 の足し算、掛け算で体を考えます。ここでは、mod 3 であることを忘れないように、下添え字の 3 をつけます。可換であることを踏まえて通常の計算と異なるのは、以下の3つです。 mod 3 だということを考えれば特に問題無いかと思います。 とても簡単ですが、「体の元から零元を除くと乗法に関して巡回群となる」について確認しておくと、 となるので、零元を除くと、乗法に関して元の数が2の巡回群となっていることがわかります。 なんで、こんな元の数が少ない体にしたかと言えば、後でこの体を拡大します。 有限体で方程式を解く 前回 の記事で、体の中で方程式を解くのが普通にやってるのと同じと書いたので、実数の世界で -2 と 1/2 が解の二次方程式を mod 3 の体で解いてみたいと思います。この二次方程式は次のように書けます。 これは、特に上に書いた計算を利用することで、1 3 + 2 3 = 0 3 、2 3 * 2 3 – 1 3 = 1 3 – 1 3 = 0 3 なので、 x = 1 3 , 2 3 と解くことができます。こういった具合に、有理数係数の方程式は割と有限体の方程式にそのまま考え直すことができます。ただし、0除算があってはいけないので、この mod 3 の体で考えるには分母が3の倍数の解があると上手くいきません。 この方程式の両辺を 2 3 倍して、式を展開してみましょう 当然と言えば当然ですが、最後の式を見ると、解と係数の関係が成り立っていることも見て取れると思います。 Bitcoin や Ethereum では、secp256k1 と呼ばれる楕円曲線(のパラメータ)を採用しているようで、そこでは有限体の元の数 p は 2 256 – 2 32 – 2 9 – 2 8 – 2 7 – 2 6 – 2 4 – 1 という、とても大きな値を使っています。 完全に余談ですが、ECDSAで楕円曲線上の点と点の足し算を定義して、その繰り返しとしてスカラー倍を定めているのに、表記によってはいつのまにか、スカラー倍の部分が分数で表現されていたりするのは、繰り返しの数を表す係数側が有限体の元であり、逆数は逆元を意味しているので、分数を計算すれば自然数になることなどを前提としていますね。 せっかく有限体の話を書いてきたので、要素数が素数の有限体を拡大するところに触れたいと思います。その前に少し寄り道をします。 有理数と平方根 前回の記事の群の例を見ていただくとわかると思いますが、群となれない理由としてありがちなのは、”逆元が存在すること”が満たされない、かなと思います。 中学で平方根(ルート)を学習された/されることと思いますが、そこで分母の有理化というものがあったと思います。逆元の存在とか、演算が閉じていることとか、そんなことと絡めて授業を受けたという方は恐らくいらっしゃらないとは思いますが、「2乗すると有理数になる」、「分母に平方根があっても分母を有理化できる」など、乗法について体となかなか相性の良い性質を持っています。有理数 a, b と素数 p で a +b√p で表されるような数について掛け算と割り算は以下のようになります。(ここでは素数である必要性はないのですが、 √p が確実に有理数でないようにわかりやすく素数としています) どちらの計算結果も「有理数 a, b と素数 p を用いて a +b√p で表される形」になっています。このことから、「なんか、平方根を使っていい感じの体を作れそうな空気」が出てきます。足し算引き算も問題無さそうなのはおわかりかと思いますのでスキップします。 mod 3 の体を拡大する 先ほどの mod 3 で考えていた体に √2 に相当する数 β を仲間に加えてみます。ここで、 β*β = β 2 = 2 3 です。また、 β 自体が mod 3 でどうなるかは考えず、その係数で mod 3 を考えます。別の表現をするなら、 1 3 + 2 3 = 0 3 の両辺に β を掛けて次の式が得られます。 この集合は、mod 3 の体の元 a, b を用いて、 a+bβ で表すことのできる 9個の元を作れることがわかります。また、この集合が体になることがわかります。 逆元がわかりにくいところだと、 となります。あと、有限体の性質のうち、零元を除いて乗法が巡回群となるということについて、ちょっと計算してみると、 のように、 1 3 + β を生成元にできることがわかります。(上の結果を見て想像できるように他にも生成元にできる元があります) ここでは、 mod 3 の有限体に β 2 = 2 3 となる元 β を加えて、新しい体を作りました。 これと同じように一般的には、既約多項式という「より低次の多項式で因数分解できない多項式」を使って、”既約多項式 = 0”で定義される新しい元( α とします)を定め、体を拡大します。また、その時、拡大前の体の元の数を p 、既約多項式の次数を n とすると、各 α k (k=0, 1, …, n-1) の係数が p 種類取り得るので、作られる体の元の数は p n となります。今回の例の β であれば、 β 2 = 2 3 の右辺が 0 になるように変形して既約多項式は β 2 + 1 3 となります。 先の例よりも次数が上の具体例として、これまで扱ってきた mod 3 の体に α 3 + 2 3 α + 1 3 = 0 3 で定められる α を追加すると、四則演算によって、 a + b α + cα 2 で表される 3 3 個の元ができ、それらによって、体が作られます。ここで、 a, b, c は mod 3の体の元で、1つ前の体と比べると、 cα 2 が増えた分、元の数が3倍になっています。 α の多項式の次数が2次以下となるのは α 3 = -2 3 α – 1 3 なので、 α の3次以上の項は次数を落とすことができることからわかります。 おわりに 前回同様、全く技術的なことには触れずに数学の体について書きました。駆け足ではありますが、有限体のがどのようなものか紹介しました。難しくならないようにできるだけ深入りせずにざっと書いてみましたが、いかがだったでしょうか。次はどのように有限体が使われているのか書ければ良いなと思っています。 ということで、今回はここで終わりとなります。 またかきます またね ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 数学に詳しくないエンジニアの 有限体 first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの遠藤です。今回はNext.js 15でApp Routerを利用した際のStatic Exportsについて整理します。 Static Exportsとは? Static Exportsは、build時に、アプリケーションを静的コンテンツとしてエクスポートできる機能です。Static Exportsを利用せずにNext.jsのアプリケーションを公開する場合はNode.jsランタイムが必要となりますが、Static Exportsを利用してbuildをかけた場合はクライアントサイドだけで動くコードが吐き出されるので、Node.js環境が必要なくなるのがメリットです。 Static Exportsのやり方 公式ガイド : https://nextjs.org/docs/app/building-your-application/deploying/static-exports next.config.jsにて output: ‘export’, の記述を追加すると、静的エクスポートを利用できるようになります。 import type { NextConfig } from "next"; const nextConfig: NextConfig = { output: "export", }; export default nextConfig; その後、 npm run build を実行すると、 out ディレクトリにHTML/CSS/JS assetsが作成されます。 Static ExportsでServer Componentsを利用した場合の挙動 名前にserverとついているためややこしいですが、Static ExportsでServer Componentsを使用することは可能です。ただ、注意点があり、ビルド時にServer Componentsが実行されて生成されたコンテンツに固定されます。 わかりやすく説明するために、ランダムに4桁の数字を返してくれるAPIのFetchのClient Components上で行った場合と、Server Components上で行った場合に分けて考えてみましょう。 通常のデプロイを利用してNode.jsサーバー上にデプロイ Client Components上で行った場合とServer Components上で行った場の両方でリクエストが来るたびにフェッチを行い、毎回ランダムな値(一回目 : 3592, 二回目 : 5285 …)を表示します。 Static Exportsを利用 Client Components上で行った場合 リクエストが来るたびにフェッチを行い、毎回ランダムな値(一回目 : 3592, 二回目 : 5285 …)を表示します。 Server Components上で行った場合 ビルド時にServer Componentsが実行されて一度フェッチが行われます。(値は6472)。サーバーを起動してリクエストを送ると以降6472だけ表示され続けられます。 Dynamic Routesの使用に制限がある Next.jsにはリクエストに応じて動的データからルートを作成してくれるDynamic Routesという機能があります。以下のように[slug]ディレクトリを作成することで使用することができます。 ルート URLの例 params app/blog/[slug]/page.js /blog/a { slug: 'a' } app/blog/[slug]/page.js /blog/b { slug: 'b' } app/blog/[slug]/page.js /blog/c { slug: 'c' } このDynamic Routesの機能ですが、リクエストごとにサーバーでルートを作成するといった機能のため、基本的にはStatic Exportsでは利用することが出来ません。 ただ、Dynamic Routesに、 generateStaticParams という関数を利用することで、ビルド時にfetchを行い、静的にルートを作成することができます。この機能を利用することで、Static Exportsを利用した場合でも静的にルートを作成したページに関してはアクセスできるようになります。 そのほかサポートされていない機能 Static Exportsではサーバーで動く機能は使用できないため、MiddlewareやServer Actionsといった機能を使用することが出来ません。2025/02の段階で使用できない機能は以下の機能です。 Dynamic Routes  with  dynamicParams: true Dynamic Routes  without  generateStaticParams() Route Handlers  that rely on Request Cookies Rewrites Redirects Headers Middleware Incremental Static Regeneration Image Optimization  with the default  loader Draft Mode Server Actions Intercepting Routes 公式ガイド : https://nextjs.org/docs/app/building-your-application/deploying/static-exports#unsupported-features まとめ 今回はNext.jsのStatic Exportsについてまとめました。特にServer Componentsについては曖昧な理解だったため今回実際にコードを書いて挙動を確認したことにより理解を確かにすることが出来ました。「Static ExportsでServer Componentsを利用しているのになぜbuildが通るんだろう?」といった疑問を持っていた方の助けになれば幸いです! ではまた~ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【Next.js】App Routerを利用した際のStatic Exportsについて整理する first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの和田です。今回はEthereumのウォレットアドレス(EOA)の本人確認をMetaMaskの署名機能を使って試したので、実装方法などを書いていきたいと思います。 署名の検証方法 署名の検証を実装するために、以下のツールを使用しました。 フレームワーク Next ライブラリ React Web3 ethers ブラウザ拡張機能 MetaMask 署名の検証プロセスは以下の手順で行います。 MetaMaskでウォレットを接続する MetaMaskで署名を作成する 署名とメッセージをサーバーに送信する サーバー側で署名を検証する クライアント側で署名の検証結果を受け取る 実装 それでは実装していきたいと思います。以下に実際に試したソースコードを記載します。 app/components/Web3Auth.tsx "use client"; import { useState, useEffect } from "react"; import Web3 from "web3"; declare global { interface Window { ethereum?: any; } } const Web3AuthClient = () => { const [account, setAccount] = useState<string | null>(null); const [message, setMessage] = useState<string>("Sign this message to login."); const [web3, setWeb3] = useState<Web3 | null>(null); const [recoveredAddress, setRecoveredAddress] = useState<string>(""); useEffect(() => { if (window.ethereum) { setWeb3(new Web3(window.ethereum)); } }, []); const connectWallet = async () => { if (!web3) { alert("MetaMaskをインストールしてください。"); return; } try { const accounts = await window.ethereum.request({ method: "eth_requestAccounts", }); setAccount(accounts[0]); } catch (error) { console.error(error); } }; const signMessage = async () => { if (!web3 || !account) return; try { const signedMessage = await web3.eth.personal.sign(message, account, ""); // API に署名データを送信して検証 const response = await fetch("/api/verify-signature", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message, account, signature: signedMessage }), }); const result = await response.json(); if (result.verified) { alert("署名の検証に成功しました。"); setRecoveredAddress(result.recoveredAddress); } else { alert("署名の検証に失敗しました。"); } } catch (error) { console.error(error); } }; return ( <div className="p-4 border rounded shadow-md"> <h2 className="text-lg font-bold mb-2">Web3 認証</h2> {account ? ( <> <p> 接続したアカウント: <strong>{account}</strong> </p> <p> 復元されたアドレス: <strong>{recoveredAddress.toLowerCase()}</strong> </p> <button onClick={signMessage} className="px-4 py-2 bg-blue-500 text-white rounded mt-2"> 署名を検証する </button> </> ) : ( <button onClick={connectWallet} className="px-4 py-2 bg-green-500 text-white rounded"> ウォレットに接続する </button> )} </div> ); }; export default Web3AuthClient; app/api/verify-signature/route.ts import { NextRequest, NextResponse } from "next/server"; import { verifyMessage } from "ethers"; export async function POST(req: NextRequest) { try { const { message, account, signature } = await req.json(); if (!message || !account || !signature) { return NextResponse.json({ error: "Missing parameters" }, { status: 400 }); } const recoveredAddress = verifyMessage(message, signature); const isValid = recoveredAddress.toLowerCase() === account.toLowerCase(); return NextResponse.json({ verified: isValid, recoveredAddress }, { status: isValid ? 200 : 401 }); } catch (error) { console.error("Signature verification error:", error); return NextResponse.json({ error: "Internal server error" }, { status: 500 }); } } app/page.tsx import Web3Auth from "./components/Web3Auth"; export default function Home() { return ( <div className="min-h-screen flex items-center justify-center bg-gray-100"> <Web3Auth /> </div> ); } 動作確認 上記で作成したアプリを動かしてみます。 npm run dev で起動し、 localhost:3000 にアクセスします。すると以下のような画面が表示されます。 ウォレットに接続するをクリックするとメタマスクのパスワードを聞かれるので入力してロック解除します。 すると以下のような画面が表示されます。メタマスクで選択されているアカウントが表示されています。 署名を検証するをクリックします。クリック後、以下のような署名要求のポップアップが表示されるので、確認をクリックします。 確認をクリック後、以下のように署名の検証結果が表示されます。 OKをクリックすると、以下のように復元されたアドレスが表示されます。サーバー側で署名から復元したアドレスと接続したアカウントのアドレスが一致していることが確認できました。 まとめ 今回は MetaMask の署名機能を利用して EOA の所有者を検証する簡単なアプリケーションを実装しました。署名の検証プロセスを通じて、EOA 所有者の本人確認を実現できることが確認できました。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Ethereumウォレットアドレスの署名検証試してみた first appeared on SIOS Tech. Lab .
アバター