TECH PLAY

SCSKクラウドソリューション

SCSKクラウドソリューション の技術ブログ

1268

SCSKの畑です。9回目の投稿です。 今回は、 6回目 のエントリで少し言及したアプリケーションの初期化処理について、詳細について記載してみます。 アーキテクチャ概要 そろそろ食傷気味かもしれませんがいつもの図を。今回はほとんどアプリケーション側の話題ですが、一部 Amazon Cognito 認証の話が出てきます。 背景 これまでのエントリで説明した通り、本アプリケーションではテーブルのステータス(編集状態)管理が重要となりますが、中でもステータスの初期化をどのタイミングで実施するかというのが特に重要となります。同エントリで言及した通り、ステータスをアプリケーションのルーティングや画面制御に使用するため、できる限り早いタイミングで初期化するのが実装上望ましいです。 このため、フレームワーク (Nuxt3) のライフサイクルを確認の上、具体的には Nuxt3 の Plugin 内で初期化するような方針としました。Nuxt3 を SSR で使用していることもあり、サーバサイドとクライアントサイド両方から実行され得るため、両対応した上でなるべく早いタイミングで実行できる方法として最適であると考えました。 aws-amplify における SSR 対応についても、公式ドキュメント内では Plugin を使用しているというのも判断材料の一つになりました。 https://nuxt.com/docs/guide/going-further/hooks 問題発生 ということで、この実装においてしばらく開発を続けていたのですが、アプリケーションにおける Cognito ユーザ/グループ権限周りの機能を実装し始めたタイミングで問題が発生しました。 同機能の動作確認を実施するにあたり、当然ながら複数のユーザでログイン/ログアウトを繰り返しながら画面を操作する必要があるため、一度ログアウトしてから別ユーザでログインしたところ・・ あれ、左ペインのメニューが空になっている。本来は赤枠部分にメンテナンス対象のテーブルが一覧表示されるはず・・ということでブラウザのコンソールを見てみたところ、以下のようなエラーが出ていました。(当時は開発真っ最中だったこともあり、エラー出力用の汎用コンポーネントなどはまだ用意していませんでした) UserUnAuthenticatedException: User needs to be authenticated to call this API. 見た通り、AppSync API が Unauthorized で叩けないというエラーで、これがステータス初期化関数内での AppSync API 実行時に出力されていたのが本事象の原因でした。ただ、ステータス初期化処理や Cognito 認証周りの実装は変更していなかったので、最初は何故こういうエラーが発生したのか分かりませんでした。 しかも、この画面でリロードすると正常に画面表示されるんですよね。そうだよね、実装変えてないんだから問題ないはずよね・・というところまで確認したところでようやく気づきました。 あ、Plugin 内でステータス初期化されるタイミングで、まだユーザが Cognito 認証されていないのでは、と・・ 2回目 のエントリで AppSync API の認可方式を説明した通り、本アプリケーションにおいては IAM を使用しています。では、この IAM 権限はどこから付与されるのかというと、Cognito ID プールの「認証されたアクセス」を通してとなります。つまり、当然ですが Cognito 未認証の時点(=未ログイン時点)ではこの AppSync API を叩ける権限が付与されていません。Plugin が実行されるタイミングは Nuxt.js のライフサイクルにおけるアプリケーションの作成時であり、Cognito の認証画面の表示前に実行されるため、上記のエラーが出力されたという流れになります。 Nuxt3入門(第7回) - Nuxt3のプラグイン・ミドルウェアを使う | 豆蔵デベロッパーサイト 前回はNuxt3のエラーハンドリングについて見てきました。今回はプラグインとミドルウェアを見ていきます。両方とも必須という訳ではありませんが、うまく使えばアプリケーション開発を効率化できます。プラグインはNuxtアプリケーション初期化時に実... developer.mamezou-tech.com ただ、アプリケーションの実装として plugin で初期化する考え自体は先般の通り間違っていないと考えていたため、未ログイン(未認証)の場合はログイン直後に初期化するようなロジックを追加しようと考えました。ログイン(認証)した状態でアプリケーションを使用することが前提である以上、ログイン直後のタイミングで初期化できていれば問題ないだろうという判断です。 2回目のエントリで記載したCognito ID プールのゲストアクセスを使用して、 ゲストアクセス経由で付与される IAM ロールにステータス初期化で使用する graphql query のみ許可するような IAM 権限を割り当てることで、未ログイン時でもステータス初期化を行えるようにする 、というのも今振り返ると一つの手ではあったと思います。ただ当時はそこまで発想が及ばなかったというのが正直なところです。 また、その場合は以下項目について考慮・検討しておく必要があったと考えています。 IAM ポリシーにおいて resource 句内の type や field を同クエリのみ許可するよう正確に設定する cognito 未認証(≒本アプリケーションを使用する権限の無いユーザも含む)がどこまでアプリケーション内部で情報を取得できてよいのかの検討 試行錯誤その1 ではログイン画面のロジックを変更して問題解決・・と一筋縄にはいきませんでした。理由は、ログイン画面の実装でお馴染みの Amplify UI モジュールを使用していたためです。こちらも改めて説明の必要がないくらいには便利なモジュールですが、自前で実装していない以上簡単に変更できない&変更できたとしてもモジュールを使用する以上保守の観点からソースコード自体を変更したくありませんでした。 そこで公式ドキュメントを改めて調べたところ、ログイン直後に任意の処理を実行するような「Override Function Calls」という機能がありました。正に今回のようなシチュエーションで欲しかった機能であり、さすがに広く使われているモジュールだなあと感心したのですが・・ https://ui.docs.amplify.aws/vue/connected-components/authenticator/customization この機能、私が試した限りでは Nuxt3 (Vue3) では動作しませんでした。。。 文字通り、対象の関数をオーバーライドするような仕組みだったので、動かした結果何かしらのエラーが出るのであればデバッグのしようもあるなと思ったのですがうんともすんとも言わず、ほぼサンプルコード通りの実装でも NG。同機能を使ってみたような事例自体は WEB 上からいくつか見つかったのですがほぼ Next.js (React ) だったので、ひょっとするとこれ Nuxt.js (Vue) で動作した実績が少ないのではないかと邪推して、この機能を使うのは一旦諦めました。 なお、本件について試行したのは数ヶ月前のことになるため、もし現時点で正常に動作するという情報をお持ちの方がいればご教示頂けますと幸いです。 試行錯誤その2 それでは仕方がないので、ログイン画面を自前で実装するしかないか・・とも考えたのですがこちらは割とすぐに諦めました。理由は明快で 、Amplify UI の作りというか機能性が単純に優れていたためです。 単純な Cognito の認証画面であれば作れると思っていたのですが、実際の認証フローを想定すると、メールアドレスの検証やパスワード変更・リセットのようなケースについても対応する必要があるため、それらの画面なりロジックを用意するとなると一から作るのはそれなりのコストがかかりそうだなと。元々ログイン画面は Amplify UI に任せるつもりで実装の計画を立てていたこともあり、 やはり公式のライブラリ/モジュールを使用しておくのがベターと判断しました。 解決 とは言え、先述の通り Amplify UI の機能で解決することもできそうにないし、どうしたものか・・ということで、一時は完全にスタックしていたのですが、方向性自体は先般の通り「ログイン直後のタイミングで初期化する」が正しいと考えていたため、それを実現するための方法をあれこれ考えた結果、最終的に以下のように解決することができました。 ログイン直後に実行したい処理を実行するための、画面なしページを用意 未ログイン時は必ず同ページにリダイレクトするように middleware でルーティング(ルートミドルウェアを使用) ログインして同ページ内の処理を実行後、トップページに遷移 まず前提として、Amplify UI & Nuxt3 (Vue3) における Cognito 認証は app.vue に以下のようなコードを実装することで実現しています。 <Authenticator> タグで囲われている要素が Cognito 認証されていないと見えなくなり、代わりにログイン画面が表示されます。以下実装例の場合は実質的にアプリケーション上の全ページ/コンポーネントが認証の対象となります。 そして、ログインに成功するとアクセスしている URL に対応したページに遷移するというような挙動となります。このため、少なくとも以下実装例においては、いわゆるログインページのような固有の URL パスは存在しません。 <template>   <Authenticator :hide-sign-up="true">       <NuxtLayout>           <NuxtPage />       </NuxtLayout>   </Authenticator> </template> <script setup lang="ts"> import { Authenticator } from "@aws-amplify/ui-vue"; import "@aws-amplify/ui-vue/styles.css"; </script> Authenticator | Amplify UI for Vue Authenticator component adds complete authentication flows to your application with minimal boilerplate. ui.docs.amplify.aws このため、未ログイン状態でも事実上全ての URL にアクセスすること自体はできてしまい、「ログイン直後に特定の処理を実行する」ような実装が難しい原因となっていたのですが、それを middleware を使用したルーティングにより解決することができました。同処理のためのページに画面は不要なので、そのままトップページにリダイレクトするような実装としています。 最後にそれぞれ実装例を示して本エントリを終わりたいと思います。 ログイン直後の処理実行用ページ (login.vue) 先述の通り、本ページにおける処理内容はシンプルです。updateTableStatusDict() でテーブルステータスの初期化を実施した後、実行後にトップページにリダイレクトするような処理となっています。なお、実案件事例では他情報の初期化なども合わせて実施していますが、説明のため省略します。 また、Nuxt3 の Layout で各ページのレイアウトを制御していますが、本ページにはメニューバーやナビゲーションバーのような共通表示コンポーネントも不要なため、専用レイアウトを適用して非表示としています。 <template></template> <script setup lang="ts"> definePageMeta({ layout: 'login' }) const { updateTableStatusDict } = useTableStatus() onMounted(async () => { await updateTableStatusDict() return navigateTo('/') }) </script> middleware によるルーティング (auth.global.ts) こちらも実装例に落とすと内容はシンプルです。ルートミドルウェアの定義が defineNuxtRouteMiddleware に対応します。引数の to が遷移先のルート(パス)、 from が遷移元のルート(パス)を示しており、他の情報(ユーザのアプリケーション権限や、テーブルのステータスなど)と組み合わせてルーティングの制御を行っています。 ただ、この短いコードの中で躓いたポイントが実は2つほどあったため、以下にまとめました。 Cognito ユーザ認証済みかどうかを直接確認するメソッドは aws-amplify/auth には存在しないようです。このため、未認証時に実行した場合例外がスローされるようなメソッド(以下実装例の場合は getCurrentUser())を実行し、その例外を catch することでユーザ認証済みかどうかの判定をしています。 error を catch した場合は Cognito ユーザ未認証のため、上記で説明したログイン直後の処理実行用ページに対応するルート(以下実装例の場合は /login)に遷移させたいのですが、ルートミドルウェアはページの遷移ごとに呼び出されるため、単純に実装すると未認証時のルーティングが無限ループしてしまいます。このため、遷移先が /login でない場合のみ遷移するようにすることで、無限ループを抑止しています。 export default defineNuxtRouteMiddleware( async(to, from) => {   try { if ( await useNuxtApp().$Amplify.Auth.getCurrentUser() ) {       // Cognitoログイン済みの場合       //// 以下、ユーザのアプリケーション権限に応じたルーティングを制御 //// }   } catch (error) {       // Cognito未ログインの場合       // ルーティングの無限ループを防ぐため、ログインページへの遷移は一度だけにする       if (to.path !== '/login') {           return navigateTo('/login')       }   } } まとめ 今振り返ると意外とあっさり解決したように思えるのですが、開発中はあれこれ思い悩んでいた記憶があります。一時は AppSync の認可方式から見直すことも考えていましたが、先述したような ID プールのゲストアクセスを使用する発想には至らず。最悪 Lambda に変更すれば力業でどうにかなるんじゃないかと思ったりもしましたが、さすがに筋が悪すぎであろうと言うことで結局脳内で却下しました。 というか、アプリケーション開発においてこういう話って正直あるあるというか、真っ先に考えておかないといけないことの一つではないかと思い至り、改めて私自身の開発経験不足を実感した次第です。 最終的に当初の方向性で解決策に辿りつけたこと自体は良かったと思うのですが、もっと精進したいですね。。 本投稿がどなたかの役に立てば幸いです。
本記事は 新人ブログマラソン2024 の記事です 。 こんにちは!SCSKの新人、黄です。 前回の記事では、Rubrikというバックアップデータ管理に特化したIT企業と、その主要製品や機能についてご紹介しました。 多くの方に読んでいただき、とても嬉しかったです! 「まだ読んでいない!」という方は、ぜひ以下の記事をご覧ください。 Rubrikとは?クラウド&オンプレにも対応する次世代バックアップ管理を紹介 – TechHarmony さて、今回は前回少し触れた「Rubrikのランサムウェア対策」をさらに掘り下げ、自分でも実際に試してみた結果を共有したいと思います。 それでは、さっそく始めましょう! はじめに ランサムウェアという言葉、きっと一度は耳にしたことがあるのではないでしょうか? データを暗号化し、その解除のために身代金を要求するサイバー攻撃で、ここ数年、その被害は世界中で広がり続けています。 これだけでも十分厄介ですが、実は 攻撃者の狙い は、通常のデータだけに留まりません。 なんと バックアップデータまで標的 にしているのです! 「バックアップがあれば安心」と考えるのは当然ですが、攻撃者たちはその油断をついてきます。 一例として、下記のような手口でバックアップを狙ってきます: バックアップデータの暗号化 バックアップデータを暗号化し、復旧を不可能にします。 バックアップデータの削除 大切なバックアップデータを削除し、復元手段を完全に奪います。 バックアップソフトの停止 バックアップ機能そのものを無効化します。 こうした脅威に対応するために、Rubrikは「保存するだけ」ではない多角的な対策を提案しています。 その詳細について、次の章で詳しく解説します。 Rubrikのランサムウェア対策の概要 Rubrikのランサムウェア対策には、主に三つのキーワードがあります: 不可変バックアップ ふるまい検知(異常検知) 脅威ハンティング(脅威追跡) 前回の記事では、不可変バックアップとふるまい検知・脅威ハンティングを並列に紹介しましたが、実際のところ、 不可変バックアップはRubrikのランサムウェア対策の「土台」 となる存在です。 不可変バックアップ:Rubrikが独自に開発したOSによって、データがRubrikに保存される際に 不変の形式でロック される仕組みです。 この仕組みにより、攻撃者がバックアップデータを改ざんしたり削除したりすることは物理的に不可能となります。 不可変バックアップにより、「バックアップが改ざんされるかも……」という不安を取り除き、安全にデータを保管することができます。 さらに、この仕組みがあるからこそ、Rubrikの ふるまい検知 や 脅威ハンティング が最大限に効果を発揮します。 ふるまい検知(Anomaly Detection) Rubrikのふるまい検知は、 メタデータ監視 と エントロピー解析 を組み合わせた多層的な仕組みで、ランサムウェア攻撃を早期に検知することができます。 では、「どうやって異常を検知するの?」その仕組みを詳しくご紹介します。 メタデータを活用した監視 まず最初に注目すべきポイントは、ふるまい検知がデータそのものではなく、 メタデータ を監視の対象としていることです。 メタデータ:ファイル名、ディレクトリ構造、サイズ、UUIDなどの情報を指します。 メタデータを活用することで、データそのものには触れずにプライバシーを保護しながら、正確な監視を実現します。 そして、Rubrikのふるまい検知では主に以下の方法で異常を見つけます: 機械学習で長期的な変更パターンの分析 Rubrikは、過去のバックアップデータをもとに、通常のデータ変更のパターンを学習します。そのパターンを超えるような突然の大規模な変化をキャッチする仕組みです。 たとえばこんな場合に検知します: 毎日数MB程度しか変更のないデータが、ある日突然数GBの変更を記録した場合。 「2025年度計画」というフォルダに10個のファイルが保存されていたのに、次のバックアップではそのフォルダが丸ごと消えていた場合。 これらのケースでは、「異常な変化」としてRubrikが警告を出します。 エントロピー解析による異常検知 しかし、メタデータだけを基にした異常検知には限界があります。例えば、 攻撃者の手口が巧妙だった場合 攻撃者がローカル環境でファイルを暗号化してからバックアップが行われた場合、メタデータには特に目立った変化が現れない。 利用者の行動による誤検知 正常な操作の一環として大量のファイルを追加した場合でも、異常と判断されてしまう。 このようなケースに対応するために、Rubrikは エントロピー を利用しています。 エントロピー:データのランダム性を表す指標です。データがどれくらいバラバラで規則性がないかを示します。 暗号化されたデータはランダム性が高まるという特徴を活かし、この変化をキャッチすることで、ランサムウェア攻撃の兆候を検知します。 例として: 攻撃前のファイル名:「顧客情報.xlsx」「見積書.pdf」 攻撃後のファイル名:「abc123.xyz」「xyz789.enc」 暗号化されたファイルの名前や構造が大きく変わると、エントロピーが急激に増加します。Rubrikはこの変化をいち早く検知し、警告を発することで早期対応を促します。 脅威ハンティング (Threat Hunting) 脅威ハンティング は YARAルール のような具体的な痕跡情報を基に、バックアップデータを詳細にスキャンし、 感染源や侵入時期を特定する 機能です。 ふるまい検知との違い: ふるまい検知 :機械学習を活用してバックアップデータの変更パターンを分析し、 ランサムウェア攻撃後 に発生する異常(大量の変更や暗号化)を検知する。 脅威ハンティング :具体的な痕跡情報を基に、 攻撃を受けたかどうかに関係なく 、感染源やランサムウェアの侵入時期をチェックする。 脅威ハンティングでは、以下のプロセスでバックアップデータを解析します: 痕跡情報を入力 まず、ランサムウェアに関連する YARAルール を入力します。 YARAルールとは、攻撃者が使用する特定の暗号化方法や拡張子(例:「.encrypted」や「.lock」)を検出するためのルールセットです。 もしご興味があれば、下記のドキュメントをご参照ください。(おそらく英語版のみですが、ご参考になるかと思います。) Welcome to YARA’s documentation! — yara 4.5.0 documentation ユーザーが自身で入力する必要がありますが、RubrikはあらかじめデフォルトのYARAルールも提供しており、初めての方でもスムーズに利用を開始できます。 バックアップデータを照合 次に、Rubrikが入力された情報を基にバックアップデータをスキャンします。例えば以下のようなことをチェックします: 特定のファイルやフォルダが存在していないか。 ランサムウェアによる暗号化の痕跡がないか。 感染システムの特定 スキャンが完了すると、もしランサムウェアが侵入していた場合、その 侵入タイミング や 感染したシステム が特定されます。 これにより、問題が発生した箇所やその影響範囲が明確になり、適切な対策を講じやすくなります。 Rubrikのランサムウェア対策を実際に試してみた これまでRubrikのランサムウェア対策についてご紹介してきましたが、「実際にどのように動くのか?」と思った方も多いのではないでしょうか? 今回は、その仕組みを確かめるために、 ふるまい検知 を使ったシミュレーション を行いました。 Rubrikはオンプレミス環境だけでなく、AWSやGoogle Cloud、vSphereなど、さまざまなクラウド環境に対応していますが、 今回はその中でも vSphereの仮想マシン を使用して実験を行いました。その様子をぜひご覧ください! シミュレーション方法 Rubrikのふるまい検知は、 大量のファイル追加や暗号化などの異常な動き を検知する仕組みなので、 今回のシミュレーションでは、以下の手順で検証を進めました。   目的 手順 1 正常時の状態取得 正常なバックアップデータを複数回取得し、RSC(Rubrik Security Cloud)に連携する。 2 ランサムウェア攻撃の再現 仮想マシン内で大量のファイルを追加し、それらを暗号化する。 3 異常検知の確認 再度バックアップを取得し、RSCでどのように検知されるかを確認する。 シミュレーション結果 正常時の状態取得 まずは、バックアップデータの長期的なパターンを学習できるように、正常な状態のバックアップデータを 5回分 取得しました。 これらのデータはRSC上にも反映されており、Rubrikはこれらをもとに機械学習で 通常の動き を学習します。 以下の図は、正常時のバックアップがどのように記録されているかを示しています: ランサムウェア攻撃の再現 次に、仮想マシン内で大量のファイルを追加し、それらを暗号化しました。 以下の図にある 「xxxxx.encrypted」 のようなファイルは、暗号化されたことを示しています: その後、もう一度バックアップを取得し、Rubrikがどのように反応するかを確認しました。 異常検知の確認 結果は期待通りでした! 大量のファイル変更や暗号化 を素早く感知し、異常として Event に記録しました。 以下の図のように、 Event Timeline に、今回の変更内容( 5003件の追加、17件の変更、2件の削除 )が詳細に記録されています: また、RSCではふるまい検知の結果が下の図のように、可視化されて表示されます。 便利なのは、 Anomaliesセクション を見るだけで、過去24時間以内に 1件の異常 が検知されていることを簡単に確認できます! さらに、そのAnomaliesをクリックすることで、 どの機器のどのフォルダ内のどのファイルが影響を受けたのか まで特定することができました。 最後にメールを確認すると、検知アラートが届いていました。便利で、状況をすぐに把握することができました。 最後に 今回の記事では、Rubrikのランサムウェア対策について、仕組みの解説とシミュレーションの結果を交えながらお届けしました。「ふるまい検知」と「脅威ハンティング」の2つの対策が、ランサムウェア攻撃にどう役立つのか、少しでもお伝えできていれば嬉しいです! 特にシミュレーションを通じて、ふるまい検知がいかに迅速かつ正確に異常を発見できるか、その便利さを感じていただけたでしょうか? 今回は脅威ハンティングのシミュレーションまではできませんでしたが、気になる方はぜひ試してみてください。 最後まで読んでいただき、ありがとうございました!
本記事は、「 AWS User Notifications で EC2 の AWS Health イベントを通知してみた! 」の続編となります。 前回の記事で実装したAWS User Notificationsをテストするため、AWS Health イベントを発生させ、Eメールで通知が届くことを確認したいと思います。 実施内容 概要 以下の内容でテストを実施します。 EC2に関するAWS Health イベントを発生させる EC2に関するAWS Health イベントがEメールで通知されることを確認する それでは早速ですが、実際にテストしていきましょう!! 1. EC2に関するAWS Health イベントを発生させる AWS Healthテストイベントを発生させたい場合は、AWSサポートにリクエストすることができます。 ※リクエストをするためには、デベロッパー、ビジネスまたはエンタープライズサポートプランに加入している必要があります。 ※本記事はAWS User Notificationsをテストを目的としているため、AWSサポートへのリクエストの詳細な手順については割愛いたします。リクエストを行う場合は、以下リンクをご確認いただき実施することをおすすめします。 テスト用の AWS Health イベントをリクエストする AWS Health イベントを使用して AWS Health との統合をテストしたいと思います。 repost.aws 今回は、以下のAWS Health イベントをAWSサポートにリクエストしました。 リージョン東京リージョン: ap-northeast-1 イベントタイプのカテゴリー: accountNotification イベントタイプのコード: AWS_EC2_OPERATIONAL_NOTIFICATION EC2のサービスに問題がある場合に発生するイベントを指定しています。 どのようなイベントタイプがあるのかについては、「 AWS User Notifications で EC2 の AWS Health イベントを通知してみた! 」の事前確認で紹介したAWS Health APIで確認することが可能です。 2.EC2に関するAWS Health イベントがEメールで通知されることを確認する AWSサポートへのリクエスト時に、テストイベントを作成させたい日時を指定したので、指定した時間にAWS Health イベントが発生していることを確認します。 ※テストイベントを作成させたいイベントの日時は、希望の日時での対応を約束するものではないため、テストイベントの発行まで最大3営業日かかることがあるようです。 指定した日時に以下のようなメールを受信しました。こちらで問題なく、AWS User Notificationsが設定できていることが確認できました。 ちなみに、コンソールからもAWS Health イベントを確認することができます。 まとめ AWS Healthイベントを利用することで以下のようなメリットを得ることができます。 障害発生時の切り分けを素早く行うことができる 障害やメンテナンスの通知を受け取ることで、準備や対処ができる サービスの可用性を高めることができる さらにAWS User Notificationsを利用して通知機能を実装することで、比較的容易に設定することが可能だと思います。 皆さんもAWS User Notificationsを利用したAWS Health イベントの実装をしてみるのはいかがでしょうか。 皆様のお役に立ちましたら、嬉しいです。 最後までお読みいただきありがとうございました。
ServiceNowのUI Builderを触る機会があったため、一部ですがその機能を紹介させていただきます。 本記事は執筆時点(2025年1月)の情報です。最新の情報は製品ドキュメントを参考にしてください。 UI Builderとは UI Builderとは、ワークスペースなどのユーザーインターフェースを作成することができるローコードツールです。 様々な種類のコンポーネントが標準で用意されており、これらを使用してレイアウトやスタイリング、インスタンス内に蓄積されたデータを可視化することで、各ユーザーに対してパーソナライズされたエクスペリエンスを提供することができます。 実際に触ってみる レイアウト UI Builderで画面を作る際は、まずレイアウトを決めて、コンポーネントを配置していく流れとなります。 画像のようにフレキシブルにレイアウトを設定することが可能です。   データの可視化 ServiceNow上に蓄積されたトランザクションデータを視覚的に分かりやすくユーザーに対して表示することができます。 画像のように自分にアサインされたタスク、今週期限のインシデント対応状況など、各ペルソナの目的に応じて柔軟に可視化することができます。(ログインしているユーザーにより、値は動的に変化します) ログイン後の最初の画面で表示すれば、ユーザーは瞬時に自身、自身が所属するグループのタスク状況を把握することができます。 また、画面をクリックすることで対象のレコードに遷移することも可能です。   メニュー・リスト 自由にメニューを設定できます。各ペルソナが業務を遂行する上で必要なテーブルのみをメニューとして用意すれば、より効率的に作業を進めることができそうです。 インスタンスにログインした際に画面左に表示されるアプリケーションナビゲーターでは、閲覧権限のあるモジュールが全て表示されるので、目的のモジュールの場所が覚えられない、検索するのが大変といった場合に活用できそうと思いました。 カレンダー カレンダー形式で自身のタスクを管理することが可能です。 画像では自身のタスクについて、起票日から対応期限までを帯としてカレンダー表示させています。   サポートセンター的なページを作成してみる 標準で用意されているコンポーネントを使用するだけなので、30分程度で簡単に作成することができました。 ・画面上部 マニュアルをツリー形式で表示しています。リンクをクリックすると外部URLに遷移できます。 ・画面中央 ServiceNow上に作成されたナレッジ記事をFAQとして一覧表示しました。ボタンをクリックすることでナレッジ記事を閲覧できます。 ・画面下部 問合せフォームを表示しています。マニュアルやFAQで解決しない場合にサポートへ問合せすることができます。 感想 今回記事にしたのは一部のコンポーネントのみであり、他にも沢山の機能が用意されています。(数えてみると約150個ありました) 企業内でServiceNowを使用していく際、様々なペルソナが登場します。各々が見たい情報や業務の遂行に必要な機能を一つの画面に表示するなどなど、パーソナライズされたエクスペリエンスをユーザーに提供できることが利点と思いました。 ServiceNow ServiceNowは、企業の生産性向上や業務プロセス改善を支えるサービスマネジメントクラウドソリューションです。従業員満足度・顧客満足度の向上、業務効率化を強力に支援します。 www.scsk.jp
こんにちは、広野です。 AWS AppSync は Amazon API Gateway と同様に API を公開してくれるサービスですが、必ず何かしらの認証がないと動かないサービスです。私は Amazon Cognito 認証を使用することが多いのですが、Amazon Cognito で認証されたユーザーであれば誰でも実行できるクエリもあれば、例えば管理者のみに実行させたいクエリもあります。 そんなときに Amazon Cognito ユーザーの属性としてグループがあり、それを拠り所にして権限を分ける方法が最も簡易です。AWS IAM ロールを使用した方法も実装可能だと思いますが、IAM ロールをグループごとに用意しないといけないので面倒です。 今回は、AWS AppSync Lambda リゾルバ (JavaScript) の標準的な書き方説明も兼ねて、権限分けの書き方も紹介したいと思います。ちなみに本件は AWS 公式ドキュメントでは以下のページが関連します。 AWS AppSync JavaScript リゾルバーの概要 - AWS AppSync AWS AppSync の JavaScript リゾルバーの概要 docs.aws.amazon.com リクエストとレスポンスを保護するためのアクセスコントロールのユースケース - AWS AppSync AWS AppSync の承認シナリオ。 docs.aws.amazon.com サンプルアーキテクチャ 以下の構成例で紹介します。主に緑色の線の部分にフォーカスします。 AWS AppSync は Amazon Cognito 認証です。基本的に、Amazon Cognito 認証を受けたユーザーは AWS AppSync にアクセスできる構成です。 AWS AppSync に Lambda リゾルバを作成し、AWS Lambda 関数を関連付けます。 AWS Lambda 関数は、Amazon Cognito ユーザーのリストを取得します。 AWS AppSync では、データベースへの読み書きは AWS Lambda 関数抜きで基本的なことはできるので、AWS Lambda 関数を使用する機会が少ないです。そして、AWS Lambda 関数が無い方がレスポンスが速くなります。なので、AWS Lambda 関数を使用する例として Amazon Cognito ユーザーのリストを取得することを採用しています。 この例のように Amazon Cognito のユーザーリストを取得して良い人は、一般的には管理者に限られているはずです。そのため、この Lambda リゾルバは管理者用の Amazon Cognito グループに所属している人でないと実行できないようにしたいです。 実装するもの 権限関連設定について少しブレークダウンして説明します。 まず、アプリ側では、管理者以外に見せたくない画面は表示させないようにします。アプリ内で Amazon Cognito ユーザーの属性は取得できるので、Cognito グループ属性をもとに表示する/しないを判別させます。 ただし、それはあくまでもアプリによる表面的な表示制御だけです。ユーザーに無用な API アクセスをさせないためだけの。AWS AppSync に何らかの形で直接命令が来てしまったときは許可してしまうので、AWS AppSync 側でも Cognito グループによる実行制御を入れます。Lambda リゾルバ内で Amazon Cognito 認証に使用されたトークンの内容を取得できるので、それをもとに判別します。AWS Lambda 関数を呼び出す前にリゾルバ内で処理します。 アプリ側の画面表示制御 本記事の本題ではないですが、一応 React アプリを例としてコードを載せます。 前提として、以下の記事で紹介しているような React アプリから AWS AppSync に Amazon Cognito 認証でアクセスする設定が済んでいることとします。 AWS AppSync のリゾルバ内で Amazon Cognito ユーザーの email 属性やカスタム属性を取得する AWS Lambda 関数を使用せずに実現する方法がわかったので紹介します。 blog.usize-tech.com 2025.01.08 まず、React アプリ内で Amazon Cognito の属性情報は以下のコードで取得できます。書き方はいろいろあるので、一例です。 //必要モジュールインポート import { fetchAuthSession } from 'aws-amplify/auth'; //state初期化 const [email, setEmail] = useState(); //ユーザーのメールアドレス const [authToken, setAuthToken] = useState(); //ユーザーのトークン const [groups, setGroups] = useState(); //ユーザーのCognitoグループ //セッション情報取得 const getSession = async () => { const { tokens } = await fetchAuthSession(); setEmail(tokens?.idToken?.payload.email); //メールアドレスはこれで取得 setGroups(tokens?.idToken?.payload["cognito:groups"]); //Cognitoグループはこれで取得 setAuthToken(tokens?.idToken?.toString()); //IDトークンはこれで取得 }; useEffect(() => { getSession(); }, []); こうして取得した groups の中にユーザーが所属する Cognito グループが配列で格納されているので、(複数所属している可能性もあるので) この情報を活用して該当の画面を表示/非表示を制御できます。 例えば、管理者用のグループ名が ADMIN だったとすると、シンプルに以下のように書きます。メニュー画面のうち、管理者用画面へのリンクを表示制御する例です。groups に ADMIN が含まれているかどうかを条件にしています。 {/* ホーム画面ボタン */} <Button to="/" onClick={handleCloseNavMenu} component={Link} sx={{mr:2,color:"white",display:"block"}}>ホーム</Button> {/* 設定メニューボタン */} <Button to="/config" onClick={handleCloseNavMenu} component={Link} sx={{mr:2,color:"white",display:"block"}}>設定</Button> {/* Adminメニューボタン */} {(groups.includes("ADMIN")) && ( <Button to="/admin" onClick={handleCloseNavMenu} component={Link} sx={{mr:2,color:"white",display:"block"}}>Admin</Button> )} {/* サインアウトボタン */} <Button onClick={signOut} sx={{mr:2,color:"white",display:"block"}}>サインアウト</Button> もちろん、リンク先のパスを知られていたら意味がないので、リンク先の方でも画面全体に同様の表示制御条件をかけます。 管理者用画面では、Amazon Cognito ユーザーリストを取得するためのクエリを書きます。 ここで、想定するリクエストとレスポンスの書式を整理します。 以降、このデータ授受をする前提で AWS AppSync の設定を書きます。 AWS AppSync の設定 スキーマ 必要な箇所を抜粋します。 schema { query: Query mutation: Mutation subscription: Subscription } type CognitoUsersItems { id: Int email: String createddate: String status: String } type CognitoUsers { items: [CognitoUsersItems] } type Query { queryListCognitoUsers(dummy: String!): CognitoUsers } リクエストで dummy というパラメータを渡すので、Query の定義に入れています。 レスポンスは items というキーで配列を格納する階層構造になっているので、それを表現しています。 データソース AWS AppSync の Lambda リゾルバを使用する場合、データソースのタイプを Lambda にする必要があります。データソースの設定で、関連付ける AWS Lambda 関数や、その AWS Lambda 関数を呼び出せる権限 (IAM ロール) を設定します。この詳細は割愛します。 Lambda リゾルバ リゾルバの設定で、データソースを上述のものを指定します。これにより、そのリゾルバがデータソースで設定された AWS Lambda 関数と連携できるようになります。記事の本題の1つでもある、このリゾルバの書き方を説明します。 JavaScript リゾルバでは、マッピングテンプレートのリクエスト、レスポンスは 1 つの JavaScript コードの中で表現します。VTL ではそれぞれが分かれていましたが。 今回のリクエスト、レスポンス仕様を実現すると、以下のコードになります。 //リゾルバ内で使用可能なユーティリティ関数をインポート import { util } from '@aws-appsync/utils'; //リクエストマッピングテンプレート export function request(ctx) { //ctx の中にアプリから渡された引数(args)やヘッダー情報が格納される const groups = ctx.identity.claims['cognito:groups'] //ここで、トークンに格納された Cognito グループを取得 if (groups.indexOf('ADMIN') === -1) { //Cognitoグループに ADMIN が含まれているかチェック util.unauthorized(); //ADMIN が含まれていなければ、認証エラーとして返す } else { return { operation: 'Invoke', //Lambda関数を呼び出すオペレーション定義 payload: { ctx.args //Lambda関数に渡すペイロード、ここではアプリからもらった値(dummy)をパススルーする } }; } } //レスポンスマッピングテンプレート export function response(ctx) { //ctx の中にLambda関数のレスポンスが格納される if (ctx.error) { util.error(ctx.error.message, ctx.error.type); //レスポンスにエラーが含まれていればエラーで終了させる } return ctx.result; //Lambda関数から return で返した値は ctx.result に格納される、ここではそのまま AppSync にパススルーする } ctx.identity.claims[‘cognito:groups’] に格納されているグループ名の配列に ADMIN が含まれているかチェックするのに includes を使用したいところですが、JavaScript リゾルバではサポートされていないので、代わりに indexOf を使用しています。JavaScript の機能は何でもリゾルバ内で使えるかと言うと全然そうではなくて、かなり限定されているので注意が必要です。 リゾルバーおよび関数の AWS AppSync JavaScript ランタイム機能 - AWS AppSync AWS AppSync の JavaScript リゾルバーユーティリティヘルパーについて説明します。 docs.aws.amazon.com このリゾルバ構成は、結構標準的に使えるのではないかと考えています。 AWS Lambda 関数 リゾルバから呼び出された AWS Lambda 関数は、AWS AppSync 経由でアプリから渡された引数 (ここでは dummy の値) を取得できます。コード内では表現していませんが、本記事のリクエスト仕様では、event[‘dummy’] で “dummy” を取得できます。 また、レスポンスとして想定しているフォーマットでレスポンスを返すよう、書いたものが以下のコードになります。Python です。 Amazon Cognito ユーザープール ID は環境変数にしています。IAM ロールは割愛します。 import json import boto3 import os LIMIT = 60 # Amazon Cognito の list_users は最大60件しか一度にデータを取得できない USER_POOL_ID = os.environ['CognitoUserPoolID'] client = boto3.client('cognito-idp') def lambda_handler(event, context): try: response = client.list_users( UserPoolId=USER_POOL_ID, AttributesToGet=['email'], Limit=LIMIT ) user_records = response['Users'] while True: # PaginationToken が無くなるまで(データを全て取得するまで)データ取得処理を繰り返す if 'PaginationToken' not in response: break # PagenationToken がある限り、データを取得する response = client.list_users( UserPoolId=USER_POOL_ID, AttributesToGet=['email'], Limit=LIMIT, PaginationToken=response['PaginationToken'] ) user_records.extend(response['Users']) # user_records に最終的に全データが格納される except Exception as e: print(e) reserr = { "items": [ { "id": 0, "email": "Error", "createddate": None, "status": "Error" } ] } return reserr # エラー発生時はエラー用のレスポンスを AppSync に返す else: output = [] # Amazon Cognito から返ってきた生データを、必要なデータだけ抽出する for i, row in enumerate(user_records): transformed = { "id": i, "email": next(attr["Value"] for attr in row["Attributes"] if attr["Name"] == "email"), "createddate": str(row["UserCreateDate"]), "status": row["UserStatus"] } output.append(transformed) return { "items": output # 抽出したデータ(配列)を items に格納して AppSync に返す } 最後、return で items を返しています。これが、AWS AppSync をパススルーしてアプリに返ってきます。 アプリ側の AppSync クエリ実行 アプリ側では、上述のレスポンスを取得するためのクエリを実行します。本記事のシナリオでは、管理者用画面で実行するものです。 AWS AppSync スキーマの仕様に合わせて記述します。前述のスキーマ設定と照らし合わせるとわかりやすいと思います。記述箇所が離れててすみません。リクエスト、レスポンスのデータ定義がスキーマの仕様と少しでも合わないとエラーになります。厄介です。 //AppSync呼出用モジュールインポート import { generateClient } from 'aws-amplify/api'; import gql from 'graphql-tag'; //AppSync Client const client = generateClient(); //Cognitoユーザーリスト取得関数 const listCognitoUsers = async () => { const queryListCognitoUsers = gql` query queryListCognitoUsers($dummy: String!) { queryListCognitoUsers(dummy: $dummy) { items { id email createddate status } } } `; const res = await client.graphql({ query: queryListCognitoUsers, variables: { dummy: 'dummy' //dummyをAppSyncに渡す(本記事の説明用途で) } }); return res.data.queryListCognitoUsers.items; //AppSyncからのレスポンスは、data.Query名に格納される }; この listCognitoUsers 関数で、アプリから Amazon Cognito ユーザーのリストを配列で取得できます。フォーマットは必要情報を抽出したものになっていますが。 まとめ いかがでしたでしょうか。 アプリ、AWS AppSync、AWS Lambda 関数の間でデータを授受する仕様が理解できるのではないかと思います。同時に、Amazon Cognito グループによるリゾルバの実行制御を簡易に仕掛けることができています。 本記事が皆様のお役に立てれば幸いです。
本記事は 新人ブログマラソン2024 の記事です 。 皆さんこんにちは!入社して間もない新米エンジニアの佐々木です。 前回、前々回と2回に渡って、Snowflake CortexAIを使ってドキュメント検索アシスタントを構築するチュートリアルに挑戦し、その様子を記事にまとめさせていただきました。多くの方々に読んでいただき、大変嬉しく思っています。 まだ読めていないという方は、以下の記事をまずは読んでいただけると幸いです!! 新米エンジニアが挑む!Snowflake CortexAIでドキュメント検索アシスタントを構築してみる – TechHarmony 新米エンジニアが挑む!Snowflake CortexAIでドキュメント検索アシスタントを構築してみる【チャットボットバージョン】 – TechHarmony さて今回の記事は、ようやくこのチュートリアルの 最終話 となります。最終話の立ち位置としては、前回までの内容にオプション機能を追加するライトな内容となります! 具体的には、前回まででチャットボットバージョンのドキュメント検索アシスタントの構築が完了したため、今回は 新しいドキュメントの自動処理 に踏み込んでみたいと思います。 それでは早速行きましょう!! 前回までの振り返り 前回、前々回と以下の公式チュートリアルをハンズオン形式で実践し、 Snowflake Cortex AIのベクトル埋め込み機能を利用して、ドキュメント検索アシスタントを構築 しました! Build A Document Search Assistant using Vector Embeddings in Cortex AI 特に前回は、前々回の内容にプラスして より高度なチャットボットバージョンのドキュメント検索アシスタントの構築 をしました! 具体的なStreamlitのコード解説が気になる方は前回の記事を参照していただきたいのですが、最終的には以下のようなアプリを構築することができました! 上記を見てわかる通り、 ユーザーとの会話内容を記憶して、チャットボットのように会話できるアプリ となっています。 なぜこのようなチャットのような会話が実現できるのかについて簡単に説明します。 まず、ユーザーが質問を投げかけると予めSnowflake上に格納されたドキュメントをもとにLLMが回答を作成(RAG)し応答します。このとき、ユーザーの質問と作成された回答をペアとしてStreamlitのセッション領域に一時的に格納します。 次に、ユーザーが続いて質問をすると、セッションに格納した チャット履歴と質問内容を組み合わせて要約された内容がLLMに渡され、回答作成および応答するという仕組みです。 これにより、前回の会話内容を記憶した会話形式が実現できるわけです。 大雑把な説明とはなってしまいましたが、詳細が気になる方は前回の記事をぜひご覧ください!! 今回実施するチュートリアルの概要 前回実施したチュートリアルでは前述したとおり、ユーザーとの会話内容を記憶して、チャットボットのように会話できるアプリを構築しました。 ここまでの内容でドキュメント検索アシスタントの9割以上は構築完了しているのですが、1つだけ不便な点があります。 それは 新しいドキュメントを追加したいとき です。 現状だと、Snowflakeのステージ上に新しいドキュメントを追加しただけでは、RAGで検索対象のドキュメントとはなりません。なぜなら、ドキュメントを検索する際にはステージ上のドキュメントそのものを参照するのではなく、ドキュメントのチャンクが格納された独自のテーブル(docs_chunk_table)を参照するためです。このようにしている理由は、検索効率を向上させるためです。 つまり、現状だと新しいドキュメントをRAGの検索対象にしたいのであれば、ドキュメントをもとに分割されたチャンクをdocs_chunk_tableにinsertするコマンドを手動で実行しなくてはならないという不便さがあります。 そこで、より快適な運用に向けて 新しいドキュメントを自動処理する仕組みを構築 していきたいと思います! 実際に挑戦してみた!! ワークロード定義 新しいドキュメントの自動処理を実現するために、今回はSnowflakeの ストリーム と タスク という2つを用います! 早速ですが、Snowflakeワークシートで以下のコードを実行します。 create or replace stream docs_stream on stage docs ; create or replace task task_extract_chunk_vec_from_pdf warehouse = COMPUTE_WH schedule = '1 minute' when system$stream_has_data ( 'docs_stream' ) as insert into docs_chunks_table ( relative_path , size , file_url , scoped_file_url , chunk , chunk_vec ) select relative_path , size , file_url , build_scoped_file_url ( @docs , relative_path ) as scoped_file_url , func . chunk as chunk , SNOWFLAKE . CORTEX . EMBED_TEXT_768 ( 'e5-base-v2' , chunk ) as chunk_vec from docs_stream , TABLE ( pdf_text_chunker ( build_scoped_file_url ( @docs , relative_path ))) as func ; alter task task_extract_chunk_vec_from_pdf resume ; 上記のコードについて補足説明をします。 コード全体としては、 新しいPDFファイルがステージに追加されるたびに、自動的にテキストチャンクを抽出し、埋め込みベクトルを生成するというワークロードを定義しています。 では具体的に、各行がどのような処理を行っているのかについて触れていきます。 1行目: create or replace stream docs_stream on stage docs ; docs_streamという名前のストリームを新たに作成しています。 ストリームは、テーブル、ビュー、またはスキーマに加えられたDML変更(挿入、更新、削除)の変更履歴を記録する役割を持ちます。これにより、新しいPDFファイルがdocsステージに追加されると、docs_streamはこれらの変更を記録することができます。 3行目: create or replace task task_extract_chunk_vec_from_pdf task_extract_chunk_vec_from_pdfという名前のタスクを作成しています。 タスクは、SQLステートメント(クエリ、DML操作、ストアドプロシージャの実行など)を自動的に実行するようにスケジュールできるデータベースオブジェクトです。 4行目~6行目: warehouse = XS_WH schedule = '1 minute' when system$stream_has_data ( 'docs_stream' ) 上記タスクを実行するウェアハウス、頻度、条件を定義しています。 具体的には以下の定義です。 ウェアハウス:COMPUTE_WH 頻度: 毎分 条件:docs_streamストリームに新しいレコードがある場合に実行 9行目~19行目: insert into docs_chunks_table ( relative_path , size , file_url , scoped_file_url , chunk , chunk_vec ) ・・・ タスクで実行するSQLステートメントを定義しています。 具体的には、docs_chunks_tableテーブルにデータを挿入するクエリを定義しています。テーブルには、ファイルパス、サイズ、URL、チャンクテキスト、そして埋め込みベクトルなどの情報が格納されます。 21行目: alter task task_extract_chunk_vec_from_pdf resume ; タスクを再開するための処理です。 タスクは手動停止やエラー発生時など様々な理由で一時停止状態になることがあります。そのため、一時停止状態のタスクを再開し、スケジュールされた通り、またはトリガー条件が満たされたときに実行されるようにできます。 検証 では実際に新しいドキュメントをステージ上にアップロードすることで、自動的にタスクが実行されるかについて確認してみましょう! 具体的には、ドキュメントをアップロードする前と後の状態でドキュメントに関連する質問を投げかけ、的確な回答が返ってくるか否かを確認してみます。想定としては、ドキュメントをアップロードする前の状態だと的確な回答が返ってこないのに対して、アップロードした後の状態だと的確な回答が返ってくるはずです。 では実際にアップロードするドキュメントについてですが、今回は公式チュートリアルから拝借した「The_Xtreme_Road_Bike_105_SL.pdf」というエクストリームロードバイクに関するPDFファイルを使用したいと思います。 そして、このドキュメントに関連する「Is there any discount for the Xtreme Road Bike?(エクストリームロードバイクの割引はありますか?)」という質問をチャットで投げかけてみたいと思います。 ドキュメントアップロード前 まずは、ドキュメントをアップロードする前の状態でチャットに質問を投げかけてみます。 投げかけた結果、以下のような回答が返されました。 上記のチャット内容を日本語に訳すと以下の通りです。ここで、黒文字が質問、赤文字が回答となります。 エクストリームロードバイクの割引はありますか? エクストリーム・ロードバイクの割引情報はありません。最も正確で最新の価格情報については、メーカーのウェブサイトを確認するか、カスタマーサービスに問い合わせることをお勧めします。 上記の通り、ドキュメントアップロード前では、ドキュメントに関するユーザーの質問に対して適切な回答を返せていないことがわかります。 ドキュメントアップロード後 次に、ドキュメントをアップロードした前の状態でチャットに質問を投げかけてみます。 投げかけた結果、以下のような回答が返されました。 上記のチャット内容を日本語に訳すと以下の通りです。ここで、黒文字が質問、赤文字が回答となります。 エクストリームロードバイクの割引はありますか? はい、割引の話があります。自分のサドルを持参すると100ドル割引になります。 上記の通り、ドキュメントアップロード後では、ドキュメントに関するユーザーの質問に対して割引額も含めた適切な回答が返せていることがわかります。 つまり、新しいドキュメントをステージ上にアップロードするだけで、自動的にドキュメントのチャンク分割やテーブル挿入、そして結果的にRAGの検索対象にできていると言えます! まとめ 本記事では、前回の記事までで作成したチャットボットUIに対して、 新たなドキュメントを自動処理するためのワークフローについて解説 しました。 従来、ドキュメントの検索や分析には、手動でのアップロードやインデックス作成が必要で、時間と労力を要していました。しかし、Snowflakeが提供するストリームやタスクの機能を使うことで、これらのプロセスを自動化できることが分かりました! これにより、迅速な情報検索が可能になり、業務効率の大幅な向上が期待できるというメリットがあります。 さらに、様々なファイル形式に対応しているため、様々な種類のドキュメントを統一的に管理・検索できる点も大きなメリットだと考えられます。そのため、大量のドキュメントを扱う企業にとって、この機能は業務効率化の強力なツールとなるでしょう。 ぜひ、前回、前々回で紹介したCortexAIを用いたドキュメント検索アシスタント、本記事で紹介したドキュメントの自動処理機能を参考にして、RAGシステムの構築や情報検索の効率化に活かしてみてください!
こんにちは、SCSK株式会社の嶋谷です。2024年度入社の新入社員です。 現在私は、AIプラットフォームやWebサーバ・監視サーバの構築といったインフラ基盤の構築業務に携わっています。 今回は、Docker上に構築したAIプラットフォーム基盤をMackerelで監視してみたいと思います。 Mackerelとは Mackerelは、株式会社はてなが提供するSaaS型の監視ツールです。 サーバの状態(CPUやメモリ)を監視し、事前に設定された閾値を超えたときにオペレータや管理者に通知を行うことができるツールです。加えて、オンプレミス環境だけでなくクラウド環境やコンテナ環境も監視することができます。 Mackerelの詳細情報については、下記リンクをご参照ください。 Mackerel(マカレル): 始めやすくて奥深い、可観測性プラットフォーム Mackerel(マカレル)は誰でも簡単に始めやすく奥深い可観測性プラットフォーム。運用をイージーにするオブザーバビリティを高め、未知の問題に立ち向かう開発者に力を与えます。サーバー監視をMackerelではじめてみませんか?無料プランや2... ja.mackerel.io MackerelでDockerを監視する記事が数少ないため、今回実際に実装して記事を書きたいと思いました。 Dockerを監視してみた(監視対象について) 今回の検証では、Dockerを監視するためにコンテナを作成する必要があります。そこで、今流行りのLLMやRAGを活用し、簡単にアプリ開発が可能なAIプラットフォーム「Dify」をAWS環境に構築しました。 監視対象の構成図は以下の通りで、AWSのEC2上にDocker環境を構築し、Docker上でDify環境を構築しています。 Difyについては本記事の主要なトピックではないため、興味がある方は下記リンクをご参照ください。 Dify.AI · 先進的なAIアプリケーションのためのイノベーションエンジン 先進的なAIアプリケーションのためのイノベーションエンジン dify.ai Dockerを監視してみた(設定方法) それでは、MackerelでDockerを監視する方法を説明していきます。(今回はAmazonLinux2のOSを使用しているため、その方法について記載します。) エージェントのインストールと起動 まずは、監視対象ホストにエージェントをインストールします。 以下のコマンドを実行してインストールと起動を行います。  APIキーはMackerelのWebコンソールから参照しています。               curl -fsSL https://mackerel.io/file/script/amznlinux/setup-all-yum-v2.sh | MACKEREL_APIKEY='YOUR_API_KEY' sh sudo systemctl start mackerel-agent 起動するとMackerelに監視対象ホスト(EC2)が登録されます。 Mackerelの画面では以下の表示になり、ホスト名・ホストの情報・取得したメトリックのグラフが表示されています。 ドキュメントどおりにコマンドを実行するだけでホストを登録できました。 ドキュメントどおりに設定しても動作しない作業も経験してきたので、この作業はサクッと完了して感激しました。 公式プラグインのインストール Dockerを監視するために公式プラグインをインストールする必要があります。 以下のコマンドを実行して公式プラグインをインストールします。(プラグインを3の手順で使用します。)                                        sudo yum install mackerel-agent-plugins この作業もドキュメントどおりにコマンドを実行するだけで完了します。 設定ファイルの修正 最後に設定ファイルに以下のプラグインを追記します。        [plugin.metrics.docker] command = ["mackerel-plugin-docker", "-name-format", "name_id"] -name-formatではコンテナ名(name)やコンテナID(id)やコンテナ名+コンテナID(name_id)など複数のオプションを設定することができます。このオプションによってMackerel上のグラフ表示に違いがあります。 ここで注意が必要です! 安易にオプションをコンテナIDに設定してしまうとMackerel上でコンテナIDしか表示されず、何の機能を持つコンテナかわからなくなります。 そのため今回はコンテナ名+コンテナID(name_id)に設定することで名前とIDをグラフ上で紐づけています。 プラグインを追記後、エージェントを以下のコマンドで再起動します。 sudo systemctl restart mackerel-agent すると、Mackerel上でDockerコンテナのCPUの使用率がグラフとして表示されました! 今回作成した環境はコンテナが9つ作成されており、グラフはコンテナ毎に折れ線グラフとして表示されています(下記グラフは視認性のために2つのコンテナのグラフを表示しています)。それぞれの折れ線グラフの名前が手順3の-name-formatで設定したコンテナ名+コンテナID(先頭6文字)になっています。グラフの縦軸は使用量(%)、横軸は時刻となっています。 ちなみに-name-formatをコンテナID(id)に設定するとMackerel上では以下の画像のように表示されます。非常に見にくく、IDとコンテナの役割を紐づけることが困難です。みなさんも設定するときは気をつけましょう。 MackerelのDocker監視ではCPU・メモリ・I/O操作の3つのメトリックを監視することができます。ログ監視はできません。それぞれの詳細なメトリックについては下記をご参照ください。 mackerel-agent-plugins/mackerel-plugin-docker at master · mackerelio/mackerel-agent-plugins Plugins for mackerel-agent. Contribute to mackerelio/mackerel-agent-plugins development by creating an account on GitHub... github.com おわりに MackerelでDocker環境を監視することができました。Mackerel上での監視設定はドキュメント通りに作業を進めることで簡単に設定することができました。しかし、-name-formatの設定によるMackerel上での表示内容の違いは、オプションを変更してみたことで気づくことができました。やってみないとわからないことがあり、とても勉強になりました。 今回はDocker環境を監視しましたが、Mackerelはオンプレミス環境だけでなくクラウド環境も監視することができるためさまざまな環境を監視してみようと思います。 次回は、Mackerelと弊社の統合監視基盤FusionCOREとの連携についてのブログを投稿したいと思います。
SCSKの畑です。8回目の投稿です。 今回は、 初回のエントリ で少しだけ触れた、AWS Amplify が生成した AWS AppSync リソースの手動移行に関する備忘録のような、Tips のような内容になります。如何せんこのような作業が必要となるような案件もといシチュエーション自体が珍しいと思いますので、ニッチな内容になるとは思いますが御覧頂ければ幸いです。 なお、タイトル通り Amplify と AppSync の話題に閉じているのでアーキテクチャ図は載せません。それも何気に今回が初めてですね・・   前提条件(初回エントリのおさらい含む) お客さんが主体的に管理している AWS 環境上で各種 AWS サービスやリソースの構築・実装を行った、 CloudFormation や AWS CDK、terraform などの使用は原則 NG。AWS マネジメントコンソールの使用を使用した構築が基本。 Amplify についてはお客さんの環境ポリシー上導入 NG だったため、弊社の AWS 環境上で Amplify により作成された各種 AppSync リソースを、お客さんの AWS 環境に手動移行する必要があった。 移行対象はスキーマ、リゾルバ、関数の3つ。 データソースについては上記3つと比較して変更のタイミングが限定的であり、かつリソースとなる DynamoDB のテーブルや Lambda 関数についても申請の上お客さん側で作成頂く必要があるため、設定情報をお客さんに連携して作成頂く形式とした。 以上の内容を踏まえて、いくつかの観点から備忘録替わりに記載していきます。   余分な query や mutation を Amplify に生成させない まずは Amplify スキーマ定義上の工夫から。 @model として定義すると、テーブルだけでなく対応した query なり mutation を自動生成してくれるのが Amplify の便利な点なのですが、アプリケーションから使用しないことが明確なものもあるため、そのような不要なものは生成しないようにすることで移行作業の対象を減らすのが目的です。具体的には、スキーマ定義において生成対象の type を指定することで実現できます。 排他制御の実装例(第一回) で例示した TableInfo において、query は list、mutation は create のみをそれぞれ生成するようにする場合は以下のように定義することで実現できます。 type TableInfo @ model ( queries: { list: "listTableInfos" }, mutations: { create : "createTableInfo" }, ) @ auth ( rules : [ { allow : public, provider : apiKey}, { allow : private, provider : iam}, ]) { name : String! @primaryKey status : TableStatus! editor : String locked_by : String }   手動移行時に設定変更が必要なマッピングテンプレートの整理 次に、実作業についてですが・・こちらは単純に AWS マネジメントコンソールを弊社の AWS 環境とお客さんの AWS 環境で2画面開いて、愚直に手動で移行(もといコピペ)していただけなので、特に内容として取り上げたいものはありません。もっと楽なやり方を採用したかったとは思いますが。。 ただ、AWS 環境が異なる以上、移行時に変更する必要がある項目も幾つかあったためその点には留意しました。特に、リゾルバのリクエストマッピングテンプレートについては VTL を書き換える必要があったため、備忘録として以下に記載しておきます。逆に言うと、注意深く変更する必要があったのはこの項目くらいだったため、移行自体は思っていたほど時間がかからなかったのは幸いでした。 例えば、データソース名はお客さんに作成頂く必要があることもあり、Amplify による生成時の名称から意図的に変更していますが、データソースを意図的に指定する必要があるのは関数やリゾルバ作成時程度なので、あまり気になりませんでした。 下記例の通り、大体コピペのままで行けるのですが以下2点については修正の必要がありました。 authRole, unAuthRole を使用しないため、マッピングテンプレートから削除 adminRoles の AWS アカウント ID をお客さんの AWS 環境のものに変更 ## [Start] Stash resolver specific context.. ** $util.qr($ctx.stash.put("typeName", "<Query or Mutation or Subscription>")) $util.qr($ctx.stash.put("fieldName", "<QueryName or MutationName or SubscriptionName>")) $util.qr($ctx.stash.put("adminRoles", ["arn:aws:sts::<AWS Account ID>:assumed-role"])) {} ## [End] Stash resolver specific context.. **   手動移行した AppSync リソースの設定チェックにおける工夫 最後に、手動移行後の AppSync 設定確認方法について。 手動で移行したこともあり、環境間で移行したリソース設定をどのように比較チェックするのかがちょっとした課題でした。スキーマは画面からコピペしたものを diff 比較すれば良いとしても、関数/リゾルバのマッピングテンプレートで都度それをやるのはさすがに面倒だし、マッピングテンプレート内の関数実行順などは画面上で比較するしかないということで、どうしたものかと。当初は目視確認もやむなしかと思っていたのですが、案の定設定ミスで動作しなかった mutation が出てしまい、ある程度はちゃんと機械的に比較できる方法を考える必要がありました。 と言っても、先述の通り IaC に該当する仕組みは使用できなかったので、大人しく Python/Boto3 で AppSync の設定を一覧化して S3 に出力するようなコードを書くことにしました。弊社/お客さんの AWS 環境で出力したものを diff 比較するようにすれば、AWS アカウント ID など差異が発生する項目は出るにせよ、画面の目視確認よりは大分マシだろうなと。 なお、順番が前後してしまいますが、現在は Amplify スキーマ更新時などにどこが変更されたのか(=どのリゾルバ/関数を手動移行する必要があるのか)を検出するためにも使用しています。単純な type の追加程度なら何を移行すれば良いのかある程度直感的に分かると思うのですが、変更(更新)とかになるとリゾルバ内のどの関数なりマッピングテンプレートが変更されたか分からないですからね。。 ということで内容は大したことないのですが、以下 Lambda による実装例です。幸い Boto3 にそのものズバリなメソッドが揃っていたため、それらを順次実行した結果を S3 に出力しているだけの簡単仕様ですが、少しだけ補足です。 get_introspection_schema() と list_types() が返す情報はほぼ同義ですが(スキーマ設定により各 type が定義されるので)、一応両方出力するようにしています。 前者は JSON ではなく SDL で出力しているので、streamingBody を read() したものをそのまま出力しています。 list_resolvers() の引数として、リゾルバを実装した type 名を指定する必要があります。Amplify を使用していることもあり、graphql のクエリ言語に対応するものをそのまま直指定しています。 また、出力結果はフォルダごと Winmerge などの diff ツールで比較するような使い方をしていますが、functionID 絡みで比較に支障があった部分については以下のように変換、ソート処理を入れています。 list_functions() の結果がどうやら functionId でソートされて返ってくるようなのですが、ID は当然ながら環境間で異なるためこのままだと有意な比較ができません。よって、 name(関数名)でソートし直したものを出力しています。 リゾルバ内の関数実行定義も functionId による表現となっているため、functionId から name を導出できるような dict を用意の上、定義内容を置換しています。 他にも、マッピングテンプレートや types における definition の出力結果を整形するなど、もうちょっと頑張りどころはありそうなんですが、それらの内容で差分が生じてもそこまで比較に支障が出なさそうだったので、一旦そのまま出力してしまっています。 import json import datetime import boto3 import botocore BUCKET_NAME = '<BUCKET_NAME>' API_ID = '<APPSYNC_API_ID>' appsync = boto3.client('appsync') s3 = boto3.resource('s3') bucket = s3.Bucket(BUCKET_NAME) def put_json_to_S3(obj_key, json_data):   obj = bucket.Object(obj_key)   obj.put(Body = json.dumps(json_data, indent=4, sort_keys=True, separators=(',', ': '))) def lambda_handler(event, context):   obj_key_prefix = f'appsync_setting_dumps/{datetime.datetime.now().strftime("%Y%m%d_%H%M%S")}'   # 情報取得(functions)   response = appsync.list_functions(       apiId=API_ID   )   # functionの名前でソート   sorted_func_json = sorted(response['functions'], key=lambda x: x['name'])   put_json_to_S3(f'{obj_key_prefix}/functions.json', sorted_func_json)   # 情報取得(resolvers)   id_name_dict = {item['functionId']: item['name'] for item in sorted_func_json}   for typename in ['Query', 'Mutation', 'Subscription']:       response = appsync.list_resolvers(           apiId=API_ID,           typeName=typename       )       # functions実行定義をIDから名前に変換       converted_resolvers_json = response['resolvers']       for item in converted_resolvers_json:           pipeline_config = item["pipelineConfig"]           if "functions" in pipeline_config:               pipeline_config["functions"] = [id_name_dict[id] for id in pipeline_config["functions"]]       put_json_to_S3(f'{obj_key_prefix}/resolvers_{typename}.json', converted_resolvers_json)     # 情報取得(types)   response = appsync.list_types(       apiId=API_ID,       format='JSON'   )   put_json_to_S3(f'{obj_key_prefix}/types.json', response['types'])     # 情報取得(schema)   response = appsync.get_introspection_schema(       apiId=API_ID,       format='SDL'   )   obj = bucket.Object(f'{obj_key_prefix}/introspection_schema.json')   obj.put(Body = response['schema'].read()) なお、この方向性で AppSync リソースの作成や更新も自動化すればいいじゃんというセルフツッコミが成立しそうなのですが、車輪の再発明感がものすごかったので今回はやめました。もっと対象のリソース数が多かったりしたら検討したかもしれませんが、それよりはお客さんに Amplify の使用許可を貰えるよう交渉を頑張るとか、Amplifyをやめて自前でリゾルバを実装するようにした方が色んな意味で健全かなと思います。。。   余談 Amplify の自動生成が便利とはいえ手動移行に手間をかけるより、自分でリゾルバを書けば良いんじゃないの?と疑問を持たれた方もいるかと思うのですが、その考え自体は正しいと思います。何故それでも Amplify を採用したのかというと、単純に期間や工数との兼ね合いからでした。AppSync を本格的に扱うのが初めてだったということもあるのですが、リゾルバの作成を未経験の言語(VTL or AppSync JS)で頑張るのはちょっと現実的ではなかったです。特に VTL は正直構文とか見てもあまり好きになれず・・ データソースが全て Lambda になるようなアプリケーションであれば、AppSync をやめて API Gateway + Lambda という構成でもよかったのですが、その場合はアプリケーションで使用するDBをどのように用意するか、というのがネックでした。なるべくサーバレスのサービスで構成しようとすると結局一番筋が良さそうなのは DynamoDB になりますが、それなら AppSync 及び Amplify と組み合わせて使った方が筋が良いのではないかと。Aurora Serverless も選択肢ではありましたが、さすがに今回の用途だとオーバースペックでしたし。後は、別エントリで説明した通り AppSync の Subscription をアプリケーション側で活用することを念頭に置いていたという要因も大きいです。   まとめ 本来であればこのような対応を取らないに越したことはないのですが、手動移行したことによりリゾルバや関数の仕組みや実装について理解が深まったことも確かなので、トータルでは良かったのかもしれません。現在もアプリケーションの開発は継続しており、ちょくちょく弊社環境からお客さん環境に AppSync 更新分を移行する機会はあるのですが、これらの取り組みにより移行自体にかかる工数は大分削減できるようになりました。ただ、もし別の案件で同じような対応を迫られた場合は、もっとラクなやり方を検討したいところではあります。。 本記事がどなたかの役に立てば幸いです。
企業ネットワークにおけるクラウドサービス利用のリスク対策として、Cloud Access Security Broker(CASB)の導入が増えています。 CASBは「可視化(Visibility)」「アセスメント(Assessment)」「強制(Enforcement)」「防御(Protection)」の4つのプロセスで説明されます。このうち「強制」では、クラウドサービスの利用許可・拒否等のルールを設定することとなりますが、具体的にどういったルールにするべきか、ご相談をいただくことが多いです。 そこでこの記事では、 実際に設定されることの多いCASBルールの例をご紹介します。 設定はSASEソリューションである「Catoクラウド」のCASB機能に沿ってご紹介しますが、他のCASB製品においてもルール検討のご参考にしていただけましたら幸いです。 なお、CatoクラウドのCASB機能については、以下の記事もご参照ください。 CatoクラウドのCASBについて Catoクラウドのセキュリティオプション CASB について解説します。 blog.usize-tech.com 2023.09.12 Catoクラウドでアプリケーションを制御するには CASB機能のApplication Controlの設定方法や実際にどのような制限ができるのかについて解説していきたいと思います。 blog.usize-tech.com 2023.11.02 CASBではどんな制御ができる? CASBでの制御は、次世代ファイアウォール(L7FW、NGFW)でのアクセス制御とは少し違います。 次世代ファイアウォールでの制御 アプリケーション識別をして、そのアプリケーションへの通信を許可・拒否することができる。 例) X(旧Twitter)へのアクセスを拒否する。閲覧も投稿もできない。 CASBでの制御 アプリケーション識別をして、そのアプリケーションの 特定の 操作を指定して許可・拒否 することができる。 例) X(旧Twitter)の閲覧は許可するが、投稿のみ禁止することができる。 このような違いがあるため、CASBのルール作成においては、許可・拒否するアプリケーションに加えどの操作を許可・拒否するかを検討する必要があって、検討が難しく感じられるのではないかと思います。 なお、Catoクラウドにおいては、CASBはオプション機能となっています。CASBオプションを追加することで、クラウドサービスに対する操作など、詳細な情報が取得できるようになります。 CASBでのよくある制御ルール例 では、CASBを導入されている企業ではどのような制御をされているのでしょうか。SCSKで実際にご相談を受けたり、設定をご案内する中で多い例をご紹介します。 クラウドサービスでの個人アカウント利用の制限 会社で利用するクラウドサービス、具体的にはMicrosoftのM365やGoogle Workspaceについて、 会社のメールアドレスでのログインを許可するが、その他のメールアドレスでのログインを拒否する という設定です。 この設定により、会社の端末から個人のGoogleDriveに業務情報をアップロードするといった操作を抑止できます。 また、同様にChatGPTへの社内情報のアップロードを抑止する目的で、OpenAIへのログインを制御しているご利用例もあります。 Catoクラウドでの設定例 以下は、CatoクラウドのCASB機能であるApplication Controlでの設定例です。それぞれ上の行で会社アドレスでのログインを許可し、下の行でその他のすべてのログインを拒否する設定です。 なお、Catoクラウドではこの他にTenant Restriction(旧名称:Header Injection)機能を利用し、特定のサービスへの通信時にHTTPヘッダを挿入することが可能です。これにより、クラウドサービス側で、アクセス可能なアカウントやテナントを制限するなどの動作が可能です。詳細は下記記事をご参照ください。 Catoでやる「Microsoft 365」のテナント制御~「Header Injection」機能の紹介~ Catoの「Header Injection」機能を使って「Microsoft365」のテナント制御を実施する方法を紹介します。 blog.usize-tech.com 2024.07.12 ファイル共有サービスへのアップロードの制限 Dropbox、boxなどといった、 ファイル共有サービスやオンラインストレージに対し、アップロードのみを拒否する 設定です。 これらのサービスは、取引先等からのファイル送付に利用されることもあるため、アクセス自体を拒否してしまうとやりとりに支障が出てしまいます。このため、以下のようなルールにされる場合が多いです。 自社で契約しているファイル共有サービスは全アクションを許可 それ以外のファイル共有・オンラインストレージは、アップロードのみを拒否 (閲覧・ダウンロードは可能) Catoクラウドでの設定例 Custom Categoriesにて、アップロード拒否対象とするカテゴリを作成し、 Application Controlにて、Dropboxのみ全アクションを許可、その他Custom Categoriesに対するUploadのみを拒否とします。 ※2025年1月現在の仕様で、Application Categoriesに対してはActivitiesが指定できないため、Custom Categoriesを作成しています。 SNS投稿の制限 XやInstagram等の SNSに対し、投稿のみを拒否する 設定です。 情報収集のため閲覧は許可したいが、リスク管理上、社内からの投稿はできないようにしたいという場合に有効です。 Catoクラウドでの設定例 例として、広報担当のみは投稿含めすべての操作を許可し、その他ユーザは投稿を拒否する設定です。 リスクが高いクラウドサービスの利用制限 世の中にはクラウドサービスが無数にあり、利用者がその一つ一つの安全性を判断したり、ルールを設定するのは現実的に不可能です。 この問題への解決策として、各CASBではクラウドサービスに対し独自のリスク判定を行っており、このリスク判定をもとに、利用者がルールを設定することが可能です。 Catoクラウドでの設定例 Catoクラウドでは、クラウドサービスの認定取得やセキュリティ対策状況をもとに、1(低)~10(高)のリスクスコアをつけています。例として、Gmailのリスクスコアは「4」です(2025年1月時点)。 このリスクスコアを元に、 スコア8以上のクラウドサービスはアクセスを拒否する などの設定を行うことで、セキュリティリスクが高いと判定されたサービスの利用を制限することができます。 すべてのクラウドサービスへのアップロードをログに記録 クラウドサービスごとのCASBルールを設定した上で、その他すべてのクラウドサービスに対し、 ファイルのアップロード動作をログに残す設定 です。 万が一情報の持ち出し等があった場合に、ログから追えるようにしたいという目的です。 Catoクラウドでの設定例 すべてのクラウドサービスへのUploadを、許可はするが、Event(ログ)に残すという内容です。 補足 CatoクラウドのCASB機能では、ルールを全体にかけるだけでなく、ユーザやグループを指定して設定することもできるので、例えば個人情報を扱う部署にはより厳しいルールを適用したり、特定のユーザはルールの対象外にしたりといった、細かな設定が可能です。 また、設定例の中にもあるように、指定の動作について、拒否ではなく「許可するがログに残す」「許可するが管理者にメール通知する」といった指定も可能です。 拒否ルールを急に設定すると業務に支障が出る場合もあることから、 まずは「ログに残す」設定でルール適用し、その後ログ状況を見て拒否に変えていく運用がおすすめ です。 最後に 本記事では、実際にCASBで設定されることの多いルールをご紹介しました。CASB導入時のルール検討にご活用いただけますと幸いです。 自社環境に合ったCASB制御を行うには、クラウドサービスの実際の利用状況を定期的に確認し、自社のポリシーと照らし合わせてルールをブラッシュアップしていく必要があります。「可視化(Visibility)」「アセスメント(Assessment)」「強制(Enforcement)」「防御(Protection)」のサイクルの運用です。 SCSKでは、運用経験豊かなエンジニアが多数在籍しており、お客様にあわせたご支援が可能です。お困りごとなどございましたら、ぜひご相談ください。
Amazon Athena と AWS Glue を使用して、Amazon S3 上にある CSV データに対してクエリを実行してみたので、その手順をまとめます。 AWS Glue と は AWS Glueは、Amazon Web Servicesが提供するフルマネージドのETL(抽出、変換、ロード)サービスです。このサービスは、大量のデータを効率的に準備し、変換し、異なるデータストア間で移動させることを目的としています。 Glueを利用するにあたり、登場する用語について簡単に説明します。 ETL(Extract, Transform, Load): データを抽出(Extract)し、変換(Transform)し、ロード(Load)するプロセスを指します。AWS GlueはこのETLプロセスを自動化および簡素化するためのサービスです。 AWS Glue Data Catalog: AWS Glue Data Catalogは、メタデータを管理するための中心的なサービスです。データストアのスキーマ情報、データの場所、その他のメタデータを保持し、データの整理と管理をサポートします。 AWS Glue データベース: AWS Glueデータベースは、Data Catalog内でテーブルをグループ化するための論理的なコンテナです。データベースはスキーマを含んでおり、データの整理とスキーマの一貫性を保つために使用されます。 AWS Glue テーブル: AWS Glueテーブルは、データとその構造に関するメタデータを表します。通常は特定のデータセットのスキーマ情報を含み、データドリブンなアプリケーションで使用されます。 クローラー: クローラーは、自動的にデータソースを探索し、データカタログにメタデータを登録するためのコンポーネントです。クローラーはデータの構造を解析し、テーブル定義を生成または更新します。 AWS Glue ジョブ: AWS Glueジョブは、ETL(抽出、変換、ロード)プロセスを実行するための基本単位です。ジョブを作成して、データを変換し、必要な出力先にロードすることができます。 AWS Glue 接続: AWS Glue接続は、Glueがデータストアにアクセスする際の接続情報を保存します。ホスト名、ポート番号、認証情報などが含まれ、データアクセスを安全かつ効率的に行えます。 Amazon Athena とは Amazon Athenaは、Amazon Web Services(AWS)が提供するサーバーレスのインタラクティブクエリサービスです。Athenaを使用すると、Amazon S3に保存されているデータに対して直接SQLクエリを実行し、複雑なETLプロセスを必要とせずにデータを分析できます。 Amazon Athenaには、以下の特徴があります。: サーバーレス: Athenaは完全にサーバーレスであるため、インフラストラクチャの管理が不要です。ユーザーはクエリの実行に対してのみ料金を支払います。 SQLサポート: Athenaは標準SQLを使用してクエリを記述できるため、SQLに精通したユーザーが簡単に利用できます。 データソースのサポート: 主にAmazon S3に保存されたデータに対してクエリを実行しますが、Athenaを他のデータソースに接続することも可能です。 使いやすさ: ノードやクラスタの管理が不要で、クエリを書くだけでデータ分析を始められます。 統合機能: Amazon QuickSightなどの他のAWSサービスと統合しやすく、ダッシュボードを通じたビジュアル分析が可能です。 データ形式のサポート: AthenaはCSV、JSON、ORC、Avro、Parquetなど、さまざまなデータ形式に対応しています。 これらの特徴により、Amazon Athenaはデータアナリストやデータサイエンティストにとって、スピーディーで効果的なデータ分析ツールとなっています。 やってみた AWS Glue 1.データベースの作成 まずはクローラの出力先となるデータベースを作成します。 ここでは名前のみ指定しました。 2.クローラーの作成 クローラーの名前を入力します。   「Add a data source」からデータソースを指定します。 今回はS3のデータを利用したいので、データを配置したS3のパスを指定します。 また、「Subsequent crawler runs」では、クローラーの再実行時にどのフォルダをクロールするかを選択できます。 今回は、全て再クロールする「Crawl all folders」を選択しました。   データソースを指定できたら「Next」へ進みます。 IAMロールを選択します。 今回は「AWSGlueServiceRole」およびソースデータを配置したS3へのアクセス権限を付与しました。 クローラの出力先を指定します。 「1.データベースの作成」で事前に作成したデータベースを指定します。 設定に問題がなければ「Create crawler」をクリックします。 以上でクローラの作成は完了です!   3.クローラの実行 早速クローラを実行してみます。 作成したクローラの詳細画面から「Run crawler」クリックします。 出力先に設定したデータベースを確認すると、クローラによりS3上のデータが自動で読み取られ、構造化されたインデックスがカタログ(Glue Data Catalog)として保存されています。 Amazon Athena クエリの実行 Athenaでクエリを実行してみます。 Athenaでのクエリ実行時に、データベースにGlue Data Catalogのデータベースを指定することで、データベース内のテーブルやテーブル内の項目名を使うことができます。 データソース欄で「AWSDataCatalog」を指定すると、データベース欄からGlueで作成したデータカタログ内のデータベースが選択できます。   クエリを実行してみました。 select文を実行して、テーブル内の項目を抽出することができました! さいごに S3のデータをGlueでデータベースに格納して、Athena で必要なデータを取得するまでの手順をまとめました。Glueを利用することで簡単にS3上のデータを構造化し、容易に必要な値を取得することができました。 次回はAthenaのデータをQuickSightに連携し、可視化してみたいと思います。
こんにちは。ひるたんぬです。 本年もよろしくお願いいたします。今年は巳年ですね。 小さい頃に「十二支は神様の号令の下、競争して早い順に決まった」と言われた記憶があるのですが、実際に本当に早い順に十二支を並べるとどのようになるのか、ふと気になってしまいました。 調べたところ、すでにこの疑問に対して調査をしてくださっていた記事がありましたので一部を抜粋してご紹介します。 本やネットに速度が出ているものは、それを採用し、それ以外のものは、筆者の経験その他から強引に算出するなどした結果、13匹の動物たちの速度は以下のようになった。 子=鼠:時速10km 丑=牛:時速4km(注1) 寅=虎:時速64km 卯=兎:時速72km 辰=竜:時速360km以上(注2) 巳=蛇:時速16km(注3) 午=馬:時速68km(注4) 未=羊:時速13km(注5) 申=猿:時速30km(注6) 酉=鶏:時速18km(注7) 戌=犬:時速36km(注8) 亥=猪:時速45km 猫:時速48km(注9) 引用:Yahoo! ニュース「 「十二支」の順番がナットクできない。実際の動物のスピードを考えると、どんな順番になるか? 」 上記から、令和版の十二支は「辰卯午寅猫亥戌申酉巳未子」となるようですね。竜が圧勝のようです。 戦略を練って二位に漕ぎ着けていた牛は、スタメンから外れてしまいました。 また、これを元に考えると、今年は亥年ということになりますね。 …さて、今回はIaCというものを少し使いこなせるようになって調子に乗っていた結果、大きな落とし穴にはまってしまったので、自戒の意味も込めてご紹介いたします。 やりたいこと とあるログをEventBridgeを経由してCloudWatch Logsに送ろう!というよくあるものです。 せっかくなのでマネジメントコンソールからではなく、IaC(CloudFormation)を使用して構築しよう!ということになりました。 やったこと マネジメントコンソールではEventBridgeのルール作成とCloudWatch Logsのロググループ作成のみで実施できたので、同じようにCloudFormationを書いてみました。今回は検証のため、EC2に関する任意のイベントをCloudWatch Logsのロググループへ転送することにします。 AWSTemplateFormatVersion: '2010-09-09' Resources: RuleEC2Activity: Type: AWS::Events::Rule Properties: EventPattern: { "source": ["aws.ec2"] } Targets: - Arn: !GetAtt LogEC2Activity.Arn Id: LogGroup LogEC2Activity: Type: AWS::Logs::LogGroup Properties: RetentionInDays: 14 LogGroupName: /aws/events/EC2Activity 上記のファイルのデプロイは問題なく完了しました。 起きたこと ログへ転送がされるかを確認するために、EC2インスタンスを立ち上げました。しかし、待てど暮らせどログは出力されません。 おかしいな…と思い、EventBridgeの当該ルールのモニタリングをチェックすることにしました。すると… EventBridgeのルール自体は動いていたのですが、そのすべての実行が失敗(FailedInvocationsに記録)していたのです。 原因調査 こんなときに役に立つのは、インターネットに広がる知識の海です。調査してみたところ、公式ドキュメントに気になる記載がありました。 CloudWatch Logs がルールのターゲットである場合、EventBridge がログストリームを作成し、CloudWatch Logs がログエントリとしてイベントからテキストを保存します。EventBridge がログストリームを作成してイベントを記録するためには、EventBridge が CloudWatch Logs に書き込むことを可能にするリソースベースポリシーを CloudWatch Logs に含める必要があります。 AWS Management Console を使用して、CloudWatch Logs をルールのターゲットとして追加する場合、リソースベースのポリシーは自動的に作成されます。AWS CLI を使用してターゲットを追加し、ポリシーがまだ存在しない場合は、作成する必要があります。 引用:AWS「 Amazon EventBridge ユーザーガイド:Amazon EventBridge のリソースベースのポリシーを使用する 」 今までの私は前者(マネジメントコンソールでの操作)しか行ったことがなかったので気づかなかったのですが、実はリソースベースポリシーの設定が必要なようですね。言われてみれば、権限も設定していないのに書き込めるのは不思議ですもんね。。。 解決に向けて… リソースベースポリシーをIaCにて設定 後述する参考記事によると、CloudFormationでリソースベースポリシーを設定できるようなので、まずはこちらを試してみます。 下記のテンプレートファイルを作成し、新規でスタックを作成してデプロイします。 PolicyDocument内のアカウントIDやロググループ名などは適宜修正してください。 AWSTemplateFormatVersion: '2010-09-09' Resources: EventBridgeToCWLPolicy: Type: AWS::Logs::ResourcePolicy Properties: PolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"TrustEventsToStoreLogEvent\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"delivery.logs.amazonaws.com\",\"events.amazonaws.com\"]},\"Action\":[\"logs:CreateLogStream\",\"logs:PutLogEvents\"],\"Resource\":\"arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/events/*:*\"}]}" PolicyName: TrustEventsToStoreLogEvents これにより、正常にログが転送されました! また、このスタックを削除することによりポリシーがなくなり、ログ転送ができなくなる点も確認できました。 リソースベースポリシーをマネジメントコンソールにて設定 では、マネジメントコンソールでルールを作成した場合はどうなるのでしょうか。 リソースベースポリシーが裏で設定されているとのことだったので、その挙動を中心に確認してみます。 ルールを作成した後にCloudShellにてリソースベースポリシーの有無を確認してみると… 先ほどIaCにて設定したポリシーと同様のものが付与されていました。 知らないところで動かせるようにしてくれていたのですね。 …ここで気になった点がありました。 「このマネジメントコンソールで作ったルールを消したらリソースベースポリシーはどうなっちゃうの問題」です。 実際にやってみる他ありませんね!やってみました。 …残っていました。そのため、 マネジメントコンソールからEventBridgeのルールを一度でも作成している場合、このブログのような症状には出会わない可能性もある ということですね。 おわりに 改めて、AWSの権限に対する考え方を痛感する良いきっかけとなりました。 何事にも許可がいる…現実でもクラウド上でもそんな認識でいるのが大事なのかなと思います。 また、今回の記事は以下を参考に作成いたしました。Web上の皆さまの記事にはいつも助けられてばかりです。。 CloudWatch LogsをターゲットとするEventBridgeルールをCloudFormationで作成するには | DevelopersIO dev.classmethod.jp 上記サイトにも記載がありましたが、このリソースベースポリシーはマネジメントコンソール上から確認することができないようです。(CLIを用いて確認する) あまり需要はないかもしれませんが、これも気軽に確認できるようになるとより良いのかな…と思った次第です。 余談ですが、十二支の昔話はYouTubeでも観れるのですね。
みなさん、Amazon Redshift Serverlessを使ったことはありますか? プロビジョンドタイプと異なり、サーバレスの強みを生かした高可用性、高スケーラビリティ、コストメリット有りと使いやすさ抜群のサーバレスタイプですが、 サーバレス特有のコンピューティングキャパシティの考え方を理解すると、よりメリットを享受することが出来そうです。 今回自分が携わったプロジェクトでRedshift Serverlessを構築したので、その際に分かったこと・試したことを記事にまとめてみました。 ※最新情報についてはAWS公式ドキュメントを参照ください。 Redshiftプロビジョンドタイプとサーバレスタイプの違い まずRedshiftとは、 フルマネージド型のデータウェアハウスサービス です。 高速なリアルタイム分析が得意で、BIツールと連携も可能なため、企業の意思決定に大いに役立つサービスです。 プロビジョンドタイプはクラスタの管理が必要で、クラスタのサイズを手動で設定する必要がありますが、サーバレスタイプは ワークロードに応じて自動でスケーリング します。 後に説明する「ベース容量」から「最大容量」までの範囲で自動でスケーリングがおこなわれるため、コスト効率化、運用負荷削減に有効です。 コンピューティングキャパシティの考え方 Redshift Serverlessでは、コンピューティングキャパシティを RPU(Redshift Processing Unit) という単位で設定します。 1つのRPUは16GBのメモリを提供します。 設定箇所としては「ベース容量」、オプションで「最大容量」、「使用制限」があるので、それぞれワークロードのクエリパフォーマンスや目標コストに応じてRPUを設定していきます。 ベース容量(設定はマスト) クエリの処理に使用するコンピューティングリソースの基本容量をここで設定します。 デフォルト値は128RPUで、8RPU から512RPU まで8単位で調整可能です。 ベース容量を大きく設定することで、クエリのパフォーマンスが向上します。 最大容量(設定はオプション) Redshiftが スケールアップできるRPUの上限 をここで設定します。 クエリ実行開始時は「ベース容量」で設定したRPUで処理が実行されますが、クエリの負荷に応じてこの「最大容量」まで自動でスケーリングされます。 使用制限(設定はオプション) Redshiftの 使用を制限したい場合 はここで設定します。 頻度(毎日/毎週/毎月)、制限RPU数、アクション(ログ出力/アラート/クエリ無効)が設定できます。 Redshiftはクエリの実行に使用されたRPU時間に応じて課金されるため、これ以上コストをかけたくない、という目安がある場合には設定がお勧めです。 ベース容量と最大容量の決め方 ベース容量と最大容量はどのように定めるのが良いでしょうか? また設定後に「容量を変更すべき」という判断の基準をどのように持っておけば良いのでしょうか。 ここからは自分が実際に試してみた設定値決めの方法をご紹介します。 ざっくりとRPU設定値を決める RPUは基本的には、 「データ量」 と 「クエリの複雑さ、頻度」 で考えていきます。 仮で、以下要件の場合のベース容量を考えてみます。 ・Redshiftでの分析の元となるデータ量は3TB程度。 ・分析のために実行するクエリは集計などのSQL文。実行時間は平日の勤務時間内。 「データ量」で考えると、公式ドキュメントで「8、16、24RPU の基本RPU容量は、128TB 未満のデータが必要なワークロードを対象としています。」との記載があり、今回の対象データは3TBであることから、8~24RPUを選択の候補とします。 「クエリの複雑さ、頻度」で考えると、正直どの程度データ分析でRedshiftを使用するかによるため、一旦16RPUを選択し、検証の中で設定値を調整していく形とします。 検証で設定値をチューニングする 上記の形で一旦設定したRPUが適切かどうかを実際の利用シーンを想定してクエリを実行してみることで、設定値の妥当性を判断します。 RPU値をチューニングする時には、以下2点をポイントとしてチェックすると良いです。 ComputeCapacityメトリクス クエリ実行すると様々なメトリクスが取得できますが、その中の[ComputeCapacity]メトリクスをCloudWatchで確認することで、クエリ実行時に使用されているRPUが分かります。 メトリクスの値がベース容量のRPUから最大容量のRPUにスケールアップし、最大容量に近い時間が多い場合は「ベース容量が足りていない」と判断する材料になります。 ただし、クエリについては単一のクエリからの負荷増加に応じたRPUスケールアップはおこなわれず、 同時実行クエリのみがスケールアップの対象 であることを念頭においておくことが必要です。 クエリ実行時間 以下の方法でクエリ実行時間を確認し、 想定内の実行時間であるか を評価する方法も有効です。                SYS_QUERY_HISTORYビュー Redshift内でSQLを実行しビューを表示させ、クエリ実行に関する詳細情報を確認する方法。 例えば「elapsed_time」を表示させると、クエリの実行時に消費した合計時間がわかります。 elapsed_time :クエリの実行が消費した合計時間(マイクロ秒)。           監査ログ 監査ログを有効にし、クエリ実行時間に関する情報を確認する方法。 QueryDuration :クエリを完了するまでの平均時間(マイクロ秒)。 QueryRuntimeBreakdown :クエリステージごとの、クエリが実行された合計時間(ミリ秒)。   まとめ コンピューティングキャパシティの考え方を理解して、パフォーマンス的にもコスト的にも有効に使用できると良いですね。 最後まで読んでいただきありがとうございました! この記事が皆さんのお役に立てれば幸いです。
こんにちは、2024年度入社の秋葉です! 2025年の1月14日を持ちまして、約3ヶ月のクラウド人材研修が終了いたしました。 クラウドは利用しているけど、”クラウド人材研修”って一体何?と疑問に思っているあなたに向けてクラウド人材研修とは何か、その具体的な内容、そして研修によって何を得ることができたのか記載していきます! クラウド人材研修の背景・目的 近年の急速なデジタル化は、市場や社会の変化を激化させており、顧客のDX推進を支援するためには、クラウドネイティブな知識とアジャイルな開発スタイルを備えた、即戦力となる人材が不可欠です。 しかし、経験豊富なクラウドエンジニアは不足しており、育成が急務となっています。特に、アプリケーション開発とインフラ運用、双方の知識とスキルを兼ね備えた「 フルスタック人材 」の育成は喫緊の課題です。 この背景から、SCシステム事業本部では新人育成プログラムとしてクラウド人材研修を実施しています。本研修の目的は、将来的なフルスタック人材育成を視野に入れ、クラウド基盤技術の習得、アジャイル開発手法の実践を通じて、クラウドネイティブな開発スタイルを早期に経験させることです。 技術スタック フルスタックを目的とした技術スタックはAWSを基本に以下のように構成されていました! Javaアプリケーション SpringBoot IaC クラウド(AWS CloudFormation)                                   コンテナ(Docker/Amazon ECR/Amazon ECS) CI / CD イミュータブルインフラストラクチャ ビルド/デプロイ自動化(AWS CodePipeline) ステートレス(Amazon ElastiCache) 負荷分散 / 冗長化 ロードバランサー(Elastic Load Balancing ALB) 監視 / 通知 監視(Amazon CloudWatch Alarm) 通知(Amazon SNS) 研修内容 CNCF Cloud Native Trail Map をベースに構成したLesson0~Lesson9までの全10レッスンのカリキュラムで、クラウドネイティブの基礎から応用までを網羅的に学習してきました! 各Lessonのテーマは以下のようになっています。研修は、2人1チームの計8チームで進められておりチーム内だけでなく各チーム間で協力して研修に取り組んだことで全チームが期間内にすべてのLessonを終えることができました! Lesson Theme Lesson 0 Preparation Lesson 1                  Introduction Lesson 2                  IaC Level 1 Lesson 3                  IaC Level 2 Lesson 4                  Redundancy Lesson 5 Containerization and Orchestration Lesson 6                      CI/CD Lesson 7                  Observability Lesson 8          Operations Automation Lesson 9               Disaster Recovery 全Lessonが終了すると以下のような構成図になります。 この一連のレッスンでは、Spring Bootを用いたWebアプリケーションをAWSクラウド上で構築し、CI/CDパイプラインによる 自動デプロイ と 監視機能 を備えた、 堅牢なシステム の実現を目指します。 Lesson0では、Spring Boot・AWS・Gitなど開発に必要なツールの使い方や学習から始まり、最後の Lesson 9 では、災害などに備えて、システムをより堅牢にするための仕組みを作りました。具体的には、Lesson9ではDNS Failoverを実現することで東京リージョンで障害が発生した場合に備え、Webアプリケーションを自動的に別のリージョン(バージニア北部)に切り替えて、静的なエラー画面を表示する仕組みを構築しました。 このように、この研修を通して、Webアプリケーションの 開発 から 運用 、そして 災害対策 まで、一連の流れを学ぶことができたと思います! 研修を終えた新人たちの声 Kさん 当たり前のことだがコミュニケーションを密に取ることが大切だということを再実感した。また、問題が発生した際に原因の切り分けを行いながらトラブルシューティングを行うスキルが身に染み付いたと思っているので今後の業務に活かしていきたい! Sさん 権限の管理に関して、必要最低限のポリシーをアタッチすることが簡単なようでとても難しかった。必要以上のポリシーを付与してついつい楽をしがちだが、不要なポリシーが無いか確認し同じロールを使いまわさないことを意識して業務に臨みたい。 Tさん エラー発生時に画面共有しながら、ペアプログラミングできた経験はすごく良かったと感じた。今後の案件では研修を通して身に着けた、明確な作業内容報告の実施をチームメンバーに対して心がけていきたい! Oさん   研修を通して一番忍耐力が身についたと思いました。開発中1つエラーを解決したら別のところでまたエラー、エラー三昧になってしまうこともあったけどそれに負けず最後までやり遂げれました。今後の業務にもエラー三味に負けない心で挑みたい!     終わりに 3ヶ月間のクラウド人材研修は、私にとって大きな経験であり、同時に大きな成長の機会となりました。右も左も分からない状態からスタートしましたが、CNCF Cloud Native Trail Mapをベースとした体系的なカリキュラム、そしてチームメンバーや講師の方々のサポートのおかげで、クラウドネイティブ開発の基礎をしっかりと築くことができました。 研修を通して特に印象に残っているのは、実践的な課題を通じて、座学だけでは得られないリアルな経験を積むことができた点です。エラーに遭遇した時の原因究明やトラブルシューティング、チームでの協力作業など、実際の開発現場で直面するであろう課題に取り組むことで、問題解決能力やコミュニケーション能力を向上させることができました。また、Kさんが述べているように、チームメンバーと密にコミュニケーションを取りながら課題に取り組む重要性を改めて認識しました。 最後に、この研修に関わってくださった全ての方々に感謝申し上げます。ありがとうございました!
AWS System Manager (SSM) の Patch Manager 機能を利用すると、OS パッチ適用を自動化できるそうです。 すごく便利だと思ったので、設定手順をまとめてみました。   アーキテクチャ 今回は、下図の構成で実装してみました。   Systems Managerとは、 オンプレミスでもマルチクラウド環境でも、AWS 内のノードを広範囲にわたって一元的に表示、管理、および運用できます。統合コンソールエクスペリエンスのリリースにより、Systems Manager では AWS アカウントとリージョン全体にわたってよく使用するノードタスクを完了できるさまざまなツールが統合されています。 AWS Systems Manager とは – AWS Systems Manager   Patch Managerとは、 オペレーティングシステムとアプリケーションの両方にパッチを適用することができます。 AWS Systems Manager Patch Manager – AWS Systems Manager   設定手順 今回は、以下手順でOS「Windows2022」のEC2に、毎月第4月曜日の9:00にパッチ適用する手順をまとめました。 1.パッチベースラインの作成 2.パッチグループの作成 3.メンテナンスウィンドウの作成 4.ターゲットの登録 5.Run Commandタスクの登録   1.パッチベースラインの作成 AWS System Managerの左メニュー「パッチマネージャー」をクリックし、「パッチベースライン」>「パッチベースラインを作成する」をクリックします。   ▼ パッチベースラインの詳細 「名前」欄は、任意のパッチベースライン名を入力します。 「オペレーティングシステム」欄は、パッチ適用対象のインスタンスのOSを選択します。 今回は、windowsを選択しました。   ▼ オペレーティングシステムの承認ルール 「製品」欄は、パッチ適用対象のインスタンスのOSイメージを選択します。 「分類」欄は「CriticalUpdates」、「SecurityUpdates」を選択します。 「重要度」欄は「Critical」、「Important」を選択します。 「コンプライアンスレポート」欄は、「重大」を選択します。 そのほかはデフォルトのままにします。   以下の欄はデフォルトのままにし、「パッチベースラインの作成」をクリックします。 ▼アプリケーションの承認ルール ▼パッチの例外 ▼タグの管理   2.パッチグループの作成 作成したバッチペースラインのIDをクリックし、「アクション」>「パッチグループの変更」をクリックします。   「バッチグループ」欄に任意のパッチグループ名を入力し、「追加」をクリックします。 入力したパッチグループ名が追加されたことを確認後、「閉じる」をクリックします。   3.メンテナンスウィンドウの作成 AWS System Managerの左メニュー「メンテナンスウィンドウ」をクリックし、「メンテナンスウィンドウの作成」をクリックします。   ▼ メンテナンスウィンドウの詳細の入力 「名前」欄は、任意のメンテナンスウィンドウ名を入力します。 そのほかはデフォルトのままにします。   ▼ スケジュール 「次で指定」欄は、「CRON/Rate 式」を選択し、実行させる時間帯をcronで記載します。 ※今回は第4月曜日の9時に実行させたいので、cron(00 09 ? * MON#4 *)で登録してみます。 「期間」欄は、「3」時間を選択します。 「タスクの開始を停止する」欄は、「0」を選択します。 「スケジュールのタイムゾーン」欄は、任意のタイムゾーンを選択します。 ※今回は「(GMT+09:00) Asia/Tokyo」を選択しました。 そのほかはデフォルトのままにします。   ▼ スケジュール デフォルトのままにし、「メンテナンスウィンドウの作成」をクリックします。   4.ターゲットの登録 作成したメンテナンスウィンドウのIDをクリックし、「アクション」>「ターゲットの登録」をクリックします。   ▼ Maintenance window target details 「Target name」欄は、任意のターゲット名を入力します。 そのほかはデフォルトのままにします。   ▼ ターゲット 「ターゲットの選択」欄は、「インスタンスタグを指定」を選択します。 「インスタンスタグを指定」欄は、パッチ適用対象のインスタンスにアタッチしている任意のタグを入力し、「Add」をクリックします。 入力したタグが追加されたことを確認後、「Register target」をクリックします。   5.Run Commandタスクの登録 作成したメンテナンスウィンドウのIDをクリックし、「アクション」>「Run Commandタスクの登録」をクリックします。   ▼ メンテナンスウィンドウタスクの詳細 「名前」欄は、任意のメンテナンスウィンドウタスク名を入力します。 そのほかはデフォルトのままにします。   ▼ コマンドドキュメント 「ドキュメント」欄は、「AWS-RunPatchBaseline」を選択します。 そのほかはデフォルトのままにします。   ▼ ターゲット 「登録済みターゲットグループの選択」を選択し、「4.ターゲットの登録」で設定したターゲットを選択します。   ▼ レート制御 「同時実行数」欄は、「1」ターゲットを選択します。 「エラーのしきい値」欄は、「1」エラーを選択します。 ▼ IAM サービスロール パッチ適用用のIAMロールを選択します。   ▼ 出力オプション 「CloudWatch output」を選択し、任意のロググループ名を入力します。 ※パッチ適用の実行結果がCloudWatch logsに出力されます。   ▼ SNS通知 デフォルトのままにします。 ▼ パラメータ 「Operation」欄は、「Install」を選択します。 「Snapshot Id」欄は、「 {{WINDOW_EXECUTION_ID}} 」を入力します。 そのほかはデフォルトのままにします。 ▼ CloudWatch alarm デフォルトのままにします。 すべての項目を設定後、「Run Commandタスクの登録」をクリックします。   パッチ実行結果確認 パッチ適用結果は以下の方法で確認することができます。 ①「メンテナンスウィンドウ」>「履歴」 ②CloudWatch logsに出力する設定をした場合は、 「ロググループ」>「ロググループ名」>「ログストリーム」   まとめ 今回は、Windowsのパッチ適用を設定するところまでをまとめてみました。 適用結果をSNSで通知させる方法もあるみたいなので、次の記事でご紹介します。 パッチ適用以外にもSSMの機能でいろいろできそうだと思ったので、もう少し調べてみようと思います。
こんにちは、冨岡です。 私は12月までAWSの研修を受けていました。 今では案件に入り、私の社会人生活が始まった気がします。 しかし私が扱うのはAWSではなく、なんと Azure 。 心を入れ替えて頑張ります! はじめに 私が今取り組んでいるのは、アプリ機能の新規開発と既存システムを改修し Azure上へ移設する案件です。 Azure上にVM、ストレージ、ネットワーク等々を構築しています。 これらのサービスは本番環境として東日本リージョン、 災対環境 として西日本リージョンで構成します。 私はこの 災対環境 を担当しております。 私が興味を持ったサービスは Azure Spring Apps です。 オンラインWebアプリケーションを実行する用途で使われるサービスになります。 実はこのAzure Spring Apps、 西日本リージョンで非対応 なのです! 代わりに 東南アジアリージョン に作成しますので、備忘録として記事にまとめます。 災対環境とは 本番環境障害によりシステム継続負荷の際に稼働する環境。 本番環境が構築される東京リージョンの 大規模障害時 において、 西日本・東南アジアリージョンでシステムを構築します。 Azure Spring Apps Azure Spring Appsとは コードを変更せずに、Spring BootアプリケーションをAzureにデプロイできるPaaSのサービス。 私の環境でHello World表示させてみます。 参考: クイックスタート – 初めてのアプリケーションを Azure Spring Apps にデプロイする | Microsoft Learn 対応リージョン 公式ドキュメント見ましたが、やはり西日本リージョンなかったです。 Azure Spring Apps の信頼性 | Microsoft Learn      試用 Microsoft Learnに沿って、Azure Spring Appsでアプリをデプロイしてみたいと思います。   Azure Spring Appsの作成   Azureポータル上でも西日本リージョンがないこと確認 アプリをデプロイ   ブラウザからHello Worldにアクセスできること確認 案件ではちゃんとしたシステムにアクセスできるようになると考えると、胸が高まります。   注意         公式ドキュメントに、 廃止 のお知らせがありました。 Azure Container Apps への移行が推奨されていました。 こちらを使うことなったら、また記事にしようと思います。 Azure Spring Apps の提供終了のお知らせ | Microsoft Learn   さいごに TechHarmonyにはAzureの記事がAWSと比較して少ないと感じました。 私がたくさん書いてAzureの記事を増やそうと思います。
SCSKの畑です。7回目の投稿です。 今回は小ネタ・・のつもりだったのですがいくつか加筆していたらそれなりの分量になってしまいました。内容は正直タイトルから大体想像付くところだとは思うのですが、ご了承ください。 アーキテクチャ概要 今回はタイトル通り AWS AppSync と AWS Lambda がメインですが、少しだけ Amazon Redshift も話に出てきます。 背景 ほぼ毎回説明しているかもしれませんが、本アプリケーションの主目的(機能)はデータベース/DWH のテーブルデータメンテナンスです。本アーキテクチャ構成上、アプリケーションと Redshift の間でテーブルデータの読み書きをする場合は AppSync 及び Lambda を介するため、対象テーブルのデータサイズ次第で AppSync もしくは Lambda のペイロードサイズに制約を受ける可能性があります。 AppSync は、レスポンス/レスポンス共に最大ペイロードサイズが 5MB です。 厳密には、評価されたマッピングテンプレートの最大サイズが 5MB という解釈が正しいようです。下記 URL における「evaluated resolver, function, or handler template」という表現ですね。 AWS AppSync endpoints and quotas - AWS General Reference The following are the service endpoints and service quotas for this service. To connect programmatically to an AWS servi... docs.aws.amazon.com Lambda は、同期呼び出しにおける最大ペイロードサイズはリクエスト/レスポンス共に 6MB です。 Lambda quotas - AWS Lambda Maximum sizes, limits, and quotas for Lambda functions and API requests. docs.aws.amazon.com よって、今回のアーキテクチャにおいて、AppSync API 実行時の入出力データサイズはいずれも 5MB  未満に抑える必要があります。 なお、ダイレクト Lambda リゾルバを使用した場合は、最大ペイロードサイズが Lambda の値に準拠するようです。(=最大 6MB)ただ、Amplify で @function ディレクティブを使用して Lambda を query なり mutation に紐付けた場合は、マッピングテンプレートを使用する通常の Lambda リゾルバになるようです。gen2 であればオプション等で対応していたりするんでしょうか・・? 今回はダイレクト Lambda リゾルバの使用が必須となるような要件はなかったため、使用していません。 ダイレクト Lambda リゾルバー (VTL) を使用した VTL マッピングテンプレートの無効化 - AWS AppSync ダイレクト Lambda リゾルバーを使用して VTL テンプレートのをバイパスする方法について説明します。 docs.aws.amazon.com もちろん、アーキテクチャ設計時にはこの点は考慮に入れており、以下2点より当面は特に対策しなくても大丈夫だろうと考えていました。 本アプリケーションにおけるメンテナンス対象はいわゆる「マスタテーブル」であるため、データサイズが小さい 「トランザクションテーブル」のようなデータサイズの多いテーブルではない 現時点で一番データ量の大きいテーブルのデータサイズが CSV ファイル形式 で 約 800KB である かつ、今後データサイズが爆発的に増大する可能性が少ない ところが、開発中に同テーブルに対するリクエスト/レスポンスのデータサイズを計測してみたところ、 3MB 程度になってしまっていることが判明しました。上記の通りまだ最大サイズ内ではあるものの、既に半分を超えてしまっていることになるため、慌てて原因を調べることになりました。 データサイズ差異の原因 何故このようなデータサイズの差異が生じてしまったのかですが、原因は AppSync 及び Lambda の入出力におけるテーブルデータのフォーマットにありました。当初、AppSync のスキーマにおけるテーブルデータは、以下のように 文字列の配列 として定義しており、この形式でAppSync/Lambda で入出力を行っていました。 table_data: [String]! 具体的には、以下のような JSON Lines 形式 のデータを行単位でリストに格納していました。 {"id":"001","value":"sample_data_1", "create_day":"2025-01-01 00:00:00","update_day":null,"creater_name":"admin","updater_name":"","delete_flg":"N" } {"id":"002","value":"sample_data_2", "create_day":"2025-01-02 00:00:00","update_day":null,"creater_name":"admin","updater_name":"","delete_flg":"N" } {"id":"003","value":"sample_data_3", "create_day":"2025-01-03 00:00:00","update_day":null,"creater_name":"admin","updater_name":"","delete_flg":"N" } 以下理由よりトータルで一番楽に実装できると判断したため、このようなフォーマットでデータを保持していた訳ですが・・ Redshift ⇔ Lambda 間のデータ読み書きに COPY / UNLOAD 文を使用しており、そのSQL文からそのまま使用できるフォーマットであるため COPY / UNLOAD 文でサポートされているフォーマットは JSON 含めて複数存在するが、JSON にしておけば後述するようにアプリケーションからも容易に使用できる https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/copy-parameters-data-format.html#copy-json https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/r_UNLOAD.html アプリケーション側で使用しているテーブル表示用のライブラリにおいて、テーブルのデータを JSON 形式で保持する必要がある JSON Lines形式から変換する必要はあるものの、最小限の対応で済む さて、もう既にお分かりかと思いますが、ここまで 黄色アンダーライン でマークした通り、テーブルデータのフォーマットを JSON Lines 形式で持つようにしたことがデータ量増大の原因となっていました。テーブルデータを同形式で持つということは、当然ながら実データだけでなく JSON フォーマットで付加される情報(列名、中括弧、ダブルクォートなど)のデータも各行ごとに含まれます。つまり、テーブルのデータ量が大きいほど、JSON フォーマットで付加される情報が占めるデータサイズも相対的に大きくなってしまうことになります。 厳密には、列数・列名の長さといったメタ情報や、行数の方が要因としてより多くを占めるものと思いますが・・ そこで、改めて対象テーブルにおける実データ以外のデータサイズを改めて計算したところ、 行数:約 10000 行 1行あたりの JSON フォーマット分のデータサイズ:約 220Byte 計算結果:210 B * 10000 ≒ 2.2MB ということで、当然ですがデータサイズの差異分とほぼ一致する結果となり、裏付けが取れてしまった格好になりました。。というか、実データの約 2.5 倍のサイズとなってしまっているんですよね。どう考えてもサイズ効率は劣悪ですし、他のメンテナンス対象テーブルについても同様の傾向にあることが見て取れましたので、根本的な対策を行うこととしました。 解決策 一方で、JSON (Lines) 形式そのものは実装面から変えたくなかったのでちょっと悩んだのですが、今回のように「とりあえずサイズを小さくできればよい」ケースで最も良く使用される手法を用いてあっさり解決しました。そう、 圧縮 です。 ということで、まずスキーマ定義を文字列の配列から文字列に変更しました。 table_data: String! 後は、Lambda と テーブルデータをやり取りする時に、JSON 形式のデータをダンプしてから圧縮/展開してしまえば OK です。以下、Lambda側 で圧縮したテーブルデータを Typescript 側で展開する場合の実装例を示します。 Lambda JSON 形式のテーブルデータを Lambda 側で圧縮する例です。 table_data_json_base64 = base64.b64encode(gzip.compress(json.dumps(table_data_json).encode('utf-8'))).decode('utf-8') 圧縮する以上、lambda コード内でテーブルデータを JSON Lines 形式で扱う必要性がなくなったため、通常の JSON 配列として扱っています。table_data_json が対象の変数です。 Typescript Lambda / AppSync から受け取った圧縮されたテーブルデータを展開して、JSON 配列に変換する例です。 const table_datas = JSON.parse(pako.ungzip(Base64.toUint8Array(table_data_json_base64), { to: 'string' })) table_data_json_base64 変数に圧縮データが格納されている想定です。また、下記サイト様の情報より、gzip/ungzip 用のモジュールとして pako を使用しています。 zlibでGZIP形式で圧縮された文字列のレスポンスデータをJavaScriptで解凍する - Qiita Lambdaで構築されたAPIで返却値が大きいせいでペイロードのデータサイズ制限(6MB以上は制限に引っかかる)に引っかかってしまうとのことで、Lambda側でgzip圧縮されたデータをフロントで解… qiita.com これらの対応により、対象テーブルデータのサイズを 約 160KB に低減することができました。最大サイズ 5MB と比較しても十分に小さいため、この方式を採用して解決と相成りました。JSON 形式のデータフォーマットである以上はある程度圧縮が効くものと見込んでいましたが、データそのものの特性もあり想定以上の圧縮率となりました。 なお、より根本的な対策として、Lambda にテーブルデータを入力する(=アプリケーションからテーブルデータを更新する)場合は、全テーブルデータを渡して truncate + load するのではなく、更新差分のみを渡すという方法もあります。 おおよそのケースでデータサイズを抑えることが可能な半面、差分更新を行うための SQL 文を生成するロジックを実装する必要があるため、実装難易度は上がります。また、更新量によっては truncate + load より処理速度が遅くなる可能性もあります。(Redshift のように、列指向でデータを持つ DWH ではよりその傾向が強くなります) 今回はメンテナンス対象テーブルのデータサイズを鑑みて、差分更新を実装する必要はないと判断しました。 まとめ 言い訳がましいのですが、CSV 形式より JSON 形式の方がトータルでデータサイズが増大すること自体は認識していました。ただ、実データに基づく試算などの定量的な裏取りはしておらず、何となく大丈夫だろう程度の感覚で実装を優先してしまっていたのが今回の根本原因であり、反省点です。。。特に今回は事前に試算することも容易なデータサイズ/データフォーマットであったため、尚更ですね。 なお、本記事を書き終わった後に初めて知ったのですが、実は AppSync 側で API レスポンスの圧縮を行う機能もあることに気づきました。本アプリケーションの実装は変更しない予定ですが、別の機会で試してみても良いかもしれないですね。 AWS AppSync でのサーバー側のキャッシュと API ペイロード圧縮の設定 - AWS AppSync AWS AppSync のサーバー側のデータキャッシュと圧縮機能について学んでください。 docs.aws.amazon.com ただ、実アプリケーションで採用する場合、以下一文が気になるところではありますが・・ AWS AppSync はベストエフォートベースでオブジェクトを 圧縮 します。まれに、AWS AppSync が現在の容量を含むさまざまな要因に基づいて、圧縮をスキップすることがあります。 本記事がどなたかの役に立てば幸いです。
こんにちは、SCSKの茂木です。 昨今、重要なシステムのBCP対策としてマルチリージョンでの冗長化をよく目にします。 障害や災害が発生した際のダウンタイムを最小化 や データを複数の場所に分散することによるシステムの信頼性向上 などを目的としたこの構成ですが、AWS上でどう実装すればいいのか気になりました。 なので、今回はAWS Transit Gatewayを用いてマルチリージョンにおける高可用性方式を実装してみます。 はじめに 本記事では、DRBDでのデータレプリケーションを想定したマルチリージョン構成について解説します。 DRBD(Distributed Replicated Block Device)は、Linuxシステムにおけるストレージレベルのデータレプリケーションソフトウェアで、複数のサーバー間でデータをリアルタイムに同期し、高可用性を実現するために使用されます。 第10回 DRBD × LifeKeeperの高可用性リアルタイムレプリケーションを探る データのリアルタイムレプリケーションを実現するDRBD(Distributed Replicated Block Device)とLifeKeeperの可能性について説明します。 blog.usize-tech.com 2024.10.25 マルチリージョンでの冗長化を図るために、クラスタノードは3台構成とし、 クライアント及びノード間の通信をを効率的に管理するためにAWS Transit Gatewayを採用しております。 クライアント通信は仮想IPアドレスをターゲットに行われます。この仮想IPアドレスは、複数の拠点やデータセンターからの通信を一元的にハンドリングする役割を果たします。 なお、本稿では一般的なAWSサービス(EC2など)の構築手順や基本的な説明については割愛しています。そのため、AWSサービスの基本的な操作方法については、別途AWSの公式ドキュメントなどをご参照ください。 全体構成   構成要素と役割   構成要素 説明 NATインスタンス (LKDRBD-nat,LKDRBD-nat-2) Elastic IPを設定し、Publicなサブネットに配置します。 クラスターノードはNATインスタンス経由でAWSのAPIを使ってルートテーブルを制御の内容を書き換えます。 AWSのAPIを使うにはインターネットにアクセスできることが前提となります。 AWSの新機能のPrivate Linkを使うことでインターネットに出ずにAPIをたたけるようになりましたが、本記事では従来通りのNATインスタンスを使う方式を前提としています。 踏み台Windowsサーバー (LKDRBD-windows) Publicなサブネットに配置します。クラスターノードの設定やメンテナンス作業は全て踏み台サーバー経由で行います。 お手元のPCからRDP接続し、踏み台Windowsサーバーとクラスターノード間はSSHで通信します。 クラスターノード (LKDRBD-1,LKDRBD-2,LKDRBD-3) インターネットにアクセスできる必要が無いので、Privateなサブネットに配置します。 今回の検証環境ではファイルシステムの冗長化を行っています。 仮想IPアドレス 意図的にVPC外のダミーのIPアドレス(20.1.1.1)をクライアントから参照させることでルートテーブルを参照させて、そのルートテーブルに登録されているターゲットのENIにアクセスさせます。 詳しくは過去の記事をご参照ください。                            第4回 【LifeKeeper】AWSでは仮想IPアドレスが使えない!?をこうして解決する!! オンプレや仮想環境でHAクラスタを構築する際は仮想IPアドレスを使えましたが、AWSでは仮想IPアドレスの機能が提供されません。この課題をLifeKeeperがどのように解決しているかを解説します。 blog.usize-tech.com 2023.11.29 フェイルオーバーの際には、AWSのAPIをキックしてターゲットを稼動系から待機系にルーティング変更します。この制御により、クライアントは常に稼動系のクラスターノードにアクセスできます。   NWの構成 各サービスのパラメータ 本環境で利用しているNW関連サービスのパラメータを紹介します。 内容が重複するため東京リージョンのリソースのみとします。 ルートテーブル publicとprivateで分けていますがどちらも大阪に抜ける経路は同じとなります。 Transit Gateway 必要に応じてオプション機能を有効化します。 今回は「DNSサポート」のみ有効化しています。   Transit Gatewayアタッチメント 東京側のアタッチメント(tgw-at-tokyo)では東京のpublicとprivateサブネットを関連付けています。 Transit Gatewayルートテーブル アタッチメントごとにルートテーブルを作成して関連付けるイメージです。 ルートアナライザーでの疎通確認 ルートアナライザーを使うと簡易的に疎通確認ができます。 疎通が失敗する場合は原因がどこかを特定しましょう。 仮想IPのルーティングシナリオ 仮想IPアドレスを使ったクライアント通信経路に関して、想定されるルーティングシナリオを3パターンご紹介します。 クラスターノードの稼働状況に応じてAWS側でルーティングを切り替える必要があります。 ここでは仮想IPアドレスを「20.1.1.1」とし、東京1号機(LKDRBD-1)から順に障害によるダウンをしたと仮定します。 例ではTGWアタッチメントをサーバと同じプライベートサブネットに配置していますが、 AWSのベストプラクティスはTGWアタッチメント専用のサブネットを分けることが推奨されています。                              Amazon VPC Transit Gateway 設計のベストプラクティス - Amazon VPC Transit Gateway 設計のベストプラクティスについて学ぶ。 docs.aws.amazon.com   東京1号機が稼働機の場合 各ルートテーブルは以下のように設定します。 ※設定不要なルートテーブルは割愛します。 ・rtb-tokyo 送信先 ターゲット 20.1.1.1/32 LKDRBD-1のENI ・tgw-rtb-tokyo 送信先 ターゲット 20.1.1.1/32 tgw-at-tokyo ・tgw-rtb-tokyo-osaka 送信先 ターゲット 20.1.1.1/32 tgw-at-tokyo   東京2号機が稼働機の場合 各ルートテーブルは以下のように設定します。 ※設定不要なルートテーブルは割愛します。 ・rtb-tokyo 送信先 ターゲット 20.1.1.1/32 LKDRBD-2のENI ・tgw-rtb-tokyo 送信先 ターゲット 20.1.1.1/32 tgw-at-tokyo ・tgw-rtb-tokyo-osaka 送信先 ターゲット 20.1.1.1/32 tgw-at-tokyo   大阪1号機が稼働機の場合 各ルートテーブルは以下のように設定します。 ※設定不要なルートテーブルは割愛します。 ・rtb-osaka 送信先 ターゲット 20.1.1.1/32 LKDRBD-3のENI ・tgw-rtb-osaka 送信先 ターゲット 20.1.1.1/32 tgw-at-osaka ・tgw-rtb-osaka-tokyo 送信先 ターゲット 20.1.1.1/32 tgw-at-osaka ルーティング切替の実装方針 上述のルーティングシナリオを実現するためにAWSのAPIを使用することでルーティングの切替を行います。 ざっくりした実装方式は以下を想定しており、別記事でスクリプトや挙動のご紹介を予定しています。 ①障害が発生 ②稼働機に昇格したクラスタノードからスクリプトをキック ③スクリプト内でルートテーブルを差し替えて仮想IPを新稼働機までルーティング ④新稼働機でサービス継続 最後に 以上、駆け足でしたがAWS Transit Gatewayを用いたマルチリージョン構成ができたことになります。 最後までお読みいただき、ありがとうございました。 次回はスクリプトによるルーティング切替の詳細について記事を書こうと思います。
先日初めて、AWS User Notificationsというサービスを利用しました。 AWSからの通知を設定、管理していく際に便利だと感じましたので、 EC2に関するAWS Healthイベントの通知をAWS User Notificationsを利用して実装した検証内容をまとめてみました。 概要 AWS Health イベントとAWS User Notificationsについて簡単に説明します。 詳細についてはリンクをご参照ください。 AWS Health イベント AWS Health イベントとはAWS Healthが送信する通知のことで、AWSの障害情報やアカウントに影響を与える可能性がある変更などについて通知を行います。 とは AWS Health - AWS Health AWS Health Dashboard と を使用して、 AWS リソース、サービス、アカウントの状態を継続的に可視化します AWS Health。 docs.aws.amazon.com AWS User Notifications AWS User Notificationsは、AWSサービスからの通知を一元的に設定および確認できるサービスです。 AWS ユーザー通知 aws.amazon.com 検証内容 今回は、EC2に関するAWS Health イベントの通知をAWS User Notificationsで実装し、Eメールで通知を受け取ってみました。 事前確認 AWS Health APIを利用して、EC2のAWS Health イベントに関する情報を確認します。 今回はAWS CloudShellで実行します。AWS Health APIの詳細は以下リンクをご確認ください。 Actions - AWS Health The following actions are supported: docs.aws.amazon.com 以下を実行することで、どのような内容のEC2のAWS Health イベントが通知されるのかを確認できます。 出力結果は2025/1/8時点の情報であり、出力結果が長いため省略しています。 [cloudshell-user@ip-xx-xx-xx-xx ~]$ aws health describe-event-types --region us-east-1 --filter "services=EC2" --output table ------------------------------------------------------------------------------------------------------- | DescribeEventTypes | +-----------------------------------------------------------------------------------------------------+ || eventTypes || |+---------------------+-----------------------------------------------------------------+-----------+| || category | code | service || |+---------------------+-----------------------------------------------------------------+-----------+| || issue | AWS_EC2_API_ISSUE | EC2 || || accountNotification| AWS_EC2_BILLING_NOTIFICATION | EC2 || || accountNotification| AWS_EC2_BYOIP_ROAS_EXPIRING | EC2 || || accountNotification| AWS_EC2_BYOIP_RPKI_INVALID | EC2 || || accountNotification| AWS_EC2_BYOIP_RPKI_UNKNOWN | EC2 || || issue | AWS_EC2_CAPACITY_BLOCKS_API_ISSUE | EC2 || || issue | AWS_EC2_CAPACITY_BLOCKS_API_LATENCY_ISSUE | EC2 || ・・・(中略)・・・ || accountNotification| AWS_EC2_SIMPLIFIED_AUTO_RECOVERY_SUCCESS | EC2 || || issue | AWS_EC2_SPOT_API_UNAVAILABILITY | EC2 || || scheduledChange | AWS_EC2_ULTRASERVER_CAPACITY_REDUCED | EC2 || || scheduledChange | AWS_EC2_ULTRASERVER_MAINTENANCE_INITIATED | EC2 || || accountNotification| AWS_EC2_ULTRASERVER_RECOVERY_COMPLETED | EC2 || || issue | AWS_EC2_VPC_API_ISSUE | EC2 || || issue | AWS_EC2_VPC_NETWORK_HEALTH_INTERNET_ISSUE | EC2 || || issue | AWS_EC2_VPC_NETWORK_HEALTH_INTER_AZ_ISSUE | EC2 || || issue | AWS_EC2_VPC_NETWORK_HEALTH_INTRA_AZ_ISSUE | EC2 || |+---------------------+-----------------------------------------------------------------+-----------+| (END) AWS User Notificationsの設定 AWS User Notificationsを設定していきます。 Creating your first notification configuration in AWS User Notifications - AWS User Notifications Get started with User Notifications. docs.aws.amazon.com 今回は、以下の流れで設定を実施します。 通知設定 通知ハブの設定 配信チャネルの設定 AWS User Notificationsのマネジメントコンソールには、画面上部の以下マークをクリックすることで遷移できます。 1.通知設定 通知を受け取りたいサービスとイベントルールの設定を行います。 「通知設定を作成」をクリックします。 「クイックセットアップ」と「名前と説明」を設定します。 クイックセットアップを利用することで、迅速に通知設定を作成することができます。 「イベントルール」を設定します。 今回は、EC2に関するAWS Health イベントの通知のみを受け取りたいので、高度なフィルターで以下のjsonを記述しました。 { "source": [ "aws.health" ], "detail-type": [ "AWS Health Event" ], "detail": { "service": [ "EC2" ] } } 「集約設定」と「配信チャネル」を設定します。 集約設定では通知の受信頻度、配信チャネルでは通知の送信先を定義します。 配信チャネルでは、「Eメール」、「AWSコンソールモバイルアプリ」、「チャットチャネル」を選択可能です。 配信チャネルは後述で設定するため、詳細は 3.配信チャネルの設定 をご確認ください。 設定が完了したら、「通知設定を作成」をクリックします。 イベントルールの「リージョン別のステータス」が「アクティブ」になっています。 2.配信ハブの設定 通信ハブでは、通知データの保存や処理、またはレプリケートをする特定のリージョンを選択します。 今回は、東京リージョン(ap-northeast-1)を選択し、「保存して続行」をクリックします。 Asia Pacific (Tokyo)が「アクティブ」になっていることがわかります。 3.配信チャネルの設定 配信チャネルでは通知先を設定します。 今回は通知先にはEメールを選択します。 「Eメールを追加」をクリックします。 受信者にメールアドレス、名前にメールアドレスの所有者がわかるような名前を設定し、「Eメールの追加」をクリックします。 設定したメールアドレス宛にAWSから以下のようなメールが送信されます。 内容を確認の上、「Verify email」をクリックします。 「Eメールが検証されました」と表示されます。 配信チャネルを確認すると、先ほど登録したメールアドレスの検証ステータスが「アクティブ」になっています。 登録した配信チャネルを通知設定に設定します。 通知設定の設定画面にて、「配信チャネル」をクリックします。 追加した配信チャネルを選択し、「通知設定を更新」をクリックします。 配信チャネルが通知設定に追加されています。   AWS User Notificationsの設定は以上です。 まとめ 今回は、AWS Health イベントをEメールで通知できるように設定まで行いました。 テストの方法についても今後まとめられたらと思っております。 EC2のAWS Health イベントをEメールで通知できるように設定しましたが、 UserNotificationsは他にもCloudWatchなどにも対応しているので、運用機能実装時に大変役立つサービスだと思います。 皆様のお役に立ちましたら、嬉しいです。
SCSKの畑です。6回目の投稿です。 今回も引き続きアプリケーションにおける排他制御の話題ですが、よりアプリケーション側の実装にフォーカスしています。 はじめに 第一回 で整理していた排他制御の要件は以下の通りです。 主にデータベース/DWH 上のテーブルデータのメンテナンスを実施するアプリケーション 以下の要件に基づき、アプリケーション側で排他制御(ロック)を実装 テーブルデータ編集時に、テーブル単位での排他制御(ロック)を行う 外部キーを持つテーブルのデータ編集時には、外部キーの参照先テーブルについても合わせて排他制御(ロック)を行う 対象テーブルのステータス(編集状態)は DynamoDB で管理する ステータスの遷移(更新)時に、DynamoDB、Lambda、AppSync を用いて排他制御(ロック)を行う ここ2回のエントリでは主に4点目の内容について記載しましたが、今回は2点目の内容について記載します。 今回の案件事例では、2点目の内容を満たす設計・実装として、具体的には以下のような方式を採りました。 あるテーブルをユーザAが編集中の場合、 ユーザAにはテーブルデータの編集画面を表示する それ以外のユーザにはテーブルデータの参照画面を表示する(編集はできない) この場合、DynamoDB で管理されているテーブルのステータスを元に、ユーザ毎にアプリケーション側で画面を制御したり、ルーティングを変更したりする必要があります。よって、このステータスをどのようにアプリケーション内で扱うかが重要なポイントとなったため、考慮・工夫した点を以下に記載していきたいと思います。   アーキテクチャ概要 こちらも載せておきます。 本投稿で言及するのはほぼアプリケーション(Nuxt.js)部分になりますが、ちょこちょこ AppSync の話も書きます。なお、本投稿では Nuxt.js(Vue)自体の説明は割愛します。ご了承ください。 Amazon DynamoDB テーブルのステータス(編集状態)管理 AWS Lambda 排他制御を考慮したテーブルのステータス更新ロジックの実装 AWS AppSync(AWS Amplify) 上記 Lambda をアプリケーション上から実行するためのスキーマ定義 アプリケーション(Nuxt.js) テーブルのステータスに応じた画面制御   テーブルのステータスをコンポーネント間で共有する 冒頭の繰り返しになりますが、テーブルのステータス(編集状態)をベースにアプリケーション側でルーティングや画面の制御を行う必要があるため、同情報はアプリケーション内の各コンポーネントから参照できることが望ましいです。このような仕組みをどのように実装するかはフレームワークに拠りますが、Nuxt3 の場合は useState を使用すると楽に実装できます。 なお、テーブルのステータス情報を、アプリケーションのライフサイクルにおいて適切なタイミングで初期化することも非常に重要なのですが、その話はまた別のエントリで触れたいと思います。最もハマったポイントの1つだったので・・ 実装にあたっては以下サイト様の内容がとても参考になりました。 Nuxt3入門(第8回) - Nuxt3のuseStateでコンポーネント間で状態を共有する | 豆蔵デベロッパーサイト 前回はNuxt3でプラグイン、ミドルウェアの導入について見てきました。今回はNuxt3が提供する状態管理について見ていきます。Nuxt2では、コンポーネント間で状態を共有するには、Nuxt2にバンドルされているVuexを使うのが一般的でした... developer.mamezou-tech.com 以下実装例です。composable として実装しています。 import * as custom_queries from "@/src/graphql/customQueries"; import * as models from "@/src/API"; export type TableStatus = {   status: models.TableStatus,   editor: string,   locked_by: string, } export const useTableStatus = () => {     // stateの定義   const TableStatusDict:Ref<{[key: string]: TableStatus}> = useState('g_TableStatusDict', () => ({}))     // stateの更新処理   const updateTableStatusDict = async () => {       const client = useNuxtApp().$Amplify.GraphQL.client;       const result=await client.graphql({           query: custom_queries.listTablesCustom,       })       let master_table_status:{[key: string]: TableStatus} = {}       for (let item of result.data.listTableInfos.items) {           master_table_status[item.name] = {               'status': item.status,               'editor': item.editor,               'locked_by': item.locked_by,           }       }         TableStatusDict.value = master_table_status;   }     return {       TableStatusDict: readonly(TableStatusDict),       updateTableStatusDict: updateTableStatusDict,   } } useState() で定義したオブジェクトは Nuxt3(Vue3) における Ref となるため、typescript 内でアクセスする際は .value を付けてアクセスする必要があります。また、export 時は readonly として読み取り専用とし、内容の更新は専用のメソッド「updateTableStatusDict」を通してのみ実行するようにしています。 更新処理自体は、前回のエントリで定義した DynamoDB のテーブルの内容を graphql 経由で取得してきた上で、dict 形式に変換してuseState() で定義したオブジェクトを更新しているだけですね。   テーブルのステータスをアプリケーション側にリアルタイム反映する テーブルのステータスについてはなるべくリアルタイムでアプリケーション側に反映できるとユーザビリティの観点からは望ましいのですが、そのような仕組みを自前で用意するのは実装面でなかなかハードルが高いところです。そこで今回は、AppSync の subscription を使用したプッシュ通知によるリアルタイム更新を採用しました。この仕組みによりステータスの反映処理の大凡を任せることができて実装も楽になったので、正に一石二鳥でした。この仕組みを使いたいが故に今回 AppSync を使用したと言っても過言ではないかもしれません。 subscription については、平野さんが執筆された素晴らしいエントリがありますのでこちらも合わせてご参照ください。私自身も大いに参考にさせて頂きました。 AWS AppSync を使って React アプリからキックした非同期ジョブの結果をプッシュ通知で受け取る 非同期ジョブを実行した後、結果をどう受け取るか?というのは開発者として作り込み甲斐のあるテーマです。今回は React アプリが非同期ジョブを実行した後に、AWS AppSync 経由でジョブ完了のプッシュ通知を受け取る仕組みを紹介します。 blog.usize-tech.com 2022.12.01 以下、schema.graphql ファイルにおける実装例です。 type Subscription { onStatusChangeTableWithLock: ResultTableStatus @aws_subscribe(mutations: ["ChangeTableStatusWithLock"]) @aws_api_key @aws_iam } 今回 subscription を設定したいのは、前回のエントリで作成したステータス更新用の mutation (ChangeTableStatusWithLock) となるので、それを3行目の「mutations:」で指定したリスト内で定義すれば OK です。リスト内に複数の mutation を指定して、単一の subscription に対応させることも可能です。 以下実装例です。こちらは、メンテナンス対象のテーブル一覧を表示するためのメニュー用 component 内のメソッドとして定義しており、onMounted() のタイミングで実行するようにしています。 const subscribeTableList = async () => {   varSubscriptionStatus.value = client       .graphql({ query: subscriptions.onStatusChangeTableWithLock })       .subscribe({           next: async (data: any) => {               await updateTableStatusDict()               await updateTableMenu()           },           error: (error: any) => console.warn(error)       }); } onMounted(async () => { await updateTableMenu(); await subscribeTableList(); }); なお、 subscription の返り値は「mutations:」で指定した mutation と同一でないといけない点に注意してください。 mutation を複数指定する場合も同様です。 よって、今回の実装においてはアプリケーション側でこの subscription によるプッシュ通知を受け取った後に、1つ前のセクションで記載したようなテーブルステータス更新用のメソッドを実行する必要があります。この subscription の役割はあくまでいずれかのテーブルのステータスに更新があったことまでで、各テーブルのステータスは改めて取得する必要があるということですね。 前回及び今回の実装例における改善点があるとすると、この返り値である ResultTableStatus にテーブルステータス更新用のメソッドの返り値と同じような情報も含むようにすれば、AppSync API のコール数が1回減ってより効率的になったというところでしょうか。もちろんその場合は composable の仕様についても見直す必要がありますが。 ほか、メニュー画面にテーブルのステータスを表示している都合上画面も更新する必要があるため、そのための関数「updateTableMenu()」も合わせて実行しています。(同関数の内容については今回触れません)   aws-amplify の SSR (Nuxt3)対応 最後に、本筋とは少し異なる話題になりますが触れておこうと思います。 今回は Web アプリケーションフレームワークとして Nuxt3 を使用している関係上、レンダリングモードとして SSR (Server-Side Rendering) を使用しています。このため、厳密には実装次第ではありますが、aws-amplify モジュールの各関数がクライアントサイドだけでなく、サーバサイドでも実行され得ます。しかし、aws-amplify は原則 React や Vue などの SPA (Single-Page Application) で使用することが前提のため、Nuxt3 (Nuxt.js) において SSR 対応させるためには別途準備が必要となります。 なお、Next.js は下記 URL を見る限り、バージョン縛りはあるもののサポートされているようです。 Use Amplify categories APIs from Next.js - Next.js - AWS Amplify Gen 1 Documentation Use Amplify categories APIs from Next.js server-side runtimes. AWS Amplify Documentation docs.amplify.aws 具体的には、Nuxt3 の plugin を使用して、クライアントサイドとサーバサイドで使用する aws-amplify のインターフェース(関数)をそれぞれ分けて定義するような形式になります。ただ、サーバサイドで使用できる機能は限定されているため留意が必要です。詳細は以下 URL を参照ください。 Use Amplify categories APIs from Nuxt 3 - Next.js - AWS Amplify Gen 1 Documentation Use Amplify categories APIs from Nuxt 3 AWS Amplify Documentation docs.amplify.aws なお、1つ目のセクションで示した実装例も、上記の通り plugin 内で初期化した graphql の client を使用しています。このため、コード内で aws-amplify モジュール を import していません。この composable は実装上クライアントサイド/サーバサイド両方で実行され得るため、ここまで記載したような対策が必要となります。 本件は、詳細に踏み込もうとすると aws-amplify モジュールの初期設定にも関連する話になるため、また別のエントリで改めて説明する形にさせて頂こうと思います。内容としては公式 URL をある程度なぞる形にはなりますが。。   まとめ いずれのセクションの内容もアプリケーションの設計・実装上は重要な要素だったと感じています。特に、aws-amplify の SSR 対応についてはアプリケーション開発当初に調べていた内容なのですが、調査の過程でアプリケーションフレームワーク自体の挙動や SPA との差異など、他の重要な知識についてもある程度知ることができたのが今振り返るととても良かったです。 第四回を書くかは現時点では未定ですが、書く場合は Nuxt.js における具体的なルーティングの実装例について幾つか記載してみようと思います。 本記事の内容がどなたかの役に立てば幸いです。
こんにちは、広野です。 React アプリから実行できる Amazon DynamoDB へのデータインポート画面を作成したときの話です。 インプットするデータ量は少ないのですが、データを加工したりデータから AI サービスを使用してさらにデータを作成したりしなければならなかったので、裏で AWS Step Functions ステートマシンを使用します。AWS AppSync から Mutation でインポートデータをステートマシンに渡し、開始させる仕組みを作成しました。そこで AWS AppSync の HTTP リゾルバという設定を使いましたが、少し工夫が必要な面があったので紹介します。 つくったもの 主に、以下の図の 緑色の線 の部分を説明します。 AWS AppSync への命令は Mutation にしましたが、Query でも動くと思います。ただ、データを取得するのが目的ではなかったので Mutation にしました。 HTTP リゾルバから実行するアクションは states:StartExecution を使用します。AWS AppSync はステートマシンを開始させたら一旦役目は終了です。以降、結果は非同期に通知される仕様とします。※本記事では言及しません。 AWS AppSync には主にデータベースと接続するための専用リゾルバが用意されていますが、それ以外の AWS サービスを呼び出すには HTTP リゾルバを使用します。呼び出すのに必要な Sigv4 と呼ばれる署名プロセスも自動で実施してくれます。 AWS AppSync での HTTP リゾルバーの使用 - AWS AppSync AWS AppSync の HTTP リゾルバーのチュートリアル。 docs.aws.amazon.com 以降、必要な設定を紹介します。 AWS AppSync スキーマ 例として、startImportJob という Mutation を作成し、data という string 型のデータをやり取りする簡単な設定にしています。 type ImportData { data: String } type Mutation { startImportJob(data: String!): ImportData } schema { query: Query mutation: Mutation subscription: Subscription } AWS AppSync データソース AWS Step Functions ステートマシンをデータソースと見立てて、設定します。タイプは HTTP にします。このデータソースに関連付ける IAM ロールを別途作成する必要があります。今回は、AWS AppSync が states:StartExecution を実行できる権限さえあれば良いです。 AWS AppSync リゾルバ 今回は JavaScript リゾルバを使用しています。 import { util } from '@aws-appsync/utils'; export function request(ctx) { return { method: 'POST', params: { headers: { 'Content-Type': 'application/x-amz-json-1.0', 'x-amz-target': 'AWSStepFunctions.StartExecution' }, body: { 'stateMachineArn': 'arn:aws:states:ap-northeast-1:231376435333:stateMachine:xxxxxxxxxxx', 'input': JSON.stringify({ "data": ctx.args.data, //アプリから渡された引数 "username": ctx.identity.claims.email //emailアドレスをIDトークンから取得している }) } }, resourcePath: '/' }; } export function response(ctx) { if (ctx.error) { util.error(ctx.error.message, ctx.error.type); } return { "data": JSON.stringify({ "statusCode": ctx.result.statusCode, //ステートマシンの実行結果 "result": JSON.parse(ctx.result.body) //結果メッセージ }) } } ステートマシンに渡すパラメータを説明します。 headers の部分はこのままにします。 body.stateMachineArn には呼び出したいステートマシンの ARN を入れます。 body.input に入れたデータが、そのままステートマシンへの input になります。ただし、格納するデータは JSON.stringify で文字列化した JSON データに変換する必要があります。(VTL の場合、$util.toJson を使用します) レスポンスについて説明します。 あらかじめ定義していた data (string 型) 変数を使用してアプリに結果を戻します。そのため、戻りのデータを JSON.stringify で文字列化しています。AWS Step Functions から戻ってきた結果は、ctx.result に入ります。エラーがあると、ctx.result.statusCode が 200 以外になります。エラーの内容は ctx.result.body に入っているのですが、これが JSON オブジェクトではなく文字列化された JSON で格納されているので、ctx.result.body を JSON.parse をかけて JSON オブジェクトとして読み取れる状態に変換しています。そうして statusCode とマージさせた JSON を stringify させて返す、という処理にしています。当初ここで結果を読み取ることができず、苦労しました。 この呼び出し方は、説明は雑でしたが以下の AWS ブログに書いてありました。 Invoking AWS Step Functions short- and long-running workflows from AWS AppSync | Amazon Web Services AWS AppSync is a fully managed GraphQL service allowing developers to easily build GraphQL APIs. AWS AppSync lets develo... aws.amazon.com React  React アプリ側の GraphQL コードも書いておきます。 import { generateClient } from 'aws-amplify/api'; import gql from 'graphql-tag'; //create AppSync client const client = generateClient(); //インポートジョブ開始関数 const startImportJob = async (data) => { const mutateImportJob = gql` mutation startImportJob($data: String!) { startImportJob(data: $data) { data } } `; const res = await client.graphql({ query: mutateImportJob, variables: { data: data } }); return JSON.parse(res.data.startImportJob.data); //文字列化されたJSONが返されるのでparseしている }; export { startImportJob }; AWS CloudFormation テンプレート 抜粋ですが、関連する部分のテンプレートを貼っておきます。詳細な設定はこちらをご覧ください。AWS Step Functions は省略しています。検証した AWS AppSync が Amazon Cognito ユーザープールで認証するタイプになっているので、その記述が紛れ込んでいます。 AWSTemplateFormatVersion: 2010-09-09 Description: The CloudFormation template that creates an AppSync API to start a Step Functions state machine and relevant IAM roles. # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: SubName: Type: String Description: System sub name that is used for all deployed resources. (e.g. prod or dev) Default: dev MaxLength: 10 MinLength: 1 Resources: # ------------------------------------------------------------# # AppSync # ------------------------------------------------------------# AppSyncApi: Type: AWS::AppSync::GraphQLApi Description: AppSync API for example Properties: Name: !Sub example-${SubName} AuthenticationType: AMAZON_COGNITO_USER_POOLS AdditionalAuthenticationProviders: - AuthenticationType: AWS_IAM UserPoolConfig: UserPoolId: xxxxx //CognitoのユーザープールID を入れる AwsRegion: !Sub ${AWS::Region} DefaultAction: "ALLOW" IntrospectionConfig: DISABLED LogConfig: CloudWatchLogsRoleArn: !GetAtt AppSyncCloudWatchLogsPushRole.Arn ExcludeVerboseContent: true FieldLogLevel: ALL Visibility: GLOBAL XrayEnabled: true Tags: - Key: Cost Value: !Sub example-${SubName} DependsOn: - AppSyncCloudWatchLogsPushRole AppSyncSchema: Type: AWS::AppSync::GraphQLSchema Properties: ApiId: !GetAtt AppSyncApi.ApiId Definition: | schema { query: Query mutation: Mutation subscription: Subscription } type ImportData { data: String } type Mutation { startImportJob(data: String!): ImportData } DependsOn: - AppSyncApi AppSyncDataSourceStateMachines: Type: AWS::AppSync::DataSource Properties: ApiId: !GetAtt AppSyncApi.ApiId Name: !Sub example${SubName}StateMachines Description: AppSync DataSource to call State Machines. Type: HTTP ServiceRoleArn: !GetAtt AppSyncStepFunctionsRole.Arn HttpConfig: Endpoint: !Sub https://states.${AWS::Region}.amazonaws.com/ AuthorizationConfig: AuthorizationType: AWS_IAM AwsIamConfig: SigningRegion: !Ref AWS::Region SigningServiceName: states DependsOn: - AppSyncApi - AppSyncStepFunctionsRole AppSyncResolverStartImportJob: Type: AWS::AppSync::Resolver Properties: ApiId: !GetAtt AppSyncApi.ApiId TypeName: Mutation FieldName: startImportJob DataSourceName: !GetAtt AppSyncDataSourceStateMachines.Name Kind: UNIT Runtime: Name: APPSYNC_JS RuntimeVersion: 1.0.0 Code: | import { util } from '@aws-appsync/utils'; export function request(ctx) { return { method: 'POST', params: { headers: { 'Content-Type': 'application/x-amz-json-1.0', 'x-amz-target': 'AWSStepFunctions.StartExecution' }, body: { 'stateMachineArn': 'arn:aws:states:ap-northeast-1:231376435333:stateMachine:example-xxxxx', 'input': JSON.stringify({ "data": ctx.args.data, "username": ctx.identity.claims.email }) } }, resourcePath: '/' }; } export function response(ctx) { if (ctx.error) { util.error(ctx.error.message, ctx.error.type); } return { "data": JSON.stringify({ "statusCode": ctx.result.statusCode, "result": JSON.parse(ctx.result.body) }) } } DependsOn: - AppSyncDataSourceStateMachines # ------------------------------------------------------------# # AppSync CloudWatch Invocation Role (IAM) # ------------------------------------------------------------# AppSyncCloudWatchLogsPushRole: Type: AWS::IAM::Role Properties: RoleName: !Sub example-AppSyncCloudWatchLogsPushRole-${SubName} Description: This role allows AppSync to push logs to CloudWatch Logs. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - appsync.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSAppSyncPushToCloudWatchLogs # ------------------------------------------------------------# # AppSync Step Functions Invocation Role (IAM) # ------------------------------------------------------------# AppSyncStepFunctionsRole: Type: AWS::IAM::Role Properties: RoleName: !Sub example-AppSyncStepFunctionsRole-${SubName} Description: This role allows AppSync to invoke Step Functions. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - appsync.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: !Sub example-AppSyncStepFunctionsPolicy-${SubName} PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - "states:StartExecution" Resource: - "arn:aws:states:ap-northeast-1:231376435333:stateMachine:example-xxxxx" まとめ いかがでしたでしょうか? 時間のかかる処理を AWS AppSync の裏側で AWS Step Functions を非同期実行させるのに活用できると思います。もちろん、AWS AppSync のタイムアウト範囲内で終わることが確実な処理であれば同期処理にしてしまってもよいでしょう。 本記事が皆様のお役に立てれば幸いです。