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

TECH PLAY

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

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

672

はじめに こんにちはサイオステクノロジーの小野です。 前回 はOpenShiftにGPUを追加する方法について解説しました。今回はそのGPUをクラスタ内で利用する設定の方法を解説します。 また、GPUはいくつかの会社が作成していますが、多くのAI開発ライブラリが対応しているNVIDIA製のGPUの設定を行います。 OpenshiftでGPUを利用するためのオペレータ Node Feature Discovery (NFD) オペレータ クラスター内のノードのハードウェア機能やカーネル機能を自動的に検出し、それらの機能をラベルとしてノードに追加するオペレータです。これによりノードにGPUがあることを検出できます。NVIDIA GPUオペレータを利用するために必要なオペレータです。 NVIDIA GPU オペレータ NVIDIA GPUを搭載したサーバーでGPUリソースを管理し、活用できるようにするためのオペレータです。 Intel Device Plugins オペレータ Intelのハードウェア機能をOpenshiftクラスタに認識させるために使用されるオペレータです。IntelのGPUを利用する際はこのオペレータを使用してください。 AWSにおけるGPU利用設定手順 設定手順概要 これからAWSに構築したOpenshiftでNVIDIAのGPUを利用する方法を解説します。手順は以下の流れで進んでいきます。 NFDオペレータの設定 NFDオペレータの正常性確認 NVIDIA GPU オペレータの設定 NVIDIA GPU オペレータの正常性確認 GPUモニタリングの設定 前 提条件 OpenShift:4.17 構築する環境:AWS(amd64) リージョン:us-east-2 クラスター名:ocp-test AWSにNVIDIAのGPUノードを追加済み NFDオペレータの設定 NodeFeatureDiscoveryオペレータのインストール 最初にOperator > OperatorHubでNFDと検索します。 OperatorHubでNFDと検索 Node Feature Discovery Operatorが2つ存在するのでRed Hatと書かれている方を選択して、インストールを押します。 NFD Operatorの説明 インストール時の設定は何も変更せず、インストールを押します。 NFDのインストール設定 インストールが完了するまで待ちます。 ステータスがSucceededになったらインストール完了です。 NFDのステータスがSucceededになったらインストール完了 CRの設定 (NodeFeatureDiscovery) 続いてカスタムリソースの設定を行います。インストール済みOperatorの画面からNFDオペレータをクリックします。 NFDオペレータの詳細画面に遷移するので、提供されるAPIからNodeFeatureDiscoveryのインスタンスの作成を押します。 NFDの詳細 NFDの設定は何も変更せず、作成を押します。 NFDのカスタムリソース編集 作成が完了するまで待ちます。 ステータスが「Conditions: Available Upgradeable」となっていれば作成完了です。 カスタムリソースのステータスがConditions: Available Upgradeableなら作成完了 NFDオペレータ正常性確認 NFDオペレータの導入によって、GPUノードにGPUのラベルが付与されていることを確認します。 以下のコマンドを入力します。 $ oc describe node | egrep 'Roles|pci' | grep -v master Roles:              worker                     feature.node.kubernetes.io/pci-1d0f.present=true Roles:              worker                     feature.node.kubernetes.io/pci-10de.present=true                     feature.node.kubernetes.io/pci-1d0f.present=true Roles:              worker                     feature.node.kubernetes.io/pci-1d0f.present=true Roles:              worker                    feature.node.kubernetes.io/pci-1d0f.present=true NFDオペレータの導入が正常に行われたら、GPUが搭載されたノードに「pci-10de」というラベルが付与されていることが確認できます。 NVIDIA GPU オペレータの設定 NVIDIA GPU オペレータのインストール 次にNVIDIA GPU オペレータのインストールを行います。Operator > OperatorHubでNVIDIAと検索します。 OperatorHubでNVIDIAと検索 NVIDIA GPU Operatorを選択してインストールを押します。 NVIDIA GPU Operatorの説明 インストール時の設定は何も変更せず、インストールを押します。 NVIDIA GPU オペレータのインストール設定 インストールが完了するまで待ちます。 ステータスがSucceededになったらインストール完了です。 NVIDIA GPU オペレータのステータスがSucceededになったらインストール完了 CRの設定 (ClusterPolicy) 続いてカスタムリソースの設定を行います。インストール済みOperatorの画面からNVIDIA GPU オペレータをクリックします。 NVIDIA GPUオペレータの詳細画面に遷移するので、提供されるAPIからClusterPolicyのインスタンスの作成を押します。 NVIDIA GPU オペレータの詳細 ClusterPolicyの設定は何も変更せず、作成を押します。 ClusterPolicyのカスタムリソース編集 作成が完了するまで待ちます。少し時間がかかるので注意してください(10分前後)。 ステータスが「State: ready」となっていれば作成完了です。 カスタムリソースのステータスがState: readyなら作成完了 NVIDIA GPU オペレータの正常性確認 GPUが動作するかの正常性の確認を行います。今回は2種類の正常性確認を行います。 サンプルアプリ実行 GPUを利用してベクトルの加算を行うサンプルアプリを実行します。(参考: https://docs.nvidia.com/datacenter/cloud-native/openshift/latest/install-gpu-ocp.html#running-a-sample-gpu-application ) 以下のコマンドを実行してpodを作成します。 $ cat << EOF | oc create -f - apiVersion: v1 kind: Pod metadata: name: cuda-vectoradd spec: restartPolicy: OnFailure containers: - name: cuda-vectoradd image: "nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda12.5.0-ubi8" resources: limits: nvidia.com/gpu: 1 EOF pod/cuda-vectoradd created コンテナのログでサンプルアプリが動作していることを確認します。 $ oc logs cuda-vectoradd [Vector addition of 50000 elements] Copy input data from the host memory to the CUDA device CUDA kernel launch with 196 blocks of 256 threads Copy output data from the CUDA device to the host memory Test PASSED Done GPUの状態確認 GPUの状態を表示するコマンドを実行します。(参考: https://docs.nvidia.com/datacenter/cloud-native/openshift/latest/install-gpu-ocp.html#getting-information-about-the-gpu ) nvidia-gpu-operator プロジェクトに変更します。 $ oc project nvidia-gpu-operator Now using project "nvidia-gpu-operator" on server " https://api.ocp-test.cpawstest.ps.staging-test.sios.jp:6443 ". pod名を取得します。 $ oc get pod -owide -lopenshift.driver-toolkit=true NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nvidia-driver-daemonset-417.94.202410090854-0-n7xjw 2/2 Running 0 28m 10.130.2.9 ip-10-0-18-161.us-east-2.compute.internal <none> <none> 取得したpod内でnvidia-smiコマンドを実行します。GPUのメモリ使用量、使用率、温度が確認できます。 $ oc exec -it nvidia-driver-daemonset-417.94.202410090854-0-n7xjw -- nvidia-smi Mon Oct 28 08:36:36 2024 +-----------------------------------------------------------------------------------------+ | NVIDIA-SMI 550.90.07 Driver Version: 550.90.07 CUDA Version: 12.4 | |-----------------------------------------+------------------------+----------------------+ | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |=========================================+========================+======================| | 0 Tesla T4 On | 00000000:00:1E.0 Off | 0 | | N/A 26C P8 14W / 70W | 1MiB / 15360MiB | 0% Default | | | | N/A | +-----------------------------------------+------------------------+----------------------+ +-----------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=========================================================================================| | No running processes found | +-----------------------------------------------------------------------------------------+ GPU のモニタリング設定 GPUの管理に必要なモニタリングの設定方法について説明します(参考: https://docs.nvidia.com/datacenter/cloud-native/openshift/latest/enable-gpu-monitoring-dashboard.html )。 GitHub の DCGM エクスポーター リポジトリから最新の NVIDIA DCGM エクスポーター ダッシュボードをダウンロードします。 $ curl -LfO https://github.com/NVIDIA/dcgm-exporter/raw/main/grafana/dcgm-exporter-dashboard.json % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 18114 100 18114 0 0 19498 0 --:--:-- --:--:-- --:--:-- 19498 namespaceにダウンロードしたファイルからconfigmapを作成します。 $ oc create configmap nvidia-dcgm-exporter-dashboard -n openshift-config-managed --from-file=dcgm-exporter-dashboard.json configmap/nvidia-dcgm-exporter-dashboard created configmapにラベルをつけて、管理者のコンソールでGPUのダッシュボードを確認できるようにします。 $ oc label configmap nvidia-dcgm-exporter-dashboard -n openshift-config-managed "console.openshift.io/dashboard=true" configmap/nvidia-dcgm-exporter-dashboard labeled 同様に開発者のコンソールでGPUのダッシュボードを確認できるようにします。 $ oc label configmap nvidia-dcgm-exporter-dashboard -n openshift-config-managed "console.openshift.io/odc-dashboard=true" configmap/nvidia-dcgm-exporter-dashboard labeled 作成されたリソースを表示し、ラベルがつけられていることを確認します。 $ oc -n openshift-config-managed get cm nvidia-dcgm-exporter-dashboard --show-labels NAME DATA AGE LABELS nvidia-dcgm-exporter-dashboard 1 90s console.openshift.io/dashboard=true,console.openshift.io/odc-dashboard=true コンソール画面のモニタリング > ダッシュボードに移動し、ダッシュボードのリストから「NVIDIA DCGM Exporter Dashboard」を選択します。 GPUの各種情報をコンソール画面でモニタリングできるようになりました。 GPUのモニタリング画面 最後に NFDオペレータとNVIDIAオペレータを導入することで、OpenShiftでGPUを取り扱えるようになりました。 これでようやくOpenShift AIを導入するための準備が完了しました。次回はいよいよOpenShift AIを導入する方法について解説したいと思います。 参考 NVIDIA GPU オペレータ: https://docs.nvidia.com/datacenter/cloud-native/openshift/latest/index.html Intel Device Plugins オペレータ: https://intel.github.io/intel-technology-enabling-for-openshift/development/device_plugins/README.html ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OpenShiftのGPU設定の方法について first appeared on SIOS Tech. Lab .
こんにちは、 PS-SL アプリチーム新卒 1 年目の織田です。 今回はLLM開発向けのフレームワークであるLangChainのサンプルコードを作成してみました。 コチラ から参照できますので、よろしければご活用ください。 サンプルコードを作成した理由 LangChainは非常に多種多様な機能を提供しており、どこから触れば良いのか分からず困ってしまう方もいらっしゃると思います。 また、ネット上には既に多くの方がLangChainのサンプルコードをまとめていますが、その多くが古いバージョンのもので2024年現在は使えない、あるいは非推奨の機能を用いている場合があります。 また、私は業務上AzureOpenAIを利用することが多いのですが、サンプルコードの多くにOpenAI APIが用いられています。名前はほぼ同じなのですが、実際に利用する際には細かい違いがあり、修正の手間がとても煩わしいです。 そこで、今回はAzureOpenAIユーザ向けに2024年10月時点で利用可能な基本的な機能をサンプルコード化しました。 使用方法 今回はGithub上でコードを公開しているので、直接ダウンローしていただくかgit cloneをしてご利用ください。言語はPythonを用いているので、必要に応じてインストールをお願いします。(インストール手順もReadmeに記載済みです) また、先述の通りAzureOpenAI(AOAI)リソースが必要になりますので、アカウント発行やリソース作成なども必要に応じて実施してください。 GPTのバージョンによって使用できる機能に制限がありますが、その点も調査・補足してあります。 試用可能な機能 今回サンプルコード化した機能は各種AOAIリソースを呼び出すための初歩的なものや、 OSC広島 の際にキャッチアップを進めたコードなどが中心になります。 これらの機能は今後のバージョンアップに伴ってなるべく更新をかけていきたいと思います。また、より多くの機能をサンプルコード化できるよう頑張ります。   ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post LangChainのサンプルコードを作ってみた first appeared on SIOS Tech. Lab .
こんにちは。サイオステクノロジー OSS サポート担当 山本 です。 今回も solr のお話です。 今回は前回まで使っていた demo をベースにして、フィールドの内容を変更した新しいコアを作ってみることで、コアを作る手順を確認しようかと思います。 ■おさらい:コア?フィールド? コアを作っていく前に、solr の基本的な部分の用語を再確認しておきましょう。 今回見る必要な範囲でいうと、大まかに以下のような感じです。 コア : 箱 (空間) のようなもの。一連の 設定の適用範囲 であり、また検索の際にはコアを指定して検索します。 ドキュメント :ユーザが登録したデータ。その コア の スキーマ で定義された フィールド に合わせた、 一連のデータの組 を1つの単位として登録されます。 フィールド : ドキュメント の構造を決定する要素。型 (数値か文字列か) や、必須であるかどうか、 解析を行うかどうか (また、その解析方法) などをフィールド毎に決定します。 スキーマ で設定します。 スキーマ : フィールド の名前や型や 解析方法 、フィールドで使用される型などの定義を行うための設定ファイル。 …これだけ言われてもピンと来ないかもしれませんので、この先やってみながら確認していきましょう。 ■コアを作る ということで新しいコアを作ってみます。 一つの solr で複数のコアを作ることができる ので、今回は前回までにも使用していたデモ用の環境にコアを追加してみます。 …と軽く言ってはみますが、 solr のコアを作るには準備が必要 です。コマンド一つでお手軽簡単!!とはいきません。 用意するファイルの数もそこそこあるので、今回はフィールドの内容を決定する スキーマファイル のみ作成し、他の必要なファイルはデモ用環境のコア “demo” のものを流用してコアを作っていきます。 ■デモ環境 (コンテナ) の起動 まずはベースとなる solr 環境として、公式コンテナイメージからデモ用の環境を作ります。 これは前回まででやったのと同じです。podman を導入して以下のコマンドを実行すれば OK ですね。 ## 前回の環境を消してしまっている場合 $ podman run -dt --name test-solr -p 8984:8983 solr solr-demo ## 前回の環境から続けて試す場合 $ podman start test-solr ブラウザを開いて以下の URL にアクセスし、管理画面が開けることを確認できたら一旦 OK です。 http://(IPアドレス or ホスト名):8984/solr ■スキーマファイルの作成 続いて、 今回のお話のキー である スキーマ を作成します。 スキーマは、 “managed-schema.xml” (または “schema.xml”) という名前の xml ファイル で定義していきます。 今回は日本語を扱う 2つのフィールド “test_1” “test_2” と、managed-schema.xml で最低限必要になるフィールドだけを持ったスキーマファイルを作ってみます。 適当なテキストエディタ (vi など) を使って、以下のような内容を持つ “ managed-schema.xml ” を作成してください。 <?xml version="1.0" encoding="UTF-8"?> <schema name="default-config" version="1.6"> <types> <fieldType name="booleans" class="solr.BoolField" sortMissingLast="true" multiValued="true"/> <fieldType name="pdates" class="solr.DatePointField" docValues="true" multiValued="true"/> <fieldType name="plongs" class="solr.LongPointField" docValues="true" multiValued="true"/> <fieldType name="pdoubles" class="solr.DoublePointField" docValues="true" multiValued="true"/> <fieldType name="pint" class="solr.IntPointField" docValues="true"/> <fieldType name="plong" class="solr.LongPointField" docValues="true"/> <fieldType name="string" class="solr.StrField" sortMissingLast="true" docValues="true"/> <fieldType name="text_general" class="solr.TextField" positionIncrementGap="100" multiValued="true"> <analyzer type="index"> <tokenizer name="standard"/> <filter ignoreCase="true" words="stopwords.txt" name="stop"/> <filter name="lowercase"/> </analyzer> <analyzer type="query"> <tokenizer name="standard"/> <filter ignoreCase="true" words="stopwords.txt" name="stop"/> <filter ignoreCase="true" synonyms="synonyms.txt" name="synonymGraph" expand="true"/> <filter name="lowercase"/> </analyzer> </fieldType> <!-- ここまで必須分 --> <!-- ここから独自フィールド用の設定 --> <fieldType name="text_ja" class="solr.TextField" autoGeneratePhraseQueries="false" positionIncrementGap="100"> <analyzer> <tokenizer mode="search" name="japanese"/> <filter name="japaneseBaseForm"/> <filter tags="lang/stoptags_ja.txt" name="japanesePartOfSpeechStop"/> <filter name="cjkWidth"/> <filter ignoreCase="true" words="lang/stopwords_ja.txt" name="stop"/> <filter name="japaneseKatakanaStem" minimumLength="4"/> <filter name="lowercase"/> </analyzer> </fieldType> </types> <fields> <field name="_version_" type="plong" indexed="false" stored="false"/> <field name="id" type="string" multiValued="false" required="true" stored="true"/> <!-- ここまで必須分 --> <!-- ここから独自フィールド用の設定 --> <field name="test_1" type="text_ja"/> <field name="test_2" type="text_ja"/> </fields> <uniqueKey>id</uniqueKey> </schema> これでスキーマファイルの準備は OK です。 ■スキーマファイルの中身の基本的な見方 折角なので、今回のスキーマファイルの設定内容についても一部確認してみましょう。 まずざっくり見ると、 “<field>” と “<fieldType>” という要素を大量に定義してるなー、というのがわかるかと思います。 “<field>” 要素の方から確認してみましょう。 <fields> <field name="_version_" type="plong" indexed="false" stored="false"/> <field name="id" type="string" multiValued="false" required="true" stored="true"/> <!-- ここまで必須分 --> <!-- ここから独自フィールド用の設定 --> <field name="test_1" type="text_ja"/> <field name="test_2" type="text_ja"/> </fields> よくよく見てみると、 name 属性に今回作ると宣言していたフィールド名 “test_1” “test_2” を含むものがありますね。 お察しのとおり、 “<field>” 要素こそがこのスキーマの、つまりはこのスキーマを使うコアで扱えるフィールド を定義するための要素になります。 例えば、この行は “_version_” という名前のフィールドを定義しています。 <field name="_version_" type="plong" indexed="false" stored="false"/> ところで、<field> 要素の設定には name 属性の他に type 属性 が必ず含まれているのに気づくかと思います。 この “_version_” フィールドでは type は “plong” ですね。 ここで “<fieldType>” 要素に目を向けてみましょう。 <fieldType name="plong" class="solr.LongPointField" docValues="true"/> 探してみると、<fieldType> の中にはこのように name 属性が先の “plong” と同じ名前になっているものが存在しているはずです。 これは勿論偶然などではなく、”_version_” フィールドで使用される “plong” type がどんなものなのかを定義 するものになります。 実際にどんなものなのかはこの <fieldType> 要素の各属性を確認する必要がありますが、特に重要なのは class 属性です。 基本的に使用される class 属性の値については、 こちらのドキュメント を確認してください。例えば、ここで使用されている “solr.LongPointField” なら Long 値 (64bit 符号なし整数) ということになります。 もう一つ見てみましょう。”test_1″ “test_2” フィールドの設定部分です。 <field name="test_1" type="text_ja"/> <field name="test_2" type="text_ja"/> これらのフィールドで使用している type “text_ja” の定義を確認してみます。 <fieldType name="text_ja" class="solr.TextField" autoGeneratePhraseQueries="false" positionIncrementGap="100"> <analyzer> <tokenizer mode="search" name="japanese"/> <filter name="japaneseBaseForm"/> <filter tags="lang/stoptags_ja.txt" name="japanesePartOfSpeechStop"/> <filter name="cjkWidth"/> <filter ignoreCase="true" words="lang/stopwords_ja.txt" name="stop"/> <filter name="japaneseKatakanaStem" minimumLength="4"/> <filter name="lowercase"/> </analyzer> </fieldType> ちょっと長いですが、これ全部で fieldType “text_ja” の定義になります。 これは 前回 に長々とお話しした、solr の 文字列解析 を行うための設定をしたものになります。 class 属性に “solr.TextField”、子要素に <analyzer> を設定することで、この fieldType が解析処理を行う対象であることを定義 できます。 解析時の処理内容としては、<analyzer> の子要素として処理の内容を上から順番に書いていきます。 上記の例の場合だと、tokenizer の後に “japaneseBaseForm”、”japanesePartOfSpeechStop”、……という順番でフィルタ処理を行う形になります。 <analyzer> の中身についての詳細は、 こちらのドキュメント を確認してください。 なお、今回作ったスキーマファイルは非常にコンパクトな内容です。 色々と設定されている例が見たい場合は、デモ環境のデフォルトコア demo のスキーマファイルを覗いてみるといいかもしれません。 ■必要になるディレクトリ作成とファイル配置 ではコア作成の準備の仕上げとして、ディレクトリを作成してファイルを配置していきます。 とりあえず今回はデモ環境のコンテナ内に入って操作していきます。 $ podman exec -it test-solr /bin/bash まず、 環境変数 “ SOLR_HOME ” で設定されている solr のホームディレクトリ (デモ環境では “/var/solr/data/” になっているはずです) に、新しいコア用のディレクトリを作成します。 ここで作成する ディレクトリ名 がそのまま 新しいコアのコア名 になります。今回は “test-new-core” というコア名にしてみます。 (※コンテナ内での操作) $ mkdir /var/solr/data/test-new-core 続いて、必要になるファイルをこのディレクトリに配置していきます。 先述のとおり、今回は managed-schema.xml 以外はデモ環境 “demo” から流用するので、流用するファイルをコピーしていきます。 (実際に運用する環境を構築する際には、これらのファイルもコピーではなくちゃんと確認して設定しましょう。特に solrconfig.xml や solr.xml など) (※コンテナ内での操作) $ mkdir /var/solr/data/test-new-core/data $ mkdir /var/solr/data/test-new-core/conf $ mkdir /var/solr/data/test-new-core/conf/lang $ cp /var/solr/data/demo/core.properties /var/solr/data/test-new-core/ $ cp /var/solr/data/demo/conf/solrconfig.xml /var/solr/data/test-new-core/conf/ $ cp /var/solr/data/demo/conf/stopwords.txt /var/solr/data/test-new-core/conf/ $ cp /var/solr/data/demo/conf/synonyms.txt /var/solr/data/test-new-core/conf/ $ cp /var/solr/data/demo/conf/lang/stoptags_ja.txt /var/solr/data/test-new-core/conf/lang/ $ cp /var/solr/data/demo/conf/lang/stopwords_ja.txt /var/solr/data/test-new-core/conf/lang/ (※ コンテナ内の操作終わり) $ exit 最後に、作成した managed-schema.xml を配置すればファイルの配置は完了です。 $ podman cp ./managed-schema.xml test-solr:/var/solr/data/test-new-core/conf/managed-schema.xml ■コア作成の実行 必要な各種ファイルの配置ができたら、コア作成を実行します。 コマンドからでも Web の管理画面からでもコアは作成できます が、今回は Web の管理画面で追加する方法を見ていきます。 まずは適当なブラウザから管理画面にアクセスします。今回のコンテナの実行方法なら以下のアドレスですね。 http://(IPアドレス or ホスト名):8984/solr 管理画面にアクセスできたら、画面左メニューから “ Core-Admin ” を開きます。 “Add Core” から、”name” と “instanceDir” に 先の手順で作成した コア用のディレクトリと同じ名前 に変更して “Add Core” ボタンで… 新しいコアが登録できました。 画面左メニューの “Core Selector” プルダウンにも、新しいコアが出てくるようになっているはずです。 ■ドキュメントの登録と検索 新しくコアを作れたので、このコアを使って基本的なドキュメントの登録や検索の方法も確認しましょう。 ■ドキュメントの登録 ドキュメントの登録は、 Web 管理画面 または curl などを用いた直接のリクエスト により、 xml や JSON などの形式の ファイル または文字列の直接入力によって行うことができます。 今回は管理画面から xml ファイルを使って登録する方法を確認していきます。 まずは以下のような xml ファイルを作りましょう。(“<field> ” の中身はお好みの内容で OK です。) <add> <doc> <field name="test_1">サイオステクノロジー株式会社</field> <field name="test_2">SIOS Tech.Lab</field> </doc> <doc> <field name="test_1">OSSのsolrを試しています</field> <field name="test_2">ドキュメントの登録</field> </doc> <doc> <field name="test_1">これらの "doc" 内の "field" 要素は</field> <field name="test_2">スキーマで定義したフィールドに合わせて作りましょう</field> </doc> <doc> <field name="test_1">サンプルドキュメント</field> <field name="test_2">サンプルのドキュメント</field> </doc> <doc> <field name="test_1">OSSよろず相談室</field> <field name="test_2">some document</field> </doc> </add> 続いて、管理画面を開いて画面左メニューの “Core Selector” プルダウンから先ほど作ったコア名を選択し、” Documents ” を開きます。 “ Document Type ” で “ File Upload ” を選択したら、” Document(s) ” で先に作った xml ファイルを選択して “ Submit Document ” を押下すれば OK です。 ■ドキュメントの検索 続いて登録したドキュメントを検索してみましょう。 前々回もやった内容なので、ざっくりいきます。 まずは管理画面から登録に成功しているか確認してみます。 画面左メニュー “Core Selector” で先ほどのコア名を選択した状態で “ Query ” 画面を開き、 画面下 の “ Execute Query ” ボタンを押下します。 すると、登録したドキュメントが表示されるはずです。 この画面の “ q ” に、例えば “test_2:ドキュメント” や “test_2:ドキュメント AND test_1:OSS” などのように “ (フィールド名) : (検索文字列) ” という形式で条件を指定することで、検索条件を指定してドキュメントを表示させることもできます。 また、以下のような形式でアドレスに直接リクエストを行うことで、ドキュメントを取得することもできます。 http://(IPアドレス or ホスト名):(ポート)/solr/(コア名)/select?q=(条件) 例えば、curl を使って今回作った環境のドキュメントを取得すると以下のようになります。 ※ 記号や全角文字は URL エンコードする必要があります 。 $ curl 'http://(IP アドレス or ホスト名):8984/solr/test-new-core/select?q=test_1%3AOSS%20AND%20test_2%3Asome' { "responseHeader":{ "status":0, "QTime":2, "params":{ "q":"test_1:OSS AND test_2:some" } }, "response":{ "numFound":1, "start":0, "numFoundExact":true, "docs":[{ "id":"0fa6bbf2-b548-4933-b2de-107eebaea887", "test_1":"OSSよろず相談室", "test_2":"some document", "_version_":1814150309105106944 }] } } ■最後に 今回は solr に新しいコアを追加する手順を確認してみました。 今回はスキーマファイルのみ自作し、他の必要ファイルはデモ環境から流用するという実験用の手順大幅削減アプローチでやってみましたが、それでも確認すべきことは結構ありましたね。 勿論、今回流用した部分や見なかった部分にも確認すべき内容は多数あり、気軽に使うにはやや敷居が高いと感じるところはあります。 ただ、solr には活用することでより検索の利便性を向上させる機能がまだまだありますので、次回はそのうちの一つを紹介してみたいと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Solr って何者?③:コアを作ってみる first appeared on SIOS Tech. Lab .
はじめに こんにちはサイオステクノロジーの小野です。 前回 はOpenShift AIの概要について簡単に説明しました。今回はOpenShiftのノードの追加方法について解説します。OpenShift AIを利用するためには、GPUノードを追加する必要があるのでしっかりと操作を覚えましょう。 OpenShift(AWS)について OpenShiftはAmazon Web Services、Google Cloud Platform、IBM Cloud、Microsoft Azure のクラウド、vSphereなど多くのプラットフォーム上に簡単に構築することができます。AWS上で構築する際はEC2にノードがデプロイされるので、幅広いスペックを選べたり、簡単にサーバー移行が可能であったりといったメリットがあります。したがって、今回はAWS上にOpenShiftを構築してノードを追加します。 ノードの種類 マスターノード(コントロールプレーンノード) Kubernetesクラスターの管理と制御を行う役割を持つノードです。Kubernetesの特徴である負荷分散や高可用性はマスターノードによって管理されます。 ワーカーノード コンテナ化されたアプリケーションが動作するノードです。 インフラノード ルーティングやロギング、モニタリングなどのインフラストラクチャサービスを実行するために専用で用意するノードです。ワーカーノードと分けることでワーカーノードが落ちた場合やアップデートする場合にインフラを保つことができます。ちなみにインフラノードという用語はOpenShiftのドキュメントではよく出てきますが、Kubernetesのドキュメントには登場しません。 ノードの管理 マシンセット (MachineSet) 定義された数の同一のマシン(ノード)を保持し、管理するためのリソースです。マシンセットは、特定の設定を持つマシン(ノード)のレプリカを作成し、所望のレプリカ数を維持します。マシンセットは、ノードのスケーリングや自動修復(失敗したノードの置き換え)を行うために使用されます。 マシンコンフィグ(MachineConfig) マシンコンフィグはマシンの構成を定義します。 マシンコンフィグを使用することでクラスター内のノードに対して一貫した構成を適用できます。 マシンコンフィグプール (MachineConfigPool) マシンコンフィグプールは、特定のマシンコンフィグを適用するノードのグループを定義します。 マシンコンフィグプールを使用すると、異なるタイプや設定を持つノードの集まりをより簡単に管理できます。例えば、一部のノードはGPUを搭載しているが、他のノードは搭載していない、といった場合に便利です。 AWSでのノードの追加手順 OpenShiftのノード追加の方法についてこれから解説します。マシンセットを設定することで簡単にノードの追加が可能です。なお、今回行う手順(高いインスタンス等を設定)では高額な利用料金が発生する場合があるのでご注意ください。 前提条件 OpenShiftをIPIで構築した直後の状態から操作します。 OpenShift:4.17.1 構築する環境:AWS(amd64) リージョン:us-east-2 クラスター名:ocp-test cluster-admin権限があるユーザ操作 マシンセットの確認 コンソール画面のコンピュート > MachineSetsに移動します。 3つのマシンセットが存在することを確認します。 新規マシンセットの作成 MachineSetの作成を押します。 Red Hatのドキュメントに載っている サンプル を用いてマシンセットを作成します。 apiVersion: machine.openshift.io/v1beta1 kind: MachineSet metadata: labels: machine.openshift.io/cluster-api-cluster: <infrastructure_id> name: <infrastructure_id>-<role>-<zone> namespace: openshift-machine-api spec: replicas: 1 selector: matchLabels: machine.openshift.io/cluster-api-cluster: <infrastructure_id> machine.openshift.io/cluster-api-machineset: <infrastructure_id>-<role>-<zone> template: metadata: labels: machine.openshift.io/cluster-api-cluster: <infrastructure_id> machine.openshift.io/cluster-api-machine-role: <role> machine.openshift.io/cluster-api-machine-type: <role> machine.openshift.io/cluster-api-machineset: <infrastructure_id>-<role>-<zone> spec: metadata: labels: node-role.kubernetes.io/<role>: "" providerSpec: value: ami: id: ami-046fe691f52a953f9 apiVersion: machine.openshift.io/v1beta1 blockDevices: - ebs: iops: 0 volumeSize: 120 volumeType: gp2 credentialsSecret: name: aws-cloud-credentials deviceIndex: 0 iamInstanceProfile: id: <infrastructure_id>-worker-profile instanceType: m6i.large kind: AWSMachineProviderConfig placement: availabilityZone: <zone> region: <region> securityGroups: - filters: - name: tag:Name values: - <infrastructure_id>-worker-sg subnet: filters: - name: tag:Name values: - <infrastructure_id>-private-<zone> tags: - name: kubernetes.io/cluster/<infrastructure_id> value: owned - name: <custom_tag_name> value: <custom_tag_value> userDataSecret: name: worker-user-dataname: worker-user-data サンプルの変更点 基本的には既存のマシンセットを参考にして入力してください。 <infrastructure_id>:<クラスター名>-<ランダム文字列> infrastructure_idは以下のコマンドで確認するか、既存のマシンセットのyamlを見て確認してください。 $ oc get infrastructure cluster -o jsonpath='{.status.infrastructureName}' ocp-test-sp9lv <role>:worker <region>:us-east-2 <zone>:us-east-2a(リージョンとは別なので注意) metadata.name:マシンセットの名前(サンプルの通りに設定すると既存のマシンセットの名前と被ってエラーが出るので注意) instanceType:g4dn.xlarge(今回はGPUノードを追加することを考えるのでGPU用のインスタンスタイプを選択) securityGroupsのfliters:’tag:Name’ securityGroupsのvalues:<infrastructure_id>-node, <infrastructure_id>-lb(2つ設定する) ami.id:ami-048d893cb41cbd3cf(インスタンスタイプに対応したAMI) subnetのvalues:<infrastructure_id>-subnet-private-<zone> 変更後のyaml apiVersion: machine.openshift.io/v1beta1 kind: MachineSet metadata: labels: machine.openshift.io/cluster-api-cluster: ocp-test-sp9lv name: ocp-test-sp9lv-worker-us-east-2a-gpu namespace: openshift-machine-api spec: replicas: 1 selector: matchLabels: machine.openshift.io/cluster-api-cluster: ocp-test-sp9lv machine.openshift.io/cluster-api-machineset: ocp-test-sp9lv-worker-us-east-2a template: metadata: labels: machine.openshift.io/cluster-api-cluster: ocp-test-sp9lv machine.openshift.io/cluster-api-machine-role: worker machine.openshift.io/cluster-api-machine-type: worker machine.openshift.io/cluster-api-machineset: ocp-test-sp9lv-worker-us-east-2a spec: metadata: labels: node-role.kubernetes.io/<role>: "" providerSpec: value: ami: id: ami-048d893cb41cbd3cf apiVersion: awsproviderconfig.openshift.io/v1beta1 blockDevices: - ebs: iops: 0 volumeSize: 120 volumeType: gp2 credentialsSecret: name: aws-cloud-credentials deviceIndex: 0 iamInstanceProfile: id: ocp-test-sp9lv-worker-profile instanceType: g4dn.xlarge kind: AWSMachineProviderConfig placement: availabilityZone: us-east-2a region: us-east-2 securityGroups: - filters: - name: 'tag:Name' values: - ocp-test-sp9lv-node - filters: - name: 'tag:Name' values: - ocp-test-sp9lv-lb subnet: filters: - name: tag:Name values: - ocp-test-sp9lv-subnet-private-us-east-2a tags: - name: kubernetes.io/cluster/ocp-test-sp9lv value: owned userDataSecret: name: worker-user-data マシンセットの作成 サンプルの変更が完了したものを入力して作成を押します。 マシンセットが作成されたら、マシンの作成が始まるのでしばらく待ちます。 マシンセットのデプロイ 作成したMachineSetのMachineのメッセージで「Machine successfully created」が表示されたらマシン作成完了です。 作成したMachineSetのMachinesタブに移動して作成したMachineの欄にノードが表示されていて、ノードのステータスがReadyになっていればノードの追加完了です。 AWS画面での確認 AWSコンソールにログインし、リージョンをus-east-2に設定した後、EC2 > インスタンスのページを開きます。 OpenShiftのマシンセットを作成することで、 自動的にEC2のインスタンスが作成されていることが確認できます。 [画像] 注意事項 AMIのアーキテクチャがx86_64であるかarm64であるかによって使えるインスタンスタイプが変わるので注意してください。 AMIのアーキテクチャ確認 $ aws ec2 describe-images --image-ids ami-048d893cb41cbd3cf --query "Images[*].{Architecture:Architecture}" [ { "Architecture": "x86_64" } ] インスタンスタイプが対応しているアーキテクチャ確認 $ aws ec2 describe-instance-types --region us-east-2 --instance-types g4dn.xlarge --query "InstanceTypes[*].ProcessorInfo.SupportedArchitectures" [ [ "x86_64" ] ] AMIの起動モードがUEFIであるかlegacy-biosであるかによっても使えるインスタンスタイプが変わるので注意してください。 AMIの起動モード確認 $ aws ec2 describe-images --image-ids ami-048d893cb41cbd3cf --query "Images[*].BootMode" [ "legacy-bios" ] インスタンスタイプの対応している起動モード確認 $ aws ec2 describe-instance-types --region us-east-2 --instance-types g4dn.xlarge --query "InstanceTypes[*].SupportedBootModes" [ [ "legacy-bios", "uefi" ] ] 最後に AWS上にOpenShiftを構築してマシンセットを作成することで、GPUノードの追加ができました。以上の手順を参考にしてノード追加を行ってみてください。 最後に重要な注意事項として、GPUノードを追加しましたが、今の段階だとまだGPUがノードに認識されていません。したがってGPUを用いて機械学習を行うことができません。次回はノードにGPUを認識させるためにNode Feature Discovery OperatorとNVIDIA Operatorを導入する方法について解説します。お楽しみに。 参考 AWSインスタンスの料金表: https://aws.amazon.com/jp/ec2/instance-types/ アーキテクチャごとのAMIの設定: https://github.com/openshift/openshift-docs/blob/main/modules/installation-aws-user-infra-rhcos-ami.adoc https://b.chiroito.dev/entry/2020/04/23/173356 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OpenShift(AWS)へのノード追加 first appeared on SIOS Tech. Lab .
CI/CD の導入は現代のアプリケーション開発において不可欠な要素となっています。 前回の記事 では、CI 部分を担う OpenShift Pipelines について解説しました。続いて、本記事では CD 部分を担う OpenShift GitOps について解説します。 CI/CD について 再度の記載になりますが、Continuous Integration / Continuous Delivery の略称で、継続的インテグレーション&継続的デリバリーという意味です。具体的に CI と CD が何を指すかというと以下の通りです。 CI は、ビルドとテスト等を自動化させることを指します。コード変更がメインブランチにマージされるたびに自動ビルドとテストが行われ、問題を早期に検出して対処することができます。 CD は、CI のプロセスをさらに拡張し、アプリケーションを自動的にリリース準備段階まで(継続的デリバリー)または本番環境にデプロイ(継続的デプロイメント)することを目指します。 前回の記事で解説した OpenShift Pipelines は CI 部分を担っていました。OpenShift Pipelines でも CD 部分は実装が可能ですが、Push 型のデプロイになってしまいました。OpenShift GitOps では開発環境や本番環境の状態を検知して、あるべき状態に変更する Pull 型の実装が可能なため、CI と CD は分離して CD 部分は OpenShift GitOps で実装すると様々なパターンに対して柔軟に対応できます。分離した構成図の例が下記になります。CI と CD が独立していますが、一つの CI/CD パイプラインとして動作可能です。 GitOps とは? GitOps とは、DevOps の概念をインフラ管理に適用した考え方です。Git リポジトリ内のマニフェストを正の状態としてインフラとアプリの状態を Git リポジトリと同期させる手法で、インフラストラクチャのコード化(Infrastructure as Code)と CI/CD を組み合わせたものになります。マニフェストを正の状態とすることで、 OpenShift 上のリソースが誤って削除された場合などに、Git リポジトリと同期してリソースを復元できたりもします。GitOps は大規模な共同作業が可能ですが万能なものではなく、直接環境を編集する場合に比べると、プルリクエストの作成やその承認などのプロセスは増えてしまいます。 OpenShift GitOps とは? Argo CD をベースにしており、Git リポジトリに格納されたマニフェストファイルを元に OpenShift のリソースを管理します。クラスタの状態を更新したい時は Git リポジトリに変更を加えるだけで自動的に更新することが出来ます。これによって、開発、ステージング、本番環境など異なる環境のクラスターへアプリケーションをデプロイする際に、一貫性を保ちながら効率化を図ることが出来ます。CD 部分以外にもデプロイメントと Kubernetes リソースの可視化などを実現できる点も利点となっています。 ベースとなっている Argo CD については 初心者でもわかる!Argo CDとは? という記事でデモまで行っているので、読んでもらえると動作のイメージをつかめると思います。 その他の CD ソリューションについて Spinnaker Spinnaker は、マルチクラウド環境でのアプリケーションデプロイが可能です。AWS、Google Cloud Platform、Azure など様々なクラウドプラットフォームに対応しており、柔軟なパイプライン構築や複雑なデプロイメント戦略にも対応しています。しかし、GUI で更新差分を見ることが出来ないため、直感的な見やすさでは ArgoCD に劣ります。 GitLab CI/CD GitLab リポジトリに統合されたCD ソリューションです。コードの変更をトリガーとして自動的にビルド、テスト、デプロイを行います。セルフホスト型とクラウド型の両方が利用可能で、多様な運用環境に対応しています。ArgoCD とは異なり、ソース管理と CI/CD が統合されており、一貫した管理が可能です。GitLab 以外のソース管理システムとの統合が難しい点が ArgoCD との違いになります。 まとめ CI/CD の導入は現代のアプリケーション開発において不可欠な要素となっており、更に関連技術で GitOps などの様々な概念も生まれています。本記事では CI/CD の振り返りと GitOps、 OpenShift GitOps について解説しました。次の記事では実際に OpenShift Pipelines の導入を行っていきたいと思います。 参考文献 https://www.redhat.com/ja/technologies/cloud-computing/openshift/gitops https://www.redhat.com/ja/topics/devops/what-is-gitops https://github.com/mamoru1112/openshift-gitops-handson https://spinnaker.io/ https://media.colorfulpalette.co.jp/n/n934459568564 https://gitlab-docs.creationline.com/ee/ci/ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Red Hat OpenShift GitOps とは? first appeared on SIOS Tech. Lab .
はじめに 皆さんこんにちは。エンジニアの細川です。 皆さんはTypeScriptで開発を行っていたときに、型の特定のプロパティだけ必須にしたいと思ったことはありませんか? 例えば以下のようなHuman型を考えてみます。 type Human = { name: string; age: number; phoneNumber?: number email?: string; } このHuman型のphoneNumberを必須にした型をが欲しいときに、いちいち以下のように全プロパティを定義しなおすのは面倒です。 type HumanRequiredPhoneNumber = { name: string; age: number; phoneNumber: number email?: string; } このような場合に利用できるUtility型の作り方を紹介します。 指定したプロパティを必須にするユーティリティ型を作成 ある型の特定のプロパティを必須にするには以下のようなユーティリティ型を用意します。 /** * Tのうち、Kで指定されたプロパティを必須にする */ type MakeRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>; 使い方は以下の通りです。 先ほどと同じようにHuman型のphoneNumberを必須にしたい場合は以下のように書きます。 type HumanRequiredPhoneNumber = MakeRequired<Human, "phoneNumber"> このように第1引数に型名、第2引数に必須化したいプロパティ名を書くことで、以下のようにphoneNumberが必須になっていることを確認できます。 また、emailも必須にしたい場合は以下のように定義すればOKです。 type HumanRequired = MakeRequired<Human, "phoneNumber" | "email"> こうすると、phoneNumberに加えてemailも必須にすることができます。 null許容も除外したい場合 上記の型を利用すればoptionalなプロパティを必須にできるのですが、場合によってはnull許容のプロパティを必須にしたい場合もあるかと思います。 以下のような Human 型から HumanRequiredNotNull 型を作るような場合を考えます。 type Human = { name: string; age: number; phoneNumber?: number | null; email?: string | null; } ↓ type HumanRequiredNotNull = { name: string; age: number; phoneNumber: number; email: string } この場合は以下のようなユーティリティ型を用意します。 /** * Tのうち、Kで指定されたプロパティを必須にし、nullを除去する */ type MakeRequiredNonNullable<T, K extends keyof T> = Omit<T, K> & Required<{ [P in K]: NonNullable<T[P]> }>; 使い方は先ほどと同様に一つもしくは複数のプロパティを指定して、必須かつnull許容では無いプロパティにすることができます。 type HumanRequiredNotNull = MakeRequiredNonNullable<Human, "phoneNumber" | "email"> 実装してみると以下のようにエラーが出るようになります。 まとめ オプショナルなプロパティを必須にしたい場合は以下 /** * Tのうち、Kで指定されたプロパティを必須にする */ type MakeRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>; // 利用する場合 type HumanRequired = MakeRequired<Human, "phoneNumber" | "email"> const taro: HumanRequired = { name: "taro", age: 20, phoneNumber: 00000000000, email: "xxx@xxx.com" } null許容かつオプショナルなプロパティを必須にし、nullを除外したい場合は以下 /** * Tのうち、Kで指定されたプロパティを必須にし、nullを除去する */ type MakeRequiredNonNullable<T, K extends keyof T> = Omit<T, K> & Required<{ [P in K]: NonNullable<T[P]> }>; // 利用する場合 type HumanRequiredNotNull = MakeRequiredNonNullable<Human, "phoneNumber" | "email"> const taro: HumanRequiredNotNull = { name: "taro", age: 20, phoneNumber: 00000000000, email: "xxx@xxx.com" } おわりに 今回は既存の型の特定のプロパティのみを必須にする型を紹介しました。 パッケージなどによって作られる型などは特に再定義するのが面倒なことも多いと思うので少しでも皆さんの役に立てば幸いです。 ちなみに紹介した型の中で利用している、OmitやRequiredなどは事前にTypeScriptに用意されているユーティリティ型です。便利なものも多いので気になる方は こちら でどんな型があるか調べてみてください。 他にもTypeScriptのあれこれを紹介していますので、ぜひ 他の記事 も見てみてください!     ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post TypeScriptのあれこれ⑦ 型の特定のプロパティだけ必須化したい! first appeared on SIOS Tech. Lab .
こんにちは。サイオステクノロジーの和田です。今回はNFTの発行ができる簡単なアプリを作成したので共有したいと思います。 今回作るもの 今回はNFTを発行することができるWebアプリケーションを作成し、動作確認までしたいと思います。具体的にはウォレットアドレスとトークンIDを入力することで、NFTを発行できるようにしたいと思います。それではいきましょう。 作成手順 以下の手順で進めていきたいと思います。 スマートコントラクトの作成 バックエンドのコード作成 フロントエンドのコード作成 動作確認 スマートコントラクト作成 今回はRemixを使ってコントラクトを作成していきます。 以下のようにコントラクトを作成しました。 // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract MyToken is ERC721, Ownable { constructor(address initialOwner) ERC721("MyToken", "MTK") Ownable(initialOwner) {} function safeMint(address to, uint256 tokenId) public onlyOwner { _safeMint(to, tokenId); } } こちらのファイルをRemixの画面で作成します。まず、アイコンパネルからファイルを選択し、contractsフォルダの中に上記内容のファイルを作成します。 続いてファイルをコンパイルしていきます。コンパイルのアイコンをクリックしてコンパイル画面を表示します。青色のボタンをクリックすることで、コンパイルが完了します。 コンパイルが完了すると以下のように表示が追加されるので、ABIをコピーしておいてください。バックエンド実装で後に使用します。 続いてデプロイを行います。デプロイのアイコンをクリックしてデプロイ画面に行きます。ENVIRONMENTをMetaMaskに設定し、ネットワークはSepoliaにデプロイします。デプロイ時の初期値として自分のウォレットアドレスを入れておきます。 デプロイが完了すると、以下のようにDeployed Contractsが表示されるので、コントラクトアドレスをコピーしておきます。こちらも後でバックエンドの実装で使用します。 バックエンド実装 次に、バックエンドの実装をしていきたいと思います。今回はNest.jsを使用して作成しました。Nest.jsはNode.js用のフレームワークで、APIの作成を容易にすることができます。ControllerとServiceがあり、Controllerはルーティングを定義し、Serviceは実際の処理を記述します。今回のケースでは、NFTのMint処理を記述することになります。 nest generateコマンドでControllerとServiceを作ったので、以下に記載します。 ディレクトリ構造 nft ├── nft.controller.spec.ts ├── nft.controller.ts ├── nft.service.spec.ts └── nft.service.ts nft.controller.ts import { Controller, Post, Body } from '@nestjs/common'; import { NftService } from './nft.service'; @Controller('nft') export class NftController { constructor(private readonly nftService: NftService) {} @Post() async mint( @Body('accountAddress') accountAddress: string, @Body('tokenId') tokenId: string, ) { return await this.nftService.mintNft(accountAddress, tokenId); } } nft.service.ts import { Injectable } from '@nestjs/common'; import Web3 from 'web3'; import 'dotenv/config'; @Injectable() export class NftService { async mintNft(accountAddress: string, tokenId: string) { const network = process.env.ETHEREUM_NETWORK; const web3 = new Web3( new Web3.providers.HttpProvider( `https://${network}.infura.io/v3/${process.env.INFURA_API_KEY}`, ), ); const contractABI = [...]; // 作成したコントラクトのABI const contractAddress = String(process.env.CONTRACT_ADDRESS); // スマートコントラクトのアドレス const myContract = new web3.eth.Contract(contractABI, contractAddress); const privateKey = String(process.env.PRIVATE_KEY); // あなたのプライベートキー const data = myContract.methods .safeMint(accountAddress, tokenId) .encodeABI(); const tx = { from: accountAddress, to: contractAddress, data: data, gas: 100000, gasPrice: web3.utils.toWei('250', 'gwei'), }; try { const signed = await web3.eth.accounts.signTransaction(tx, privateKey); const receipt = await web3.eth.sendSignedTransaction( signed.rawTransaction, ); console.log('Transaction receipt:', receipt); return receipt; } catch (error) { console.error('NFTの発行に失敗しました:', error); throw error; } } } contractABIには先ほどスマートコントラクトの作成でコピーしたものを張り付けてください。 また、プロジェクトルートに.envファイルを配置して、以下のように設定しました。 .env PRIVATE_KEY="あなたのアカウントのプライベートキー" INFURA_API_KEY="インフラのAPIキー" ETHEREUM_NETWORK="sepolia" CONTRACT_ADDRESS="作成したスマートコントラクトのアドレス" 今回は Infura を使ってネットワークにアクセスしました。 Infuraでプロジェクトを作成し、APIキーを取得して貼り付けてください。ネットワークはスマートコントラクトをデプロイしたsepoliaとしています。 フロントエンド実装 続いて、フロントエンドの実装をしていきます。今回はNext.jsを用いてフロントを作成しました。下記のようなアドレスとトークンIDを受け取ってNFTを発行できるフォームを作りました。 "use client"; import React, { useState } from "react"; import { Button, TextField, Container, Box, Typography } from "@mui/material"; export default function FormComponent() { const [address, setAddress] = useState(""); const [tokenId, setTokenId] = useState(""); const [result, setResult] = useState(""); const handleSubmit = async (e: { preventDefault: () => void }) => { e.preventDefault(); try { const response = await fetch("<http://localhost:5000/nft>", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ accountAddress: address, tokenId: tokenId, }), }); if (!response.ok) { throw new Error(`Error: ${response.status}`); } // レスポンスデータを取得 const mintResult = await response.json(); console.log(mintResult); // 成功した場合、入力フィールドをクリアし、結果メッセージを設定 setAddress(""); setTokenId(""); setResult("登録成功"); } catch (error) { // エラー処理 console.error("Error minting NFT:", error); setResult("登録失敗"); } }; return ( <Container maxWidth="sm"> <Box display="flex" flexDirection="column" alignItems="center" justifyContent="center" height="100vh"> <Typography variant="h4" component="h1" gutterBottom> トークンの登録 </Typography> <form onSubmit={handleSubmit} style={{ width: "100%" }}> <Box mb={2}> <TextField fullWidth label="アドレス" variant="outlined" value={address} onChange={(e: { target: { value: React.SetStateAction<string> } }) => setAddress(e.target.value)} required /> </Box> <Box mb={2}> <TextField fullWidth label="トークンID" variant="outlined" value={tokenId} onChange={(e: { target: { value: React.SetStateAction<string> } }) => setTokenId(e.target.value)} required /> </Box> <Box width="100%" display="flex" justifyContent="center" mt={2}> {result} </Box> <Box width="100%" display="flex" justifyContent="center" mt={2}> <Button type="submit" variant="contained" color="primary" size="large"> 登録 </Button> </Box> </form> </Box> </Container> ); } それでは実際に動作確認していきたいと思います。フロントとバックをそれぞれ立ち上げて接続してみます。 以下のようにアドレスとトークンIDを入力します。ここで入力するアドレスはウォレットのアドレスを入力してください。 実行した結果、以下のように登録成功しました。 アカウントにNFTが発行されているのか、Metamaskで確認したいと思います。 画像のようにNFTをインポートをクリックして、NFTをインポートします。NFTをインポートする際に入れるアドレスはスマートコントラクトのアドレスを入れてください。インポートをクリックすると、NFTが無事追加されたことが確認できました。 まとめ 今回はNFTを発行することができる簡単なWebアプリケーションを作成してみました。実際に実装してみることで、NFTの仕組みをより深く理解することができました。今回は実装しませんでしたが、今後NFTの画像も表示できるように拡張してみたいと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post NFT発行アプリ作ってみた first appeared on SIOS Tech. Lab .
こんにちは! 今月も「OSSのサポートエンジニアが気になった!OSSの最新ニュース」をお届けします。 2024/10/15、Cisco が不正アクセスがあった旨を公式発表し、DevHub ポータルを非公開にしました。 Cisco Event Response: Reports of Security Incident https://sec.cloudapps.cisco.com/security/center/resources/october_15_2024 2024/10/28~29、Open Source Summit Japan が東京で開催されます。 https://events.linuxfoundation.org/open-source-summit-japan/ https://events.linuxfoundation.org/open-source-summit-japan/ 2024/10/21、サイバートラスト社は SBOM に対応した Linux OS「Enterprise Pack for AlmaLinux」の提供を開始すると発表しました。 サイバートラスト、AlmaLinux に SBOM 対応の独自機能を追加した「Enterprise Pack for AlmaLinux」を提供開始 https://www.cybertrust.co.jp/pressrelease/2024/1021-enterprise-pack.html ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【2024年10月】OSSサポートエンジニアが気になった!OSS最新ニュース first appeared on SIOS Tech. Lab .
今号は、前号の「 パッケージマネージャについて 」の続きになります! 今回は Debian 系システムの apt についてご紹介します。 yum/dnf と apt の違いは? Red Hat 系システムで使用される yum/dnf と Debian 系システムで使用される apt では、下記の様な違いがあります。 yum/dnf は RPM 形式のパッケージを使用するのに対し、apt は DEB 形式のパッケージを使用します。 yum/dnf は /etc/yum.repos.d 配下の repo ファイルを使用してリポジトリを管理するのに対し、apt は /etc/apt/sources.list.d 配下のファイルを使用してリポジトリを管理します。 yum/dnf は yum、dnf コマンドにサブコマンドを指定してパッケージ管理をするのに対し、apt は apt-get や apt-cache コマンドなど、様々なコマンドを使用してパッケージ管理をします (この点については次節で詳しく説明します)。 基本の操作方法 (検索、インストール、アップデート、削除) apt-get コマンドの基本操作 (パッケージの検索、インストール、アップデート、削除) について、それぞれ説明します。 検索 特定のパッケージを検索するには、 apt-cache コマンドを実行します。 例えば、git というパッケージを検索する場合は、下記の様なコマンドになります。 # apt-cache search git eject - Linux での CD のイジェクトおよび CD チェンジャー操作 freetype-doc - FreeType 2 font engine, development documentation fwupd - ファームウェア更新デーモン fwupd-doc - Firmware update daemon documentation (HTML format) gir1.2-geocodeglib-1.0 - geocode-glib ライブラリの introspection データ gir1.2-gxps-0.1 - GObject introspection data for the gpxs library git - 速く、スケーラブルな分散型リビジョン管理システム repo - repository management tool built on top of git steam-installer - Intaller for valve's Steam digital software delivery system w3-recs - Recommendations of the World Wide Web Consortium (W3C) xtrs - emulator for TRS-80 Model I/III/4/4P computers steam - valve's Steam digital software delivery system この時、結果には git という文字列を含むパッケージすべてと、要約 (説明文) が表示されます。 インストール パッケージをインストールするには、 apt-get install コマンドを実行します。 例えば、git というパッケージをインストールする場合は、下記の様なコマンドになります。 # apt-get install git パッケージリストを読みjんでいます... 完了 依存関係ツリーを作成しています... 完了 状態情報を読み取っています... 完了 … 以下のパッケージが新たにインストールされます: git git-man liberror-perl アップグレード: 0個、新規インストール: 3個、削除: 0個、保留: 592個。 4,146 kb のアーカイブを取得する必要があります。 この操作後に追加で 21.0 MB のディスク容量が消費されます。 続行しますか? [Y/n] この時、指定したパッケージのみではなく git と依存関係があるパッケージも併せてインストールされます。 なお、上記の様にインストールを開始する前に 続行しますか? [Y/n] の文字列が表示され、インストールを続行するかを聞かれます。 Y (もしくは y) を押下するとインストール続行、n を押下するとインストールを中止 します。 アップデート パッケージをアップデートするには、 apt-get upgrade コマンドを実行します。 例えば、既にインストール済みの apt というパッケージをアップデートする場合は、下記の様なコマンドになります。 # apt-get upgrade apt パッケージリストを読みjんでいます... 完了 依存関係ツリーを作成しています... 完了 状態情報を読み取っています... 完了 アップグレードパッケージを検出しています... 完了 … 以下のパッケージはアップグレードされます:  accountsservice alsa-ucm-conf amd64-microcode apparmor apport apport-gtk apt  apt-utils avahi-autolpd avahi-daemon avahi-utils base-files bash  bind9-dnsutils bind9-host bind9-libs bluez bluez-cups bluez-obexd brltty … (長いため省略) … アップグレード: 570個、新規インストール: 0個、削除: 0個、保留: 22個。 344 standard security updates 786 MB 中 682 MB のアーカイブを取得する必要があります。 この操作後に追加で 299 MB のディスク容量が消費されます。 続行しますか? [Y/n] 削除 パッケージをシステム上から削除するには、 apt-get remove コマンドを実行します。 例えば、既にインストール済みの git というパッケージを削除する場合は、下記の様なコマンドになります。 # apt-get remove git パッケージリストを読みjんでいます... 完了 依存関係ツリーを作成しています... 完了 状態情報を読み取っています... 完了 … 以下のパッケージは「削除」されます: git アップグレード: 0個、新規インストール: 0個、削除: 1個、保留: 592個。 この操作後に追加で 18.9 MB のディスク容量が解放されます。 続行しますか? [Y/n] apt-cache、apt-get コマンドについての補足事項 apt-get install コマンド実行時に対象パッケージが既にインストール済みである場合はアップデートが実行され、反対に apt-get upgrade コマンド実行時に対象パッケージがインストールされていない場合はインストールが実行されます。 特定のパッケージではなく、システム全体のパッケージをアップデートしたい場合は、パッケージの指定なしで apt-get upgrade コマンドを使用します。 ただし、このコマンドはカーネルやその他の重要なシステムコンポーネントをインストールしないため、これらを含めてアップグレードするには apt-get dist-upgrade コマンドを使用します。 特定のパッケージではなく、システム全体で他のどのパッケージからも必要とされていない (依存関係がない) パッケージを自動的に削除するために、 apt-get autoremove コマンドが使用できます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 知っておくとちょっと便利!パッケージマネージャについて2 ~Debian 系システム編~ first appeared on SIOS Tech. Lab .
PS/SLの佐々木です。 今回はEthereumを使用して簡単なDID/VCを実現してみようと思います。 DID/VCとは DID/VCとは分散IDと検証可能なデジタル証明書のことを指します。 通常の認証とは異なり、認証(VCを検証)する際に発行元に問い合わせる必要がありません。 よって発行元がなくなってしまっても問題なく検証ができます。 また検証もDID Resolverを使用することで誰でも検証することができるため、特定の機関が発行した証明書(VC)を他サービスで検証することも容易です。 DIDとEthereumの関係性 ブロックチェーンの技術はDID/VCの文脈で相性が良くたびたび話題に出ますが、一体どこでブロックチェーンが使用されているのでしょうか? DID/VCの文脈でブロックチェーンが登場するときには検証用の公開鍵の保存先としてブロックチェーンが登場します。 例えば今回実装を紹介するEthereumでは EIP1056 という規格で規定されています。 よってVCの中身の情報であったり実際の検証に関してはブロックチェーンが登場することはありません。 ではなぜブロックチェーンと相性が良いのでしょうか? それはどちらも分散型である点が挙げられます。 例えばdid:webの場合特定のドメインに依存する形でDIDを解決をする必要があります。一方でdid:ethrを使用する場合には分散ネットワーク上にあるため世界中のどこの誰であってもアクセスすることができるためDID/VCの世界観により近いと言えます。 DID/VCの実装にあたって必要な予備知識 今回はタイトル通りdid:ethrを使用してDID/VCを実現します。 Ethereumで実現するためにはEIP1056というDIDを管理する標準的な仕様があります。 ERC1056は、W3Cが提案するDID仕様と互換性があり、Ethereumエコシステムの中で自己主権型IDを管理するための標準的な手段となっています。 このERC1056を実装するためのライブラリ、コントラクトとして以下のようなものが提供されており、今回はこれらに倣って実装していきます。 ethr-did-registry 公開鍵を保存したり、did-documentに表示される属性を更新できるようにするEthereumのコントラクトコードです。 これらは自分でデプロイしてそこに公開鍵やその他属性を追加してもよいですが、 ここ に各ネットワークにデプロイされているコントラクトがあるのでそれを利用しても問題ないです。(今回は事前にデプロイされているSepoliaネットワークのコントラクトを使用します) ethr-did-resolver 与えられたdidメソッド( did:ethr:development:0xf3beac30c498d9e26865f34fcaa57dbb935b0d74 )をもとにDIDドキュメントを解決したり、VCを検証したりし、DIDドキュメントを返します。 ethr-did DIDの作成や更新をするために使用します。 実装するユースケース 大学の証明書を発行しそれを検証するようなものを想定します。 今回はKYC認証は行わないのでVCを提出しているユーザーがVCの本人かどうかは保証しません。あくまでVCが改ざんされていないかを検証するところのみになります。 実装手順 DIDの登録 大学が検証用の公開鍵をEthereum に登録します。 VCの発行 大学が証明書を発行 VCを検証 準備 Metamask Ethereumでトランザクションを発行するための秘密鍵と検証用公開鍵を取得する https://metamask.io/ja/ Alchemy Etheruemネットワークにアクセスするためにノードを提供してくれるサービス API Keyを取得する https://www.alchemy.com/ Sepoliaテストネットトークン ガス代の支払に使用するためのトークン https://cloud.google.com/application/web3/faucet/ethereum/sepolia https://www.infura.io/faucet/sepolia 実装 今回実装するソースコードは こちら で公開しています。 フロントエンド:React API:Nest.js どちらも簡単なコードで作りこみはしていません。 フロントエンドはフォームとレスポンスをわかりやすく作っているだけなので今回はAPIの app.service.ts を見ていきます。( app.controller.ts はルーティングしかしてませんす) 事前に紹介した三つの機能についてそれぞれ実装を紹介します。 DID登録 import { Injectable } from '@nestjs/common'; import { Resolver } from 'did-resolver'; import { getResolver } from 'ethr-did-resolver'; import { VerifiedCredential, verifyCredential } from 'did-jwt-vc'; import { EthrDID, KeyPair } from 'ethr-did'; import { ethers, Wallet } from 'ethers'; async registerDID(): Promise<void> { const alchemyApiKey = process.env.ALCHEMY_API_KEY!!; const privateKey = process.env.PRIVATE_KEY!!; const wallet = new Wallet(privateKey); const pubkey = wallet.signingKey.publicKey; const address = wallet.address; const provider = new ethers.AlchemyProvider('sepolia', alchemyApiKey); const txSigner = new Wallet(privateKey, provider); const keypair: KeyPair = { privateKey, publicKey: pubkey, address, identifier: pubkey, }; // ユーザーのDID作成 const ethrDid = new EthrDID({ ...keypair, provider: provider, txSigner: txSigner, // こいつを渡さないと失敗する <https://github.com/uport-project/ethr-did/issues/81#issuecomment-1030181286> chainNameOrId: 'sepolia', registry: '0x03d5003bf0e79C5F5223588F347ebA39AfbC3818', }); // DID登録 await ethrDid.setAttribute( 'did/pub/Secp256k1/sigAuth/hex', pubkey, 31104000, ); } ここでは検証用に使用する公開鍵をEtheruemに登録する処理を行っています。 登録するスマートコントラクトはすでにライブラリ開発者がでプロしてくれているsepoliaのスマートコントラクトをレジストリとして使用させてもらいます。 0x03d5003bf0e79C5F5223588F347ebA39AfbC3818 Ethereumに登録するトランザクションはこれだけなので公開鍵の登録が終わったらガス代がこれ以降かかることはありません。 DID作成の時には公開鍵、秘密鍵、EOAアドレス、AlchemyProvider、Wallet、デプロイ先のチェーン、スマートコントラクトのアドレスを渡します。 const keypair: KeyPair = { privateKey, publicKey: pubkey, address, identifier: pubkey, }; // ユーザーのDID作成 const ethrDid = new EthrDID({ ...keypair, provider: provider, txSigner: txSigner, // こいつを渡さないと失敗する <https://github.com/uport-project/ethr-did/issues/81#issuecomment-1030181286> chainNameOrId: 'sepolia', registry: '0x03d5003bf0e79C5F5223588F347ebA39AfbC3818', }); そして公開鍵の登録の際には公開鍵の形式と用途を指定して登録します。 登録方法は このように なっており、今回は以下のような意味を持っています・ DIDの公開鍵 であること。 Secp256k1 曲線に基づいた公開鍵暗号アルゴリズムを使用していること。 署名と認証 のために使用される公開鍵であること。 公開鍵の値は 16進数形式 で表現されていること。 // DID登録 await ethrDid.setAttribute( 'did/pub/Secp256k1/sigAuth/hex', pubkey, 31104000, ); VCの発行 async issueVc( hodlerAddress: string, type: string, name: string, ): Promise<string> { const vcpayload = { '@context': ['<https://www.w3.org/2018/credentials/v1>'], type: ['VerifiableCredential'], issuer: 'did:ethr:sepolia:0x145242286AE8184cA885E6B134E1A1bA73858BE8', issuanceDate: new Date().toISOString(), credentialSubject: { id: `did:ethr:sepolia:${hodlerAddress}`, degree: { type: type, name: name, }, }, proof: { type: 'EcdsaSecp256k1Signature2019', created: new Date().toISOString(), proofPurpose: 'assertionMethod', verificationMethod: 'did:ethr:sepolia:0x145242286AE8184cA885E6B134E1A1bA73858BE8#delegate-1', }, }; const alchemyApiKey = process.env.ALCHEMY_API_KEY!!; const privateKey = process.env.PRIVATE_KEY!!; const wallet = new Wallet(privateKey); const pubkey = wallet.signingKey.publicKey; const address = wallet.address; const provider = new ethers.AlchemyProvider('sepolia', alchemyApiKey); const txSigner = new Wallet(privateKey, provider); const keypair: KeyPair = { privateKey, publicKey: pubkey, address, identifier: address, }; // ユーザーのDID作成 const ethrDid = new EthrDID({ ...keypair, provider: provider, txSigner: txSigner, // こいつを渡さないと失敗する <https://github.com/uport-project/ethr-did/issues/81#issuecomment-1030181286> chainNameOrId: 'sepolia', registry: '0x03d5003bf0e79C5F5223588F347ebA39AfbC3818', }); const vc = await ethrDid.signJWT(vcpayload); return vc; } VCにもW3Cが定めている規格が存在しています。 https://www.w3.org/TR/vc-data-model-2.0/ 今回は実装に焦点を当てるため詳細な説明は行いませんが、発行したいVCに合うような形でVCを作成する必要があります。 以下は今回使用するVCの簡単な紹介です。 @context VCが準拠する 標準的なデータ構造の文脈 を示します。 ['<https://www.w3.org/2018/credentials/v1>'] はW3Cが定めたVerifiable Credentialsの標準的な構造に従うことを示します。 type VCの種類を示すプロパティです。 ['VerifiableCredential'] は、この証明書が検証可能な証明書であることを明示しています。 issuer 証明書の発行者(Issuer)のDIDを示します。 この場合、発行者は EthereumのSepoliaテストネット 上にある did:ethr:sepolia:0x145242286AE8184cA885E6B134E1A1bA73858BE8 というDIDを持つエンティティです。 issuanceDate この証明書が発行された 日付 を示します。 new Date().toISOString() を使って現在の日時をISO 8601形式で自動的に生成します。 credentialSubject 証明書が関連する 主体(Credential Subject)を定義します。このVCに記載される情報の対象 です。 id: did:ethr:sepolia:${hodlerAddress} では、証明書の対象となる人物や組織のDIDを示します。この部分は hodlerAddress という変数で動的に設定されるEthereumのアドレスです。 degree フィールドは証明書の内容を示しています。具体的には「学位」情報として、 type (学位の種類)と name (学位の名称)が含まれています。これらも変数 type と name によって動的に設定される情報です。 proof このVCがどのように 署名され、検証されるか を示す情報です。 type : EcdsaSecp256k1Signature2019 は、 Secp256k1 楕円曲線を使ったECDSA(楕円曲線デジタル署名アルゴリズム)による署名方式を指定しています。これはEthereumなどのブロックチェーンで広く使われている暗号方式です。 created : 署名が生成された日時です。ここでも new Date().toISOString() が使われ、現在の日時が設定されます。 proofPurpose : この署名が何のために行われるのかを示します。 assertionMethod は、発行者が証明書の内容を**主張(assert)**するために行った署名であることを意味します。 verificationMethod : 証明書の署名を 検証するための公開鍵 や検証方法を指定しています。この場合、 did:ethr:sepolia:0x145242286AE8184cA885E6B134E1A1bA73858BE8#delegate-1 が指定されています。この公開鍵は #delegate-1 という識別子で特定されています。 またVCはJWTトークンとして返却されます。 const vc = await ethrDid.signJWT(vcpayload); 例えば以下のような内容でVCを作成しようとすると 以下のようなトークンが返ってきます。 これがVCになります。 eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NkstUiJ9.eyJpYXQiOjE3Mjk1OTE2ODcsIkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmV0aHI6c2Vwb2xpYToweDE0NTI0MjI4NkFFODE4NGNBODg1RTZCMTM0RTFBMWJBNzM4NThCRTgiLCJpc3N1YW5jZURhdGUiOiIyMDI0LTEwLTIyVDEwOjA4OjA3LjUwNFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDpldGhyOnNlcG9saWE6MHhFNDk1RkEwMmI3N0I3ZmVFMzVBMDQ2ODYwNDhEMDg0NzY1MTczMThFIiwiZGVncmVlIjp7InR5cGUiOiLmg4XloLHlt6XlraYiLCJuYW1lIjoi5bel5a2mIn19LCJwcm9vZiI6eyJ0eXBlIjoiRWNkc2FTZWNwMjU2azFTaWduYXR1cmUyMDE5IiwiY3JlYXRlZCI6IjIwMjQtMTAtMjJUMTA6MDg6MDcuNTA0WiIsInByb29mUHVycG9zZSI6ImFzc2VydGlvbk1ldGhvZCIsInZlcmlmaWNhdGlvbk1ldGhvZCI6ImRpZDpldGhyOnNlcG9saWE6MHgxNDUyNDIyODZBRTgxODRjQTg4NUU2QjEzNEUxQTFiQTczODU4QkU4I2RlbGVnYXRlLTEifSwiaXNzIjoiZGlkOmV0aHI6c2Vwb2xpYToweDE0NTI0MjI4NkFFODE4NGNBODg1RTZCMTM0RTFBMWJBNzM4NThCRTgifQ.nMBiW1Yi5vyghoorBRAwMIFHTRfATn_P3hv0ZF_aeeUowti7epUep90ldsuubkylccAmjlIiZwGcB9SgCd5IWAA VCの検証 最後に先ほど取得したVCを検証してみます。 async verifyVc(vc: string): Promise<VerifiedCredential | boolean> { const providerConfig = { // While experimenting, you can set a rpc endpoint to be used by the web3 provider rpcUrl: `https://eth-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`, // You can also set the address for your own ethr-did-registry (ERC1056) contract registry: '0x03d5003bf0e79C5F5223588F347ebA39AfbC3818', name: 'sepolia', // this becomes did:ethr:development:0x... }; // It's recommended to use the multi-network configuration when using this in production // since that allows you to resolve on multiple public and private networks at the same time. // getResolver will return an object with a key/value pair of { "ethr": resolver } where resolver is a function used by the generic did resolver. const ethrDidResolver = getResolver(providerConfig); const didResolver = new Resolver(ethrDidResolver); try { const result = await verifyCredential(vc, didResolver); return result; } catch (error) { return false; } } DIDリゾルバーを使用して発行元のDIDドキュメントを取得し、ドキュメントから公開鍵を取得しユーザーから提供されたVCを検証します。 DIDリゾルバーはDIDメソッドごとに異なります。 DIDメソッドを管理している団体がDIDリゾルバを提供していることが多いようです。今回はERC1056で紹介されていたDIDリゾルバを使用します。 DIDの解決から検証までは以下のコードになります。 const ethrDidResolver = getResolver(providerConfig); const didResolver = new Resolver(ethrDidResolver); const result = await verifyCredential(vc, didResolver); 提供されているライブラリが優秀すぎて何をしているのかよくわからないので順に解説していきます。 まず最初の2行でDIDリゾルバーの設定を行います。 これはネットワークやリゾルバが参照するコントラクトのアドレスを設定します。 最後の1行でDIDを解決するところからVCの検証まで一括して行っています。 DIDの解決では先ほどのJWTトークンの中にあるissuerというプロパティのDIDを解決します。 参考までに先ほどのJWTトークンをデコードしてみます。 { "iat": 1729591687, "@context": [ "<https://www.w3.org/2018/credentials/v1>" ], "type": [ "VerifiableCredential" ], "issuer": "did:ethr:sepolia:0x145242286AE8184cA885E6B134E1A1bA73858BE8", "issuanceDate": "2024-10-22T10:08:07.504Z", "credentialSubject": { "id": "did:ethr:sepolia:0xE495FA02b77B7feE35A04686048D08476517318E", "degree": { "type": "情報工学", "name": "工学" } }, "proof": { "type": "EcdsaSecp256k1Signature2019", "created": "2024-10-22T10:08:07.504Z", "proofPurpose": "assertionMethod", "verificationMethod": "did:ethr:sepolia:0x145242286AE8184cA885E6B134E1A1bA73858BE8#delegate-1" }, "iss": "did:ethr:sepolia:0x145242286AE8184cA885E6B134E1A1bA73858BE8" } これを見ると did:ethr:sepolia:0x145242286AE8184cA885E6B134E1A1bA73858BE8 このDIDを解決していることがわかります。 続いてこのDIDを解決してみましょう。 DID解決を行ってくれる Universal Resolver というサイトがあります。 ここで先ほどのDIDを入力すると以下のような結果になります。 これを見ると以下の公開鍵で検証すればよいことがわかります。 今回検証に使うのは proof.verificationMethod に指定されているため 04a48ac40eade831fa352e56aaaab5396cf7f87627913543cf6d1dff5f5c0b496da62401ea4b0629fb3370d56855b2cf32d333e5c1bd0e53cf53722187d7eb3ff1 こちらの公開鍵を使用すればよいことがわかります。 "proof": { "type": "EcdsaSecp256k1Signature2019", "created": "2024-10-22T10:08:07.504Z", "proofPurpose": "assertionMethod", "verificationMethod": "did:ethr:sepolia:0x145242286AE8184cA885E6B134E1A1bA73858BE8#delegate-1" }, 実際に今回のサンプルで検証してみるとvefified:trueと帰ってくるので検証に成功していることがわかります。 最後の検証の部分で渡しているものはVCのみで発行者の情報などはVCからすべて取得できているため発行者に検証に関する問い合わせは行っていないことがわかります。 終わりに 今回はEthereumを使用したDID/VCを実装してみました。 DID/VCを手軽に実装している記事が少ないため実装にはかなり苦労しましたが、動くものを見ると具体的なイメージがわきやすいですね。 DID/VCについてはまだまだ不勉強な部分も多く、デファクトスタンダードと呼ばれるものもないため今後の動向に注目しつつ、今回説明できなかったDID/VCの細かい部分も今後紹介できたらと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post EthereumでDID/VCを実装してみる first appeared on SIOS Tech. Lab .
bashの設定ファイルとは? 環境変数やエイリアス、関数等の定義はシェルが削除されるたびにその定義も一緒に削除されてしまいます。そこでそれらの定義をbash起動時に自動で行うための設定ファイルというものが用意されています。 表:bashの設定ファイル一覧 ※注意事項 /etcディレクトリ以下の設定ファイルは全ユーザーに作用するので、自分専用の設定を施したい場合はホームディレクトリ以下の設定ファイルにしましょう。 設定ファイルの実行順序 bashが起動された場合、以下のフロー図に従って設定ファイルが読み込まれることになります。 図:設定ファイルの実行順序 ※ 用語 ログインシェル:ユーザーがシステムにログインしたときに起動されるシェルです。ログインシェルは、ユーザーの認証後に起動する最初のシェルであり、ユーザーのプロファイルや環境設定を読み込みます。 対話型シェル:ユーザーがコマンドを直接入力して対話するために起動されるシェルです。ログインシェルではないが、コマンドラインで操作できるシェルを指します。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post bashの設定ファイルについて first appeared on SIOS Tech. Lab .
サイオステクノロジーの菊地啓哉です。久しぶりのブログです。最近、 Blockchain の調査検証を進めておりますが、その中でエンタープライズ向け利用の可能性を感じた、Avalanche について調査しております。 AvaCloud というサービスを利用して devnet を構築し、 SmartContract を deploy・実行したので、今回はその手順について書きたいと思います。 Avalanche とは? Avalanche は Blockchainプラットフォーム で以下のような特徴を持ちます。 SmartContract が実行される C-Chain(Contract Chain)、サブネットの作成などで使用される P-Chain(Platform Chain)、Asset のやりとりで使用される X-Chain(Exchange Chain) の3つのチェーンで構成される( 参考 ) Primary Network の中に Avalanche L1(旧: Subnet)と呼ばれるサブネットを作成することができ、Avalanche L1 で Permissioned なネットワークを作成することもできる(Avalanche L1 は Layer 1 の L1 と紛らわしいので、この記事中は旧称である Subnet と併記します。 参考 ) Avalanche L1(旧: Subnet)は Permissioned/Permissionless の設定、独自の token economics の構築、ガス代の設定など、要件に応じた柔軟な設定ができる( 参考 ) AvaCloud とは? Avalanche の開発元である Ava Labs が提供する Node as a Service で、簡単に Avalanche L1(旧: Subnet)を構築することができます。Google アカウントで簡単に登録でき、Free Tier で devnet の作成ができます。 devnet とは? その名の通り、開発で使用する Avalanche network です。クラウド上に deploy されているが、mainnet や testnet などの Avalanche network とはつながっていない、独立したネットワークです。( 参考 ) 前提 Core という Wallet が必要になります。この手順ではブラウザの拡張機能をインストール済みの状態で実施しております。 AvaCloud には登録済みとします。 devnet の作成 AvaCloud ログイン後のトップの画面(My Blockchains)で「+ Create L1」をクリックします。 ここに表示されていますが、登録直後は無料試用期間が30日あり、devnet を動かしている間だけ、残り時間が減っていくような動きになっているようです。画面に表示されている devnet のように Disabled になっていればこのカウントダウンは進んでいません。 Devnet を選択します。 ここで Testnet や Mainnet を選択すると、それぞれ作成の為に契約できるプランが表示されます。 画像のように Wallet の接続が促されるので、Core Wallet を接続します。 一般設定を入力します。Chain ID は恐らく使用可能な ID を設定してくれていると思います。変更したい項目があれば好きに変更してください。 Admin wallet address は Core Wallet で使えるアドレスが表示されていると思います。 Begin customizing をクリックします。 誰がトランザクションを送信できるか設定します。任意のアカウントが実行できるように設定することもできますが、ここでは指定したアカウントに限定します。 Address は使えるものを入力し、 Roleは Admin か User の2択から選びます。 SmartContract を deploy できるアドレスを設定します。 作成する devnet の native token を Mintできるアドレスを指定します。 Reward を設定します。 ガス代を設定します。 設定した内容を確認できます。Next で進みます。 確認画面が表示されるので、「Create L1」で devnet を作成します。 これで devnet が作成できました。 MetaMask に devnet を追加 Remix から SmartContract を deploy できるようにするため、作成した devnet を MetaMask に追加します。 devnet 作成後の My Blockchains で作成した devnet の右側にある右矢印をクリックし、 devnet の詳細を開きます。 Dashboard、Validators、Interoperability は無料の範囲では使えないようです。 Details を選択すると devnet の詳細が表示されます。この内容を MetaMask に手動で追加します。 以下の内容を入力して、保存します。 ネットワーク名:MetaMask上で管理しやすい名前 新しいRPC URL:Details の中の RPC URL チェーンID:Details の中の EVM Chain ID 通貨記号:Details の中の Token Symbol ブロックエクスプローラーのURL(オプション):入力しない これで、ネットワークが追加され、そのネットワークに切り替えると、devnet 作成時に指定したアカウントが指定量のトークンを持っていることを確認できると思います。 Remix から devnet に SmartContract deploy・実行 お馴染みの Remix を使って、今作った devnet に SmartContract を deployし、関数を実行してみます。先ほどの Details の画面にも表示されていましたが、VM Type が Subnet-EVM なので、EVM互換性があります。 Remix上の細かい操作は省略しますが、もしわからなければ、 こちらの記事 を参照ください。 MetaMask が devnet を選択している状態で Remix上の ENVIRONMENT を Injected Provider - MetaMask にすると、 Custom (EVM Chain ID) network のように表示されると思います。この状態であれば devnet とつながっているので、書き込み処理のある SmartContract を deployします。 上の画像が deploy 及び、書き込み処理を実行した結果となります。 期待通りに実行できていそうなことが確認できます。 devnet を disabled にする 最後に作成した devnet を disabled にしておきます。 Avalanche L1 の画面の右上にあるゴミ箱のアイコンをクリックすると確認のダイアログが表示された後、停止できます。 おまけ AvaCloud の料金プランは何故か AvaCloud のページ内で見つけられていません。ただ、testnet や mainnet で L1 を作ろうとすると料金プランの表示が出てきます。また、これまた何故か、 サポートページ にはその料金プランのページのスクリーンショットが掲載されています。見つけられていないだけかもしれませんが、料金プランなんて大事な情報は見せても良いならわかりやすい場所に掲載してほしいですね。こちらの情報はあくまで 2024/10/22 時点の情報になります。今は Testnet Starter 5日間の無料お試し期間がありましたが、数日前まではありませんでした。 Testnet Starter は月額 $1,599.00 の固定費に加えて使用量に応じたストレージ費用がかかり Mainnet Starter は同様に月額 $3,999.00 の固定費と使用量に応じたストレージ費用となっています。 まとめ 以上が、 AvaCloud上での devnet の作成、 Remix から devnet に SmartContract を deploy・実行するまでをご紹介しました。 割と簡単にできそうに見えたのではないでしょうか? 引き続き、Avalanche の調査・検証を進めていきたいと思います。 またかきます またね ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AvaCloud で Avalanche の devnet をつくってみた first appeared on SIOS Tech. Lab .
概要 こんにちは!サイオステクノロジーの安藤 浩です。 Bicep を利用して、 Flex Consumption  のAzure Functions で Event Grid トリガー とバインドした Event Subscription を Deploy します。 Flex Consumptionはプレビュー版ですが、仮想ネットワークのサポートや常時起動などの機能が利用できることが特徴です。また、現状では利用できるリージョンも限られています。 利用できるリージョンは以下のコマンドで確認できます。 $ az functionapp list-flexconsumption-locations --output table northeurope southeastasia eastasia eastus2 southcentralus australiaeast eastus northcentralus(stage) westus2 uksouth eastus2euap westus3 swedencentral オペレーティング システムのサポート や Function App タイムアウト などは以下を参照してください。 https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-scale また、Flex Consumptionでは以下のURLに記載のパラメータは非推奨です。後ほどAzure Function のDeploy の際に出てくる ENABLE_ORYX_BUILD , SCM_DO_BUILD_DURING_DEPLOYMENT は非推奨です。 https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-app-settings#flex-consumption-plan-deprecations Blob 上のファイル更新があった際に Event Subscription とバインドしたEvent Grid Trigger が実行されるようにしたいときに利用できます。 Storage AccountのContainer にファイルを配置したら、Event Subscription がEventが発火して、Azure Function の関数が実行されます。 以下のイメージです。 前提条件 以下が利用できることを前提としています。 ツール Version Visual Studio Code Version: 1.94.2 Visual Studio Code 用の Bicep 拡張機能 0.30.23 最新の Azure CLI ツールまたは最新の Azure PowerShell バージョン Azure CLI : 2.65.0 ここではAzure CLI を利用します。 Deploy 手順 Flex Consumption の場合は通常のConsumption とはBicep の記法が異なるので、注意です。 また、Event Grid Subscription を Deploy する際には既にEvent Grid Trigger の関数が存在しなければならないので、以下の手順でDeploy する必要があります。 手順1. AppService Plan: Flex Consumption での Azure Function をDeployする。 手順2. Event Grid Subscription と紐づける Azure Function にEvent Grid Trigger の関数をDeployする。 手順3. Event Grid(System Topic) と Event Grid Subscription をDeployする。 手順1. AppService Plan: Flex Consumption での Azure Function をDeployする Flex Consumption のAzure Function をDeployするコードは以下です。 ※Application Insights などは省いています。 Flex Consumption の Bicep コード ポイント: serverfarms の sku をFlex Consumptionを指定 modules/func/func.bicep の serverFarmId で PlanId を指定 従量課金のSKUのAzure Function とは記述が若干異なる Function から Storage Accountへのアクセス権限(ストレージ BLOB データ所有者)を付与する (Bicep コード実行時に Role Based Access Control Administrator または、 User Access Administrator の権限が必要) modules/func/func.bicep param hostingPlanName string param functionName string param location string = resourceGroup().location param storageAccountName string param deploymentStorageContainerName string param applicationInsightsName string param tags object = {} param functionAppRuntime string = 'python' param functionAppRuntimeVersion string = '3.11' param maximumInstanceCount int = 100 param instanceMemoryMB int = 2048 param eventGridStorageAccountName string resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' existing = { name: storageAccountName } resource eventGridStorage 'Microsoft.Storage/storageAccounts@2023-01-01' existing = { name: eventGridStorageAccountName } resource appInsights 'Microsoft.Insights/components@2020-02-02' existing = { name: applicationInsightsName } resource flexFuncPlan 'Microsoft.Web/serverfarms@2023-12-01' existing = { name: hostingPlanName } resource flexFuncApp 'Microsoft.Web/sites@2023-12-01' = { name: functionName location: location tags: tags kind: 'functionapp,linux' identity: { type: 'SystemAssigned' } properties: { serverFarmId: flexFuncPlan.id siteConfig: { appSettings: [ { name: 'AzureWebJobsStorage__accountName' value: storage.name } { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' value: appInsights.properties.ConnectionString } { name: 'FUNCTIONS_EXTENSION_VERSION' value: '~4' } { name: 'BLOB_CONNECTION_STRING' value: 'DefaultEndpointsProtocol=https;AccountName=${eventGridStorage.name};AccountKey=${eventGridStorage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' } ] } functionAppConfig: { deployment: { storage: { type: 'blobContainer' value: '${storage.properties.primaryEndpoints.blob}${deploymentStorageContainerName}' authentication: { type: 'SystemAssignedIdentity' } } } scaleAndConcurrency: { maximumInstanceCount: maximumInstanceCount instanceMemoryMB: instanceMemoryMB } runtime: { name: functionAppRuntime version: functionAppRuntimeVersion } } } dependsOn:[ appInsights storage eventGridStorage ] } var storageRoleDefinitionId = 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b' //Storage Blob Data Owner role // Allow access from function app to storage account using a managed identity resource storageRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { name: guid(storage.id, storageRoleDefinitionId) scope: storage properties: { roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', storageRoleDefinitionId) principalId: flexFuncApp.identity.principalId principalType: 'ServicePrincipal' } } // resource function 'Microsoft.Web/sites/functions@2020-12-01' = { // parent: flexFuncApp // name: functionNameComputed // properties: { // config: { // disabled: false // bindings: [ // { // type: 'eventGridTrigger' // name: 'event' // direction: 'in' // } // ] // } // files: { // '__init__.py': loadTextContent('__init__.py') // } // } // } ※コメントアウトの箇所は従量課金用のSKUでは動作しましたが、Flex Consumptionでは動作しません。 modules/host/asp.bicep param hostingPlanName string param location string = resourceGroup().location param tags object = {} param sku object = {} param kind string = 'linux' //https://github.com/Azure/azure-quickstart-templates/blob/master/quickstarts/microsoft.web/app-function/main.bicep resource hostingPlan 'Microsoft.Web/serverfarms@2023-12-01' = { name: hostingPlanName location: location tags: tags kind: kind properties: { reserved: true } sku: sku } output id string = hostingPlan.id output name string = hostingPlan.name modules/storage/storage.bicep @description('Storage Account type') @allowed([ 'Premium_LRS' 'Premium_ZRS' 'Standard_GRS' 'Standard_GZRS' 'Standard_LRS' 'Standard_RAGRS' 'Standard_RAGZRS' 'Standard_ZRS' ]) param storageAccountType string = 'Standard_LRS' @description('The storage account location.') param location string = resourceGroup().location param tags object = {} @description('The name of the storage account') param storageAccountName string param containerNames array resource sa 'Microsoft.Storage/storageAccounts@2023-01-01' = { name: storageAccountName location: location tags: tags sku: { name: storageAccountType } kind: 'StorageV2' properties: {} } resource blobServices 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = { parent: sa name: 'default' } resource containers 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = [for containerName in containerNames: { parent: blobServices name: containerName }] output storageAccountName string = storageAccountName output storageAccountId string = sa.id main.bicep targetScope = 'resourceGroup' param location string = resourceGroup().location @description('The environment designator for the deployment. Replaces {env} in namingConvention.') @allowed([ 'dev' //Develop 'stg' //Staging 'prd' //Production ]) param enviromentName string = 'dev' var enviromentResourceNameWithoutHyphen = replace(enviromentName, '-', '') @allowed(['northeurope', 'southeastasia', 'eastasia', 'eastus2', 'southcentralus', 'australiaeast', 'eastus', 'westus2', 'uksouth', 'eastus2euap', 'westus3', 'swedencentral']) param hostingPlanLocation string = 'eastus2' @description('The workload name. Replaces {workloadName} in namingConvention.') param workloadName string = 'pj' param deploymentStorageContainerName string = 'app-pkg-func' param eventGridContainerName string = 'eventcontainer' var suffixResourceName = '-${workloadName}' var suffixResourceNameWithoutHyphen = replace(suffixResourceName, '-', '') param convertedEpoch int = dateTimeToEpoch(dateTimeAdd(utcNow(), 'P1Y')) var abbrs = json(loadTextContent('abbreviations.json')) var tags = { workload: workloadName environment: enviromentName } //https://learn.microsoft.com/ja-jp/dotnet/api/microsoft.azure.management.operationalinsights.models.workspacesku?view=azure-dotnet-legacy ////https://github.com/mspnp/microservices-reference-implementation/blob/e903447c7b8b6af6302a5f5a7c672572471d01e4/azuredeploy.bicep#L77 module logAnalyticsWorkspace 'modules/monitor/logAnayticsWorkspace.bicep' = { name: 'logAnalyticsWorkspace' params:{ logAnalyticsName: '${abbrs.operationalInsightsWorkspaces}${enviromentName}${suffixResourceName}' location: location } } module appInsights 'modules/monitor/appInsights.bicep' = { name: 'appInsights' params:{ name: '${abbrs.insightsComponents}${enviromentName}${suffixResourceName}' location: location tags: tags logAnalyticsWorkspaceId: logAnalyticsWorkspace.outputs.id } } module storage 'modules/storage/storage.bicep' = { name: 'storage' params: { storageAccountName: '${abbrs.storageStorageAccounts}${enviromentResourceNameWithoutHyphen}${suffixResourceNameWithoutHyphen}' location: location tags: tags storageAccountType: 'Standard_LRS' containerNames: [deploymentStorageContainerName] } } module evetGridStorage 'modules/storage/storage.bicep' = { name: 'evetGridStorage' params: { storageAccountName: '${abbrs.storageStorageAccounts}${enviromentResourceNameWithoutHyphen}egst${suffixResourceNameWithoutHyphen}' location: location tags: tags storageAccountType: 'Standard_LRS' containerNames: [eventGridContainerName] } } var aspSku = { tier: 'FlexConsumption' name: 'FC1' } module hostingPlan 'modules/host/asp.bicep' = { name: 'hostingPlan' params:{ hostingPlanName: '${abbrs.webServerFarms}${enviromentName}${suffixResourceName}' location: !empty(hostingPlanLocation) ? hostingPlanLocation : location tags: tags sku: aspSku kind: 'functionapp,linux' } } module flexFunction 'modules/function/flexFunction.bicep' = if(aspSku.tier == 'FlexConsumption'){ name: 'flexFunction' params:{ functionName: '${abbrs.webSitesFunctions}${enviromentName}-flex${suffixResourceName}' location: hostingPlanLocation tags: tags hostingPlanName: hostingPlan.outputs.name storageAccountName: storage.outputs.storageAccountName applicationInsightsName: appInsights.outputs.appInsightsName functionAppRuntime: 'python' functionAppRuntimeVersion: '3.11' deploymentStorageContainerName: deploymentStorageContainerName eventGridStorageAccountName: evetGridStorage.outputs.storageAccountName } } main.parameters.dev.json { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "location": { "value": "eastus2" }, "enviromentName": { "value": "dev" }, "hostingPlanLocation": { "value": "eastus2" }, "workloadName": { "value": "pj-bicep" }, "deploymentStorageContainerName": { "value": "app-pkg-func-flex" } } } Complete モードでBicep コードを実行する。 az deployment group create \ --name {Resource Group 名}-deploy \ --mode Complete \ --resource-group {Resource Group 名} \ --confirm-with-what-if \ --template-file ./main.bicep \ --parameters ./main.parameters.dev.json 手順2.Event Grid Subscription と紐づける Azure Function にEvent Grid Trigger の関数をDeployする。 Github actions のWorkflowの一部ですが、Flex Consumptionの場合、sku と remote-build を以下のように指定する必要がありました。 v1.5.2 でFlex Consumptionに対応 したので厳密なVersion指定をしています。 deploy: runs-on: ubuntu-22.04 environment: 'dev' steps: - name: Run Azure Functions Action. uses: Azure/functions-action@v1.5.2 id: deploy-to-function with: app-name: ${{ vars.AZURE_FUNCTIONAPP_NAME }} package: ${{ env.DOWNLOAD_ARTIFACT_DIRPATH }} publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }} sku: flexconsumption remote-build: false sku については以下に記載の通り、flexconsumption と指定が必要です。 https://github.com/Azure/functions-action/blob/067064f06ab9a7aeb62be38d35d45287dfa39443/action.yml#L49 remote-build については 以下のようにtrue にする必要があると書いてありますが、false としてRun Azure Functions Action. が実行される前にrequirements.txt からインストールすることで、Deployに成功しました。’scm-do-build-during-deployment’ and ‘enable-oryx-build’ は利用できないのでDefault値のままfalseです。  https://github.com/Azure/functions-action/issues/245  でコメントされている通り、同様に困惑しているようです。 https://github.com/Azure/functions-action/blob/067064f06ab9a7aeb62be38d35d45287dfa39443/action.yml#L54 Event Trigger は以下のようにログ出力するだけの関数を用意しています。 function_app.py import logging import azure.functions as func app = func.FunctionApp() @app.function_name(name="eventGridTrigger01") @app.event_grid_trigger(arg_name="event") def event_grid_trigger(event: func.EventGridEvent): """Process an event grid trigger for a new blob in the container.""" logging.info("Processing event %s", event.id) 手順3. Event Grid(System Topic) と Event Grid Subscription をDeployする ポイント: 事前にAzure Functions にEvent Grid Trigger の関数がDeployされていること。 Event Grid System Topic 自体はAzure Function に Event Grid Trigger の関数がDeployされていなくとも作成できるが、Event subscription は関数名を指定する必要がある。 Event subscription のリソース作成では関数名の数分でループして作成。main.parameters.dev.json の箇所の eventGridFunctionsInJson でJson形式で関数をParameter に渡している。 eventGridStorageName と functionAppName はそれぞれ手順1でDeployしたStorage Account名とAzure Function 名を指定する。 Event Grid 用のBicep コード modules/storage/storage.bicep param name string resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' existing = { name: name } output id string = storage.id output name string = storage.name output location string = storage.location modules/event/systemTopic.bicep @description('The name of the Event Grid custom topic.') param eventGridSystemTopicName string = 'topic-${uniqueString(resourceGroup().id)}' @description('The name of the Event Grid custom topic\'s subscription.') param eventGridSystemTopicSubscriptionName string = 'sub-${uniqueString(resourceGroup().id)}' param location string = resourceGroup().location param storageAccountId string param tags object = {} param functionAppName string param functions array // Microsoft.EventGrid/systemTopics の作成する // https://github.com/Azure/azure-quickstart-templates/blob/master/quickstarts/microsoft.eventgrid/event-grid-subscription-and-storage/main.bicep resource systemTopic 'Microsoft.EventGrid/systemTopics@2023-12-15-preview' = { name: eventGridSystemTopicName location: location identity: { type: 'SystemAssigned' } tags: tags properties: { source: storageAccountId topicType: 'Microsoft.Storage.StorageAccounts' } } // 関数の数分、Event Grid Subscription を作成する resource eventSubscription 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2023-12-15-preview' = [for function in functions: { parent: systemTopic name: '${eventGridSystemTopicSubscriptionName}-${function.name}' properties: { destination: { endpointType: 'AzureFunction' properties: { resourceId: resourceId('Microsoft.Web/sites/functions', functionAppName, function.name) } } eventDeliverySchema: 'EventGridSchema' filter: { includedEventTypes: function.includedEventTypes } } }] abbreviations.json { "operationalInsightsWorkspaces": "log-", "insightsComponents": "appi-", "storageStorageAccounts": "st", "webServerFarms": "plan-", "webSitesFunctions": "func-", "eventGridSystemTopics": "egst-" } main.bicep targetScope = 'resourceGroup' @minLength(1) @maxLength(16) @description('Name of the the environment which is used to generate a short unique hash used in all resources.') param environmentName string = 'test' @description('The workload name. Replaces {workloadName} in namingConvention.') param workloadName string = 'pj' var suffixResourceName = '-${workloadName}' var suffixResourceNameWithoutHyphen = replace(suffixResourceName, '-', '') param eventGridFunctionsInJson object var eventGridFunctions = eventGridFunctionsInJson.eventGridFunctions param eventGridStorageName string param functionAppName string var tags = { 'workload': workloadName, 'environment': environmentName } var abbrs = loadJsonContent('./abbreviations.json') // Event Grid 用のストレージアカウントを参照する。 module eventGridStorage './modules/storage/storage.bicep' = { name: 'eventGridStorage' params: { name: eventGridStorageName } } // NOTE: Function App に Event Grid Trigger を追加後に、Event Grid System Topic と Event Grid Subscription を作成する。 // Event Grid (System Topic) , Event Grid Subscription を作成する。 // https://learn.microsoft.com/ja-jp/azure/templates/microsoft.storage/storageaccounts/blobservices/containers?pivots=deployment-language-bicep // https://learn.microsoft.com/ja-jp/azure/templates/microsoft.eventgrid/eventsubscriptions?pivots=deployment-language-bicep module eventGridSystemTopic './modules/event/systemTopic.bicep' = { name: 'eventGridSystemTopic' params: { eventGridSystemTopicName: '${abbrs.eventGridSystemTopics}${environmentName}${suffixResourceName}' eventGridSystemTopicSubscriptionName: '${abbrs.eventGridEventSubscriptions}${environmentName}${suffixResourceName}' location: eventGridStorage.outputs.location //NOTE: eventGrid Storage と同じ Region にする storageAccountId: eventGridStorage.outputs.id functionAppName: functionAppName functions: eventGridFunctions tags: tags } } main.parameters.dev.json { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "environmentName": { "value": "dev" }, "workloadName": { "value": "pj-bicep" }, "eventGridStorageName": { "value": "stdevegstpjbicep" }, "functionAppName": { "value": "func-dev-flex-pj-bicep" }, "eventGridFunctionsInJson": { "value": {"eventGridFunctions":[{"name": "eventGridTrigger01", "includedEventTypes": ["Microsoft.Storage.BlobCreated"]}]} } } } Incremental モードでBicep コードを実行する。 az deployment group create \ --name {Resource Group 名}-deploy-eventgrid \ --mode Incremental \ --resource-group {Resource Group 名} \ --confirm-with-what-if \ --template-file ./main.bicep \ --parameters ./main.parameters.dev.json 確認方法 手順1で作成したEvent Grid 用のStorage Accountのコンテナに任意のファイルをアップロードすることでAzure Function のEvent Grid Trigger が実行されることを確認します。 ここでは Azure Function にDeployした関数: eventGridTrigger01 の呼び出しタブから実行結果を確認しました。  まとめ Flex Consumptionはまだ Preview 版のため本番運用では非推奨ですが、常時使用可能でタイムアウトの上限がないなどメリットがあります。今回はFlex ConsumptionのBicep コードによるDeployとGithub actionsでのDeployのハマりポイントを踏まえながら説明しました。 参考URL Azure Event Grid とは Azure Functions のホスティング オプション Azure Functions の Flex 従量課金プラン ホスティング Flex 従量課金プランの非推奨 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Bicepによる Event Grid トリガーとバインドした Event Subscription の作成 (Flex Consumption のAzure Functionsの利用) first appeared on SIOS Tech. Lab .
こんにちは、サイオステクノロジーの和田です。 今回は こちら のドキュメントを参考に、GincoさんのAPIを動かしてみました。ドキュメントにあるINDEXERのEthereum APIを一通り動かしてみたので、順を追って説明していきたいと思います。 Gincoが提供しているIndexer APIとは Gincoが提供しているIndexer APIでは、Webhook通知やインデックスされたトランザクション情報を提供することで、アドレスに対して発生したイベントの追跡を容易にすることが可能となっています。 このブログの目的 実際にGincoのAPIを使ってウォレットを作成し、取引の通知(Webhook)と履歴(Transfer)の確認ができることを検証していきたいと思います。 前提条件 今回はTypescriptをベースにコードを書いていきます。実行環境はNode.jsです。 下記コードをベースに変更箇所のみ記載していきます。 import * as crypto from "crypto"; import fetch from "node-fetch"; const main = async () => { const key = "<YOUR API KEY>"; const secret = "<YOUR API SECRET>"; const now = Date.now(); const oneSecondAgo = now - 1000; const nonce = oneSecondAgo; console.log(now); console.log(oneSecondAgo); const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/GetWallet"; // NOTE: The JSON payload has to be sorted in alphabetical order. const body = JSON.stringify({ wallet_id: "95c1126d-d338-481e-8edb-5d66add6cfbb", }); const msg = `${nonce}${path}${body}`; const hmac = crypto.createHmac("sha256", secret); const signature = hmac.update(msg).digest("base64"); const headers = { accept: "application/json", "Content-Type": "application/json", "X-API-KEY": key, "X-API-SIGNATURE": signature, "X-API-NONCE": `${nonce}`, }; const url = "<https://web3-cloud-testnet-prd.gincoapis.com>" + path; const response = await fetch(url, { method: "POST", body: body, headers: headers, }); const resp = await response.json(); console.log(resp); }; main().catch((e) => { console.error(e); process.exit(1); }); APIキーに関しては、 こちら のページでログインすることで確認できます。 動作検証 以下の流れで検証を行いました。 ウォレットの作成 アドレスの登録 コントラクトの登録 アセットの登録 Webhookの検証 トランスファーの表示 まず最初にウォレットを作成します。 ウォレットの作成 変更箇所 const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/RegisterWallet"; const body = JSON.stringify({ name: "Alice Wallet", }); 実行結果 { wallet_id: 'a0344698-5062-40c3-9bac-bd95d5ebac79' } これで、ウォレットが作成できました。一覧表示で確認してみます。 ウォレットの一覧表示 変更箇所 const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/ListWallets"; const body = JSON.stringify({}); 実行結果 { wallets: [ { wallet_id: 'a0344698-5062-40c3-9bac-bd95d5ebac79', name: 'Alice Wallet', addresses: [], confirmation: '10', create_time: '2024-09-04T04:04:06.483198Z', update_time: '2024-09-04T04:04:06.483199Z' } ], pagination: { next_page_token: '' } } ウォレットが作成されていることが確認できました。次に、作成したウォレットにアドレスを登録していきたいと思います。 アドレス登録の事前準備として下記コードでプライベートキーとアドレスを生成します。 import { ethers } from "ethers"; // 秘密鍵をランダムに生成 const wallet = ethers.Wallet.createRandom(); // 秘密鍵とアドレスを表示 console.log("Private Key:", wallet.privateKey); console.log("Address:", wallet.address); console.log("Address without checksum:", wallet.address.toLowerCase()); 生成したプライベートキーとアドレスはなくさないようにメモしておいてください。 では、ウォレットに生成したアドレスを登録していきます。 アドレスの登録 変更箇所 const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/RegisterAddress"; const body = JSON.stringify({ address: "0xcfd0da83f1764c04b65fc9222d2ddcc0a8ef819e", wallet_id: "a0344698-5062-40c3-9bac-bd95d5ebac79", }); 実行結果 {} アドレスを登録できたかどうかウォレットを表示して確認していきます。 ウォレットの表示 変更箇所 const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/GetWallet"; const body = JSON.stringify({ wallet_id: "a0344698-5062-40c3-9bac-bd95d5ebac79", }); 実行結果 { wallet: { wallet_id: 'a0344698-5062-40c3-9bac-bd95d5ebac79', name: 'Alice Wallet', addresses: [ '0xcfd0da83f1764c04b65fc9222d2ddcc0a8ef819e' ], confirmation: '10', create_time: '2024-09-04T04:04:06.483198Z', update_time: '2024-09-04T04:04:06.483199Z' } } アドレスが追加されたことを確認することができました。 次にコントラクトを作成して登録していきたいと思います。 事前準備として、Remixなどでコントラクトを事前にテストネットにデプロイしておいてください。今回はERC721のコントラクトで動作確認していきます。 コントラクトの登録 変更箇所 const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/RegisterContract"; const body = JSON.stringify({ contract_address: "0xa3292990a1ce2287d53b80e23727877a23c29c60".toLowerCase(), contract_type: "CONTRACT_TYPE_ERC721", }); 実行結果 {} コントラクトが登録できたか確認します。 コントラクトの確認 変更箇所 const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/GetContract"; const body = JSON.stringify({ contract_address: "0xbBd6212581C0900fe9e253eb054AC93b7BDFed44".toLowerCase(), }); 実行結果 { "contract": { "contract_address": "0xa3292990a1ce2287d53b80e23727877a23c29c60", "contract_type": "CONTRACT_TYPE_ERC721", "contract_metadata": { "erc20": null, "erc721": { "name": "MyToken", "symbol": "MTK" }, "erc1155": null } } } コントラクトが登録されたことを確認することができました。 続いてアセットのインポートを試したいと思います。アセットとはERC20トークンやERC721のNFTのことです。 アセットのインポート 変更箇所 const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/ImportAsset"; const body = JSON.stringify({ address: "0xcfd0da83f1764c04b65fc9222d2ddcc0a8ef819e", asset_type: "ASSET_TYPE_ERC721", contract_address: "0xa3292990a1ce2287d53b80e23727877a23c29c60", token_id: "0", }); 実行結果 {} { assets: [ { wallet_id: 'a0344698-5062-40c3-9bac-bd95d5ebac79', address: '0xcfd0da83f1764c04b65fc9222d2ddcc0a8ef819e', asset_type: 'ASSET_TYPE_ERC721', balance: '0', asset_metadata: [Object] } ], pagination: { next_page_token: '' } } アセットを追加することができました。一覧で表示してみます。 アセットの一覧表示 変更箇所 const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/ListAssets"; const body = JSON.stringify({ asset_type: "ASSET_TYPE_ERC721", wallet_id: "a0344698-5062-40c3-9bac-bd95d5ebac79", }); 実行結果 { "assets": [ { "wallet_id": "a0344698-5062-40c3-9bac-bd95d5ebac79", "address": "0xcfd0da83f1764c04b65fc9222d2ddcc0a8ef819e", "asset_type": "ASSET_TYPE_ERC721", "balance": "1", "asset_metadata": { "erc20": null, "erc721": { "contract_address": "0xa3292990a1ce2287d53b80e23727877a23c29c60", "name": "MyToken", "symbol": "MTK", "token_id": "0", "token_uri": "ipfs://testtesttest.json", "token_data": "" }, "erc1155": null } } ], "pagination": { "next_page_token": "" } } 続いて、Webhookの検証を行いたいと思います。 事前準備として、通知を受け取るためのサーバーを用意する必要があります。 Webhookエンドポイントの事前準備 今回は、EC2上にNginxとPythonのFlaskでWebhookエンドポイント用のサーバーを立てました。 /webhookに送信されたPOSTリクエストを表示するように構築しました。 下記Nginxのコンフィグ user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /run/nginx.pid; # Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. include /usr/share/nginx/modules/*.conf; events { worker_connections 1024; } http { 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; types_hash_max_size 4096; include /etc/nginx/mime.types; default_type application/octet-stream; include /etc/nginx/conf.d/*.conf; server { listen 80; listen [::]:80; server_name _; root /usr/share/nginx/html; location /webhook { proxy_pass <http://127.0.0.1:5000>; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Load configuration files for the default server block. include /etc/nginx/default.d/*.conf; error_page 404 /404.html; location = /404.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } } } 下記Pythonコード from flask import Flask, request app = Flask(__name__) @app.route("/webhook", methods=['POST']) def webhook(): data = request.get_data(as_text=True) headers = request.headers print(f"Received headers\\n{headers}") print(f"Received data\\n{data}") response_text = f"Headers: {headers}\\nData: {data}" return response_text 次に、作成したサーバーのエンドポイントをWebhookに登録していきます。 Webhookの登録 変更箇所 const ipAddressPort = "<YOUR IPADDRESS PORT>"; const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/RegisterWebhook"; const body = JSON.stringify({ confirmation: 10, wallet_id: "a0344698-5062-40c3-9bac-bd95d5ebac79", webhook_endpoint: `http://${ipAddressPort}/webhook`, }); 実行結果 { webhook_id: '35241b70-4e23-4b6f-a9d6-6067f11848e0' } Webhookを登録することができました。一覧表示で確認してみます。 Webhookの一覧表示 変更箇所 const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/ListWebhooks"; const body = JSON.stringify({}); 実行結果 { webhooks: [ { webhook_id: '35241b70-4e23-4b6f-a9d6-6067f11848e0', wallet_id: 'a0344698-5062-40c3-9bac-bd95d5ebac79', confirmation: '10', webhook_endpoint: 'http://<YOUR IPADDRESS PORT>/webhook' } ] } これで、Webhookを登録できたことが確認できました。次に、Webhookで送信されるリクエストのヘッダーを追加したいと思います。 WebhookHeaderの登録 変更箇所 const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/RegisterWebhookHeader"; const body = JSON.stringify({ header_key: "Test-Header", header_value: "Test-Value", }); 実行結果 { webhook_header_id: '0be1968a-3136-4e5d-968c-9c2f3ed97cce' } テスト用にheader_keyとheader_valueを設定しました。これでWebhookエンドポイントで通知を受け取った際にヘッダーが追加されていることが確認できます。ヘッダーが登録されたか確認します。 WebhookHeaderの一覧表示 変更箇所 const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/ListWebhookHeaders"; const body = JSON.stringify({}); 実行結果 { webhook_headers: [ { webhook_header_id: '0be1968a-3136-4e5d-968c-9c2f3ed97cce', webhook_id: '', header_key: 'Test-Header', header_value: 'Test-Value' } ] } 続いて、Webhookの動作確認を行いたいと思います。MetaMaskなどで他のウォレットから今回作成したウォレットに送金して、結果が通知されるか確認します。 Webhook動作確認 他のウォレットから作成したウォレットに送金したところ、下記のとおりWebhookで通知が来たことを確認できました。 Received headers Host: 13.236.177.75 X-Real-Ip: 34.146.200.240 X-Forwarded-For: 34.146.200.240 X-Forwarded-Proto: http Connection: close Content-Length: 391 User-Agent: Go-http-client/1.1 Test-Header: Test-Value Accept-Encoding: gzip Received data {"webhook_id":"35241b70-4e23-4b6f-a9d6-6067f11848e0","webhook_endpoint":"http://<YOUR IPADDRESS PORT>/webhook","notification_type":"NOTIFICATION_TYPE_EVM_TRANSACTION_UPDATED","event":{"transaction_hash":"0x06b31dd924f1f1650e4952add792072a9618a4e3745c5c2e02cc36b6e362daea","address":"0xcfd0da83f1764c04b65fc9222d2ddcc0a8ef819e","confirmation":1,"wallet_id":"a0344698-5062-40c3-9bac-bd95d5ebac79"}} 先ほど設定したTest-Headerも追加されていることが確認できました。 最後にトランスファーの一覧表示を確認してみたいと思います。こちらで取引履歴を確認することができます。 トランスファーの一覧表示 変更箇所 const path = "/gincoinc.web3cloud.ethereum.gateway.v1.GatewayService/ListTransfers"; const body = JSON.stringify({ wallet_id: "a0344698-5062-40c3-9bac-bd95d5ebac79", }); 実行結果 { "transfers": [ { "transaction_hash": "0xcd515bc6b288bd085051bd174aa04886d3058a8ceffe0d477b22440bec62b287", "asset_type": "ASSET_TYPE_ERC721", "nonce": "1063", "from": "0x0000000000000000000000000000000000000000", "to": "0xcfd0da83f1764c04b65fc9222d2ddcc0a8ef819e", "value": "1", "transaction_fee": "0.001203297764048282", "transfer_state": "TRANSFER_STATE_SUCCESS", "block_number": "6659705", "block_timestamp": "1725864948", "transfer_metadata": { "erc20": null, "erc721": { "contract_address": "0xa3292990a1ce2287d53b80e23727877a23c29c60", "name": "MyToken", "token_id": "0", "symbol": "MTK", "token_uri": "ipfs://testtesttest.json", "token_data": "" }, "erc1155": null } }, { "transaction_hash": "0x06b31dd924f1f1650e4952add792072a9618a4e3745c5c2e02cc36b6e362daea", "asset_type": "ASSET_TYPE_ETH", "nonce": "7", "from": "0x68aabf716de793b59fd83265b42aa053be1c0161", "to": "0xcfd0da83f1764c04b65fc9222d2ddcc0a8ef819e", "value": "0.001", "transaction_fee": "0.000051917709102", "transfer_state": "TRANSFER_STATE_SUCCESS", "block_number": "6659301", "block_timestamp": "1725858912", "transfer_metadata": { "erc20": null, "erc721": null, "erc1155": null } }, ], "pagination": { "next_page_token": "" } } 取引の履歴を確認することができました。 今回はMetaMaskで外部のウォレットからの送金も行いましたが、その履歴もすべて表示されることが確認できました。 終わりに 今回は、GincoのINDEXER APIを一通り動かしてみました。実装してみた感想としては、Webhookの通知設定ができるところが便利だと思いました。また、INDEXER APIを使って、仮想通貨の取引情報を簡単に管理できることが実感できました。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GincoのIndexer APIを試してみた first appeared on SIOS Tech. Lab .
初めまして、新卒1年目の伊藤です。 今回は、新卒研修にてGoogleによるSSO(シングルサインオン)を持つAzure Static Web Appsのアプリを作成したため、その方法をまとめていきます。 Azure Static Web Appsを構築する まずは、Webアプリ用のファイルを作成します。今回は、index.htmlとstaticwebapp.config.jsonファイルを作成します。 index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>StaticWebApp_Google</title> </head> <body> <h1>StaticWebApp_GoogleでGoogleログインしました</h1> </body> </html>   staticwebapp.config.json { "$schema": "https://json.schemastore.org/staticwebapp.config.json", "auth": { "identityProviders": { "google": { "registration": { "clientIdSettingName": "GOOGLE_CLIENT_ID", "clientSecretSettingName": "GOOGLE_PROVIDER_AUTHENTICATION_SECRET" } } } }, "routes": [ { "route": "/*", "allowedRoles": ["authenticated"] } ], "responseOverrides": { "401": { "statusCode": 302, "redirect": "/.auth/login/google" } } } staticwebapp.config.jsonはAzure Static Web Appsの設定ファイルであり、認証プロバイダやWebアプリの権限、リダイレクトなどの設定を行うことができます。詳細は、Microsoftのドキュメントを参考にしてみてください。 https://learn.microsoft.com/ja-jp/azure/static-web-apps/configuration 今回の設定では、auth項目でGoogleのクライアント情報を設定し、routes項目でアプリケーション全体へのアクセスを制限しています。また、responseOverrides項目で401エラー(未認証)の場合はGoogleのログインにリダイレクトさせる設定となっています。 作成したファイルは、GitHubのリポジトリに置いておきます。(staticwebapp_googleとする) 次に、Azure Static Web Appsのリソースを構築します。 設定項目は次の通りです。 名前 staticwebapp-google プランの種類 Standard ソース GitHub GitHubリポジトリ staticwebapp_google リソースの情報です。WebアプリのURLはGoogleの認証プロバイダの設定で使用します。以降ではhttps://example.azurestaticapps.netとします。 Googleの認証プロバイダを設定する Google Cloudコンソールでプロジェクトを新規作成します。 作成後の画面です。 APIとサービスで、OAuth同意画面を作成します。User Typeを内部に設定すると組織内のユーザーのみ使用できるWebアプリを作成することができます。 Googleの認証同意画面を設定します。 設定項目は次の通りです。 アプリ名 staticwebapp google ユーザーサポートメール、デベロッパーの連絡先情報 設定者のメールアドレス アプリケーションのホームページ https://example.azurestaticapps.net 承認済みドメイン example.azurestaticapps.net 次にスコープを設定します。今回は、userinfo.profileを設定します。 概要では、設定の確認を行うことができます。 同意画面の設定後は、OAuthクライアントIDを選択して認証プロバイダを設定します。 設定項目は次の通りです。 アプリケーションの種類 ウェブアプリケーション 名前 staticwebapp-google 承認済みのJavascript生成元 https://example.azurestaticapps.net 承認済みのリダイレクト URI https://example.azurestaticapps.net/.auth/login/google/callback 設定後はGoogleのクライアント情報が表示されます。 Azure Static Web Appsのリソースの環境変数で、Googleクライアント情報を設定します。staticwebapp.config.jsonファイルのGOOGLE_CLIENT_ID、GOOGLE_PROVIDER_AUTHENTICATION_SECRETにそれぞれクライアントIDとクライアントシークレットの値を適用します。 動作確認 Webアプリにアクセスします。初回はGoogleのログインが求められます。 ログイン時には、同意画面が表示されます。 Webアプリの画面が表示されました。 組織外のアカウントでログインしようとすると、組織内でのみ利用可能となっていることを確認できます。 まとめ Azure Static Web Appsを使用することでGoogleによるSSOを持つWebアプリを簡単に作成することができました。 少しでも参考になれば幸いです。 参考 https://learn.microsoft.com/ja-jp/azure/static-web-apps/configuration https://learn.microsoft.com/ja-jp/azure/app-service/configure-authentication-provider-google https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid?hl=ja ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GoogleによるSSOを持つAzure Static Web Appsのアプリを作成する first appeared on SIOS Tech. Lab .
はじめに こんにちは、サイオステクノロジーの小野です。 昨今のAI開発において、便利なプラットフォームが数多く登場していますが、その中でもOpenShift AIに対する需要が高まっていると感じています 。したがってこの度、OpenShift AIの調査検証を行うことになりました。それにつきまして、これからOpenShift AIについての記事を記載します。今回は初回なので、OpenShift AIの概要について紹介します。 OpenShift AIとは OpenShift AIの概要 コンテナ技術を利用して、MLOpsを実現させるためのプラットフォームであるOpenShift AIは、データの収集と準備、モデル 1 の学習と評価、モデルの提供と監視を可能にします。 Red Hat OpenShift上でデプロイされるため、オンプレミスかクラウドかデプロイする環境を柔軟に選ぶことが可能です。 また、OpenShift AIでは、機械学習のモデルの開発や管理に必要な複数のOSSを利用しています。 OpenShift AIで利用しているOSS OpenShiftAIで利用しているOSS Open Data Hub MLOpsに必要なkubeflowやJupyterなどの以下に記載されているコンポーネントを提供するプラットフォーム。 Kubeflow Kubernetes上で機械学習のワークフローを簡単に構築、デプロイ、管理するためのプラットフォーム。 Jupyter ソースコード、説明テキスト、数式、グラフなどを1つのドキュメントにまとめることができ、簡単に共有が可能なプラットフォーム。 Tensorflow モデルの構築、学習を行うためのライブラリ。簡単に実装が可能。ビジネス方面で強い。 Pytorch モデルの構築、学習を行うためのライブラリ。一からモデルを構築することや既存のモデルのカスタマイズがしやすい。研究方面で強い。 Scikit-learn データ加工やデータ分析を行うためのライブラリ。 MLOpsとは DevOpsの概念を機械学習の分野に応用し、機械学習のプロジェクトを効率的に進めるためのワークフローを「MLOps」といいます。 MLOpsは以下の流れで機械学習のプロジェクトが進んでいきます。 MLOpsのワークフローの流れ データの収集 様々なデータソースからデータを収集します。 データの分析 収集したデータを分析してモデルの構築に使えるデータを選択します。 データの準備 データをモデルの学習がしやすい形式に変換したり、学習用のデータと評価用のデータを分けたりします。 モデルの学習 準備された学習用データを用いて機械学習のモデルを学習していきます。 モデルの評価 学習が完了したモデルを評価用のデータで評価します。 モデルの検証 評価されたモデルの予測パフォーマンスが目標の品質に到達しているかどうか検証します。 モデルの提供 検証されたモデルを本番環境にデプロイします。 モデルの監視 モデルの予測パフォーマンスを監視します。必要に応じてデータの収集からやり直してモデルを改修します。 MLOpsのメリットとしては、 監視からモデルを再開発しデプロイするまで迅速に行えるのでデータドリフトを防ぐことができます。 データドリフトとは、予測する対象が時間が経過することによって変化していくことで過去のモデルが現在の対象を予測できなくなる現象を言います。 また、 ワークフローを定めることによって、機械学習プロジェクトを効率よく進めることができます。 他AIプラットフォーム比較 AIプラットフォームとして代表的なDify、Azure AI Studio、OpenShift AIの3つを比較します。 Dify 誰でも簡単にAIアプリを開発することができるOSSです。ローカルとSaaS両方で利用可能です。LLM(大規模言語モデル) 2 を中心とした機能が豊富で、RAG 3 もドキュメントをドラッグ&ドロップするだけで実装できます。ノーコードで構築できたり、無料版があったりとにかく気軽に開発ができるので、プログラミング初心者、AI初心者におすすめのプラットフォームです。しかし、自由にモデルをカスタマイズできないので、複雑なタスクを行うことは難しいです。 Azure AI Studio クラウドプラットフォームであるMicrosoft Azureで提供されるAIサービスです。画像分類や物体検出もできますが、LLMに特化したサポートが充実しています。また、Difyのようにノーコードで開発できます。最大のメリットとしては、Azureの豊富なクラウドリソースを活用することができることです。機械学習に必要なMLOpsのプロセスをすべてAzure内で完結させることができます。そのメリットの反面、Azureの環境に大きく依存するデメリットがあります。 OpenShift AI Red HatのOpenShiftを基盤とするAIプラットフォームです。オンプレミスとクラウド環境を選ぶことができます。Kubernetesのスケーラビリティ、自動化、高可用性を活用して、MLOpsにおけるモデルの開発、デプロイ、管理を効率的に行えます。また、LLM以外のモデルの開発も可能です。OpenShiftの環境構築やノーコードでの開発は難しいですが、柔軟なモデル構築が可能です。 最後に 2024年5月にRed Hatの年次カンファレンスであるRed Hat Summit 2024が開催されました。また、2024年10月17日にRed Hat Summit: Connect Tokyo 2024が開催されました。どちらもAIが中心にトピックされて、OpenShift AIも大きく紹介されました。それぐらいOpenShift AIは今最もホットなソリューションです。サイオステクノロジーはこの動向に積極的に追従して情報を発信していこうと思いますので、今後ともよろしくお願いいたします。 注釈 モデル:ある入力に対して結果を出力する関数のこと。入力されたデータのパターンや関係性を分析し、そのデータに対して理想の結果を出力するように関数を調整することを「モデルの学習」という。 LLM:大量のテキストデータから学習したモデル。代表的なモデルにGPTやBERTがある。 RAG:LLMでテキストを生成する際に、外部の情報を参照させる技術のこと。 参考 OpenShift AI: https://www.redhat.com/ja/technologies/cloud-computing/openshift/openshift-ai MLOps: https://cloud.google.com/architecture/mlops-continuous-delivery-and-automation-pipelines-in-machine-learning Dify: https://dify.ai/jp Azure AI Studio: https://azure.microsoft.com/ja-jp/products/ai-studio Red Hat Summit: Connect Tokyo 2024: https://www.redhat.com/ja/summit/connect/apac/tokyo-2024 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Red Hat OpenShift AIとは? first appeared on SIOS Tech. Lab .
開発者が アプリケーション のリリースプロセスを効率化する際、継続的インテグレーション/継続的デリバリー(CI/CD)の手法を取り入れる必要があります。OpenShift 環境では、Red Hat OpenShift Pipelines を導入することで CI/CD ワークフローを自動化することが出来ます。本記事では CI/CD について、OpenShift Pipelines について、その他のCIソリューションとの比較を 解説します。 本記事に続いて、CD 部分である OpenShift Gitops、OpenShift Pipelines のハンズオンなどの記事も投稿する予定なので、そちらもぜひ読んでみてください。 CI/CD について Continuous Integration / Continuous Deliveryの略称で、継続的インテグレーション&継続的デリバリーという意味です。具体的に CI と CD が何を指すかというと以下の通りです。 CI は、ビルドとテスト等を自動化させることを指します。コード変更がメインブランチにマージされるたびに自動ビルドとテストが行われ、問題を早期に検出して対処することができます。 CD は、CI のプロセスをさらに拡張し、アプリケーションを自動的にリリース準備段階に(継続的デリバリー)または本番環境にデプロイ(継続的デプロイメント)することを目指します。 Openshift Pipelines について OpenShift Pipelines は、Red Hat のエンタープライズ向け Kubernetes プラットフォームである OpenShift 上で動作する、CI/CD ソリューションです。オープンソースフレームワークの CI/CD ソリューションである Tekton を基盤としたこのツールは、開発者がアプリケーションのビルド、テスト、デプロイメントを自動化し、より迅速にアプリケーションをリリースできます。 OpenShift Pipelines では CD 部分についても実装が可能ですが、いわゆる Push 型のデプロイになってしまいます。CD 部分では開発環境や本番環境の状態を検知してあるべき状態に変更する Pull 型の実装が望ましいとされているため、本記事では OpenShift Pipelines で CI 部分のみ実装することを想定しています。 CD 部分は OpenShift Gitops で実装します。 ここで、OpenShift Pipelines のユースケースを一つ挙げてみます。例えば、開発者はリポジトリにコミットをプッシュします。すると webhook が送信され、パイプラインが実行されます。パイプラインが実行されると、アプリケーションのビルドとテスト、イメージのビルドとレジストリへのプッシュが自動的に実行されます。 Openshift Pipelines のコンポーネントについて Task ビルドやテストといったパイプラインの各動作は Task と呼ばれる 作業単位で定義します。Task の中では Step と呼ばれるコマンドの実行単位を定義します。Task は Pod として構成され、Step はコンテナとして実行されます。 Tekton Hub に公開されている Task を使用することも出来たり、ソースコードの取得といったよく使われる Task は ClusterTask と呼ばれるクラスタ全体で共通利用できる Task として登録して再利用したり出来る点が一番の利点と言えます。 Pipeline 実行したい Pipeline の流れを Task で定義します。実行する Task の順序や実行条件、失敗した際のリトライ回数などを定義します。 Workspaces パイプラインが実行される際に使用される永続的なストレージ領域で、Task 間のデータ共有をするために使用されます。 Trigger Trigger と呼ばれる機能を定義して外部イベントを webhook などで受け取り、パイプラインをインスタンス化して動作させるといったメカニズムを定義します。 その他の CI ソリューションについて 他にも多くの CI ソリューションが存在します。Openshift Pipelines の基盤となっているTekton 以外の CI ソリューションと Tekton との比較をいくつか紹介します。 Jenkins Jenkins は Java で開発されたオープンソースの自動化サーバです。プラグインの豊富さと柔軟性が特徴で、多くの企業で広く採用されています。Tekton と比較すると、Jenkins は長い歴史があり、コミュニティが活発でサポートされるプラグインが多い点が異なります。一方で、Jenkins は従来のサーバーベースで動作するため、クラウドネイティブな環境に最適化されている Tekton に比べると、スケーリングやコンテナ管理においては劣る面があります。 CircleCI CircleCI は SaaS の CI/CD ソリューションで、GUI で直感的なワークフローを作成できます。Tekton と比較すると、CircleCI は SaaS として利用できるため、インフラの管理が不要である点が異なります。CircleCI はパフォーマンスに優れていますが、Tekton は Kubernetes 上で動作するため、Kubernetes との親和性が高いという利点があります。 Travis CI Travis CI は GitHub リポジトリと連携して自動ビルドやテストを行うことが出来る CI ソリューションです。設定がシンプルで簡単に始められる点が特徴です。Travis CI はシンプルな分、カスタマイズ性や拡張性では Tekton が優れています。 各 CI ソリューションにはそれぞれの特徴があり、プロジェクトの要件や環境に応じて適切なツールを選択することが重要です。 まとめ 本記事では CI/CD について、OpenShift Pipelines について、Openshift Pipelines のコンポーネントについて、その他の CI ソリューションとの比較を解説しました。各 CI ソリューションには適切なツールを選択する必要があり、OpenShift 環境では、Kubernetes との親和性が高い Tekton を基盤として OpenShift に最適化された OpenShift Pipelines を採用することが望ましいです。 本記事に続いて、CD 部分である OpenShift Gitops、OpenShift Pipelines のハンズオンなどの記事も投稿する予定なので、そちらもぜひ読んでみてください。 参考文献 https://docs.openshift.com/container-platform/4.17/cicd/index.html https://developers.redhat.com/articles/2024/09/17/devops-openshift-pipelines-gitops#continuous_integration_with_openshift_pipelines https://github.com/openshift/pipelines-tutorial?tab=readme-ov-file#trigger https://www.jenkins.io/ https://circleci.com/ja/ https://www.travis-ci.com/ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Red Hat OpenShift Pipelines とは? first appeared on SIOS Tech. Lab .
こんにちは。サイオステクノロジーの橋本です。 前回は CPU 使用率の見方について確認したので 今回はリソースの見方 (メモリ使用率) について今一度確認してみましょう 前回 : リソースの見方を今一度確認してみよう~CPU使用率~ 前提 前回 にもお伝えしましたが「使用率が高い状況 = 悪」と一概には言えない点に注意してください。 特にメモリに関しては急激に使用率が上がったり、下がったりという事はあまりないです。 そのためメモリ使用率が 90 % で安定しているサーバも多数存在するものと考えます。 サーバに搭載されているメモリ量の確認方法 /proc/meminfo を確認いただければメモリ量が確認できます。 $ grep MemTotal /proc/meminfo MemTotal: 3704456 kB $ 現在の メモリ 使用率の見方 さてメモリ使用率ですが、CPU 使用率と違いちょっと癖があります。 まずはメモリ使用率を確認する中で最もメジャーと言えるコマンド free で確認してみましょう RHEL 7~9 いずれのバージョンでも以下のような見え方となります。 $ free total used free shared buff/cache available Mem: 3704456 161332 2557232 46964 985892 3266472 Swap: 0 0 0 $ シンプルにメモリの空き容量を確認したいなら「available」を確認してください。 ※ RHEL 6 系では項目 available は存在しません 基本的に Linux は空きメモリをキャッシュに割り当てようとします。 そのため稼働が長いサーバであるなら、必然的に free (空きメモリサイズ) は小さくなる傾向があります。 キャッシュの中でも開放ができる領域と開放ができない領域があります。 もし、メモリが必要になった場合、解放できるキャッシュ領域を解放し、必要なメモリに割り当て直します。 つまり、available は free 領域に加えて解放可能なキャッシュ領域の値を加算した値となります。 基本的に available に空きがあれば、問題ないと判断できます。 free をみて「空き領域が少ない!」とならないように気を付けてください。 他に確認したい項目としては sar -B で確認できる pgscand/s があります。 # sar -B Linux 4.18.0-477.43.1.el8_8.x86_64 (ip-172-31-11-137.ap-northeast-1.compute.internal) 2024年10月02日 _x86_64_ (2 CPU) 15:50:54 LINUX RESTART (2 CPU) 16時00分06秒 pgpgin/s pgpgout/s fault/s majflt/s pgfree/s pgscank/s pgscand/s pgsteal/s %vmeff 16時10分01秒 1.72 2.54 258.94 0.03 174.93 0.00 0.00 0.00 0.00 16時20分01秒 254.52 12.28 382.56 0.10 284.06 0.00 0.00 0.00 0.00 16時30分06秒 0.00 3.28 246.27 0.00 166.72 0.00 0.00 0.00 0.00 # ログローテートなどで巨大なファイルを処理するシーンなど、それなりのサイズのメモリが必要になることがあります。 この際、Linux は巨大なファイルを扱うだけのサイズをメモリを確保しようとし、 開放可能なキャッシュ領域を走査し、解放します。 システムが至急必要になったため、メモリの空き領域をスキャンしようとした回数が pgscand/s となります。 定期的に pgscand/s も確認し、非常に高い値を記録しているなら メモリ不足を疑う…という定常作業も行うとより good です。 ※メモリが不足しているか否かのもう一つの判断ポイントは SWAP ですが、 それは別回で解説します。 vmstat でもメモリ使用率は確認できなくはないですが、-a オプションをつける必要があります。 $ vmstat -at 1 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- -----timestamp----- r b swpd free inact active si so bi bo in cs us sy id wa st JST 0 0 0 393816 1205080 1989776 0 0 34 667 274 511 4 3 92 0 1 2024-10-03 14:56:13 0 0 0 393592 1205308 1989780 0 0 0 8 487 476 1 2 96 0 0 2024-10-03 14:56:14 0 0 0 393676 1205264 1989780 0 0 0 8 379 414 1 2 97 0 1 2024-10-03 14:56:15 $ 「free + inact ≒ available」となります。 そのため直感的なわかりやすさで言うと free コマンドに軍配があがりますね メモリ を利用しているプロセスの特定方法 コマンド「ps auxww –sort=-%mem」で特定可能です。 このコマンドで以下のように CPU 使用率が高い順で表示してくれます。 # ps auxww --sort=-%mem | head USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND pg-user 1016 0.0 1.2 702004 45620 ? Ss 15:45 0:00 /usr/pgsql-12/bin/postmaster -D /pgdb/data root 680 0.3 0.8 558064 31836 ? Ssl 15:45 0:02 /usr/libexec/platform-python -Es /usr/sbin/tuned -l -P polkitd 923 0.0 0.6 1610032 23736 ? Ssl 15:45 0:00 /usr/lib/polkit-1/polkitd --no-debug root 676 0.0 0.4 379688 18508 ? Ssl 15:45 0:00 /usr/sbin/NetworkManager --no-daemon # 過去のメモリ使用率の見方 基本的に過去のリソース使用状況はデフォルトの状態では見ることはできません。 sysstat サービスを有効化し、sar コマンドが利用できる状況にしておく必要があります。 sar コマンドでは「どのプロセスが メモリ を利用しているか」まではわからないので、 将来的に発生する可能性がある障害に備えて 5 分間隔で ps auxww コマンドの結果をログに残す等の監視をご検討ください。 特にメモリリークが発生している場合、ある程度の期間分の ps auxww の結果を比較する必要となり、 保存する必要性が高いと言えます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post リソースの見方を今一度確認してみよう~メモリ使用率~ first appeared on SIOS Tech. Lab .
こんにちは、OSSよろず相談室のSKです。 OSS に関するお問い合わせが日々寄せられる中で、今回はよくお問い合わせのあるエラーについて深掘りしていくシリーズを始めます! シリーズ第1弾は、Apache のエラー AH03490 に焦点を当て、原因や対処法を解説していきます。 AH03490: scoreboard is full, not at MaxRequestWorkers. Increase ServerLimit. このメッセージは、Apache Http Serverのエラーログに、httpdのリロード時や起動時に出力されることがあります。 以下の流れでこのエラーを深堀りしてゆきたいと思います。 メッセージの意味は? 直訳します。 MaxRequestWorkersの制限には達していませんが、スコアボードがいっぱいです。 ServerLimitの値を増やしてください。 このメッセージは、 Apache の MPM というモジュールの設定が影響しています。 MPM とは 、 Apache HTTP サーバがリクエストを処理するためのプロセスやスレッドの管理方法を決定するモジュール です。 Apacheは並行して複数のリクエストを処理するために、MPMを使ってプロセスの生成、管理、終了を行います。 MPMはApacheのパフォーマンスや動作に大きな影響を与えます。 まずはメッセージの意味を1つ1つ確認しましょう。 scoreboard is full / スコアボードがいっぱいです スコアボード は、 Apacheのメモリ領域で、1つ1つのリクエストの処理の状態が記録される領域 です。 1つ1つのリクエストはスロットという入れ物で処理します。 使用するモジュールが Prefork MPM の場合はスロットはプロセスで、Worker MPM や EVent MPM の場合はスレッドです。 インストール時にどの MPM モジュールが選択されるかは OS をインストールするときに決まりますが、通常は EVent MPM です。(※) エラーの意味に戻りますが、「スコアボードがいっぱいです」ということなので、リクエストがたくさん来て、スロットがなくなり、スコアボードがいっぱいになってしまっていることが分かります。 ※どの種類のMPMが使用されるかは、以下のドキュメントを参考にしてください。(日本語にすると詳細の説明が無いので英語版を案内しています) Multi-Processing Modules (MPMs) / MPM Defaults not at MaxRequestWorkers / MaxRequestWorkersの制限には達していませんが MaxRequestWorkers は、 Apacheが同時に処理できるリクエストの最大数 です。 httpd.confの <IfModule mpm_event_module> タグ内に設定する設定項目です。 例えば、httpd.confで以下のように設定されています。 ★mpm_event_moduleの設定例 <ifmodule mpm_event_module> ServerLimit 16 StartServers 2 MaxRequestWorkers 150 MinSpareThreads 25 MaxSpareThreads 75 ThreadsPerChild 25 </ifmodule> Apacheドキュメント / Apache MPM worker より設定の典型例を抜粋 Increase ServerLimit / ServerLimitの値を増やしてください ServerLimit は、 Apacheがリクエストを処理するために生成できる子プロセス数の最大数 です。 httpd.confの <IfModule mpm_event_module> タグ内に設定する設定項目です。 ServerLimitの設定は、MaxRequestWorkers の値を設定する際に影響します。 上記の ★mpm_event_moduleの設定例 をもう一度見てみましょう。 ThreadsPerChildは各子プロセスが生成するスレッドの数です。 ですので、以下のように設定する必要があり、以下のように、MaxRequestWorkersを75に設定する必要があります。 ServerLimit × ThreadsPerChild = MaxRequestWorkers 上記の ★mpm_event_moduleの設定例 の値を当てはめると、以下となります。 子プロセスの最大数(16) × 子プロセスが生成するスレッド数(25) = 最大リクエスト処理数(150) つまり、サーバへのアクセスが多く、リクエストがたくさん来てスレッドが使われてしまっているので、子プロセスの最大数(ServerLimit)と最大リクエスト処理数(MaxRequestWorkers)を増やしてください、というメッセージになります。 なぜ出力されるのか ざっくりいうと、リクエスト数がMPMに設定した値よりも多く来た場合にこの出力がされるのです。 Apacheのドキュメントにこのエラーについての説明があります。 Apache MPM event / Graceful process termination and Scoreboard usage Apacheのソースコードも併せて確認すると、このエラーが出力される条件は、以下の3つの条件が重なった場合です。 アイドルスレッド数が MinSpareThreads 未満となった 追加子プロセスが起動できる状態 必要な子プロセス数に対して、MaxRequestWorkers や ServerLimit に制限されて起動できる子プロセス数が少ない状態が 3回 (以上) 続いた場合 アイドルスレッド とは、 現在特定のタスクを実行していないが、すぐに新しいタスクを処理できる状態で待機しているスレッド のことです。 MinSpareThreads は アイドルスレッドの最小数 を定義します。 Apache では必ず MinSpareThreads 数 (以上) のアイドルスレッドを確保します。 どうやったら出力されないのか メッセージが出力されないように対策をするのであれば、以下のようにするとよいでしょう。 MaxRequestWorkersを「想定最大アクティブスレッド数」+「MinSpareThreads」+α (アクセスが集中した場合の余力) となるように設定 StartServers、ServerLimit、ThreadsPerChild、MaxSpareThreads については以下の関係式のように設定 ServerLimit x ThreadsPerChild = MaxRequestWorkers StartServers x ThreadsPerChild ≧ MaxSpareThreads 出力されても問題ないのか AH03490 は、エラーログとして出力されますが、プロセス起動の余裕が少なくなっている旨の INFO レベルのメッセージですので、このメッセージの出力自体は無視しても構わないと思います。 リクエストの待機状態が発生してパフォーマンスが悪い状態ではあるので、頻発するようであれば設定値を見直して再設定しましょう。 参考文献 StartServers、ServerLimit、ThreadsPerChild、MaxSpareThread等、各MPMの値のパラメータの説明は以下を参照してください。 Apacheドキュメント / Apache MPM Common Directives なお、このエラーメッセージについては、ApacheやRed Hat社の公式ドキュメントにも書かれています。(Red Hat社の有料ポータルログインIDが必要です) Red Hatドキュメント / AH03490: scoreboard is full, not at MaxRequestWorkers.Increase ServerLimit. ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OSSサポートエンジニアの現場から!Apacheエラー「AH03490」を徹底解説 first appeared on SIOS Tech. Lab .
環境変数 環境変数はシェル自身と、そのシェルから起動されるすべての新規シェルで有効となる変数です。 例えば、Linuxにおいて馴染みのある環境変数は「PATH」や「HOME」等が挙げられます。 この2つの変数は最初から自動的に環境変数として読み込まれているため値を利用することが出来ます。 シェル変数 シェル変数はそのシェル内のみ利用できる変数です。 シェル変数を環境変数にしたい場合は後述の「export」コマンドを利用します。 変数の使用方法 シェル変数を扱う方法を以下に記述します。 VAR1=1 # シェル変数を定義 echo $VAR1 # シェル変数を標準出力 またシェル変数はexportコマンドを利用することによってシェル変数を環境変数にすることが出来ます。 シェル変数を環境変数にする方法を以下に記述します。 export VAR1 # シェル変数を環境変数に変更 echo $VAR1 # 環境変数を標準出力 環境変数はシェル変数を介さずとも以下のように定義することが出来ます。 export VAR1=1 # 環境変数を定義 echo $VAR1 # 環境変数を標準出力 変数の使い分け シェルの中だけで使う変数ならシェル変数を使う: 短期間、シェルスクリプトや対話型シェル内でだけ使用する場合は、シェル変数で十分です。 他のプログラムや子プロセスで使いたい場合は環境変数を使う: プログラム全体で利用される設定値や、複数のプロセス間で共有したい値がある場合は、環境変数としてエクスポートする必要があります。 環境設定やシステム設定には環境変数を使う: システムの設定やパス設定など、複数のプログラムやプロセスで必要となる情報は、通常環境変数として設定します。 スクリプト内での使い分け: シェルスクリプトを書くときは、基本的にスクリプト内でのみ使う変数はシェル変数として使用し、外部プログラムや他のスクリプトからもアクセスしたい編素は環境変数にするのが一般的です。 まとめ 私的に使い分けを簡単に考えると 複数の箇所(別シェル)から参照されそう→環境変数 この空間(シェル内)でしか使わなそう→シェル変数 となります。 「環境変数はグローバル変数でシェル変数はローカル変数なんだ」くらいの認識で良いと思います。 ご覧いただきありがとうございました。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 環境変数・シェル変数 first appeared on SIOS Tech. Lab .