TECH PLAY

電通総研

電通総研 の技術ブログ

822

こんにちは、ISID 金融ソリューション事業部の孫です。 皆さんはGitで ソースコード の構成管理を行う中で、バイナリファイルのサイズが大きすぎて GitHub などの ホスティング サービスからブロックされたりした経験はないでしょうか。 実は、最近UnrealEngineを使ってゲームを開発しているところで、そうした大容量ファイルの管理問題に直面しました。 こういう問題を解決するために、 Git LFS というアップローチがあります。 今回の記事では、 Amazon S3 (以下 S3)を用いて独自のGit LFS サーバを建てる方法を紹介します。 ※構成イメージは以下の図をご参照ください。 Git LFS とは GitHub でのサイズ制限 によって GitHub では100MBを超えるファイルをブロックします。 サイズ制限を解消するため、大容量ファイルを効率的に扱う「Git LFS 」が利用されます。 Git LFS を利用することで、レポジトリではハッシュのみを管理し、データ自体は別のストレージ(上記図の Amazon S3 )で管理することなどが可能になります。 GitHub でGit LFS を使う場合の選択肢 GitHub では、公式サービスとして GitHub LFS が提供されています。 しかし、 GitHub が提供している LFS サーバーは無料枠でアカウントごとに2GBまで使用することが可能であるものの、それを超えて使用する場合には「data pack」を購入する必要があります。 コストを削減したい為、 GitHub LFS をS3に切り替えることにしました。 S3ベースのGit LFS サーバーの構築にあたり、いくつかOpenSourceがありますが、今回は構築の利便性および導入後の運用性を考慮した上でRudolfsを選定しました。 以下は当初検討になった各OpenSourceです。 lfs-test-server S3と連携する仕組みがない為却下 LFS2S3Proxy 個人の製品ですからセキュリティおよび今後のアップデートを考慮した上で却下 meltingice/Git LFS S3 Ruby で開発した製品ですが、チーム内に Ruby に詳しいメンバーがいない為却下 Rudolfs ★S3をバックエンドとして利用可能、かつDocker化されており運用もしやすい為採用★ 実施手順 以下の流れで、Rudolfsを用いてGit LFS サーバーを構築します。 事前準備 Rudolfsのセットアップ 検証リソースの準備 動作確認 1.事前準備 Dockerをセットアップします 以下のドキュメントに沿ってDocker Desktopをインストールします Linux OS Mac OS Windows OS Docker Composeのインストールを確認します コマンド docker compose version でバージョンが表示されればOKです 一般的にDocker Desktopのインストールパッケージに含まれるはずですが、もし一部の linux パーケージになければ、 こちら に沿ってDocker Compose プラグイン をインストールします その他、Dockerの使い方についての説明は割愛します 「git lfs 」コマンドプライグインをインストールします こちらのページ に沿ってインストールします AWS アカウントがない場合、作成する必要があります 今なら1年間無料で Amazon EC2 やS3を利用できるため、無料枠で大丈夫です AWS コンソールからアクセスキーを作成します AWSドキュメント に沿って作成します AWS コンソールからS3 バケット を作成します AWS S3のバケットの作り方 を参照して作成します S3 バケット 名: 今回は「test- lfs -s3」にしました 2.Rudolfsのセットアップ 基本はRudolfs リポジトリ の Readme を参考にしてセットアップを行います。 Rudolfsの ソースコード をダウンロードします 以下のコマンドで ソースコード をクローンします git clone https://github.com/jasonwhite/rudolfs.git Rudolfsで利用する暗号化キーを生成します openssl rand -hex 32 コマンドで生成します ダウンロードしたRudolfsフォルダに移動して新規に .env ファイルを作成します 事前準備で作成した AWS アクセスキー、暗号化キーとS3 バケット 名を埋め込んで .env ファイルに書きます AWS_ACCESS_KEY_ID=「AWSアクセスキーID」 AWS_SECRET_ACCESS_KEY=「AWSアクセスキー」 AWS_DEFAULT_REGION=「AWSリージョン名」 LFS_ENCRYPTION_KEY=「Rudolfsの暗号化キー」 LFS_S3_BUCKET=「作成したbucket名」 LFS_MAX_CACHE_SIZE=10GB 以下のコマンドを叩いてRudolfsコンテナを起動します docker-compose --env-file .env up -d コンテナの起動ステータスを確認します docker compose ps を叩きます 「STATUS」は running で表示することを確認します 3.検証リソースの準備 GitHub でテスト用 リポジトリ を作成します リポジトリ 名は「test- lfs 」にしました 詳細な手順は リポジトリを作成する を参照して作成してください テス トリポジ トリをクローンします git clone リポジトリのURL test- lfs フォルダに移動し .lfsconfig ファイルを作成します 以下の規則に沿って LFS サーバのURLを定義します git のglobal settingを設定します Readmeでパフォーマンスに関するおすすめ設定です 今回はこの設定によってどの程度の効果が発揮できるまでは検証していません # Increase the number of worker threads git config --global lfs.concurrenttransfers 64 # Use a global LFS cache to make re-cloning faster git config --global lfs.storage ~/.cache/lfs .gitattributes を作成し、該当の LFS ファイルを定義します 今回のテストファイルの拡張子は .test にしました *.test filter=lfs diff=lfs merge=lfs -text テストファイルを作成します GitHub は100MBを超えるファイルをブロックします 大容量ファイルを認識させるため、120MBのテストファイルを作成します # MAC OSコマンド $ mkfile -n 120m bigdata1.test # Linux OSコマンド $ dd if=/dev/zero of=bigdata1.test bs=1M count=120 # Window OSコマンド $ fsutil file createnew "bigdata1.test" 125829120 4.動作確認 Push前にS3 バケット を確認します 何もオブジェクトがないことを確認します Git LFS を使用しない場合のエラーを確認します 先ほど作成した大容量テストファイルをPushします Git LFS を使用しない場合、前述 GitHub でのサイズ制限でエラーになることを確認します # 大容量ファイルをgitに追加してコミットします $ git add -A $ git commit -m "test rudolfs s3 bigdata1" # GitHubへのPushでファイルサイズの超過エラーが発生します $ git push origin main ------エラー情報の抜粋-------- remote: error: Trace: 9256fd02811ab3b1e6f389b62f285e730ea5d39cbff2226baefcd80261028e5b remote: error: See https://gh.io/lfs for more information. ★ remote: error: File bigdata1.test is 120.00 MB; this exceeds GitHub's file size limit of 100.00 MB ★ remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com. --------------------------- Git LFS を初期化します git lfs install 再度大容量テストファイルをPushします Git LFS を使用する場合、Pushが問題なく完了することを確認します $ git push origin main Push後にS3 バケット と GitHub リポジトリ を確認します S3 バケット の中に、オブジェクトが存在することを確認します GitHub リポジトリ の中に、bigdata1.testファイル内容は ハッシュ値 であることを確認します bigdata1.testの中身の ハッシュ値 は、S3 バケット のファイル名と一致していることを確認します ここまで、今回の構成に対する動作確認は完了しました! おわりに というわけで、今回はRudolfs+S3を導入することによって大容量ファイルの構成管理ができて、また リポジトリ のサイズが抑えられました。 またチームで開発する場合は、Rudolfsの暗号キーさえチームメンバー内で一致していれば、各メンバーのS3権限を待たせる運用で進められます。 ただし、暗号キー自体は全てのメンバーに配布する都合で、セキュリティ観点で考慮する必要があります。 本記事ではセキュリティの側面について深掘りしないですが、もう少しセキュアな運用にしたい場合、一つの案として暗号キーの共有ではなくて安全なプライベートネットワーク内にRudolfsコンテナを一つ建て共有するアップローチがあります。 今回の構成は特に AWS プロジェクトにおいてコストを抑えたい方に向いています。 LFS で大容量ファイルを管理した上で、 リポジトリ のCloneやPull、Pushなどの速度が早くなり開発の効率化を図ることができます! 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://github.com/jasonwhite/rudolfs https://git-lfs.com/ https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-git-large-file-storage 執筆: @chen.sun 、 @kase.teruyoshi 、レビュー: @yamashita.yuki ( Shodo で執筆されました )
アバター
こんにちは、ISID 金融ソリューション事業部の孫です。 皆さんはGitで ソースコード の構成管理を行う中で、バイナリファイルのサイズが大きすぎて GitHub などの ホスティング サービスからブロックされたりした経験はないでしょうか。 実は、最近UnrealEngineを使ってゲームを開発しているところで、そうした大容量ファイルの管理問題に直面しました。 こういう問題を解決するために、 Git LFS というアップローチがあります。 今回の記事では、 Amazon S3 (以下 S3)を用いて独自のGit LFS サーバを建てる方法を紹介します。 ※構成イメージは以下の図をご参照ください。 Git LFS とは GitHub でのサイズ制限 によって GitHub では100MBを超えるファイルをブロックします。 サイズ制限を解消するため、大容量ファイルを効率的に扱う「Git LFS 」が利用されます。 Git LFS を利用することで、レポジトリではハッシュのみを管理し、データ自体は別のストレージ(上記図の Amazon S3 )で管理することなどが可能になります。 GitHub でGit LFS を使う場合の選択肢 GitHub では、公式サービスとして GitHub LFS が提供されています。 しかし、 GitHub が提供している LFS サーバーは無料枠でアカウントごとに2GBまで使用することが可能であるものの、それを超えて使用する場合には「data pack」を購入する必要があります。 コストを削減したい為、 GitHub LFS をS3に切り替えることにしました。 S3ベースのGit LFS サーバーの構築にあたり、いくつかOpenSourceがありますが、今回は構築の利便性および導入後の運用性を考慮した上でRudolfsを選定しました。 以下は当初検討になった各OpenSourceです。 lfs-test-server S3と連携する仕組みがない為却下 LFS2S3Proxy 個人の製品ですからセキュリティおよび今後のアップデートを考慮した上で却下 meltingice/Git LFS S3 Ruby で開発した製品ですが、チーム内に Ruby に詳しいメンバーがいない為却下 Rudolfs ★S3をバックエンドとして利用可能、かつDocker化されており運用もしやすい為採用★ 実施手順 以下の流れで、Rudolfsを用いてGit LFS サーバーを構築します。 事前準備 Rudolfsのセットアップ 検証リソースの準備 動作確認 1.事前準備 Dockerをセットアップします 以下のドキュメントに沿ってDocker Desktopをインストールします Linux OS Mac OS Windows OS Docker Composeのインストールを確認します コマンド docker compose version でバージョンが表示されればOKです 一般的にDocker Desktopのインストールパッケージに含まれるはずですが、もし一部の linux パーケージになければ、 こちら に沿ってDocker Compose プラグイン をインストールします その他、Dockerの使い方についての説明は割愛します 「git lfs 」コマンドプライグインをインストールします こちらのページ に沿ってインストールします AWS アカウントがない場合、作成する必要があります 今なら1年間無料で Amazon EC2 やS3を利用できるため、無料枠で大丈夫です AWS コンソールからアクセスキーを作成します AWSドキュメント に沿って作成します AWS コンソールからS3 バケット を作成します AWS S3のバケットの作り方 を参照して作成します S3 バケット 名: 今回は「test- lfs -s3」にしました 2.Rudolfsのセットアップ 基本はRudolfs リポジトリ の Readme を参考にしてセットアップを行います。 Rudolfsの ソースコード をダウンロードします 以下のコマンドで ソースコード をクローンします git clone https://github.com/jasonwhite/rudolfs.git Rudolfsで利用する暗号化キーを生成します openssl rand -hex 32 コマンドで生成します ダウンロードしたRudolfsフォルダに移動して新規に .env ファイルを作成します 事前準備で作成した AWS アクセスキー、暗号化キーとS3 バケット 名を埋め込んで .env ファイルに書きます AWS_ACCESS_KEY_ID=「AWSアクセスキーID」 AWS_SECRET_ACCESS_KEY=「AWSアクセスキー」 AWS_DEFAULT_REGION=「AWSリージョン名」 LFS_ENCRYPTION_KEY=「Rudolfsの暗号化キー」 LFS_S3_BUCKET=「作成したbucket名」 LFS_MAX_CACHE_SIZE=10GB 以下のコマンドを叩いてRudolfsコンテナを起動します docker-compose --env-file .env up -d コンテナの起動ステータスを確認します docker compose ps を叩きます 「STATUS」は running で表示することを確認します 3.検証リソースの準備 GitHub でテスト用 リポジトリ を作成します リポジトリ 名は「test- lfs 」にしました 詳細な手順は リポジトリを作成する を参照して作成してください テス トリポジ トリをクローンします git clone リポジトリのURL test- lfs フォルダに移動し .lfsconfig ファイルを作成します 以下の規則に沿って LFS サーバのURLを定義します git のglobal settingを設定します Readmeでパフォーマンスに関するおすすめ設定です 今回はこの設定によってどの程度の効果が発揮できるまでは検証していません # Increase the number of worker threads git config --global lfs.concurrenttransfers 64 # Use a global LFS cache to make re-cloning faster git config --global lfs.storage ~/.cache/lfs .gitattributes を作成し、該当の LFS ファイルを定義します 今回のテストファイルの拡張子は .test にしました *.test filter=lfs diff=lfs merge=lfs -text テストファイルを作成します GitHub は100MBを超えるファイルをブロックします 大容量ファイルを認識させるため、120MBのテストファイルを作成します # MAC OSコマンド $ mkfile -n 120m bigdata1.test # Linux OSコマンド $ dd if=/dev/zero of=bigdata1.test bs=1M count=120 # Window OSコマンド $ fsutil file createnew "bigdata1.test" 125829120 4.動作確認 Push前にS3 バケット を確認します 何もオブジェクトがないことを確認します Git LFS を使用しない場合のエラーを確認します 先ほど作成した大容量テストファイルをPushします Git LFS を使用しない場合、前述 GitHub でのサイズ制限でエラーになることを確認します # 大容量ファイルをgitに追加してコミットします $ git add -A $ git commit -m "test rudolfs s3 bigdata1" # GitHubへのPushでファイルサイズの超過エラーが発生します $ git push origin main ------エラー情報の抜粋-------- remote: error: Trace: 9256fd02811ab3b1e6f389b62f285e730ea5d39cbff2226baefcd80261028e5b remote: error: See https://gh.io/lfs for more information. ★ remote: error: File bigdata1.test is 120.00 MB; this exceeds GitHub's file size limit of 100.00 MB ★ remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com. --------------------------- Git LFS を初期化します git lfs install 再度大容量テストファイルをPushします Git LFS を使用する場合、Pushが問題なく完了することを確認します $ git push origin main Push後にS3 バケット と GitHub リポジトリ を確認します S3 バケット の中に、オブジェクトが存在することを確認します GitHub リポジトリ の中に、bigdata1.testファイル内容は ハッシュ値 であることを確認します bigdata1.testの中身の ハッシュ値 は、S3 バケット のファイル名と一致していることを確認します ここまで、今回の構成に対する動作確認は完了しました! おわりに というわけで、今回はRudolfs+S3を導入することによって大容量ファイルの構成管理ができて、また リポジトリ のサイズが抑えられました。 またチームで開発する場合は、Rudolfsの暗号キーさえチームメンバー内で一致していれば、各メンバーのS3権限を待たせる運用で進められます。 ただし、暗号キー自体は全てのメンバーに配布する都合で、セキュリティ観点で考慮する必要があります。 本記事ではセキュリティの側面について深掘りしないですが、もう少しセキュアな運用にしたい場合、一つの案として暗号キーの共有ではなくて安全なプライベートネットワーク内にRudolfsコンテナを一つ建て共有するアップローチがあります。 今回の構成は特に AWS プロジェクトにおいてコストを抑えたい方に向いています。 LFS で大容量ファイルを管理した上で、 リポジトリ のCloneやPull、Pushなどの速度が早くなり開発の効率化を図ることができます! 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://github.com/jasonwhite/rudolfs https://git-lfs.com/ https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-git-large-file-storage 執筆: @chen.sun 、 @kase.teruyoshi 、レビュー: @yamashita.yuki ( Shodo で執筆されました )
アバター
こんにちは!製造ソリューション事業部6年目の小林です。 製品 開発プロセス で発生する様々な情報を一元管理するPLM(Product Lifecycle Management)システムの導入、セールス支援をしています。 この記事では、私の仕事内容と仕事の楽しいポイントをご紹介します。 簡単な自己紹介 通信系の大学を卒業後入社し、製造ソリューション事業部に配属され、6年目になります。 就職活動では、メーカーに対するITソリューションを軸に活動をしていました。 他の企業説明会などにもたくさん参加をしましたが、最終的には インターン に参加した時の雰囲気から、とても刺激的な毎日が送れるのではないかと直感的に感じるものがあり、入社を決めました。 仕事内容 私の所属する製造ソリューション事業部 東日本技術ユニットでは、製品 開発プロセス で発生する様々な情報を管理するPLMシステム、製品開発設計で扱うCADシステムを軸とした製造ソリューションを扱っています。 そこで私は主に、プロジェクトマネージャー、プロジェクトリーダーといった役割で仕事をしています。 ※PLMシステムに興味がある方は以下リンクをご参照ください。 製造業DXサイト 製品> PLM(Product Lifecycle Management) https://mfg.isid.co.jp/product/plm/ 私の仕事のやりがい PLMシステム導入プロジェクトは最終的な目標(新システムが本番運用している状態)達成まで約1~3年ほどの期間がかかります。 この仕事の一番のやりがいを感じる時は、やはり、プロジェクトを完遂後、 ユーザーよりお礼の言葉をいただいた時 です。 ただ、このピークでやりがいを感じる機会は、数年に1回あるか、ないか、ですので、この記事では、プロジェクト遂行中にやりがいを感じるポイントを2点ご紹介します。 ①見えないものを頭をフル回転させて見ようとする プロジェクト発足時に最初に来るフェーズは”計画フェーズ”です。 製造ソリューション事業部では、基本的には、プリセールスで担当したエンジニアがプロジェクトマネージャーになります。 そのため、導入するシステムに期待することなどを、お客様の想いを理解した上で、目的・目標・計画をすり合わせを行います。 ただ、この時の計画は予測ですので、正解(実績)は、やってみないと誰にも分かりません。 何もモノがない状態で、頭をフル回転させて、 あらゆるリスクを予測しスケジュールを組んだり、精度が荒い部分、不明な部分に対して社内外にアクションを働きかけているとき、仕事を創造しているような楽しさがあります。 また、「お客様のシステム導入にリスクのある計画になっていないか」という顧客視点は、もちろん、「自社の後続フェーズを踏まえて何をいつまでに完了しないといけないか」といった自社内作業の視点を含めた2つの視点で頭を使うところが コンサルタント にはない、プロジェクトマネージャーの役割の醍醐味です。 ②実現したいことを把握した上でロジックを組み立てる 計画フェーズが終了すると、"要件定義・基本設計フェーズ"に移ります。 このフェーズでは、お客様の実現したいことに対して、システム要件として文書化して整理をし、仕様確定します。 製造ソリューション事業部では、ITシステム コンサルタント と システムエンジニア の役目を両方を担当する社員が多いです。 お客様の実現したいことを自身で ヒアリ ングし、仕様・ロジックに落とし込んでいくといった一連の流れをすべて把握しながら要件定義・基本設計をする楽しさがあります。 一日のスケジュール 9:30始業で、週2で客先訪問、週3でテレワーク業務です。 客先訪問の前後は、近くの サテライトオフィス を利用し、社内業務をします。 基本18:30には終業していますが、技術調査の探求心が止まらない場合は、遅い時間まで仕事を進めることもあります 学生の時にやっておいた方がいいこと なんでもよいので、”真剣にやって、楽しむ”という経験を一度するとよいと思います。 今回の記事では仕事の楽しいポイントを書きましたが、「やりたいこと」と同時に、「やらなければいけないこと」も付いてきています。 ただ、私の場合は、「真剣に仕事で楽しいこと」を追求しているので、ほぼ無意識にその「やらなければいけないこと」は知らず知らずクリアしているケースがほとんどです。 最後に ~就活生の方へのメッセージ~ 私も就活生の時は、製造業界に向けて何かで寄与したいという強い想いはありましたが、「本当に自分がやりたいこと」は明確に分からず、悩みを持っていた時期がありました。 そんな悩みを抱えていた時に、ISIDの説明会などに参加するという手段を使い、「やりたいこと」の精度を上げ、悩みを解決することができました。 私たちは今、同じチームで働いてくれる仲間を探しています。 既にITに興味があるという方はもちろんですが、○○業界に寄与したい強い想いがあるという方も是非ISIDの門戸を叩いてみてください。 私たちは一緒に働いてくれる仲間を募集しています! ISID 募集職種一覧 www.isid.co.jp 執筆: @kobayashi.takahiro 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
アバター
こんにちは!製造ソリューション事業部6年目の小林です。 製品 開発プロセス で発生する様々な情報を一元管理するPLM(Product Lifecycle Management)システムの導入、セールス支援をしています。 この記事では、私の仕事内容と仕事の楽しいポイントをご紹介します。 簡単な自己紹介 通信系の大学を卒業後入社し、製造ソリューション事業部に配属され、6年目になります。 就職活動では、メーカーに対するITソリューションを軸に活動をしていました。 他の企業説明会などにもたくさん参加をしましたが、最終的には インターン に参加した時の雰囲気から、とても刺激的な毎日が送れるのではないかと直感的に感じるものがあり、入社を決めました。 仕事内容 私の所属する製造ソリューション事業部 東日本技術ユニットでは、製品 開発プロセス で発生する様々な情報を管理するPLMシステム、製品開発設計で扱うCADシステムを軸とした製造ソリューションを扱っています。 そこで私は主に、プロジェクトマネージャー、プロジェクトリーダーといった役割で仕事をしています。 ※PLMシステムに興味がある方は以下リンクをご参照ください。 製造業DXサイト 製品> PLM(Product Lifecycle Management) https://mfg.isid.co.jp/product/plm/ 私の仕事のやりがい PLMシステム導入プロジェクトは最終的な目標(新システムが本番運用している状態)達成まで約1~3年ほどの期間がかかります。 この仕事の一番のやりがいを感じる時は、やはり、プロジェクトを完遂後、 ユーザーよりお礼の言葉をいただいた時 です。 ただ、このピークでやりがいを感じる機会は、数年に1回あるか、ないか、ですので、この記事では、プロジェクト遂行中にやりがいを感じるポイントを2点ご紹介します。 ①見えないものを頭をフル回転させて見ようとする プロジェクト発足時に最初に来るフェーズは”計画フェーズ”です。 製造ソリューション事業部では、基本的には、プリセールスで担当したエンジニアがプロジェクトマネージャーになります。 そのため、導入するシステムに期待することなどを、お客様の想いを理解した上で、目的・目標・計画をすり合わせを行います。 ただ、この時の計画は予測ですので、正解(実績)は、やってみないと誰にも分かりません。 何もモノがない状態で、頭をフル回転させて、 あらゆるリスクを予測しスケジュールを組んだり、精度が荒い部分、不明な部分に対して社内外にアクションを働きかけているとき、仕事を創造しているような楽しさがあります。 また、「お客様のシステム導入にリスクのある計画になっていないか」という顧客視点は、もちろん、「自社の後続フェーズを踏まえて何をいつまでに完了しないといけないか」といった自社内作業の視点を含めた2つの視点で頭を使うところが コンサルタント にはない、プロジェクトマネージャーの役割の醍醐味です。 ②実現したいことを把握した上でロジックを組み立てる 計画フェーズが終了すると、"要件定義・基本設計フェーズ"に移ります。 このフェーズでは、お客様の実現したいことに対して、システム要件として文書化して整理をし、仕様確定します。 製造ソリューション事業部では、ITシステム コンサルタント と システムエンジニア の役目を両方を担当する社員が多いです。 お客様の実現したいことを自身で ヒアリ ングし、仕様・ロジックに落とし込んでいくといった一連の流れをすべて把握しながら要件定義・基本設計をする楽しさがあります。 一日のスケジュール 9:30始業で、週2で客先訪問、週3でテレワーク業務です。 客先訪問の前後は、近くの サテライトオフィス を利用し、社内業務をします。 基本18:30には終業していますが、技術調査の探求心が止まらない場合は、遅い時間まで仕事を進めることもあります 学生の時にやっておいた方がいいこと なんでもよいので、”真剣にやって、楽しむ”という経験を一度するとよいと思います。 今回の記事では仕事の楽しいポイントを書きましたが、「やりたいこと」と同時に、「やらなければいけないこと」も付いてきています。 ただ、私の場合は、「真剣に仕事で楽しいこと」を追求しているので、ほぼ無意識にその「やらなければいけないこと」は知らず知らずクリアしているケースがほとんどです。 最後に ~就活生の方へのメッセージ~ 私も就活生の時は、製造業界に向けて何かで寄与したいという強い想いはありましたが、「本当に自分がやりたいこと」は明確に分からず、悩みを持っていた時期がありました。 そんな悩みを抱えていた時に、ISIDの説明会などに参加するという手段を使い、「やりたいこと」の精度を上げ、悩みを解決することができました。 私たちは今、同じチームで働いてくれる仲間を探しています。 既にITに興味があるという方はもちろんですが、○○業界に寄与したい強い想いがあるという方も是非ISIDの門戸を叩いてみてください。 私たちは一緒に働いてくれる仲間を募集しています! ISID 募集職種一覧 www.isid.co.jp 執筆: @kobayashi.takahiro 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
アバター
こんにちは、ISID 金融ソリューション事業部の岡崎です。 今回はUE5 PixelStreamingで、マウスカーソルを別の画像に変更してクリックイベントを作成します。 前回までの記事でもPixelStreamingについて調べているので、まだご覧になっていない方はそちらも参考にしていただけると嬉しいです。 UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する UE5 PixelStreamingで、Blueprint経由でWebブラウザを操作する はじめに PixelStreamingを利用してブラウザからUEを操作する利用場面を考えた際、重要になってくるUXのひとつとしてマウスクリックが考えられます。 例えばUE上にショップのようなものを展開し、ユーザーの気になるオブジェクト(商品サンプルなど)がある場合、マウスクリックを通じて ECサイト へ遷移することなどできます。 今回はそのようなPixelStreaming上でのマウスクリックやマウスカーソルに関する調査を行いました。 検証環境/ツール Unreal Engine5.1.1 AWS EC2 Windows _Server-2022-English-Full-Base-2023.01.19 Chrome ver.110.0.5481.177 実装手順 マウスカーソルの座標を取得 マウスカーソル用の画像を設定する マウスカーソルの動きに画像が連動するようにする 作成した ウィジェット をUEプロジェクトに適用する 作成した ウィジェット をPixelStreamingでも動くようにする 1. マウスカーソルの座標を取得 まず初めにマウスがクリックした際の座標を取得するBlueprintを作成します。 適当なプロジェクトを用意して、LevelBlueprintを開きます。 Blueprintでマウスの座標を取得するための関数は2つ存在します。 Get Mouse Position Get Mouse Position Scaled by DPI 今回は下の「Get Mouse Position Scaled by DPI」を使用します。 DPIとは解像度のことで、「Get Mouse Position Scaled by DPI」を使用すると解像度に合わせてマウスの座標を算出してくれます。 「Get Mouse Position Scaled by DPI」を使用してマウスの座標を取得するために、まずはコントローラーの情報を取得するための「Get Player Controller」のノードを作成します。 作成したノードのReturn Value から上記で説明した「Get Mouse Position Scaled by DPI」のノードを作成します。 このLocation X/Y にマウスの座標が入っているので、それぞれを「Print String」でUEに表示させます。 このままでは、まだマウスの座標を表示させるためのイベントが設定されていないので、今回はマウスを左クリックした際に座標を表示させるようにします。 「Left Mouse Button」のイベントノードを作成し、先ほど作成した「Print String」に繋ぐことで、左クリックをした際にマウスの座標をUEに表示するイベントが発火するようになります。 ※この段階でPixelStreamingでプレイをしても後述する設定がまだ完了していないためうまく作動しません。 2. マウスカーソル用の画像を設定する 次に、マウスカーソルを任意の画像に変えるためのBlueprintを作成します。 ContentDrawerを開き、任意の場所にWidgetBlueprintを作成します。 ( User Interface > Widget Blueprint) 今回は「WBP_MouseCursor」という名前で作成しました。 続いて、マウスカーソルとして使う画像も用意します。今回はグレーの丸い png 画像を用意しました。 同じ階層にインポートを行います。 それではBlueprintの作成に入っていきます。 先ほど作成した「WBP_MouseCursor」を開き、デザイナーエディターで作業を行います。 WidgetBlueprintを開くと最初に表示されている画面がデザイナーエディターなので、そのまま進めます。 まずは左上のpaletteにImageと記述し、検索します。 出てきたImageを左下のWBP_MouseCursorの中に ドラッグ&ドロップ をして配置します。 次に左下に配置したImageを右クリックし、「Wrap With > Canvas Panel」を選択し、 Canvas Panelでラッピングします。 これにより使用する画像のサイズや、位置の調整を行えるようになります。 またImageに「image_数字」という名前が付けられているので、任意の名前に変更します。今回はgray_circleという名前に変更しています。 Imageを追加しただけだと、白い四角い画像が追加されているだけなので、先ほどインポートしたグレーの丸い画像に変更します。 画面右側のBrush配下のImageのセレクトボックスから追加した画像の名前を検索します。 画面右側からサイズも変更します。 菊の花のようになっている部分の中心がカーソルの先端部分になるので、追加したグレーの丸の中心が菊の花の中心になるように画面右のPosition X/Y を変更します。今回は「-32」を記述しました。 下画像のようになっていたら成功です。 続いてグラフエディターに移動します。 3. マウスカーソルの動きに画像が連動するようにする マウスカーソルの動きに先ほど作成した画像を連動させるために、グラフエディターを使用します。 画面右上のGraphを選択し、グラフエディターに移動します。 先ほど作成したグレーの丸い画像をマウスの位置に置くために、一番初めに行ったマウスの座標を取得する処理と同じものを作成します。 「Get Player Controller」のノードを作成し、「Get Mouse Position Scaled by DPI」のノードにつなげます。 次に、先ほどデザイナーで作成したgray_circleを使うために、ノード追加で「Get gray_circle」と検索し追加します。 続いて、追加した「Gray Circle」からピンを伸ばし、「Slot as Canvas Slot」ノードを追加します。 次に、「Slot as Canvas Slot」のReturn Value から「Set Position」ノードを繋ぎ、先ほど作った「Get Mouse Position Scaled by DPI」のLocation X/Y も「Set Position」に繋ぎます。 最後に「Event Tick」と「Set Position」を繋いでグラフエディターでの作業も完了です。 4. 作成した ウィジェット をUEプロジェクトに適用する 今まで作成したWidgetBlueprintを本プロジェクトに追加するために、LevelBlueprintに処理を追加します。 今回は「Event BeginPlay」を使用します。実行ピンを伸ばし「Create Widget 」ノードを作成します。 作成すると属性の中にクラスを選択するセレクトボックスがあるので、先ほど作成した「WBP_MouseCursor」を選択します。 先ほど作成した「Create Widget 」ノードから「Add to Viewport」を繋げます。これでUEをプレイ時にマウスカーソル位置にグレーの丸い画像が表示されるようになります。 実際にプレイ画面を見てみると、マウスカーソルの位置にグレーの画像が表示されていることと、クリックした位置で座標がプリントされていることが確認できます。 5. 作成した ウィジェット をPixelStreamingでも動くようにする 次は、作成したプロジェクトをPixelStreamingでブラウザに反映させていきます。 PixelStreamingの設定方法は こちらの金融ソリューション事業部の山下さんの記事 を参考にしました。 ここまで作成したものをPixelStreaming上で接続すると、マウスクリックを行うたびに ポインター が画面の左上(座標0.0の位置)に移動してしまいます。 これはPixelStreamingを行う際、プレイヤーがUE画面をクリックすると、マウスカーソルをキャプチャしてロックする仕様で起こる事象なので、この設定を変えていきます。 変更するファイルは、 こちらの記事(UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する) で修正を行ったplayer.htmlになります。 先ほどの記事で「emitUIInteraction」を追加している近くの箇所で下記記述を追加します。 //マウスイベント変更 inputOptions.controlScheme = ControlSchemeType.HoveringMouse; inputOptions.hideBrowserCursor = true; この設定により、カーソルの制御をUE画面上でも行えるようになります。 またカーソルを隠すことにより、UE画面上でのカーソルを設定したグレーの丸い画像に置き換えることができます。 これにより、マウスクリックを行うたびに ポインター が画面の左上(座標0.0の位置)に移動してしまうこともなく、想定通りの挙動をブラウザ上でも行うことができます。 所感 今回はPixelStreamingの プラグイン を用いて、マウスクリックイベント(クリックした座標を表示させる)と、マウスカーソルを任意の画像に変更させてみました。 UEだけでプロジェクトを行う時とPixelStreamingを用いる時で、ユーザーインプットの扱い方があまりよくわかっていませんでしたが、今回の実装でよく理解することができました。 今回の調査を通じて、マウスクリックを使用してキャ ラク ターオブジェクトの移動や、 ECサイト への遷移などができることで、ゲームに慣れていない人もさまざまなシーンでUEを使うことができるのではないかと感じました。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://docs.unrealengine.com/4.27/ja/SharingAndReleasing/PixelStreaming/CustomPlayer/ https://papersloth.hatenablog.com/entry/2018/02/06/220632 執筆: @okazaki.wataru 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
こんにちは、ISID 金融ソリューション事業部の岡崎です。 今回はUE5 PixelStreamingで、マウスカーソルを別の画像に変更してクリックイベントを作成します。 前回までの記事でもPixelStreamingについて調べているので、まだご覧になっていない方はそちらも参考にしていただけると嬉しいです。 UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する UE5 PixelStreamingで、Blueprint経由でWebブラウザを操作する はじめに PixelStreamingを利用してブラウザからUEを操作する利用場面を考えた際、重要になってくるUXのひとつとしてマウスクリックが考えられます。 例えばUE上にショップのようなものを展開し、ユーザーの気になるオブジェクト(商品サンプルなど)がある場合、マウスクリックを通じて ECサイト へ遷移することなどできます。 今回はそのようなPixelStreaming上でのマウスクリックやマウスカーソルに関する調査を行いました。 検証環境/ツール Unreal Engine5.1.1 AWS EC2 Windows _Server-2022-English-Full-Base-2023.01.19 Chrome ver.110.0.5481.177 実装手順 マウスカーソルの座標を取得 マウスカーソル用の画像を設定する マウスカーソルの動きに画像が連動するようにする 作成した ウィジェット をUEプロジェクトに適用する 作成した ウィジェット をPixelStreamingでも動くようにする 1. マウスカーソルの座標を取得 まず初めにマウスがクリックした際の座標を取得するBlueprintを作成します。 適当なプロジェクトを用意して、LevelBlueprintを開きます。 Blueprintでマウスの座標を取得するための関数は2つ存在します。 Get Mouse Position Get Mouse Position Scaled by DPI 今回は下の「Get Mouse Position Scaled by DPI」を使用します。 DPIとは解像度のことで、「Get Mouse Position Scaled by DPI」を使用すると解像度に合わせてマウスの座標を算出してくれます。 「Get Mouse Position Scaled by DPI」を使用してマウスの座標を取得するために、まずはコントローラーの情報を取得するための「Get Player Controller」のノードを作成します。 作成したノードのReturn Value から上記で説明した「Get Mouse Position Scaled by DPI」のノードを作成します。 このLocation X/Y にマウスの座標が入っているので、それぞれを「Print String」でUEに表示させます。 このままでは、まだマウスの座標を表示させるためのイベントが設定されていないので、今回はマウスを左クリックした際に座標を表示させるようにします。 「Left Mouse Button」のイベントノードを作成し、先ほど作成した「Print String」に繋ぐことで、左クリックをした際にマウスの座標をUEに表示するイベントが発火するようになります。 ※この段階でPixelStreamingでプレイをしても後述する設定がまだ完了していないためうまく作動しません。 2. マウスカーソル用の画像を設定する 次に、マウスカーソルを任意の画像に変えるためのBlueprintを作成します。 ContentDrawerを開き、任意の場所にWidgetBlueprintを作成します。 ( User Interface > Widget Blueprint) 今回は「WBP_MouseCursor」という名前で作成しました。 続いて、マウスカーソルとして使う画像も用意します。今回はグレーの丸い png 画像を用意しました。 同じ階層にインポートを行います。 それではBlueprintの作成に入っていきます。 先ほど作成した「WBP_MouseCursor」を開き、デザイナーエディターで作業を行います。 WidgetBlueprintを開くと最初に表示されている画面がデザイナーエディターなので、そのまま進めます。 まずは左上のpaletteにImageと記述し、検索します。 出てきたImageを左下のWBP_MouseCursorの中に ドラッグ&ドロップ をして配置します。 次に左下に配置したImageを右クリックし、「Wrap With > Canvas Panel」を選択し、 Canvas Panelでラッピングします。 これにより使用する画像のサイズや、位置の調整を行えるようになります。 またImageに「image_数字」という名前が付けられているので、任意の名前に変更します。今回はgray_circleという名前に変更しています。 Imageを追加しただけだと、白い四角い画像が追加されているだけなので、先ほどインポートしたグレーの丸い画像に変更します。 画面右側のBrush配下のImageのセレクトボックスから追加した画像の名前を検索します。 画面右側からサイズも変更します。 菊の花のようになっている部分の中心がカーソルの先端部分になるので、追加したグレーの丸の中心が菊の花の中心になるように画面右のPosition X/Y を変更します。今回は「-32」を記述しました。 下画像のようになっていたら成功です。 続いてグラフエディターに移動します。 3. マウスカーソルの動きに画像が連動するようにする マウスカーソルの動きに先ほど作成した画像を連動させるために、グラフエディターを使用します。 画面右上のGraphを選択し、グラフエディターに移動します。 先ほど作成したグレーの丸い画像をマウスの位置に置くために、一番初めに行ったマウスの座標を取得する処理と同じものを作成します。 「Get Player Controller」のノードを作成し、「Get Mouse Position Scaled by DPI」のノードにつなげます。 次に、先ほどデザイナーで作成したgray_circleを使うために、ノード追加で「Get gray_circle」と検索し追加します。 続いて、追加した「Gray Circle」からピンを伸ばし、「Slot as Canvas Slot」ノードを追加します。 次に、「Slot as Canvas Slot」のReturn Value から「Set Position」ノードを繋ぎ、先ほど作った「Get Mouse Position Scaled by DPI」のLocation X/Y も「Set Position」に繋ぎます。 最後に「Event Tick」と「Set Position」を繋いでグラフエディターでの作業も完了です。 4. 作成した ウィジェット をUEプロジェクトに適用する 今まで作成したWidgetBlueprintを本プロジェクトに追加するために、LevelBlueprintに処理を追加します。 今回は「Event BeginPlay」を使用します。実行ピンを伸ばし「Create Widget 」ノードを作成します。 作成すると属性の中にクラスを選択するセレクトボックスがあるので、先ほど作成した「WBP_MouseCursor」を選択します。 先ほど作成した「Create Widget 」ノードから「Add to Viewport」を繋げます。これでUEをプレイ時にマウスカーソル位置にグレーの丸い画像が表示されるようになります。 実際にプレイ画面を見てみると、マウスカーソルの位置にグレーの画像が表示されていることと、クリックした位置で座標がプリントされていることが確認できます。 5. 作成した ウィジェット をPixelStreamingでも動くようにする 次は、作成したプロジェクトをPixelStreamingでブラウザに反映させていきます。 PixelStreamingの設定方法は こちらの金融ソリューション事業部の山下さんの記事 を参考にしました。 ここまで作成したものをPixelStreaming上で接続すると、マウスクリックを行うたびに ポインター が画面の左上(座標0.0の位置)に移動してしまいます。 これはPixelStreamingを行う際、プレイヤーがUE画面をクリックすると、マウスカーソルをキャプチャしてロックする仕様で起こる事象なので、この設定を変えていきます。 変更するファイルは、 こちらの記事(UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する) で修正を行ったplayer.htmlになります。 先ほどの記事で「emitUIInteraction」を追加している近くの箇所で下記記述を追加します。 //マウスイベント変更 inputOptions.controlScheme = ControlSchemeType.HoveringMouse; inputOptions.hideBrowserCursor = true; この設定により、カーソルの制御をUE画面上でも行えるようになります。 またカーソルを隠すことにより、UE画面上でのカーソルを設定したグレーの丸い画像に置き換えることができます。 これにより、マウスクリックを行うたびに ポインター が画面の左上(座標0.0の位置)に移動してしまうこともなく、想定通りの挙動をブラウザ上でも行うことができます。 所感 今回はPixelStreamingの プラグイン を用いて、マウスクリックイベント(クリックした座標を表示させる)と、マウスカーソルを任意の画像に変更させてみました。 UEだけでプロジェクトを行う時とPixelStreamingを用いる時で、ユーザーインプットの扱い方があまりよくわかっていませんでしたが、今回の実装でよく理解することができました。 今回の調査を通じて、マウスクリックを使用してキャ ラク ターオブジェクトの移動や、 ECサイト への遷移などができることで、ゲームに慣れていない人もさまざまなシーンでUEを使うことができるのではないかと感じました。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://docs.unrealengine.com/4.27/ja/SharingAndReleasing/PixelStreaming/CustomPlayer/ https://papersloth.hatenablog.com/entry/2018/02/06/220632 執筆: @okazaki.wataru 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
はじめに ISID X(クロス) イノベーション 本部 の三浦です。 筆者の関わってる案件では、コンテナ利用、 AWS Fargate利用を進めております。 AWS Fargateのお手軽さは非常に重宝しております。 しかし、そこで問題になってくるのが、管理接続、踏み台系をどうするかです。 アプリケーション本体をせっかく AWS Fargateでやっているので、管理系接続、踏み台的な作業もできるだけ非EC2でやりたいですよね? しかし、以前検証した際には、ローカルPC⇒ AWS Fargate経由⇒RDSといった接続することができませんでした。が、今回、 AWS 様のアップデートにより上記ができるようになりましたので、ご紹介いたします。 前記事: AWS FargateでECS Exec、session managerモロモロのTips 目次 はじめに 目次 実現できること 解説 本機能を使いやすくするために 実現できること 特に踏み台を準備せず、アプリが稼働してるコンテナからポート フォワ ードをし、任意の環境からRDSに接続できます。 接続例を下記に図示します。 AWS CLI で、 AWS Fargateをターゲットにして、 リモートホスト ポート フォワ ード用のドキュメント( AWS -StartPortForwardingSessionToRemoteHost)を実行。 pgAdminから接続。 解説 下記は、 windows 上での実行例です。 aws ssm start-session ^ --profile dev93 ^ --target ecs:%cluster_name% %task_id% %container_runtime_id% ^ --document-name AWS -StartPortForwardingSessionToRemoteHost ^ --parameters "{"host":["XXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com"],"portNumber":["5432"], "localPortNumber":["15432"]}" " キーとなるのは、 aws ssm start-sessionで、targetをどう指定するかです。 https://github.com/aws/aws-cli/blob/c0edee0a7427b6e7b654df0696015e96105497a3/awscli/customizations/ecs/executecommand.py#L61-L73 で、上記のコードに、 AWS FargateでのSSM呼び出しの書式が書かれており AWS -StartPortForwardingSessionToRemoteHostだけでなく他のドキュメントも呼び出し可能です。 なお、必要となる値は下記より確認可能です。 cluster_name task_id container_runtime_id 本機能を使いやすくするために cluster_nameは、環境に応じた固定的な値ですが、task_id、container_runtime_idは、ESC TASKの再起動のたびに変わってしまう動的な値です。ですので、これらを取得する スクリプト と組み合わせると利用しやすくなります(私は、 クラスタ ー名、サービス名を指定するとtask_id、container_runtime_idを取得する スクリプト と組み合わせて使ってます)。 また、ecsを使いやすくするツールを開発されている方々もおられますので、これらのツールを使ってもよいかもしれません。 ecstaを使ってAmazon ECSコンテナ開発効率を上げよう! github fujiwara/ecsta なお、当初はecstaを触ってみた記事を書くつもりだったのですが2023/02時点で windows 用のバイナリがなかったため今回の内容にしました。 Mac 、 Linux ユーザーの方は、ecstaを使ってみるとよいのではないかと思います。 執筆: @miura.toshihiko 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
はじめに ISID X(クロス) イノベーション 本部 の三浦です。 筆者の関わってる案件では、コンテナ利用、 AWS Fargate利用を進めております。 AWS Fargateのお手軽さは非常に重宝しております。 しかし、そこで問題になってくるのが、管理接続、踏み台系をどうするかです。 アプリケーション本体をせっかく AWS Fargateでやっているので、管理系接続、踏み台的な作業もできるだけ非EC2でやりたいですよね? しかし、以前検証した際には、ローカルPC⇒ AWS Fargate経由⇒RDSといった接続することができませんでした。が、今回、 AWS 様のアップデートにより上記ができるようになりましたので、ご紹介いたします。 前記事: AWS FargateでECS Exec、session managerモロモロのTips 目次 はじめに 目次 実現できること 解説 本機能を使いやすくするために 実現できること 特に踏み台を準備せず、アプリが稼働してるコンテナからポート フォワ ードをし、任意の環境からRDSに接続できます。 接続例を下記に図示します。 AWS CLI で、 AWS Fargateをターゲットにして、 リモートホスト ポート フォワ ード用のドキュメント( AWS -StartPortForwardingSessionToRemoteHost)を実行。 pgAdminから接続。 解説 下記は、 windows 上での実行例です。 aws ssm start-session ^ --profile dev93 ^ --target ecs:%cluster_name% %task_id% %container_runtime_id% ^ --document-name AWS -StartPortForwardingSessionToRemoteHost ^ --parameters "{"host":["XXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com"],"portNumber":["5432"], "localPortNumber":["15432"]}" " キーとなるのは、 aws ssm start-sessionで、targetをどう指定するかです。 https://github.com/aws/aws-cli/blob/c0edee0a7427b6e7b654df0696015e96105497a3/awscli/customizations/ecs/executecommand.py#L61-L73 で、上記のコードに、 AWS FargateでのSSM呼び出しの書式が書かれており AWS -StartPortForwardingSessionToRemoteHostだけでなく他のドキュメントも呼び出し可能です。 なお、必要となる値は下記より確認可能です。 cluster_name task_id container_runtime_id 本機能を使いやすくするために cluster_nameは、環境に応じた固定的な値ですが、task_id、container_runtime_idは、ESC TASKの再起動のたびに変わってしまう動的な値です。ですので、これらを取得する スクリプト と組み合わせると利用しやすくなります(私は、 クラスタ ー名、サービス名を指定するとtask_id、container_runtime_idを取得する スクリプト と組み合わせて使ってます)。 また、ecsを使いやすくするツールを開発されている方々もおられますので、これらのツールを使ってもよいかもしれません。 ecstaを使ってAmazon ECSコンテナ開発効率を上げよう! github fujiwara/ecsta なお、当初はecstaを触ってみた記事を書くつもりだったのですが2023/02時点で windows 用のバイナリがなかったため今回の内容にしました。 Mac 、 Linux ユーザーの方は、ecstaを使ってみるとよいのではないかと思います。 執筆: @miura.toshihiko 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
電通国際情報サービス 、オープン イノベーション ラボの 比嘉康雄 です。 僕の所属する部署は、旬な技術に対して、レポートを継続して書いています。そのレポートをGPT4に書いてもらおうというのが今回の記事になります。はやりのプロンプトエンジニアリングですね。 この記事を書いている2023/03/24の時点では、GPT4は、Webページにアクセスして情報を取得することができません。GPT4が学習したデータは、2021年9月までのものらしいので、旬な技術のことはほとんど知りません。 それをどう解決するかが、苦労したポイントでした。もうすぐ、ChatGPTのBrowsing Pluginが出ると思うので、この問題は解決しますが、テク ノロ ジー レポートを書くという本筋は変わらないはずです。 レポートのテーマ キーワードのピックアップ 情報参照サイトのピックアップ 情報参照サイトの要約 プロンプトの実行 実行結果 まとめ 仲間募集 レポートのテーマ レポートのテーマは、 Orbs という ブロックチェーン です。 Orbsを選んだ深い理由はないのですが、2022年度以降に盛り上がっている ブロックチェーン で、僕がほとんど知らない ブロックチェーン を選んでみました。 キーワードのピックアップ 軽くググって、レポートに必要そうなキーワードをピックアップしました。そのキーワードは下記の通りです。実は、キーワードのピックアップが最も重要な作業になります。 GPT4に何を書いてほしいのかは、人が決める必要があります。それを決めないと、出来上がったレポートの良し悪しも判断できません。 Layer-3 Orbs 3.0 PoS V3 Multi-chain Staking Orbs Lambda Orbs VM EVM TON Orbs Governance 情報参照サイトのピックアップ orbs キーワード で、情報の参照サイトを探します。斜め読みして分かりやすいサイトを選びます。1キーワード、1サイトで良いでしょう。 サイトを斜め読みしているので、この段階で、Orbsに対する知識がそれなりに付いてきています。 情報参照サイトの要約 Edge(Bing)で、サイトにアクセスし、CMD/CTRL + Aでページのコンテンツを全選択してコピーします。プロンプトを出し、 summarize と入力し、CMD/CTRL Vでページのコンテンツを貼り付けます。 出力された要約をプロンプトの [inputs]: の下に下記のように貼り付けます。 [inputs]: This page is about the Orbs project and its progress in 2022. The article discusses the key components of Orbs 3.0, including its place in web3, multi-chain staking (PoS V3), and Orbs as a layer-3 (L3) blockchain. The project has made significant progress in each of these areas, including the launch of multi-chain staking on Ethereum and Polygon, the development of Orbs Lambda and Orbs VM as L3 execution services, and the introduction and launch of Orbs governance. The project has also integrated with the TON blockchain and has plans to continue building and developing in 2023. Edgeでなくても、Webページを要約できるなら、何を使っても構いません。 ページの要約と [inputs]: への貼付けを情報参照サイトの数分繰り返します。 プロンプトの実行 これで、プロンプトを実行する準備が整いました。下記のプロンプトを実行しましょう。 You are an experienced writer who is good at writing technology reports. [theme]:orbs [inputs]: ... [c1]:Generate some popular questions about [theme] and save [Q#]. [c2]:Write as detailed an article as possible about all [Q#]. 実行結果 実行結果は下記のようになりました。結構よくできたレポートではないでしょうか。 [c1]: Generate some popular questions about [theme] and save [Q#]. Q1: What is the Orbs project and its main objectives? Q2: How does Orbs fit into the Web 3.0 ecosystem? Q3: What is multi-chain staking (PoS V3) in Orbs 3.0? Q4: How does Orbs function as a Layer-3 blockchain? Q5: What are Orbs Lambda and Orbs VM, and how do they work? Q6: How does Orbs enhance existing EVM and TON smart contracts? Q7: What are the limitations of existing smart contracts that Orbs addresses? Q8: How is the Orbs project related to the TON blockchain? [c2]: Write as detailed an article as possible about all [Q#]. Title: Orbs 3.0: Unlocking the Full Potential of Web 3.0 and Blockchain Technology Introduction The Orbs project has been making significant strides in 2022, with the launch of Orbs 3.0, which includes multi-chain staking, integration with the TON blockchain, and the introduction of Orbs Lambda and Orbs VM. In this article, we'll explore the key components of Orbs 3.0 and how it's revolutionizing the Web 3.0 and blockchain ecosystems. Q1 & Q2: The Orbs Project and Its Place in the Web 3.0 Ecosystem Orbs is a decentralized and public blockchain infrastructure that utilizes a Proof-of-Stake (PoS) consensus algorithm. The Orbs network is secured by permissionless validators called Guardians, who are elected using PoS. The Orbs PoS algorithm is implemented as EVM smart contracts running on the Ethereum mainnet, providing strong security guarantees. As a Layer 3 protocol, Orbs runs over existing L1/L2 blockchains like Ethereum and TON, enhancing the capabilities of their existing smart contracts, and playing a crucial role in the Web 3.0 ecosystem. Q3: Multi-chain Staking (PoS V3) in Orbs 3.0 Orbs 3.0 features multi-chain staking, allowing users to stake their tokens on multiple blockchain networks, such as Ethereum and Polygon. This development provides additional flexibility and security to token holders, as they can now participate in the Orbs ecosystem across multiple chains. Q4 & Q6: Orbs as a Layer-3 Blockchain and Enhancing EVM and TON Smart Contracts Orbs is a decentralized execution layer that operates between existing L1/L2 solutions and the application layer. By functioning as a Layer 3 blockchain, Orbs enhances the capabilities of existing EVM and TON smart contracts, enabling new possibilities for Web 3.0, DeFi, NFTs, and GameFi applications. Q5: Orbs Lambda and Orbs VM Orbs Lambda is a decentralized, serverless cloud function similar to AWS Lambda. It is fully transparent, decentralized, and event-driven, relying on independent network validators for guaranteed execution. Developers can write cloud functions in JavaScript or TypeScript, and trigger them through scheduled time intervals, on-chain events from multiple L1 blockchains, or HTTP requests. Orbs VM, on the other hand, is a decentralized virtual machine akin to AWS EC2. Implemented as Docker containers deployed to the Orbs Network, it is fully transparent and decentralized. Services can be implemented in any familiar programming language and are always-on. Both Orbs Lambda and Orbs VM enhance existing smart contracts without replacing them. Q7: Addressing Limitations of Existing Smart Contracts Orbs tackles several limitations of existing smart contracts, including the inability to access data outside the chain, trigger future actions, limited language support, and inability to access historic data. By addressing these limitations, Orbs enables developers to create more sophisticated and feature-rich decentralized applications, leveraging the full potential of blockchain technology. Q8: Orbs Project's Relation to the TON Blockchain Orbs has integrated with the TON blockchain, a decentralized platform that uses blockchain technology to provide secure and efficient interaction with online services. TON, or The Open Network, was initially launched by Telegram in 2018 as an open-source project to create an ecosystem for distributed applications (dApps), smart contracts, decentralized storage solutions, and other innovations. Although Telegram was forced to abandon its plans to issue Gram tokens due to a dispute with the SEC, the TON network has continued to develop as a separate entity. Orbs' integration with TON further expands its ecosystem and enhances the capabilities of TON-based smart contracts. Conclusion The Orbs project has made impressive progress in 2022, with the launch of Orbs 3.0 and its integration with the TON blockchain. Through multi-chain staking, the development of Orbs Lambda and Orbs VM, and the enhancement of existing EVM and TON smart contracts, Orbs is poised to unlock the full potential of Web 3.0 and blockchain technology. As the project continues to grow and develop in 2023, we can expect further innovations and breakthroughs that will shape the future of decentralized applications and services. まとめ GPT4の書いたテク ノロ ジー レポートを読んだ後に、情報参照サイトのコンテンツを読むと、かなりよく理解できます。 仲間募集 私たちは同じグループで共に働いていただける仲間を募集しています。 現在、以下のような職種を募集しています。 ソリューションアーキテクト AIエンジニア 執筆: @higa 、レビュー: @higa ( Shodo で執筆されました )
アバター
電通国際情報サービス 、オープン イノベーション ラボの 比嘉康雄 です。 僕の所属する部署は、旬な技術に対して、レポートを継続して書いています。そのレポートをGPT4に書いてもらおうというのが今回の記事になります。はやりのプロンプトエンジニアリングですね。 この記事を書いている2023/03/24の時点では、GPT4は、Webページにアクセスして情報を取得することができません。GPT4が学習したデータは、2021年9月までのものらしいので、旬な技術のことはほとんど知りません。 それをどう解決するかが、苦労したポイントでした。もうすぐ、ChatGPTのBrowsing Pluginが出ると思うので、この問題は解決しますが、テク ノロ ジー レポートを書くという本筋は変わらないはずです。 レポートのテーマ キーワードのピックアップ 情報参照サイトのピックアップ 情報参照サイトの要約 プロンプトの実行 実行結果 まとめ 仲間募集 レポートのテーマ レポートのテーマは、 Orbs という ブロックチェーン です。 Orbsを選んだ深い理由はないのですが、2022年度以降に盛り上がっている ブロックチェーン で、僕がほとんど知らない ブロックチェーン を選んでみました。 キーワードのピックアップ 軽くググって、レポートに必要そうなキーワードをピックアップしました。そのキーワードは下記の通りです。実は、キーワードのピックアップが最も重要な作業になります。 GPT4に何を書いてほしいのかは、人が決める必要があります。それを決めないと、出来上がったレポートの良し悪しも判断できません。 Layer-3 Orbs 3.0 PoS V3 Multi-chain Staking Orbs Lambda Orbs VM EVM TON Orbs Governance 情報参照サイトのピックアップ orbs キーワード で、情報の参照サイトを探します。斜め読みして分かりやすいサイトを選びます。1キーワード、1サイトで良いでしょう。 サイトを斜め読みしているので、この段階で、Orbsに対する知識がそれなりに付いてきています。 情報参照サイトの要約 Edge(Bing)で、サイトにアクセスし、CMD/CTRL + Aでページのコンテンツを全選択してコピーします。プロンプトを出し、 summarize と入力し、CMD/CTRL Vでページのコンテンツを貼り付けます。 出力された要約をプロンプトの [inputs]: の下に下記のように貼り付けます。 [inputs]: This page is about the Orbs project and its progress in 2022. The article discusses the key components of Orbs 3.0, including its place in web3, multi-chain staking (PoS V3), and Orbs as a layer-3 (L3) blockchain. The project has made significant progress in each of these areas, including the launch of multi-chain staking on Ethereum and Polygon, the development of Orbs Lambda and Orbs VM as L3 execution services, and the introduction and launch of Orbs governance. The project has also integrated with the TON blockchain and has plans to continue building and developing in 2023. Edgeでなくても、Webページを要約できるなら、何を使っても構いません。 ページの要約と [inputs]: への貼付けを情報参照サイトの数分繰り返します。 プロンプトの実行 これで、プロンプトを実行する準備が整いました。下記のプロンプトを実行しましょう。 You are an experienced writer who is good at writing technology reports. [theme]:orbs [inputs]: ... [c1]:Generate some popular questions about [theme] and save [Q#]. [c2]:Write as detailed an article as possible about all [Q#]. 実行結果 実行結果は下記のようになりました。結構よくできたレポートではないでしょうか。 [c1]: Generate some popular questions about [theme] and save [Q#]. Q1: What is the Orbs project and its main objectives? Q2: How does Orbs fit into the Web 3.0 ecosystem? Q3: What is multi-chain staking (PoS V3) in Orbs 3.0? Q4: How does Orbs function as a Layer-3 blockchain? Q5: What are Orbs Lambda and Orbs VM, and how do they work? Q6: How does Orbs enhance existing EVM and TON smart contracts? Q7: What are the limitations of existing smart contracts that Orbs addresses? Q8: How is the Orbs project related to the TON blockchain? [c2]: Write as detailed an article as possible about all [Q#]. Title: Orbs 3.0: Unlocking the Full Potential of Web 3.0 and Blockchain Technology Introduction The Orbs project has been making significant strides in 2022, with the launch of Orbs 3.0, which includes multi-chain staking, integration with the TON blockchain, and the introduction of Orbs Lambda and Orbs VM. In this article, we'll explore the key components of Orbs 3.0 and how it's revolutionizing the Web 3.0 and blockchain ecosystems. Q1 & Q2: The Orbs Project and Its Place in the Web 3.0 Ecosystem Orbs is a decentralized and public blockchain infrastructure that utilizes a Proof-of-Stake (PoS) consensus algorithm. The Orbs network is secured by permissionless validators called Guardians, who are elected using PoS. The Orbs PoS algorithm is implemented as EVM smart contracts running on the Ethereum mainnet, providing strong security guarantees. As a Layer 3 protocol, Orbs runs over existing L1/L2 blockchains like Ethereum and TON, enhancing the capabilities of their existing smart contracts, and playing a crucial role in the Web 3.0 ecosystem. Q3: Multi-chain Staking (PoS V3) in Orbs 3.0 Orbs 3.0 features multi-chain staking, allowing users to stake their tokens on multiple blockchain networks, such as Ethereum and Polygon. This development provides additional flexibility and security to token holders, as they can now participate in the Orbs ecosystem across multiple chains. Q4 & Q6: Orbs as a Layer-3 Blockchain and Enhancing EVM and TON Smart Contracts Orbs is a decentralized execution layer that operates between existing L1/L2 solutions and the application layer. By functioning as a Layer 3 blockchain, Orbs enhances the capabilities of existing EVM and TON smart contracts, enabling new possibilities for Web 3.0, DeFi, NFTs, and GameFi applications. Q5: Orbs Lambda and Orbs VM Orbs Lambda is a decentralized, serverless cloud function similar to AWS Lambda. It is fully transparent, decentralized, and event-driven, relying on independent network validators for guaranteed execution. Developers can write cloud functions in JavaScript or TypeScript, and trigger them through scheduled time intervals, on-chain events from multiple L1 blockchains, or HTTP requests. Orbs VM, on the other hand, is a decentralized virtual machine akin to AWS EC2. Implemented as Docker containers deployed to the Orbs Network, it is fully transparent and decentralized. Services can be implemented in any familiar programming language and are always-on. Both Orbs Lambda and Orbs VM enhance existing smart contracts without replacing them. Q7: Addressing Limitations of Existing Smart Contracts Orbs tackles several limitations of existing smart contracts, including the inability to access data outside the chain, trigger future actions, limited language support, and inability to access historic data. By addressing these limitations, Orbs enables developers to create more sophisticated and feature-rich decentralized applications, leveraging the full potential of blockchain technology. Q8: Orbs Project's Relation to the TON Blockchain Orbs has integrated with the TON blockchain, a decentralized platform that uses blockchain technology to provide secure and efficient interaction with online services. TON, or The Open Network, was initially launched by Telegram in 2018 as an open-source project to create an ecosystem for distributed applications (dApps), smart contracts, decentralized storage solutions, and other innovations. Although Telegram was forced to abandon its plans to issue Gram tokens due to a dispute with the SEC, the TON network has continued to develop as a separate entity. Orbs' integration with TON further expands its ecosystem and enhances the capabilities of TON-based smart contracts. Conclusion The Orbs project has made impressive progress in 2022, with the launch of Orbs 3.0 and its integration with the TON blockchain. Through multi-chain staking, the development of Orbs Lambda and Orbs VM, and the enhancement of existing EVM and TON smart contracts, Orbs is poised to unlock the full potential of Web 3.0 and blockchain technology. As the project continues to grow and develop in 2023, we can expect further innovations and breakthroughs that will shape the future of decentralized applications and services. まとめ GPT4の書いたテク ノロ ジー レポートを読んだ後に、情報参照サイトのコンテンツを読むと、かなりよく理解できます。 仲間募集 私たちは同じグループで共に働いていただける仲間を募集しています。 現在、以下のような職種を募集しています。 ソリューションアーキテクト AIエンジニア 執筆: @higa 、レビュー: @higa ( Shodo で執筆されました )
アバター
こんにちは!グループ経営ソリューション( GMS )事業部5年目の川島です。現在は自社製品Ci*Xの導入やセールス支援をしています。 この記事では私のやっている仕事の内容や職場の雰囲気について、また、今学生で今後ISIDを目指す方向けに就活のお話も少しできればと思います。 本記事を通して私の仕事を知ってもらえたり、IT経験のない方でIT企業を目指す方に少しでも勇気を持ってもらえたりしたらうれしいです。 自己紹介 まずは簡単に、自己紹介を。 1. 年次 新卒で2019年にISIDに入社し、2023年1月で5年目になりました。 2. ISIDの入社のきめて 最初は大学の同級生に「ISIDの インターン が面白かった!」と言われ、その場の勢いでエントリーしました。 (採用面接でも何を思ったか正直に↑を伝えました。笑っていただけて安心した記憶が・・・) ・面接が楽しく、自分らしくいられたこと ・入社を悩んでいるときに会わせていただいた先輩社員が全員素敵だったこと を決め手に入社を決意しました。 3. 大学の専攻 大学、大学院と数学科に所属し、 素数 周りの研究をしていました。IT経験はありませんでした。 余談ですが:数学科の人ならわかると思いますが、 TeX をプログラミングだと思っていたレベルです。 4. ISID内の所属 グループ経営ソリューション事業部(会計のシステムを扱う事業部です)に所属しています。 配属から部署は変わっていません。 どんな環境でどんな仕事をしているの? QA形式で私の仕事を紹介します。 Q1:なんの仕事をしているのですか? 「Ci*X」という自社会計システムの導入やセールス支援の仕事をしています。 Q2:「導入」とは?「セールス支援」とは何ですか? 同じ事業部所属の後輩、安田さんの記事にわかりやすくまとめてあるので割愛します! 導入/セールスする製品は違いますが、やっていることはほぼ同じです。 安田さんの記事はこちら>新卒3年目社員の働き方紹介(GMS事業部) Q3:「Ci*X」とは何ですか? サイクロスと読みます。サイクロスシリーズは ISIDが作った 会計システムです。 シリーズとしては「Ci*X Expense」という経費精算システムを皮切りに、「Journalizer」(仕訳エンジン)、「Workflow」(汎用ワークフロー)、「Financials」(グループ統合会計システム)が続々とリリースされています。 Q4:「Ci*X」の魅力って何ですか? たくさんありますが、代表すると下記の2点です。 ・使いやすさ(UIにこだわっています) ・他システムとの連携のしやすさ(Journalizerを利用することで他システムへの柔軟な連携を実現します) ぜひ Ci*Xのホームページ も見ていただきたいです! Q5:プログラミングしていますか? プログラミングはしませんが、読むことはあります。 製品の仕様確認のために JAVA や SQL を読むこともあるので、知っておくことに損はありません。 (私は研修&配属後の勉強で知識を付けました) Q6:どんな人と仕事をしているのですか? 私の所属する導入グループは20名いて、年次や新卒or中途入社もバラバラです。 (ちょっと特徴的なのは、このグループには女性のほうが多い!ということですかね) Q7:1日の仕事の流れはどんな感じですか? 打合せと作業を交互に繰り返します。 入社してすぐは作業のほうが多かったですが、今はどの案件でもリーダー以上の役割が多いため、打合せ多めです。 Q8:在宅勤務は多いですか? 多いです。 今は OJT リーダーとして新人の教育担当をしており、1年目の後輩とは週1日程度一緒に出社してランチしています。 Q9:教育体制や先輩とのつながりはどんな感じですか? 部署によっても様々ですが、 OJT 制度はもちろん、私の部署では新任者向けのCi*X Expense/Journalizerの教育コンテンツも用意しています。 また、同部署だけでなく別部署の先輩とも同好会やメンターなどでつながることができるので、仕事やプライベートな相談もできますよ。 やりがいは? 私の仕事のやりがいは大きく2つあります。 ①お客様の反応をダイレクトに感じられること 導入後の感想など、お客様からダイレクトに反応をいただけることはとてもうれしいです。 お客様に直接やり取りできる導入の特権です。 ②自社製品なので今後よりよくするために社内で直接働きかけられること ISIDが作っている製品なので、お客様からの意見も元にしながら、製品をよりよくしていく働きかけをできることがうれしいです。 開発を行ったメンバが近くにいるので、実際の開発秘話なども聞けるのはとても楽しいし、愛着が湧きます。 大変なことは? 日々勉強が必要です。 自社の会計システム(私だとCi*X)を導入する上では ①製品知識 ②業務知識 の2つの知識を大前提にしています。 導入している製品を知り尽くし、お客様の業務背景を把握しきることで、課題に対して最も適切な解決策を提示できます。 大変な時もありますが、勉強したことを活用して、顧客課題に対してベストな解決策を提案できてお客様に喜んでいただけたときはこの上なく達成感を感じます。 システム「導入」にはどんな人が向いているのか? 丸4年働いて、私の考える導入に向いている人の特徴は下記4つだと思います。 ①チームで仕事をするのが好きな人 チームの大小はありますが、導入はチームプレイです。必ずお客様や社内のメンバで案件を進めるので、1人で何かをするだけではありません。人と一緒に何かをすることが好きな人は楽しいと思います。 ②人のために何かしたい!と思える人 導入はお客様企業の業務効率化を目指す仕事です。 お客様の業務をより効率的にいいものになるようにしたい!という思いをもって取り組んでいます。 ③人と話すことが好きな人 導入はとにかくコミュニケーションをとります。 ミーティング、メール、TEAMS・・・ツールはいろいろありますが、課題を「聞き出し」、解決策を「伝える」能力は必須です。 ④考えることが好きな人 時にはその場で解決できないような大きな課題に当たることがあります。 状況を整理し、コツコツ解決までたどり着けるように道筋を立てながら進める必要があります。 SIer はIT経験がなくても大丈夫なのか? IT経験なしで入社した私からQA形式で回答します。 Q1:IT経験がなくても大丈夫ですか? 世の中のどの記事を読んでも「大丈夫」と書いてあるものが多いので、 あえて私からは「"IT"や"課題解決の取り組み"に 嫌悪感がないのであれば 大丈夫」と伝えます。 私の経験からは、3日や1週間等ちょっと長めの(プログラミングとまではいかなくても)IT企業の課題解決系 インターン に行ってみることをお勧めします。 いろいろな資料を読んで頭を悩ませたり、 ヒアリ ングしたりすることが楽しい!と思えるのであればIT企業の仕事は楽しいと思います。 世の中にはたくさんのIT企業がありますので、IT経験の全くない方はぜひ1度 インターン に行ってみることをお勧めします! (もちろんISIDに来ていただけたらうれしいですが!) Q2:IT経験のある人と差を感じますか? 入社直後の研修の時はIT経験があったり、すでに資格を持っていたりする同期とは差を感じることがありました。 (そういう同期にプログラミングを教えてもらっていました) ですが、ISIDは研修制度がかなりしっかりしているので、研修が終わるころには実際の業務にあたってあまり引け目を感じないレベルまで知識はついたと思います。 Q3:IT経験がないと就活で不利そう。就活でどういうアピールをしていましたか? 実際に働いてみると、学生時代何をしていたか?なんてあまり気にならないので、個人的にあまり不利ではないと思います。 とはいえ、就活ではそうもいかないと思うので、私は就活で ①ITに興味があること ②自分に素質があること をアピールしていました。 ①はQ1で書いた通り インターン 経験を伝え、私はIT企業の仕事を楽しく感じます!とアピールしていました。 ②は、数学科でとにかく「論理的に考える」ことを実践していたので、それをアピールしていました。 Q4:大学の時の経験は今の仕事に役に立っていますか? 直接的ではないですが、以下の2つがとても役に立っています。 ①論理的思考力 私は 素数 を勉強していたので、直接的に 素数 が役に立っているのか?といわれるとそうではないのですが、研究活動を経て得た論理的に物事を考え、解決する力は今でも一番自分の武器だと自負しています。 ②教育経験 ずっと数学の先生を目指していたので、人に何か伝えることについては学生時代から塾のアルバイトなどを通して実践をしてきました。(教育実習も行きました) 今の仕事の中でも、講師役をやったり、人に何かを伝えたりする際にこの経験は強く活きています。 さいごに ISIDは新卒・中途関わらず一緒に働く仲間を募集しています 私の所属する部は、中途社員と新卒社員が半々、女性男性比率も半々、年齢も幅広いです。 いろいろな価値観を持つ方と、同じ目標をもって仕事をすることはとても面白いですよ! もしこの記事を読んでISIDや私の部署に少しでも興味を持っていただけたら、ぜひISIDの新卒採用サイトを覗いてみてください。 www.isid.co.jp (余談ですが:恥ずかしながら私も採用サイトの動画にコッソリ出させていただいております・・・!) 執筆: @kawashima.rei 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
アバター
こんにちは!グループ経営ソリューション( GMS )事業部5年目の川島です。現在は自社製品Ci*Xの導入やセールス支援をしています。 この記事では私のやっている仕事の内容や職場の雰囲気について、また、今学生で今後ISIDを目指す方向けに就活のお話も少しできればと思います。 本記事を通して私の仕事を知ってもらえたり、IT経験のない方でIT企業を目指す方に少しでも勇気を持ってもらえたりしたらうれしいです。 自己紹介 まずは簡単に、自己紹介を。 1. 年次 新卒で2019年にISIDに入社し、2023年1月で5年目になりました。 2. ISIDの入社のきめて 最初は大学の同級生に「ISIDの インターン が面白かった!」と言われ、その場の勢いでエントリーしました。 (採用面接でも何を思ったか正直に↑を伝えました。笑っていただけて安心した記憶が・・・) ・面接が楽しく、自分らしくいられたこと ・入社を悩んでいるときに会わせていただいた先輩社員が全員素敵だったこと を決め手に入社を決意しました。 3. 大学の専攻 大学、大学院と数学科に所属し、 素数 周りの研究をしていました。IT経験はありませんでした。 余談ですが:数学科の人ならわかると思いますが、 TeX をプログラミングだと思っていたレベルです。 4. ISID内の所属 グループ経営ソリューション事業部(会計のシステムを扱う事業部です)に所属しています。 配属から部署は変わっていません。 どんな環境でどんな仕事をしているの? QA形式で私の仕事を紹介します。 Q1:なんの仕事をしているのですか? 「Ci*X」という自社会計システムの導入やセールス支援の仕事をしています。 Q2:「導入」とは?「セールス支援」とは何ですか? 同じ事業部所属の後輩、安田さんの記事にわかりやすくまとめてあるので割愛します! 導入/セールスする製品は違いますが、やっていることはほぼ同じです。 安田さんの記事はこちら>新卒3年目社員の働き方紹介(GMS事業部) Q3:「Ci*X」とは何ですか? サイクロスと読みます。サイクロスシリーズは ISIDが作った 会計システムです。 シリーズとしては「Ci*X Expense」という経費精算システムを皮切りに、「Journalizer」(仕訳エンジン)、「Workflow」(汎用ワークフロー)、「Financials」(グループ統合会計システム)が続々とリリースされています。 Q4:「Ci*X」の魅力って何ですか? たくさんありますが、代表すると下記の2点です。 ・使いやすさ(UIにこだわっています) ・他システムとの連携のしやすさ(Journalizerを利用することで他システムへの柔軟な連携を実現します) ぜひ Ci*Xのホームページ も見ていただきたいです! Q5:プログラミングしていますか? プログラミングはしませんが、読むことはあります。 製品の仕様確認のために JAVA や SQL を読むこともあるので、知っておくことに損はありません。 (私は研修&配属後の勉強で知識を付けました) Q6:どんな人と仕事をしているのですか? 私の所属する導入グループは20名いて、年次や新卒or中途入社もバラバラです。 (ちょっと特徴的なのは、このグループには女性のほうが多い!ということですかね) Q7:1日の仕事の流れはどんな感じですか? 打合せと作業を交互に繰り返します。 入社してすぐは作業のほうが多かったですが、今はどの案件でもリーダー以上の役割が多いため、打合せ多めです。 Q8:在宅勤務は多いですか? 多いです。 今は OJT リーダーとして新人の教育担当をしており、1年目の後輩とは週1日程度一緒に出社してランチしています。 Q9:教育体制や先輩とのつながりはどんな感じですか? 部署によっても様々ですが、 OJT 制度はもちろん、私の部署では新任者向けのCi*X Expense/Journalizerの教育コンテンツも用意しています。 また、同部署だけでなく別部署の先輩とも同好会やメンターなどでつながることができるので、仕事やプライベートな相談もできますよ。 やりがいは? 私の仕事のやりがいは大きく2つあります。 ①お客様の反応をダイレクトに感じられること 導入後の感想など、お客様からダイレクトに反応をいただけることはとてもうれしいです。 お客様に直接やり取りできる導入の特権です。 ②自社製品なので今後よりよくするために社内で直接働きかけられること ISIDが作っている製品なので、お客様からの意見も元にしながら、製品をよりよくしていく働きかけをできることがうれしいです。 開発を行ったメンバが近くにいるので、実際の開発秘話なども聞けるのはとても楽しいし、愛着が湧きます。 大変なことは? 日々勉強が必要です。 自社の会計システム(私だとCi*X)を導入する上では ①製品知識 ②業務知識 の2つの知識を大前提にしています。 導入している製品を知り尽くし、お客様の業務背景を把握しきることで、課題に対して最も適切な解決策を提示できます。 大変な時もありますが、勉強したことを活用して、顧客課題に対してベストな解決策を提案できてお客様に喜んでいただけたときはこの上なく達成感を感じます。 システム「導入」にはどんな人が向いているのか? 丸4年働いて、私の考える導入に向いている人の特徴は下記4つだと思います。 ①チームで仕事をするのが好きな人 チームの大小はありますが、導入はチームプレイです。必ずお客様や社内のメンバで案件を進めるので、1人で何かをするだけではありません。人と一緒に何かをすることが好きな人は楽しいと思います。 ②人のために何かしたい!と思える人 導入はお客様企業の業務効率化を目指す仕事です。 お客様の業務をより効率的にいいものになるようにしたい!という思いをもって取り組んでいます。 ③人と話すことが好きな人 導入はとにかくコミュニケーションをとります。 ミーティング、メール、TEAMS・・・ツールはいろいろありますが、課題を「聞き出し」、解決策を「伝える」能力は必須です。 ④考えることが好きな人 時にはその場で解決できないような大きな課題に当たることがあります。 状況を整理し、コツコツ解決までたどり着けるように道筋を立てながら進める必要があります。 SIer はIT経験がなくても大丈夫なのか? IT経験なしで入社した私からQA形式で回答します。 Q1:IT経験がなくても大丈夫ですか? 世の中のどの記事を読んでも「大丈夫」と書いてあるものが多いので、 あえて私からは「"IT"や"課題解決の取り組み"に 嫌悪感がないのであれば 大丈夫」と伝えます。 私の経験からは、3日や1週間等ちょっと長めの(プログラミングとまではいかなくても)IT企業の課題解決系 インターン に行ってみることをお勧めします。 いろいろな資料を読んで頭を悩ませたり、 ヒアリ ングしたりすることが楽しい!と思えるのであればIT企業の仕事は楽しいと思います。 世の中にはたくさんのIT企業がありますので、IT経験の全くない方はぜひ1度 インターン に行ってみることをお勧めします! (もちろんISIDに来ていただけたらうれしいですが!) Q2:IT経験のある人と差を感じますか? 入社直後の研修の時はIT経験があったり、すでに資格を持っていたりする同期とは差を感じることがありました。 (そういう同期にプログラミングを教えてもらっていました) ですが、ISIDは研修制度がかなりしっかりしているので、研修が終わるころには実際の業務にあたってあまり引け目を感じないレベルまで知識はついたと思います。 Q3:IT経験がないと就活で不利そう。就活でどういうアピールをしていましたか? 実際に働いてみると、学生時代何をしていたか?なんてあまり気にならないので、個人的にあまり不利ではないと思います。 とはいえ、就活ではそうもいかないと思うので、私は就活で ①ITに興味があること ②自分に素質があること をアピールしていました。 ①はQ1で書いた通り インターン 経験を伝え、私はIT企業の仕事を楽しく感じます!とアピールしていました。 ②は、数学科でとにかく「論理的に考える」ことを実践していたので、それをアピールしていました。 Q4:大学の時の経験は今の仕事に役に立っていますか? 直接的ではないですが、以下の2つがとても役に立っています。 ①論理的思考力 私は 素数 を勉強していたので、直接的に 素数 が役に立っているのか?といわれるとそうではないのですが、研究活動を経て得た論理的に物事を考え、解決する力は今でも一番自分の武器だと自負しています。 ②教育経験 ずっと数学の先生を目指していたので、人に何か伝えることについては学生時代から塾のアルバイトなどを通して実践をしてきました。(教育実習も行きました) 今の仕事の中でも、講師役をやったり、人に何かを伝えたりする際にこの経験は強く活きています。 さいごに ISIDは新卒・中途関わらず一緒に働く仲間を募集しています 私の所属する部は、中途社員と新卒社員が半々、女性男性比率も半々、年齢も幅広いです。 いろいろな価値観を持つ方と、同じ目標をもって仕事をすることはとても面白いですよ! もしこの記事を読んでISIDや私の部署に少しでも興味を持っていただけたら、ぜひISIDの新卒採用サイトを覗いてみてください。 www.isid.co.jp (余談ですが:恥ずかしながら私も採用サイトの動画にコッソリ出させていただいております・・・!) 執筆: @kawashima.rei 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
アバター
こんにちは、ISID 金融ソリューション事業部の岡崎です。 今回は 前回のこちらの記事(UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する) の続きとして、 EpicGames社が提供する ゲームエンジン 、UnrealEngine5 のPlugin「PixelStreaming」を使用し、UE5でブラウザの JavaScript で反応可能なカスタムイベントの作成や、一定時間非アクティブであったユーザーの接続を自動的に切断する タイムアウト の作成などを行いました。 はじめに PixelStreamingを利用してブラウザの JavaScript を発火させるためには、UE上のBlueprintと JavaScript を紐づける必要があります。 前回のこちらの記事(UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する) では、ブラウザ上にボタンを設置し押下時にBlueprintを利用してUE画面上に文字をプリントする実装を行いました。 今回はUEを操作してプレイヤーと任意のオブジェクトが 接触 した際にBlueprintで 接触 を感知し、 JavaScript に通信を行いブラウザ上でイベントを発火させる方法について解説します。 検証環境/ツール Unreal Engine5.1 AWS EC2 Windows _Server-2022-English-Full-Base-2023.01.19 Chrome ver.110.0.5481.177 実装手順 1. プレイヤーが任意のオブジェクトに 接触 した際にイベントを発火させる処理 1-1. プレイヤーの他オブジェクトの 接触 を感知するBlueprintを作成 1-2. UEからブラウザに通信する処理を作成 1-3. JavaScript でUEからの通信を受け取る処理を作成 1-4. PixelStreamingを起動して接続テスト 2. 一定時間非アクティブであったユーザーがいた場合 タイムアウト する処理 2-1. タイムアウト 処理の設定 2-2. タイムアウト 処理の実行テスト 1. プレイヤーが任意のオブジェクトに 接触 した際にイベントを発火させる処理 1-1. プレイヤーの他オブジェクトの 接触 を感知するBlueprintを作成 今回の検証では「ThirdPersonTemplate」を使用してプロジェクトを作成します。 プロジェクト作成からPixelStreamingの プラグイン を有効にするまでの流れは、 前回のこちらの記事(UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する) を参考にしてください。 まずは、プレイヤーが何かに 接触 した際、UE上に文字列を表示するBlueprintを作成します。 使用するBlueprintは「ThirdPersonTemplate」でプロジェクトを作成した際に自動的に作られるプレイヤーアクターのBlueprintである「BP_ThirdPersonCharacter」を使用します。 All>Content>ThirdPerson>Blueprints内に「BP_ThirdPersonCharacter」を見つけることができます。 Blueprint内のEventGraphには既にプレイヤーの動きやカメラ操作についてなどの処理が記述されています。 そのファイル内の空いている箇所に「Event Hit」というイベントノードを追加し、PrintStringと繋ぎます。 これにより、UEプレイ中にプレイヤーアクターが何かに 接触 した際、UE画面上に「Hit」の文字がプリントされます。 1-2. UEからブラウザに通信する処理を作成 ここまではUE上で「Hit」の文字をプリントするだけでしたが、今回はこの 接触 した際のイベントをUEからブラウザに通信しなければいけないので、その処理を追加します。 PixelStreamingの プラグイン を追加した状態でBlueprintエディターの左側のComponentsタブのAddを押下し、PixelStreamingInputを検索し左側の コンポーネント 一覧に追加します。 次に追加されたPixelStreamingInputを右側のグラフに ドラッグ&ドロップ を行い、ノードを追加します。 接触 した際のイベントをUEからブラウザに通信するためには今追加したPixelStreamingInput内のSendPixelStreamingResponseというファンクションを使用します。 SendPixelStreamingResponse内にDescriptorのボックスがあるので、今回はわかりやすく「PlayerHitEvent」と記述します。 次にこのファンクションをプレイヤーが 接触 した際に発火するようにするため、先ほど作成した「Event Hit」のノードとつなげます。 以上でUE側の、ブラウザに通信するための処理は終わりなので JavaScript 側の処理に移っていきます。 1-3. JavaScript でUEからの通信を受け取る処理を作成 前回のブラウザ上にボタンを設置し押下時にBlueprintを利用してUE画面上に文字をプリントする実装の記事 と同様に、 player.html内部に JavaScript の処理を追加していきました。 まずはplayer.html内で呼ばれているapp.jsで提供されているaddResponseEventListener関数を使用して、イベントハンドル用の関数を登録します。 次にplayer.htmlの JavaScript に、UEからの通信を受信するたびに起動する イベントハンドラ 関数を記述します。 これにより、UEのBlueprintで設定したSendPixelStreamingResponseノードにより送信された文字列引数(Descriptorで指定した「PlayerHitEvent」の文字列)が渡されます。 // リスナー登録 addResponseEventListener("handle_responses", myHandleResponseFunction); //イベントハンドラ関数作成 function myHandleResponseFunction(data) { if (data === "PlayerHitEvent") { console.log("プレイヤーがオブジェクトに接触しました"); } else { console.log("その他のイベント"); } } この関数を作成することで、UE側からプレイヤーがオブジェクトに 接触 した際に発行されるBlueprintで、Descriptorで指定した「PlayerHitEvent」の文字列を JavaScript 側で受信できるようになります。 また、より複雑なデータをやり取りする必要がある場合は、Descriptor内の文字列を JSON 形式に記述し、 JavaScript 側で文字列をデコードし JSON として使用する方法も公式で推奨されています。 1-4. PixelStreamingを起動して接続テスト ここまでで、UE側のBlueprintの設定と JavaScript で イベントハンドラ ーとしての設定、ログを出力する処理の全ての実装が完了したので、実際にPixelStreamingを起動して接続テストを行います。 UEのプロジェクトをパッケージ化し、SignallingServerを立ち上げてアプリを起動します。 プレイヤーを操作し、ゲーム内のオブジェクトや壁などに 接触 すると、ブラウザの検証画面から JavaScript で出力したコンソールログを見ることができます。 今回はプレイヤーをジャンプさせ、着地したときに地面のオブジェクトとプレイヤーが 接触 したため、 JavaScript で「プレイヤーがオブジェクトと 接触 しました」というログを出力しています。 今回は検証のため 接触 とログ出力だけを行いましたが、Blueprintと JavaScript を変更することで、 例えばプレイヤーが獲得したアイテムを実際に購入できるECページに遷移させるといった従来の JavaScript で行える処理なども記述できます。 2. 一定時間非アクティブであったユーザーがいた場合 タイムアウト する処理 2-1. タイムアウト 処理の設定 UEを使用してマッチメイキングを行うアプリなどをPixelStreamingで配信したい際、UEのアプリケーションやサーバー負荷の観点から、非アクティブなユーザーの接続を切りたい場合などが出てくると思います。 そういった際に必要になってくるのが非アクティブなユーザーに対する タイムアウト 処理です。 PixelStreaming プラグイン では、app.jsの中で既に タイムアウト の関数が既に用意されています。 デフォルトでは タイムアウト をしない設定になっているので、 JavaScript を使用して設定を変えていきます。 player.htmlの中に下記3つの記述を JavaScript を追加していきます。 afk.enabled = true; AFKとはAway From Keybordの略で、ユーザーが一定期間操作を行っていない事を指します。 デフォルトではfalseになっているので、有効に変更します。 afk.warnTimeout = 60; ここでは タイムアウト までの時間を設定します。デフォルトでは120秒になっているので、今回は60秒に変更しました。 afk.closeTimeout = 20; ここでは「afk.warnTimeout」の期間が過ぎてから、ユーザーに対して接続切断の警告メッセージを表示する秒数を設定します。 画面上ではここで設定した秒数のカウントダウンが表示され、設定した時間を過ぎると接続が切断されます。 2-2. タイムアウト 処理の実行テスト 最後に実際に タイムアウト 処理が実行できるかテストをしてみます。 afk.warnTimeoutで設定した秒数を放置した後、下のような画面が出ていれば、正常に タイムアウト 処理の設定が変更できています。 また、AFKにはemitCommandやemitUIInteraction関数で設定したインタ ラク ションも挙動に反映されてしまうので注意が必要です。 タイムアウト により接続が切断されると、PixelStreaming開始時に表示される画面に変わります。 タイムアウト の処理の説明は以上になります。 所感 UEから JavaScript への通信や、逆に JavaScript からUEへの通信の仕方はとても簡略化してあり、使いやすいと感じました。 文字列でのやり取りだけでなく、Blueprintを工夫することで JSON 形式に変更することで、Webアプリケーションでできることの幅はとても広がりそうだと感じました。 特に ノンゲーム の領域では、別のURLへ遷移させたり、Webアプリケーションにつなげることでプロダクトの幅や品質を向上させることができると思います。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://docs.unrealengine.com/5.1/ja/customizing-the-player-web-page-in-unreal-engine/ 執筆: @okazaki.wataru 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
こんにちは、ISID 金融ソリューション事業部の岡崎です。 今回は 前回のこちらの記事(UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する) の続きとして、 EpicGames社が提供する ゲームエンジン 、UnrealEngine5 のPlugin「PixelStreaming」を使用し、UE5でブラウザの JavaScript で反応可能なカスタムイベントの作成や、一定時間非アクティブであったユーザーの接続を自動的に切断する タイムアウト の作成などを行いました。 はじめに PixelStreamingを利用してブラウザの JavaScript を発火させるためには、UE上のBlueprintと JavaScript を紐づける必要があります。 前回のこちらの記事(UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する) では、ブラウザ上にボタンを設置し押下時にBlueprintを利用してUE画面上に文字をプリントする実装を行いました。 今回はUEを操作してプレイヤーと任意のオブジェクトが 接触 した際にBlueprintで 接触 を感知し、 JavaScript に通信を行いブラウザ上でイベントを発火させる方法について解説します。 検証環境/ツール Unreal Engine5.1 AWS EC2 Windows _Server-2022-English-Full-Base-2023.01.19 Chrome ver.110.0.5481.177 実装手順 1. プレイヤーが任意のオブジェクトに 接触 した際にイベントを発火させる処理 1-1. プレイヤーの他オブジェクトの 接触 を感知するBlueprintを作成 1-2. UEからブラウザに通信する処理を作成 1-3. JavaScript でUEからの通信を受け取る処理を作成 1-4. PixelStreamingを起動して接続テスト 2. 一定時間非アクティブであったユーザーがいた場合 タイムアウト する処理 2-1. タイムアウト 処理の設定 2-2. タイムアウト 処理の実行テスト 1. プレイヤーが任意のオブジェクトに 接触 した際にイベントを発火させる処理 1-1. プレイヤーの他オブジェクトの 接触 を感知するBlueprintを作成 今回の検証では「ThirdPersonTemplate」を使用してプロジェクトを作成します。 プロジェクト作成からPixelStreamingの プラグイン を有効にするまでの流れは、 前回のこちらの記事(UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する) を参考にしてください。 まずは、プレイヤーが何かに 接触 した際、UE上に文字列を表示するBlueprintを作成します。 使用するBlueprintは「ThirdPersonTemplate」でプロジェクトを作成した際に自動的に作られるプレイヤーアクターのBlueprintである「BP_ThirdPersonCharacter」を使用します。 All>Content>ThirdPerson>Blueprints内に「BP_ThirdPersonCharacter」を見つけることができます。 Blueprint内のEventGraphには既にプレイヤーの動きやカメラ操作についてなどの処理が記述されています。 そのファイル内の空いている箇所に「Event Hit」というイベントノードを追加し、PrintStringと繋ぎます。 これにより、UEプレイ中にプレイヤーアクターが何かに 接触 した際、UE画面上に「Hit」の文字がプリントされます。 1-2. UEからブラウザに通信する処理を作成 ここまではUE上で「Hit」の文字をプリントするだけでしたが、今回はこの 接触 した際のイベントをUEからブラウザに通信しなければいけないので、その処理を追加します。 PixelStreamingの プラグイン を追加した状態でBlueprintエディターの左側のComponentsタブのAddを押下し、PixelStreamingInputを検索し左側の コンポーネント 一覧に追加します。 次に追加されたPixelStreamingInputを右側のグラフに ドラッグ&ドロップ を行い、ノードを追加します。 接触 した際のイベントをUEからブラウザに通信するためには今追加したPixelStreamingInput内のSendPixelStreamingResponseというファンクションを使用します。 SendPixelStreamingResponse内にDescriptorのボックスがあるので、今回はわかりやすく「PlayerHitEvent」と記述します。 次にこのファンクションをプレイヤーが 接触 した際に発火するようにするため、先ほど作成した「Event Hit」のノードとつなげます。 以上でUE側の、ブラウザに通信するための処理は終わりなので JavaScript 側の処理に移っていきます。 1-3. JavaScript でUEからの通信を受け取る処理を作成 前回のブラウザ上にボタンを設置し押下時にBlueprintを利用してUE画面上に文字をプリントする実装の記事 と同様に、 player.html内部に JavaScript の処理を追加していきました。 まずはplayer.html内で呼ばれているapp.jsで提供されているaddResponseEventListener関数を使用して、イベントハンドル用の関数を登録します。 次にplayer.htmlの JavaScript に、UEからの通信を受信するたびに起動する イベントハンドラ 関数を記述します。 これにより、UEのBlueprintで設定したSendPixelStreamingResponseノードにより送信された文字列引数(Descriptorで指定した「PlayerHitEvent」の文字列)が渡されます。 // リスナー登録 addResponseEventListener("handle_responses", myHandleResponseFunction); //イベントハンドラ関数作成 function myHandleResponseFunction(data) { if (data === "PlayerHitEvent") { console.log("プレイヤーがオブジェクトに接触しました"); } else { console.log("その他のイベント"); } } この関数を作成することで、UE側からプレイヤーがオブジェクトに 接触 した際に発行されるBlueprintで、Descriptorで指定した「PlayerHitEvent」の文字列を JavaScript 側で受信できるようになります。 また、より複雑なデータをやり取りする必要がある場合は、Descriptor内の文字列を JSON 形式に記述し、 JavaScript 側で文字列をデコードし JSON として使用する方法も公式で推奨されています。 1-4. PixelStreamingを起動して接続テスト ここまでで、UE側のBlueprintの設定と JavaScript で イベントハンドラ ーとしての設定、ログを出力する処理の全ての実装が完了したので、実際にPixelStreamingを起動して接続テストを行います。 UEのプロジェクトをパッケージ化し、SignallingServerを立ち上げてアプリを起動します。 プレイヤーを操作し、ゲーム内のオブジェクトや壁などに 接触 すると、ブラウザの検証画面から JavaScript で出力したコンソールログを見ることができます。 今回はプレイヤーをジャンプさせ、着地したときに地面のオブジェクトとプレイヤーが 接触 したため、 JavaScript で「プレイヤーがオブジェクトと 接触 しました」というログを出力しています。 今回は検証のため 接触 とログ出力だけを行いましたが、Blueprintと JavaScript を変更することで、 例えばプレイヤーが獲得したアイテムを実際に購入できるECページに遷移させるといった従来の JavaScript で行える処理なども記述できます。 2. 一定時間非アクティブであったユーザーがいた場合 タイムアウト する処理 2-1. タイムアウト 処理の設定 UEを使用してマッチメイキングを行うアプリなどをPixelStreamingで配信したい際、UEのアプリケーションやサーバー負荷の観点から、非アクティブなユーザーの接続を切りたい場合などが出てくると思います。 そういった際に必要になってくるのが非アクティブなユーザーに対する タイムアウト 処理です。 PixelStreaming プラグイン では、app.jsの中で既に タイムアウト の関数が既に用意されています。 デフォルトでは タイムアウト をしない設定になっているので、 JavaScript を使用して設定を変えていきます。 player.htmlの中に下記3つの記述を JavaScript を追加していきます。 afk.enabled = true; AFKとはAway From Keybordの略で、ユーザーが一定期間操作を行っていない事を指します。 デフォルトではfalseになっているので、有効に変更します。 afk.warnTimeout = 60; ここでは タイムアウト までの時間を設定します。デフォルトでは120秒になっているので、今回は60秒に変更しました。 afk.closeTimeout = 20; ここでは「afk.warnTimeout」の期間が過ぎてから、ユーザーに対して接続切断の警告メッセージを表示する秒数を設定します。 画面上ではここで設定した秒数のカウントダウンが表示され、設定した時間を過ぎると接続が切断されます。 2-2. タイムアウト 処理の実行テスト 最後に実際に タイムアウト 処理が実行できるかテストをしてみます。 afk.warnTimeoutで設定した秒数を放置した後、下のような画面が出ていれば、正常に タイムアウト 処理の設定が変更できています。 また、AFKにはemitCommandやemitUIInteraction関数で設定したインタ ラク ションも挙動に反映されてしまうので注意が必要です。 タイムアウト により接続が切断されると、PixelStreaming開始時に表示される画面に変わります。 タイムアウト の処理の説明は以上になります。 所感 UEから JavaScript への通信や、逆に JavaScript からUEへの通信の仕方はとても簡略化してあり、使いやすいと感じました。 文字列でのやり取りだけでなく、Blueprintを工夫することで JSON 形式に変更することで、Webアプリケーションでできることの幅はとても広がりそうだと感じました。 特に ノンゲーム の領域では、別のURLへ遷移させたり、Webアプリケーションにつなげることでプロダクトの幅や品質を向上させることができると思います。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://docs.unrealengine.com/5.1/ja/customizing-the-player-web-page-in-unreal-engine/ 執筆: @okazaki.wataru 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
金融ソリューション事業部 石沢です。ISIDは現在全社で1on1の実施を奨励しており、私自身も部署のメンバーに対して1on1を実施しています。本記事ではISIDにおける1on1の状況と、昨年参加したエール社提供の1on1改善ワークショップ【聴くトレ】に参加した経験と感想をご紹介します。 ISIDにおける1on1の状況 ISIDでは現在1on1を人事施策として奨励しています。実施は強制ではないですが、社として様々な支援を受けることが可能で、実施のためのガイダンスやツール( TeamUp )を希望者は全員利用できるようになっています。ISIDの1on1の取組みは以下サイトでも紹介されていますので、興味があれば参照してください。 わたしと1on1 現在は社の人事施策として推進されている1on1ですが、わたし自身はそれより数年前から書籍やネット記事などを参考に自分のプロジェクトに実験的に適用していました。その時に参考にしていた情報は書籍「 HIGH OUTPUT MANAGEMENT 」(当時は旧版「 インテル 経営の秘密」)など、あとは海外で働く日本人エンジニアの記ネット記事などです。あとは、 見よう見まね ですね。 ISIDでは現場でいろいろ勝手に工夫する文化があるので、わたしと同じようにいろいろな現場で1on1の取組みは進んでいたようです。すぐに社内でも「1on1をやってみたらよかった」「所属部員とのコミュニケーションに1on1を活用するのが良い」という声がすぐに聞こえるようになっていました。社の規定で定められた上司部下の面談はあるのですが、それを拡張してみんな工夫していったという感じです。 そんな中で、2021年には人事部主導で1on1を支援するツールのトライアルや全社展開なども行われ、1on1に関する基本的な ガイドライン なども公開されました。現在では多くの社員が1on1をやっている状況になっています。利用は任意ですが、支援ツールの TeamUp はリマインドや記録機能などが便利で、わたしは愛用しています。わたし自身は隔週の頻度でメンバーとの1on1を日常的に実施しています。 【聴くトレ】参加経緯 日常的に1on1をやってはいるのですが、すこし不安はありました。 自分が1on1を効果的にやれているのかわからない 改善すべき点があるのかどうかもわからない ちょうどそのころ読んでいた「 ヤフーの1on1 」という本で、ヤフーさんでは教育施策として「1on1チェック」というアセスメントや研修をやっているという事が紹介されていました。 うらやま…… 実際のメンバーとの1on1は、内容的にも第 三者 に見てもらったり、話した内容を開示するわけにもいかないし、振返ってみると 練習する場に飢えていたんだ と思います。 素振りがしたいんや…… そう思っていたところに、人事部から エール社さんの 【聴くトレ】 のトライアルをやるので参加者を公募するという話が飛び込んできたので、さっそく飛びついて参加してみたのです。 【聴くトレ】とは 具体的な内容は公式サイトを見ていただくとして、ざっくりこのような内容でした。 キック オフミ ーティングで取組みの説明とコンセプトの話を聞く 自習用動画が配布されるので、【聴くトレ】が重視する注意点、コツを学ぶ サポーターと呼ばれる社外メンターと練習や相談をする 実際に1on1をやる 1on1を評価してもらい、よりよくするためのアド バイス を貰う アド バイス して貰ったことが実務に旨く適用できたかのふりかえりを一緒にやる これは私の問題意識にピッタリでした。 【聴くトレ】の感想 個人的には自分の1on1について振り返る良い機会だったと思います。また社外の方と1on1をすることも新鮮で受講して良かった。ずっと受講し続けたいというわけではありませんが、定期的にこういったサービスで自分のフォームを点検できるのは大変良いと思います。今回はトライアルということでしたが、人事部でも今後継続する方向性で検討しているということで、期待です。 その他の細かい感想はこちら コストパフォーマンスは未確認です(人事部さんありがとう!) サポーターとのマッチング問題はありそうですが、わたしを担当していただいた方は非常に面白かったです。ただ、実際の1on1でも相性の問題はあるはずなので苦手なタイプの人とマッチングしてしまっても、それも修行だと思えば楽しめそう セッションはVoice Onlyのオンライン実施形式(専用のサポートサイト経由での実施となります)。表情が見えないのはちょっと残念でしたが、場所を選ばずに実施できるというメリットもありそう セッションは緊張します(特に初回)。どんなことを言われるのだろうかという不安もありましたが、至らない点もやさしくフィードバックしてもらえて杞憂でしたが…… 普段つかわない筋肉を使ったような疲れはありました(それがやりたかったので問題はないのですが) 詳しくはナイショですが、自分自身の1on1についてはひとつ重要な課題を発見できて良かったです。 客観的に観察してもらえるというのは、やはりよいですね 。うまく解消できているかはわかりませんが、よりそったアド バイス をしていただき、練習させてもらったので改善したと信じたい むすびにかえて 本記事ではISIDの1on1の取組みと、管理職として1on1力をレベルアップしようとしてみたわたしの経験についてご紹介しましたが、いかがでしたか。1on1がうまくいかないというお悩みをお持ちの管理職な方はエールさんのサービスをチェックすると良いと思いますが、むしろそういったサポートも受けられるISIDへの転職もご検討してはいかがでしょうか? 私たちは一緒に働いてくれる仲間を募集しています! ISID 募集職種一覧 執筆: Ishizawa Kento (@kent) 、レビュー: @kou.kinyo ( Shodo で執筆されました )
アバター
金融ソリューション事業部 石沢です。ISIDは現在全社で1on1の実施を奨励しており、私自身も部署のメンバーに対して1on1を実施しています。本記事ではISIDにおける1on1の状況と、昨年参加したエール社提供の1on1改善ワークショップ【聴くトレ】に参加した経験と感想をご紹介します。 ISIDにおける1on1の状況 ISIDでは現在1on1を人事施策として奨励しています。実施は強制ではないですが、社として様々な支援を受けることが可能で、実施のためのガイダンスやツール( TeamUp )を希望者は全員利用できるようになっています。ISIDの1on1の取組みは以下サイトでも紹介されていますので、興味があれば参照してください。 わたしと1on1 現在は社の人事施策として推進されている1on1ですが、わたし自身はそれより数年前から書籍やネット記事などを参考に自分のプロジェクトに実験的に適用していました。その時に参考にしていた情報は書籍「 HIGH OUTPUT MANAGEMENT 」(当時は旧版「 インテル 経営の秘密」)など、あとは海外で働く日本人エンジニアの記ネット記事などです。あとは、 見よう見まね ですね。 ISIDでは現場でいろいろ勝手に工夫する文化があるので、わたしと同じようにいろいろな現場で1on1の取組みは進んでいたようです。すぐに社内でも「1on1をやってみたらよかった」「所属部員とのコミュニケーションに1on1を活用するのが良い」という声がすぐに聞こえるようになっていました。社の規定で定められた上司部下の面談はあるのですが、それを拡張してみんな工夫していったという感じです。 そんな中で、2021年には人事部主導で1on1を支援するツールのトライアルや全社展開なども行われ、1on1に関する基本的な ガイドライン なども公開されました。現在では多くの社員が1on1をやっている状況になっています。利用は任意ですが、支援ツールの TeamUp はリマインドや記録機能などが便利で、わたしは愛用しています。わたし自身は隔週の頻度でメンバーとの1on1を日常的に実施しています。 【聴くトレ】参加経緯 日常的に1on1をやってはいるのですが、すこし不安はありました。 自分が1on1を効果的にやれているのかわからない 改善すべき点があるのかどうかもわからない ちょうどそのころ読んでいた「 ヤフーの1on1 」という本で、ヤフーさんでは教育施策として「1on1チェック」というアセスメントや研修をやっているという事が紹介されていました。 うらやま…… 実際のメンバーとの1on1は、内容的にも第 三者 に見てもらったり、話した内容を開示するわけにもいかないし、振返ってみると 練習する場に飢えていたんだ と思います。 素振りがしたいんや…… そう思っていたところに、人事部から エール社さんの 【聴くトレ】 のトライアルをやるので参加者を公募するという話が飛び込んできたので、さっそく飛びついて参加してみたのです。 【聴くトレ】とは 具体的な内容は公式サイトを見ていただくとして、ざっくりこのような内容でした。 キック オフミ ーティングで取組みの説明とコンセプトの話を聞く 自習用動画が配布されるので、【聴くトレ】が重視する注意点、コツを学ぶ サポーターと呼ばれる社外メンターと練習や相談をする 実際に1on1をやる 1on1を評価してもらい、よりよくするためのアド バイス を貰う アド バイス して貰ったことが実務に旨く適用できたかのふりかえりを一緒にやる これは私の問題意識にピッタリでした。 【聴くトレ】の感想 個人的には自分の1on1について振り返る良い機会だったと思います。また社外の方と1on1をすることも新鮮で受講して良かった。ずっと受講し続けたいというわけではありませんが、定期的にこういったサービスで自分のフォームを点検できるのは大変良いと思います。今回はトライアルということでしたが、人事部でも今後継続する方向性で検討しているということで、期待です。 その他の細かい感想はこちら コストパフォーマンスは未確認です(人事部さんありがとう!) サポーターとのマッチング問題はありそうですが、わたしを担当していただいた方は非常に面白かったです。ただ、実際の1on1でも相性の問題はあるはずなので苦手なタイプの人とマッチングしてしまっても、それも修行だと思えば楽しめそう セッションはVoice Onlyのオンライン実施形式(専用のサポートサイト経由での実施となります)。表情が見えないのはちょっと残念でしたが、場所を選ばずに実施できるというメリットもありそう セッションは緊張します(特に初回)。どんなことを言われるのだろうかという不安もありましたが、至らない点もやさしくフィードバックしてもらえて杞憂でしたが…… 普段つかわない筋肉を使ったような疲れはありました(それがやりたかったので問題はないのですが) 詳しくはナイショですが、自分自身の1on1についてはひとつ重要な課題を発見できて良かったです。 客観的に観察してもらえるというのは、やはりよいですね 。うまく解消できているかはわかりませんが、よりそったアド バイス をしていただき、練習させてもらったので改善したと信じたい むすびにかえて 本記事ではISIDの1on1の取組みと、管理職として1on1力をレベルアップしようとしてみたわたしの経験についてご紹介しましたが、いかがでしたか。1on1がうまくいかないというお悩みをお持ちの管理職な方はエールさんのサービスをチェックすると良いと思いますが、むしろそういったサポートも受けられるISIDへの転職もご検討してはいかがでしょうか? 私たちは一緒に働いてくれる仲間を募集しています! ISID 募集職種一覧 執筆: Ishizawa Kento (@kent) 、レビュー: @kou.kinyo ( Shodo で執筆されました )
アバター
こんにちは! 電通国際情報サービス 人事部 人材・組織開発グループの川谷です。 今回は、 UdemyBusiness (以下UB)を活用し、 個々に沿った内定者向け教育コンテンツを提供した内容を共有します。 本記事をこんな方におすすめします 就職活動を控えた大学生の方 Udemy,UBの受講を検討されている方 自社でUBを導入済み / 検討中の研修担当者 本記事のあらすじ ISIDでの内定者向け教育コンテンツにおいて、従来の全体一律ト レーニン グ型から「全体のベースアップ+個々人に沿うスキルト レーニン グ」のハイブリッド型に提供をシフトした。 内定者が各自必要とするインプットを得られるようにUBを導入した。そして、入社後の教育にもUBを利用することで、働きながら学ぶ姿勢を支援できる仕組みとした。 Udemy・Udemy Businessとは Udemyは、プログラミングやビジネス、 自己啓発 など様々な分野のコンテンツを提供する、オンライン学習プラットフォームです。各分野の専門家が、合計200,000を超えるコンテンツを提供しています。 UBは、そんなUdemyの講座群から、一般レビューをもとに厳選された講座を受け放題で受講できる法人向けのサービスです。 ISIDにおけるこれまでのUB活用 ISIDでは、2022年上旬にUBを全社導入しました。 公募制で展開しており、23年2月21日現在、約500名程度が受講しています。 コンテンツの質・受講自由度の高さから、受講人数は期を重ねるごとに増加傾向にあり、効果的な受講支援や階層別研修の補助利用など活用の幅を広げております。(UB受講者用Teamsの作成、新人研修の補助としての導入など) これまでのISIDにおける内定者教育について ISIDでは内定者に対して、入社後の早期活躍を支援すべく「心・技・体」の観点で学習機会を提供してきました。 (記載の教育施策は一例です) これまでの教育施策は、主にIT理解度( 基本情報技術者試験 (FE)の取得状況など)に応じて個別に内容を変更していました。しかし、その施策内容の検討/選定が難しく課題となっていました。 そのため、内定者自身が、各自のレベルに合った講座を自由に選べる点が、UB内定者提供に踏み切った理由の一つでした。 そんな内定者教育に、UBをどう活用しているのか? UBのコンテンツ数の多さ、そしてそれらを自由に選んで受講できるメリットを活かし、下記の通り必須受講と任意受講を組み合わせて運用しています。 必須受講 IT理解度が低い人向けに、IT基礎知識を学ぶ講座を人事部で選定し、受講を指示( 基本情報技術者試験 に関するコンテンツなど) 任意受講 一部講座を推薦(ビジネスマナーやセルフマネジメント、論理思考力など) その他、自身が学習したい分野、不足していると思う分野に関する講座 上記の通りISIDではUBを、内定者個々の学習意欲に合わせた教育コンテンツ提供にも活用しています。 どう管理しているの?受講生の実施までチェックしているの? 前章で紹介した推奨コンテンツは、UBの"ラーニングパス機能"を用いて、各内定者の受講状況をチェックできるようにしています。ラーニングパスとは、UB上の講座や社内のコンテンツ、外部リンクなどを組み合わせて作成する"音楽のプレイリスト"のようなものであり、受講状況もチェック可能です。 この機能を活用し、学んでほしい内容を詰め込んだ内定者教育専用のラーニングパスを作成して、展開および受講状況をチェックしています。 人事部としての所感 結論としては、UBは内定者教育にもフィットしている実感があります。 理由は、これまで課題と感じていた「内定者それぞれに合わせた教育コンテンツ提供」が、前述の通りUBが有する豊富なコンテンツ量・受講者の管理が容易なラーニングパスによって対処できていると感じるためです。 内定者によるフィードバックはこれからですが、自身の興味に合うコンテンツをいつでも自由に受講できるUBは、受講者側にも大きなメリットなのではないかと個人的に思っています。 今後の展望 今後の展望としては、以下のようにUBを活用したいと考えています。 (入社後の教育)入社後の教育にもUBを活用し、学び続ける姿勢をサポートしたい。 (今後の内定者研修)今年度と同様に内定者期間にUBを提供。内定者からのフィードバックを受け、ラーニングパスの中身をブラッシュアップする。 おわりに 以上が、弊社の内定者教育にUBを活用した事例の紹介です。 UBを内定者に提供することは、業務の傍ら学び続ける習慣のキッカケになると確信しております。 もちろん内定者教育をeラーニングだけに留めようという思いはありません。ただUBが簡単に活用できるからこそ、より一層狙いを持って、eラーニングもしくは対面型研修を選択するべきだと私個人としては考えています。 本記事が誰かのお役に立てば幸いです。 執筆: @kawatani.tomoro 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
アバター
こんにちは! 電通国際情報サービス 人事部 人材・組織開発グループの川谷です。 今回は、 UdemyBusiness (以下UB)を活用し、 個々に沿った内定者向け教育コンテンツを提供した内容を共有します。 本記事をこんな方におすすめします 就職活動を控えた大学生の方 Udemy,UBの受講を検討されている方 自社でUBを導入済み / 検討中の研修担当者 本記事のあらすじ ISIDでの内定者向け教育コンテンツにおいて、従来の全体一律ト レーニン グ型から「全体のベースアップ+個々人に沿うスキルト レーニン グ」のハイブリッド型に提供をシフトした。 内定者が各自必要とするインプットを得られるようにUBを導入した。そして、入社後の教育にもUBを利用することで、働きながら学ぶ姿勢を支援できる仕組みとした。 Udemy・Udemy Businessとは Udemyは、プログラミングやビジネス、 自己啓発 など様々な分野のコンテンツを提供する、オンライン学習プラットフォームです。各分野の専門家が、合計200,000を超えるコンテンツを提供しています。 UBは、そんなUdemyの講座群から、一般レビューをもとに厳選された講座を受け放題で受講できる法人向けのサービスです。 ISIDにおけるこれまでのUB活用 ISIDでは、2022年上旬にUBを全社導入しました。 公募制で展開しており、23年2月21日現在、約500名程度が受講しています。 コンテンツの質・受講自由度の高さから、受講人数は期を重ねるごとに増加傾向にあり、効果的な受講支援や階層別研修の補助利用など活用の幅を広げております。(UB受講者用Teamsの作成、新人研修の補助としての導入など) これまでのISIDにおける内定者教育について ISIDでは内定者に対して、入社後の早期活躍を支援すべく「心・技・体」の観点で学習機会を提供してきました。 (記載の教育施策は一例です) これまでの教育施策は、主にIT理解度( 基本情報技術者試験 (FE)の取得状況など)に応じて個別に内容を変更していました。しかし、その施策内容の検討/選定が難しく課題となっていました。 そのため、内定者自身が、各自のレベルに合った講座を自由に選べる点が、UB内定者提供に踏み切った理由の一つでした。 そんな内定者教育に、UBをどう活用しているのか? UBのコンテンツ数の多さ、そしてそれらを自由に選んで受講できるメリットを活かし、下記の通り必須受講と任意受講を組み合わせて運用しています。 必須受講 IT理解度が低い人向けに、IT基礎知識を学ぶ講座を人事部で選定し、受講を指示( 基本情報技術者試験 に関するコンテンツなど) 任意受講 一部講座を推薦(ビジネスマナーやセルフマネジメント、論理思考力など) その他、自身が学習したい分野、不足していると思う分野に関する講座 上記の通りISIDではUBを、内定者個々の学習意欲に合わせた教育コンテンツ提供にも活用しています。 どう管理しているの?受講生の実施までチェックしているの? 前章で紹介した推奨コンテンツは、UBの"ラーニングパス機能"を用いて、各内定者の受講状況をチェックできるようにしています。ラーニングパスとは、UB上の講座や社内のコンテンツ、外部リンクなどを組み合わせて作成する"音楽のプレイリスト"のようなものであり、受講状況もチェック可能です。 この機能を活用し、学んでほしい内容を詰め込んだ内定者教育専用のラーニングパスを作成して、展開および受講状況をチェックしています。 人事部としての所感 結論としては、UBは内定者教育にもフィットしている実感があります。 理由は、これまで課題と感じていた「内定者それぞれに合わせた教育コンテンツ提供」が、前述の通りUBが有する豊富なコンテンツ量・受講者の管理が容易なラーニングパスによって対処できていると感じるためです。 内定者によるフィードバックはこれからですが、自身の興味に合うコンテンツをいつでも自由に受講できるUBは、受講者側にも大きなメリットなのではないかと個人的に思っています。 今後の展望 今後の展望としては、以下のようにUBを活用したいと考えています。 (入社後の教育)入社後の教育にもUBを活用し、学び続ける姿勢をサポートしたい。 (今後の内定者研修)今年度と同様に内定者期間にUBを提供。内定者からのフィードバックを受け、ラーニングパスの中身をブラッシュアップする。 おわりに 以上が、弊社の内定者教育にUBを活用した事例の紹介です。 UBを内定者に提供することは、業務の傍ら学び続ける習慣のキッカケになると確信しております。 もちろん内定者教育をeラーニングだけに留めようという思いはありません。ただUBが簡単に活用できるからこそ、より一層狙いを持って、eラーニングもしくは対面型研修を選択するべきだと私個人としては考えています。 本記事が誰かのお役に立てば幸いです。 執筆: @kawatani.tomoro 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
アバター
スマートなタイトルが思いつきませんでした。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 インターネットにWebアプリケーションを公開すると、さまざまなHTTPリク エス トを受けることになります。中にはアプリケーションを利用する目的ではない、 情報収集や 脆弱性 を突いて攻撃を狙うリク エス トも多数含まれています。攻撃への根本対策はWebアプリケーションレベルで行うべきですが、対策漏れや ゼロデイ攻撃 に対してはWAFの利用も効果的です。 AWS WAFには AWSマネージドルール が多数用意されており、簡単に導入し多層防御を実現できます。この記事では AWS WAFによるブロックをより厳しくする方法として、WAFのルールがブロックしたリク エス トの IPアドレス を、悪意のあるリク エス トを送信してくる可能性がある IPアドレス とみなして、一定期間 IPアドレス レベルでブロックし続ける仕組みをご紹介します。一度WAFのWeb ACL でブロックに成功しても、攻撃者はパターンを変えて何度も攻撃を試みる場合があり、IPレベルで一定期間ブロックすることで他のWAFルールが対応していないパターンで攻撃が成立するリスクを減らすことを期待しています。 インフラは AWS CDK で実装します。 どんな Web アプリが適しているか アーキテクチャ 1.サブスクリプションフィルターでWAFのログからLambdaを起動 2.DynamoDBのTTLで一定期間後にブロックを解除 3.CDKデプロイ用にSSM Parameter Storeを利用 4.WAF IPセットの楽観ロックで同時更新に対応 サンプルコード 事前準備 (CDK) IPアドレスリストの取得 (CDK) WAFの作成 (CDK) WAFのログを出力 (CDK) DynamoDBテーブルの作成 (CDK) ブロック対象を追加するLambda関数 (CDK) サブスクリプションフィルター (CDK) EventBridgeでIPアドレスリストを同期 (Lambda) パッケージのインポートとSDKクライアントの作成 (Lambda) ハンドラー (Lambda) ログの抽出 (Lambda) IPアドレスをDynamoDBテーブルに追加 (Lambda) DynamoDBからIPアドレスリストを取得 (Lambda) DynamoDBのIPアドレスリストを同期する関数 どんな Web アプリが適しているか 不特定多数の利用者を想定した一般公開のアプリには、これから紹介する仕組みは向かないと思います。WAFの誤検知によってリク エス トをブロックしてしまった場合の影響が大きいからです。社内用アプリなど、利用ユーザーは特定できるがインターネットからアクセスできるアプリがこの仕組みに適していると思います。 アーキテクチャ 1. サブスクリプション フィルターでWAFのログからLambdaを起動 AWS WAFで IPアドレス レンジレベルでリク エス トを許可・ブロックするためには、 IPセット を定義します。WAFのブロック結果からこのIPセットを動的に変更すれば良いので、WAFのブロックログをCloudWatch Logsに出力し、 サブスクリプションフィルター で特定のログをトリガーにLambda関数を起動します。 2.DynamoDBの TTL で一定期間後にブロックを解除 IPアドレス とユーザーの関連は変わり得るので、一定期間経過後は IPアドレス レベルのブロックを自動で解除し、ブロック対象の IPアドレス リストの長さが増え続けないようにします。誤動作で特定の IPアドレス がブロックされてしまった場合の影響を小さくします。 DynamoDBの TTL を利用します。ブロック対象となる IPアドレス をDynamoDBテーブルのレコードとして保持し、 TTL を設定します。EventBridgeで定期的にLambda関数を起動し、DynamoDBテーブルのアイテムのうち、有効期限内の IPアドレス のみでWAFのIPセットを更新します。 3.CDKデプロイ用にSSM Parameter Storeを利用 今回はCDKでインフラを構築する前提なので、IPセットもCDKコードに記載することになります。ただしIPセットの中身は、CDKデプロイ時に先祖返りしないように SSM Parameter Storeパラメータから取得 するようにします。Lambda関数でDynamoDBテーブルから有効な IPアドレス を取得した後は、このパラメータも更新するようにします。 最終的な アーキテクチャ は次の図の通りになります。ブロック対象の IPアドレス 情報はWAFのIPセット、DynamoDBテーブル、Parameter Storeパラメータと3箇所に分散していますが、DynamoDBテーブルのデータを正とし、IPセットとParameter StoreパラメータのデータはDynamoDBテーブルから結果整合的に更新されるようになっています。 4.WAF IPセットの楽観ロックで同時更新に対応 同じIPセットを更新する箇所が複数あり、また複数のWAFブロックログが同時に出力された場合はブロック対象 IPアドレス の追加が同時に実行される可能性があります。この複数の更新に対する 排他制御 の仕組みが、IPセットの API にあります。 WAF IPセットを取得する GetIPSet API のレスポンスで LockToken を取得し、IPセットを更新する UpdateIPSet API を呼びだす時のリク エス トに付加することで、楽観ロックを容易に実現できます。 WAF IPセットを更新するUpdateIPSet API は、個別の IPアドレス レンジを追加、削除するのではなく、更新時に IPアドレス レンジの全体を置き換えるような API 仕様になっているため、このような楽観ロックの仕組みが用意されているのだと思います。 サンプルコード 以上で説明した仕組みを実現するためのサンプルコードをご紹介します。 事前準備 ブロック対象の IPアドレス リストを保持するParameter Storeパラメータを手動で作成しておきます。ここでは MALICIOUS_IP_LIST というパラメータ名とします。空文字では登録できないので初期値は半角スペース 1 つとし、Lambda関数で動的に更新していきます。 1.1.1.1/32;2.2.2.2/32 のように、複数のIP CIDRを セミ コロンで区切るフォーマットを想定します。 (CDK) IPアドレス リストの取得 Parameter Storeパラメータより IPアドレス リストを取得し、文字列型の配列に変換します。それを利用してWAFのIPセットを定義します。 const ipListParamName = "MALICIOUS_IP_LIST" ; const maliciousIpSet = ssm.StringParameter.valueFromLookup ( this , ipListParamName ) .split ( ";" ) .map (( ip ) => ip.trim ()) .filter (( ip ) => ip ); const wafIpSetName = "MaliciousIpSet" ; const wafIpSet = new wafv2.CfnIPSet ( this , "MaliciousIpSet" , { name: wafIpSetName , ipAddressVersion: "IPV4" , scope: "REGIONAL" , // CloudFront用のWAFの場合は "CLOUDFRONT" addresses: maliciousIpSet , } ); またCDKデプロイする際には、 コンテキスト をリセットして常にParameter Storeから最新のパラメータを取得するように、 cdk deploy の前に cdk context --clear を実行するようにします。 cdk context --clear cdk deploy MyStack --require-approval never (CDK) WAFの作成 WAFを作成します。最初に評価されるルールに IpConstraintStatement を作成し、先ほど作成した IPセットをブロックします。他のルールは AWSManagedRulesCommonRuleSet など、自由に追加します。 WAFのALBやCloudFrontなどへの関連付けは省略します。 const wafWebAcl = new wafv2.CfnWebACL ( this , "WafV2WebAcl" , { defaultAction: { allow: {} } , scope: "REGIONAL" , // CloudFront用のWAFの場合は "CLOUDFRONT" visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "WafV2WebAcl" , } , rules: [ { name: "IpConstraintStatement" , priority: 10 , statement: { ipSetReferenceStatement: { arn: wafIpSet.attrArn , } , } , action: { block: {} } , visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "IpConstraintStatement" , } , } , { name: "AWSManagedRulesCommonRuleSet" , priority: 20 , statement: { managedRuleGroupStatement: { vendorName: "AWS" , name: "AWSManagedRulesCommonRuleSet" , } , } , overrideAction: { none: {} } , visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "AWSManagedRulesCommonRuleSet" , } , } , // 他のルールは省略 ] , } ); (CDK) WAFのログを出力 WAFのBLOCKログとCOUNTログをCloudWatch Logsに出力します。 const wafLogGroup = new logs.LogGroup ( this , "WafLogGroup" , { logGroupName: "waf-logs" , } ); const logConfig = new wafv2.CfnLoggingConfiguration ( this , "WafV2LoggingConfiguration" , { logDestinationConfigs: [ `arn:aws:logs: ${ region } : ${ accountId } :log-group: ${ wafLogGroup.logGroupName } ` ] , resourceArn: wafWebAcl.attrArn , loggingFilter: { DefaultBehavior: "DROP" , Filters: [ { Behavior: "KEEP" , Conditions: [{ ActionCondition: { Action: "BLOCK" } } , { ActionCondition: { Action: "COUNT" } }] , Requirement: "MEETS_ANY" , } , ] , } , } ); logConfig.addDependsOn ( wafWebAcl ); (CDK) DynamoDBテーブルの作成 ブロック対象 IPアドレス 用のDynamoDBテーブルを作成します。 パーティション キーはIP CIDRで、 ip_range というキー名にします。 TTL は expires という名前の属性にしています。 const ipTable = new dynamodb.Table ( this , "MaliciousIpTable" , { tableName: "malicious-ip" , partitionKey: { name: "ip_range" , type : dynamodb.AttributeType.STRING } , billingMode: dynamodb.BillingMode.PAY_PER_REQUEST , timeToLiveAttribute: "expires" , } ); (CDK) ブロック対象を追加するLambda関数 ブロック対象の IPアドレス を追加するLambda関数を作成します。関数内で利用するための 環境変数 をいくつか設定し、必要な API コールをするための権限を渡します。 const autoBlockMaliciousIpFunction = new lambdaNodejs.NodejsFunction ( this , "AutoBlockMaliciousIpFunction" , { entry: "functions/auto-block-malicious-ip.ts" , runtime: Runtime.NODEJS_18_X , timeout: Duration.minutes ( 3 ), environment: { TABLE_NAME: ipTable.tableName , // DynamoDBテーブル名 IP_LIST_PARAMETER_NAME: ipListParamName , // Parameter Storeパラメータ名 IP_SET_NAME: wafIpSetName , // IPセット名 IP_SET_ID: wafIpSet.attrId , // IPセットID IP_SET_SCOPE: "REGIONAL" , // IPセットスコープ。CloudFront用のWAFの場合は "CLOUDFRONT" } , } ); // DynamoDBテーブルの参照・更新権限 ipTable.grantReadWriteData ( autoBlockMaliciousIpFunction ); // IPセットの参照・更新権限 autoBlockMaliciousIpFunction.addToRolePolicy ( new iam.PolicyStatement ( { resources: [ wafIpSet.attrArn ] , actions: [ "wafv2:GetIpSet" , "wafv2:UpdateIPSet" ] , effect: iam.Effect.ALLOW , } ) ); // パラメータの更新権限 autoBlockMaliciousIpFunction.addToRolePolicy ( new iam.PolicyStatement ( { resources: [ `arn:aws:ssm: ${ region } : ${ accountId } :parameter/ ${ ipListParamName } ` ] , actions: [ "ssm:PutParameter" ] , effect: iam.Effect.ALLOW , } ) ); (CDK) サブスクリプション フィルター ブロックログに対して サブスクリプション フィルターでLambda関数を起動させます。ただし IpConstraintStatement ルール自体でブロックされた場合はLambda関数を起動しないようにします。他にも地理的一致条件や、他の IPアドレス 条件でブロックされた場合、対象の IPアドレス はどちらにせよブロックされるので、Lambda関数の起動条件から除外しても良いでしょう。さらに会社の IPアドレス の場合はブロック対象外とするなど、フィルターパターンは要件に応じていろいろとカスタマイズできるでしょう。 const filter = wafLogGroup.addSubscriptionFilter ( "WafLogFilter" , { destination: new destinations.LambdaDestination ( autoBlockMaliciousIpFunction ), filterPattern: logs.FilterPattern.literal ( '{ $.action = "BLOCK" && $.terminatingRuleId != "IpConstraintStatement" }' ), } ); // https://github.com/aws/aws-cdk/issues/23177 へのワークアラウンド // これがないと初回デプロイで失敗する ( filter.node.defaultChild as logs.CfnSubscriptionFilter ) .addDependency ( filter.node.findChild ( "CanInvokeLambda" ) as CfnPermission ); (CDK) EventBridgeで IPアドレス リストを同期 定期的にEventBridgeで起動し、DynamoDBの IPアドレス リストをParameter StoreパラメータとIPセットに同期するLambda関数を作成します。EventBridgeルールは、ここでは1時間に1度起動するようにしています。 const autoUpdateMaliciousIpFunction = new lambdaNodejs.NodejsFunction ( this , "AutoUpdateMaliciousIpFunction" , { entry: "functions/auto-update-malicious-ip.ts" , runtime: Runtime.NODEJS_18_X , timeout: Duration.minutes ( 1 ), environment: { TABLE_NAME: ipTable.tableName , IP_LIST_PARAMETER_NAME: ipListParamName , IP_SET_NAME: wafIpSetName , IP_SET_ID: wafIpSet.attrId , IP_SET_SCOPE: "REGIONAL" , } , } ); // DynamoDBテーブルの参照権限のみ ipTable.grantReadData ( autoUpdateMaliciousIpFunction ); autoUpdateMaliciousIpFunction.addToRolePolicy ( new iam.PolicyStatement ( { resources: [ wafIpSet.attrArn ] , actions: [ "wafv2:GetIpSet" , "wafv2:UpdateIPSet" ] , effect: iam.Effect.ALLOW , } ) ); autoUpdateMaliciousIpFunction.addToRolePolicy ( new iam.PolicyStatement ( { resources: [ `arn:aws:ssm: ${ region } : ${ accountId } :parameter/ ${ ipListParamName } ` ] , actions: [ "ssm:PutParameter" ] , effect: iam.Effect.ALLOW , } ) ); new events.Rule ( this , "AutoUpdateWafIpListEventRule" , { schedule: events.Schedule.cron ( { minute: "0" } ), targets: [ new eventTargets.LambdaFunction ( autoUpdateMaliciousIpFunction ) ] , } ); (Lambda) パッケージのインポートと SDK クライアントの作成 ここからはブロック対象の IPアドレス を追加するLambda関数のコードを紹介します。まず必要なパッケージのインポートと、使用する各サービスの SDK クライアントを作成する部分です。 // functions/auto-block-malicious-ip.ts import * as zlib from "zlib"; import { DynamoDBClient, ScanCommand, ScanCommandInput, AttributeValue, PutItemCommand, } from "@aws-sdk/client-dynamodb"; import { SSMClient, PutParameterCommand } from "@aws-sdk/client-ssm"; import { WAFV2Client, UpdateIPSetCommand, GetIPSetCommand } from "@aws-sdk/client-wafv2"; import { Handler, CloudWatchLogsEvent, CloudWatchLogsLogEvent, CloudWatchLogsDecodedData } from "aws-lambda"; const dynamodbClient = new DynamoDBClient({ region: process.env.AWS_REGION }); const ssmClient = new SSMClient({ region: process.env.AWS_REGION }); const wafClient = new WAFV2Client({ region: process.env.AWS_REGION }); (Lambda) ハンドラー サブスクリプション フィルターを受けて起動する関数なので、引数のイベントは CloudWatchLogsEvent 型になります。全体の処理の流れはこの通りです。 1. ログイベントを抽出 2. IPアドレスをDynamoDBテーブルに追加 3. WAF IPセットのロックトークンを取得(楽観ロック開始) 4. DynamoDBからIPアドレスリストを取得 5. Parameter Storeパラメータを更新 6. WAF IPセットを更新。失敗したら3からやり直す export const handler: Handler = async ( input: CloudWatchLogsEvent ) => { if ( ! process .env.IP_SET_ID || ! process .env.IP_SET_NAME || ! process .env.TABLE_NAME || ! process .env.IP_LIST_PARAMETER_NAME || ! process .env.IP_SET_SCOPE || ! [ "REGIONAL" , "CLOUDFRONT" ] .includes ( process .env.IP_SET_SCOPE ) ) { return; } // 1. ログイベントを抽出(関数の中身は後述) const logEvents = await extractAwsLogEvents ( input ); // 各イベントに対してループ処理 for ( const event of logEvents ) { type LogMessageType = { httpRequest?: { clientIp?: string ; } ; } ; const message = JSON .parse ( event.message ) as LogMessageType ; if ( ! message.httpRequest || ! message.httpRequest.clientIp ) continue; // 2. IPアドレスをDynamoDBテーブルに追加(関数の中身は後述) await addDynamodbItem ( { tableName: process .env.TABLE_NAME , ipAddress: message.httpRequest.clientIp } ); let retryCount = 10 ; while ( retryCount > 0 ) { // 3. WAF IPセットのロックトークンを取得(楽観ロック開始) const { LockToken: lockToken } = await wafClient.send ( new GetIPSetCommand ( { Name: process .env.IP_SET_NAME , Scope: process .env.IP_SET_SCOPE , Id: process .env.IP_SET_ID , } ) ); // 4. DynamoDBからIPアドレスリストを取得(関数の中身は後述) const ipRanges: string [] = await getAllIpsFromDynamodb ( { tableName: process .env.TABLE_NAME } ); const ipRangeParam = ipRanges.length > 0 ? ipRanges.join ( ";" ) : " " ; // 5. Parameter Storeパラメータを更新 await ssmClient.send ( new PutParameterCommand ( { Name: process .env.IP_LIST_PARAMETER_NAME , Value: ipRangeParam , Overwrite: true } ) ); try { // 6. WAF IPセットを更新 // lockToken発行後に更新されているとエラーがThrowされる await wafClient.send ( new UpdateIPSetCommand ( { Name: process .env.IP_SET_NAME , Id: process .env.IP_SET_ID , Scope: process .env.IP_SET_SCOPE , Addresses: ipRanges , LockToken: lockToken , } ) ); retryCount = 0 ; } catch { // 楽観ロックで失敗したら一定回数リトライ retryCount -= 1 ; } } } } ; (Lambda) ログの抽出 ハンドラーの引数からログイベントの中身を抽出する関数は こちら を参考にして実装します。 const extractAwsLogEvents = async ( input: CloudWatchLogsEvent ) : Promise < CloudWatchLogsLogEvent [] > => { const payload = Buffer . from( input.awslogs.data , "base64" ); const result = await new Promise < string >(( resolve , reject ) => { zlib.gunzip ( payload , ( e , result ) => { return e ? reject ( e ) : resolve ( result.toString ( "ascii" )); } ); } ); return ( JSON .parse ( result ) as CloudWatchLogsDecodedData ) .logEvents ; } ; (Lambda) IPアドレス をDynamoDBテーブルに追加 ログに記録された IPアドレス をDynamoDBテーブルにアイテムとして追加する部分は次のようになります。 TTL のフォーマットは Unix エポック時間形式の秒単位 であり、現在時刻より1時間後としています。 const addDynamodbItem = async ( props: { tableName: string ; ipAddress: string } ) : Promise < void > => { // 1時間ブロックする const expires = Math .floor (new Date () .getTime () / 1000 ) + 3600 ; const command = new PutItemCommand ( { TableName: props.tableName , Item: { ip_range: { S: ` ${ props.ipAddress } /32` } , expires: { N: ` ${ expires } ` } , } , } ); await dynamodbClient.send ( command ); } ; (Lambda) DynamoDBから IPアドレス リストを取得 Parameter StoreパラメータとIPセットを更新するために、DynamoDBテーブルをスキャンし全 IPアドレス を取得する部分です。DynamoDBの TTL で有効期限が切れてもすぐにアイテムが削除される保証はなく、最大で48時間テーブルに残るため、スキャン後に フィルター で TTL が有効なアイテムをフィルタリングします。また読み取りは 強力な整合性 とし、直前で追加した IPアドレス が確実に結果に含まれるようにします。(結果整合性よりも多くのキャパシティユニットを消費することにご留意ください) const getAllIpsFromDynamodb = async ( props: { tableName: string } ) : Promise < string [] > => { let startKey: Record < string , AttributeValue > | undefined = undefined ; let shouldScanNext = true ; const ipRanges: string [] = [] ; const currentTime = Math .floor (new Date () .getTime () / 1000 ); while ( shouldScanNext ) { const params: ScanCommandInput = { TableName: props.tableName , ConsistentRead: true , ExpressionAttributeNames: { "#e" : "expires" } , ExpressionAttributeValues: { ":1" : { N: ` ${ currentTime } ` } } , FilterExpression: "#e >= :1" , ExclusiveStartKey: startKey , } ; const { Items , LastEvaluatedKey } = await dynamodbClient.send (new ScanCommand ( params )); Items?.forEach (( item ) => { if ( item [ "ip_range" ] .S ) ipRanges.push ( item [ "ip_range" ] .S ); } ); if ( LastEvaluatedKey ) { startKey = LastEvaluatedKey ; } else { shouldScanNext = false ; } } return ipRanges ; } ; これで、ブロック対象の IPアドレス をIPセットに追加するLambda関数を作成できました。 (Lambda) DynamoDBの IPアドレス リストを同期する関数 EventBridgeを受けて定期的に起動し、DynamoDBの IPアドレス リストをParameter StoreパラメータとIPセットに同期するLambda関数は、先ほどのLambda関数の 3 ~ 6 の処理を利用すれば作成できます。 3. WAF IPセットのロックトークンを取得(楽観ロック開始) 4. DynamoDBからIPアドレスリストを取得 5. Parameter Storeパラメータを更新 6. WAF IPセットを更新。失敗したら3からやり直す 以上で、 AWS WAFでブロックしたリク エス トの IPアドレス を、一定期間 IPアドレス レベルでブロックし続ける仕組みを実現できました。人手を介さずに完全に自動で動き、ブロック対象の IPアドレス は一定期間後に自動消去されるので増え続けることもなく、CDKのデプロイに対してデータが上書きされることもありません。 お読みいただいてありがとうございました! 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア(セキュリティ設計) 執筆: @kou.kinyo 、レビュー: 寺山 輝 (@terayama.akira) ( Shodo で執筆されました )
アバター
スマートなタイトルが思いつきませんでした。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 インターネットにWebアプリケーションを公開すると、さまざまなHTTPリク エス トを受けることになります。中にはアプリケーションを利用する目的ではない、 情報収集や 脆弱性 を突いて攻撃を狙うリク エス トも多数含まれています。攻撃への根本対策はWebアプリケーションレベルで行うべきですが、対策漏れや ゼロデイ攻撃 に対してはWAFの利用も効果的です。 AWS WAFには AWSマネージドルール が多数用意されており、簡単に導入し多層防御を実現できます。この記事では AWS WAFによるブロックをより厳しくする方法として、WAFのルールがブロックしたリク エス トの IPアドレス を、悪意のあるリク エス トを送信してくる可能性がある IPアドレス とみなして、一定期間 IPアドレス レベルでブロックし続ける仕組みをご紹介します。一度WAFのWeb ACL でブロックに成功しても、攻撃者はパターンを変えて何度も攻撃を試みる場合があり、IPレベルで一定期間ブロックすることで他のWAFルールが対応していないパターンで攻撃が成立するリスクを減らすことを期待しています。 インフラは AWS CDK で実装します。 どんな Web アプリが適しているか アーキテクチャ 1.サブスクリプションフィルターでWAFのログからLambdaを起動 2.DynamoDBのTTLで一定期間後にブロックを解除 3.CDKデプロイ用にSSM Parameter Storeを利用 4.WAF IPセットの楽観ロックで同時更新に対応 サンプルコード 事前準備 (CDK) IPアドレスリストの取得 (CDK) WAFの作成 (CDK) WAFのログを出力 (CDK) DynamoDBテーブルの作成 (CDK) ブロック対象を追加するLambda関数 (CDK) サブスクリプションフィルター (CDK) EventBridgeでIPアドレスリストを同期 (Lambda) パッケージのインポートとSDKクライアントの作成 (Lambda) ハンドラー (Lambda) ログの抽出 (Lambda) IPアドレスをDynamoDBテーブルに追加 (Lambda) DynamoDBからIPアドレスリストを取得 (Lambda) DynamoDBのIPアドレスリストを同期する関数 どんな Web アプリが適しているか 不特定多数の利用者を想定した一般公開のアプリには、これから紹介する仕組みは向かないと思います。WAFの誤検知によってリク エス トをブロックしてしまった場合の影響が大きいからです。社内用アプリなど、利用ユーザーは特定できるがインターネットからアクセスできるアプリがこの仕組みに適していると思います。 アーキテクチャ 1. サブスクリプション フィルターでWAFのログからLambdaを起動 AWS WAFで IPアドレス レンジレベルでリク エス トを許可・ブロックするためには、 IPセット を定義します。WAFのブロック結果からこのIPセットを動的に変更すれば良いので、WAFのブロックログをCloudWatch Logsに出力し、 サブスクリプションフィルター で特定のログをトリガーにLambda関数を起動します。 2.DynamoDBの TTL で一定期間後にブロックを解除 IPアドレス とユーザーの関連は変わり得るので、一定期間経過後は IPアドレス レベルのブロックを自動で解除し、ブロック対象の IPアドレス リストの長さが増え続けないようにします。誤動作で特定の IPアドレス がブロックされてしまった場合の影響を小さくします。 DynamoDBの TTL を利用します。ブロック対象となる IPアドレス をDynamoDBテーブルのレコードとして保持し、 TTL を設定します。EventBridgeで定期的にLambda関数を起動し、DynamoDBテーブルのアイテムのうち、有効期限内の IPアドレス のみでWAFのIPセットを更新します。 3.CDKデプロイ用にSSM Parameter Storeを利用 今回はCDKでインフラを構築する前提なので、IPセットもCDKコードに記載することになります。ただしIPセットの中身は、CDKデプロイ時に先祖返りしないように SSM Parameter Storeパラメータから取得 するようにします。Lambda関数でDynamoDBテーブルから有効な IPアドレス を取得した後は、このパラメータも更新するようにします。 最終的な アーキテクチャ は次の図の通りになります。ブロック対象の IPアドレス 情報はWAFのIPセット、DynamoDBテーブル、Parameter Storeパラメータと3箇所に分散していますが、DynamoDBテーブルのデータを正とし、IPセットとParameter StoreパラメータのデータはDynamoDBテーブルから結果整合的に更新されるようになっています。 4.WAF IPセットの楽観ロックで同時更新に対応 同じIPセットを更新する箇所が複数あり、また複数のWAFブロックログが同時に出力された場合はブロック対象 IPアドレス の追加が同時に実行される可能性があります。この複数の更新に対する 排他制御 の仕組みが、IPセットの API にあります。 WAF IPセットを取得する GetIPSet API のレスポンスで LockToken を取得し、IPセットを更新する UpdateIPSet API を呼びだす時のリク エス トに付加することで、楽観ロックを容易に実現できます。 WAF IPセットを更新するUpdateIPSet API は、個別の IPアドレス レンジを追加、削除するのではなく、更新時に IPアドレス レンジの全体を置き換えるような API 仕様になっているため、このような楽観ロックの仕組みが用意されているのだと思います。 サンプルコード 以上で説明した仕組みを実現するためのサンプルコードをご紹介します。 事前準備 ブロック対象の IPアドレス リストを保持するParameter Storeパラメータを手動で作成しておきます。ここでは MALICIOUS_IP_LIST というパラメータ名とします。空文字では登録できないので初期値は半角スペース 1 つとし、Lambda関数で動的に更新していきます。 1.1.1.1/32;2.2.2.2/32 のように、複数のIP CIDRを セミ コロンで区切るフォーマットを想定します。 (CDK) IPアドレス リストの取得 Parameter Storeパラメータより IPアドレス リストを取得し、文字列型の配列に変換します。それを利用してWAFのIPセットを定義します。 const ipListParamName = "MALICIOUS_IP_LIST" ; const maliciousIpSet = ssm.StringParameter.valueFromLookup ( this , ipListParamName ) .split ( ";" ) .map (( ip ) => ip.trim ()) .filter (( ip ) => ip ); const wafIpSetName = "MaliciousIpSet" ; const wafIpSet = new wafv2.CfnIPSet ( this , "MaliciousIpSet" , { name: wafIpSetName , ipAddressVersion: "IPV4" , scope: "REGIONAL" , // CloudFront用のWAFの場合は "CLOUDFRONT" addresses: maliciousIpSet , } ); またCDKデプロイする際には、 コンテキスト をリセットして常にParameter Storeから最新のパラメータを取得するように、 cdk deploy の前に cdk context --clear を実行するようにします。 cdk context --clear cdk deploy MyStack --require-approval never (CDK) WAFの作成 WAFを作成します。最初に評価されるルールに IpConstraintStatement を作成し、先ほど作成した IPセットをブロックします。他のルールは AWSManagedRulesCommonRuleSet など、自由に追加します。 WAFのALBやCloudFrontなどへの関連付けは省略します。 const wafWebAcl = new wafv2.CfnWebACL ( this , "WafV2WebAcl" , { defaultAction: { allow: {} } , scope: "REGIONAL" , // CloudFront用のWAFの場合は "CLOUDFRONT" visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "WafV2WebAcl" , } , rules: [ { name: "IpConstraintStatement" , priority: 10 , statement: { ipSetReferenceStatement: { arn: wafIpSet.attrArn , } , } , action: { block: {} } , visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "IpConstraintStatement" , } , } , { name: "AWSManagedRulesCommonRuleSet" , priority: 20 , statement: { managedRuleGroupStatement: { vendorName: "AWS" , name: "AWSManagedRulesCommonRuleSet" , } , } , overrideAction: { none: {} } , visibilityConfig: { cloudWatchMetricsEnabled: true , sampledRequestsEnabled: true , metricName: "AWSManagedRulesCommonRuleSet" , } , } , // 他のルールは省略 ] , } ); (CDK) WAFのログを出力 WAFのBLOCKログとCOUNTログをCloudWatch Logsに出力します。 const wafLogGroup = new logs.LogGroup ( this , "WafLogGroup" , { logGroupName: "waf-logs" , } ); const logConfig = new wafv2.CfnLoggingConfiguration ( this , "WafV2LoggingConfiguration" , { logDestinationConfigs: [ `arn:aws:logs: ${ region } : ${ accountId } :log-group: ${ wafLogGroup.logGroupName } ` ] , resourceArn: wafWebAcl.attrArn , loggingFilter: { DefaultBehavior: "DROP" , Filters: [ { Behavior: "KEEP" , Conditions: [{ ActionCondition: { Action: "BLOCK" } } , { ActionCondition: { Action: "COUNT" } }] , Requirement: "MEETS_ANY" , } , ] , } , } ); logConfig.addDependsOn ( wafWebAcl ); (CDK) DynamoDBテーブルの作成 ブロック対象 IPアドレス 用のDynamoDBテーブルを作成します。 パーティション キーはIP CIDRで、 ip_range というキー名にします。 TTL は expires という名前の属性にしています。 const ipTable = new dynamodb.Table ( this , "MaliciousIpTable" , { tableName: "malicious-ip" , partitionKey: { name: "ip_range" , type : dynamodb.AttributeType.STRING } , billingMode: dynamodb.BillingMode.PAY_PER_REQUEST , timeToLiveAttribute: "expires" , } ); (CDK) ブロック対象を追加するLambda関数 ブロック対象の IPアドレス を追加するLambda関数を作成します。関数内で利用するための 環境変数 をいくつか設定し、必要な API コールをするための権限を渡します。 const autoBlockMaliciousIpFunction = new lambdaNodejs.NodejsFunction ( this , "AutoBlockMaliciousIpFunction" , { entry: "functions/auto-block-malicious-ip.ts" , runtime: Runtime.NODEJS_18_X , timeout: Duration.minutes ( 3 ), environment: { TABLE_NAME: ipTable.tableName , // DynamoDBテーブル名 IP_LIST_PARAMETER_NAME: ipListParamName , // Parameter Storeパラメータ名 IP_SET_NAME: wafIpSetName , // IPセット名 IP_SET_ID: wafIpSet.attrId , // IPセットID IP_SET_SCOPE: "REGIONAL" , // IPセットスコープ。CloudFront用のWAFの場合は "CLOUDFRONT" } , } ); // DynamoDBテーブルの参照・更新権限 ipTable.grantReadWriteData ( autoBlockMaliciousIpFunction ); // IPセットの参照・更新権限 autoBlockMaliciousIpFunction.addToRolePolicy ( new iam.PolicyStatement ( { resources: [ wafIpSet.attrArn ] , actions: [ "wafv2:GetIpSet" , "wafv2:UpdateIPSet" ] , effect: iam.Effect.ALLOW , } ) ); // パラメータの更新権限 autoBlockMaliciousIpFunction.addToRolePolicy ( new iam.PolicyStatement ( { resources: [ `arn:aws:ssm: ${ region } : ${ accountId } :parameter/ ${ ipListParamName } ` ] , actions: [ "ssm:PutParameter" ] , effect: iam.Effect.ALLOW , } ) ); (CDK) サブスクリプション フィルター ブロックログに対して サブスクリプション フィルターでLambda関数を起動させます。ただし IpConstraintStatement ルール自体でブロックされた場合はLambda関数を起動しないようにします。他にも地理的一致条件や、他の IPアドレス 条件でブロックされた場合、対象の IPアドレス はどちらにせよブロックされるので、Lambda関数の起動条件から除外しても良いでしょう。さらに会社の IPアドレス の場合はブロック対象外とするなど、フィルターパターンは要件に応じていろいろとカスタマイズできるでしょう。 const filter = wafLogGroup.addSubscriptionFilter ( "WafLogFilter" , { destination: new destinations.LambdaDestination ( autoBlockMaliciousIpFunction ), filterPattern: logs.FilterPattern.literal ( '{ $.action = "BLOCK" && $.terminatingRuleId != "IpConstraintStatement" }' ), } ); // https://github.com/aws/aws-cdk/issues/23177 へのワークアラウンド // これがないと初回デプロイで失敗する ( filter.node.defaultChild as logs.CfnSubscriptionFilter ) .addDependency ( filter.node.findChild ( "CanInvokeLambda" ) as CfnPermission ); (CDK) EventBridgeで IPアドレス リストを同期 定期的にEventBridgeで起動し、DynamoDBの IPアドレス リストをParameter StoreパラメータとIPセットに同期するLambda関数を作成します。EventBridgeルールは、ここでは1時間に1度起動するようにしています。 const autoUpdateMaliciousIpFunction = new lambdaNodejs.NodejsFunction ( this , "AutoUpdateMaliciousIpFunction" , { entry: "functions/auto-update-malicious-ip.ts" , runtime: Runtime.NODEJS_18_X , timeout: Duration.minutes ( 1 ), environment: { TABLE_NAME: ipTable.tableName , IP_LIST_PARAMETER_NAME: ipListParamName , IP_SET_NAME: wafIpSetName , IP_SET_ID: wafIpSet.attrId , IP_SET_SCOPE: "REGIONAL" , } , } ); // DynamoDBテーブルの参照権限のみ ipTable.grantReadData ( autoUpdateMaliciousIpFunction ); autoUpdateMaliciousIpFunction.addToRolePolicy ( new iam.PolicyStatement ( { resources: [ wafIpSet.attrArn ] , actions: [ "wafv2:GetIpSet" , "wafv2:UpdateIPSet" ] , effect: iam.Effect.ALLOW , } ) ); autoUpdateMaliciousIpFunction.addToRolePolicy ( new iam.PolicyStatement ( { resources: [ `arn:aws:ssm: ${ region } : ${ accountId } :parameter/ ${ ipListParamName } ` ] , actions: [ "ssm:PutParameter" ] , effect: iam.Effect.ALLOW , } ) ); new events.Rule ( this , "AutoUpdateWafIpListEventRule" , { schedule: events.Schedule.cron ( { minute: "0" } ), targets: [ new eventTargets.LambdaFunction ( autoUpdateMaliciousIpFunction ) ] , } ); (Lambda) パッケージのインポートと SDK クライアントの作成 ここからはブロック対象の IPアドレス を追加するLambda関数のコードを紹介します。まず必要なパッケージのインポートと、使用する各サービスの SDK クライアントを作成する部分です。 // functions/auto-block-malicious-ip.ts import * as zlib from "zlib"; import { DynamoDBClient, ScanCommand, ScanCommandInput, AttributeValue, PutItemCommand, } from "@aws-sdk/client-dynamodb"; import { SSMClient, PutParameterCommand } from "@aws-sdk/client-ssm"; import { WAFV2Client, UpdateIPSetCommand, GetIPSetCommand } from "@aws-sdk/client-wafv2"; import { Handler, CloudWatchLogsEvent, CloudWatchLogsLogEvent, CloudWatchLogsDecodedData } from "aws-lambda"; const dynamodbClient = new DynamoDBClient({ region: process.env.AWS_REGION }); const ssmClient = new SSMClient({ region: process.env.AWS_REGION }); const wafClient = new WAFV2Client({ region: process.env.AWS_REGION }); (Lambda) ハンドラー サブスクリプション フィルターを受けて起動する関数なので、引数のイベントは CloudWatchLogsEvent 型になります。全体の処理の流れはこの通りです。 1. ログイベントを抽出 2. IPアドレスをDynamoDBテーブルに追加 3. WAF IPセットのロックトークンを取得(楽観ロック開始) 4. DynamoDBからIPアドレスリストを取得 5. Parameter Storeパラメータを更新 6. WAF IPセットを更新。失敗したら3からやり直す export const handler: Handler = async ( input: CloudWatchLogsEvent ) => { if ( ! process .env.IP_SET_ID || ! process .env.IP_SET_NAME || ! process .env.TABLE_NAME || ! process .env.IP_LIST_PARAMETER_NAME || ! process .env.IP_SET_SCOPE || ! [ "REGIONAL" , "CLOUDFRONT" ] .includes ( process .env.IP_SET_SCOPE ) ) { return; } // 1. ログイベントを抽出(関数の中身は後述) const logEvents = await extractAwsLogEvents ( input ); // 各イベントに対してループ処理 for ( const event of logEvents ) { type LogMessageType = { httpRequest?: { clientIp?: string ; } ; } ; const message = JSON .parse ( event.message ) as LogMessageType ; if ( ! message.httpRequest || ! message.httpRequest.clientIp ) continue; // 2. IPアドレスをDynamoDBテーブルに追加(関数の中身は後述) await addDynamodbItem ( { tableName: process .env.TABLE_NAME , ipAddress: message.httpRequest.clientIp } ); let retryCount = 10 ; while ( retryCount > 0 ) { // 3. WAF IPセットのロックトークンを取得(楽観ロック開始) const { LockToken: lockToken } = await wafClient.send ( new GetIPSetCommand ( { Name: process .env.IP_SET_NAME , Scope: process .env.IP_SET_SCOPE , Id: process .env.IP_SET_ID , } ) ); // 4. DynamoDBからIPアドレスリストを取得(関数の中身は後述) const ipRanges: string [] = await getAllIpsFromDynamodb ( { tableName: process .env.TABLE_NAME } ); const ipRangeParam = ipRanges.length > 0 ? ipRanges.join ( ";" ) : " " ; // 5. Parameter Storeパラメータを更新 await ssmClient.send ( new PutParameterCommand ( { Name: process .env.IP_LIST_PARAMETER_NAME , Value: ipRangeParam , Overwrite: true } ) ); try { // 6. WAF IPセットを更新 // lockToken発行後に更新されているとエラーがThrowされる await wafClient.send ( new UpdateIPSetCommand ( { Name: process .env.IP_SET_NAME , Id: process .env.IP_SET_ID , Scope: process .env.IP_SET_SCOPE , Addresses: ipRanges , LockToken: lockToken , } ) ); retryCount = 0 ; } catch { // 楽観ロックで失敗したら一定回数リトライ retryCount -= 1 ; } } } } ; (Lambda) ログの抽出 ハンドラーの引数からログイベントの中身を抽出する関数は こちら を参考にして実装します。 const extractAwsLogEvents = async ( input: CloudWatchLogsEvent ) : Promise < CloudWatchLogsLogEvent [] > => { const payload = Buffer . from( input.awslogs.data , "base64" ); const result = await new Promise < string >(( resolve , reject ) => { zlib.gunzip ( payload , ( e , result ) => { return e ? reject ( e ) : resolve ( result.toString ( "ascii" )); } ); } ); return ( JSON .parse ( result ) as CloudWatchLogsDecodedData ) .logEvents ; } ; (Lambda) IPアドレス をDynamoDBテーブルに追加 ログに記録された IPアドレス をDynamoDBテーブルにアイテムとして追加する部分は次のようになります。 TTL のフォーマットは Unix エポック時間形式の秒単位 であり、現在時刻より1時間後としています。 const addDynamodbItem = async ( props: { tableName: string ; ipAddress: string } ) : Promise < void > => { // 1時間ブロックする const expires = Math .floor (new Date () .getTime () / 1000 ) + 3600 ; const command = new PutItemCommand ( { TableName: props.tableName , Item: { ip_range: { S: ` ${ props.ipAddress } /32` } , expires: { N: ` ${ expires } ` } , } , } ); await dynamodbClient.send ( command ); } ; (Lambda) DynamoDBから IPアドレス リストを取得 Parameter StoreパラメータとIPセットを更新するために、DynamoDBテーブルをスキャンし全 IPアドレス を取得する部分です。DynamoDBの TTL で有効期限が切れてもすぐにアイテムが削除される保証はなく、最大で48時間テーブルに残るため、スキャン後に フィルター で TTL が有効なアイテムをフィルタリングします。また読み取りは 強力な整合性 とし、直前で追加した IPアドレス が確実に結果に含まれるようにします。(結果整合性よりも多くのキャパシティユニットを消費することにご留意ください) const getAllIpsFromDynamodb = async ( props: { tableName: string } ) : Promise < string [] > => { let startKey: Record < string , AttributeValue > | undefined = undefined ; let shouldScanNext = true ; const ipRanges: string [] = [] ; const currentTime = Math .floor (new Date () .getTime () / 1000 ); while ( shouldScanNext ) { const params: ScanCommandInput = { TableName: props.tableName , ConsistentRead: true , ExpressionAttributeNames: { "#e" : "expires" } , ExpressionAttributeValues: { ":1" : { N: ` ${ currentTime } ` } } , FilterExpression: "#e >= :1" , ExclusiveStartKey: startKey , } ; const { Items , LastEvaluatedKey } = await dynamodbClient.send (new ScanCommand ( params )); Items?.forEach (( item ) => { if ( item [ "ip_range" ] .S ) ipRanges.push ( item [ "ip_range" ] .S ); } ); if ( LastEvaluatedKey ) { startKey = LastEvaluatedKey ; } else { shouldScanNext = false ; } } return ipRanges ; } ; これで、ブロック対象の IPアドレス をIPセットに追加するLambda関数を作成できました。 (Lambda) DynamoDBの IPアドレス リストを同期する関数 EventBridgeを受けて定期的に起動し、DynamoDBの IPアドレス リストをParameter StoreパラメータとIPセットに同期するLambda関数は、先ほどのLambda関数の 3 ~ 6 の処理を利用すれば作成できます。 3. WAF IPセットのロックトークンを取得(楽観ロック開始) 4. DynamoDBからIPアドレスリストを取得 5. Parameter Storeパラメータを更新 6. WAF IPセットを更新。失敗したら3からやり直す 以上で、 AWS WAFでブロックしたリク エス トの IPアドレス を、一定期間 IPアドレス レベルでブロックし続ける仕組みを実現できました。人手を介さずに完全に自動で動き、ブロック対象の IPアドレス は一定期間後に自動消去されるので増え続けることもなく、CDKのデプロイに対してデータが上書きされることもありません。 お読みいただいてありがとうございました! 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア(セキュリティ設計) 執筆: @kou.kinyo 、レビュー: 寺山 輝 (@terayama.akira) ( Shodo で執筆されました )
アバター