TECH PLAY

電通総研

電通総研 の技術ブログ

822

こんにちは。コーポレート本部 サイバーセキュリティ推進部の耿です。 ElastiCache for Valkey が 日本時間 2024/10/9 に利用できるようになりました 。Valkey とは、ライセンス変更が発生した Redis をフォークしたインメモリデータベースであり、今後も継続して OSS として使用できるよう開発を進めていくことが発表されています。 Redis の代替の選択肢となり得る ElastiCache for Valkey を早速 AWS CDK で作ってみました。 オンデマンド (Self-designed) クラスタを作成する Serverlessクラスタを作成する よもやま話 Valkey の Serverless は安い Node.js 用のクライアント オンデマンド (Self-designed) クラスタ を作成する ElastiCache for Valkey の公式アナウンスからも Serverless クラスタ がプッシュされているのだろうと想像できますが、Serverless ではないオンデマンド クラスタ も作ることができました。最新の ドキュメント では「Self-designed クラスタ 」と表現されているようです。 ElastiCache には L2 コンスト ラク トがないため、L1 で作成します。 import * as elasticache from "aws-cdk-lib/aws-elasticache" ; export class CacheConstruct extends Construct { constructor ( scope : Construct , id : string ) { super (scope, id); // ...(VPC, サブネット, SGの作成は省略) const description = "test-cache" ; new elasticache.CfnReplicationGroup( this , "ReplicationGroup" , { replicationGroupDescription : description, engine : "valkey" , // engine は valkey を指定 engineVersion : "7.2" , // engineVersion は 7.2 を指定すると作成できた cacheNodeType : "cache.t3.micro" , cacheSubnetGroupName : new elasticache.CfnSubnetGroup( this , "SubnetGroup" , { description , subnetIds : vpc.selectSubnets( { subnetGroupName : "private-subnet" } ).subnetIds, } ).ref, cacheParameterGroupName : new elasticache.CfnParameterGroup( this , "ParameterGroup" , { description , cacheParameterGroupFamily : "valkey7" , // パラメータグループは valkey7 を指定 } ).ref, numNodeGroups : 1 , replicasPerNodeGroup : 1 , securityGroupIds : [ mySecurityGroup.securityGroupId ] , atRestEncryptionEnabled : true , transitEncryptionEnabled : true , } ); } } マネジメントコンソールで確認すると、問題なく作成できていました。 Serverless クラスタ を作成する 続いてプッシュされている Serverless クラスタ も作成してみます。L1 の CfnServerlessCache コンスト ラク トを使用します。 ServerlessCache で特徴的なのは cacheUsageLimits パラメータです。Serverless クラスタ の場合、時間あたりの「データストレージ」と「コンピューティングリソース(ECPU)」によって課金額が変わります。 cacheUsageLimits を利用することにより、利用料が想定を超えないようリソース上限を設けることが可能です。Valkey のストレージの最小値は 100MB ですが、 cacheUsageLimits で設定できる「上限値」は現状 1GB でした。ただしこれはスケールした時の上限値なので、保管データ量が少なければ最小 100MB 分の料金しかかからないと思われます。 また、保管時と通信時の暗号化は指定する必要がなく、デフォルトで有効となっていました。 import * as elasticache from "aws-cdk-lib/aws-elasticache" ; export class CacheConstruct extends Construct { constructor ( scope : Construct , id : string ) { super (scope, id); // ...(VPC, サブネット, SGの作成は省略) const serverless = new elasticache.CfnServerlessCache( this , "ServerlessCache" , { serverlessCacheName : "serverless-valkey" , engine : "valkey" , // engine は valkey を指定 cacheUsageLimits : { dataStorage : { // データストレージの上限を指定 unit : "GB" , maximum : 1 , } , ecpuPerSecond : { // ECPUの上限を指定 maximum : 1000 , } , } , majorEngineVersion : "7" , subnetIds : vpc.selectSubnets( { subnetGroupName : "private-subnet" } ).subnetIds, securityGroupIds : [ mySecurityGroup.securityGroupId ] , } ); } } マネジメントコンソールで確認すると、こちらも問題なく作成できていました。 自動でマルチAZとなっているので高可用性が実現できています。 よもやま話 Valkey の Serverless は安い 公式アナウンス に customers can create a cache in under a minute and get started as low as $6/month. とある通り、Valkey を Serverless で使えば従来に比べてとても安く済む可能性があります。ElastiCache の 料金ページ に従って、東京リージョンにおける毎月のデータストレージの最小利用料を計算してみました。最小のデータストレージは 100 MB(0.1 GB)なので、 0.1 (GB) * 0.101 (ドル/ GB-hour) * 24 * 30 (hour/月) = 7.272 (ドル/月) となります。従来のオンデマンド クラスタ だと最小でも14.4 (ドル/月) かかるため、小規模なアプリケーションでも ElastiCache を利用しやすくなったと言えるでしょう。 (これにECPU料金が上乗せされますが、仮定を設けるのがちょっと難しいです。ただ小規模な利用であれば非常に安く済みます。) Node.js 用のクライアント アプリケーションから Valkey に接続するためのクライアントですが、Node.js の場合は ioredis からフォークした iovalkey があります。 参考: Getting started with Valkey using JavaScript ただし今のところ ioredis と大きな変更点はなさそうなので、基本的なオペレーションは ioredis をそのまま利用できました。(全てのオペレーションについては確認できていないため、利用の際には自己責任でお願いします。) iovalkey はまだメジャーバージョンが 1 に到達していないため、適切な時期に iovalkey に移行すると良いでしょう。 私たちは共に働いていただける仲間を募集しています! みなさまのご応募、お待ちしています! 株式会社 電通総研 新卒採用サイト 執筆: @kou.kinyo 、レビュー: @nagamatsu.yuji ( Shodo で執筆されました )
アバター
XI本部 プロダクト イノベーション センター アジャイル 開発グループの徳山です。 前回の記事「 FlutterFlow vs Adalo:ノーコードモバイルアプリ開発ツールの比較 」では人気のノーコード開発ツールのAdaloと比較することでFlutterFlowとの機能の違いやメリットを紹介しました。今回は、FlutterFlowの制約について焦点を当てることでFlutterFlowを利用した開発ではどのような注意点があるのか理解を深めていただきます。 はじめに 制約 各制約について 1. デザインの制約 タブバーのデザインが調整できない 多言語に対応していないウィジェットがある 2. ロジックの制約 制約例 標準でサポートしているバックエンドが限定的 エラー発生時の共通処理ができない リストの並び替えができない 開発環境に関する制約 制約例 ブラウザでは、テスト画面の表示に手間がかかる カスタムコードの利用には、FlutterやDartの知識を有する開発者が必要 複数人の開発では作業内容の管理に注意が必要 まとめ 参考資料 はじめに 本記事の目的は、FlutterFlowの制約を明らかにし、開発者が直面する可能性のある課題を理解するための情報を提供します。 FlutterFlowの基本的な知識をまだお持ちでない方は、まず前回の記事「 FlutterFlowとは?ノーコードでスマホアプリ開発を始める方法 」を読んでいただくことをお勧めします。 また、本記事は2024年10月現在の内容に基づいて作成しております。 制約 FlutterFlowには多くの機能やメリットがありますが、一方で以下のような制約もあるため、本格的な アプリ開発 を行う際には、それらに注意する必要があります。 デザインの制約 ロジックの制約 開発環境における制約 各制約について 1. デザインの制約 FlutterFlowは、 ドラッグ&ドロップ による直感的な GUI 操作で画面を作成できる点が大きな特徴です。 しかし、標準で用意されている ウィジェット のビジュアルデザイン調整には限界があり、カスタムコードでの調整が必要な場合があります。検証の中で実際に遭遇したデザイン制約例をいくつか示します。 タブバーのデザインが調整できない 上の図はとあるサンプルアプリのデザインとFlutterFlowで実際に作成した画面の比較画像です。タブバーの ウィジェット はFlutterFlowで用意されており簡単に実装できます。デザインではタブ(新着)の丸っぽい赤い下線がありこれを再現したかったのですが、FlutterFlowではカスタマイズができず再現ができませんでした。 多言語に対応していない ウィジェット がある FlutterFlowには動画プレイヤー用の ウィジェット も用意されています。しかし、再生スピードなどの設定項目が英語でしか表示されず日本語での表示に対応することができません。 2. ロジックの制約 複雑なロジックや独自の機能を実装したい場合、カスタムコードを利用しなければ解決できない(場合によっては利用しても解決できない)制約があります。例としては下記のようなものが挙げられます。 制約例 標準でサポートしているバックエンドが限定的 標準でサポートされているバックエンドサービス(BaaS)はFirebaseまたはSupabaseに限定されています。それ以外の認証サービスやデータベース、ストレージを利用することも可能ですが、「連携することが可能なのか」、「要件に必要な機能は利用できるのか」、「セキュリティ面に問題はないのか」といった項目を別途確認する必要があります。いずれのバックエンドサービスを利用する場合でも必要な機能がない場合はカスタムコードを書く必要があるので、却ってス クラッチ の開発より実装が大変になってしまうといったことも考えられます。 エラー発生時の共通処理ができない アプリ内でエラー(例外)が起きた場合にそれを一括で処理する共通の機能を実装することができません。例えば「〇〇のエラーが起きたらエラーページを表示する」ことを行いたい場合、そのエラーが起こる可能性がある箇所に個別で処理を実装する必要があります。こうした処理の共 通化 はカスタムコードを利用しても対応することはできません。 リストの並び替えができない FlutterFlowではリストの表示を行うために ListView という ウィジェット を利用できます。ListViewにはReorderbleという並び替えを可能にするためのプロパティが用意されているのですが、この標準プロパティだけでは並び替え機能を実装できません。実現するためには下記のようなカスタムコードを実装する必要があります。 List<MenuItemParamsStruct> reorderMenuItems( List<MenuItemParamsStruct> list, int oldIndex, int newIndex, ) { /// MODIFY CODE ONLY BELOW THIS LINE // If the item is being moved to a position further down the list // (i.e., to a higher index), decrement the newIndex by 1. // This adjustment is needed because removing an item from its original // position will shift the indices of all subsequent items. if (oldIndex < newIndex) { newIndex -= 1; } final newList = [...list]; // Remove the item from its original position in the list and store // it in the 'item' variable. final item = newList.removeAt(oldIndex); // Insert the removed item into its new position in the list. newList.insert(newIndex, item); // Return the modified list. return newList; /// MODIFY CODE ONLY ABOVE THIS LINE } このように、FlutterFlowで用意されている機能でもカスタムコードの利用が前提となっている場合があります。 開発環境に関する制約 上記以外の開発手法や体制面といった制約については以下のような例があります。 制約例 ブラウザでは、テスト画面の表示に手間がかかる テスト画面のビルドには2〜3分の時間がかかります。また、変更を反映するには都度リロードを行う必要があり、リロード後に画面が表示されるまで毎回10秒ほど時間がかかります。このように、ブラウザを利用した開発体験は快適とはいえません。 ちなみに、別途セットアップが必要になりますが、デスクトップアプリを利用したローカルでの開発機能( Local Run機能 )を利用することで変更を即座に反映した開発を行うことは可能です。 カスタムコードの利用には、Flutterや Dart の知識を有する開発者が必要 これまで見てきたようにFlutterFlowでモバイルアプリを開発する場合はカスタムコードを利用する場面が多くあります。特に複雑なデザインやロジックを実装する場合は避けられません。そのため、開発の経験が乏しいメンバーだけで凝ったアプリを開発したい場合はカスタムコードの実装難易度の高さから実現が難しい機能やデザインも出てくることを認識する必要があります。 複数人の開発では作業内容の管理に注意が必要 FlutterFlowではブランチ機能が用意されており、開発メンバーによって作業場所を分けながら安心して開発を進めることができます。2024年9月下旬に FlutterFlow5.0がリリース され、自分が新しくどこを開発したかが把握しやすくなりました。一方で、FlutterFlowは日々機能追加が行われているので、まれにブランチのマージ時にエラーが起きて失敗したりします。このため、万が一の デグレ ーション(作業の戻り)に備えてチーム開発では変更内容をきちんと頭に入れて置いたり、ソースを GitHub に連携するなどの予備的対策が必要です。 まとめ 今回は、FlutterFlowの制約についてみてきました。 前回の記事まではメリットを中心にお伝えしてきましたが、今回のようなデメリットや制約もFlutterFlowのモバイル アプリ開発 ではしっかりと理解しておく必要があります。 次回は、FlutterFlowと相性の良いBaaSであるFirebaseとの連携について解説します。 参考資料 FlutterFlow ドキュメント 執筆: @tokuyama.raido 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
アバター
皆さんこんにちは。システム推進の佐藤太一です。 このエントリでは、 Windows 上にインストールした複数の JVM を上手く切り替える方法について説明します。 はじめに JBangのインストール JBangを使ったJavaのインストール デフォルトのJavaを設定する JBangを使ってJavaを切り替える PowerShellの補助関数を使って切り替える PowerShellのプロファイルを作りこんで自動で切り替える Start-ThreadJob を使う理由 バージョンの評価ロジックについて グローバル変数を使った処理回数の低減 類似するツール まとめ はじめに Java でアプリケーション開発をしていると、気が付いたらたくさんの JVM をインストールしていませんか? 私はCorretto やAdoptOpenJDKなど様々な ディストリビューション の Java 8、 Java 17、 Java 21、 Java 23をそれぞれインストールしています。 eclipse や IntelliJ といった IDE 上でのビルドでは、 IDE がランタイムの選択をサポートしてくれます。 Gradle や Maven にはtoolchainの仕組みがありますので、一旦ビルドが始まってしまえば、適切なランタイムが選択されるでしょう。 しかし、 Windows ではそこに至るまでに動作する JVM を上手く管理する方法が少ないです。 このエントリでは、 JBang を使って複数のバージョンの Java をインストールして管理する方法を説明します。 最後は Mac や Linux では日常的に行われているバージョンの自動切り替えについても、 Windows の PowerShell を使って実現する方法をご紹介します。 今回の説明で使う PowerShell は7.4系です。事前に PowerShell とscoopをインストールしておいてください。 Windows への PowerShell のインストール scoop JBangのインストール まずは、JBangをインストールしましょう。非常に丁寧に書かれたインストールマニュアルがあるので、それほど迷うことはないはずです。 cf. Installation 今回はscoopを使ってインストールしますので以下のコマンドを実行します。 scoop bucket add jbangdev https://github.com/jbangdev/scoop-bucket scoop install jbang JBang用の バケット を追加した上で、その バケット からインストールしていますね。 JBangを使った Java のインストール JBangをインストールしたら、次は Java をインストールします。 JBangで簡易的にインストールできる ディストリビューション はAdoptOpenJDKのみです。 それ以外の ディストリビューション をインストールしたいのであれば、別途インストールした上でJBangの管理下に置く必要があります。今回は簡易的なインストールだけを説明します。 例えば、 Java 8 をインストールするなら以下のコマンドを実行します。 jbang jdk install 8 次は、 Java 17と21をインストールしてみましょう。 jbang jdk install 17 jbang jdk install 21 ディストリビューション が固定されているので、コマンドが非常に簡潔ですね。 インストール済みの Java を一覧にしてみましょう。以下のコマンドを実行します。 jbang jdk list 私の環境では以下のように出力されます。 Installed JDKs (<=default): 8 (1.8.0_412) 17 (17.0.12+7) 21 (21.0.1+12-29) < 21 の右端に < が付いているのは、これをデフォルトの Java に設定しているからです。 デフォルトの Java を設定する では、JBangを使ってデフォルトの Java を設定しましょう。 jbang jdk default 21 これで、デフォルトの Java が21になりました。ついでに 環境変数 も追加しておきましょう。 JBangによってインストールした Java のフルパスを確認するには、以下のコマンドを実行します。 jbang jdk java-env これを実行すると、以下のように出力されます。 $env:PATH="C:\Users\taichi\.m2\jdks\jdk-21.0.1\bin;$env:PATH" $env:JAVA_HOME="C:\Users\taichi\.m2\jdks\jdk-21.0.1" # Run this command to configure your environment: # jbang jdk java-env | iex ユーザのホーム ディレクト リ以下にファイルが作成されている様子を確認できますね。 JBangは、 jdk java-env コマンドの実行結果をiexつまり、 Invoke-Expression することで実行環境を切り替えていくツールというわけです。 何もしていない時に動作する Java を指定するために、 Windows の 環境変数 を設定するツールでPATH 環境変数 と JAVA _HOME 環境変数 を設定しておきましょう。 JBangを使って Java を切り替える 次は、 PowerShell 上で利用する Java を切り替えてみましょう。 まず、 PowerShell のターミナルを開いて現在の Java を確認します。 C:\Users\taichi> java -version openjdk version "21.0.1" 2023-10-17 OpenJDK Runtime Environment (build 21.0.1+12-29) OpenJDK 64-Bit Server VM (build 21.0.1+12-29, mixed mode, sharing) PATH 環境変数 に Java 21が設定されているので、このように表示されます。 これを、 Java 17に切り替えてみましょう。以下のコマンドを実行します。 jbang jdk java-env 17 | iex これで切り替わりましたので、確認してみましょう。 C:\Users\taichi> java -version openjdk version "17.0.12" 2024-07-16 OpenJDK Runtime Environment Temurin-17.0.12+7 (build 17.0.12+7) OpenJDK 64-Bit Server VM Temurin-17.0.12+7 (build 17.0.12+7, mixed mode, sharing) 切り替わっていますね。 Maven やGradleが参照する JAVA _HOME 環境変数 も確認してみましょう。 C:\Users\taichi> $env:JAVA_HOME C:\Users\taichi\.jbang\cache\jdks\17 正しく切り替わっています。 PowerShell の補助関数を使って切り替える JBangを使った切り替えはコマンドが少し長いので、覚えていられないという問題があります。 そこで、 PowerShell のProfileという機能を使って切り替え用のコマンドを実装してみましょう。 プロファイルの詳細については、以下のドキュメントを参照してください。 プロファイルについて 要は $PROFILE でパスを確認できる PowerShell スクリプト にコードを書いておくと PowerShell が起動されるたびに、それが自動的に動く仕組みです。 プロファイルに以下のような関数を定義します。 function global:switchJava([string]$version) { jbang jdk java-env $version | iex java -version } 以下のコマンドを実行してプロファイルをリロードします。 . $PROFILE 補助関数を実行して利用する Java のバージョンを変更してみましょう。新しいターミナルを起動して、以下のコマンドを実行します。 switchJava 17 以下のように出力されて Java のバージョンが切り替わります。 C:\Users\taichi> switchJava 17 openjdk version "17.0.12" 2024-07-16 OpenJDK Runtime Environment Temurin-17.0.12+7 (build 17.0.12+7) OpenJDK 64-Bit Server VM Temurin-17.0.12+7 (build 17.0.12+7, mixed mode, sharing) PowerShell のプロファイルを作りこんで自動で切り替える 補助関数でのバージョン切り替えは便利ですが、ターミナルを起動するたびに利用するべき Java のバージョンを確認して切り替えていくのは面倒です。 現在の ディレクト リ内にある設定ファイルを見て利用する Java を自動的に切り替えてほしいですよね。 Windows 以外の環境で動作する bash や zsh といったシェルでは、例えば .java-version のようなファイルの存在確認をしてその中身によってバージョンを切り替えるという事が一般的に行われています。 Windows ではあまり広く行われていませんが、 PowerShell を使えば実装可能であることを紹介します。 まずは、プロファイルに以下のコードをそのまま追加します。 ただし、既にプロファイルをカスタマイズしている人は global:prompt の編集だけ注意してください。 要は、prompt関数の好きな場所に autoJava を追加すれば動作します。 <# Copyright 2024 DENTSU SOKEN INC. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. #> function global:prompt { $origLastExitCode = $LASTEXITCODE autoJava $LASTEXITCODE = $origLastExitCode } function switchJava([string]$version) { $SB = { param([Parameter(Mandatory = $true)] $version) jbang jdk java-env $version if ($LASTEXITCODE -ne 0) { throw "jbang jdk java-env command failed with exit code $LASTEXITCODE" } } $output = Start-ThreadJob -ScriptBlock $SB -ArgumentList $version | Receive-Job -Wait $output -join "`n" | iex java -version } $global:lastKnownLocation = "" function autoJava { $p = $PWD.Path if ($p -ne $global:lastKnownLocation) { guessJava $global:lastKnownLocation = $p } } function guessJava { $versionFile = Join-Path -Path $PWD -ChildPath ".java-version" if (Test-Path $versionFile) { $content = Get-Content $versionFile $version = $content.Trim() if (findJava $version -eq $true) { $res = switchJava $version } else { Write-Host ".java-versionファイルでは $version が指定されていますが、そのバージョンのJavaはインストールされていません。" Write-Host "jbang jdk install $version を実行するとインストールできるかもしれません。" } } else { useDefaultJava } } function useDefaultJava { foreach ($tup in listJava) { if ($tup.IsDefault -eq $true) { switchJava $tup.Label return } } } function findJava([string]$version) { foreach ($tup in listJava) { if ($tup.Label -eq $version) { return $true } if ($tup.FullVersion -eq $version) { return $true } if ($tup.FullVersion.StartsWith($version)) { return $true } } return $false } function listJava { $SB = { jbang jdk list if ($LASTEXITCODE -ne 0) { throw "jbang jdk list command failed with exit code $LASTEXITCODE" } } $output = Start-ThreadJob -ScriptBlock $SB | Receive-Job -Wait return $output | Select-Object -Skip 1 | ForEach-Object { if ($_ -match '^\s*(\d+)\s+\(([\d\._\+\-]+)\)\s*(<)?\s*') { [PSCustomObject]@{ Label = $Matches[1] FullVersion = $Matches[2] IsDefault = $Matches[3] -eq "<" } } } } コードの細かい処理内容については説明しませんが、いくつかある奇妙な部分について説明します。 Start-ThreadJob を使う理由 jbangコマンドが PowerShell スクリプト だからです。 prompt関数から PowerShell スクリプト が推移的に呼び出されると、呼び出された スクリプト の終了時点で解釈が終了するのです。 私の場合は、 posh-git などを使ってプロンプトを装飾しています。 prompt関数の戻り値がないと、デフォルトのプロンプトになってしまうので困ります。 というわけで、jbangコマンドをジョブとして実行してその結果の標準出力だけを使っているのです。 バージョンの評価ロジックについて findJava関数に実装してあるバージョンの評価ロジックは非常に単純なものです。 JBangでインストールされているバイナリのメジャーバージョン番号との完全一致 JBangでインストールされているバイナリの詳細なバージョン番号との完全一致 JBangでインストールされているバイナリの詳細なバージョン番号との前方一致 . java -versionに記載されているバージョン番号は多くの場合に、メジャーバージョン番号のみです。 Java は非常に高度な互換性を保ちながら開発されているので、日常的な開発において細かいリビジョン番号やパッチ番号を気にする必要はないと考えています。 もし細かい処理が必要な方はこのコードを是非改善して、その内容をブログに書いて貰えるとありがたいですね。 グローバル変数 を使った処理回数の低減 prompt関数は、 PowerShell のプロンプトが表示されるたびに実行される関数なので非常に実行回数が多いものです。 なので、カレント ディレクト リが変更されるまでは Java のバージョンを確認する処理をしないようにしてあります。 そのために使っている変数が lastKnownLocation です。 類似するツール JEnv-for-Windows SDKMAN mise spack まとめ このエントリでは、JBangとそれを使った PowerShell スクリプト を紹介しました。 この スクリプト は少し応用すれば、nodeや Python といった他の言語でも利用できるものです。 このエントリによって、 Windows 環境で快適に開発できる技術者が少しでも増えることを願っています。 私たちは共に働いていただける仲間を募集しています! みなさまのご応募、お待ちしています! 株式会社 電通総研 新卒採用サイト 執筆: @sato.taichi 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
アバター
XI本部 プロダクト イノベーション センター アジャイル 開発グループの徳山です。 前回の記事「 FlutterFlowとは?ノーコードでスマホアプリ開発を始める方法 」ではFlutterFlowについての特徴や機能といった基本知識を紹介しました。今回は、FlutterFlowを同じく人気のあるノーコードツールであるAdaloと比較することで両者の特徴や違いを解説します。よりFlutterFlowへの理解を深める助けになれば幸いです。 はじめに Adaloの概略 FlutterFlowとAdaloの違いについて Adaloの特徴 シンプルなビジュアルエディター 内蔵データベース 各ツールに適した場面 FlutterFlowが適している場面 Adaloが適している場面 まとめ 参考資料 はじめに 本記事の目的は、FlutterFlowとAdaloの特徴や機能を比較し、FluterFlowにどのような強みや弱みがあるのかを理解するための情報を提供します。 FlutterFlowの基本的な知識はある上で、他のローコードツールと具体的にどのような違いがあるのかについて興味がある方を対象としています。 Adaloの概略 Adalo(アダロ) は、FlutterFlowと同じく ドラッグ&ドロップ のビジュアルエディターでモバイルアプリを開発できます。異なる点としてはコードによる機能追加が提供されていないためノーコードでの開発となります。また、最初からデータベースが提供されておりUIのデザインからデータベース設定まで一貫して行うことができる点も異なります。FlutterFlowより用意されている標準の ウィジェット 数やアクション数は少ないですが、その分シンプルなため非エンジニアの方でも手軽に アプリ開発 を始めることができます。 Adaloを利用した アプリ開発 者数の公開はありませんが、Adaloで作成されたアプリの数は100万以上、アプリのエンドユーザー数は200万人以上と多くの利用者に支持されていることが伺えます。 FlutterFlowとAdaloの違いについて 各サービスの代表的な違いについては下表となります。 項目 FlutterFlow Adalo サービス開始 2020 2018 価格(月払い) $30〜 $45〜 アプリの開発言語 Flutter React Native(カスタムコード不可) 対応プラットフォーム iOS 、 Android 、Web、デスクトップ( Windows 、 macOS 、 Linux ) iOS 、 Android 、Web(PWA) ビジュアルエディター Flutter製のモダンな ウィジェット を利用して柔軟なUIデザインが可能 スタイルの制限はあるが、シンプルで直感的なUIデザインが可能 ウィジェット / コンポーネント 豊富な ウィジェット とカスタム コンポーネント の作成が可能 基本的な コンポーネント がそろっているが種類は限定的 バックエンド 外部サービス(Firebase、Supabaseなど)と連携が必要 内蔵データベースを使用可能 独自のロジック カスタムコード( Dart )により挿入可能 カスタムコードの挿入は不可 学習コスト 中程度( Dart やBaaSの知識があると有利) 低い(プログラミング知識不要) コードのエクスポート 可能 不可 拡張性 高い(カスタムコードと外部サービス連携で拡張可能) 低い(プラットフォーム内の機能に制限される) Adaloの特徴 シンプルなビジュアルエディター FlutterFlowと比較すると ツールバー の表示項目(履歴管理や開発者向けのメニュー など)や、 コンポーネント のスタイルプロパティの表示位置が変わったシンプルなエディター画面となります。 FLutterFlowと異なり複数画面を俯瞰しながらデザインができますが、その分画面数が増えると管理が大変になります。 標準の ウィジェット 数は50種類に満たないですが、 マーケットプレイス で無料の ウィジェット が多く用意されているためインストールして利用できます(有料のものもあります)。 一方で、FlutterFlowでは柔軟なスタイル変更が可能でしたがAdaloでは「誰でも簡単に」といったアプリ設計となっているため高度なスタイルのカスタマイズには対応できません。例えば、要素間で〇〇pxの余白を取りたいといった具体的な指定ができないといったことやモバイルヘッダーの高さを細かく調整できないといった制約があります。 内蔵データベース Adalo自身が独自のデータベース機能を持ち、FutterFlowのように外部サービスの利用を前提とした作りになっていません。もちろんAdaloの API で サードパーティ サービスや独自サーバーに接続することは可能です。 各ツールに適した場面 これまでの違いから、それぞれに適した場面は下記の通りです。 FlutterFlowが適している場面 高度なデザインやロジック、独自の機能が求められるアプリ マルチプラットフォーム での対応が必要なアプリ 将来的にコードをエクスポート・拡張したい場合 Adaloが適している場面 シンプルなデザインや機能が限定されたアプリ 開発未経験のメンバーのみでアプリを開発する場合 内蔵データベースを利用して、簡単にデータ管理を行いたい場合 まとめ ここまでAdaloの特徴やFlutterFlowとの違いについてみてきました。 Adaloの他にも私たちのグループでは複数のノーコード・ローコードツールについて調査しましたが、カスタマイズ性や機能性といった点からモバイルアプリ向けではFlutteFlowが最も適していると判断しました。もちろんこれは私たちが業務アプリを扱うことが多いためなので、読者の方の中にはAdaloの方が適している場合もあるでしょう。 それぞれの特徴を捉えた上で、プロジェクトの目的やニーズに合わせて適切なツールを選択することが重要です。 次回はFlutterFlowの具体的な制約について触れ、FlutterFlowでモバイル アプリ開発 を行う上での注意点について解説いたします。 参考資料 FlutterFlow ドキュメント Adalo 公式サイト Adalo ドキュメント 私たちは共に働いていただける仲間を募集しています! みなさまのご応募、お待ちしています! 株式会社 電通総研 新卒採用サイト 執筆: @tokuyama.raido 、レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
はじめに 背景 Azure Data Factory利用時の注意点 Azure DatabricksとAzure Data Factoryの比較 Azure Databricksの利用方法 Azure Databricksの作成 クラスターの作成 PySparkの記述方法 storesコレクション ordersコレクション merged_storesコレクション 終わりに はじめに 電通 総研XI本部AIトランスフォーメーションセンターの岩本です。この記事では、Azure Cosmos DB for MongoDBのデータベース移行手段としてAzure DatabricksとAzure Data Factoryを取り上げ、それぞれの方法のメリット・デメリットを比較します。また、Azure Data Factoryを用いた具体的な実装例を紹介します。 背景 AIトランスフォーメーションセンターでは、Azure OpenAI Serviceを活用したAIチャット・RAGの製品であるKnow Narratorを開発しています。Know NarratorのバックエンドではスケーラブルなNoSQLデータベースであるAzure Cosmos DB for MongoDBを利用しています。理由としては以下の2点です。 Know Narratorのユーザー数は導入いただいているお客様によって様々であり、DBはスケーラブルである必要がある 頻繁なアップデートで後から機能を追加することを想定しているため、DB スキーマ は柔軟に変更可能である必要がある AIトランスフォーメーションセンターでは、今年3月にデータベース移行を伴うKnow Narratorの大型アップデートを顧客に提供しました。データベース移行手段として当初はAzure Data Factoryを検討していましたが、Data Factory上でのUUIDの取り扱いに制限があることが分かったため、最終的にはAzure Databricksを利用することに決定しました。 Azure Data Factory利用時の注意点 UUIDはMongoDB上では binary と type の2つのフィールドを持つオブジェクトとして表現されます。 type はUUIDの生成方法の違いを表しており、これが異なるとUUIDの一意性を維持できません。 CosmosDB for Mongo API に対しData Factoryを利用する際の注意として、元々DBに type=4 として保存している値が、Data Factory上にデータを読み出す際に type=3 として認識されてしまいます。Data Factory上で type を書き換えることはできますが、DBに書き出す際に再度 type=3 となってしまいます。 Microsoft サポートによると、これはData FactoryのCosmosDB for MongoDBコネクタの仕様であり、解消するには書き込み後にCosmosDB側で type を書き換えるコマンドを実行する必要があるとのことでした。 マイグレーション 作業手順が複雑になることを避けるため、今回はData Factoryを利用しませんでした。 Azure DatabricksとAzure Data Factoryの比較 以下のようにAzure DatabricksとAzure Data Factoryはそれぞれ異なる目的と機能を持っています。 Azure Databricks: 高度な分析、データサイエンス、 機械学習 のためのプラットフォーム。 Apache Sparkベースで、高速な ビッグデータ 処理が可能。 ノートブック環境を提供し、データ探索、視覚化、モデルのト レーニン グが容易。 Python 、 Scala 、 SQL 、Rなど多くの言語をサポート。 Azure Data Factory: データ統合とETL(Extract, Transform, Load)パイプラインの構築に特化。 データの移動、変換、 オーケストレーション を視覚的にデザイン可能。 さまざまなデータソース(オンプレミスや クラウド )と連携。 定義されたスケジュールやイベントに基づいたデータパイプラインの実行。 Databricksはデータ処理エンジンとして Apache Sparkを使用しており、Data Factoryに比べて非常に高速にデータ処理を行うことができます。また、料金体系に関してもDatabricksがランタイムの稼働時間のみに課金されるのに対し、Data Factoryはランタイムの稼働時間に加えてデータ量や計算量に応じた課金となるため、一度に大量のデータを処理する場合にはDatabricksが適していると言えます。 また、Databricksは Python や SQL で処理を記述できるため複雑なデータ操作に適しています。Data Factoryは GUI で直感的に処理フローを構成できますが、その分処理の自由度は落ちます。今回のDB マイグレーション では単純にデータを移行するだけでなく、あるコレクションのドキュメントを別のコレクションのドキュメントのフィールドの 入れ子 にするなどある程度複雑な操作が伴いました。このような操作はData Factoryでも実現不可能ではありませんが、複雑な操作は GUI ではかえって時間がかかってしまうためDatabricksを選択しました。 Azure Databricksの利用方法 Azure Databricksの作成 Azure Portal 上でリソースを作成します。 DatabricksからCosmosDBに接続するには、DatabricksのリソースをCosmosDBと同じVNetに配置する(VNetインジェクション)必要があります。 DatabricksのVNetインジェクションでは2つのサブネット(コンテナサブネットとホストサブネット)を利用するため、これらのサブネットを新たにCosmosDBが配置されているVNet上に作成する必要があります。サブネットの範囲はDatabricksで使用できる クラスタ ーノード数に影響するため、 /26 より小さいサブネットは推奨されません。 サブネット範囲と クラスタ ーノード数の具体的な関係については以下のページに記載されています。 learn.microsoft.com クラスタ ーの作成 ワークスペース を起動し、コンピューティング クラスタ ーを作成します。デフォルトの設定値でも特に問題はありませんが、ワーカータイプを安価なものに変更したり、開発環境であればスポット インスタンス を選択するなどしてコストを抑えることができます。 クラスタ ー作成後、ライブラリタブの[新規をインストール]をクリックし、以下の Maven 座標を使って Apache SparkとMongoDBのコネクタをインストールします。 org.mongodb.spark:mongo-spark-connector_2.12:3.0.1 ノートブックを作成し、[接続]をクリックして先ほど作成した クラスタ ーを選択します。 クラスタ ーの起動が完了していれば以下のように クラスタ ー名が表示され、 Python コードが実行可能な状態となります。 ![]( https://media.shodousercontents.com/task_images/983/14ccd520-da84-472e-b3b1-0985c4fea8ec.png PySparkの記述方法 今回、具体的なデータ操作の記述には Apache Sparkの Python API であるPySparkを使用します。Pandasのデータフレームと同じような要領でデータを操作することができ、Pandasを使ったことがある場合はそれほど学習コストをかけずに利用できます。 2つのコレクション (stores, orders) を結合し、ネストされたフィールドをもつコレクションmerged_storesを作成するというシナリオでサンプルコードを以下に示します。 storesコレクション _id store_id phone-number region 60c5ba5e4f1a2f6d2c9b7f92 {"type": 4, "data": "U3RvcmUx"} 123-456-7890 North 60c5ba5e4f1a2f6d2c9b7f93 {"type": 4, "data": "U3RvcmUy"} 987-654-3210 South ordersコレクション _id order_id price created_at store_id 70d5ba6e4f1a2f6d2c9b7f94 {"type": 4, "data": "T3JkZXIw"} 100 2021-06-13 12:00:00 {"type": 4, "data": "U3RvcmUx"} 70d5ba6e4f1a2f6d2c9b7f95 {"type": 4, "data": "T3JkZXIx"} 150 2021-06-14 13:00:00 {"type": 4, "data": "U3RvcmUx"} 70d5ba6e4f1a2f6d2c9b7f96 {"type": 4, "data": "T3JkZXIy"} 200 2021-06-15 14:00:00 {"type": 4, "data": "U3RvcmUy"} 70d5ba6e4f1a2f6d2c9b7f97 {"type": 4, "data": "T3JkZXIz"} 250 2021-06-16 15:00:00 {"type": 4, "data": "U3RvcmUy"} merged_storesコレクション _id store_id phone-number region orders 60c5ba5e4f1a2f6d2c9b7f92 {"type": 4, "data": "U3RvcmUx"} 123-456-7890 North { id: 70d5ba6e4f1a2f6d2c9b7f94, order_id: {"type": 4, "data": "T3JkZXIw"}, price: 100, created_at: 2021-06-13 12:00:00 }, { id: 70d5ba6e4f1a2f6d2c9b7f95, order_id: {"type": 4, "data": "T3JkZXIx"}, price: 150, created_at: 2021-06-14 13:00:00 } 60c5ba5e4f1a2f6d2c9b7f93 {"type": 4, "data": "U3RvcmUy"} 987-654-3210 South { id: 70d5ba6e4f1a2f6d2c9b7f96, order_id: {"type": 4, "data": "T3JkZXIy"}, price: 200, created_at: 2021-06-15 14:00:00 }, { id: 70d5ba6e4f1a2f6d2c9b7f97, order_id: {"type": 4, "data": "T3JkZXIz"}, price: 250, created_at: 2021-06-16 15:00:00 } # 必要なライブラリをインポート from pyspark.sql import SparkSession from pyspark.sql.functions import collect_list, struct from pyspark.sql.types import ( BinaryType, ByteType, IntegerType, StringType, StructField, StructType, TimestampType, ) # データベース接続に必要な情報を設定 src_connection_string = "<移行元DBの接続文字列>" dest_connection_string = "<移行先DBの接続文字列>" source_db = "<移行元DB名>" target_db = "<移行先DB名>" stores_collection = "stores" orders_collection = "orders" merged_stores_collection = "merged_stores" # SparkSessionの初期化 my_spark = SparkSession.builder.appName( "myApp" ).getOrCreate() # storesコレクションのスキーマを定義 stores_schema = StructType( [ StructField( "_id" , StructType([StructField( "oid" , StringType(), True )]), True ), StructField( "store_id" , StructType( [ StructField( "subType" , ByteType(), False ), StructField( "data" , BinaryType(), True ), ] ), True , ), StructField( "phone-number" , StringType(), True ), StructField( "region" , StringType(), True ), ] ) # storesコレクションからデータを読み込み df_stores = ( my_spark.read.schema(stores_schema) .format( "com.mongodb.spark.sql.DefaultSource" ) .option( "uri" , src_connection_string) .option( "database" , source_db) .option( "collection" , stores_collection) .load() ) # ordersコレクションのスキーマを定義 orders_schema = StructType( [ StructField( "_id" , StructType([StructField( "oid" , StringType(), True )]), True ), StructField( "order_id" , StructType( [ StructField( "subType" , ByteType(), False ), StructField( "data" , BinaryType(), True ), ] ), True , ), StructField( "price" , IntegerType(), True ), StructField( "created_at" , TimestampType(), True ), StructField( "store_id" , StructType( [ StructField( "subType" , ByteType(), False ), StructField( "data" , BinaryType(), True ), ] ), True , ), ] ) # ordersコレクションからデータを読み込み df_orders = ( my_spark.read.schema(orders_schema) .format( "com.mongodb.spark.sql.DefaultSource" ) .option( "uri" , src_connection_string) .option( "database" , source_db) .option( "collection" , orders_collection) .load() ) # 初期状態のstoresデータフレームの列名を保存 stores_new_columns = df_stores.columns # 結合時に列名の重複を避けるためstoresデータフレームの列名を変更 df_stores = df_stores.toDF( *[column_name + "_stores" for column_name in df_stores.columns] ) # storesデータフレームとordersデータフレームを結合 df_joined = df_stores.join( df_orders, df_stores.stores_id_stores == df_orders.stores_id, "left_outer" , ) # stores_idを除外したうえでordersをグループ化 orders_new_columns = df_orders.columns orders_new_columns.remove( "stores_id" ) df_merged_stores = df_joined.groupBy(df_stores.columns).agg( collect_list(struct(orders_new_columns)).alias( "orders" ) ) # 列名リストにordersを追加したうえで重複を避けるために変更した列名を元に戻す stores_new_columns.append( "orders" ) df_merged_stores = df_merged_stores.toDF(*stores_new_columns) # 結合されたデータを新しいコレクションに保存 df_merged_stores.write.format( "mongo" ).mode( "append" ).option( "uri" , dest_connection_string ).option( "maxBatchSize" , 1024 ).option( "database" , target_db).option( "collection" , merged_stores_collection ).save() 注意すべき点として、ネストされたフィールドをもつコレクションは読み込み時に スキーマ が自動で推論されません。 スキーマ を指定しないと、ネストされたフィールドの値は構造化されたデータではなくただのテキストとして扱われてしまいます。これを避けるため、サンプルコードではDBからのデータ読み込み時に スキーマ を指定するようにしています。 終わりに この記事ではAzure Cosmos DB for MongoDBのデータベース移行に関してAzure DatabricksとAzure Data Factoryを比較し、Azure Databricksを使った実装例を紹介しました。最後までご覧いただきありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! AI系プロジェクトマネージャー/リーダー(◎AIコンサル ◎AIプロジェクトマネージャー) AIサービス開発エンジニア 執筆: @iwamoto.yoshik85ca341e9dca4b22 、レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
XI本部 プロダクト イノベーション センター アジャイル 開発グループの徳山です。 私たちのグループは現在ローコードツール、その中でも マルチプラットフォーム 向けにアプリケーションを開発できるFlutterFlowの活用を行っており、業務アプリのMVP開発などに活用しています。 昨年「 ノーコードツール「FlutterFlow」を利用すると5時間でどんなアプリを作ることができるのか 」というタイトルで記事を書き多くの方に閲覧していただきました。 ノーコードやローコードに対する関心も高まってきているため、改めて「FlutterFlowのことを知りたい」、「FlutterFlowで開発を始めたい」という方向けにこれから数回に分けてFlutterFlowの情報を様々な側面から発信できればと思います。 はじめに FlutterFlowの概略 FlutterFlowの特徴 わかりやすいビジュアルエディター 豊富なウィジェット バックエンドサービスとの統合 カスタムコードによる自前の実装の組み込み 対応プラットフォームが豊富 スマホアプリ開発を始める手順 メリットとデメリット メリット 迅速な開発 マルチプラットフォーム対応 柔軟な拡張性とバックエンドとの統合 デメリット 学習のハードル プラットフォーム依存性 まとめ 参考資料 はじめに 本記事の目的は、FlutterFlowの機能や特徴を紹介し、 スマホ アプリ開発 を始めるための前提知識を提供することです。 対象読者としては、 ビジネスパーソン や開発初心者で、ノーコードやローコードでの アプリ開発 に興味がある方を想定しています。 FlutterFlowの概略 FlutterFlowとは、 Google 製のUI フレームワーク であるFlutterをベースにしたローコードの アプリ開発 プラットフォームとなります。 ドラッグ&ドロップ でUIをデザインすることができ、ほとんどコードを書かずにアプリを構築可能です。FlutterFlowを用いればア イデア 次第で誰でもアプリクリエイターになれます。 2024年9月に実施された FlutterFlow Developer Conference 2024 によればユーザー数はすでに160万人を超えており、人気のサービスとなっています。昨年実施された同イベントでは75万人との公表だったため1年間で85万人も利用者が増えたことになります。 なお、FlutterFlowは必要に応じてカスタムコードを書くことで柔軟に機能を追加できるためローコードに分類されます。 FlutterFlowの特徴 FlutterFlowは多機能ですが、主な機能としては下記のようなものが挙げられます。 わかりやすいビジュアルエディター (参考: FlutterFlow App Builder ) FlutterFlowでのアプリの作成は上図のような画面で行うことができます。最初は覚えることが大変ですが、各機能が非常に考えられて配置されておりすぐに操作できるようになります。画面の構築やロジック(イベント処理)の構築はもちろん、レスポンシブやダークモードでの表示、ブランチやバージョンの切り替え、エラーや警告の有無、他の機能へのナビゲーションが全てこの一画面で完結します。 豊富な ウィジェット アプリ開発 では欠かせない80種類近い部品が用意されており、パズルのように組み合わせることで簡単にUIが作成できます。Flutterをベースとしているため、各 ウィジェット の可能な配置もFlutterのルールに合わせたものとなります。例えばContainerと呼ばれる ウィジェット の中に複数の別 ウィジェット を配置することはできません。 上図のように配置できない場合はFlutterFlowが警告を表示し、正常な配置について教えてくれるのでFlutterについてわからない方でも安心して画面を作成できます。 自分でカスタムして コンポーネント を作成することもできるため、何度も同じ コンポーネント を作成するといった手間もかかりません。 バックエンドサービスとの統合 FlutterFlowはデータベースや認証、ストレージといった自前のバックエンド機能を持ち合わせていません。そのため、外部のサービスと連携する必要がありますがFirebaseやSupabaseといったBaaS(Backend as as Service)と簡単に連携できる機能があります。 FlutterFlowの開発者が Google 出身者ということもあるためか、特にFirebaseとの連携はシームレスに行うことができます。Firebaseとの連携については別の記事で別途取り扱います。 その他の サードパーティ サービスや独自サーバーへの接続についても API Connetorという機能が用意されており、 REST API を通じて接続ができます。 カスタムコードによる自前の実装の組み込み 必要に応じてコードを書くことで独自の機能を追加できます。FlutterFlowでは多くの ウィジェット や スマートフォン も考慮されたロジックが用意されていますが、開発でよく利用されるメソッドが用意されていないことがあります。 例えば簡単な例だと、取得した配列を特定の文字でsplitしたいというような機能は用意されていません。 この機能を実装したい場合は下記のようにCustom Code中のCustom Functionsという関数定義ができる場所で関数を作成することで実現できます。 開発の経験がない方だと実装ハードルは高いかと思いますが、開発経験がある方はCode CopilotというAIのコードアシスタント機能が用意されているのでそちらを利用することで実装を進めることができます。 対応プラットフォームが豊富 Flutterと同じく「 iOS , Android , Web, Windows , macOS , Linux 」といったプラットフォームのアプリケーションを作成できます。 Flutterをベースとしているため当然全てのプラットフォームに完璧に対応できているわけではありませんが、1つのコードベースで多くのプラットフォームに向けたアプリを作成できる点は他のノーコードやローコードサービスにない特徴の1つです。 スマホ アプリ開発 を始める手順 それでは、実際にFlutterFlowを使って スマホ アプリ開発 を始める手順を簡潔にご紹介します。 細かい設定は省略しているので大まかな流れと捉えてください。 FlutterFlowにユーザー登録する(無料プランあり) FlutterFlowの公式サイト にアクセスし、メールアドレスや Google アカウントで登録します。 プロジェクトを作成する ダッシュ ボードから「Create New Project」をクリックし、テンプレートを選ぶかブランクプロジェクトを作成します。 アプリのUIを作成する ビジュアルエディターで ウィジェット を ドラッグ&ドロップ し、画面レイアウトを作成します。 バックエンド接続の設定を行う FirebaseやSupabaseなどのBaaSや サードパーティ サービスとの接続設定を行います。 ロジックとデータ操作の実装 アクションやデータ バインディング を設定して、ユーザーの操作に応じた動作を実装します。 独自の機能やロジックが必要な場合は、Custom FunctionsやCustom Widgetsでコードを追加します。 アプリをテストする プレビューやテスト機能を使ってアプリの見た目やロジック、動作が正常かを確認して必要に応じて修正します。 アプリをリリースする 各プラットフォーム( iOS 、 Android 、Web)に応じて、 App Store や Google Play 、ウェブサーバーなどにアプリを公開します。 メリットとデメリット ここまでFlutterFlowの概要や機能、 スマホ アプリ開発 の流れを紹介してきましたが改めてメリットとデメリットをまとめてみたいと思います。 メリット 迅速な開発 豊富な ウィジェット や柔軟なロジックを利用することで短期間で開発できます。特にプロトタイプやMVP開発といった大規模でなく要件も複雑化していないフェーズにおいてはFlutterFlowは採用しやすいと考えています。 マルチプラットフォーム 対応 Flutterをベースとしているため、モダンで整ったUIを実現できます。1つのコードベースで複数のプラットフォーム向けにアプリを作成できるため開発の効率も良くなります。 柔軟な拡張性とバックエンドとの統合 カスタムコードを挿入することで、独自の機能やロジックを実装できます。AIの力を借りながら実装できるためFlutterをあまり触ったことのない開発者の方だと実装ハードルは低くなります。また、FirebaseやSupabaseなどのバックエンドサービスと簡単に連携でき、 サードパーティ 製のサービスとの連携も可能です。アプリケーションにおいて必須なデータベース操作やユーザー認証が手軽に行えます。 デメリット 学習のハードル FlutterFlow独自の操作方法やFlutterのレイアウトルール、連携するBaaSサービスの特徴、その他多くの機能など本格的な開発を行うためのキャッチアップには時間を要します。また、FlutterFlowはコミュニティは活発でドキュメントもしっかりと用意されていますが基本的には英語です。そのため、開発経験者の方でも数日〜1週間程度はキャッチアップ期間として見ておくと良いかと思います。 プラットフォーム依存性 FlutterFlowはバックエンドサービスを持たないため サードパーティ 製のサービスとの連携が基本となります。標準でサポートしているBaaSはFirebaseまたはSupabaseに限られます。そのため、FlutterFlowがサポートしていないサービスを使う場合は公式サポートやコミュニティに接続方法を相談する必要も出てきます。 まとめ ここまで読んでくださりありがとうございます。まとめると、FlutterFlowとは下記の特徴を有したツールと言えます。 ローコードで迅速な アプリ開発 を可能にする強力なツール ただし、使いこなすには一定の習熟が必要 制約や依存性もあるが、BaaSやカスタムコードで拡張性や柔軟性がある 次回は、FlutterFlowについてより深く理解できるように同じく人気のあるローコードツールである「Adalo」との比較を行う記事も書きますので、合わせて読んでいただけると嬉しいです。 参考資料 FlutterFlow ドキュメント FlutterFlow YouTube チャンネル コミュニティフォーラム FlutterFlow Developer Conference 2024 私たちは共に働いていただける仲間を募集しています! みなさまのご応募、お待ちしています! 株式会社 電通総研 新卒採用サイト 執筆: @tokuyama.raido 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
アバター
こんにちは、 電通 総研の瀧川亮弘です。 本記事ではSupabaseとDrizzle ORMを利用する場合のDB スキーマ の管理方法について記載します。 テーブル定義は、Drizzleのお作法に則り、TypeScriptで管理しています。 schema.ts import { boolean, pgEnum, pgTable, primaryKey, timestamp, uuid, } from "drizzle-orm/pg-core"; export const roleEnum = pgEnum("Role", ["admin", "general"]); export const users = pgTable("users", { authUserId: uuid("auth_user_id").primaryKey(), createdAt: timestamp("created_at").notNull().defaultNow(), updatedAt: timestamp("updated_at").notNull().defaultNow(), role: roleEnum("role").notNull(), isDeleted: boolean("is_deleted").default(false), }); // 省略 テーブル定義に変更があった場合、コマンドひとつ npm run gen-ddl で差分 DDL をSupabaseの マイグレーション フォルダ supabase/migrations に出力できるようにしています。 差分 DDL を出力後、Supabase CLI のコマンドを実行することで、対象のSupabaseプロジェクトに差分 DDL を実行できます。 このようにDrizzleとSupabaseの マイグレーション 機能がスムーズに連携され便利です。 package. json { " scripts ": { " gen-ddl ": " drizzle-kit generate:pg ", " gen-ddl-custom ": " drizzle-kit generate:pg --custom " } , " devDependencies ": { " drizzle-kit ": " ^0.20.14 " } } drizzle.config.ts import type { Config } from "drizzle-kit"; export default { schema: "./supabase/functions/_shared/schema.ts", out: "./supabase/migrations", } satisfies Config; ただし、RLSやチェック制約などの定義はDrizzleがサポートしていないためschema.tsで表現できません。 つまり、差分 SQL を自動生成できないため、 npm run gen-ddl-custom により空の マイグレーション ファイルを作成し、手動で SQL を書いて対応しています。 生成されるファイルには適当な名前が自動付与されるため、手動で適切な名前に変更しています。 Drizzleが変更差分を管理する メタデータ meta/_journal.json にもファイル名が含まれるため、合わせて修正が必要です。 終わりに Supabaseはとても好きなサービスなので今後も使っていこうと思います。 では良いSupabase生活を! 参考 https://github.com/thorwebdev/edgy-drizzle 執筆: @takigawa.akihiro ( Shodo で執筆されました )
アバター
こんにちは、 電通 総研の瀧川亮弘です。 現在、Supabaseによる アプリ開発 を行っています。 本記事ではSupabaseの認可制御をどのような方針で実装しているのか紹介します。 前提 アプリからSupabaseへのリク エス トは2つの API を使い分けています。 一つ目にSupabaseが スキーマ 情報をもとに自動生成するRESTful API です。 内部的にはPostgRESTというライブラリが用いられています。 こちらは単純な CRUD 操作しかできません。 二つ目にEdge Functionsです。 こちらはTypeScriptベースのサーバーレス関数です。 任意の処理をかけるため複雑な要件に対応できます。 また、Supabaseは postgreSQL の機能である、RLS(Row Level Security)による認可制御を推奨しています。 https://supabase.com/docs/guides/database/postgres/row-level-security 実装方針 認可制御の実装方針として2つの作戦を考えました。 一律RLS大作戦 PostgREST: RLSを有効化(全ての CRUD 処理についてロールベース制御を行う) Edge Functions: RLSを有効化(PostgRESTと同様) ※Edge FunctionsのDrizzle ORMからDBへのアクセスにおいて、RLSを有効化する方法は以下のissueが参考になりました。 https://github.com/drizzle-team/drizzle-orm/issues/594#issuecomment-2016607868 参照系のみRLS大作戦 PostgREST: RLSの有効化(R(参照)のみロールベース制御を行い、CUD(副作用を伴う処理)は全て拒否する) Edge Functions: RLSを無効化し、Edge Functionsにてロールベース制御を実装する 結論、「参照系のみRLS大作戦」を採用しました。 つまり、参照機能とそれ以外の副作業を伴う機能とで実装方法を分けました。 前者はRLSで認可を行い、後者はEdge Functionsで認可を行います。 理由は、PostgRESTからの操作は拒否したいが、Edge Functions からの操作は許可したいというケースが存在するからです。 たとえば、Aテーブル、Bテーブルの2つのテーブルに同じ トランザクション 内で更新をかけたい場合です。 「一律RLS大作戦」で認可制御を行う場合、各テーブルへの更新権限を許可することとなります。 しかし、そのするとPostgRESTを通して個々のテーブルへの更新処理が可能となるため、Aテーブルのみを単独で更新することができてしまいます。 そのため、PostgRESTからの操作はRLSでブロックし、Edge Functionsからの操作についてはRLSをバイパスすることとしました。 PostgREST側は厳しめに安全な方に寄せて認可制御を行い、その分、Edge Functions側では要件に応じた柔軟な認可制御を行います。 実装 ロールごとの各テーブルへの CRUD 権限を定義します。 GRANT文で参照権限のみ許可した上で、RLSでさらに詳細な権限設定を行っています。 /* SELECT権限のみ付与する */ REVOKE ALL ON ALL TABLES IN SCHEMA public FROM anon,authenticated; GRANT SELECT ON ALL TABLES IN SCHEMA public TO anon,authenticated; /* 将来的にテーブル追加された場合も同様とする */ ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE ALL ON TABLES FROM anon,authenticated; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO anon,authenticated; DROP POLICY IF EXISTS " read access " ON public .booking_info; DROP POLICY IF EXISTS " read access for anon " ON public .booking_info; CREATE POLICY " read access " ON public .booking_info FOR SELECT TO authenticated USING ( CASE WHEN (( select auth.jwt()) ->> ' user_role ' ) = ' vendor ' THEN true WHEN (( select auth.jwt()) ->> ' user_role ' ) = ' client ' THEN client_user_id = ( select auth. uid ()) ELSE false END ); CREATE POLICY " read access for anon " ON public .booking_info FOR SELECT TO anon USING (is_public); ALTER TABLE public .booking_info ENABLE ROW LEVEL SECURITY; また、Edge Functionsでロールに基づく認可制御が行えるよう、ログイン時のフックポイントでアクセス トーク ン(JWT)にロール情報を格納します。 API 呼び出し時はアクセス トーク ンのロール情報を参照し、権限判定を行います。 以下が参考になりました。 https://supabase.com/docs/guides/database/postgres/custom-claims-and-role-based-access-control-rbac 終わりに Supabaseに限らず、FirebaseなどのBaaSにおいても、DB スキーマ から API を自動生成してくれます。 便利な一方、認可制御の考え方が通常のス クラッチ 開発と異なる点は要注意だなと感じました。 Supabaseはとても好きなサービスなので、今後も楽しいSupabase生活を送っていこうと思います! 執筆: @takigawa.akihiro ( Shodo で執筆されました )
アバター
こんにちは。コーポレート本部 サイバーセキュリティ推進部の耿です。 2024/6に Amazon Inspector が GitHub Actions でのコンテナイメージスキャンをサポートしたとの アナウンス がありました。コンテナイメージの 脆弱性 スキャンに既にTrivyを利用している方も多いと思いますが、別の選択肢として Inspector によるスキャンを試してみました。 また、実はコンテナイメージのスキャンだけではなく、言語パッケージのバージョンファイルやDockerfileを静的解析することも可能のため、それもやってみました。 仕組み アクションを紐解く リポジトリ内のファイルをスキャンする場合 試してみた サマリページの結果 CSV形式の検出結果 JSON形式の検出結果 Markdown形式の検出結果 脆弱性が検出されなかった場合 コンテナイメージをスキャンする場合 サマリページの結果 リポジトリ内のファイルスキャンと、コンテナイメージスキャンの使い分けについて まとめ 仕組み 公式ドキュメント: https://docs.aws.amazon.com/inspector/latest/user/scanning-cicd.html アクション「 aws-actions/vulnerability-scan-github-action-for-amazon-inspector 」を利用します。仕組みは以下です。 Amazon Inspector SBOM Generator を使い、CycloneDX 形式の SBOM を生成する Amazon Inspector SBOM Generator は Linux で動くツール 生成した SBOM を Amazon Inspector に送信し、 脆弱性 を解析する そのため AWS アカウントおよび inspector-scan:ScanSbom 権限が必要 気になるのは何に基づいて SBOM を生成するかですが、4つの選択肢があります。 リポジトリ 内のファイル コンテナイメージ コンパイル 後のGoやRustのバイナリ .zip .tar .tar.gz の アーカイブ GitHub Actionsの場合、アクションの入力パラメータで指定します。この記事では Node.js アプリを例に、「 リポジトリ 内のファイル」と「コンテナイメージ」の2つを試してみます。 アクションを紐解く 書き方のサンプルです。 - name : Inspector Scan id : inspector uses : aws-actions/vulnerability-scan-github-action-for-amazon-inspector@v1.1.3 with : artifact_type : "repository" artifact_path : "./" display_vulnerability_findings : "enabled" critical_threshold : 1 high_threshold : 1 medium_threshold : 1 low_threshold : 1 other_threshold : 1 scanners : "javascript-nodemodules" よく使いそうなパラメータには以下があります。 artifact_type リポジトリ 内のファイルをスキャンする場合は repository コンテナをスキャンする場合は container artifact_path リポジトリ 内のファイルをスキャンする場合は、基本的に リポジトリ ルート コンテナをスキャンする場合はコンテナイメージ名 display_vulnerability_findings GitHub Actions の実行サマリページに結果を表示するか。表示する場合は enabled 便利なので基本的に enabled がおすすめ sbomgen_version 使用する Amazon Inspector SBOM Generator のバージョンを明示的に指定したい場合に使う critical_threshold high_threshold medium_threshold low_threshold other_threshold 脆弱性 の重要度ごとに検出数の 閾値 を設定 閾値 以上の 脆弱性 が検出されると、出力パラメータの vulnerability_threshold_exceeded が 1 になる scanners 利用するスキャナを指定できる。指定がない場合は全てのスキャナが有効 複数を利用する場合は , 区切りで記述 skip_scanners 利用を除外するスキャナを指定できる 複数を除外する場合は , 区切りで記述 skip_files スキャン対象から除外したいファイルを明示的に記述 複数指定する場合は , 区切りで記述 特に scanners と skip_scanners の設定値がわかりにくいですが、SBOM Generator v1.4.0 で利用できる設定値には以下の33種類ありました。 (SBOM Generator の inspector-sbomgen list-scanners コマンドで確認した結果) NAME GROUPS DESCRIPTION alpine-apk os pkg-scanner Scans packages installed with apk apache - httpd extra-ecosystems pkg-scanner Scans Apache HTTP Server based on contents of ap_release.h binaries binary pkg-scanner Scans compiled Rust and Go binaries for package dependencies csharp-csproj pkg-scanner programming-language-packages Scans C# packages based on contents of .csproj files csharp-depsjson pkg-scanner programming-language-packages Scans C# packages based on contents of .deps. json files csharp-pkgconfig pkg-scanner programming-language-packages Scans C# packages based on contents of Packages.config files csharp-pkglock pkg-scanner programming-language-packages Scans C# packages based on contents of packages.lock. json files debian -distroless os pkg-scanner Scans packages installed in Debian distroless containers dockerfile dockerfile pkg-scanner Scans Dockerfile contents for security issues dpkg os pkg-scanner Scans installed Debian packages go-gopkg pkg-scanner programming-language-packages Scans Go packages based on go.sum go-modcache pkg-scanner programming-language-packages Scans Go packages based on contents of $HOME/go/pkg/mod directory java -installation extra-ecosystems pkg-scanner Scans for Java installations in default paths java-jar pkg-scanner programming-language-packages Scans Java packages based on contents of pom.properties and archive files (.jar, .par, .war, .ear) java -pomxml pkg-scanner programming-language-packages Scans Java dependencies based on the content of pom. xml javascript -nodemodules pkg-scanner programming-language-packages Scans for installed packages based on contents of node_modules/*/package. json files javascript -npm-packagelock pkg-scanner programming-language-packages Scans NPM dependencies based on the content of package-lock. json file javascript -pnpm- yaml pkg-scanner programming-language-packages Scans PNPM dependencies based on the content of pnpm-lock. yaml file javascript -yarnlock pkg-scanner programming-language-packages Scans Yarn dependencies based on the content of yarn.lock file php pkg-scanner programming-language-packages Scans PHP packages based on contents of composer.lock and installed. json files python -pipfile pkg-scanner programming-language-packages Scans python packages based on contents of Pipfile.lock files python -pkg pkg-scanner programming-language-packages Scans python packages based on contents of egg-info and dist-info files python -poetry pkg-scanner programming-language-packages Scans python packages based on contents of poetry.lock files python -requirements pkg-scanner programming-language-packages Scans python packages based on content of requirements.txt files rhel - rpm os pkg-scanner Scans installed rpm packages ruby -gemfiles pkg-scanner programming-language-packages Scans Ruby packages based on contents of Gemfile.lock files ruby -gemspec pkg-scanner programming-language-packages Scans Ruby packages based on contents of .gemspec files ruby -globalgems pkg-scanner programming-language-packages Scans Ruby packages based on contents of globally installed gems rust-cargolock pkg-scanner programming-language-packages Scans Rust packages based on contents of Cargo.lock files rust-cargotoml pkg-scanner programming-language-packages Scans Rust packages based on contents of Cargo.toml files wordpress -installation extra-ecosystems pkg-scanner Scans for Wordpress wordpress -plugin-installation extra-ecosystems pkg-scanner Scans for Wordpress plugins wordpress -theme-installation extra-ecosystems pkg-scanner Scans for Wordpress themes 余談ですが、v1.3.2 では javascript-nodemodules javascript-npm-packagelock javascript-pnpm-yaml javascript-yarnlock が存在せず、代わりに javascript-nodejs を指定できました。これを使っていたところ、8月下旬にアクションが利用する SBOM Generator のデフォルトバージョンが 1.4.0 に切り替わったらしく、「何もしていないのに壊れた」状態になり焦りました。 リポジトリ 内のファイルをスキャンする場合 artifact_type 入力パラメータは repository を指定します。ジョブの全体像は次のようになります。ファイルを静的解析するため、コンテナをビルドする必要はありません。 生成した SBOM を Inspector に渡すため、 aws-actions/configure-aws-credentials アクションで一時的なクレデンシャルを取得します。利用するロールには inspector-scan:ScanSbom 権限をつけておきましょう。 jobs : scan : runs-on : ubuntu-latest # コンテナアクションなので Linux のみで実行可能 timeout-minutes : 20 steps : - name : Checkout Respository uses : actions/checkout@v4 # AWS クレデンシャルの設定 - name : Configure AWS Credentials uses : aws-actions/configure-aws-credentials@v4 with : role-to-assume : arn:aws:iam::111122223333:role/my-sample-role-name aws-region : ap-northeast-1 - name : Inspector Scan id : inspector uses : aws-actions/vulnerability-scan-github-action-for-amazon-inspector@v1.1.3 with : artifact_type : "repository" # repository を指定 artifact_path : "./" # リポジトリのルートを指定 display_vulnerability_findings : "enabled" critical_threshold : 1 high_threshold : 1 medium_threshold : 1 low_threshold : 1 other_threshold : 1 # SBOM の表示(必要に応じて利用) - name : Display CycloneDX SBOM (JSON) run : cat ${{ steps.inspector.outputs.artifact_sbom }} # JSON 形式の検出結果を表示(必要に応じて利用) - name : Display Inspector vulnerability scan results (JSON) run : cat ${{ steps.inspector.outputs.inspector_scan_results }} # CSV 形式の検出結果を表示(必要に応じて利用) - name : Display Inspector vulnerability scan results (CSV) run : cat ${{ steps.inspector.outputs.inspector_scan_results_csv }} # Markdown 形式の検出結果を表示(必要に応じて利用) - name : Display Inspector vulnerability scan results (Markdown) run : cat ${{ steps.inspector.outputs.inspector_scan_results_markdown }} # CSV 形式のDockerfile検出結果を表示(必要に応じて利用) - name : Display Dockerfile scan results (CSV) run : cat ${{ steps.inspector.outputs.inspector_dockerile_scan_results_csv }} # Markdown 形式のDockerfile検出結果を表示(必要に応じて利用) - name : Display Dockerfile scan results (Markdown) run : cat ${{ steps.inspector.outputs.inspector_dockerile_scan_results_markdown }} # スキャン結果をアーティファクトにアップロード - name : Upload Scan Results uses : actions/upload-artifact@v4 with : name : Inspector Vulnerability Scan Artifacts path : | ${{ steps.inspector.outputs.inspector_scan_results }} ${{ steps.inspector.outputs.inspector_scan_results_csv }} ${{ steps.inspector.outputs.artifact_sbom }} ${{ steps.inspector.outputs.inspector_scan_results_markdown }} ${{ steps.inspector.outputs.inspector_dockerile_scan_results_csv }} ${{ steps.inspector.outputs.inspector_dockerile_scan_results_markdown }} # 検出した脆弱性の数が閾値を超えた場合、ジョブを失敗ステータスにする - name : Fail job if vulnerability threshold is exceeded run : exit ${{ steps.inspector.outputs.vulnerability_threshold_exceeded }} 試してみた 以下の 脆弱性 のあるパッケージバージョンをインストールし、スキャンさせてみました。このうち、 micromatch は devDependencies に記載したパッケージ由来のものであり、本番にビルドするコンテナイメージに含めないように構成しています。 npmパッケージ名 バージョン CVE 本番コンテナに含まれるか next 14.1.0 CVE-2024-34351 ✅ fast- xml -parser 4.2.5 CVE-2024-41818 ✅ micromatch 14.0.5 CVE-2024-4067 ❌ また Dockerfile の設定もスキャン可能とのことなので、Dockerfile に次の一行を含めます。 USER root 実行すると、 脆弱性 が検出されたためジョブが失敗ステータスになりました。 サマリページの結果 サマリページに次のような結果が表示されました。 3つのパッケージの 脆弱性 、および Dockerfile にて root ユーザーを使用しているとの検出結果が表示されています。 パッケージの 脆弱性 については、 リポジトリ 内の yarn.lock ファイルから検出していることがわかります。従って本番のコンテナイメージには含まれない micromatch の 脆弱性 も検出されています。 CSV 形式の検出結果 (2024/11/19)Dockerfileの検出結果について追記しました。 CSV 形式の検出結果のうち、 inspector_scan_ とファイル名に付くもの( outputs.inspector_scan_results_csv )は次のようになりました。 こちらはパッケージの 脆弱性 のみで、Dockerfile の設定については出力がありません。 "#artifact_name:./","artifact_type:directory","artifact_hash:null","build_id:null" "#critical_vulnerabilities:0","high_vulnerabilities:1","medium_vulnerabilities:1","low_vulnerabilities:0","other_vulnerabilities:2" "ID","Severity","Source","CVSS","Installed Package","Fixed Package","Path","EPSS","Exploit Available","Exploit Last Seen","CWEs" "CVE-2024-4067","medium","MITRE","5.3","pkg:npm/micromatch@4.0.5","4.0.8","yarn.lock","0.00045","true","2024-08-26T14:20:30Z","CWE-1333" "CVE-2024-41818","untriaged","NVD","null","pkg:npm/fast-xml-parser@4.2.5","4.4.1","yarn.lock","0.00045","null","null","null" "CVE-2024-34351","high","MITRE","7.5","pkg:npm/next@14.1.0","14.1.1","yarn.lock","0.00119","true","2024-08-24T07:21:02Z","CWE-918" inspector_dockerfile_scan_ とファイル名に付くもの( outputs.inspector_dockerile_scan_results_csv )は次のようになりました。 こちらにDockerfile の設定についての検出結果が書かれています。 ID,SEVERITY,DESCRIPTION,FILE,LINE IN-DOCKER-003,info,Last USER is root: If a service can run without privileges use USER to change to a non-root user.,dockerfile:Dockerfile,41-41 JSON 形式の検出結果 JSON 形式の検出結果は次のようになりました。 脆弱性 だけではなく コンポーネント が全て記載されているので大変長いのですが、 sbom.vulnerabilities に 脆弱性 の情報が記録されていました。Dockerfile の設定についても記載されています。 JSON形式の検出結果 { " sbom ": { " specVersion ": " 1.5 ", " metadata ": { // (省略) } , " components ": [ // (省略) ] , " bomFormat ": " CycloneDX ", " vulnerabilities ": [ { " advisories ": [ { " url ": " https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1071631 " } , { " url ": " https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-4067 " } , { " url ": " https://www.cve.org/CVERecord?id=CVE-2024-4067 " } ] , " bom-ref ": " vuln-1 ", " references ": [ { " id ": " GHSA-952p-6rrq-rcjv ", " source ": { " name ": " GITHUB ", " url ": " https://github.com/advisories/GHSA-952p-6rrq-rcjv " } } ] , " created ": " 2024-05-14T15:42:47Z ", " description ": " The NPM package `micromatch` is vulnerable to Regular Expression Denial of Service (ReDoS). The vulnerability occurs in `micromatch.braces()` in `index.js` because the pattern `.*` will greedily match anything. By passing a malicious payload, the pattern matching will keep backtracking to the input while it doesn't find the closing bracket. As the input size increases, the consumption time will also increase until it causes the application to hang or slow down. There was a merged fix but further testing shows the issue persists. This issue should be mitigated by using a safe pattern that won't start backtracking the regular expression due to greedy matching. ", " affects ": [ { " ref ": " comp-988 " } ] , " source ": { " name ": " NVD ", " url ": " https://nvd.nist.gov/vuln/detail/CVE-2024-4067 " } , " cwes ": [ 1333 ] , " analysis ": { " state ": " exploitable " } , " ratings ": [ { " severity ": " none ", " score ": 0.00045 , " method ": " other ", " vector ": " model:v2023.03.01,date:2024-08-27T00:00:00+0000 ", " source ": { " name ": " EPSS ", " url ": " https://api.first.org/data/v1/epss?cve=CVE-2024-4067 " } } , { " severity ": " medium ", " score ": 5.3 , " method ": " CVSSv31 ", " vector ": " CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L ", " source ": { " name ": " MITRE ", " url ": " https://cve.org/CVERecord?id=CVE-2024-4067 " } } ] , " id ": " CVE-2024-4067 ", " updated ": " 2024-05-22T12:15:10Z ", " properties ": [ { " name ": " amazon:inspector:sbom_scanner:priority ", " value ": " standard " } , { " name ": " amazon:inspector:sbom_scanner:priority_intelligence ", " value ": " unverified " } , { " name ": " amazon:inspector:sbom_scanner:exploit_available ", " value ": " true " } , { " name ": " amazon:inspector:sbom_scanner:exploit_last_seen_in_public ", " value ": " 2024-08-26T14:20:30Z " } , { " name ": " amazon:inspector:sbom_scanner:fixed_version:comp-988 ", " value ": " 4.0.8 " } ] } , { " advisories ": [ { " url ": " https://docs.docker.com/develop/develop-images/instructions/ " } ] , " bom-ref ": " vuln-2 ", " ratings ": [ { " severity ": " info ", " method ": " other ", " source ": { " name ": " AMAZON_INSPECTOR ", " url ": " https://aws.amazon.com/inspector/ " } } ] , " created ": " 2024-03-27T14:36:39Z ", " description ": " Last USER is root: If a service can run without privileges, use USER to change to a non-root user. ", " affects ": [ { " ref ": " comp-1106 " } ] , " id ": " IN-DOCKER-003 ", " source ": { " name ": " AMAZON_INSPECTOR ", " url ": " https://aws.amazon.com/inspector/ " } , " analysis ": { " state ": " in_triage " } , " updated ": " 2024-03-27T14:36:39Z " } , { " advisories ": [ { " url ": " https://access.redhat.com/errata/RHSA-2024:5054 " } ] , " bom-ref ": " vuln-3 ", " references ": [ { " id ": " GHSA-mpg4-rc92-vx8v ", " source ": { " name ": " GITHUB ", " url ": " https://github.com/advisories/GHSA-mpg4-rc92-vx8v " } } ] , " ratings ": [ { " severity ": " none ", " score ": 0.00045 , " method ": " other ", " vector ": " model:v2023.03.01,date:2024-08-27T00:00:00+0000 ", " source ": { " name ": " EPSS ", " url ": " https://api.first.org/data/v1/epss?cve=CVE-2024-41818 " } } ] , " created ": " 2024-07-29T16:15:05Z ", " description ": " fast-xml-parser is an open source, pure javascript xml parser. a ReDOS exists on currency.js. This vulnerability is fixed in 4.4.1. ", " affects ": [ { " ref ": " comp-1068 " } ] , " id ": " CVE-2024-41818 ", " source ": { " name ": " NVD ", " url ": " https://nvd.nist.gov/vuln/detail/CVE-2024-41818 " } , " analysis ": { " state ": " in_triage " } , " updated ": " 2024-08-02T20:17:01Z ", " properties ": [ { " name ": " amazon:inspector:sbom_scanner:priority ", " value ": " standard " } , { " name ": " amazon:inspector:sbom_scanner:priority_intelligence ", " value ": " unverified " } , { " name ": " amazon:inspector:sbom_scanner:fixed_version:comp-1068 ", " value ": " 4.4.1 " } ] } , { " advisories ": [ { " url ": " https://nvd.nist.gov/vuln/detail/CVE-2024-34351 " } ] , " bom-ref ": " vuln-4 ", " references ": [ { " id ": " GHSA-fr5h-rqp8-mj6g ", " source ": { " name ": " GITHUB ", " url ": " https://github.com/advisories/GHSA-fr5h-rqp8-mj6g " } } ] , " created ": " 2024-05-14T15:38:42Z ", " description ": " Next.js is a React framework that can provide building blocks to create web applications. A Server-Side Request Forgery (SSRF) vulnerability was identified in Next.js Server Actions. If the `Host` header is modified, and the below conditions are also met, an attacker may be able to make requests that appear to be originating from the Next.js application server itself. The required conditions are 1) Next.js is running in a self-hosted manner; 2) the Next.js application makes use of Server Actions; and 3) the Server Action performs a redirect to a relative path which starts with a `/`. This vulnerability was fixed in Next.js `14.1.1`. ", " affects ": [ { " ref ": " comp-829 " } ] , " source ": { " name ": " NVD ", " url ": " https://nvd.nist.gov/vuln/detail/CVE-2024-34351 " } , " cwes ": [ 918 ] , " analysis ": { " state ": " exploitable " } , " ratings ": [ { " severity ": " none ", " score ": 0.00119 , " method ": " other ", " vector ": " model:v2023.03.01,date:2024-08-27T00:00:00+0000 ", " source ": { " name ": " EPSS ", " url ": " https://api.first.org/data/v1/epss?cve=CVE-2024-34351 " } } , { " severity ": " high ", " score ": 7.5 , " method ": " CVSSv31 ", " vector ": " CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N ", " source ": { " name ": " MITRE ", " url ": " https://cve.org/CVERecord?id=CVE-2024-34351 " } } ] , " id ": " CVE-2024-34351 ", " updated ": " 2024-05-14T16:12:23Z ", " properties ": [ { " name ": " amazon:inspector:sbom_scanner:priority ", " value ": " standard " } , { " name ": " amazon:inspector:sbom_scanner:priority_intelligence ", " value ": " unverified " } , { " name ": " amazon:inspector:sbom_scanner:exploit_available ", " value ": " true " } , { " name ": " amazon:inspector:sbom_scanner:exploit_last_seen_in_public ", " value ": " 2024-08-24T07:21:02Z " } , { " name ": " amazon:inspector:sbom_scanner:fixed_version:comp-829 ", " value ": " 14.1.1 " } ] } ] } } Markdown 形式の検出結果 (2024/11/19)Dockerfileの検出結果について追記しました。 Markdown 形式の検出結果のうち、 inspector_scan_ とファイル名に付くもの( outputs.inspector_scan_results_markdown )は次のようになりました。 こちらはパッケージの 脆弱性 のみで、Dockerfile の設定については出力がありません。 # Amazon Inspector Scan Results Artifact Type: repository ## Vulnerability Counts by Severity | Severity | Count | |----------|-------| | Critical | 0| | High | 1| | Medium | 1| | Low | 0| | Other | 2| ## Vulnerability Findings | ID | Severity | Source | [ CVSS ]( https://www.first.org/cvss/ ) | Installed Package ([ PURL ]( https://github.com/package-url/purl-spec/tree/master?tab=readme-ov-file#purl )) | Fixed Package | Path | [ EPSS ]( https://www.first.org/epss/ ) | Exploit Available | Exploit Last Seen | CWEs | | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | | CVE-2024-34351 | high | MITRE | 7.5 | ` pkg:npm/next@14.1.0 ` | ` 14.1.1 ` | ` yarn.lock ` | 0.00119 | true | 2024-08-24T07:21:02Z | ` CWE-918 ` | | CVE-2024-4067 | medium | MITRE | 5.3 | ` pkg:npm/micromatch@4.0.5 ` | ` 4.0.8 ` | ` yarn.lock ` | 0.00045 | true | 2024-08-26T14:20:30Z | ` CWE-1333 ` | | CVE-2024-41818 | untriaged | NVD | | ` pkg:npm/fast-xml-parser@4.2.5 ` | ` 4.4.1 ` | ` yarn.lock ` | 0.00045 | | | | inspector_dockerfile_scan_ とファイル名に付くもの( outputs.inspector_dockerile_scan_results_markdown )は次のようになりました。 こちらにDockerfile の設定についての検出結果が書かれています。 ## Dockerfile Findings |ID|SEVERITY|DESCRIPTION|FILE|LINES| |---|---|---|---|---| | IN-DOCKER-003 | info | Last USER is root: If a service can run without privileges, use USER to change to a non-root user. | dockerfile:Dockerfile | 41-41 | 脆弱性 が検出されなかった場合 (2024/11/18追記)本セクションに記載している問題は v1.1.4 にて解消されました。 ちなみに 脆弱性 が1つも検出されなかった場合、 CSV 形式のレポートは生成されないようで、以下のようなステップの書き方にすると、 脆弱性 がないにも関わらずジョブが失敗してしまいます。 - name : Display Inspector vulnerability scan results (CSV) run : cat ${{ steps.inspector.outputs.inspector_scan_results_csv }} そこで cat コマンドが失敗しても異常終了させないようにしておくと良いです。 - name : Display Inspector vulnerability scan results (CSV) run : cat ${{ steps.inspector.outputs.inspector_scan_results_csv }} || true コンテナイメージをスキャンする場合 コンテナイメージをスキャンする場合のジョブの記述例です。 artifact_type 入力パラメータは container を指定し、 artifact_path にはコンテナイメージ名を渡します。事前にコンテナをビルドしておく必要があります。 個人的な推奨として、コンテナイメージのスキャンの場合は lock ファイルをスキャン対象外にしておくと良いと思います。 devDependencies の由来パッケージは実際には本番コンテナに含まれないようにしていることが多いと思いますが、ビルド済みのコンテナイメージをスキャンするからには、実際にコンテナに入っているパッケージのみを対象とした方が稼働環境の 脆弱性 がわかりやすいです。開発のみで使用しているパッケージの 脆弱性 を知りたい場合は、わざわざ時間をかけてコンテナをビルドせずに リポジトリ 内のファイルをスキャンすれば良いのです。 # docker/build-push-action のセットアップ - name : Set up docker build prereqs (QEMU) uses : docker/setup-qemu-action@v3 - name : Set up docker build prereqs (Buildx) uses : docker/setup-buildx-action@v3 # イメージをビルド - name : Build Docker image uses : docker/build-push-action@v5 with : context : . file : ./Dockerfile push : false tags : my-container:${{ github.sha }} load : true - name : Inspector Scan id : inspector uses : aws-actions/vulnerability-scan-github-action-for-amazon-inspector@v1.1.3 with : artifact_type : "container" artifact_path : "my-container:${{ github.sha }}" display_vulnerability_findings : "enabled" critical_threshold : 1 high_threshold : 1 medium_threshold : 1 low_threshold : 1 other_threshold : 1 skip_files : "/usr/app/yarn.lock" # コンテナ内のロックファイルをスキャン対象から除外 サマリページの結果 サマリページに次のような結果が表示されました。 skip_files: "/usr/app/yarn.lock" と設定したため、本番コンテナに含める2つのパッケージの 脆弱性 のみが検出されました。検出したソースは node_modules にインストールされたパッケージ内の package.json ファイルだとわかります。 Dockerfile についてもスキャン対象ではありますが、今回はDockerfile 自体をコンテナ内にコピーしていないため、ここでは検出されていません。 リポジトリ 内のファイルスキャンと、コンテナイメージスキャンの使い分けについて 2つのスキャン対象について試しましたが、使い分けについて考えてみたいと思います。 まずコンテナイメージスキャンですが、コンテナをビルドするためスキャン速度は遅く、頻繁な実行には向きません。しかし実際のコンテナビルドに使う Dockerfile を利用してビルドした結果の状態を見ることができるため、 本番へデプロイするワークフローで実行するのが良いでしょう。 そのためにコンテナ内の lock ファイルはスキャン対象から除外し、開発のみで利用しているパッケージの 脆弱性 は出力させないことでノイズを減らします。 Dockerfile もスキャン対象ではありますが、Dockerfile 自体をコンテナ内にコピーしていない場合もあるので、別のところでスキャンさせることを考えます。 リポジトリ 内のファイルスキャンですが、コンテナをビルドする必要がないのでスキャン速度は速いです。 開発用ブランチへのプッシュごとに実行すると良いでしょう。 開発のみで利用しているパッケージや Dockerfile の不適切な設定もスキャン対象とし、開発中に修正するきっかけにします。 まとめ Node.js アプリを例に GitHub Actions で Amazon Inspector を利用した 脆弱性 スキャンを試しました。 他の言語についても試した方がいれば、ぜひ情報をお待ちしております。 執筆: @kou.kinyo 、レビュー: 寺山 輝 (@terayama.akira) ( Shodo で執筆されました )
アバター
XI本部スマート ソサエティ センターの野網です。データ連携基盤に関連する 自治 体案件に携わっています。この 自治 体案件ではデータ連携基盤だけでなく、基盤上で提供するサービスについても検討しています。防災やインフラ管理の観点では、地図上でさまざまなデータを重ね合わせて可視化する GIS (地理情報システム)の実現が重要視されています。 Leaflet 今回は地図機能を提供する オープンソース の JavaScript ライブラリである「 Leaflet 」を触って色々と機能を確認してみました。 ベースとなる地図を指定できるか 地図上にピンを立てることができるか( Google Maps とかでよく見るもの) 地図上に任意の点/線/面を表示できるか 地図上に シェープファイル を表示できるか ベースマップ&ピンの表示 ベースマップとして、 OpenStreetMap 1 と 地理院 地図 2 を使用しました。また、駅の位置にピンを表示するレイヤーを作成しました。 Control.Layers を利用することで、ベースマップの切り替えやレイヤーの表示/非表示を実現しています。 // ベースマップ let osm = L . tileLayer ( 'https://tile.openstreetmap.org/{z}/{x}/{y}.png' , { maxZoom : 19 , attribution : '© OpenStreetMap' }) ; let gsi = L . tileLayer ( 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png' , { attribution : '© 地理院地図' }) ; // ピン let shinagawa = L . marker ([ 35 . 6291112 , 139 . 7389313 ]) . bindPopup ( '品川駅' ) ; let takanawa_gateway = L . marker ([ 35 . 6355406 , 139 . 7407245 ]) . bindPopup ( '高輪ゲートウェイ駅' ) ; let stations = L . layerGroup ([ shinagawa , takanawa_gateway ]) ; // 地図セットアップ map = L . map ( 'map' , { center : [ 35 . 62455060680114 , 139 . 7395781036654 ] , zoom : 16 , layers : [ osm , stations ] }) ; let baseMaps = { "OpenStreetMap" : osm , "地理院地図" : gsi } ; let overlayMaps = { "駅" : stations } ; let layerControl = L . control . layers ( baseMaps , overlayMaps ) . addTo ( map ) ; 点/線/面、 シェープファイル の表示 最も基本的な描画機能である点/線/面の表示も確認します。これらはGeoJSON形式のファイルを読み込む形としており、非同期処理になっています。このファイルは手打ちで作成しました。 また、 GIS でよく出てくる シェープファイル の表示も行いました。 シェープファイル は 国土数値情報で公開されている道路のデータ を取得しています。Leafletには様々な プラグイン があり、 シェープファイル の表示に対応するものもありましたが、 日本語に対応していないという記事 があったので、この記事で紹介されている mapshaper を利用して シェープファイル をGeoJSON形式に事前に変換するという方法をとりました。 これらを先ほどのControl.Layersに追加し、期待していた機能を試すことができました。 async function fetchGeoJson ( filename ) { let response = await fetch ( filename ) ; return await response . json () ; } async function add_layer () { let my_geo_json = await fetchGeoJson ( "my_geojson_data.json" ) ; // GeoJSONレイヤーを追加 let my_geo_json_layer = L . geoJSON ( my_geo_json , { onEachFeature : function ( feature , layer ) { if ( feature . properties && feature . properties . name ) { layer . bindPopup ( feature . properties . name ) ; } } , style : function ( feature ) { switch ( feature . geometry . type ) { case 'Point' : return { color : "#ff0000" } ; case 'LineString' : return { color : "#0000ff" } ; case 'Polygon' : return { color : "#00ff00" } ; } } , pointToLayer : function ( feature , latlng ) { return L . circleMarker ( latlng , { radius : 8 , fillColor : "#ff0000" , color : "#000" , weight : 1 , opacity : 1 , fillOpacity : 0 . 8 }) ; } }) . addTo ( map ) ; my_geo_json_layer_group = L . layerGroup ([ my_geo_json_layer ]) ; layerControl . addOverlay ( my_geo_json_layer_group , "点/線/面" ) // 道路を追加 let road_feature = await fetchGeoJson ( "N01-07L-2K-13_Road.json" ) ; let road_layer = L . geoJSON ( road_feature , { style : function ( feature ) { return { color : "#ff00ff" } ; } }) . addTo ( map ) ; road_layer_group = L . layerGroup ([ road_layer ]) ; layerControl . addOverlay ( road_layer_group , "道路" ) } add_layer () ; サンプルコード 参考に今回の機能確認で利用したコードの全容を記載します。 index.html: <!DOCTYPE html> < html > < head > < title > Leaflet Sample Map </ title > < meta charset = "utf-8" /> < meta name = "viewport" content = "width=device-width, initial-scale=1.0" > <!-- Leaflet CSS --> < link rel = "stylesheet" href = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity= "sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin = "" /> <!-- Leaflet Plugin CSS --> < link rel = "stylesheet" href = "https://unpkg.com/leaflet-switch-basemap@1.0.6/src/L.switchBasemap.css" crossorigin = "" /> < style > #map { width : 100% ; height : 600px ; } </ style > </ head > < body > < h1 > Leaflet Sample Map </ h1 > < div id = "map" ></ div > <!-- Leaflet JavaScript --> < script src = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity= "sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin = "" ></ script > <!-- アプリケーションのJavaScript --> < script src = "app.js" ></ script > </ body > </ html > app.js: // ベースマップ let osm = L . tileLayer ( 'https://tile.openstreetmap.org/{z}/{x}/{y}.png' , { maxZoom : 19 , attribution : '© OpenStreetMap' }) ; let gsi = L . tileLayer ( 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png' , { attribution : '© 地理院地図' }) ; // ピン let shinagawa = L . marker ([ 35 . 6291112 , 139 . 7389313 ]) . bindPopup ( '品川駅' ) ; let takanawa_gateway = L . marker ([ 35 . 6355406 , 139 . 7407245 ]) . bindPopup ( '高輪ゲートウェイ駅' ) ; let stations = L . layerGroup ([ shinagawa , takanawa_gateway ]) ; // 地図セットアップ map = L . map ( 'map' , { center : [ 35 . 62455060680114 , 139 . 7395781036654 ] , zoom : 16 , layers : [ osm , stations ] }) ; let baseMaps = { "OpenStreetMap" : osm , "地理院地図" : gsi } ; let overlayMaps = { "駅" : stations } ; let layerControl = L . control . layers ( baseMaps , overlayMaps ) . addTo ( map ) ; async function fetchGeoJson ( filename ) { let response = await fetch ( filename ) ; return await response . json () ; } async function add_layer () { let my_geo_json = await fetchGeoJson ( "my_geojson_data.json" ) ; // GeoJSONレイヤーを追加 let my_geo_json_layer = L . geoJSON ( my_geo_json , { onEachFeature : function ( feature , layer ) { if ( feature . properties && feature . properties . name ) { layer . bindPopup ( feature . properties . name ) ; } } , style : function ( feature ) { switch ( feature . geometry . type ) { case 'Point' : return { color : "#ff0000" } ; case 'LineString' : return { color : "#0000ff" } ; case 'Polygon' : return { color : "#00ff00" } ; } } , pointToLayer : function ( feature , latlng ) { return L . circleMarker ( latlng , { radius : 8 , fillColor : "#ff0000" , color : "#000" , weight : 1 , opacity : 1 , fillOpacity : 0 . 8 }) ; } }) . addTo ( map ) ; my_geo_json_layer_group = L . layerGroup ([ my_geo_json_layer ]) ; layerControl . addOverlay ( my_geo_json_layer_group , "点/線/面" ) // 道路を追加 let road_feature = await fetchGeoJson ( "N01-07L-2K-13_Road.json" ) ; let road_layer = L . geoJSON ( road_feature , { style : function ( feature ) { return { color : "#ff00ff" } ; } }) . addTo ( map ) ; road_layer_group = L . layerGroup ([ road_layer ]) ; layerControl . addOverlay ( road_layer_group , "道路" ) } add_layer () ; app.js で参照している N01-07L-2K-13_Road.json は国土数値情報から取得した道路データです。色々なデータで試してみてください。 my_geojson_data. json : [ { " type ": " Feature ", " geometry ": { " type ": " Point ", " coordinates ": [ 139.7395781036654 , 35.62455060680114 ] } , " properties ": { " name ": " Sample Point " } } , { " type ": " Feature ", " geometry ": { " type ": " LineString ", " coordinates ": [ [ 139.73944850383708 , 35.62501310494848 ] , [ 139.73909069834616 , 35.62487771334422 ] , [ 139.73946289831088 , 35.62424755434228 ] ] } , " properties ": { " name ": " Sample Line " } } , { " type ": " Feature ", " geometry ": { " type ": " Polygon ", " coordinates ": [ [ [ 139.74004599801373 , 35.62494392209728 ] , [ 139.73961416239794 , 35.62473352999296 ] , [ 139.7398032581179 , 35.62440431429395 ] , [ 139.7401841317666 , 35.62449152388262 ] ] ] } , " properties ": { " name ": " Sample Polygon " } } ] おわりに シェープファイル をGeoJSON形式に変換が必要となる部分など、詰まることもありましたが、Leafletのような、 オープンソース のツールを使うことで、手軽に GIS の可能性を試すことができるのは魅力的だと感じました。 プラグイン も含めて機能が多数あり、サンプルも充実しているので、地図サービスに興味があればチェックしていただく価値があると思います。 私たちは一緒に働いてくれる仲間を募集しています! 社会課題解決ソリューションプロジェクト_システム開発プロジェクトマネージャー/リーダー 社会課題解決ソリューションプロジェクト_アーキテクト/テックリード クラウドアーキテクト 執筆: @noami.shunsuke 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました ) OpenStreetMap ↩ 国土地理院 ↩
アバター
こんにちは。コミュニケーションIT事業部 ITソリューション部の英です。 普段はWebアプリや スマホ アプリの案件などを担当しています。あと、趣味でAIを勉強しています。 前回はSageMakerの線形学習モデルを使って、S&P500の100日後のスコアを予測しました。 今回は同じデータを使って、 Amazon Forecastで予測をしてみましょう。 → 前回の記事 Amazon Forecastはマネージド型の 機械学習 サービスです。 これにより、専門的な 機械学習 の知識がなくても、需要予測、在庫管理、収益予測などを簡単に行うことができます。 ちなみに、 Amazon Forecastは新規ユーザーへの提供終了が発表されており、後続のサービスはSageMaker Canvas だそうです。SageMaker Canvas は、また別の記事で解説したいと思います。 → SageMaker Canvas 今回の検証では、過去の株価の数値だけを参考値として予測するため、決して皆さまの資産運用の参考にはしないようにお願いいたします。 資産運用には世界情勢などの"不確かさ"が付き物です。そこが面白さでもあります。投資は自己責任です。 ここから本題 STEP1:学習用データの準備 学習用データを作成して任意のS3 バケット にアップロードしましょう。 !pip install yfinance pandas boto3 import yfinance as yf import pandas as pd import boto3 import numpy as np # データ取得 sp500 = yf.Ticker('^GSPC') df = sp500.history(period='10y') # すべての日付を取得 full_date_range = pd.date_range(start=df.index.min(), end=df.index.max(), freq='D') # インデックスを再設定 df = df.reindex(full_date_range) # データの補間 df['Close'].ffill(inplace=True) # 特徴量の計算 def calculate_features(df): df['MA_50'] = df['Close'].rolling(window=50).mean() df['MA_100'] = df['Close'].rolling(window=100).mean() df['MA_200'] = df['Close'].rolling(window=200).mean() df['BB_MID'] = df['Close'].rolling(window=20).mean() df['BB_STD'] = df['Close'].rolling(window=20).std() df['BB_UPPER'] = df['BB_MID'] + (df['BB_STD'] * 2) df['BB_LOWER'] = df['BB_MID'] - (df['BB_STD'] * 2) calculate_features(df) # 最初の200行を削除 df = df.iloc[200:] # 日付フォーマットをyyyy-MM-ddに変更 df['Date'] = df.index.strftime('%Y-%m-%d') df.reset_index(drop=True, inplace=True) # item_id列に単一のアイテムIDを設定 df['item_id'] = 'sp500' # 必要な列のみ選択し、target_valueとしてCloseを設定 df = df[['item_id', 'Date', 'Close', 'MA_50', 'MA_100', 'MA_200', 'BB_MID', 'BB_UPPER', 'BB_LOWER']] df.rename(columns={'Close': 'target_value', 'Date': 'timestamp'}, inplace=True) # CSVに保存 df.to_csv('sp500_features.csv', index=False) # S3にデータをアップロード s3 = boto3.client('s3') bucket_name = '(バケット名)' file_name = 'sp500_features.csv' s3.upload_file(file_name, bucket_name, file_name) print(f"データをS3にアップロードしました: s3://{bucket_name}/{file_name}") STEP2:デー タセット の作成 デー タセット グループという分析用の箱を用意します。 ドメイン では様々なビジ ネスケ ースを選択できますが、株価予測は フレームワーク として用意がなかったので「Custom」を選択しておきます。 スキーマ を以下のように設定します。 予約型以外はStringにすることが必須のため、 移動平均線 などのような数値もStringとして取り込みます。 先ほど作成した CSV ファイルを選択します。 デー タセット グループを作成すると、インポートが始まります。 今回のケースでは約30分ほどかかりました。 STEP3:ト レーニン グの開始 Startを押してト レーニン グの開始設定に進みます。 以下のとおり設定してみます。 予測対象は100日後の株価なので、Forecast horizonは「100」を設定します。 今回は 終値 のみでト レーニン グしたいので、特徴量の次元(Forecast dimensions)については、「設定なし」とします。 以前はここに アルゴリズム 選択があったのですが、2024年8月の現在は選択できなくなっていますね。 米国の株式市場は休場の概念があるので、米国のHolidaysを選択しておきます。 今回の設定ではト レーニン グに3時間31分かかるようです。待ちましょう。 Activeになりました。 STEP4:予測データの作成 さっそく予測を作成します。 予測作成の設定画面では先ほど作成した予測モデルを選択します。 予測の作成が始まりました。1時間ほどかかるので終わるのを待ちます。 Activeになりました。 STEP5:予測データの可視化 最後にQuery forecastから予測値の可視化を行います。 現在から前後100日をプロットします。 いいですね~。100日後は5843ポイントで現在のスコアから1.12%上昇という予測で全体的にポジティブですね。 株式市場は年末に上昇する アノマリー がありますが、そこもしっかり反映されていることが確認できます。 ※ アノマリー :理論の枠組みでは説明することができないものの、経験的に観測できるマーケットの規則性のこと グラフの見方は以下のとおり。 P10: 実際の値が予測値よりも低い確率が10%であることを意味します。つまり、予測値が実際の値よりも高くなる確率が90%です。 P50: 実際の値が予測値よりも低い確率が50%であることを意味します。予測値が実際の値よりも高くなる確率も50%です。中央値の予測です。 P90: 実際の値が予測値よりも低い確率が90%であることを意味します。つまり、予測値が実際の値よりも高くなる確率が10%です。 → Evaluating Predictor Accuracy おわりに 現在、 Amazon Forecastは新規ユーザーには提供されておらず、後継サービスはSageMaker Canvas とのこと。 次回の記事ではSageMaker Canvas を使ってみようかと思います。 少し期間が空いてしまうかもですがご期待ください。 これからも AWS ×AI関連の検証記事をたくさん書いていきます。 ↓ のスターを押していただけると嬉しいです。励みになります。 最後まで読んでいただき、ありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! コミュニケーションIT事業部 執筆: 英 良治 (@hanabusa.ryoji) 、レビュー: @handa.kenta ( Shodo で執筆されました )
アバター
こんにちは。金融ソリューション事業部の姫野です。 1. 今回実装する内容 1.1 実装内容のキーポイント 1.2 ユーザー体験の流れ 1.3 ガス代について 2. 利用サービス 2.1 thirdwebサービス 2.1.1 Connect 2.1.2 Engine 2.1.3 Drop(ERC1155) 2.1.4 React SDK 2.2 その他 3. 実装詳細 3.1 配布用のNFTコントラクトの作成 3.1.1 thirdwebへウォレットでログイン(or 新規作成) 3.1.2 コントラクトの作成、デプロイ 3.1.3 NFTの発行準備 3.2 トランザクションプロバイダの構築 3.2.1 Postgresコンテナの立ち上げ 3.2.2 Engineコンテナの立ち上げ 3.2.3 Engineコンテナをthirdwebダッシュボードに登録 3.3 NFT配布用のバックエンドウォレットの作成 3.3.1 バックエンドウォレットの作成 3.3.2 ガス代の準備 3.4 Webページの作成 3.4.1 サンプルコードのクローンと依存関係のインストール 3.4.2 evnファイルの準備 3.4.3 ソースコードの編集①(page.tsx) 3.4.4 ソースコードの編集②(route.ts) 3.5 実行 4. 総括 4.1 類似サービスでの構築 4.2 今回実装したサービスがweb3に与える影響 1. 今回実装する内容 以前のブログ で、web3ウォレットを手軽に提供するWaaS(Wallet as a Service)について比較検討しました。本ブログでは、その中から thirdweb が提供するweb3サービスを使用し、 SNS ログインでNFTを発行するシステムを構築しました。このシステムは誰もが簡単にNFTを取得できるように設計されており、非常にユーザーフレンドリーです。さらに、このブログの手順に従えば簡単に同様のシステムを構築できますので、ぜひ挑戦してみてください。 1.1 実装内容のキーポイント UI操作のみでNFT コントラ クトを作成 SNS アカウントでweb3ウォレットの生成 ガスレスでNFTを取得 ブロックチェーン に対する トランザクション はthirdwebサービスが代行 1.2 ユーザー体験の流れ Webサイトにアクセス Google ログイン NFT取得ボタンをクリック NFTの受け取り 1.3 ガス代について ブロックチェーン の トランザクション には手数料(ガス代)がかかります。ガス代は、 トランザクション を実行するために必要な計算リソースのコストをカバーするもので、 トランザクション の複雑さやネットワークの混雑状況に応じて変動します。通常、NFTを受け取る際にはNFT自体の価格が0であってもユーザーの手数料が発生しますが、本システムでは実装者側が代替することで解消可能です。具体的な手法については後述します。 2. 利用サービス 今回の実装には、thirdwebのサービスを主に使用しました。thirdwebは開発者にとって扱いやすく、 API との連携もシンプルです。一つのサービスプロバイダーを利用することで、 API キーの管理が一元化され、 開発プロセス がより効率的になります。このため、他のプラットフォームを組み合わせることなくthirdwebサービスに統一して構築しました。 基本的にthirdwebのサービスは無料で利用でき、今回は無料の範囲で構築しましたが、一部機能やアクセス数に応じて有料となる場合があるため、詳細は こちら を参照してください。 2.1 thirdwebサービス 2.1.1 Connect thirdwebのConnectは、ウェブサイトに訪れたユーザーが自動的にweb3ウォレットを生成できる機能です。この機能を選んだ理由は、ユーザーが煩雑なプロセスを経ずに直接ウェブサイトでweb3ウォレットを持てるようにするためです。 類似の機能を提供する他のサービスとしては、 以前のブログ で取り上げています。 2.1.2 Engine Engineはユーザーのガスレス トランザクション を実現する トランザクション 代行サービスです。このサービスを選んだのは、開発初心者でも手軽にNFTの発行を行えるためです。 類似の機能を提供する他のサービスには、 Openzeppelin Defender や unWallet などがあります。thirdwebとOpenzeppelin Defenderの連携は こちら が参考になります。 2.1.3 Drop(ERC1155) Drop はユーザーが独自のNFTを簡単に作成できるサービスで、ERC1155規格に基づいています。これを選んだ理由は、柔軟性と多機能性を兼ね備えているためです。 類似のサービスとしては、 Manifold 、 Chocofactory 、 Bueno 、 NFT Garden 、NFT マーケットプレイス 各種など多くのサービスがあります。 2.1.4 React SDK thirdwebは、さまざまな開発 フレームワーク や環境に対応する SDK を提供しています。本ブログでは、Webフロントエンド開発のためにReact SDK を選択しました。thirdwebはReactの他にも、TypeScript、React Native、Unity、Solidity、.NETといった言語や フレームワーク 用の SDK を提供しています。 2.2 その他 その他、以下の開発ツールや フレームワーク を利用していますが、本ブログではインストール方法や基礎的な利用方法は割愛します。Docker Desktopはthirdweb Engineをホストするために使用しましたが、課金すればthirdweb側での ホスティング で代替できます。 Docker Desktop Visual Studio Code npm React.js 3. 実装詳細 本題の実装内容については、以下のステップで行いました。順に手順を解説します。 3.1 配布用のNFTコントラクトの作成 3.2 トランザクションプロバイダの構築 3.3 NFT配布用のバックエンドウォレットの作成 3.4 Webページの作成 3.5 実行 3.1 配布用のNFT コントラ クトの作成 初めに、NFTをユーザーに配布するための コントラ クトを作成します。この コントラ クトはthirdwebのプラットフォームを通じて簡単に作成可能です。 3.1.1 thirdwebへウォレットでログイン(or 新規作成) thirdwebのContract作成画面 で、[Connect Wallet]を行います。 (※ログインが求められたら次の手順を先に実施してください。) web3ウォレットを持っていない方はSocial Loginでアカウント作成。持っている方は任意の方法でログインします。 ログイン後、右上がConnect Walletからログイン後のUIに変化したら成功です。クリックすると詳細がみられます。 これだけでweb3ウォレットが作成できます。銀行口座開設に比べて簡単ですね。 割り振られた42桁の数字は0xから始まる16進数を示す数字でウォレットアドレスと呼ばれ、 ブロックチェーン 上で透明性のある一意のユーザーを示します。 3.1.2 コントラ クトの作成、デプロイ 続いて、 thirdwebダッシュボード >[Contracts]>[Explore]> Edition Drop をクリックします(今回はERC1155規格の コントラ クトを作成します)。 [Deploy now]をクリックします。 Nameに任意の値を入れます。 Networkをクリックし、デプロイしたい ブロックチェーン を選択します。今回はEthereumのテストネットワークである「Sepolia」を選択しました。その後、[Deploy Now]をクリックします。 Deployの完了を待ちます。本来、 ブロックチェーン の トランザクション を実行する際は、署名画面での署名と少量のガス代が必要ですが、どちらも不要でした。おそらくテストネットのため、thirdwebプラットフォームが代行してくれたのではないかと推測してます。 ※ コントラ クトをデプロイするためにガス代を要求された場合はこちらでマイニングして少量取得するのが早いと思います。 https://sepolia-faucet.pk910.de/ コントラ クトの作成が完了しました。 コントラ クトにも コントラ クトアドレスと呼ばれる42桁の数字が生成され、 ブロックチェーン 上のプログラムの住所を示すような役割を担います。 3.1.3 NFTの発行準備 続いて1つNFTを用意しましょう。左のタブ[NFTs]から[Single Upload]を選択します。 NFTのNameを入れ、[Lazy Mint NFT]をクリックします。NFTの画像は任意で挿入可能です。Lazy Mintでは画面で入力したNFTの メタデータ (設定情報)を ブロックチェーン に書き込むための準備処理を行います。 Token ID: 0のNFTが用意されました。 クリックすると詳細画面が開くので、Token ID: 0のNFTを発行するための設定を行います。 [Claim Conditions]>[Add Phase]をクリックします。 [Public]をクリック デフォルト設定のまま、[Save Phases]をクリック Phaseが追加され、NFTの発行準備が完了しました。 配布用のNFT コントラ クト作成と準備はこれで完了です。 3.2 トランザクション プロバイダの構築 次に、 thirdweb Engine を用いて ブロックチェーン 上で トランザクション を代理で実行し、ユーザーにシームレスな体験を提供する トランザクション プロバイダを構築します。このプロバイダは第 三者 のサービスに依存せずに、すべての トランザクション の代行が可能です。 Engineの実装はthirdwebの ホスティング 環境を利用する方法(有料)とセルフ ホスティング の方法(無料)があり、今回は大人の都合で(社内決済に時間を要したくなかったので)、 Engineセルフホスト 手順に沿って、ローカル環境にEngineを無料で ホスティング しました。 検証環境ではセルフ ホスティング でも問題ありませんが、本番運用を考える場合は、冗長性やセキュリティ対策が不可欠のため、UIの簡単操作で構築が可能な有料のthirdweb ホスティング を利用することをお勧めします。 3.2.1 Postgresコンテナの立ち上げ まず、ターミナルで以下コマンドを実行し、PostgresコンテナをDocker上に起動します。 docker run -p 5432:5432 -e POSTGRES_PASSWORD=postgres -d postgres DockerでPostgresコンテナが起動していることを確認できました。 3.2.2 Engineコンテナの立ち上げ 続いて、以下のコマンドをターミナルで実行し、thirdweb EngineをDocker上で起動します。 docker run ` -e ENCRYPTION_PASSWORD="encryption_password" ` -e THIRDWEB_API_SECRET_KEY="①" ` -e ADMIN_WALLET_ADDRESS="②" ` -e POSTGRES_CONNECTION_URL="postgresql://postgres:postgres@host.docker.internal:5432/postgres?sslmode=disable" ` -e ENABLE_HTTPS=false ` -p 3005:3005 ` --pull=always ` --cpus="0.5" ` thirdweb/engine:latest コマンド実行で必要となる①②は各自で取得します。 ①については thirdwebダッシュボード >[Setting]タブ>[ API Keys]タブ>[Create API Key]から取得可能です。 詳細画面で[Secret Key]を確認できます。 ②については右上のウォレットアドレスを入力しましょう。 ①②を入力し、ターミナルで実行します。 Dockerで2つ目のコンテナが起動していることを確認できました。 以下のログが表示されていれば、サーバ実行中であることを確認できます。 Listening on https://localhost:3005. Manage your Engine from https://thirdweb.com/dashboard/engine. 3.2.3 Engineコンテナをthirdwebダッシュボードに登録 続いて、立ち上げたコンテナをthirdwebダッシュボードに登録します。 thirdwebダッシュボード >[Engine]タブ>[Manage]タブ>[Import]で http://localhost:3005 を入力します。 初回アクセス時には以下の手順が必要です: ブラウザで http://localhost:3005/json を入力してアクセスします。 「あなたの接続はプライベートではありません」という警告ページが表示されます。 [詳細を表示]をクリックし、[ローカルホストに進む (安全ではありません)]を選択します。 これにより JSON ファイルが表示され、ブラウザがローカルのEngine インスタンス に接続できます。 Dockerとうまく連携できていれば、詳細画面を開くことができます。 これで トランザクション プロバイダの構築は完了となります。 3.3 NFT配布用のバックエンドウォレットの作成 続いてNFTの取得をガスレスで実行するために配布用のバックエンドウォレットを作成します。 3.3.1 バックエンドウォレットの作成 thirdwebダッシュボード >[Engine]>[Manage]>[登録した インスタンス ]>[Overview]でネットワークを「Sepolia」に変更し、[Create]をクリックします。 バックエンドウォレットが作成されました。 3.3.2 ガス代の準備 バックエンドウォレットに少量のガス代を入れてあげましょう。 ※ 保有 するtoken(SepoliaネットワークのEthereum token)がない場合は、こちらで少量マイニングを行いログイン時に作成したウォレットアドレスに補充しましょう。 https://sepolia-faucet.pk910.de/ 右上のログイン時に作成したウォレットアドレスをクリック Sendから送付先であるバックエンドアドレスを入力し、送付します。0.01もあれば十分です。 tokenの送付が完了しました。 これでNFT配布用のバックエンドウォレットの作成と準備は完了です。 3.4 Webページの作成 次に、サービスのフロントエンドとなるWebページを作成します。今回はthirdwebのReact SDK を使用しています。 3.4.1 サンプルコードのクローンと依存関係のインストール まず、thirdwebの GitHub より、サンプルコードをクローンします。 https://github.com/thirdweb-example/engine-minting-api クローンしたルート ディレクト リに移動し、[npm install]で依存関係をインストールします。 [npm install encoding]と[npm install pino -pretty]も同様に実行してください。 3.4.2 evnファイルの準備 以下のとおり「.env.local」ファイルを作成し、クローンしたフォルダへ配置します。①~④は各自で取得しましょう。 ENGINE_URL="http://localhost:3005" THIRDWEB_CLIENT_ID="①" NEXT_PUBLIC_THIRDWEB_CLIENT_ID="①" THIRDWEB_SECRET_KEY="②" BACKEND_WALLET_ADDRESS="③" NFT_CONTRACT_ADDRESS="④" NEXT_PUBLIC_NFT_CONTRACT_ADDRESS="④" ①②は thirdwebダッシュボード >[Setting]タブ>[ API Keys]タブ>[Create API Key]から取得可能です。 詳細画面で①②を確認できます。 ③は先ほど作成したバックエンドアドレスを入力してください。 ④は先ほど作成したNFTの コントラ クトアドレスを入力してください。 3.4.3 ソースコード の編集①(page. tsx ) クローンした ソースコード の中で、[src]>[app] フォルダ内の page. tsx ファイルを以下のように編集します。この編集で、ログイン認証の ユーザーインターフェース をカスタマイズし、ユーザーのNFT 保有 情報を表示する機能を追加しています。 "use client"; import { ConnectWallet, ThirdwebProvider, useAddress, inAppWallet, ja, useContract, useNFTBalance, } from "@thirdweb-dev/react"; export default function Home() { return ( <ThirdwebProvider activeChain="sepolia" clientId={process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID} locale={ja()} supportedWallets={[ inAppWallet({ auth: { options: ["google"], }, }), ]} > <ClaimPage /> <Component /> </ThirdwebProvider> ); } function ClaimPage() { const userWalletAddress = useAddress(); const onClick = async () => { const resp = await fetch("/api/claim", { method: "POST", body: JSON.stringify({ userWalletAddress }), }); if (resp.ok) { alert(`🎉 A reward has been sent to your wallet: ${userWalletAddress}`); } }; return ( <main className="flex flex-col gap-y-8 items-center p-24"> <h2 className="text-4xl font-extrabold"> NFT EngineAirDrop Demo </h2> <ConnectWallet theme={"dark"} btnTitle={"wallet接続"} modalTitle={"demo test"} switchToActiveChain={true} modalSize={"compact"} welcomeScreen={{ title: "Welcome Screen Title", subtitle: "Welcome Screen SubTitle", img: { src: "https://raw.seadn.io/files/6b9077cdd2b43af0bef05fb98c63369a.png", width: 150, height: 150, }, }} modalTitleIconUrl={""} showThirdwebBranding={false} /> {userWalletAddress && ( <button className="bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-4 rounded-lg" onClick={onClick} > ✨Get NFT </button> )} </main> ); } function Component() { const userWalletAddress = useAddress(); const { contract, isLoading: isLoadingContract } = useContract(process.env.NEXT_PUBLIC_NFT_CONTRACT_ADDRESS); const tokenId = 0; const { data: ownerBalance, isLoading: isLoadingBalance, error } = useNFTBalance(contract, userWalletAddress, tokenId); if (isLoadingContract || isLoadingBalance) { return ( <div className="flex flex-col p-16 items-center font-extrabold"> <p>Loading...(接続後に保有枚数を表示します)</p> </div> ); } if (error) { const errorMessage = error.message ? error.message : "An unknown error occurred"; return <div>Error: {errorMessage}</div>; } // すべてが読み込まれたら、所有者の残高を表示 return ( <div className="flex flex-col gap-y-4 items-center font-extrabold"> <p>NFT獲得まで約1分程度かかります</p> <p>Wallet Address: {userWalletAddress}</p> <p>Balance: {ownerBalance ? ownerBalance.toString() : "N/A" }</p> </div> ); } ※ログイン インターフェイス のカスタマイズについては こちら を参考にしました。 ※ コントラ クト情報の取得は thirdwebダッシュボード >[Contract]タブ>[作成した コントラ クトの詳細画面]>[Code Snippets]タブを参考に ソースコード へ組み込みました。 3.4.4 ソースコード の編集②(route.ts) 続いて、クローンした ソースコード の中で、[src]>[app]>[ api ]>[claim] フォルダ内にある route.ts ファイルを以下のように編集します。この変更により、作成したNFT コントラ クトからclaim API を呼びだすように設定しています。 import { NextResponse } from "next/server"; const { BACKEND_WALLET_ADDRESS, NFT_CONTRACT_ADDRESS, ENGINE_URL, THIRDWEB_SECRET_KEY, } = process.env; export async function POST(request: Request) { if ( !BACKEND_WALLET_ADDRESS || !NFT_CONTRACT_ADDRESS || !ENGINE_URL || !THIRDWEB_SECRET_KEY ) { throw 'Server misconfigured. Did you forget to add a ".env.local" file?'; } const { userWalletAddress } = await request.json(); const resp = await fetch( `${ENGINE_URL}/contract/sepolia/${NFT_CONTRACT_ADDRESS}/erc1155/claim-to?simulateTx=false`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${THIRDWEB_SECRET_KEY}`, "x-backend-wallet-address": BACKEND_WALLET_ADDRESS, }, body: JSON.stringify({ receiver: userWalletAddress, tokenId: "0", quantity: "1", }), } ); if (resp.ok) { console.log("[DEBUG] ok", await resp.json()); } else { console.log("[DEBUG] not ok", await resp.text()); } return NextResponse.json({ message: "Success!" }); } ※Engineを経由した コントラ クトへのNFT発行指示は thirdwebダッシュボード >[Engine]タブ>[登録した インスタンス の詳細画面]>[Exploer]タブ>[ERC1155]を参考に ソースコード へ組み込みました。 以上でWebページの作成は完了です。 3.5 実行 ここまで準備が完了したら[npm run dev]コマンドで動かしてみましょう。 このような画面が表示されると思いますので、ウォレット接続をしてみましょう。 ログイン インターフェイス はシンプルにしています。 Google でサインイン。 任意の Google アカウントで今回作成したデモアプリ専用のウォレットを作成しましょう。 画面上の方で新規にweb3ウォレットアドレスが生成されました。 このweb3ウォレットはデモアプリの SNS ログインを通じてアクセス可能です。他のweb3アプリケーションへの接続や他のweb3ウォレットアプリケーションへ持ち出す方法もありますが、今回は実装していません。 画面上の赤枠部分をクリックすると、基本的な操作(tokenの送付など)が可能です。 画面下の方では接続ウォレットアドレスと 保有 NFTの枚数が正しく表示されています。 NFTの取得をしてみましょう。 ポップアップでNFT獲得のメッセージが表示されます。 数秒後に画面をリロードしてみると、Balanceがアップデートされました。 Engineの方をみてみると、 トランザクション が1件発生しているのがわかります。バックエンドウォレットからはガス代が引かれ、ユーザーはガスレスでNFTを獲得できています。 ブロックチェーン の取引を誰でも参照できる ブロックエクスプローラ にて同様に取引のログが確認できました。 トランザクション の詳細画面では TokenID: 0 のNFTが0xF6...のアドレスへ1枚転送されていることがわかります。 これで実装はすべて完了です。お疲れ様でした!🎉 4. 総括 今回はthirdwebサービスを組み合わせてweb3サービスを構築してみました。 DockerやReact、thirdwebAPIなど初めて触れる内容で苦労する点はありましたが、今回のテックスタックに馴染みの少ない自分でも一通り構築することができ、開発の容易性を実感することができました。 4.1 類似サービスでの構築 今回はthirdwebサービスを利用した構築を行いましたが、他のソリューションでも同様にNFT配布システムが構築可能であり、ベストプ ラク ティスの模索やメリット比較のために検証する価値は十分にあるかと思います。気になるソリューションは下記しておきます。 Web3Authサービスでの構築 thirdweb ホスティング 環境のEngine利用(有料) Engineに代替するOpenzepplinDefenderの利用 thirdwebのAccount Abstraction機能を利用したガスレス トランザクション  など 4.2 今回実装したサービスがweb3に与える影響 今回実装したthirdweb ConnectはWallet as a Service(WaaS)を提供し、thirdweb Engineはガスレス トランザクション を可能にするソリューションです。これらのサービスは、ユーザービリティを向上させ、web3エコシステムにおいて重要な役割を果たしています。これらのサービスモデルが広く採用されれば、次のような影響が期待できます: web3技術の普及 WaaSの利用により、技術的知識がないユーザーでもウォレットの作成・管理が容易になります。これはweb3技術へのアクセス障壁を大幅に低下させ、より多くの人々がこの技術を利用できるようにします。さらに、ウォレットの存在をユーザーに意識させずに済むUI/UXを構築可能で、 ブロックチェーン 技術の煩雑さを隠しつつその利点を活かしたシステムを構築できる点も大きなメリットです。 ウォレット管理の強化 セキュリティ対策されたWaaSサービスを利用することで、個々のユーザーや企業はより簡単に、より安全にデジタルアセットの保管が可能となります。 多様な ブロックチェーン 展開 WaaSプロバイダーは、異なる ブロックチェーン ネットワークに対応しており、接続が容易であるため、ユーザーに対して複数の ブロックチェーン サービスの展開が可能となります。 イノベーション の促進 今後のWaaSサービスプロバイダに依存する話となりますが、WaaS企業が提供する機能や API の拡張により、開発者は新しいタイプのアプリケーションやサービスをより迅速に開発することが可能になります。これは、web3領域の イノベーション を加速し、多様なビジネスモデルの創出を促進する可能性を秘めています。 このように、WaaSはweb3の採用を広げる重要な推進力となり得るため、その発展と普及に期待をしています。 現在、 電通 総研は web3領域のグループ横断組織 を立ち上げ、web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! (電通総研の採用ページ) 執筆: @himeno 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
アバター
はじめに 電通 総研 X(クロス) イノベーション 本部 の三浦です。 2024年8月6日、 AWS Bedrockを通じてClaude 3.5 SonnetとClaude 3 Haiku が東京リージョンで利用可能になりました。 これを機に「 Amazon Bedrock」の試用をしてみました。 Amazon Bedrockとは Amazon Bedrockは、 AWS が提供する生成AIサービスプラットフォームです。様々なAIモデルを API 経由で、サーバーレスに利用できます。 トーク ン単位の課金となるため試用レベルなら非常に安く、学習環境や、検証環境でも試しやすいコスト感となっています。 参考までに2024/08/15時点の費用を転記します。最新情報は、 AWSのページ からご確認ください。Claude 3.5 Sonnetについては、US、日本で費用差はないようです。 Amazon Bedrockのより詳しい情報については、下記を参照するのがよいかと思います。 Amazon Bedrock Overview 【Amazon Bedrock Series #01】【AWS Black Belt】 Claude 3.5 Sonnetの特徴 Claude 3.5 Sonnetは、Anthropic社が開発した最新のAI 言語モデル です。以下の特徴があります: 高い日本語性能:OpenAI社のGPT-4シリーズよりも日本語性能が高いと評価 コストパフォーマンス:Claude 3 Opusよりも賢いとされつつも、価格は5分の1 バージニア 北部で試用する Claude 3.5 Sonnetが AWS 東京リージョンで利用可能になったことは、日本のユーザーにとって朗報です。 しかし海外リージョン特に米国のリージョンでは最新のAIモデルや機能がいち早く提供されることが多いです。 また、某 クラウド サービスでは、最新モデルのGPT-4o miniが海外リージョンでのみ利用可能となっており、かつ、旧来使われていたモデルが廃止される、といった問題も起きています。 したがって、明確な制約(金融系の規制、個人情報に関わるような要件)がない限り、最新モデルの恩恵を受けるために海外リージョンの利用も検討した方がいいと考えているため、東京リリースを無視して今回は バージニア 北部で試用します。 モデル有効化 AWS マネジメントコンソールから Amazon Bedrockの管理画面に移動し、「モデルアクセス」より各モデルの利用申請が可能です。 数分程度の待機でアクセスが付与されました。 (なお、プライベートではCommand R+も愛用しているため有効化しております。Claude 3.5 Sonnet とは、別軸の大きなメリットがある素敵なモデルです。) マネジメントコンソールから使う こんな感じでさらっと試用できます。 なお、OpenAI 版の ChatGPT 等でよく用いる生成 AI サービスにネットを検索させて最新情報を踏まえながら文章を作成することはできません。 しかし、リーチが容易な AWS 環境で高性能なClaude 3.5 Sonnetにアクセスできることは大きな意味があると考えています。また、ある程度の設定は必要ですが、下記のようなナレッジベースという仕組みもあり特定の知識領域に限定する場合簡易にRAG対応が可能そうです。 まとめ ということで、OpenAIやClaudeの使用のためには稟議申請が必要な組織もあるかと思いますが、既存の AWS 環境でも手軽に最新の生成AIを試せるというご紹介でした。 私たちは一緒に働いてくれる仲間を募集しています! クラウドアーキテクト 執筆: @miura.toshihiko 、レビュー: 寺山 輝 (@terayama.akira) ( Shodo で執筆されました )
アバター
こんにちは。金融ソリューション事業部の姫野です。 本ブログでは、Magic、thirdweb、Web3Authの3サービスの 秘密鍵 管理方式に焦点を当て、深く掘り下げていきます。各サービスの主要な アーキテクチャ と処理フローを、図解も含めて詳細に理解していきましょう。 1. 前回のおさらい 2.Magicの秘密鍵管理方式 2.1 アーキテクチャ:分散型キーマネジメントシステム(DKMS) 2.1.1 DKMSの機能と利点 2.2 処理フロー 2.2.1 秘密鍵生成と保管 各ステップの詳細 2.2.2 トランザクションへの署名 各ステップの詳細 2.3 セキュリティと暗号化 3. thirdwebの秘密鍵管理方式 3.1 アーキテクチャ: シャミアの秘密分散(SSS) 3.1.1 SSSの機能と利点 3.2 処理フロー 3.3 セキュリティと暗号化 4. Web3Authの秘密鍵管理方式 4.1 単要素認証(MPC) 4.1.1 アーキテクチャ 4.1.2 機能と利点 4.1.3 処理フロー 4.2 分散鍵生成としきい値署名スキーム(DKGとTSS) 4.2.1 アーキテクチャ 4.2.2 機能と利点 4.2.3 処理フロー 分散鍵生成(DKG: Distributed Key Generation)プロセス しきい値署名スキーム(TSS: Threshold Signature Scheme)プロセス: 5. 結局、どの秘密鍵管理方式がいいの? 5.1.1 Magicの優位性 5.1.2 thirdwebの優位性 5.1.3 web3authの優位性 5.1.4 Magicのリスクや懸念点 5.1.5 thirdwebのリスクや懸念点 5.1.6 Web3Authのリスクや懸念点 6. 最後に 今後の調査と検証計画 1. 前回のおさらい 前回のブログ( web3入門:ウォレットサービスとは?主要3サービスを徹底比較 )では、web3ウォレットの基本とそれをユーザーフレンドリーに提供する主要な3つのサービス(Magic、thirdweb、Web3Auth)の概要を比較しました。 サービス 認証方法 セキュリティ 対応 SDK Magic 分散型キーマネジメントシステム (DKMS) ローカルで 秘密鍵 の生成、復元が行われる 秘密鍵 は分散保管せず、 AWSのKMSサービス を利用し、 秘密鍵 の復号化キー(ユーザーマスターキー)を HSM に保存 DedicatedWallet thirdweb シャミアの秘密分散 (SSS) ローカルで 秘密鍵 の生成、復元が行われる 秘密鍵 はシャミアの秘密分散により、複数shareに分けられ分散保管 shareAはユーザーデ バイス 、shareB,Cは AWS HSM に保管 Connect Web3Auth 単要素認証 (MPC) ローカルで 秘密鍵 の生成、復元が行われる 秘密鍵 はMPCにより、ネットワーク(5/9)分散保管 Plug & Play CoreKit 分散鍵生成と しきい値 署名スキーム (DKGとTSS) 完全な 秘密鍵 は生成されず、署名情報のみがTSSプロセスにより復号される キーはDKGプロセスにより、複数Factorに分けられ分散保管 Factor1はMPCネットワーク、Factor2はユーザーデ バイス に保管 CoreKit 次のセクションからは各サービスの主要な アーキテクチャ と処理フローを、図解も含めて詳細に解説します。 2.Magicの 秘密鍵 管理方式 まずは一番シンプルなMagicの 秘密鍵 管理方式について、みていきましょう。Magicは Amazon のキーマネジメントサービスを利用した非保管型の鍵管理方式を実現しています。 2.1 アーキテクチャ :分散型キーマネジメントシステム( DKMS ) Magicの アーキテクチャ は、 Amazon のキーマネジメントサービス( AWS KMS )をベースにしたハードウェアセキュリティモジュール( HSM )を使用して、非保管型(ノン カストディ アル)の認証システムを構築しています。これにより、ユーザーは自らのデ バイス 上で 秘密鍵 を生成し、その 秘密鍵 は AWS KMSによって保護された環境で安全に保管されます。 補足説明 KMS(Key Management Service) : AWS のKMSは、鍵の管理と暗号化を クラウド で安全に行うためのサービスです。 HSM(Hardware Security Module) :HSMは、暗号化キーを生成、管理、保護するためのハードウェアデ バイス です。高度なセキュリティが求められる環境で使用されます。 ノン カストディ アル(Non-Custodial) :ユーザーが自分の 秘密鍵 を管理する仕組みを指します。サービスプロバイダーが 秘密鍵 にアクセスできないため、セキュリティが向上します。 Magicが採用しているこのDKMS アーキテクチャ は、 特許 を取得しており、中央集権型のサーバーに頼ることなく、分散型のセキュリティを実現しています。 2.1.1 DKMSの機能と利点 シンプルな非保管型(ノン カストディ アル)の認証システム : 完全な 秘密鍵 はユーザーのデ バイス 上のみで取り扱われ、外部で扱われることはありません。また、 AWS KMSのみを使ったシンプルな アーキテクチャ 構造であるため、リスクポイントが少ないです。 HSMによるマスターキーの厳格な保護 : 秘密鍵 の暗号化と復号は AWS のKMSが管理するハードウェアセキュリティモジュール(HSM)で行われますが、HSMに 秘密鍵 自体は保存されず、マスターキーのみが保管されます。マスターキーはユーザーのみがアクセス可能で、非常にセキュアな Amazon セキュリティ技術によって厳重に管理されており、外部からのアクセスは完全に遮断されています。 2.2 処理フロー Magicの非保管型キーマネジメントシステムの処理フローについて解説します。 2.2.1 秘密鍵 生成と保管 以下の図2Aはユーザーがサインアップした際に、 秘密鍵 を生成し、安全に暗号化して保存するプロセスを詳細に示しています。このフローに沿って詳細を解説します。 ( 出典:Non-custodial tool for building decentralized computer applications ) 各ステップの詳細 サインアップイベント(Step 202) : ユーザーがクライアントアプリケーション(Client 110)でサインアップを開始します。 新規ユーザー登録(Step 204) : サーバー(Server 125)は、ユーザーの情報を受け取り、新規ユーザーを登録します。 時間制限付きアクセス トーク ンの生成(Step 206) : サーバーは、 サードパーティ サービス(3rd Party Service 155)を利用して時間制限付きアクセス トーク ンを生成します。 アクセス トーク ンの返却(Step 208) : サーバーは、生成したアクセス トーク ンをクライアントに返却します。 スコープ付き資格情報の交換(Step 210) : クライアントは、サーバーにアクセス トーク ンを返送し、スコープ付き資格情報を取得します。 秘密鍵 の生成(Step 212) : クライアントは、自身のデ バイス 上で 秘密鍵 を生成します。 HSMでの暗号化(Step 216) : クライアントは、生成した 秘密鍵 を専用のHSM(Hardware Security Module)で暗号化します。HSMは、ハードウェアレベルでの高いセキュリティを提供します。 暗号化された鍵の返却(Step 218 ) : HSMは、暗号化された 秘密鍵 をクライアントに返却します。 暗号化された鍵のアップロード(Step 220) : クライアントは、暗号化された 秘密鍵 をサーバーにアップロードします。 このフローにより、ユーザーは SNS にログインするだけで、 秘密鍵 の生成からHSMへの安全な保管までが自動的に行われ、鍵の管理負担が軽減されます。 2.2.2 トランザクション への署名 以下の図2Bでは、既にログインしているユーザーが トランザクション を実行する際に、 秘密鍵 を安全に使用し署名を行うプロセスです。このフローに沿って詳細を解説します。 ( 出典:Non-custodial tool for building decentralized computer applications ) 各ステップの詳細 アクセス トーク ンの交換(Step 230) : ログイン済みのユーザーは、クライアントアプリケーション(Client 110)で時間制限付きのアクセス トーク ンを交換します。 時間制限付きスコープ資格情報の返却(Step 232) : サーバー(Server 125)は、 サードパーティ サービス(3rd Party Service 155)を介して、クライアントに時間制限付きスコープ資格情報を返却します。 暗号化された 秘密鍵 の復号(Step 234) : クライアントは、取得したスコープ資格情報を使用して、暗号化された 秘密鍵 をHSM(Hardware Security Module)に送信します。HSMはユーザーマスターキーを使用して、暗号化された 秘密鍵 を復号化します。 復号された 秘密鍵 の返却(Step 236) : HSMは、復号された 秘密鍵 を直接クライアントに返却します。 トランザクション データの署名(Step 238) : クライアントは、復号された 秘密鍵 を使用して トランザクション データに署名します。 秘密鍵 のメモリからの削除(Step 240) : 署名後、クライアントは 秘密鍵 をメモリから安全に削除します。 署名済み トランザクション データの送信(Step 242) : クライアントは、署名済みの トランザクション データをサーバーに送信します。 ブロックチェーン への トランザクション の提出(Step 244) : サーバーは、署名済みの トランザクション データを ブロックチェーン (Blockchain 185)に提出します。 提出結果の返却(Step 246) : ブロックチェーン は、 トランザクション の実行結果をサーバーに返却します。 結果の返却(Step 248) : サーバーは、実行結果をクライアントに返却します。 これらのフローを通じて、ユーザーは SNS にログインするだけで、安全かつ効率的に ブロックチェーン 上の トランザクション に署名することができます。 2.3 セキュリティと暗号化 セキュアな通信 : Magicでは、サーバーとユーザーブラウザ間で送受信されるすべてのデータはエンドツーエンドで TLS 暗号化されており、サーバーと内部アプリケーション間で送信される秘密情報も TLS で保護されています。内部アプリケーションの秘密情報は、 Hashicorp Vault で安全に管理されています。Hashicorp Vaultは、機密情報の保存、アクセス管理、および動的なシークレットの生成を行うためのセキュアなシステムです。 データの暗号化 : Magicは、 業界標準 の AES-256 暗号化 アルゴリズム を使用して、すべてのデータを暗号化しています。これには、データベースに保存される情報、データのスナップショット(ある時点のデータのコピー)、定期的に行われる自動バックアップ、およびデータのレプリカ(他の場所に保存されるコピー)が含まれます。データの暗号化と復号は、データがストレージに書き込まれる際および読み取られる際に自動的に行われます。 3. thirdwebの 秘密鍵 管理方式 次に、thirdwebの 秘密鍵 管理方式について、みていきましょう。thirdwebは AWS KMSでの鍵管理に加え、シャミアの秘密分散 アルゴリズム を利用した分散保管による鍵管理方式を実現しています。 3.1 アーキテクチャ : シャミアの秘密分散(SSS) thirdwebの アーキテクチャ は、 秘密鍵 をシャミアの秘密分散(SSS) アルゴリズム を用いて分割・管理するセキュアな アーキテクチャ を採用しています。ユーザーのデ バイス で生成された 秘密鍵 は、複数のシャードに分割され、異なる保管方法で安全に管理されます。シャミアの秘密分散では、 秘密鍵 の完全な形がユーザーのデ バイス 外で扱われることはありません。 上述のMagicの アーキテクチャ では 秘密鍵 は分割されずに暗号化されてHSMに保管されていましたが、SSSの仕組みを用いることにより、例え一部のシャードの暗号が解析されたとしても個々のシャードでは完全な鍵としての機能を果たさないため、攻撃者が 秘密鍵 全体を解明することが非常に難しくなります。 補足説明 シャミアの秘密分散(SSS: Shamir's Secret Sharing) : シャミアの秘密分散は、 秘密鍵 を複数のシャード(断片)に分割し、それぞれを別々に保管することでセキュリティを強化する アルゴリズム です。 秘密鍵 を再構築するためには、一定数のシャード( しきい値 k)が必要で、これを満たさないと再構築は不可能となります。 秘密鍵 は特別な数式( 多項式 )を使って分割されます。各シャードは、この数式に数値を代入して計算された結果です。k 個のシャードがそろうと、 ラグランジュ補間 という方法を使って元の数式を再構築します。 ラグランジュ 補間とは、いくつかの点(結果)がわかっているときに、それらの点を通る曲線(数式)を見つける方法です。この方法を使って、各シャードの情報から 多項式 を再計算し、その定数項から 秘密鍵 を復元します。 3.1.1 SSSの機能と利点 秘密鍵 のシャード化とセキュリティの向上 : ユーザーのデ バイス で直接生成される 秘密鍵 は、シャミアの秘密分散 アルゴリズム (SSS)によって3つのシャードに分割されます。これにより、 秘密鍵 全体が一箇所に存在するリスクを排除し、単一シャードでは機能しないため、改ざんや 不正アクセス に対する耐性(耐タンパ性)が向上します。 分散保管によるリスクの低減 : シャードはユーザーのデ バイス とthirdwebのサーバー間で分散して保管され、 AWS KMS ( HSM )を活用した追加の暗号化により、シャードのセキュリティがさらに強化されます。これにより、単一点による漏洩リスクが低下し、全体的なシステムの堅牢性が高まります。 3.2 処理フロー thirdwebのSSSを用いた 秘密鍵 管理プロセスは以下のステップに沿って進みます。 ( 出典:thirdweb Documentation ) ユーザー認証 :ユーザーはメール、ソーシャルログイン、またはカスタム認証メソッドを使用して初めてアプリケーションにログインします。 キーペアの生成 :ログイン後、ユーザーのデ バイス で公開鍵と 秘密鍵 のペアが生成されます。このプロセスは完全にクライアントサイドで行われます。 秘密鍵 のシャード化 :生成された 秘密鍵 はシャミアの秘密分散 アルゴリズム を用いて3つのシャードに分けられます。各シャードは個別に機能せず、 しきい値 以上のシャードがそろって初めて 秘密鍵 を再構成できます。 シャードの保管 : シャードAはユーザーのデ バイス に保存されます。 ウェブアプリケーション の場合はブラウザに、モバイルアプリの場合は セキュアエンクレーブ (デ バイス 内の安全で機密データを保護するために使用される領域)に保存されます。 シャードBはthirdwebによって AWS KMS(HSM)を利用して暗号化され、thirdwebDBに保管されます。マスターキーはthirdweb管理となります。 シャードCはユーザー認証プロセスを通じて AWS KMS(HSM)を利用して暗号化され、thirdwebサーバーに保管されます。マスターキーはユーザーのみ使用可能です。 このフローにより、ユーザーは SNS にログインするだけで、 秘密鍵 の生成からシャミアの秘密分散 アルゴリズム による分散保管までが自動的に行われ、鍵の管理負担が軽減されるとともに、セキュリティが強化されます。 3.3 セキュリティと暗号化 セキュアな通信 : thirdwebのバックエンドとのすべての通信は、 TLS により暗号化され、安全なデータ転送が確保されています。 データの暗号化 : 保存されるデータは AES-256 で暗号化され、万が一のデ バイス 紛失や障害にも備えたバックアップがとられています。 4. Web3Authの 秘密鍵 管理方式 最後に、Web3Authの 秘密鍵 管理方式について解説します。Web3Authは AWS KMSでの鍵管理に加え、複数の アルゴリズム を利用した分散保管による鍵管理方式を実現しています。Web3Authでもシャミアの秘密分散を採用していますが、シャードの保管場所以外はthirdwebと類似するため、本セクションではシャミアの秘密分散の解説は割愛します。 4.1 単要素認証 (MPC) 4.1.1 アーキテクチャ Web3Authの単要素認証(MPC)は、Authネットワークと呼ばれるMPCネットワークをベースにした分散型の参加者群(企業群)が個々に部分鍵を保管し、個々の参加者は 秘密鍵 の全貌を知ることなく 秘密鍵 の生成と使用を共同で行います。 補足説明 マルチパーティ計算(MPC: Multi-Party Computation) : マルチパーティ計算におけるシャードの分割と再構築はシャミアの秘密分散と同様に 多項式 と ラグランジュ補間 がよく用いられます。シャミアの秘密分散と異なる点は、MPCでは複数の参加者が共同で計算を行う点です。 MPCの プロトコル では、秘密情報を特定の数式を用いて部分鍵に分割し、ネットワークの各参加者に送信します。各参加者は自分の部分鍵を持ち、その部分鍵を元に個々の部分計算を行います。再構築の際には、これらの部分計算結果をローカルで集め、統合することで秘密情報を復元可能です。 4.1.2 機能と利点 複数ノードによる安全な分散管理 : Web3Authの単要素認証( SFA )は、MPC(マルチパーティ計算)技術を活用して、 秘密鍵 の断片を複数の計算ノード間で分散させます。このアプローチにより、鍵全体が一箇所に集中するリスクを排除し、耐タンパ性(改ざん耐性)を向上させます。 5/9のコンセンサスメ カニ ズム : Authネットワーク内の5/9コンセンサスメ カニ ズムによって、 秘密鍵 の断片は分散されて保管されます。このメ カニ ズムにより、9つの計算ノードのうち5つが合意に至らないと鍵の断片は再構築できません。コンセンサスメ カニ ズムとは、分散システムにおいて複数のノードが協力して一貫した決定を行うプロセスのことです。これにより、ネットワーク内での合意形成が要求され、攻撃者による 不正アクセス や鍵の漏洩のリスクが著しく低下します。また、このシステムは自律的であり、一つの組織や個人が全ての権限を持つ中央集権的な管理ではないため、単一点の障害から保護する効果があります。 4.1.3 処理フロー 単要素認証の処理フローはとてもシンプルです。ソーシャルログインの後、ローカルで生成された 秘密鍵 はAuthネットワーク内で分散して保管されます。 ( 出典:Web3Auth Documentation ) また、ソーシャルログインについても解説します。 ( 出典:Web3Auth Documentation ) ソーシャルログインの詳細 : ユーザーはWeb3Authのポータルにリダイレクトされます。 Web3Authのポータルからサービスプロバイダを経由して OAuth や JWT で認証を行います。 ユーザーのローカル環境とサービスプロバイダ間でセッション トーク ンのハンドシェイクが行われます。 サービスプロバイダからユーザーの固有情報(JWT RFC のsubjectフィールドに相当)を取得します。 取得した固有情報はハッシュ化され(オプショナル)、 秘密鍵 の一部としてAuth Network内で保管されます。 これらのフローを通じて、ユーザーは SNS にログインするだけで、 秘密鍵 の生成とMPCネットワークへの分散保管が自動的に行われます。これにより、 秘密鍵 が一箇所に集中するリスクを排除し、セキュリティが強化されます。 4.2 分散鍵生成と しきい値 署名スキーム(DKGとTSS) Web3Authではこのセクションで説明する アーキテクチャ のことを「 MPC 」と呼んでいます。Web3Authの「単要素認証」も「 MCP 」もどちらもMPCネットワークを利用しているため、本ブログでは違いを分かりやすく、主要な アーキテクチャ ベースで説明をするために「分散鍵生成と しきい値 署名スキーム(DKGとTSS)」として解説します。 4.2.1 アーキテクチャ DKG(Distributed Key Generation) : DKG アーキテクチャ は、署名に必要な部分鍵を生成するプロセスです。 非同期検証可能シークレットシェアリング(AVSS) と呼ばれる アルゴリズム の改良版を用いて、 トランザクション の署名に必要な部分鍵(Factor1, Factor2, Factor3...)を非同期に複数生成します。その後、これらのFactorは複数のシェアに分割され、分散保管されます。 TSS(Threshold Signature Scheme) : TSS アーキテクチャ は、部分鍵から署名を生成するプロセスです。各シェアはそれぞれのFactorに基づいて独立して署名を生成します。そして、ユーザーデ バイス 上でこれらの個々の署名が集約され、 トランザクション のための最終的な署名が形成されます。 マルチパーティ計算(MPC: Multi-Party Computation) : DKGプロセスによって生成されたFactorの1つは、MPC アーキテクチャ によってさらに分散保管され、MPCネットワークの各ノードは部分鍵を保管します。署名を生成する際、 トランザクション 毎に各ノードが部分鍵をもとに個々の計算を行い、各ノードで独立して部分署名を生成します。生成された部分署名は集約され、最終的な トランザクション の署名が形成されます。重要なのは、部分署名が直接集約されて最終署名が生成され、Factor自体を一度再構築することはないという点です。 4.2.2 機能と利点 完全な 秘密鍵 を生成しない : 本 アーキテクチャ では完全な 秘密鍵 はどの時点でも生成されず、Magicやthirdwebのようにローカルで 秘密鍵 を復号する必要がないため、 秘密鍵 の漏洩リスクがありません。 動的な鍵管理によるセキュリティ強化 : DKGやMPCによって生成されるFactorや部分鍵は トランザクション 毎に動的に生成されます。これにより、攻撃者が一度にアクセスできるのは単一の トランザクション データに限られ、影響を最小限に抑えることができます。また、複数の分散されたシェアから独立して署名が生成されるため、単一のシェアが侵害されても、全体のFactorや トランザクション のセキュリティが維持されます。これにより、攻撃者が全シェアにアクセスせずに トランザクション を偽造することは非常に困難になります。 4.2.3 処理フロー 分散鍵生成(DKG: Distributed Key Generation)プロセス 下図はDKGプロセスの処理フローになります。Factor1の部分は前述したMPCプロセスと類似しており、Factor2,3の部分は前述のSSSプロセスと類似しています。 ( 出典:Web3Auth Documentation ) ユーザー認証 : ユーザーはソーシャルログインによる OAuth 認証を行います。これはユーザーデ バイス 上で行われ、セキュアなログイン情報が生成されます。 キーファクターの生成 : ユーザー認証後、ユーザーの固有情報をもとにWeb3AuthのDKGプロセスにより非同期で複数のキーファクター(Factor1, Factor2, Factor3)を生成します。これらのファクターは、後の署名に必要な要素を形成します。 ファクターの分散 : Factor1 : MPCネットワーク(Auth Network)によりさらに分割・分散保管されます。 Factor2 : ユーザーデ バイス 上に直接保存されます。 Factor3 : ユーザーが リカバリ ーオプションとして設定する場合に生成され、保管されます。 このフローにより、ユーザーは SNS にログインするだけで、複数のキーファクターが生成され、分散保管されます。これにより、 秘密鍵 を完全に生成せずに、各ファクターが安全に管理されます。 しきい値 署名スキーム(TSS: Threshold Signature Scheme )プロセス: 続いて トランザクション へ署名するための しきい値 署名スキームのフローです。 ( 出典:Web3Auth Documentation ) 署名要求 : ユーザーが トランザクション を実行する際、署名要求が発生します。この要求は各シェアに送信され、分散署名プロセスが開始されます。 部分署名の生成 : 各ノードは自身のFactorや部分鍵を用いて トランザクション の部分署名を生成します。これにより、各ノードで独立して部分署名が作成されます。 最終署名の生成 : 生成された部分署名は集約され、完全な署名が形成されます。 このフローにより、ユーザーは SNS にログインするだけで、各部分鍵が独立して署名を生成し、最終的な署名が形成されます。これにより、完全な 秘密鍵 を生成せずに トランザクション の署名が可能となり、セキュリティが強化されます。 5. 結局、どの 秘密鍵 管理方式がいいの? Magic、thirdweb、Web3Auth、それぞれ異なる 秘密鍵 管理方式を採用していますが、それぞれの優位性について考察してみます。 5.1.1 Magicの優位性 Magicは 秘密鍵 を分散保管していない代わりに、設計がシンプルという利点があります。分散保管するとその分構造は複雑になるため、障害リスクも高まります。堅牢な AWS のKMSが管理するHSMを利用しつつ、ユーザーのみが(ユーザー認証によって)鍵にアクセスできる非中央集権な仕組み(DKMS)を実現しています。また、MagicはこのDKMSの特許を取得しており、構造がオープンになっているため、透明で堅牢なシステムを利用したいユーザーに最適といえます。 5.1.2 thirdwebの優位性 thirdwebはシャミアの秘密分散技術を利用しており、 秘密鍵 をシャードに分割し、それぞれ異なる場所に保管することでセキュリティを高めています。ユーザー側ではデ バイス と OAuth認証 の2つのシャード管理が必要になりますが、thirdwebが鍵の1つを管理しているため、ユーザーがどちらか1つを紛失・漏洩した場合でもウォレットを継続利用できます。ユーザーが2つの鍵を、中央集権(thirdweb社)が1つの鍵を管理する構造は、個人での鍵管理に不安のあるユーザーに最適といえます。 5.1.3 web3authの優位性 Web3Authのアプローチは、 秘密鍵 を生成せず、代わりに分散型の動的な計算(MPC)を利用して トランザクション 毎に一時的な署名を生成します。これにより、一度にアクセスできるのは一回の トランザクション データのみとなり、攻撃者による全アクセスのリスクが大幅に低下します。Web3Authでは鍵は中央集権(Web3Auth社)で管理されず、すべてユーザーが管理するため、完全な非中央集権的であるといえます。個人で完全に鍵管理をしたい、または 秘密鍵 の生成リスクをなくしたいユーザーに最適といえます。 一方でそれぞれの想定されるリスクや懸念点についても考察してみます。 5.1.4 Magicのリスクや懸念点 Magicのリスクや懸念点として、 秘密鍵 の保管先がKMSであり、 AWS サービスに アーキテクチャ が依拠していることが挙げられます。これにより、 AWS のサービスが停止した場合に影響を受けるリスクがあります。また、分散保管の仕組みを採用していないため、暗号化した 秘密鍵 が解読された場合、流出するリスクがあります。 5.1.5 thirdwebのリスクや懸念点 thirdwebのリスクや懸念点として、部分鍵の保管先の一つがKMSであり、Magicと同様に AWS サービスに依拠していることが挙げられます。また、部分鍵の保管先の一つがthirdwebであり、thirdwebのインフラに依拠していることも懸念されます。 5.1.6 Web3Authのリスクや懸念点 Web3Authのリスクや懸念点として、完全に非中央集権的な実装になっているため、ユーザーの鍵紛失リスクを全て個人が負うことになります。これにより、ユーザーが自らの責任で鍵管理を行う必要があり、管理ミスが致命的な結果を招く可能性があります。 本セクションでは各サービスの優位性や懸念点を考察しました。どのサービスも企業の認証システムや暗号復号プロセスに依存しているため、リスクを理解し、対応策を講じることが重要です。ユーザーのニーズやリスク許容度に応じて、最適なサービスを選択しましょう。 6. 最後に 今回は 秘密鍵 の管理と利用部分に特化した各サービスの優位性を比較しましたが、その他の観点(導入のし易さや企業の信頼性など)も考慮が必要になります。全体的な観点での比較は以前のブログ「 web3入門:web3ウォレットサービスとは?主要3サービスを徹底比較 」をご参考ください。また、仕組みの違いにより様々なユーザーニーズに対応できる点、どのサービスも優位性がある点は新しい気づきとなりました。 今後の調査と検証計画 Web3AuthのDKGとTSS アーキテクチャ の深掘り : これまでの私の理解では、署名に必要な情報はmessageとprivate_keyの2つでした。messageは任意に変更可能であり、よくTimeStampが利用されるため、一意の署名が生成されます。そして、もう一方の情報である 秘密鍵 が必要と認識していました。しかし、DKGとTSS アーキテクチャ を利用することで、完全な 秘密鍵 なしで署名が可能であり、具体的な アルゴリズム の理解がまだ不足しているため、今後その点について深掘りできたらブログ化したいと思います。 thirdwebを用いた検証 : 本ブログで紹介したthirdwebを用いて、実際に検証を行ったブログ「 web3入門:SNSログインでNFTを獲得!thirdwebを用いたweb3サービスを構築してみた 」を執筆中です。thirdwebは2024年5月に iPhoneのPassKeyによるログインに対応 しており、これによりユーザービリティがさらに向上しています。PassKeyを利用することで、 iPhone の 指紋認証 (Touch ID)や顔認証(Face ID)によるログインが可能となり、ユーザーはより簡単で安全にweb3に接続できます。thirdwebを用いたシステムを構築する際には、この機能も利用できるため、ぜひ上記ブログを参考にしてみてください。 また、未定ではありますがWeb3Authを用いた検証ブログも執筆したいと思っているので、気になる方は是非チェックしてみてください。 現在、 電通 総研は web3領域のグループ横断組織 を立ち上げ、web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! (電通総研の採用ページ) 執筆: @himeno 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
アバター
こんにちは、X(クロス) イノベーション 本部の 米久 保 剛です。 ITの世界は日々進化し続けており、新しい技術や概念が次々と登場しています。このような環境下で、 ITアーキテクト には常に最新の動向を把握し、適切な判断を下すことが求められます。そのために必要不可欠なのが「アーキテクト思考」です。今回は、アーキテクト思考の一つである「メンタルモデルの構築」に焦点を当て、その重要性と活用方法について解説します。 アーキテクト思考とは アーキテクト思考とは、 ITアーキテクト が仕事をする上で身につけておくべき物の考え方です。しかし、その有用性はアーキテクトの枠を超えて、他の職種にも大いに役立つものです。 アーキテクト思考と呼べるものには、今回取り上げる「メンタルモデルの構築」の他にも、「システム思考」「 トレードオフ 分析」「分割統治」など様々なものがあります。 メンタルモデルの構築:アーキテクト思考の要 アーキテクト思考の中でも特に重要なのが「メンタルモデルの構築」です。これは、個々の具体的な事象や経験、出来事を抽象化し、その本質を概念として定義することを指します。簡単に言えば、「要は〜だよね」という認知のモデルを作ることです。 メンタルモデルを構築することで得られる利点は以下のとおりです: 様々な状況下での適切な行動指針が立てやすくなる 新しい問題に対しても、既存の知識を応用しやすくなる 複雑な概念を簡潔に理解し、他者に説明しやすくなる メンタルモデルの実践例:生成AI 生成AIを例に、メンタルモデルの有用性を見てみましょう。生成AIは複雑な技術ですが、その詳細な数学的知識がなくても、効果的に活用することは可能です。 生成AIのメンタルモデルを「学習モデルに基づいて確率的にもっともらしい文章やコードを予測してくれるもの」と定義することで、以下のような利点が得られます: 生成AIの得意・不得意を直感的に理解できる 適切な ユースケース を考案しやすくなる より効果的なプロンプトを作成できる 例えばプロンプトエンジニアリングにおいても、単なるテクニックの集積ではなくその背後にある原理を理解することで、より柔軟で効果的な指示を生成AIに与えることができるようになります。 メンタルモデル構築のコツ メンタルモデルを効果的に構築するためのポイントをいくつか紹介します: 常に「なぜ」を問い続ける 表面的な事象だけでなく、その背後にある理由や原理を考える習慣をつけましょう。 抽象化と具体化を行き来する 具体的な事例から抽象的な概念を導き出し、その概念を別の具体例に当てはめてみる練習をしましょう。 多角的な視点を持つ 一つの事象を様々な角度から観察し、多面的なメンタルモデルを構築することで、より柔軟な思考が可能になります。 定期的にモデルを更新する 技術や環境の変化に応じて、自身のメンタルモデルも更新していく必要があります。 アーキテクト思考を身につけるチャンス アーキテクトがどのような思考で仕事に取り組んでいるかを直接学べる貴重な機会があります。2024年8月23日(金)に開催される「 電通 総研 Architect Meetup」です。このイベントでは、 ITアーキテクト たちがどのようにして複雑な問題に取り組み、解決策を見出しているのかを垣間見ることができます。 イベントの詳細や申し込みは こちら からご確認いただけます。 まとめ アーキテクト思考、特にメンタルモデルの構築は、 ITアーキテクト だけでなくあらゆるITプロフェッショナルにとって価値のあるスキルです。日々の業務や学習の中で意識的にメンタルモデルを構築し活用していくことで、より深い理解と効果的な問題解決が可能になります。 技術の進化が加速する現代において、こうした思考法を身につけることは、キャリアの大きな武器となるでしょう。ぜひ、日々の業務や学習の中でアーキテクト思考を意識し、実践してみてください。そして、「 電通 総研 Architect Meetup」に参加して、プロフェッショナルたちの思考プロセスを直接学ぶ機会を逃さないようにしましょう。 ※本記事は筆者が作成した原案をもとに生成AI(Claude 3.5 Sonnet)が作成した文章に、若干の修正を加えて執筆しました。 私たちは一緒に働いてくれる仲間を募集しています! プロダクトプラットフォーム開発エンジニア(アーキテクト・テックリード) 執筆: @tyonekubo 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
アバター
こんにちは。金融ソリューション事業部の姫野です。本ブログではweb3の重要な役割を担うweb3ウォレットサービス3つを解説します。前半はweb3の説明もしていますので、web3に馴染みがない方も、ぜひチェックしてみてください。 1. web3とは web3で重要な役割を担うウォレット(デジタルウォレット) Web1.0、Web2.0、web3の比較 web3における認証方式 2. web3ウォレット普及の現状 インフラの未整備とセキュリティ不安の現実 UXの課題: セキュリティの課題: 3. ウォレットアズアサービス(WaaS:Wallet as a Service)とは何か? WaaSのメリット 市場への影響 4. 主要3社のWaaSサービス比較:Web3Auth、Magic、thirdweb 5. 結局、どのサービスを使うのがいいの? 5.1 分散管理に特化したWeb3Auth 5.2 認証手段が豊富なMagic 5.3 開発者フレンドリィなthirdweb 6. web3の普及とWaaSの役割 日本におけるweb3ウォレット普及の兆し 暗号通貨ユーザーの普及予想 メタバースとの親和性 1. web3とは web3とはインターネットの新しい形態であり、 ブロックチェーン などの技術を活用した分散型ネットワークによってユーザーが 自らのデータを直接管理 できる技術です。この技術により、 デジタル アイデンティティ やデジタルアセット(例えば、NFTや仮想通貨)を自分のコン トロール 下に置く ことが可能となります。 web3で重要な役割を担うウォレット(デジタルウォレット) web3の環境では、各ユーザーが後述する 秘密鍵 を用いて、IDと残高を個人で管理します。この管理を行うアプリケーションを一般的に「 ブロックチェーン ウォレット」や「web3ウォレット」と呼びます。これらのウォレットは、 ブロックチェーン 上の仮想の個人財布と考えると理解しやすいでしょう。このウォレットを使って、ユーザーは安全にデジタルアセットを保存・交換できます。ウォレットはweb3エコシステム内での取引やインタ ラク ションの中心的な役割を担う、欠かせない存在です。 Web1.0 、 Web2.0 、web3の比較 web3の理解を深めるために、これまでのインターネットとの違いを簡単に解説します。 Web1.0 (一方通行): ユーザーは情報を「読む」だけで、 インタラクティブ な要素はほとんどありません。ウェブサイトは静的であり、主に情報の配布手段として機能していました。 Web2.0 (双方向): ユーザーは情報を「読み」、自ら「書く」(投稿する)ことができます。X(旧 Twitter )や Instagram 、 TikTok などの ソーシャルメディア では、ユーザー生成コンテンツが特徴的で、これによりダイナミックで参加型のウェブ体験が実現されました。 web3(自律分散型): ユーザーは「読み」、「書き」、さらに自分のデータを「所有」します。 ブロックチェーン と分散型テク ノロ ジー がこのステージの核心であり、プライバシーの強化とユーザーのエンパワーメントを促進します。 web3における認証方式 ウォレットを理解する上で必要となる事前知識としてもう1点、web3の認証方式について触れます。web3の認証方式はWeb2とは根本的に異なります。Web2の世界では、ユーザーの アイデンティティ は認証サーバーを通じて管理され、ログイン情報やアクセス権が中央の機関によって承認されます。一方で、web3では、ユーザー自身のクライアント側で生成された 秘密鍵 と 署名 を使用して認証が行われます。これにより、ユーザーは自らの アイデンティティ とデータを完全にコン トロール できます。 秘密鍵 とは、ユーザーだけが知っているデジタルコードであり、以下サンプルのような64桁の16進数で表されます。 4F3EDF983AC636A65A842CE7C7D36497B44A8AF2E6EAFDCE511AFA9E6F1D2B5F 署名 とは、その 秘密鍵 を使って行う一連の計算プロセスです。これにより、 トランザクション がそのユーザーから発されたものであることが証明されます。 web3ではこれらの要素を用いて認証を行う 公開鍵暗号方式 が用いられています。 公開鍵暗号方式 の詳細は本ブログでは割愛しますが、 ブロックチェーンのセキュリティに必要不可欠な鍵「秘密鍵・公開鍵」 がわかりやすく説明しています。 また、ウォレットアプリケーションであるMetaMaskの例を見るとユーザーが トランザクション を行う際やログインを行う際には署名を求められます。この署名プロセスを通じて、ユーザーは自分が行いたいアクションを承認し、その正当性を証明できます。 このようなプロセスは、ユーザーが自らの意志で明確な同意を示し、 トランザクション を実行する新しい形態の認証方法と言えます。 2. web3ウォレット普及の現状 web3を利用する上で中核となるウォレットの 保有 数は全人口のつい数%程度とされており、依然として多くのユーザーが伝統的な金融システムやWeb2のサービスに依存しているのが現実です。web3ウォレットの採用が進んでいない主な理由に、web3環境のインフラ未整備とセキュリティへの懸念が挙げられます。 インフラの未整備とセキュリティ不安の現実 UXの課題: 事前に用意した認証アプリ(ウォレット)が必要: web3環境では、ユーザー自身が上述の 秘密鍵 を用いて トランザクション を発行します。一般的にはウォレットアプリなどをインストールして設定する必要があり、このプロセスは技術的な知識を要求されることが多く、一般ユーザーにとっては大きな障壁となっています。 ガス代の準備: ガス代は、 ブロックチェーン 上で トランザクション を発行する際に必要となる手数料です。ガス代を準備するために、暗号通貨取引所の口座開設や ブロックチェーン 間をまたいだ通貨の交換などを行う必要があります。この準備が煩わしいと感じるユーザーも多く、web3ウォレット普及の障壁となっています。 デ バイス 間連携が困難: 現在のウォレットの多くは、異なるデ バイス 間でウォレットの同期を行うために 秘密鍵 の再登録が必要になります。これにより、ユーザーが複数のデ バイス を使用する際に不便を感じることがあります。 セキュリティの課題: 紛失リスクが高い: 秘密鍵 をユーザー自身で管理する必要があり、この鍵が紛失または盗難にあった場合、関連する資産を完全に失うリスクがあります。 再発行ができない: 伝統的な銀行システムではパスワードやカードを紛失した場合再発行を受けることが可能ですが、 ブロックチェーン 上の 秘密鍵 は一度失われると元に戻すことが不可能です。 これらの課題に対処するためには、よりユーザーフレンドリーなウォレットソリューションが必要です。それにより、web3技術のさらなる普及と安全な利用が期待できます。次のセクションではこれらの課題を解決するWaaSについて解説します。 3. ウォレットアズアサービス(WaaS:Wallet as a Service)とは何か? ウォレットアズアサービス(WaaS)は、 ブロックチェーン 技術を利用した クラウド ベースのサービスで、 秘密鍵 やデジタル資産の管理を簡単にするウォレットのインフラを提供します。これにより、事業者(開発者)はエンドユーザー向けにウォレットの設定や 秘密鍵 の管理を簡素化し、ユーザーは暗号通貨やデジタル資産をスムーズに取引できるようになります。 WaaSのメリット 1. アクセスの容易さ: WaaSは、ユーザーが技術的な詳細を理解していなくても、直感的なインターフェースを通じてweb3ウォレットを使用可能です。例えば、 Google loginなどの一般的なIDサービスとの連携や、事業者側によるガス代の代行などが可能になるため、前述のウォレットやガス代の準備が不要となり、従来のWeb2同様に馴染みのあるインターフェースを通じてweb3サービスを利用可能です。これにより、専門知識がないユーザーでも安心してweb3テク ノロ ジー を利用できます。 2. セキュリティの強化: WaaSプロバイダーは、 業界標準 のセキュリティ プロトコル (例: TLS )と暗号化技術(例:AES、HSM)を使用してユーザーのデータを保護します。これにより、ユーザーは自分の 秘密鍵 を直接管理するリスクを負わずに、資産の安全性が保証されます。 3. コスト効率の向上: WaaSを利用するとハードウェアウォレットの購入や維持の必要がなく、基本的にユーザーは無料でweb3ウォレットを利用できます。一方、開発者側にとっても複雑なインフラスト ラク チャの構築や管理が不要であり クラウド サービスとしての利用料のみで済みます。 4. 統合性と拡張性: WaaSは、異なる ブロックチェーン プラットフォームとの互換性を提供し、一つのインターフェースから複数の ブロックチェーン にアクセス可能です。これにより、ユーザーはさまざまなデジタル資産を一元管理でき、将来的な技術の進化にも柔軟に対応可能です。 市場への影響 WaaSは、web3の採用を加速するだけでなく、新たなビジネスモデルやサービスの創出を促します。例えば、 フィンテック 企業はWaaSを利用して、従来の銀行システムでは提供できない新しい金融サービスを開発可能です。また、企業はWaaSを通じて顧客にカスタマイズ可能なウォレットソリューションを提供し、ユーザーエクス ペリエ ンスを向上させることが可能です。 WaaSの普及により、web3技術の 潜在的 な利用シナリオが拡大し、デジタル経済の未来が形作られています。これは、単なるテク ノロ ジー の進化以上のものであり、経済全体の構造変革をもたらす可能性があります。 4. 主要3社のWaaSサービス比較:Web3Auth、Magic、thirdweb Web3Auth 、 Magic 、 thirdweb は、WaaS市場において顕著な成長を遂げている主要なプレイヤーです。これらの企業はそれぞれ独自のアプローチでサービスを展開し、web3の採用を促進しています。このセクションでは、それぞれのサービスの企業背景や特徴を比較・分析します。 まず、以下の表は各社の企業情報、監査機関の認証、導入実績、および資金調達状況についての概要を示しています。 サービス 企業情報 監査機関の認証 導入実績 調達情報 Web3Auth Web3Auth社(旧Torus Labs Pte Ltd.) 2018年設立、非上場企業 シンガポール 企業、 シンガポール 拠点 CCPA CPRA GDPR SOC 2 日産自動車 、McDonald'sなど 500社以上、1500万以上のウォレットを作成 seriesAで$1300万(2022/1/12) Magic Magic Labs, Inc. 2020年設立、非上場企業 アメリ カ企業、サンフランシスコ拠点 SOC 2 Type 2 SOC 3 Type 2 ISO 27001 HIPAA Compliance 7-eleven, Forbesなど 2500万以上のウォレットを作成 17万以上の開発者 戦略ラウンドで$5200万、累計$8000万超 (2023/5/31) thirdweb thirdweb社 2020年設立、非上場企業 アメリ カ企業、サンフランシスコ拠点 なし ( コントラ クト部分はmacro社監査済) Shopify, Raribleなど 7万以上の開発者 seriesAで$2400万(2022/8/25) 上記の表から、いくつかの重要なポイントが見えてきます。 企業の成熟度と市場への浸透 : Web3Authは2018年に設立され、多くの企業に導入されていることから、市場における成熟度と安定した存在感が伺えます。一方で、Magicとthirdwebは2020年に設立された比較的新しい企業でありながら、短期間で顕著な成長と注目を集めています。 監査と認証の重要性 : Web3AuthとMagicは複数の重要なセキュリティ認証を取得しており、企業としての信頼性とセキュリティ対策の厳格さがうかがえます。これに対して、thirdwebは コントラ クトの監査には注力していますが、企業全体としてのセキュリティ認証はまだ報告されていません。 thirdwebのドキュメント にはIn-App Wallet(Connectの主要機能)について、90日以内に GDPR およびCCPAの監査に準拠すると記載がありますが、90日以前からこの記載のまま変わっていません。また、thirdwebは2023年11月に スマートコントラクトの脆弱性 が発覚し、多くの機能が影響を受け、利用ユーザーは対応を迫られました。 脆弱性 の詳細は明かされていませんが、所感としてthirdweb コントラ クトは開発者にとって開発しやすいよう、柔軟で拡張性を持たせたスマート コントラ クトの構造になっており、その点が要因の1つと推測しています。 資金調達と企業成長の相関 : 資金調達額とそれによって可能となるリソースの拡大は、各社の技術開発と市場展開の速度に影響を与える要素となります。Magicの大規模な資金調達は、同社の製品開発と市場戦略を加速させている可能性が高いといえます。 導入実績に見る市場影響 : 導入実績は各サービスが市場でどれだけ受け入れられているかを示す バロメータ ーです。特にWeb3Authの500社以上の導入実績はその技術とサービスが広範囲にわたり採用されている証拠です。ただし、各社詳細な情報が少なく、同じ指標で測れないため、正確な優劣はつけられません。 次に、各サービスの認証方法、セキュリティ特性、および対応 SDK を比較します。表では聞きなれない用語も多いかと思いますが、詳細は 次のブログ で解説しますので、本ブログでの解説は割愛します。 サービス 認証方法 セキュリティ 対応 SDK Web3Auth 単要素認証 (MPC) ローカルで 秘密鍵 の生成、復元が行われる 秘密鍵 はMPCにより、ネットワーク(5/9)分散保管 Plug & Play CoreKit シャミアの秘密分散 (SSS) ローカルで 秘密鍵 の生成、復元が行われる 秘密鍵 はシャミアの秘密分散により、複数shareに分けられ分散保管 shareAはMPCネットワーク、shareBはユーザーデ バイス に保管 Plug & Play CoreKit 分散鍵生成と しきい値 署名スキーム (DKGとTSS) 完全な 秘密鍵 は生成されず、署名情報のみがTSSプロセスにより復号される キーはDKGプロセスにより、複数Factorに分けられ分散保管 Factor1はMPCネットワーク、Factor2はユーザーデ バイス に保管 CoreKit Magic 分散型キーマネジメントシステム (DKMS) ローカルで 秘密鍵 の生成、復元が行われる 秘密鍵 は分散保管せず、 AWS のKMSサービスを利用し、 秘密鍵 の復号化キー(ユーザーマスターキー)をHSMに保存 DedicatedWallet thirdweb シャミアの秘密分散 (SSS) ローカルで 秘密鍵 の生成、復元が行われる 秘密鍵 はシャミアの秘密分散により、複数shareに分けられ分散保管 shareAはユーザーデ バイス 、shareB,Cは AWS HSMに保管 Connect Web3Auth、Magic、そしてthirdwebはそれぞれ独自の強みを持ち、WaaSサービスを展開しています。次のセクションでは、各サービスがどのようなプロジェクトに最適かについて考察します。 5. 結局、どのサービスを使うのがいいの? 秘密鍵 管理方式の比較については 次のブログ で解説していますが、今回比較した3社のサービスについてどのサービスを利用するのが最適か、各社の特徴を元に解説します。 5.1 分散管理に特化したWeb3Auth Web3Authは、MPC(Multi-Party Computation)技術を核とした 秘密鍵 の分散保管に特化しています。Web3AuthはMPC技術に加えて分散鍵生成(DKG)や しきい値 署名スキーム(TSS)など先進的な暗号方式を採用しており、これによりユーザーの 秘密鍵 はさらに分散保管され強固に保護されます。セキュリティを重視するユーザーや、分散された管理体制を求める場合にはWeb3Authが適しています。 5.2 認証手段が豊富なMagic Magicは、ユーザービリティとセキュリティのバランスを最適化しており、 秘密鍵 は分散保管せずにキーマネージドサービス(KMS)のみを採用しています。このアプローチによりセキュリティは確保される一方で、ユーザーや企業にとっては理解しやすく、利用しやすい鍵管理プロセスが提供されています。また、MagicはWebAuthn認証に対応しているため、YubiKey(セキュリティデ バイス )やTouchIDを用いた認証も可能です。送られてくるリンクをクリックするのみでログイン可能なパスワードレス認証など、ログイン認証が豊富なのが特徴です。これらの多様な認証を取り入れる場合はMagicが最適といえます。 5.3 開発者フレンドリィなthirdweb thirdwebは、開発者にとっての使いやすさを重視しています。豊富な SDK やサンプルコード、直感的な API が提供されており、web3アプリケーションの開発を迅速に進めることが可能です。さらに、モジュラー式のアプローチにより、カスタマイズや拡張が容易であるため、開発者が特定の要件に応じたソリューションを簡単に構築できます。豊富な コントラ クトや トランザクション プロバイダなどウォレットサービス以外のweb3サービスを展開している点も魅力です。開発の効率化と柔軟性を求めるなら、thirdwebが最適な選択です。 6. web3の普及とWaaSの役割 web3技術の進化と普及は、デジタル経済に革命をもたらす可能性があります。Wallet as a Service(WaaS)はこの変革の中核をなすサービスであり、暗号通貨とデジタルアセットの管理を容易にすることでより広範なユーザー層にweb3を普及させ、さらに新しいビジネスモデルやサービスの創出が期待されています。 日本におけるweb3ウォレット普及の兆し 2023年、 岸田文雄 首相が 税制改正 を含むweb3促進政策を発表しました。これにより、創造的な産業が国内に留まるようになり、web3の活動が活発化すると期待されています。詳細は こちら 。 暗号通貨ユーザーの普及予想 暗号通貨の普及はインターネットの普及と似た推移をしています。現在の推定では約2億から3億の暗号通貨ユーザーが存在し、2026年から2027年までにその数は10億人に達する可能性があると予測されています。詳細は こちら 。 メタバース との親和性 私の所属するWEB3グループは メタバース という新たなデジタル空間にweb3技術を取り入れる計画を進行中です。この取り組みにより、ユーザーは自身のデジタル アイデンティティ とデジタルアセットを個人でコン トロール し、仮想世界での経済活動をより自由かつ安全に行えます。WaaSの利用は、このような新しいアプリケーションにおいても中心的な役割を果たし、 メタバース 内での トランザクション や アイデンティティ 管理を効率的に行うために重要な役割を担います。 このようにWaaSは、web3テク ノロ ジー の普及を加速するだけでなく、その利用の幅を広げる重要な役割を担います。将来的には、WaaSが提供する様々な機能とサービスがさらに洗練され、web3の世界がより多くの人々に受け入れられるようになる未来を期待しています。 関連ブログも執筆中ですので、ぜひご覧ください。 web3入門:最新の鍵管理手法を徹底解説!主要3サービスの秘密鍵管理方式を比較 web3入門:SNSログインでNFTを獲得!thirdwebを用いたweb3サービスを構築してみた 現在、 電通 総研は web3領域のグループ横断組織 を立ち上げ、web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! (電通総研の採用ページ) 執筆: @himeno 、レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
こんにちは。コミュニケーションIT事業部 ITソリューション部の英です。 普段はWebアプリや スマホ アプリの案件などを担当しています。あと、趣味でAIを勉強しています。 みなさん、 株式投資 はお好きですか?私は大好きです。 最近の株式市場は大荒れですね。 気になりますよね。未来の株価。 タイムトラベルの能力があれば、真っ先に株価を見に行くでしょう。 まあ、そんなことができたならば今ごろ会社員なんてやっておりません。 前置きはこれくらいにして、今回は過去の株価データを使って未来の株価を予測するモデルを作成してみましょう。 過去の株価の数値だけを参考値として予測するため、決して皆さまの資産運用の参考にはしないようにお願いいたします。 資産運用には世界情勢などの"不確かさ"が付き物です。そこが面白さでもあります。投資は自己責任です。 ちなみに AWS で時系列予測を扱う場合、アプローチの方法としては2つあります。 SageMakerを使用する (線形学習モデル) Amazon Forecastを使用する (マネージドサービス) 今回の記事ではSageMakerで予測する手法を解説し、次回は Amazon Forecastで予測させます。 予測する題材はS&P500の指数にしましょう。著名な投資家 ウォーレン・バフェット が推奨している米国の代表的な 株価指数 ですね。 ここから本題 STEP1:ライブラリの準備 今回使用するライブラリをまとめてインストールしましょう。 yfinanceはYahoo Financeから株価データを取得するためのライブラリです。 # ライブラリのインストール pip install yfinance pandas numpy matplotlib boto3 sagemaker scikit-learn # ライブラリのインポート import yfinance as yf import pandas as pd import numpy as np import matplotlib.pyplot as plt import boto3 import sagemaker import json from sagemaker import image_uris from sagemaker.tuner import HyperparameterTuner, ContinuousParameter, IntegerParameter, CategoricalParameter from matplotlib.dates import DateFormatter, YearLocator STEP2:データの取得 yfinanceでS&P500の過去10年間のデータを取得します。 GSPCはS&P500インデックスの ティッカーシンボル です。 # データ取得 sp500 = yf.Ticker('^GSPC') df = sp500.history(period='10y') df.interpolate(method='linear', inplace=True) STEP3:特徴量の計算 今回は 移動平均線 (50日、100日、200日)と ボリンジャーバンド 、 終値 を使用します。 すべて 終値 (Close)をもとに計算が可能な情報です。 # 移動平均やボリンジャーバンドなどの特徴量を計算 def calculate_features(df): df['MA_50'] = df['Close'].rolling(window=50).mean() df['MA_100'] = df['Close'].rolling(window=100).mean() df['MA_200'] = df['Close'].rolling(window=200).mean() df['BB_MID'] = df['Close'].rolling(window=20).mean() df['BB_STD'] = df['Close'].rolling(window=20).std() df['BB_UPPER'] = df['BB_MID'] + (df['BB_STD'] * 2) df['BB_LOWER'] = df['BB_MID'] - (df['BB_STD'] * 2) calculate_features(df) print("特徴量を計算しました: MA_50, MA_100, MA_200, BB_MID, BB_UPPER, BB_LOWER") STEP4:ラベル作成 現在の日付から100日後の株価をFuture Closeに設定します。 これにより、モデルが予測するターゲットが決まります。 # 100日後の株価をラベルとして設定 df['Future Close'] = df['Close'].shift(-100) print("ラベルとして100日後の株価を設定しました。") STEP5:データのクリーニング データの欠損値を補間します。 連続で0が続く場合、一発で補間することができないため、二段階で補間しています。 # データの補間を行い、欠損値がないことを確認 initial_missing_values = df.isnull().sum().sum() df.interpolate(method='linear', inplace=True) final_missing_values = df.isnull().sum().sum() print(f"補間前の欠損値の数: {initial_missing_values}") print(f"補間後の欠損値の数: {final_missing_values}") if final_missing_values > 0: df.ffill(inplace=True) df.bfill(inplace=True) final_missing_values = df.isnull().sum().sum() print(f"再補間後の欠損値の数: {final_missing_values}") if final_missing_values > 0: raise ValueError("データに欠損値が含まれています。補間が正しく行われていることを確認してください。") STEP6:データ分割 過去10年間のデータのうち直近の200日を除いて、学習データとします。 直近の200日から100日をハイパーパラメーターのチューニングに使用します。(100日後の株価(正解)を含むデータ) 直近の100日は予測に使用します。 # データをトレーニング、検証、およびテストデータに分割 train_data = df.iloc[:-200] # 直近の200日を除いた過去データ validation_data = df.iloc[-200:-100] # 直近の200日から100日を除いたデータ test_data = df.iloc[-100:].copy() # 直近の100日 test_data['Future Close'] = np.nan # Future Closeは空にする print("データの分割が完了しました。") STEP7:ローカルとS3への保存 確認用にローカルに保存し、学習用にS3にアップロードしておきます。 # トレーニングデータの保存 csv_train_data = train_data[['Close', 'MA_50', 'MA_100', 'MA_200', 'BB_MID', 'BB_UPPER', 'BB_LOWER', 'Future Close']] csv_train_data.to_csv('prepared_sp500_train_data.csv', index=False, header=False) # 検証データの保存 csv_validation_data = validation_data[['Close', 'MA_50', 'MA_100', 'MA_200', 'BB_MID', 'BB_UPPER', 'BB_LOWER', 'Future Close']] csv_validation_data.to_csv('prepared_sp500_validation_data.csv', index=False, header=False) # テストデータの保存(Future Closeを含まない) csv_test_data = test_data[['Close', 'MA_50', 'MA_100', 'MA_200', 'BB_MID', 'BB_UPPER', 'BB_LOWER']] csv_test_data.to_csv('prepared_sp500_test_data.csv', index=False, header=False) # S3へのアップロード sagemaker_session = sagemaker.Session() bucket_name = sagemaker_session.default_bucket() s3_train_data = f's3://{bucket_name}/prepared_sp500_train_data.csv' s3_validation_data = f's3://{bucket_name}/prepared_sp500_validation_data.csv' s3_test_data = f's3://{bucket_name}/prepared_sp500_test_data.csv' boto3.resource('s3').Bucket(bucket_name).Object('prepared_sp500_train_data.csv').upload_file('prepared_sp500_train_data.csv') boto3.resource('s3').Bucket(bucket_name).Object('prepared_sp500_validation_data.csv').upload_file('prepared_sp500_validation_data.csv') boto3.resource('s3').Bucket(bucket_name).Object('prepared_sp500_test_data.csv').upload_file('prepared_sp500_test_data.csv') STEP8:学習データのプロット 過去10年分のS&P500のスコアをプロットします。 # データの可視化 plt.figure(figsize=(12, 6)) # 全期間のデータをプロット plt.plot(df.index, df['Close'], label='Actual Close', color='blue', linewidth=2) # 学習データと検証データのプロット plt.plot(train_data.index, train_data['Close'], label='Train Data Close', color='lightblue', linewidth=1) plt.plot(validation_data.index, validation_data['Close'], label='Validation Data Close', color='orange', linewidth=2) # 移動平均とボリンジャーバンドをプロット plt.plot(df.index, df['MA_50'], label='50-Day MA', color='orange', linewidth=1) plt.plot(df.index, df['MA_100'], label='100-Day MA', color='green', linewidth=1) plt.plot(df.index, df['MA_200'], label='200-Day MA', color='brown', linewidth=1) plt.plot(df.index, df['BB_MID'], label='Bollinger Mid Band', color='red', linestyle='--', linewidth=1) plt.plot(df.index, df['BB_UPPER'], label='Bollinger Upper Band', color='purple', linestyle='--', linewidth=1) plt.plot(df.index, df['BB_LOWER'], label='Bollinger Lower Band', color='gray', linestyle='--', linewidth=1) # x軸のメモリを1年ごとに設定 ax = plt.gca() ax.xaxis.set_major_locator(YearLocator()) ax.xaxis.set_major_formatter(DateFormatter('%Y')) plt.xlabel('Date') plt.ylabel('Close Price') plt.title('S&P 500 Close Price with MA and Bollinger Bands') plt.legend() plt.grid(True) plt.show() print("学習データをプロットしました。") ・青色:過去10年のデータ ・水色:ト レーニン グに使用したデータ(10年分-直近の200日) ・橙色:チューニングに使用するバリデーションデータ STEP9:ハイパーパラメーターチューニングの設定 以下のように設定し、ト レーニン グを開始します。 今回使用する フレームワーク はSageMakerのLinear Learnerです。予測タイプは回帰(regressor)を選択します。 → linear-learner ランダムサーチ手法で線形学習モデル(Linear Learner)のハイパーパラメーターをチューニングしていきます。平均二乗誤差(validation:mse)を使って、予測値と実際の値のズレを評価します。 hyperparameter_rangesでハイ パーパー ラメーターの範囲を指定しています。 → HyperparameterTuner 線形学習モデルのハイパーパラメータは他にもたくさんありますが、チューニングが可能なパラメータは限られているので注意してください。 → ハイパーパラメーター一覧 # ランダムサーチの準備 role = sagemaker.get_execution_role() container = image_uris.retrieve(region=sagemaker_session.boto_region_name, framework='linear-learner', version='1') linear = sagemaker.estimator.Estimator( image_uri=container, role=role, instance_count=1, instance_type='ml.m4.xlarge', output_path=f's3://{bucket_name}/linear_learner_output', sagemaker_session=sagemaker_session ) linear.set_hyperparameters( predictor_type='regressor' ) hyperparameter_ranges = { 'learning_rate': ContinuousParameter(0.0001, 0.1), 'mini_batch_size': IntegerParameter(16, 128), 'l1': ContinuousParameter(0.0, 0.1), 'wd': ContinuousParameter(0.0, 0.1) } objective_metric_name = 'validation:mse' tuner = HyperparameterTuner( estimator=linear, objective_metric_name=objective_metric_name, hyperparameter_ranges=hyperparameter_ranges, max_jobs=20, max_parallel_jobs=3, objective_type='Minimize' ) train_input = sagemaker.inputs.TrainingInput( s3_data=s3_train_data, content_type='text/csv' ) validation_input = sagemaker.inputs.TrainingInput( s3_data=s3_validation_data, content_type='text/csv' ) # チューニングジョブの開始 tuner.fit({'train': train_input, 'validation': validation_input}) tuner.wait() # 最適なハイパーパラメータとモデル番号の出力 best_estimator = tuner.best_estimator() best_hyperparameters = best_estimator.hyperparameters() best_model_name = best_estimator.latest_training_job.name print("最適なハイパーパラメータ:", best_hyperparameters) print("最適なモデル番号:", best_model_name) チューニングが始まると並列で3ジョブ、合計で20ジョブ実行されます。 その中でもっとも優れているものを最終的に使用します。 STEP10:ト レーニン グ STEP9で導き出したbest_estimatorで再学習します。 # 最適なハイパーパラメータを使用してモデルをトレーニング best_estimator.fit({'train': train_input, 'validation': validation_input}) STEP11:モデルのデプロイ ト レーニン グが完了したので、デプロイします。 # 学習済みのモデルをデプロイ linear_predictor = best_estimator.deploy(initial_instance_count=1, instance_type='ml.m4.xlarge') print("最適なハイパーパラメータを使用してモデルをデプロイしました。") STEP12:予測結果のプロット test_data(直近100日)をlinear_predictorに渡して、1~100日後の株価を予測させます。 # test_dataを使用して予測 test_predictions = [] for i in range(len(test_data)): features = test_data[['Close', 'MA_50', 'MA_100', 'MA_200', 'BB_MID', 'BB_UPPER', 'BB_LOWER']].iloc[i].values.tolist() print(f"Test features (day {i}): {features}") # 特徴量を出力 payload = { 'instances': [{'features': features}] } result = linear_predictor.predict(json.dumps(payload), initial_args={'ContentType': 'application/json'}) prediction = json.loads(result.decode('utf-8'))['predictions'][0]['score'] print(f"Prediction for test day {i}: {prediction}") # 予測結果を出力 test_predictions.append(prediction) # test_dataの予測結果のプロット plt.figure(figsize=(14, 7)) plt.plot(df.index, df['Close'], label='10-Year Actual Close', color='blue') plt.plot(test_data.index, test_data['Close'], label='Test Data Close', color='green') # 100日後の位置にプロットするためにインデックスをシフト test_pred_index = pd.date_range(start=test_data.index[-1], periods=101, freq='B')[1:] plt.plot(test_pred_index, test_predictions, label='Predicted Close for Test Data', color='orange') plt.xlabel('Date') plt.ylabel('Close Price') plt.title('S&P 500 Close Price and Predictions for Test Data') plt.legend() plt.grid(True) plt.show() 以下のようにプロットされました。 内訳は以下のとおりです。 ・青色:ト レーニン グデータ ・緑色:テストデータ(100~1日前のデータ) ・橙色:未来の100日間の予測データ ※注意:これは一つの予測に過ぎず、未来の株価を正確に予測するものではございません。占い感覚でご覧ください。 さいごに 今回はS&P500の過去データを使って未来の株価を予測しました。 結果としては、ポジティブ(?)な予測結果となっているかと思います。 過去10年間は上昇傾向にありましたから、その傾き(トレンド)はしっかり学習できているように見受けられます。 より高度な予測を行いたい場合は、VIX指数や雇用統計など 株価指数 以外の情報も特徴量に加えると良いかと思います。 これからも AWS ×AI関連の検証記事をたくさん書いていきます。 ↓ のスターを押していただけると嬉しいです。励みになります。 最後まで読んでいただき、ありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! コミュニケーションIT事業部 執筆: 英 良治 (@hanabusa.ryoji) 、レビュー: @kobayashi.hinami ( Shodo で執筆されました )
アバター
こんにちは、 電通 総研、X イノベーション 本部 AIトランスフォーメーションセンター所属の徳原光です。 以前、 Android スマホ でAIモデルを運用するために、Pandasの特徴量計算のコードをKotlin DataFrameを使用してKotlinに移植するための変換表を公開しました。 今度は、特徴量計算のコードをSwiftに移植するため、pandasと TabularData の変換表を作成しました。 Pandas ⇔ TabularData変換表 自分がよく使用していた処理から(Pandas的に)基本的なものを抜粋しています。無理やり表にしているので改行が変ですがご容赦ください。 処理 pandas TaburalData ドキュメント データフレーム生成 df = pd.DataFrame({ "A": [2, 4, 6, 8, 10], "B": [3, 6, 9, 12, 15], "C": [5, 10, 15, 20, 25], "G": ["a", "b", "c", "a", "b"] }) var df = DataFrame(dictionaryLiteral: ("A", [2, 4, 6, 8, 10]), ("B", [3.0, 6.0, 9.0, 12.0, 15.0]), ("C", [5, 10, 15, 20, 25]), ("G", ["a", "b", "c", "a", "b"])) ? データフレーム読み込み df = pd.read_ csv ('hogehoge. csv ') let fileURL = Bundle.main.url(forResource: "hogehoge", withExtension: " csv ") else {    fatalError("File not found")}do {    let df = try DataFrame(contentsOfCSVFile: fileURL, options: options)    print("\(df)")} catch {    print("Error loading CSV : \(error)")} ? 冒頭表示 df.head() df.prefix(5) 公式ドキュメント データフレームを縦に結合 pd.concat([df1, df2], axis=0) df1.append(df2) ? 列を取得 df[”A”] df[”A”] ? 行を取得 df.loc[0] df.rows[0]df.rows[2...5] 公式ドキュメント 行数を取得 len(df) df.rows.count 公式ドキュメント 条件にあうデータを抽出 df.loc[df["G"] == "a", :] df.filter(on: "G", String.self, { $0 == "a" }) ? カラムの値を更新 df[”A”] = df[”A”] - 2 df["A"] = df["A"].map{ $0! - 2 } ? カラム名 のリストに該当するカラムを選択 list_col = ["A", "B"]df[list_col] df[ [ "A", "B" ] ] ? カラム追加 df['D'] = 1df['D'] = df['C'] - df['B'] df.append(column: Column(name: "Integers", contents: [1, 1, 1, 1, 1])) ? カラムを削除 df. drop ("A") df.removeColumn("A") ? カラム名 をリストで取得 df.columns print(df.columns.map { $0.name }) ? カラム内の値をリストで取得 df[”A”].to_list() df[”A”].map { $0 as! Double } df[”A”].map { $0 as! Int} ? カラム名 を変更 df.rename(columns={'A': 'K'}) df2.append(column: Column(name:"K", contents: df["A"].map { $0 as! Int})) «df2.removeColumn("A") ? 列の値を編集 df[”A”] = df[”A”] - 5 df[”A”] = df["A"].map{ $0! - 5 } ? 集計 df.groupby('G').agg(["count", "sum", "mean" ]) df.grouped(by: "G").sums("B", Double.self, order: .descending) df.grouped(by: "G").means("B", Double.self, order: .descending) 公式ドキュメント 外部結合 result = pd.merge(df1, df2, on='D', how='inner') df2.joined(df3, on: ("A")) df2.joined(df3, on: ("A")) ? TabularDataの使用感 SwiftのTabularDataでAIモデル運用のための特徴量計算を実装した感想は、当たり前ですが、 Python ×Pandasと同じような使用感で実装を進めることはできませんでした。 やはり Python 環境ではPandasだけではなくnumpyやmatplotlibといったライブラリも提供されているので、単純なPandasとTabularDataの機能差だけではなく、データサイエンスや単純な 数値計算 に関連するライブラリの充実度やネットに公開されている情報量の差によって思うようにSwift移植が進まない場面が多かったです。 学習データとなるネットの情報がすくないので当然ですが、生成AIによるコード生成もあまり有効ではありませんでした・・・。 Kotlin DataFrameと比較してどうだったかというと、Kotlin DataFrameと比較してもかなり苦戦しましたね。 Python →Kotlin→Swiftと実装していたので、だんだん難易度が上がって情報不足に慣れながら実装できたので良かったですが、TabularDataはKotlin DataFrameと比較して公式が出している情報量も少なく、さらに Github 上で公開されているTabularDataを使った ソースコード の量も少なかったです。 また、Kotlinでの実装は Java の豊富なライブラリを使用できることができますが、Swiftではそうはいかず、IoT機器としてAIモデルを運用するとしたら、 Android より iOS のほうが難易度が高いと感じました。 Android と IOS での 機械学習 モデルの運用 ちなみにモデルの運用は、 Android では ONNX Runtime 、 iOS では Core ML を使用しました。モデルのメタ情報について Xcode の GUI で確認できる、運用のためのクラスが自動で生成されるという点でCoreMLのほうが使用感は良かったですね。 Python で提供されている 専用のモジュール でモデルを出力し、そのデータを Xcode 上でプロジェクトの適当なグループに追加すれば、あとはモデル名と同名のクラスを呼びだすだけで使えてかなり 工数 を節約できました。 ただ、Flutterプロジェクトで使用するとプロジェクト開発言語を Objective-C からSwiftに明示的に設定を変えないと 動かない問題 や、対応している 機械学習 モデルが少ない問題(LightGBMが使えない)、対応していてもモデルの提供ライブラリのバージョンを落とさないと使えない問題がCoreMLにはありました。 TabularDataを使用するために確認しておいたほうがいい情報 developer.apple.com では、TabularDataの紹介動画が公開されています。TabularDataを使って住所情報を分析するデモンストレーションが行われているので、こちらは必ず確認したほうが良いと思います。 developer.apple.com あと、こちらの記事はサンプルコードが載っているのでよく見ていました。 swdevnotes.com いかのサイトに今回の変換表を作るにあたって使用したサンプルコードをおいています。 pickerlab.net とにかく情報不足に陥るので、活用できる情報は一通り見ておくことをお勧めします。とりあえず、今回のAIモデルを スマホ で運用するという1件を通して実装スキルが向上したなと感じました。 ちなみに、実装後のアプリの挙動はどうだったかというと、 スマホ のスペック不足を心配していましたが快適です。やはり最近のモバイル端末は一昔前のPC並に高いので、定番のモデルでしたら運用できますね。 最後に、私が所属しているAIトランスフォーメーションセンターでは、一緒に働いてくださる方を募集しております。こちらのページに採用に関する内容がまとめられております。また、カジュアル面談の募集もこちらのページからできますので、是非ご覧ください。 aitc.dentsusoken.com それでは。 執筆: @tokuhara.hikaru 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
アバター
こんにちは。X(クロス) イノベーション 本部 クラウド イノベーション センターの柴田です。 この記事では Amazon EKS マネージド型ノードグループの更新動作におけるEviction API の実行状況を確認する方法をご紹介します。 背景・課題 Eviction APIの実行状況を確認する方法 デモ おわりに 背景・課題 Amazon EKS マネージド型ノードグループでは、ノードグループのバージョンや設定を更新すると、 マネージド型ノードの更新動作 に記載された手順に従い、自動的に各ノードをローリングアップデートで再作成してくれます。 マネージド型ノードグループの更新動作は以下の4つのフェーズから構成されます。 セットアップフェーズ スケールアップフェーズ アップグレードフェーズ スケールダウンフェーズ このうちアップグレードフェーズの具体的な手順は以下のとおりです。 1 アップグレードフェーズには、次の手順があります。 ノードグループのために設定されている使用不可の最大数を上限として、アップグレードが必要なノードをランダムに選択します。 ノードから Pods をドレインします。 Pods が 15 分以内にノードを離れず、強制フラグがない場合、 PodEvictionFailure というエラーが表示され、アップグレードフェーズは失敗します。このシナリオでは、 update-nodegroup-version リク エス トで強制フラグを適用して、 Pods を削除できます。 すべての Pod が削除された後にノードを遮断し、60 秒間待ちます。これは、サー ビスコ ントローラーがこのノードに新しくリク エス トを送信しないようにするためと、アクティブなノードのリストからこのノードを削除するために行われます。 遮断されたノードの Auto Scaling グループに終了リク エス トを送信します。 以前のバージョンの起動テンプレートでデプロイされたノードグループ内にノードが存在しなくなるまで、以前のアップグレード手順を繰り返します。 手順2に注目してください。各ノードについて Eviction API を使用したPodの退避が15分以内に完了しない場合、ノードグループの更新動作が失敗してしまいます。 ノードグループの更新動作の失敗を避けるには以下の2つの方法があります。 方法1. 強制フラグを有効にします。強制フラグを有効にすると、ノード上にまだ退避が完了していないPodが存在していても、手順3へ進み、そのノードを終了します。 方法2. 退避が完了していないPodを把握して手動で対応します。 方法1だと重要なワークロードのPodが強制的に停止してしまう恐れがあります。そのため今回は方法2を採用して、Podを強制的に停止するか、それともノードグループの更新動作を失敗させるかを自分たちで判断したいと思います。 しかし残念ながら現時点では AWS のマネジメントコンソールやawscliでは退避が完了していないPodの一覧を確認することができません。 Eviction API の実行状況を確認する方法 ではどうしたらよいでしょうか。 Podの退避はkube-apiserverの Create Eviction を呼び出して Eviction API を実行することで行われます。 POST /api/v1/namespaces/{namespace}/pods/{name}/eviction この API のレスポンスは以下のとおりです。 2 200 OK :この場合、退去が許可されると Eviction サブリソースが作成され、PodのURLに DELETE リク エス トを送るのと同じように、Podが削除されます。 429 Too Many Requests :PodDisruptionBudgetの設定により、現在退去が許可されていないことを示します。しばらく時間を空けてみてください。また、 API のレート制限のため、このようなレスポンスが表示されることもあります。 500 Internal Server Error :複数のPodDisruptionBudgetが同じPodを参照している場合など、設定に誤りがあり退去が許可されないことを示します。 よってkube-apiserverの監査ログに以下の条件を満たすログが記録されていた場合、そのときそのPodの退避に失敗したことがわかります。 requestURI が /api/v1/namespaces/{namespace}/pods/{name}/eviction responseObject.code が 4xx または 5xx マネージド型ノードグループのアップグレードフェーズでは、あるPodの退避に失敗した場合、約1分後に再びそのPodの退避を試みます。 3 例えば、同じPodに対して上述の監査ログが15件以上記録されている場合、それはそのPodの退避が15分以内に完了しなかったことを意味します。 Amazon EKSではコン トロール プレーンのログをCloudWatch Logsへ出力できます。 4 この中にはkube-apiserverの監査ログも含まれています。よって、CloudWatch Logsに格納されたkube-apiserverの監査ログに対して、Eviction API の失敗に関するログを検索することで、退避が完了していないPodを把握できます。 デモ 試しにCloudWatch Logs Insightsを使ってマネージド型ノードグループの更新動作中のkube-apiserverの監査ログを見てみましょう。 5 使用するクエリは以下のとおりです。 6 fields @timestamp, @message | filter @logStream like "kube-apiserver-audit" | filter requestURI like /\/api\/v1\/namespaces\/.*\/pods\/.*\/eviction.*/ | filter (responseStatus.code like /4\d\d/ or responseStatus.code like /5\d\d/) | display @logStream, requestURI, responseObject.message | stats count(*) as retry by requestURI, responseObject.message 結果は以下のとおりです。 Pod productpage-v1-6dd7865bcd-vbz5r についてEviction API の失敗に関するログが 9 件記録されています。このことから、このPodは何らかの理由 7 で退避に連続して失敗しており、原因に応じた何らかの対応が必要かもしれないことがわかります。 おわりに この記事では Amazon EKS マネージド型ノードグループの更新動作におけるEviction API の実行状況を確認する方法をご紹介しました。最後までお読みいただき、ありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! クラウドアーキテクト 執筆: @shibata.takao 、レビュー: @kobayashi.hinami ( Shodo で執筆されました ) マネージド型ノードの更新動作 - Amazon EKS より引用。 ↩ APIを起点とした退避 | Kubernetes より引用。 ↩ これは AWS の公式ドキュメントに記載されている情報ではありません。実際にマネージド型ノードグループの更新動作を試した際のkube-apiserverの監査ログの内容からこのように推測しています。 ↩ 詳細は Amazon EKS コントロールプレーンのログ記録 - Amazon EKS をご参照ください。 ↩ kube-apiserverの監査ログのデータ量は多いです。そのためCloudWathc Logs Insightsでkube-apiserverの監査ログを検索する際、検索対象の期間をあまり長くすると、CloudWatch Logs Insightsの利用料が高額になる恐れがあります。ご注意ください。 ↩ このクエリは Amazon EKS ノードグループの更新の失敗に関する問題をトラブルシューティングする | AWS re:Post に記載されたクエリを参考にしています。 ↩ マネージド型ノードの更新動作 - Amazon EKS や Amazon EKS ノードグループの更新の失敗に関する問題をトラブルシューティングする | AWS re:Post でPodの退避に失敗する既知の原因について紹介されています。 ↩
アバター
金融ソリューション事業部 事業推進ユニット の 石沢です。ソフトウェア開発者の皆さんは「横展開調査」「水平展開調査」「ヨコテン」などの言葉を聞いたことはありますか? (本記事では以下「横展開調査」と記述します) 部門の勉強会で、改めて「横展開調査」について整理したので、本記事ではその内容をご紹介します。 所属部門ではわりとカジュアルに「横展開調査」という言葉がよく使われているのですが、人によって理解の幅に違いがあるようだったので、勉強会で取り扱ってみました。勉強会ではわかっている人、わかっていない人それぞれの理解の解像度が上がったようです。またよりよい方法についての議論も盛り上がりましたが、その内容はナイショです。 「横展開調査」ってなんだ? 「横展開調査」の2つの種類 「原因に対する横展開調査」 「真因に対する横展開調査」 「横展開調査」を実施する際の注意点 効率よく網羅的にエラーを検出することが目的 調査結果だけではなく、プロセスの検証も重要 おわりに 「横展開調査」ってなんだ? 本記事では システム開発 におけるエラーや欠陥の「横展開調査」の話題について取り扱います。 製造業を中心とした生産技術における「横展開」(調査はつかない)という考え方もあるようです ややこしいのですが、 トヨタ生産方式 (TPS)では「ヨコテン(横展)」という独特のワードがあります(たぶん、 カイゼン を組織に展開する情報共有プロセスのことです) さらにややこしいことに、TPSを参考にしたソフトウェア開発手法「リーン開発(プロセス)」というものがあるのですが、ここに逆輸入されて欧米では「Yokoten」というワードにもなっているようです なお、手元で調べた限り JSTQB /ISTQBやSQuBOKなどには「横展開調査」という言葉の定義はないようです では「横展開調査」とは何なのでしょうか? 改めて、次のように 言語化 しました。 「横展開調査とは、ソフトウェアの品質を効率よく向上させる手法である」 ソフトウェアにはエラー(欠陥)が含まれます。個々のエラーはレビューやテストを実施することで検出できますが、すべてのエラーをレビューやテストで検出すると膨大なコストがかかってしまいます。そこで、検出したエラーに類似する問題を(レビューやテストを省略して)調査することで発見することを横展開調査(または水平展開調査等)と呼びます。ポイントは、効率よく網羅的にエラーを検出することが目的だということです。 エラーの検出には手間がかかります。テストを実施し、結果を検証し、発生した想定外の事象を解析して、原因であるソフトウェアの問題がどこにあるかを特定しなければならないからです(め、めんどくせー!)。そういった手間を省略し、発見済のエラーの特徴に着目して、隠れている他の未発見エラーを(テストの前に)一網打尽にしたい。そのために実施するのが今回説明する「横展開調査」です。 「横展開調査」の2つの種類 大きく分けて「横展開調査」は2種類に分類できます(諸説あります)。それは「原因に対する横展開調査」と「真因に対する横展開調査」です。 「原因に対する横展開調査」 テスト等で検出したエラーの原因(コードの誤り等)の特徴に基づいて、同じような特徴をもつエラーを洗い出すのが「原因に対する横展開調査」です。もう少し丁寧に書くと「ソフトウェアのエラー原因に対する横展開調査」と言えばよいかもしれません。 ひとことでいえば 似たような間違い探し ということになります 「真因に対する横展開調査」 ソフトウェアのエラーは(基本的に)人間によって埋め込まれます(だって人間だもの)。しかし、エラーを埋め込んだ理由を深掘りすることによって、同じような間違いが発見できるかもしれません。これが「真因に対する横展開調査」です。もう少し丁寧に書くと「ソフトウェ アエラ ーの原因を埋め込んだ真の理由に対する横展開調査」というものになります。 例えばソフトウェアのエラーの埋め込み理由が「インプットとなる仕様ドキュメントの記述があいまい」であったとすれば、仕様ドキュメントの他の箇所にあいまいな記述がないか、そしてそこから展開されたコードは正しいかを疑うことによって、未検知のエラーが発見できるかもしれません。 ひとことでいえば 似たようなミス探し と言えるかもしれません 「横展開調査」を実施する際の注意点 さて、横展開調査にはいくつか注意点があります。 効率よく網羅的にエラーを検出することが目的 横展開調査の目的は、効率の良いエラーの検出です。よって、他に効率的/網羅的なエラーの摘出方法があるのであれば、あえて横展開調査を実施すべきではありません。たとえば今後のテストによって類似エラーの網羅的な摘出が高い確率で確約できる場合や、 アーキテクチャ 上の制約で類似エラーが存在しないことを証明できる場合、横展開調査を実施することはむしろムダになります。やみくもに横展開調査をしてはいけません。 調査結果だけではなく、プロセスの検証も重要 横展開調査は、その結果だけではなく調査手法が正しいかについて注意を払ってレビューする必要があります。着目点の正しさと、実際の調査方法(対象と洗い出し方法、範囲)が間違っていると、エラーの検出漏れにつながります。最低限ピアレビューを実施するなどして、作業内容に問題がないかダブルチェックをするようにしましょう。 よくあるチェックポイントはこのようなものだと思います。 「原因に対する横展開調査」と「真因に対する横展開調査」共通 調査方針は妥当か(利用するツール、検索キーワード、フィルタリング方法などの使い方は正しい?) 調査の範囲は妥当か(エラーが含まれる可能性のある範囲を漏れなく調査しているか) 洗い出し結果に漏れはないか(意図した内容が洗い出されているか) 洗い出された結果に対する評価は妥当か(抽出結果に対して OK、NGの評価が正しく出来ているか) 「原因に対する横展開調査」のみ 調査の基点となったエラーは抽出されているか(最初に発見したエラーが見つかっていなければ、調査方法の正しさを疑う必要あり) 「真因に対する横展開調査」のみ 真因は妥当か(エラー埋め込み理由の推定の確 からし さはあるか) こういった点に注意することで、より効率的な成果が達成できるのではないでしょうか おわりに 本記事では勉強会で実施した、 システム開発 におけるエラーの横展開調査についての整理をご紹介してみました。所属組織やプロジェクトによって言葉の定義や考え方は異なるかもしれませんが、参考にしていただければと思います。そこまでやるの?!という意見もあるかもしれませんが、プロジェクトの特性に合わせて考えていけばよいと思います。 最後までお読みいただきありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! 高い品質が要求されるミッションクリティカルなソフトウェア開発を一緒にやりませんか 【金融×IT】プロジェクトマネージャー 【金融×IT】システム アプリケーション・アーキテクト 執筆: Ishizawa Kento (@kent) 、レビュー: @nagamatsu.yuji ( Shodo で執筆されました )
アバター