TECH PLAY

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

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

546

こんにちは。サイオステクノロジーの和田です。今回は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 .
アバター
こんにちは、サイオステクノロジーの中谷です。 2024年9月24~2024年9月25日に、アメリカ・ニューヨークで開催されたGrafana ObservabilityCON2024に参加しました。 このイベントはほぼ満員の大盛況で、会場内では終始活発な議論や質問が飛び交っていました。様々な企業が自社のユースケースを紹介し、なぜ導入に踏み切ったのか、そしてどのような成果を上げたのかを熱く語りました。このブログでは、いくつかの事例をご紹介したいと思います。 1. キーノート オープニングキーノートでは、Grafana Cloudがどのように簡単かつ迅速にObservabilityを体験できるかを強調しました。また、ログ、メトリクス、トレースの統合管理や、AI/ML機能を活用したシステム監視データの自動解析および問題の早期検出といった、Grafana LGTMスタックの最新機能も紹介されました。 BlackRockおよびNVIDIAの担当者がそれぞれ自社の事例を紹介し、大規模システムの監視にGrafana Cloudを導入することで、観測データを効果的に管理し、迅速な問題解決を可能にしたといった、Grafana Cloudならではの利点を共有しました。 所感:AI/ML機能の追加により、従来の監視システムの限界が大きく改善されるのではないかと感じました。BlackRockとNVIDIAの事例からは、Grafana Cloudの柔軟性、スケーラビリティ、そしてコスト削減効果が特に印象に残りました。これは多くの企業にとって有益な情報だと思います。新技術の導入には学習コストがかかり、トレーニングやシステムの最適化が必要という課題もありますが、監視の効率化が期待できると感じました。 2. SailPoint社のGrafana Cloud活用について Grafana Cloudの導入理由:観測データ(メトリクスなど)のボリューム増大に伴い、運用やコスト管理の課題が発生したため、コスト効率やスケーラビリティに優れたGrafana Cloudを導入し、コスト削減とシステムのパフォーマンス向上を実現しました。 成果:Grafana Cloudのスケーラビリティを活用することで、増加し続けるメトリクスデータに対応し、パフォーマンスの低下や過負荷を防ぐことができ、運用の一元化による安定性の向上に寄与しました。また、データの効率的な収集と保存が可能となり、自動化による人的コストや管理コストの削減も実現しました。 所感:スケーリングに伴うObservabilityの課題に対するアプローチとして、Grafana Cloudの柔軟性やコストパフォーマンスが非常に有効であると感じました。日本の企業も同様の課題に直面しているため、このユースケースをブログ化して発信していきたいと思います。一方で、日本ではいまだにオンプレミス版にこだわる企業が多いため、Grafana Cloudをどのように促進していくかが、今後の課題でもあります。 3. Planet社のGrafana Cloud導入 Grafana Cloudの導入経緯: Splunk、SignalFx、Stackdriverなどを使用して監視業務を行ってきましたが、分散したツールを使った管理は非効率でコストがかかり、メトリクス、トレース、ログの一元管理が難しかったです。複数のツール間の統合が不十分であり、効率性やパフォーマンスにも影響を与えていました。 成果:Mimir、Tempo、Lokiを採用し、Mimirによる大規模なメトリクス管理、Tempoによる分散トレース管理、Lokiによる効率的なログ管理を実現しました。その結果、パフォーマンス向上、スケーラビリティの確保、コスト削減ができました。 所感:オンプレミスとハイブリッドクラウドの両方を利用してログとメトリクスを管理している多くの企業にとって、システムが膨らむことで管理が難しくなり、コストがかかることは大きな課題です。Planetの取り組みは非常に参考になると感じました。特に、フルマネージドのGrafana Cloudへの移行によって得られるコスト削減と運用の簡素化は大きなメリットです。他の企業にとっても、このようなフルマネージドのソリューションは運用の最適化に役立つと思います。 4. パネルディスカッション 登壇者:Frank Guerrera (Pegasystems CTO)、Neil Wilson(LexisNexis Risk Solutions Director)、Wayne Jin(Grafana VP) Frank Guerrera:Pegasystems社がObservabilityを導入した目的は、システムの拡張時に安定性と拡張性を維持しつつ、運用効率を向上させコストを抑えることでした。また、Observabilityの導入は単なる技術的選択ではなく、チーム全体に浸透し、日々のオペレーションの中心となるよう意識改革が必要だと考えています。 Neil Wilson: LexisNexisでは、Observabilityは単なるシステム監視ではなく、全体的なビジネス戦略の一環として位置づけられています。複雑なシステムが可視化されることで、エンジニアは迅速に問題を特定し解決でき、チーム全体の生産性が向上します。また、Observabilityは組織文化やチームのモチベーションにも寄与し、意思決定が迅速かつ的確になることでチームの自信が高まると述べました。 Wayne Jin:Grafana Labsの製品マーケティングの視点から、Observabilityの価値をどのようにビジネス全体に広げるかについて語りました。Observabilityは、単なる技術的なツールセットではなく、経営層にとっても価値のあるビジネス資産であることを説明し、技術とビジネスの間をつなぐ役割が必要と強調しました。 所感:ディスカッションでは、Observabilityは単にシステム監視やパフォーマンス向上のためのツールではなく、企業の戦略的資産であることが強調されました。パネリストは、Observabilityが透明性を保ちながら、複雑な問題の解決に「洞察」を提供するものだと考えていました。ビジネスの成長や変化に応じて、Observabilityの重要性がますます高まっています。これは、技術基盤を強化するために欠かせない要素です。 今後、企業がObservabilityを導入・活用する際には、技術チームだけでなく経営層やビジネスサイドにもその価値を理解させる必要があると思います。 5. IGグループのGrafana Cloud導入 Grafana Cloudの導入経緯:IG Groupは独自の監視ツールとオープンソースツールを使用しており、それぞれが別々に機能していたため、Observability性が低く、データの集約や分析が困難で、運用効率も悪かったです。 成果:Grafana Cloud Metrics、Grafana Cloud Traces、OpenTelemetryを導入し、分散した監視ソリューションを一元化し、全体の監視とトラブルシューティングが容易になり、エンジニアリングチームはデータに基づいた意思決定ができ、運用効率が改善され、迅速に問題解決ができるようになりました。 所感:IG Groupの事例から、Observalilityを導入することでシステム全体の効率化が図れることがわかりました。複数のツールが混在する環境でも、統一されたアプローチを取ることで、チーム全体がデータを活用しやすくなり、組織の意識改善につながりました。これは、他の企業にとっても大いに参考になると思います。 6. Unity TechnologiesのGrafana Mimirの活用について 課題:リアルタイム3Dコンテンツのリーディングプラットフォームの開発では、急増するメトリクスデータに対するスケーラビリティが欠如していました。さらに、複数のメトリクスバックエンドを使用していたため、データの管理や統合が困難で、SaaSプロバイダーに依存することでコスト削減も課題となりました。 成果:メトリクスバックエンドをThanosからCortexを経てGrafana Mimirに移行し、膨大なメトリクスデータを柔軟に対応できました。LGTMスタックに移行することによって、数百万ドルのコスト削減を実現しました。 所感:Grafana Mimirへの移行によって、データ管理が効率になり、コスト削減に繋がり、急成長するビジネスが直面するデータ膨張の課題を克服することができました。同じような課題を抱える日本の企業に積極的に紹介していきたいと思います。 7. ASAPPのGrafana Cloud利用 導入経緯:顧客センターの効率を高め、顧客満足度や開発チームの生産性を向上させるために、Observalilityを強化する必要があったからです。 結果: Gitベースのプロセスを導入し、Kubernetes、Argo、Go、Pythonを活用して、ダッシュボードの作成とGrafana Cloudへのインポートを迅速化しました。また、新しいワークフローにより、ダッシュボード作成の時間が大幅に短縮され、開発者の作業効率が向上しました。 所感:ASAPPの事例は、最新の技術を活用して開発者の生産性を向上させる方法を示しています。Grafana Cloudの導入により、Observalilityが強化され、ダッシュボード作成の効率が向上したことは、他の企業にとっても大いに参考になります。 全体所感:2日間のイベントに参加し、最新の技術やユースケースについて多くの貴重な情報を得ることができて、本当に良かったです。特にObservabilityの分野では、データの可視化やリアルタイム分析がビジネスにどれほど役立つかを実感しました。さまざまな業界の知見が共有され、データがサイロ化されている問題を解消することが、全体の生産性向上につながるという話が印象に残りました。 さらに、AIや機械学習の活用が進むことで、データ解析や予測がよりスムーズになるのではないか感じました。これにより、企業はより迅速に意思決定を行い、競争力を維持することができると思います。今後は、技術導入によるビジネスバリューを示すことが、マーケティングにとって重要な課題であると感じました。 このイベントを通じて、お客様が直面している課題に対する新たな解決策やアプローチのヒントを得ることができ、改めて積極的に新しい技術を取り入れていく必要性と重要性を強く感じています。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Grafana ObservabilityCON2024~可観測性ソリューションの最前線 first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの佐藤陽です。 今回はRAGの構築における「セマンティックチャンキング」の手法についてお話をしたい思います。 セマンティックチャンキング実装してみたいな、と思っている方は是非最後までご覧ください! はじめに 今回のチャンキングおよびインデクシングの構成としては以下のような形です。 DocumentIntelligenceのLayoutモデルを活用し、PDFをMarkdown形式で出力 LangChainのMarkdownHeaderTextSplitterを利用し、 出力されたMarkdownのHeader情報(#, ##)でチャンク化 チャンク化した文章をベクトルストアに格納 この大きな流れに関しては以前の記事でも紹介したので、こちらを参照ください。 【徹底解説】Document Intelligenceを利用してRAGを構築する【セマンティックチャンキング】 今回実装を行っていたRAGに関しても、この流れでインデクシングを行おうと思い取り組みました。 「上記したような流れですんなり実装できるだろうな」と思い実装の方を進めていたのですが、 いざ実装をすると想定と異なる部分もあり、やや工夫が必要なポイントがあったので、その点を紹介していきたいと思います。 要件 今回扱うPDFデータとしては、以下のようなものです。 (出典: モデル就業規則について – 厚生労働省 )  ポイントとしては 「第〇章と第〇条という親子関係になっている」 という点です。 就業規則や、会則などにおいてよくあるような形式なのではないでしょうか? 例えば今回であれば以下のような形です。 第2章 採用、異動等 第4条 採用手続 第5条 採用時の提出書類 そして今回は、こういったドキュメントに対して、 章の部分で区切ったチャンク化をしたい という要望があるとします。 いわゆるセマンティックチャンキングと呼ばれるチャンク戦略です。 章 で区切ることにより、意味ある塊でのチャンキングが可能となり、Retrievalの精度の向上が見込めます。 ちなみに 章 ではなく、 条 のほうでチャンク化した方がチャンクサイズとしては小さくなるのですが、 その場合、 章 の内容が分断され、章全体に対する質問への回答品質が低下することが懸念されました。 そのため今回においては、チャンクサイズは大きくなりますが 章 のレベルでチャンク化するものとします。 なお、このあたりはアプリに対する要件によって実装が異なってくる部分であると思うので 要求に対して適切なチャンク戦略をとってみてください。 ちなみに今回の記事の話はここは重要なポイントではないので、どちらの戦略をとる場合でもお読みいただけます。 課題 今回、このPDFに対して最初にも示したような処理を行いました。 この時、DocumentIntelligenceにおけるMarkdown出力の値を見たところ 以下のような内容で出力が行われました。 # 第1章 総則 ## 第1条 目的 ## 第2条 適用範囲 ## 第3条 規則の尊守 ## 第2章 採用、異動等 //本当は'#'であってほしい ## 第4条 採用手続き ## 第5条 採用時の提出書類 ## 第6条 ...(略)... # 第3章 服務規律 注目ポイントとしては、 第2章 のヘッダーの大きさが ## で表されている点です。 これではMarkdownHeaderTextSplitterにおいて # の単位で分割すると、以下のようにチャンク化が行われてしまいます。 chunk[0]: 第1章~~~ chunk[1]: 第3章~~~ ## で判定されてしまうこと自体はDocument IntelligenceのLayoutモデルの性能に依存してくるので、対処しようがないかなとは思います。 PDFを見た限りでは第1章も第2章も同じようなフォーマットで描かれているのですが…。 もしどうしてもDocument Intelligence使うのであれば、カスタムモデルで精度上げる等の手段を取ることになると思います。 推しのDocument Intelligenceでしたが、こうなっては仕方ないので代替案を探しました。 解決策 LangChainに頑張ってもらうことにしました。 以下のような構成に変更しました。 今回はチャンク化したい区切りが 第〇章 といった形で、分かりやすく定義されています。 なのでTextSplitterのseparatorのパラメータにこの文字を与えてあげれば区切れそうです。 ただ、章の数値は変動するので、いちいち全部をseparatorへは登録できません。 「どうしたらいいかなぁ」と思って調べていたら、separatorの値には正規表現が使える事が分かりました。 RecursiveCharacterTextSplitter の引数として is_separator_regex というものが用意されており、 この値をTrueに設定することで separator の値が正規表現の文字列として扱われます。 実際書いてみると以下のような形です。 text_splitter = RecursiveCharacterTextSplitter(     separators=["第[0-9]{1,3}章 ","第[0-9]{1,3}条 "],     chunk_size=1024,     chunk_overlap=0,     length_function=len,     is_separator_regex=True, #正規表現を有効にする ) result = text_splitter.split_text(content) 今回Separatorの値として、2つの正規表現を定義しました。 この実装の処理としては chunk_sizeに収まるように第〇章のところで区切る 〇の値としては数値に限定し、3桁まで対応 仮に1章と2章の合算がchunk_size(1024)に収まるようであれば、1つのchunkの中に入れられる 1.の処理でチャンク化した際に、1つの章のチャンクサイズがchunk_sizeを超えるようであれば、再帰的に第〇条でchunkを行う といった形となっています。 その結果以下のようにチャンク化されました。 想定した通りのチャンキングが行われていることが確認できます。 懸念点 ここでいくつか懸念点を挙げておきます。 チャンクサイズの肥大化 まず1点目はチャンクサイズの肥大化です。 これはこの記事の最初にも述べていますし、想定内のことではあるのですが 章ごとに区切ることで意味がまとまったチャンクとして扱えるのですが、チャンクサイズが想像以上に大きくなることが懸念されます。 そのため今回は 章でチャンク化することをベースとする 許容できるchunk_sizeを決めておき、それを上回る場合はチャンクサイズに収まるよう条でも区切るようにする といった形で対応しましたしました。 ただその分、今回の目的である「 章 で区切りたい」という目的からは少し逸れてしまっており、その点が悩ましい所です。 このchunk_sizeの設定に関しては、適切にチューニングを行って行く必要がありそうです。 意図しないチャンキング タイトルではなく、文章中の 第〇条 等に反応してチャンキングしてしまう可能性があることです。 社内規約の中だと例えば「第30条に記載のある~」といった、他の項目を参照するような記載はよくあると思います。 今回は ["第[0-9]{1,3}章 ", "第[0-9]{1,3}条 "] といったように、正規表現の最後に半角スペースを入れることによって、文中で出てきたワードは外すような工夫をしました。 ただ、これも絶対ではないですし、場当たり的な対応は否めないです。 この点、Markdown出力であれば # として区切り文字が表れるので、見分けがつきやすいかと思います。(#の個数はおいておき。) なので、やるとすれば 今回の正規表現を使うケースにおいても、Document IntelligenceとしてはMarkdown出力をしておき ["# 第[0-9]{1,3}章", "# 第[0-9]{1,3}条"] といったようなSeparatorを用意することで、 # を活用したチャンク化が行えそうですね。 まとめ 今回はDocument IntelligenceのMarkdown出力を使ってセマンティックチャンキングを実装してみたのですが 意外とうまくいかなかったので、その時の代替案をご紹介しました。 あんまりスマートな実装ではないですが、なんとか意図したセマンティックチャンキングは実現できました。 今後のDocument IntelligenceのMarkdown出力の精度向上に期待ですね! ではまた! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【LangChain】正規表現を利用したセマンティックチャンキングの紹介 first appeared on SIOS Tech. Lab .
アバター
PS/SLの佐々木です。 私のSLでは今年度から新しい技術開発に関する取り組みを行っており、私は3か月間Hyperledger Fabricについて、技術やユースケースの調査を行ってきました。 昨年まではEthereumやPolygonというパブリックチェーンを扱っていたため、これらのチェーンとの違いについてもご紹介できればと思います。(Hyperledgerの技術的な細かい話はしません) Hyperledger Fabricとは Hyperledger Fabricは、Linux Foundationが主導するHyperledgerプロジェクトの一環として開発された、エンタープライズ向けのオープンソースブロックチェーンフレームワークです。 Hyperledger Fabricの最大の特徴は、ネットワークへの参加が許可制であるという点です(パーミッション型ブロックチェーン)。 「ネットワークに参加する」というのは、ブロックチェーンノードを立てたり、スマートコントラクトをデプロイすることを指します。 例えば、Ethereumであれば、Gethを使うことで誰でもEthereumノードを立て、Ethereumネットワークのトランザクションを閲覧したり、トランザクションを発行してEthereumのワールドステートを変更することができます。 一方、Hyperledger Fabricではネットワーク参加に許可が必要なため、誰でもネットワーク内のトランザクションを閲覧したり、データを見ることはできません。 さらにもう1つの特徴は、ブロックチェーン上にデプロイされているアプリケーション(Chaincodeと呼びます)がGo、Java、Node.jsといった複数のプログラミング言語をサポートしている点です。これは、エンジニアにとって学習コストが低く、非常に嬉しいポイントです。 Hyperledger Fabricのユースケース サプライチェーン管理 サプライチェーンにおける製品や原材料の追跡が容易になり、取引の透明性を確保するために使用されます。例えば、食品業界や製造業における物流プロセスで、各段階の情報をリアルタイムで追跡し、データを一元管理することが可能です。 金融業界 金融機関間での取引をスムーズかつセキュアに実行するため、Hyperledger Fabricが活用されています。従来のシステムでは確認作業や取引処理に時間がかかりますが、分散型台帳を活用することでリアルタイムかつ正確に記録が残り、信頼性の高い取引が可能になります。 ヘルスケア 医療記録の管理や患者データのセキュアな共有にも利用されます。Hyperledger Fabricのパーミッション型ネットワークは、医療データのプライバシーを確保しつつ、必要な医療機関や関係者が適切なアクセス権を持って情報を参照できる環境を提供します。 トレーサビリティ 製品の起源や移動履歴の追跡に利用されます。特に農業や飲料業界において、製品の品質管理や法的要件への準拠に役立っています。 このように、Hyperledger Fabricはエンタープライズ領域で広く利用されています。ブランド企業が自社製品の品質保証を強化し、付加価値をつけるためにトレーサビリティ管理を行うケースや、カーボンクレジットの算出をサポートする目的で活用されている事例もあります。 EthereumやPolygonとの違い ネットワークモデル Hyperledger Fabricはパーミッション型ブロックチェーンで、ネットワークに参加するノードは許可制です。一方、EthereumやPolygonはパブリックブロックチェーンであり、誰でも参加できるオープンなネットワークです。 コンセンサスメカニズム Fabricはさまざまなコンセンサスアルゴリズムをサポートし、ネットワークに応じた柔軟な合意形成が可能です。対して、EthereumやPolygonはProof of Stake(PoS)やProof of Work(PoW)といった公開型コンセンサスメカニズムに依存しています。 スマートコントラクト Fabricではスマートコントラクトを「チェーンコード」と呼び、Go、Java、Node.jsなど複数の言語をサポートしていますが、Ethereumでは基本的にSolidityを使用します。PolygonはEthereumのスマートコントラクト処理速度を向上させるLayer 2ソリューションとしての役割が強調されています。 トークンのサポート PolygonはEthereumと同様にネイティブトークンの発行が容易です。しかし、Hyperledger Fabricはトークン発行を基本機能としておらず、主に企業内取引やデータ管理に焦点を当てています。 EthereumやPolygonのようなパブリックチェーンでは、既存のネットワークのルールに従い、開発を行っていく形になりますが、Hyperledger Fabricは自分たちの要件に合わせてネットワークの設計が可能です。これは柔軟性を提供する一方で、設計にあたっては慎重な検討が必要になります。 Hyperledger Fabricのメリット Hyperledger Fabricを使う最大の理由は、ネットワーク参加者を限定できる点です。エンタープライズ環境では、許可なしにすべてのトランザクションデータが公開されることは受け入れがたい場合があります。また、有事の際にネットワークを制御できる可能性を残している点も大きなメリットです。 さらに、コンソーシアム内でもデータの公開範囲を制限したり、MSP(Membership Service Provider)を用いてユーザーの権限を詳細に管理することができ、エンタープライズソリューションに必要な機能が充実しています。 また、パブリックチェーンよりもトランザクション処理能力が高く、スケーラビリティが優れているため、より高い非機能要件を満たすことができます。 Hyperledger Fabricのデメリット 1つ目のデメリットは、ネットワーク構築の複雑さです。柔軟なアーキテクチャを持つ一方で、導入や構築には専門的な知識が必要です。また、管理者が存在しない前提で設計するため、設計初期の判断が非常に重要であり、不確実性が高くなります。 2つ目は費用です。コンソーシアムチェーンの場合、最低2ノード以上が必要であり、これにはサーバーコストや運用コストがかかります。(Amazon Managed Blockchainの試算では、商用利用の場合、最低20万円以上かかるケースが多いです) さらに、真の分散型を目指すパブリックチェーン界隈からは、2ノード程度のネットワークをブロックチェーンと呼べるのか、という意見もあるのが現状です。 Hyperledger Fabricの開発方法 Hyperledger Fabricを開発で使用する場合にはネットワークレイヤーの設計をするメンバーとアプリケーションレイヤーの設計をする人に分けるべきかと思います。 私はアプリケーションレイヤーの人間なので今回はアプリケーションレイヤーの開発方法を紹介します。 パブリックチェーンで開発を行う場合にはローカルにブロックチェーンネットワークを展開できるライブラリが用意されていた李、testnetと呼ばれる検証用のチェーンをEthereum Polygonそれぞれ用意していることが多いためそれらを使用します。 Hyperledger Fabricの場合には簡易的なネットワークを構築するシェルスクリプトが用意されているためネットワークはそちらを利用させていただきます。 Chaincodeや認証のための秘密鍵などはチーム開発の場面では自分たちのリポジトリに切り出したいことが多いと思いますので、私たちのチームでは以下のようなリポジトリを作成して開発を行いました。 サンプルは公開されているので興味のある方は触ってみてください。 https://tech-lab.sios.jp/archives/42794 https://github.com/atomic-kanta-sasaki/hyperledger-fabric-application コンソーシアムチェーンの難しさ コンソーシアムチェーンを運用している実例ではどこかの企業が複数ノード運営しているケースや開発ベンダーがネットワーク内のノードを面倒見ているケースも多いようで真に分散しているネットワークを目指すうえではまだまだ課題があります。 このような課題からブロックチェーンではないといった意見や、なぜブロックチェーンを使用しているのかという疑問が発生しています。(実際に私自身もこの疑問に対しては日々考えさせられています) 一方でネットワークに参加するためには自分でノードを運営する必要があり、ブロックチェーンはたまたITに明るくない人たちが参加してくるにはかなりの障壁があることも確かです。 また技術的な問題だけではなく、ブロックチェーン領域ではコミュニティも非常に大切です。 お互いネットワークを維持していくメンバーであるためどのようにネットワークに貢献していくのか、どのようなネットワークにしていきたいのかという点についても会話をして意識をすり合わせていく必要がある点が今までのシステムと大きく異なる点かと思います。 終わりに Hyperledger Fabricの調査を3か月ほど行ってきた結果見えてきたことをざっとまとめてみました。 一般的に紹介されているユースケースは確かに有効ではあるが、実現するにはかなりの障壁があるというのも見えてきました。 現在はキラーユースケースも多くないためとりあえずユースケースを増やすこと優先で様々な事例で使用していくことが大切なフェーズだと思います。 最後の方ネガティブな話題が多くなりましたが、それ以上に現在のWebでは難しい信頼性のあるデータを作って行けたり、コンソーシアム内でデータの共有が行われることにより新しいビジネスの創出やデータの利活用が進んでいく可能性を秘めた技術になっています。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 3か月Hyperledger Fabricを使用してみてわかったこと first appeared on SIOS Tech. Lab .
アバター
はじめに こんにちは。この記事では、pythonで日本語を読み方に沿ったローマ字に変換する方法を説明します。 さて、何でそんなことすることになったかと言いますと、先日OSCに参加し、AIを使ったロボットのデモに関連しています。AIを使ったロボットについては こちらの記事 に詳しく書いてあります。 このロボット(Qumcum)には喋る機能があり、AIによって喋る内容を決めています。Qumcumはローマ字しか受け付けない仕様となっていますが、AIは喋りたい文章を日本語(漢字かな交じり)で返します。しかも、喋るということは、文字ではなく音ですので読み方に準拠したローマ字に変換する必要があります。つまり、「日本語は難しい」は「nihongowamuzukasi-」の様に「は」は「wa」に、「しい」は「si-」にする必要があります。(半角ハイフンは長音符です。) ということで、Pythonのライブラリを使って漢字かな交じりの日本語を読み方のローマ字表記に変換します。 つまりこの記事は、AIのデモのAIじゃない部分の解説ってことです。(笑) 実装 漢字かな交じり⇒カタカナ(読み) 漢字かな交じりの日本語の文章から読みの音に変換するには fugashi というライブラリを使用します。fugashiは形態素解析ツールであるMeCabのラッパーで、辞書として UniDic が推奨されています。 pip install 'fugashi[unidic-lite]' fugashi.Tagger で日本語を形態素解析した fugashi.fugashi.UnidicNode 型のリストに出来るので、各要素で読み方( w.feature.pron )を取得して結合します。ちなみにそのままの文字列は w.surface で、読み方ではなく文字としてのカタカナは w.feature.kana で取得できます。 import fugashi fugger = fugashi.Tagger() text = "日本語は難しい" # fugashiで日本語の部分を読み(カタカナ)に変換 words = fugger(text) result = [] for w in words: result.append(w.feature.pron) kana = "".join(result) print(kana) 実際に使う時には、途中に読まない文字(句読点や?など)があると消えてしまうので、 re.sub() 関数を使って、正規表現で日本語の部分だけを抜き出して変換します。この関数は、re.sub(正規表現, 関数, 入力文字列)の形で使うと、入力文字列の内、正規表現に対応する部分全てに対して、関数が実行されその返り値に置換されます。 また、辞書にない単語(固有名詞など)では、 w.feature.pron が None になり、 join 出来ないので、読みではなくそのままの文字を返します。 import fugashi import re fugger = fugashi.Tagger() text = "日本語は難しい" # fugashiで日本語の部分を読み(カタカナ)に変換 def convert_to_yomi(match): # 辞書に読みが無い場合(固有名詞など)はそのままの文字を返す return "".join([w.feature.pron if w.feature.pron else w.surface for w in fugger(match.group())]) # 正規表現でテキスト中の日本語を見つけるて読みに変換する kana = re.sub(r'[ぁ-んァ-ン一-龥々〆ヵヶー]+', convert_to_yomi, text) print(kana) ニッポンゴワムズカシー 同形異音語は処理できないので、「ニホンゴ」ではなく「ニッポンゴ」になっていますが、それ以外は正しく読み方に変換できました。 カタカナ⇒ローマ字 カタカナをローマ字に変換するには、 pykakasi というライブラリを使用しました。pykakasiは、漢字・ひらがな・カタカナの日本語テキストをローマ字に変換するライブラリで、c言語のkakasiというライブラリのpythonバージョンです。 今回は、読みの音のローマ字にしたかったので、前段でfugashiを入れましたが、ただローマ字にしたい場合は、pykakasiだけで十分だと思います。 pip install pykakasi kks.convert(text) で文節ごとに分割してローマ字の辞書のリストが出来るので、その中から、 hepburn (ヘボン式ローマ字)を取得して結合しています。他にも、 orig (入力テキスト), hira (ひらがな), kana (カタカナ), kunrei (訓令式ローマ字), passport (パスポート仕様のローマ字)が取得できます。 import pykakasi kks = pykakasi.kakasi() kana = "ニッポンゴワムズカシー" words = kks.convert(kana) print("words",words, type(words)) result = [] for w in words: result.append(w['hepburn']) roman = "".join(result) print(roman) このライブラリだと長音府が母音に変換されてしまう(「シー」⇒「shii」など)のですが、そのままにしてほしかったので、前段と同じく、re.sub関数を使って、カタカナの部分だけ、pykakasiを適応しました。 import pykakasi import re kks = pykakasi.kakasi() text = "ニッポンゴワムズカシー" def convert_to_roman(match): result = kks.convert(match.group()) return "".join([r['hepburn'] for r in result]) # 正規表現でカタカナを見つけてローマ字に変換 roman = re.sub(r'[ァ-ン]+', convert_to_roman, text) print(roman) nippongowamuzukashiー ローマ字に変換できました 全体 qumcumに喋らせるにあたって、qumcumの仕様として他に調整が必要な個所がいくつか有ったので、それらを追加すると全体のコードは以下の様になります。AIがどんなテキストを送ってくるか予想が付かないので、いろんなパターンを考慮していたら長くなってしまいました。(まだ、漢字の固有名詞など対応で来てないですが諦めました) import fugashi import re import pykakasi import unicodedata fugger = fugashi.Tagger() kks = pykakasi.kakasi() # 日本語をローマ字に変換する関数 def japanese_to_roman(text): # 半角・全角を正規化 text = unicodedata.normalize('NFKC', text) # fugashiで日本語の部分を読み(カタカナ)に変換 def convert_to_yomi(match): # 辞書に読みが無い場合(固有名詞など)はそのままの文字を返す return "".join([w.feature.pron if w.feature.pron else w.surface for w in fugger(match.group())]) # 正規表現でテキスト中の日本語を見つけるて読みに変換する yomi = re.sub(r'[ぁ-んァ-ン一-龥々〆ヵヶー]+', convert_to_yomi, text) # アルファベットはアルファベットの読みに変換 alphabet = str.maketrans({ 'A': 'エー', 'B': 'ビー', 'C': 'シー', 'D': 'ディー', 'E': 'イー', 'F': 'エフ', 'G': 'ジー', 'H': 'エイチ', 'I': 'アイ', 'J': 'ジェイ', 'K': 'ケイ', 'L': 'エル', 'M': 'エム', 'N': 'エヌ', 'O': 'オー', 'P': 'ピー', 'Q': 'キュー', 'R': 'アール', 'S': 'エス', 'T': 'ティー', 'U': 'ユー', 'V': 'ヴィー', 'W': 'ダブリュー', 'X': 'エックス', 'Y': 'ワイ', 'Z': 'ゼット', }) yomi = yomi.upper().translate(alphabet) # pykakasiでカタカナをローマ字に変換 def convert_to_roman(match): result = kks.convert(match.group()) return "".join([r['hepburn'] for r in result]) # 正規表現でカタカナを見つけてローマ字に変換 roman = re.sub(r'[ァ-ン]+', convert_to_roman, yomi) # 不要な記号を削除 roman = re.sub(r'[^a-zA-Z0-9ー、。?]', '', roman) # 数字を <NUMK VAL=(数値)> の形式に変換 def replace_match(match): return f"<NUMK VAL={match.group()}>" # 正規表現で数字部分を見つけて変換 num = re.sub(r'\d+', replace_match, roman) # その他の文字を変換 translation_table = str.maketrans({ '、': ',', '。': '.', 'ー': '-', '?': '?', }) return num.translate(translation_table) 補足 こんなに長々と変換処理を書くなら、AIでやれば良いんじゃない?って思うかもしれません。 試してみました。コードは省略しますが、以下の画像の通り、間違えることが多いですし、毎回回答が変わって不安定ですし、実行速度も遅いです。(ライブラリを使った方法は、0.002秒ぐらいです。) プロンプトなどを改善すれば、実行速度以外は良くなるかもしれませんが、私にはできませんでした。複雑で厳格なルールに則った変換はAIはまだまだ苦手そうです。 まとめ 今回は、pythonで日本語のテキストを読み方に沿ったローマ字に変換する方法について書きました。割と今回使ったロボットの仕様のせいで複雑になってるコードが多くなってしまいました。同じロボットを使ってるって方にはそのまま参考になるかもしれませんが、それ以外の方は適宜必要な個所だけ参考にしていただければと思います。 関連記事 このコードを使ったデモについて(つまり、AIのデモのAIの部分の解説)は、以下にリンク張るので、良ければ読んでいってください。 2024-10-03 「Qumcum×生成AI」ロボットで試すAIエージェント:Python ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post pythonで日本語を読み方通りのローマ字に変換する first appeared on SIOS Tech. Lab .
アバター
はじめに ファイルの管理をしていると時々、「デスクトップから特定の実行ファイルにアクセスできるようにしたい」と思うことがあります。その際に使用するのは恐らくリンクになるでしょう。 リンクとはファイルやディレクトリに別名をつけ、同じファイルにアクセスできる仕組みです。 また、リンクには「ハードリンク」、「シンボリックリンク」の2種類があります。 以降、想定するOSをLinuxとしてそれぞれの特徴をまとめていきます。 ハードリンク Linuxでは、ファイルを保存すると「iノード」と呼ばれるファイル固有の番号が割り当てられます。 iノードにはファイルのメタ情報(アクセス権、サイズ等)が関連付けられることになります。 ハードリンクは直接ファイルの実体を参照するリンクになります。 そのため、ある一つのハードリンクからファイル実体を参照し変更を加えると、同一のファイル実体を参照するすべてのハードリンクに影響を与えることになります。 例として、まずiノード番号が「x」のファイル実体があり、そのファイルを参照するハードリンク「alink」、「blink」があるとします。 「alink」からファイル実体にアクセスして変更を加えて保存します。 「blink」からファイル実体にアクセスして読み取ります。 「alink」と「blink」は同じファイル実体(iノード番号x)を参照しているので、変更が反映された状態で読み取りを行う事になります。 ハードリンクが複数作成されている場合、そのすべてのハードリンクを削除するまでファイル実体は削除されません。 シンボリックリンク シンボリックリンクはファイル実体を参照するのではなく、ファイル実体を参照するリンク元へのポインタを保持することになります。 リンク元のファイルを削除した場合、シンボリックリンクにアクセスするとエラーになります。 まとめ 最後にハードリンクとシンボリックリンクの使い分けポイントをまとめて本記事を終わりにします。 ファイルシステムを跨ぐかどうか 複数のファイルシステム間でリンクを作成したい場合はシンボリックリンクを使います。 同じファイルシステム内であればハードリンクでもシンボリックリンクでも良いです。 ファイルが削除された場合 元のファイルが削除されてもリンクを有効にしたい場合はハードリンク。 元のファイルが削除されたらリンクも不要になる場合はシンボリックリンク。 ディレクトリへのリンク ディレクトリにリンクを作成する必要がある場合はシンボリックリンクを使用します(ハードリンクではディレクトリへのリンクは作成できません) ご覧いただきありがとうございました。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post ハードリンク・シンボリックリンク first appeared on SIOS Tech. Lab .
アバター
はじめに Web開発するにあたって仮想的な開発環境を構築することは今や当たり前のように行うようになりました。 仮想的な開発環境を構築する手法として用いられるのが仮想マシン or コンテナといった技術です。 これらの違いを理解しておくことはWebエンジニアとして生きていくうえで必要だと感じたため、本ブログの執筆を決めました。 仮想マシン 仮想マシンの例として、LinuxにはKVMと呼ばれる仮想化技術が組み込まれています。 ハイパーバイザーと呼ばれる仮想化管理機構の上で仮想マシンが起動して、その内部でゲストOS(仮想マシン内で動くOS)が組み込まれることになります。 コンテナ アプリケーションの動作に必要な環境が整っている箱のような概念です。 有名な例としてはDocker技術が挙げられます。 コンテナ技術の特徴としてはホストOSとカーネルを共有している点です。 仮想マシン VS コンテナ 仮想マシン技術の美点 仮想マシンではホストOSとはまったく別のOSを動作させることが出来ますが、コンテナ技術ではできません。 コンテナ技術の美点 独立した環境それぞれでゲストOSが動くわけではないので仮想マシン技術と比較してリソース消費が少なく、サービス起動までの時間も短く済みます。 コンテナ技術の注意点 コンテナによる仮想化では、プロセスの実行環境がコンテナ毎に閉じています。 そのため、ホストOSからすべてのコンテナ内のプロセスが見えますが、各コンテナ内から外のプロセスは見えなくなっています。 まとめ 以上の点を理解できていれば、最低限仮想化について知っていることになると考えます。 コンテナは「軽い、早い」、「OSに縛り」といったことを覚えておけば役に立つ知識として 記憶に残るのではないかと思います。 私自身も呪文のようにコンテナは「軽い、早い」、「OSに縛り」と定期的につぶやく事にします。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 仮想マシン VS コンテナ first appeared on SIOS Tech. Lab .
アバター