TECH PLAY

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

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

1226

こんにちは SCSK株式会社の坂木です。 ところで、社内資料の管理、効率的ですか? 様々な形式の文書が散在し、必要な情報を探すのに時間を取られていませんか? ファイルサーバーの奥底に埋もれどこにあるか分からない、バージョン管理が混乱する、などといった課題を抱えていませんか? これらの非効率は、業務の生産性低下に直結します。 今こそ、社内資料の一元管理体制を見直しましょう! ということで、 AWS Bedrockのナレッジベース を用いた資料の一括管理およびその検索方法をご紹介します! Amazon Bedrockについて Amazon Bedrockは、AWSが提供するフルマネージドサービスで、簡単に生成AIアプリケーションを構築できます。基盤モデルと呼ばれる大規模言語モデルをAPI経由で利用可能で、テキスト生成やチャットボット、要約、翻訳など、多様なユースケースに対応しています。 特に便利なのが、ナレッジベース機能です。ナレッジベースとは、社内文書やFAQなど、組織固有の情報を集めたデータベースのことです。Bedrock と接続すれば、AI がナレッジベースの内容を学習し、より精度の高い回答や情報を生成できます。   事前準備 今回は、「データ1.txt」「データ2.xlsx」「データ3.docx」の3つのファイルをもとにデータベースを作成します。 各ファイルには料理のレシピが記載されています。   S3バケットを作成し、対象のデータをアップロードします。 このとき、ナレッジベースにサポートされているファイルの拡張子は 「.txt」「.md」「.html」「.doc/.docx」「.csv」「.xls/.xlsx」「.pdf」 となります。これら以外の拡張にて管理しているファイルは、一度拡張子を変換してからS3へアップロードする必要があります。 Amazon Bedrock ナレッジベースデータの前提条件 - Amazon Bedrock ナレッジベースにデータを使用する前に、必要な前提条件について説明します。 docs.aws.amazon.com   ナレッジベースの作成 Bedrock/ナレッジベースから、画像の画面へ遷移し 「ナレッジベースを作成」 を選択します。   今回はデータソースをS3へ保存しているため、Choose data sourceは 「Amazon S3」 を選択します。 その他の選択肢はデフォルトで、「次へ」を押します。   S3のURL 事前準備にて作成したS3バケットを選択します。 Parsing strategy 今回はデータソースがテキストベース資料のみのため、テキストのみを解析する 「default parser」 を選択します。データソースに画像を含めている場合はFoundation modelsを選択することで、画像の解析が可能となります。 データソースの解析オプション - Amazon Bedrock 解析とは、ドキュメントとその意味のあるコンポーネントの解釈を指します。Amazon Bedrock ナレッジベースには、取り込み中にデータソースを解析するための以下のオプションがあります。 docs.aws.amazon.com その他の選択肢はデフォルトで、「次へ」を押します。   今回は埋め込みモデルの中で最も料金の安い 「Titan Embeddings G1」 を選択します。その他の選択肢はデフォルトで、「次へ」を押します。 基盤モデルを使用した生成 AI アプリケーションの構築 – Amazon Bedrock の料金 – AWS オンデマンドやプロビジョニングスループットなどの Amazon Bedrock の料金モデルの詳細情報と、AI21 labs、Amazon、Anthropic、Cohere、Stability AI などのモデルプロバイダーの料金内訳をご覧... aws.amazon.com   設定を確認してナレッジベースを作成します。以上で、ナレッジベースの作成は完了です。   検索 続いて、作成したナレッジベースを用いてデータソースの内容を検索していきます。 作成したナレッジベースの画面から、生成AIモデルを選択します。筆者はClaude AI推しなので今回は 「Claude 3.5 Sonnet 」 を選択しました。   ナレッジベースを作成後、デフォルトの状態ではデータソースが同期されていません。 そのため、データソースの項目から対象のデータソース(今回の場合だと事前準備で作成したs3)を選択し、同期ボタンを押します。また、 S3に追加の資料をアップロードした場合は、 追加のたびに同期ボタンを押して最新のS3の状況を反映させる必要があります 。   では、いよいよ検索していきます。「データ1.txt」に記載のあるスクランブルエッグの作り方について聞いてみようと思います。 データ1.txtには「ボウルに卵を割り入れ、塩とコショウを加えて混ぜる。フライパンにバターを溶かし、卵液を入れる。中火でかき混ぜながら、好みの固さになるまで加熱する。」という手順が記載されていたため、同様の回答が出力されれば成功です。 聞いてみた結果、 言葉は多少違いますが概ね同じ内容の出力が得られました。また、[1]をクリックすると回答に際してどのファイルを参照したのか分かります。そのため、 質問に対して回答となる社内資料をすぐに見つけられ、ファイルサーバから対象の資料を探す手間が省けます。   次に、データソースにない料理レシピである「ます寿司」の作り方について聞いてみようと思います。 聞いてみた結果、ます寿司の作り方はデータソースに無いと返されました。データソースにナレッジが 無い場合は、web上の精度が曖昧な回答をするのではなく、データソースに無いと回答をもらえるようです。そのため、 提供される情報は登録済みのデータに基づいており、 捏造や誤った情報を返すリスクが抑えられます。   まとめ 本記事ではAmazon Bedrockのナレッジベースを用いた社内資料管理方法について紹介しました。 ナレッジベースを用いることで資料を探す手間を省け、より短時間で確認したい情報へたどり着くことができます。すなわち、生産性の向上に寄与しているのではないでしょうか。 ちなみに、ナレッジベースはOpenSearch Serverlessの料金や検索ごとに料金が発生するため、生産性と一緒にコストも上がったります。要注意です。 最後までお読みいただきありがとうございました!
アバター
概要 Azureの既存Prepaymentから新規Azureテナントのサブスクリプションを払い出してみました。 Microsoft Learnに記載されている手順を参照するだけですが、私が対応した際につまづいたポイントや補足事項を残します。 この手順はいろいろなライセンス形態の中での一例でしかないため、正式な手順はライセンス販売企業様の案内に沿っていただければと思います。 登場するリソース 既存Prepaymentが紐づくテナントA(作成済み前提) テナントAで新規作成するアカウントB 新規作成するテナントB テナントB上で新規作成するサブスクリプションB 用語や概要については以下資料を参照ください。 Microsoft Azure利用ガイド 手順、補足 1.テナントBを新規する。 下記URLから新規テナントBを作成する。 Microsoft 365 E3 (Teams なし) ポイント1→本人確認のセキュリティチェックで電話番号を登録しSMS認証か音声通話認証を実施するのですが、エラーで弾かれ接続環境を変えることで解消しました。   2.テナントAでアカウントBを作成し有効化する。 ・アカウント所有者の登録(エンタープライズ管理者の操作) Azure portal での EA 課金管理 – Microsoft Cost Management | Microsoft Learn ポイント2→アカウントの追加の中で指定するアカウント所有者のメールを新規作成したテナントBのユーザのユーザープリンシパル名を指定します。 この次の手順のアカウントの有効化では2時間半ほどかかりました。 ポイント3→有効化後テナントBのアカウントのプロパティにテナントAの課金アカウント情報も表示されます。   3.テナントBでサブスクリプションBを新規作成する サブスクリプションの作成(上記操作で登録したアカウント所有者での操作) Enterprise Agreement サブスクリプションを作成する – Azure Cost Management + Billing | Microsoft Learn つまづきポイントは特にありませんでした。 以上となります。
アバター
こんにちは、なべです。 最近レトルトカレーにはまっていて、名店のレトルトカレーの食べ比べをしています。 現在AWS環境のIaC構築を推進しており、EC2の非機能周りも自動化で実装されると嬉しいなと思いました。 関連の検索をしても非機能周りの自動実装が無かったので、私の方でログ管理のスクリプトを作成してみました。 概要 EC2起動時にユーザーデータを実行し、以下を設定します。 なお、前提としてWindowsServerであること、CloudWatchAgentをインストールするためインターネットやモジュール取得先のS3につながる環境があること、環境によっては必要なVPCエンドポイント、IAMロールが割り当てられている事が前提の条件となりますが、こちらは今回割愛します。 <CloudWatchAgent設定> ①CloudWatchAgentのインストール ②CloudWatchLogsへの出力設定(JSON) ③CloudWatchAgent起動設定 <S3イベントログアップロード設定> ①イベントログアップロードスクリプト配置 ②タスクスケジューラ設定 ③AWS Tools for PowerShellのインストール 概要図はこちらです ログはいずれもSystem,Application,Securityログを送信します。 やってみましょう 今回設定するユーザーデータスクリプトはこちらです。 スクリプト内の以下6所は要件に合わせて任意で設定いただく箇所になります。 ・ロググループ名 ・スクリプト配置パス ・任意のプレフィックス ・任意のプレフィックス ・アップロード先S3バケット名 ・タスク実行時間 # タイムゾーンを東京に設定 Set-TimeZone -Id "Tokyo Standard Time" # CloudWatch Agentのダウンロード Invoke-WebRequest -Uri "https://amazoncloudwatch-agent.s3.amazonaws.com/windows/amd64/latest/amazon-cloudwatch-agent.msi" -OutFile "amazon-cloudwatch-agent.msi" # MSIパッケージのインストール Start-Process msiexec.exe -Wait -ArgumentList "/i amazon-cloudwatch-agent.msi /qn" # # 5回までフォルダの存在を確認する # for ($i = 0; $i -lt 5; $i++) { # # フォルダの存在を確認 # if (Test-Path -Path "C:\ProgramData\Amazon\AmazonCloudWatchAgent\Configs") { # break # } else { # Start-Sleep -Seconds 5 # } # } # CloudWatch Agent設定をJSONとして保存 $configJson = @' { "agent": { "metrics_collection_interval": 60, "run_as_user": "System" }, "logs": { "logs_collected": { "windows_events": { "collect_list": [ { "event_name": "System", "event_levels": ["ERROR", "WARNING", "INFORMATION"], "log_group_name": "/nabe/tokyo/ec2/system", "log_stream_name": "{local_hostname}_{instance_id}" }, { "event_name": "Application", "event_levels": ["ERROR", "WARNING", "INFORMATION"], "log_group_name": "/nabe/tokyo/ec2/application", "log_stream_name": "{local_hostname}_{instance_id}" }, { "event_name": "Security", "event_levels": ["ERROR", "WARNING", "INFORMATION", "CRITICAL"], "log_group_name": "/nabe/tokyo/ec2/security", "log_stream_name": "{local_hostname}_{instance_id}" } ] } } }, "metrics": { "metrics_collected": { "Memory": { "measurement": [ {"name": "% Committed Bytes In Use", "unit": "Percent"} ], "metrics_collection_interval": 60 }, "LogicalDisk": { "measurement": [ {"name": "% Free Space", "unit": "Percent"} ], "metrics_collection_interval": 60, "resources": ["*"] } }, "append_dimensions": { "InstanceId": "${aws:InstanceId}", "InstanceType": "${aws:InstanceType}" } } } '@ # 設定ファイルの保存 [System.IO.File]::WriteAllText("C:\ProgramData\Amazon\AmazonCloudWatchAgent\Configs\Config.json", $configJson) # CloudWatch Agentの起動と設定 & "C:\Program Files\Amazon\AmazonCloudWatchAgent\amazon-cloudwatch-agent-ctl.ps1" -a fetch-config -m ec2 -s -c file:"C:\ProgramData\Amazon\AmazonCloudWatchAgent\Configs\Config.json" Start-Sleep -Seconds 10 & "C:\Program Files\Amazon\AmazonCloudWatchAgent\amazon-cloudwatch-agent-ctl.ps1" -a start # エージェントのステータス確認とログ出力 & "C:\Program Files\Amazon\AmazonCloudWatchAgent\amazon-cloudwatch-agent-ctl.ps1" -a status | Out-File -FilePath "C:\CloudWatchAgent\agent-status.txt" # スクリプトディレクトリの作成 New-Item -ItemType Directory -Force -Path "C:\infra\script" # S3転送スクリプトの作成 $s3TransferScript = @' # 日付とホスト情報定義 $hostName = [System.Net.Dns]::GetHostName() $date = (Get-Date).AddDays(-1).ToString("yyyyMMddHHmmss") $zipFileName = "任意の名前" -f $hostName, $date # S3のバケット名とプレフィックス $bucketName = $args[0] $prefix = "任意のプレフィックス" -f $hostName # 抽出対象イベントログの名前定義 $eventLogNames = "System", "Application", "Security" # スクリプト名定義 $scriptName = $MyInvocation.MyCommand.Name # イベントソースが存在するかどうかを確認する if (-not [System.Diagnostics.EventLog]::SourceExists($scriptName)) { # イベントソースが存在しない場合、新しく作成する New-EventLog -LogName Application -Source $scriptName } # スクリプトの開始ログを出力する Write-EventLog -LogName Application -Source $scriptName -EntryType Information -EventId 100 -Message "[Info 001]: Script started." # イベントログをエクスポートしてzip化処理 ForEach ($logName in $eventLogNames) { Try { $fileName = "{0}-{1}.txt" -f $logName, $date Get-WinEvent -FilterHashtable @{ LogName=$logName; StartTime=(Get-Date).AddDays(-1).Date; EndTime=(Get-Date).Date } | Format-Table -AutoSize -Wrap > $fileName Compress-Archive -Path $fileName -Update -DestinationPath $zipFileName Remove-Item $fileName } Catch { $errorMsg = "[Error 001]: Script failed while exporting and zipping event logs. Error details: $_" Write-Error $errorMsg Write-EventLog -LogName Application -Source $scriptName -EntryType Error -EventId 1 -Message $errorMsg Break } Finally { Write-EventLog -LogName Application -Source $scriptName -EntryType Information -EventId 101 -Message "[Info 002]: Exporting and zipping event logs completed." } } # AWS S3にZIPファイルをアップロード Try { Write-S3Object -BucketName $bucketName -File $zipFileName -Key $prefix/$zipFileName } Catch { $errorMsg = "[Error 002]: Script failed while uploading the ZIP file to S3. Error details: $_" Write-Error $errorMsg Write-EventLog -LogName Application -Source $scriptName -EntryType Error -EventId 2 -Message $errorMsg } Finally { Write-EventLog -LogName Application -Source $scriptName -EntryType Information -EventId 102 -Message "[Info 003]: Uploading the ZIP file to S3 completed." Remove-Item $zipFileName } Write-Host "ZIP file has been uploaded to S3." Write-EventLog -LogName Application -Source $scriptName -EntryType Information -EventId 103 -Message "[Info 004]: Script completed." '@ # スクリプトの保存 [System.IO.File]::WriteAllText("C:\infra\script\s3_log_transfer.ps1", $s3TransferScript) # タスクスケジューラーの設定 $action = New-ScheduledTaskAction ` -Execute "PowerShell.exe" ` -Argument "-ExecutionPolicy Bypass C:\infra\script\s3_log_transfer.ps1 アップロード先S3バケット名" $trigger = New-ScheduledTaskTrigger -Daily -At 2am $settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -DontStopIfGoingOnBatteries -AllowStartIfOnBatteries Register-ScheduledTask ` -TaskName "S3EventLogTransfer" ` -Action $action ` -Trigger $trigger ` -Settings $settings ` -User "System" ` -RunLevel Highest ` -Force # AWS Tools for PowerShellのインストール Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force Install-Module -Name AWS.Tools.Common -Force Install-Module -Name AWS.Tools.S3 -Force 周辺環境のデプロイが手間なので、今回私はAWS CDKでデプロイしますが、EC2作成時にユーザーデータ欄に以下のように<powershell></powershell>と挟んでスクリプトをコピペしてください。 動作確認 まずは、CloudWatchLogsからです、SYSTEMログとSECURITYログは割愛しますが、以下のようにデプロイ直後から出力されています。           続いて、イベントログS3アップロードです。 以下のようにEC2のタスクスケジューラにタスクがしっかり登録されていました。 では、強制的に実行してみましょう。 以下のようにイベントログ(Application)に途中経過が出力されます。 S3にログがアップロードされていることを確認できました。 今回は以上となります。 少しでも皆様のお力になれれば幸いです。 次回はAWS CDK周りの案件に合わせた実装の記事を執筆したいと思います。
アバター
みなさん、こんにちは!SCSK 池田です。 HAクラスタ製品である「LifeKeeper」ですが、何かあった場合の保険の意味合いが強いだけに、フェイルオーバが起きてしまった場合のサポートレベルはとても気になるところですよね。 今回は、そんな「LifeKeeper」のサポートの種類について解説したいと思います。 LifeKeeperのサポートにはいろいろな種類があります 一般的に「サポートの種類」と聞いてパッと思い浮かぶものとして、「平日日中サポート」と「24時間365日サポート」が挙げられると思います。「LifeKeeper」の場合も同じように「平日日中サポート」と「24時間365日サポート」がありますが、特に24時間365日サポートにはいくつかのバリエーションがあり、少々解り辛いところがあります。 LifeKeeperのサポート種類 ● 標準サポート ・・・平日日中サポート(平日 9時~17時半)と呼ばれるもの ● Premiumサポート ・・・24時間365日サポート ※シビリティ1に該当する場合の障害復旧支援 ● 拡張Premiumサポート ・・・24時間365日サポート ※シビリティ1もしくはシビリティ2に該当する場合の障害復旧支援 ● 拡張Premium++サポート ・・・24時間365日サポート ※拡張Premiumサポートに、4時間以内の回答をSLAとして付け加えた障害復旧支援 メンテナンス期間が終了したLifeKeeper製品バージョンであっても、メンテナンスサポートの範囲でサポートサービスを提供 ご覧のように24時間365日サポートには、3種類のレベルの異なるサポートがあります。下にいくほどサポートレベルが手厚くなるのが特徴です。 シビリティとは? 先述の説明に書かれている「 シビリティ 」ってなんだろうと思われたかたも多いと思います。 シビリティ とは、 重大度 を示す指標のことです。 シビリティの 数値が小さい方が「重大度が高い」 と捉えていただければと思います。 サイオステクノロジー社の定義するシビリティは以下のようになります。 シビリティ1 ・・・LifeKeeperにより保護され正常稼動していたシステムおよびサービスにおいて、 LifeKeeperで定義したリソースが正常に起動できないために、アプリケーションがサービスを提供できない状態を指します。          シビリティ2 ・・・お客様のシステムが提供するサービスは維持されていながらも、その能力が大幅に低下している状態を指します。 またLifeKeeperが提供する機能に大幅な制限が加わっている状態も含みます。 ちょっと堅苦しいので、もう少し実例を交えてご説明すると、 シビリティ1 ・・・本番稼働中のシステムで、何らかの問題が発生したもののフェイルオーバできず システムの提供ができない状態 ※但し計画メンテナンス中に発生した障害は除く          シビリティ2 ・・・本番稼働中のシステムで、何らかの問題が発生し、フェイルオーバの後にシステムの提供はできるものの、 可用性が 失われている状態 (1台のみでシステム提供している状態) となります。 これらの意味するところは何かというと、 24時間365日サポートは、重大な障害が発生している時にのみ利用できる 逆をいうと、 障害が発生していないときは平日日中のサポートレベルになる ということです。 この点は抑えておいていただきたポイントとなります。 LifeKeeper製品サポート内容 表にまとめるとこんな感じになります。   標準サポート Premiumサポート 拡張Premiumサポート 拡張Premium++サポート メール対応 平日9:00~17:30 土日祝祭日年末年始を除く ● ● ● ● 電話対応 (フリーダイアル) 24時間365日 × ● シビリティ1 ● シビリティ1 シビリティ2 ● シビリティ1 シビリティ2 4時間以内返信 (平日9:00~17:30) × × × ● ライフサイクル延長 (Ver制限あり) (※) (※) (※) ● ライフサイクル超延長 (Ver制限なし)  ×  ×  × ● (※)拡張Premium++サポートのみライフサイクル延長が付属しています。それ以外のサポートを購入している場合は、別途、延長サポートを購入いただければライフサイクル延長ができます。   まとめ 今回は、LifeKeeperのサポートの種類について解説しました。お客様の求めるRTO(目標復旧時間)に見合ったサポートレベルを適切に選択するにあたって、本記事を参考にしていただければと思います。 最後に纏めます ・LifeKeeperには、4つのサポートの種類がある ・シビリティという考え方がある ・シビリティレベルによっては、24時間365日のサービスが受けられないことがあるので注意が必要 ・最上位(拡張Premium++サポート)では、問い合わせから4時間以内の回答というサポートレベルが付加される
アバター
SCSKの畑です。 前回エントリの後編として今回はバックエンド側のサービスを対象に解説します。 前回の記事はこちら サーバレスアーキテクチャにおける Web アプリケーションの実装事例(前編) 案件事例に基づく、サーバレスアーキテクチャにおける Web アプリケーションの実装の概要について2回に分けて説明します。前編はフロントエンド側です。 blog.usize-tech.com 2025.02.07 アーキテクチャ図 いつものやつです。図内に注釈もあるので、各サービスの役割は何となくご理解いただけるものかと思います。   バックエンド側のサービス 以下の項目がバックエンド側のサービスになります。Lambda/S3 についてはバックエンド側でも活用しているので両方に出てきます。また、Redshift については本アプリケーションでの管理対象という位置づけのため、本エントリでは直接的には触れません。Redshift に関するネタは幾つかありそうなので、またその際に。 AWS Amplify AWS AppSync Amazon Cognito Amazon DynamoDB AWS Lambda Amazon S3 なお、AppSync/Amplify/Cognito あたりは他のエントリでも色々な内容を取り上げていることもあり、重複するような内容は適宜割愛しています。このため、全般的に本アプリケーションにおけるサービスの使用理由や所感などが中心の内容となっており、前編とは若干様相が異なりますがご了承ください。(詳細な内容に踏み込むと分量が多くなってしまい、バランスが悪くなるので・・)   AWS Amplify これまでのエントリで何回もメイントピックとして取り上げていますが、具体的な構成管理対象は10回目のエントリに記載した通り AppSync と DynamoDB の2つです。実質的にはほぼ GraphQL Transformer を使って AppSync を構成・デプロイするためだけに使用したと言ってよく、その用途だと自然にこの2つが対象になりました。Lambda や Cognito を管理対象としていないのも同エントリの通り、細かい設定ができない&お客さん環境で Amplify を使用できないためです。 正直なところ、今回のような使用方法だと、GraphQL Transformer なり codegen で Javascript/Typescript 用のクライアント SDK を生成する機能がローカルから使用できれば十分だったのですが、調べた限りはそのように使用できそうにはありませんでした。普通は CI/CD 機能までセットで使用することになるというか、それをメリットと感じて使用するケースが多いと思うので、上記のように使いたいというモチベーション自体が珍しいのだとは思いますが・・   AWS AppSync こちらに至ってはほぼ毎回のエントリで何かしら取り上げていますが、一部ビジネスロジックを含むアプリケーションのバックエンド処理をほぼ全て任せるような使い方をしています。概要図の通り、DynamoDB は 一部処理を除いて(Amplify によって構成した)AppSync のリゾルバにより直接やり取りしていますが、それ以外のサービス(S3/Redshift)については全て Lambda 経由でやり取りしています。 理由は単純で、AppSync が対応していないデータソースであり、自前でそれらの HTTP エンドポイントとやり取りするようなリゾルバを実装するのは、開発時点でのスキルやコストを鑑みるとハードルが高いと感じたためです。今振り返っても、Lambda 上で実装している処理の内容的に、AppSync のリゾルバ側に移管できる内容は数割程度かなと思います。例えば、Cognito からユーザ/グループ情報を取得してくるような処理などは実装できると思いますが、Redshift とのデータやり取りになってくると Lambda レイヤー経由で様々なライブラリを使用していたりするので、おそらく実装は無理だろうなと。機能的に AppSync JS がもっと充足してくればあるいはという感じでしょうか。 リゾルバーおよび関数の AWS AppSync JavaScript ランタイム機能 - AWS AppSync AWS AppSync の JavaScript リゾルバーユーティリティヘルパーについて説明します。 docs.aws.amazon.com というか、RDS に対応してるなら Redshift にも対応してくれてもいいのにと思わなくもない・・のですが、DWH (Redshift) とのやり取りとなると一般論としてデータ量が大きくなることが想定されることを考えると、AppSync の特徴や用途に適さないために対応していないということなのかなと。。   Amazon Cognito 本アプリケーションのユーザ/グループを管理するために使用しています。詳細は 2回目のエントリ で説明していますが、本アプリケーションを使用するためには Cognito によるユーザ認証・認可が必要であり、ユーザプールに紐付けた ID プールの「認証されたアクセス」経由で付与される IAM ロールの権限を以ってアプリケーションを使用します。 こちらは正直あまり書くことがないので余談寄りの話題になりますが、当初はユーザ/グループのメンテナンスに際してアプリケーション側に管理画面を作る想定はありませんでした。ただ、思ったより AWS マネジメントコンソールの画面が使いづらいところがあり、手順に落としてもやや冗長な内容になってしまうことから、お客さんからの要望もあってアプリケーション側で管理画面を作ることになりました。設定手順自体はさておき、運用管理を考えるとユーザ一覧における任意項目でのソートとか、ユーザ一覧のエクスポートくらいはできても良いんじゃないかと思ったりします。(検索はできますが・・)   Amazon DynamoDB アプリケーション用のデータベースとして使用しています。概要図にも記載がありますが、具体的な用途としては以下3つです。前者2つの用途は、本アプリケーションにおけるメンテナンス対象の Redshift テーブル、及び実行対象の ETL/ELT ジョブネット(ステートマシン)をターゲットとしています。 セッション/ステータス管理 オペレーションログ出力 アプリケーションマスタ(設定) 排他制御に関するエントリでも少し言及した通り、DynamoDB 自体を積極的に採用したというよりは、Amplify/AppSync と連携して容易に開発ができ、かつ一般的に RDBMS で備えているような機能性を持つサーバレスサービスである、という特徴から選んだような感じです。もちろん DynamoDB は本質的に KVS (Key Value Store) であり、Oracle や PostgreSQL などの RDBMS とは色々な相違点がありますが、上記のような用途においては KVS で十分でした。 最も今振り返ると、結果的にそうだった部分もあるのは否めないところです。特にテーブルの論理設計については、広野さん執筆の以下エントリなどを事前に読んでおくべきだったかもしれません。このあたりは開発時の経緯などもあるのですが、実装しながら設計を拡張していけるのが KVS/スキーマレスの良いところでも悪いところでもあると実感できました。幸いにも、本アプリケーションの実装においては良いところを享受できた割合の方が大きかったと思っています。 Amazon DynamoDB のテーブル設計で悩んだら最初に読もう -これだけ知ればある程度の検索には対応できる- Amazon DynamoDB のテーブルは構築前の設計が重要です。本記事では、設計するにあたり知っておくべき検索仕様を紹介します。 blog.usize-tech.com 2022.07.05 一方、初めて本格的に扱ったサービスであるということもありちょこちょこ躓いた点もあったため、以下にまとめてみました。本当に大した内容ではないというか、本当に初学者のような内容ですが・・ パーティションキーの定義変更は不可能であり、変更するためにはテーブルの再作成が必要なこと パーティションキーに指定している列の名前を変更したかったのですが、それもパーティションキーの定義に当然ながら含まれるということで再作成が必要となりました。今考えると DynamoDB のテーブル作成時にパーティションキーを指定する必要がある時点で、何となくそういう仕様になっていることは察せられた気もします。 1項目あたりの最大データ量が 400KB であること 実装最初期はメンテナンス対象のテーブルデータ自体を DynamoDB の項目として保持するような設計としていたのですが、この制限に引っかかったため断念しました。今振り返るとあまり筋の良い設計ではなかったので、早々に方針を転換できたことは良かったと思っています。仮に数 MB 程度だった場合はそのまま進めていたかもしれません・・   AWS Lambda AppSync の項目で記載した通り、主に Redshift/Cognito とのやり取りする機能の開発に使用しています。ランタイムは実装コストを重視して慣れている Python としました。特に Redshift とのやり取りにおいて Pandas ライブラリを使用したかったというのも理由の一つです。 詳細な内容については Lamdba ごとの各論になってしまう部分がありますが、今回の構成のように AppSync のデータソースとしての用途では思ったより大分扱いやすかったです。AppSync からの入力がどのように event に入ってくるかを調べるのが少し手間だった程度で、出力については GraphQL のスキーマ定義通りに入れてあげればそのまま AppSync 側で受け取ってくれるのでラクでした。Lambda 側での例外発生時も、例外をそのまま AppSync に渡すだけであれば特別な工夫は不要でした。(Lambda から例外を raise すれば、それをそのままフロントエンド側にも渡してくれる) 逆に、Lambda をオンライン機能のバックエンド処理用として扱う場合によく問題として挙げられる、コールドスタート発生時のレスポンス悪化も実感したところです。本アプリケーションの用途を鑑みると大きな問題ではありませんが、回避策が実質的な常時起動 (Provisioned Concurrency) となってしまうのはサーバレスの思想/メリットから遠ざかってしまうという点で悩ましいですね。。 関数に対するプロビジョニングされた同時実行数の設定 - AWS Lambda Lambda での 同時実行 は、関数が現在処理している未完了のリクエストの数です。利用できる同時実行コントロールには、次の 2 種類があります。 docs.aws.amazon.com もし別案件で Amplify を使用しない方針とした場合は、ダイレクト Lambda リゾルバについても試してみたいところです。   Amazon S3 バックエンド側では、Redshift ⇔ アプリケーション間のテーブルデータのやり取りにおけるデータの(一時)保管場所として使用しています。つまり、実質的にアプリケーションが読み書きするのは S3 上のデータとなり、 7回目のエントリ でも触れたように AppSync/Lambda を通してアプリケーション ⇔ S3 ⇔ Redshift 間でデータのやり取りをするようなアーキテクチャとなっています。 当初は S3 を間に挟む予定はなかったのですが、以下の理由によりこのような構成としました。 前編で記載した以下のアプリケーション機能を実装するにあたり、テーブルデータを保持しておく場所が必要だったため アプリケーションのメンテナンス対象テーブルの(データ)バージョン管理機能 アプリケーション経由でテーブルデータを更新する際の簡易ワークフロー機能 Redshift のテーブルデータを COPY 文で更新するにあたり(厳密には truncate -> insert)、COPY 対象のデータを S3  などの外部データストアに配置する必要があったため https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/r_COPY.html#r_COPY-syntax-overview-data-source もちろん、上記理由のみだと他の選択肢も取れたのですが、全体としての扱いやすさやお客さん環境での作成におけるハードルの低さも鑑みて S3 を使用することにしました。結論としては良い選択ができたと思っています。 以降、テーブルデータをどう保持しているかや、どのような設計思想でメンテナンス機能を実装したのかをつらつら書いていたのですが、思った以上に分量が多くなってしまいそうだったので、こちらの内容もまとめて別エントリのネタにさせていただこうと思います。。   まとめ バックエンド側の話は別エントリのネタとして適宜消化していることもあり、そんなに書けることないかなと思っていたのですが思っていた以上に捻り出せました。アーキテクチャとしては最初からある程度固まっていたように思えていたのですが、振り返ってみると色々あったなあというのが正直な感想です。 本記事がどなたかの役に立てば幸いです。
アバター
こんにちは、SCSKの茂木です。 前回ご紹介したAWSマルチリージョンにおける高可用性方式で記事に書ききれなかったルーティング切替の実装について、 今回は詳しく解説していきます。 AWSマルチリージョンにおける高可用性方式を実装してみる AWS Transit Gatewayを用いたマルチリージョンにおける高可用性方式を実装してみます。 blog.usize-tech.com 2025.01.17   背景と目的 前回の記事でも触れていましたが、マルチリージョンでの冗長化構成においては 障害や災害が発生した際のダウンタイム最小化 が重要なポイントの1つとなります。 ダウンタイムを最小化するためにはHAクラスタソフトでデータやミドルウェアを冗長化しておくことと合わせて、 障害や災害の発生時にクライアント通信がセカンダリサーバに向くようルーティングを切り替える仕組みが必要となります。 実装方針 ダウンタイム最小化のためルーティング切替は自動で行えるようにします。 切替の流れは以下を想定しており、AWS CLIを使用したスクリプトでルーティングの切替を行います。 ①障害または災害が発生しプライマリサーバがダウン ②ダウンを検知し、セカンダリサーバでスクリプトをキック ③スクリプト内でルートテーブルのエントリ差し替えを行い、仮想IPをセカンダリサーバまでルーティング ④セカンダリサーバでサービス継続 ルーティングの切替は稼働しているサービスのフェイルオーバーと合わせて行う必要があります。 非同期での切り替わりによって意図しない挙動になる可能性があります。 環境設定 設定項目 説明 AWS CLIインストール ドキュメントを参考にEC2全台にAWS CLIをインストールします。 AWS CLIの最新バージョンのインストールまたは更新 - AWS Command Line Interface AWS CLI をシステムにインストールまたは更新する手順。 docs.aws.amazon.com IAMポリシーの作成と権限の割り当て ルートテーブルとTransitGateway ルートテーブルに関する許可ポリシーを作成し、EC2に権限を割り当てます。 今回は以下4つのアクションを許可しました。 “ec2:CreateRoute” “ec2:DeleteRoute” “ec2:CreateTransitGatewayRoute” “ec2:DeleteTransitGatewayRoute” AWS CLIの設定(任意) スクリプト実行ユーザに応じてaws configureを適宜設定ください。   ルーティング切替実装 スクリプト概要 今回はbashスクリプトを使用しました。 東京-大阪間の双方で切替が必要になるため実行したEC2のリージョンでルーティングの方向を決めています。 ルートテーブルやリージョンの数に応じて分岐や処理を書き換える必要があります。 以下の記事を参考にしています。 サブネットのルートテーブルを変更する - Amazon Virtual Private Cloud VPC サブネットのルートテーブルの関連付けを理解し、カスタムのルートテーブルを含むリソースの VPC ルーティングを管理します。リソース間のトラフィックを誘導し、安全な接続を有効にします。 docs.aws.amazon.com Amazon VPC Transit Gateway を使用して静的ルートを作成する - Amazon VPC Amazon で VPC、VPN、または Transit Gateway ピアリングアタッチメントの静的ルートを作成しますVPC。 docs.aws.amazon.com スクリプト詳細 #!/bin/bash rm -rf ~/.aws/cli/cache # EC2メタデータサービスからアベイラビリティゾーンを取得する availability_zone=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone) # アベイラビリティゾーンからリージョンを抽出(末尾の文字を取り除く) current_region=${availability_zone::-1} echo "現在のAWSリージョン: $current_region" # リージョンを確認して処理を切り替える if [ "$current_region" == "ap-northeast-3" ]; then     echo "大阪リージョンです。特定の処理を実行します。"   # rtb-oya-public(tokyo)   aws ec2 delete-route \       --route-table-id rtb-0cf7a64e23924df35 \       --destination-cidr-block 20.1.1.1/32 \         --region ap-northeast-1   aws ec2 create-route \       --route-table-id rtb-0cf7a64e23924df35 \       --destination-cidr-block 20.1.1.1/32 \       --gateway-id tgw-0068638c0345e3bf5 \         --region ap-northeast-1   # rtb-oya-private(tokyo)   aws ec2 delete-route \       --route-table-id rtb-0b947251e47673818 \       --destination-cidr-block 20.1.1.1/32 \         --region ap-northeast-1   # tgw-rtb-tokyo-oya(tokyo)   aws ec2 delete-transit-gateway-route \       --destination-cidr-block 20.1.1.1/32 \       --transit-gateway-route-table-id tgw-rtb-033d37d8d27c1a288 \         --region ap-northeast-1   aws ec2 create-transit-gateway-route \       --destination-cidr-block 20.1.1.1/32 \       --transit-gateway-route-table-id tgw-rtb-033d37d8d27c1a288 \       --transit-gateway-attachment-id tgw-attach-08df5bd6f7102ce5d \         --region ap-northeast-1   # tgw-rtb-tokyo-osaka-oya(tokyo)   aws ec2 delete-transit-gateway-route \       --destination-cidr-block 20.1.1.1/32 \       --transit-gateway-route-table-id tgw-rtb-0903b5f96664d99ad \         --region ap-northeast-1   # tgw-rtb-osaka-tokyo-oya(osaka)   aws ec2 create-transit-gateway-route \       --destination-cidr-block 20.1.1.1/32 \       --transit-gateway-route-table-id tgw-rtb-08f3755f4400837a5 \       --transit-gateway-attachment-id tgw-attach-0734a75c43c8dedd9 \         --region ap-northeast-3   # tgw-rtb-osaka-oya(osaka)   aws ec2 create-transit-gateway-route \       --destination-cidr-block 20.1.1.1/32 \       --transit-gateway-route-table-id tgw-rtb-0a005c5db6c404746 \       --transit-gateway-attachment-id tgw-attach-0734a75c43c8dedd9 \         --region ap-northeast-3   # rtb-oya-private(osaka)   aws ec2 create-route \       --route-table-id rtb-05b5fd18535d843ca \       --destination-cidr-block 20.1.1.1/32 \       --network-interface-id eni-082d40701b0d371ec \       --region ap-northeast-3 else   echo "東京リージョンです。通常の処理を実行します。"   # rtb-oya-public(tokyo)       aws ec2 delete-route \           --route-table-id rtb-0cf7a64e23924df35 \           --destination-cidr-block 20.1.1.1/32 \           --region ap-northeast-1   aws ec2 create-route \       --route-table-id rtb-0cf7a64e23924df35 \       --destination-cidr-block 20.1.1.1/32 \       --network-interface-id eni-06102ba74656de21a \         --region ap-northeast-1   # rtb-oya-private(tokyo)   aws ec2 create-route \       --route-table-id rtb-0b947251e47673818 \       --destination-cidr-block 20.1.1.1/32 \       --network-interface-id eni-06102ba74656de21a \         --region ap-northeast-1   # tgw-rtb-tokyo-oya(tokyo)   aws ec2 delete-transit-gateway-route \       --destination-cidr-block 20.1.1.1/32 \       --transit-gateway-route-table-id tgw-rtb-033d37d8d27c1a288 \         --region ap-northeast-1   aws ec2 create-transit-gateway-route \       --destination-cidr-block 20.1.1.1/32 \       --transit-gateway-route-table-id tgw-rtb-033d37d8d27c1a288 \       --transit-gateway-attachment-id tgw-attach-0da655bb73febede7 \         --region ap-northeast-1   # tgw-rtb-tokyo-osaka-oya(tokyo)   aws ec2 create-transit-gateway-route \       --destination-cidr-block 20.1.1.1/32 \       --transit-gateway-route-table-id tgw-rtb-0903b5f96664d99ad \       --transit-gateway-attachment-id tgw-attach-0da655bb73febede7 \         --region ap-northeast-1   # tgw-rtb-osaka-tokyo-oya(osaka)   aws ec2 delete-transit-gateway-route \       --destination-cidr-block 20.1.1.1/32 \       --transit-gateway-route-table-id tgw-rtb-08f3755f4400837a5 \         --region ap-northeast-3   # tgw-rtb-osaka-oya(osaka)   aws ec2 delete-transit-gateway-route \       --destination-cidr-block 20.1.1.1/32 \       --transit-gateway-route-table-id tgw-rtb-0a005c5db6c404746 \         --region ap-northeast-3   # rtb-oya-private(osaka)   aws ec2 delete-route \       --route-table-id rtb-05b5fd18535d843ca \       --destination-cidr-block 20.1.1.1/32 \         --region ap-northeast-3 fi echo "All routes have been created successfully." 補足(LifeKeeperへの組み込み) LifeKeeperのGenericARKを利用することでプライマリサーバのダウン検知をトリガーにスクリプトをキックすることができます。 [Linux] GenericARK開発ガイドとサンプルスクリプト – SIOS LifeKeeper/DataKeeper User Portal
アバター
はじめに 皆さん初めまして。長谷川です。 1本目の投稿になります。 突然ですが、皆さんはAWSのリソース構築をどのように行っていますか? AWS CloudFormationをはじめ様々なIaCツールが活用できるAWSですが、上手く活用できておりますでしょうか? かくいう私も細かな検証作業などではマネコンからポチポチっとやってしまうことが多く、中々活用できていないと感じています。 今回は案件での活用も増えている「AWS CDK」を利用したリソース構築について着目したいと思います。 具体的にはCDKコンストラクトの特徴について焦点を当てて見ていきます。   AWS CDKとは? AWS公式サイトで確認すると以下のような記述になっています。 AWS Cloud Development Kit (AWS CDK) は、コードでクラウドインフラストラクチャを定義して AWS CloudFormation を通じてプロビジョニングするオープンソースのソフトウェア開発フレームワークです。 — AWS CDK とは より引用 AWS CDK とは - AWS Cloud Development Kit (AWS CDK) v2 AWS Cloud Development Kit (AWS CDK) は、コードでクラウドインフラストラクチャを定義して AWS CloudFormation を通じてプロビジョニングするオープンソースのソフトウェア開発フレームワークです... docs.aws.amazon.com TypeScript、JavaScript、Python、Java、C#/.Net、Goといったプログラミング言語でクラウドコンポーネントを定義できることがAWS CDKの大きな特徴ですが、実際に裏で動くのはAWS CloudFormationであることも大事なポイントですね。 リソース作成失敗時の自動ロールバックやリソース設定の不整合を防ぐドリフト検出といったCloudFormationの強みをそのまま活用することができます。   AWS CDK コンストラクト 本記事ではAWS CDKを利用する強みの一つである「抽象化」を支えるAWS CDK コンストラクトについて注目したいと思います。 こちらについてもAWS公式サイトの定義を紹介します。 コンストラクトは、AWS Cloud Development Kit (AWS CDK) アプリケーションの基本的な構成要素です。コンストラクトは、1 つ以上の AWS CloudFormation リソースとその設定を表すアプリケーション内のコンポーネントです。コンストラクトをインポートして設定することで、アプリケーションを 1 つずつ構築していきます。 — AWS CDK コンストラクト より引用 AWS CDKではコンストラクトを基にコードの記述を行うことで、リソースの定義をしていきます。 そして、コンストラクトのレベルとして次の3段階が用意されています。 コンストラクトレベル 説明( AWS CDK コンストラクト より引用) レベル1(L1) L1 コンストラクトは、 CFN リソース とも呼ばれ、最下位のコンストラクトであり、 抽象化は行いません 。各 L1 コンストラクトは、単一の AWS CloudFormation リソースに直接マッピングされます。 レベル2(L2) L1 コンストラクトと比較して、L2 コンストラクトは直感的なインテントベースの API を通してより 高度な抽象化を提供します 。L2 コンストラクトには、適切なデフォルトプロパティ設定、ベストプラクティスのセキュリティポリシー、および多くのボイラープレートコードやグルーロジックを生成する機能が含まれています。 レベル3(L3) L3 コンストラクトは、 パターン とも呼ばれ、最も高度な抽象レベルです。各 L3 コンストラクトには、アプリケーション内の特定のタスクまたはサービスを達成するために連携するように設定された、複数リソースの集合を含めることができます。L3 コンストラクトは、アプリケーション内の特定のユースケースの AWS アーキテクチャ全体を作成するために使用されます。 表内で強調している「抽象化の有無」について気になりました。 AWS CDKというと必要なパラメータのみ設定し、残りはベストプラクティスに基づき「よしなに」やってくれるものと捉えていましたが、そうではないのでしょうか? 実際にレベル1、レベル2コンストラクトのそれぞれを使用してリソース作成(S3,VPC)を試してみました。 $ cdk synth 上記のコマンドを使用することで、実際にリソースをデプロイする前にCloudFormationのテンプレートに落とし込んだ結果を確認することができます。   S3 まずはS3バケットの作成について、両者の結果の違いを示します。 実行環境は以下の通りです。 Node.js 10.9.2 TypeScript 5.7.3 AWS CDK 2.178.0 また、実行環境の構築やコードの記載には「 TypeScript の基礎から始める AWS CDK 開発入門 」を参考にしました。 まだまだTypeScriptを使用した経験が浅いので、ここで紹介されているハンズオンについても一度じっくりと試してみたいと考えています。 L1コンストラクト L1コンストラクトを使用したS3作成のTypeScriptコードは以下の通りです。 class CfnBucket (construct) import { Duration, Stack, StackProps } from 'aws-cdk-lib'; import * as s3 from "aws-cdk-lib/aws-s3"; import { Construct } from 'constructs'; export class WorkStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // s3 を宣言 const s3Bucket = new s3.CfnBucket(this, "s3-L1-Construct", { }); } } cdk synthコマンドを使用し、CloudFormationテンプレートを出力すると以下の通りです。 [Properties]の設定等は特になく、真っ新なS3バケットが一つ作成されました。 L2コンストラクト 続いてL2コンストラクトを使用してS3バケットを作成する場合です。 class Bucket (construct) import { Duration, Stack, StackProps } from 'aws-cdk-lib'; import * as s3 from "aws-cdk-lib/aws-s3"; import { Construct } from 'constructs'; export class WorkStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // s3 を宣言 const s3Bucket = new s3.Bucket(this, "s3-L2-Construct", { }); } } cdk synthコマンドを使用し、CloudFormationテンプレートを出力すると以下の通りです。 [Properties]の設定等は同じくありませんが、以下の項目が追加されているようです。 UpdateReplacePolicy: Retain DeletionPolicy: Retain これらはスタックの変更・削除時に対象のリソースを保持させるポリシーです。 誤った変更などをしてしまった際の保険になりますね。 しかし、L1とL2の違いとしてはそれほど大きくないですね。 続いてVPCの例を見てみましょう。   VPC L1コンストラクト L1コンストラクトを使用したVPC作成のTypeScriptコードは以下の通りです。 パラメータとしてCIDR情報が必須のため、その設定のみ記載しています。 import { Duration, Stack, StackProps } from 'aws-cdk-lib'; import * as ec2 from "aws-cdk-lib/aws-ec2"; import { Construct } from 'constructs'; export class WorkStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // vpc を宣言 const vpc = new ec2.CfnVPC(this, "VPC-L1-Construct", { cidrBlock: '10.0.0.0/16', }); } } cdk synthコマンドを使用し、CloudFormationテンプレートを出力すると以下の通りです。 [Properties]について確認すると、[CidrBlock]の情報のみが保持されていることが分かります。 ただVPCとしての枠だけが用意される形ですね。 結局何か作業をするには、サブネットやゲートウェイなどまだまだ要素が足りていません。 L2コンストラクト 次に、L2コンストラクトです。 なんとパラメータとして必須のものが存在しません! コードとして実行可能な最小限の記載をしています。 import { Duration, Stack, StackProps } from 'aws-cdk-lib'; import * as ec2 from "aws-cdk-lib/aws-ec2"; import { Construct } from 'constructs'; export class WorkStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // vpc を宣言 const vpc = new ec2.Vpc(this, "VPC-L2-Construct", { }); } } cdk synthコマンドを使用し、CloudFormationテンプレートを出力すると以下の通りです。 かなり長いですので、飛ばしていただいて大丈夫です。 Resources: VPCL2ConstructEE410864: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsHostnames: true EnableDnsSupport: true InstanceTenancy: default Tags: - Key: Name Value: WorkStack/VPC-L2-Construct Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/Resource VPCL2ConstructPublicSubnet1SubnetCC74A439: Type: AWS::EC2::Subnet Properties: AvailabilityZone: Fn::Select: - 0 - Fn::GetAZs: "" CidrBlock: 10.0.0.0/18 MapPublicIpOnLaunch: true Tags: - Key: aws-cdk:subnet-name Value: Public - Key: aws-cdk:subnet-type Value: Public - Key: Name Value: WorkStack/VPC-L2-Construct/PublicSubnet1 VpcId: Ref: VPCL2ConstructEE410864 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PublicSubnet1/Subnet VPCL2ConstructPublicSubnet1RouteTable88DC6194: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: WorkStack/VPC-L2-Construct/PublicSubnet1 VpcId: Ref: VPCL2ConstructEE410864 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PublicSubnet1/RouteTable VPCL2ConstructPublicSubnet1RouteTableAssociation731F896E: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: Ref: VPCL2ConstructPublicSubnet1RouteTable88DC6194 SubnetId: Ref: VPCL2ConstructPublicSubnet1SubnetCC74A439 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PublicSubnet1/RouteTableAssociation VPCL2ConstructPublicSubnet1DefaultRoute5581F2A1: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: Ref: VPCL2ConstructIGW1021B62D RouteTableId: Ref: VPCL2ConstructPublicSubnet1RouteTable88DC6194 DependsOn: - VPCL2ConstructVPCGW6FBE15DE Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PublicSubnet1/DefaultRoute VPCL2ConstructPublicSubnet1EIP1D9D41B6: Type: AWS::EC2::EIP Properties: Domain: vpc Tags: - Key: Name Value: WorkStack/VPC-L2-Construct/PublicSubnet1 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PublicSubnet1/EIP VPCL2ConstructPublicSubnet1NATGateway9B0E92D1: Type: AWS::EC2::NatGateway Properties: AllocationId: Fn::GetAtt: - VPCL2ConstructPublicSubnet1EIP1D9D41B6 - AllocationId SubnetId: Ref: VPCL2ConstructPublicSubnet1SubnetCC74A439 Tags: - Key: Name Value: WorkStack/VPC-L2-Construct/PublicSubnet1 DependsOn: - VPCL2ConstructPublicSubnet1DefaultRoute5581F2A1 - VPCL2ConstructPublicSubnet1RouteTableAssociation731F896E Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PublicSubnet1/NATGateway VPCL2ConstructPublicSubnet2Subnet9562D1FC: Type: AWS::EC2::Subnet Properties: AvailabilityZone: Fn::Select: - 1 - Fn::GetAZs: "" CidrBlock: 10.0.64.0/18 MapPublicIpOnLaunch: true Tags: - Key: aws-cdk:subnet-name Value: Public - Key: aws-cdk:subnet-type Value: Public - Key: Name Value: WorkStack/VPC-L2-Construct/PublicSubnet2 VpcId: Ref: VPCL2ConstructEE410864 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PublicSubnet2/Subnet VPCL2ConstructPublicSubnet2RouteTable24B41773: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: WorkStack/VPC-L2-Construct/PublicSubnet2 VpcId: Ref: VPCL2ConstructEE410864 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PublicSubnet2/RouteTable VPCL2ConstructPublicSubnet2RouteTableAssociation8AEA9A7C: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: Ref: VPCL2ConstructPublicSubnet2RouteTable24B41773 SubnetId: Ref: VPCL2ConstructPublicSubnet2Subnet9562D1FC Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PublicSubnet2/RouteTableAssociation VPCL2ConstructPublicSubnet2DefaultRouteC8976C2A: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: Ref: VPCL2ConstructIGW1021B62D RouteTableId: Ref: VPCL2ConstructPublicSubnet2RouteTable24B41773 DependsOn: - VPCL2ConstructVPCGW6FBE15DE Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PublicSubnet2/DefaultRoute VPCL2ConstructPublicSubnet2EIPB8510F82: Type: AWS::EC2::EIP Properties: Domain: vpc Tags: - Key: Name Value: WorkStack/VPC-L2-Construct/PublicSubnet2 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PublicSubnet2/EIP VPCL2ConstructPublicSubnet2NATGatewayEAF78599: Type: AWS::EC2::NatGateway Properties: AllocationId: Fn::GetAtt: - VPCL2ConstructPublicSubnet2EIPB8510F82 - AllocationId SubnetId: Ref: VPCL2ConstructPublicSubnet2Subnet9562D1FC Tags: - Key: Name Value: WorkStack/VPC-L2-Construct/PublicSubnet2 DependsOn: - VPCL2ConstructPublicSubnet2DefaultRouteC8976C2A - VPCL2ConstructPublicSubnet2RouteTableAssociation8AEA9A7C Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PublicSubnet2/NATGateway VPCL2ConstructPrivateSubnet1SubnetE5C7047B: Type: AWS::EC2::Subnet Properties: AvailabilityZone: Fn::Select: - 0 - Fn::GetAZs: "" CidrBlock: 10.0.128.0/18 MapPublicIpOnLaunch: false Tags: - Key: aws-cdk:subnet-name Value: Private - Key: aws-cdk:subnet-type Value: Private - Key: Name Value: WorkStack/VPC-L2-Construct/PrivateSubnet1 VpcId: Ref: VPCL2ConstructEE410864 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PrivateSubnet1/Subnet VPCL2ConstructPrivateSubnet1RouteTable9ABEBB2E: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: WorkStack/VPC-L2-Construct/PrivateSubnet1 VpcId: Ref: VPCL2ConstructEE410864 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PrivateSubnet1/RouteTable VPCL2ConstructPrivateSubnet1RouteTableAssociationAFAD682E: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: Ref: VPCL2ConstructPrivateSubnet1RouteTable9ABEBB2E SubnetId: Ref: VPCL2ConstructPrivateSubnet1SubnetE5C7047B Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PrivateSubnet1/RouteTableAssociation VPCL2ConstructPrivateSubnet1DefaultRoute592FE75C: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: Ref: VPCL2ConstructPublicSubnet1NATGateway9B0E92D1 RouteTableId: Ref: VPCL2ConstructPrivateSubnet1RouteTable9ABEBB2E Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PrivateSubnet1/DefaultRoute VPCL2ConstructPrivateSubnet2SubnetD1EAC9D8: Type: AWS::EC2::Subnet Properties: AvailabilityZone: Fn::Select: - 1 - Fn::GetAZs: "" CidrBlock: 10.0.192.0/18 MapPublicIpOnLaunch: false Tags: - Key: aws-cdk:subnet-name Value: Private - Key: aws-cdk:subnet-type Value: Private - Key: Name Value: WorkStack/VPC-L2-Construct/PrivateSubnet2 VpcId: Ref: VPCL2ConstructEE410864 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PrivateSubnet2/Subnet VPCL2ConstructPrivateSubnet2RouteTable0C21B8AE: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: WorkStack/VPC-L2-Construct/PrivateSubnet2 VpcId: Ref: VPCL2ConstructEE410864 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PrivateSubnet2/RouteTable VPCL2ConstructPrivateSubnet2RouteTableAssociation91E37B6F: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: Ref: VPCL2ConstructPrivateSubnet2RouteTable0C21B8AE SubnetId: Ref: VPCL2ConstructPrivateSubnet2SubnetD1EAC9D8 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PrivateSubnet2/RouteTableAssociation VPCL2ConstructPrivateSubnet2DefaultRoute99DCB01B: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: Ref: VPCL2ConstructPublicSubnet2NATGatewayEAF78599 RouteTableId: Ref: VPCL2ConstructPrivateSubnet2RouteTable0C21B8AE Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/PrivateSubnet2/DefaultRoute VPCL2ConstructIGW1021B62D: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: WorkStack/VPC-L2-Construct Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/IGW VPCL2ConstructVPCGW6FBE15DE: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: Ref: VPCL2ConstructIGW1021B62D VpcId: Ref: VPCL2ConstructEE410864 Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/VPCGW VPCL2ConstructRestrictDefaultSecurityGroupCustomResource76DD52E2: Type: Custom::VpcRestrictDefaultSG Properties: ServiceToken: Fn::GetAtt: - CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E - Arn DefaultSecurityGroupId: Fn::GetAtt: - VPCL2ConstructEE410864 - DefaultSecurityGroup Account: Ref: AWS::AccountId UpdateReplacePolicy: Delete DeletionPolicy: Delete Metadata: aws:cdk:path: WorkStack/VPC-L2-Construct/RestrictDefaultSecurityGroupCustomResource/Default CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: lambda.amazonaws.com ManagedPolicyArns: - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: Inline PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - ec2:AuthorizeSecurityGroupIngress - ec2:AuthorizeSecurityGroupEgress - ec2:RevokeSecurityGroupIngress - ec2:RevokeSecurityGroupEgress Resource: - Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":ec2:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - :security-group/ - Fn::GetAtt: - VPCL2ConstructEE410864 - DefaultSecurityGroup Metadata: aws:cdk:path: WorkStack/Custom::VpcRestrictDefaultSGCustomResourceProvider/Role CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E: Type: AWS::Lambda::Function Properties: Code: S3Bucket: Fn::Sub: cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region} S3Key: 7fa1e366ee8a9ded01fc355f704cff92bfd179574e6f9cfee800a3541df1b200.zip Timeout: 900 MemorySize: 128 Handler: __entrypoint__.handler Role: Fn::GetAtt: - CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0 - Arn Runtime: nodejs20.x Description: Lambda function for removing all inbound/outbound rules from the VPC default security group DependsOn: - CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0 Metadata: aws:cdk:path: WorkStack/Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler aws:asset:path: asset.7fa1e366ee8a9ded01fc355f704cff92bfd179574e6f9cfee800a3541df1b200 aws:asset:property: Code CDKMetadata: Type: AWS::CDK::Metadata Properties: Analytics: v2:deflate64:H4sIAAAAAAAA/82UTWvCQBCGf4t7lHVbU7DFWwylBEoNWjxUpEx2R11NdsPuJCLify8mfoDQU3vIaWbeHYaH2ZcJRP/5RTx2YOd7Um17mU7FYUogtxx2/htlIA6zQs5Zly14tDSzJOJJmWZaTsvUIM0PDCrQGaQ607T/sgbZkHUZZ1UhY3XOpVZulFm5Pdc5FM2QuBibdyiNXLMhuRI500U1iO7awXu9MnFRDUKlHHo/NpFDIG1N3XHkLaGoQZT6AHoDwh3s2bDeXGsA20Hx65pqizXGOmUTWxJ+QprhTb9pofdW6nrwtfmUvMbJKdym88TpCgj/3bFLyHwL/uJvGO0haQlG48LYEDqDVw81t+9chUQg1zkaOvIJels6ifMFj0pPNr8Kl5t5Ee7eE2crrdCNwCMPvUeaEqy0WR25sQrFxj9UQSD6T6Lf2Xite640pHMUkyb+AI/mB4K7BQAA Metadata: aws:cdk:path: WorkStack/CDKMetadata/Default Condition: CDKMetadataAvailable Conditions: --以下省略-- 作成されたリソースについてまとめると以下の通りです。 VPC(10.0.0.0/16) -IGWの紐付け PublicSubnet1(10.0.0.0/18) -RouteTable,EIP,NAT Gatewayの紐付け PublicSubnet2(10.0.64.0/18) -RouteTable,EIP,NAT Gatewayの紐付け PrivateSubnet1(10.0.128.0/18) -RouteTableの紐付け PrivateSubnet2(10.0.192.0/18) -RouteTableの紐付け デフォルトセキュリティグループ IAMロール -Lambda実行 Lambda Function -デフォルトセキュリティグループからインバウンド・アウトバウンドルールを削除 詳細な設定をしなくても一気にリソースが作成されるのはすごいですね! ネットワークへの接続経路が確保され、Public/Privateサブネットも用意されたため、すぐにサーバ等を立てて作業を開始することが出来そうです。 L2コンストラクトを使用することで「よしなに」色々とリソースがされることが分かりました。 まとめ AWS公式サイトにて「L1コンストラクトは、単一のAWS CloudFormationリソースに直接マッピングされます。」と記載があるように、L1コンストラクトはあくまで設定されたパラメータに忠実にリソースを作成することが分かりました。 また、L2コンストラクトを使用することで、あまりAWSに詳しくなくても簡単に必要なリソースが作成されるのは面白い点だと思いました。 しかし、今回のVPCの場合のように、「そこまで作らなくても...」と感じることもあるかもしれません。 「よしなに」やってくれるのは大変便利ですが、最低限必要なパラメータを設定するスキルは必要ですね。 ただ、やはりリソースの作成負担の軽減や変更のしやすさは大きなメリットになるかと思いますので、上手く使っていきたいですね。 最後まで読んでいただきありがとうございます。 もし参考になりましたら幸いです。
アバター
前回投稿した「OSパッチ適用を自動化」させた後、結果をメールに通知させる方法をまとめてみました。 OSパッチ適用を自動化させる方法はこちらをご参照ください。 AWS Systems Manager の Patch Manager で OS パッチ適用を自動化してみた AWS Systems Manager でパッチ適用を自動化する方法をまとめてみました。 blog.usize-tech.com 2025.01.22   アーキテクチャ 今回は赤枠内を実装してみました。 Amazon CloudWatch Logs に出力されたログから、指定した任意の文字列をトリガーに Amazon SNS 経由で適用結果をメールで通知する関数を作成します。   AWS Lambdaとは、 サーバーをプロビジョニングまたは管理することなくコードを実行することができるサービスです。 https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/welcome.html   Amazon SNSとは、 Amazon Data Firehose、Amazon SQS、AWS Lambda、HTTP、電子メール、モバイルプッシュ通知、モバイルテキストメッセージ (SMS) などのサポートされているエンドポイントタイプを使用して、クライアントが公開されたメッセージを受信することができるサービスです。 https://docs.aws.amazon.com/sns/latest/dg/welcome.html   設定手順 前回設定したOS「Windows2022」のEC2にパッチ適用した結果を、メール通知させる手順をまとめました。 1.トピックの作成 2.サブスクリプションの作成 3.Lambda関数の作成 4.Lambda サブスクリプションフィルターの作成   1.トピックの作成 Amazon SNSの左メニュー「トピック」をクリックし、「トピックの作成」をクリックします。   ▼ 詳細 「タイプ」欄は、「スタンダード」を選択します。 「名前」欄は、任意のトピック名を入力します。 「表示名」欄は、任意の表示名を入力します(表示名が実際のメール差出人の名前になります)。   以下の欄はデフォルトのままにし、「トピックの作成」をクリックします。 ▼暗号化 – オプション ▼アクセスポリシー – オプション ▼データ保護ポリシー – オプション ▼配信ポリシー (HTTP/S) – オプション ▼配信ステータスのログ記録 – オプション ▼タグ – オプション ▼アクティブトレース – オプション   2.サブスクリプションの作成 作成したトピックから、「サブスクリプションの作成」をクリックします。   ▼ 詳細 「プロトコル」欄は、「Eメール」を選択します。 「エンドポイント」欄は、通知先のメールアドレスを入力します。 以下の欄はデフォルトのままにし、「サブスクリプションの作成」をクリックします。 ▼サブスクリプションフィルターポリシー – オプション ▼Redrive ポリシー (デッドレターキュー) – オプション   しばらくすると、指定したアドレス宛に確認のメールが届くので、「Confirm Subscription」をクリックし、「Subscription confirmed!」が表示されることを確認する。 サブスクリプションのステータスが「確認済み」になっていることを確認する。   3.Lambda関数の作成 Lambdaの左メニュー「関数」をクリックし、「関数を作成」をクリックします。   ▼ 関数の作成 「一から作成」を選択します。   ▼ 基本的な情報 「関数」欄は、任意の関数名を入力します。 「ランタイム」欄は、Lambda関数で記述するプログラミング言語を選択します。 今回はPythonを選択しました。 ▼ アクセス権限 「実行ロール」欄は、適切なアクセス権が付与された任意のロールを選択します。   以下の欄はデフォルトのままにし、「関数の作成」をクリックします。 ▼ その他の構成   ▼ コード コードソースを以下のコードに修正し、「Deploy」をクリックする。 ※Deployをクリックしないと、変更が反映されないので注意してください。   import logging import json import base64 import gzip import boto3 import os logger = logging.getLogger() logger.setLevel(logging.INFO) def lambda_handler(event, context):   # CloudWatchLogsからのデータはbase64エンコードされているのでデコード   decoded_data = base64.b64decode(event['awslogs']['data'])   # バイナリに圧縮されているため展開   json_data = json.loads(gzip.decompress(decoded_data))     logger.info("EVENT: " + json.dumps(json_data))     # ログデータ取得   log = json_data['logEvents'][0]['message']   instanceid_position = log.find('PatchGroup')   instanceidtmp = log[:instanceid_position]   instanceid = instanceidtmp.replace('Patch Summary for ','')   print(instanceid)   result_position = log.find('Results')   result = log[result_position:]   print(result)   patchGroup_start = log.find('PatchGroup')   patchGroup_end = log.find('BaselineId')   patchGrouptmp = log[patchGroup_start:patchGroup_end]   patchGroup = patchGrouptmp.replace('PatchGroup          : ','')   print(patchGroup)   messagetmp = """ パッチ適用結果を通知します。 対象インスタンス・適用結果は下記となります。 〇対象インスタンス{0} 〇パッチグループ {1} 〇適用結果 {2}   """   message = messagetmp.format(instanceid,patchGroup,result)   print(message)     try:       sns = boto3.client('sns')       #SNS Publish       publishResponse = sns.publish(       TopicArn = os.environ['SNS_TOPIC_ARN'],       Message = message,       Subject = os.environ['ALARM_SUBJECT']       )     except Exception as e:       print(e)   ▼ 環境変数 「設定」タブ>「環境変数」にて、「編集」をクリックする。   「環境変数」をクリックし、以下のキーと値を入力し、「保存」をクリックする。 キー 値 ALARM_SUBJECT パッチ適用通知 SNS_TOPIC_ARN 作成したSNSトピックのARN名   4.Lambda サブスクリプションフィルターの作成 CloudWatchの左メニュー「ロググループ」をクリックし、SSMでログの出力先に設定したロググループを検索します。   「サブスクリプションフィルター」タブをクリックし、「作成」>「Lambdaサブスクリプションフィルターを作成」をクリックします。   ▼ 送信先を選択 「Lambda関数」欄は、「3.Lambda関数の作成」で作成した関数名を選択します。   ▼ ログ形式とフィルターを設定 「ログの形式」欄は、「JSON」を選択します。 「サブスクリプションフィルターのパターン」欄は、フィルターする任意の文字列を入力します。 「サブスクリプションフィルター名」欄は、任意のフィルター名を入力します。 すべての項目を設定後、「ストリーミングを開始」をクリックします。   メール通知確認 パッチ適用完了後に以下のようなメール届くことを確認します。   まとめ Lambda↔SNS間の通知機能を実装することで、パッチ適用結果の通知にかかわらずあらゆるサービスの状態をメールで受け取ることができるようになります。 皆さんのお役に立てば幸いです。
アバター
お疲れ様です。 今回で2本目の投稿となります。今年に入ってから、クラウド技術への関心を深めています。昨年度までは主にオンプレミスシステムを担当しており、クラウドに触れる機会はほとんどありませんでした。最近はクラウド技術に触れることが増えてきましたが知識不足を痛感し、取り敢えず体系的な知識を身に付けるために、AWSの資格をいくつか取得しました。 資格取得を通じて少しずつ設計の感覚がつかめてきた感じがしているので、今回はクラウド初心者の視点から考えた基本設計の目次を紹介したいと思います。「そんな考え方もあるのか」と気軽にご覧いただけるとうれしいです。 クラウド初学者が考える基本設計の目次 1. **システム全体構成図** 2. **利用サービス一覧** 3. **可用性/信頼性** – 3.1 SLA – 3.1.2 AWSサービス別SLA – 3.2 サービス提供時間 – 3.3 メンテナンススケジュール、計画停止 – 3.4 冗長構成パターン – 3.4.1 EC2インスタンス – 3.4.2 DBインスタンス – 3.4.3 ネットワーク – 3.4.4 ストレージリソース – 3.4.5 その他リソース – 3.5 ロードバランサー設計 4. **性能/拡張性** – 4.1 サイジング方式 – 4.2 性能、制限事項 – 4.2.1 EC2インスタンス – 4.2.2 DBインスタンス – 4.2.3 ネットワーク – 4.2.4 ストレージ系リソース – 4.2.5 その他リソース – 4.3 拡張方式 – 4.3.1 EC2インスタンス – 4.3.2 DBインスタンス – 4.3.3 ネットワーク – 4.3.4 ストレージ系リソース – 4.3.5 その他リソース 5. **ネットワーク/外部接続設計** – 5.1 ネットワーク構成図 – 5.2 VPC/サブネット分割設計 – 5.3 IPアドレス設計 – 5.4 インスタンスへの接続方式 – 5.5 インターネット接続方式 – 5.6 VPC外へのサービス接続方式 – 5.7 VPC間接続方式 – 5.8 オンプレミス環境への接続方式 – 5.9 DNS設計 – 5.10 ドメイン設計 – 5.11 NTP設計 – 5.12 セキュリティグループとファイアウォール設定 – 5.13 メール設計 – 5.14 通信要件一覧 6. **構成管理** – 6.1 各種イベントリ一覧 – 6.2 Config Rules設計 – 6.3 ドキュメント管理 – 6.4 コード管理 – 6.5 ソフトウェア一覧 – 6.6 ライセンス管理 – 6.7 保守管理 7. **バックアップ/リストア** – 7.1 システム毎のRTO、RPO、RLOの定義 – 7.2 バックアップ対象 – 7.3 バックアップ方式 – 7.3.1 システムバックアップ – 7.3.2 ボリュームバックアップ – 7.3.3 ファイルバックアップ – 7.3.4 DBバックアップ – 7.4 リストア方式 – 7.4.1 システムリストア – 7.4.2 ボリュームリストア – 7.4.3 ファイルリストア – 7.4.4 DBリストア 8. **セキュリティ** – 8.1 セキュリティガイドライン(前提条件) – 8.2 トラフィック制御 – 8.3 IPS/IDS設計 – 8.4 暗号化/通信データ管理 – 8.5 不正検知(Securityhub設計) – 8.6 証跡管理 – 8.7 認証管理 – 8.8 定期監査 – 8.9 パスワードポリシー – 8.10 証明書管理 – 8.11 キーペア管理 9. **監視設計** – 9.1 基本方針 – 9.2 監視対象 – 9.3 監視項目 – 9.4 ログ管理 – 9.5 ログ分析 – 9.6 監視フロー 10. **Active Directory設計** 11. **災害対策設計** – 11.1 フェールオーバー設計 – 11.2 フェールバック設計 – 11.3 災対切替訓練 12. **ジョブ設計** – 12.1 ジョブ実行方式 – 12.2 ジョブ一覧 – 12.3 ジョブ実行スケジュール 13. **管理機能設計** – 13.1 AWSアカウント設計 – 13.2 Organizations設計 – 13.3 SCP設計 – 13.4 IAM設計 – 13.5 OSユーザー設計 – 13.6 マネージメントコンソールアクセス要件 – 13.7 CLI利用条件 – 13.8 ControlTower設計 – 13.9 コスト管理 – 13.10 割引管理 14. **自動化設計** – 14.1 IaC設計 – 14.2 スクリプト設計 – 14.3 CI/CD設計 15. **命名規則** 16. **用語集**   章の順番を入れ替えたりするともっと良くなるかなと思いましたが、以上にしたいと思います。
アバター
SCSKの畑です。 今更感はあるのですが、今回はこれまでの投稿で取り上げてきた Web アプリケーションのアーキテクチャについて、改めて概要や各サービスの役割などを記載していこうと思います。正直、最初の方の投稿で先に説明しておくべきだったかもしれません・・ アーキテクチャ図 いつものやつです。図内に注釈もあるので、各サービスの役割は何となくご理解いただけるものかと思います。   アプリケーションの目的・用途について 初回のエントリ含め何回か記載していますが、改めて記載するとデータベース/ DWH 上のテーブルデータをメンテナンスすることが目的のアプリケーションとなります。本事例の場合のメンテナンス対象は Redshift となります。また、実はここで初めて言及するのですが、データメンテナンスの要件として、以下2点のような機能も実装しています。 アプリケーションのメンテナンス対象テーブルの(データ)バージョン管理機能 過去バージョンデータの参照や、バージョン間のデータ比較が可能 アプリケーション経由でテーブルデータを更新する際の簡易ワークフロー機能 更新を承認する権限を持った別ユーザによる承認を以って Redshift 側に更新が反映される また、アーキテクチャ概要図には載せていませんが、本事例においては DWH (Redshift) 上にトランザクションデータを投入・更新するための ETL/ELT ジョブネットをアプリケーション経由で実行する機能も実装しました。同ジョブネットは StepFunctions 及び Lambda で実装しており、 StepFunctions のステートマシンをアプリケーションから実行するような形式となります。 他、アプリケーション自体の運用管理機能として、メンテナンス対象テーブルの情報や実行対象 ETL/ELT ジョブネットの情報、Cognito のユーザ/グループなどをメンテナンスできる機能も用意しました。このあたりは当初のスコープにはなかったのですが、お客さんの要望を聞きつつ必要になりそうな機能をピックアップした形です。   アプリケーションのビルド・デプロイについて 大した内容ではありませんが、先にこちらを簡単に説明しておきます。 ビルドについては、Nuxt3 の場合サーバエンジン (nitro) の設定に Lambda デプロイ用のプリセットがあるため、それを指定してビルドするだけで OK です。このお手軽さは非常に魅力的でした。 デプロイについては、お客さん環境の都合上手動で行う必要がある関係で、CloudFront 配下の Lambda と S3 にそれぞれ AWS マネジメントコンソールから粛々とデプロイしています。ただ、 8回目のエントリ で説明した AppSync の手動移植と比較すると大分ラクなのが幸いでした。ただ、Web上には Serverless Framework を使用した手順が多く、手動でのデプロイ方法を調べるのに少し手間取りました。具体的にはほぼ以下サイト様の通りですが、今回の事例において少し異なる点もありましたので以下に記載しています。 Serverless Frameworkを使わず、Nuxt3でSSR構成(CloudFront + Lambda + S3)をする方法 - Qiita 対象者自分と同じAWS初心者「俺たちは感覚でAWSを触っている」状態の人バックエンドなんもわからんけどAWSへのデプロイを控えている人Next全盛の今NuxtのSSRで頑張っている人なぜや… qiita.com .output/public/ 配下のファイル一式を S3 にアップロード ビルドの度に静的コンテンツのファイル名がハッシュ値に応じたものに変わるため、デプロイ前に S3 上のファイルを削除しておく方がベターです。ファイル名は固定するような設定に変更することもできるようですが。 .output/server/ 配下のファイル一式を Lambda にアップロード アップロード操作を踏まえると一式を zip ファイルにしてしまうのが良いと思います。 Lambda のハンドラ設定については、上記のようにアップロードする前提においては index.handler のままで良いようです。当初は .output/index.mjs を実行する index.js を別途作っていたのですが、不要でした。   フロントエンド側のサービス フロントエンド側のサービスとしては、以下3つです。 Amazon CloudFront Amazon S3 AWS Lambda アーキテクチャ図に記載の通り、CloudFront 配下に S3 及び Lambda を配して、Nuxt.js (Nuxt3) による SSR (Server-Side Rendering) 構成で Web アプリケーションを動作させています。厳密には Nuxt3 における Universal Rendering を使用しているので CSR (Client-Side Rendering) との併用となっています。 ということで、各サービスの位置づけ・役割については概ね想像できるところだと思うのですが、少しだけ詳細な話も含めつつ以下に記載していきます。   Amazon CloudFront アーキテクチャ概要図の通り、クライアント端末からのアクセスを受けてアプリケーションを配信しています。サーバサイド生成(動的)コンテンツは Lambda 経由、静的コンテンツは S3 経由です。サーバレスアーキテクチャとしては標準的な構成と思いますし、CloudFront 自体の設定も特にトリッキーなところはなかったため、今回は取り上げません。 ただ、お客さんの AWS 環境で構築するにあたりセキュリティ関連の設定について幾つか考慮する必要があったため、以下にまとめました。 なお、原理的には CloudFront ではなく API Gateway でも代替できるものと思われます。当初はアプリケーションの要件的に CloudFront を持ち出すのは少々大袈裟かなと感じたこともあって API Gateway を試していたのですが、URL に API Gateway の Stage が含まれてしまうせいか上手く動かず。Nuxt 側の設定を色々と変更してみたのですがダメでした。 また、S3 を 使用せず Lambda 単体で動作させるように Nuxt の設定を変更することは可能で、Web 上にその事例も多く出てきたのですが、将来的なことを考えると S3 を使用するべきに構成しておくべきと判断し、CloudFront を使用することにしました。   WAF 設定 お客さんの環境ポリシーにより Web WAF を設定しています。 設定自体はお客さんの所管であったため詳細は割愛しますが、本アプリケーションはインターネット上に公開せず、アクセスできるネットワーク/IP アドレスのレンジを限定する必要があったため、その点については考慮する必要がありました。最も、概要図の通り CloudFront にアクセスする対象はクライアント端末のみであり、それらが属しているネットワーク内からのみアクセス可能とすれば OK だったので特に難しい要件ではなく、お客さん側のネットワーク構成も相まって特に問題なく設定できました。 なお、後編で取り上げるバックエンド側のサービスについて、こちらと同じように考えれば良いやと安易に構えていたところ、痛い目に遭いかけました。詳細については別エントリで改めて説明しようと思います。   Origin access control 設定 CloudFront の配下に配する S3 及び Lambda について、CloudFront からのアクセスのみを許可するような設定が必要でした。幸い、こちらも Web 上の情報や知見は豊富で、上記のようにいずれも Origin access control 設定により実現できました。 特に、Lambda については構成上 CloudFront からのアクセスに関数 URL 機能を使用する必要がありますが、同機能には WAF などの外部アクセス制限機能を適用できないことから、当初はお客さんの環境ポリシーに準拠せず NG が出ていました。幸い、2024/4 に Lambda の関数 URL も Origin access control をサポートするようになっていたため、その旨をお客さんに伝えてクローズと相成りました。 Amazon CloudFront が Lambda 関数 URL オリジンのオリジンアクセスコントロール (OAC) を新たにサポート aws.amazon.com   Amazon S3 CloudFront の項目で説明した通り、アプリケーションから使用する静的コンテンツを配置しています。 構成上必要となる設定も前述の Origin access control くらいで、正直特に言及することはありません。せいぜい、デプロイ時に AWS マネジメントコンソール経由だとネットワークの具合次第でアップロードに失敗することがあり、その場合のオペレーションが面倒だったくらいですが、まあこれって環境依存の話なので正直関係ないですね・・。   AWS Lambda CloudFront の項目で説明した通り、サーバサイドで動的なコンテンツを返すために使用しています。具体的には、ビルドしたアプリケーションのコード一式を Lambda にデプロイするような形となります。デプロイ手順としては先述した通り難しくはないのですが、S3 と比較すると幾つか躓いた点がありましたので備忘も含めて以下にまとめました。 先述した内容の繰り返しとなりますが、ハンドラ設定は index.handler で OK でした。 Lambda 実行ロールについて、当初はサーバサイド側で実行され得る機能に対応した権限(Cognito なり AppSync 関連)が必要になると考えていたのですが、いずれも不要でした。 サーバサイドで認可が必要な処理が行われるとしても、それを実行するための権限は 2回目のエントリ で書いたように Cognito 経由で付与されるロール側で付与されるためと理解しています。 これは余談寄りですが、アプリケーションで使用している pdf ファイル表示用のライブラリで不具合があった関係で、Node.js を 22 系に上げたかったのですが、Lambda におけるリリースタイミングが 2024/11 と思ったより遅かったため、不具合の解消にやや時間を要しました。(Node.js の 22 系リリースは 2024/4) https://aws.amazon.com/jp/about-aws/whats-new/2024/11/aws-lambda-support-nodejs-22/ このへんのスピード感(早い or 遅い)が肌感覚として良くわからないですね。リリースから半年と考えると標準的な気もしますし、Node.js の 22 系が安定版と考えるとちょっと遅いような気もしますし・・   余談など Next.js(React) ではなく Nuxt.js(Vue) を使用した理由をたまに問われるのですが、最終的には自分の好みです。特に、Vue ファイル内で HTML テンプレートライクに書けるのが、昔から HTML タグ打ちしていたような人間にとっては馴染みやすかったというのが大きかったです。それから調べた限りだと、今回のような少人数開発のアプリケーションにおいてはある意味どちらを選んでも大差ないと感じたのも理由の一つです。なので最終的に自分の好みを優先させてしまっても良さそうだな、と・・ また、今振り返ると、SSR 構成にする必要性は機能要件上はなかったかもしれません。当初はサーバサイドで重い処理をさせてクライアント側に返すようなことがアーキテクチャ上できるようにしておいた方が良いと考えていましたが、アプリケーションの特性上クライアント側で処理する内容が多い上、重い処理も結局のところは AppSync 経由で実行されるバックエンド処理用の Lambda が担うことがほとんどであったためです。とは言え、アクセス時のレスポンス向上などの効果はあったと思いますし、この構成にしたことによるアプリケーション実装上のデメリットが本アプリケーションについては正直思っていたほど出なかったこともあり、悪い選択でもなかったとは思っていますが。 ただ、CloudFront など一部サービスの構築は所管上お客さんにお願いすることになったこともあり、事前準備やリードタイムに一定の工数を要したのも事実ではありました・・   まとめ 今振り返ると、当時は自分なりに色々調べた上での判断とはいえ、ちょっと考慮が足りていなかったかもという点があるというのが正直なところです。特に SSR 構成を前提としてアーキテクチャを検討していた点については、もう少し深堀りして調査できていればなお良かったかもしれません。このあたりは次回以降の案件などで活かしていければと思っています。 そして、、思ったより長くなってしまったので、バックエンド側の話は後編に回したいと思います。 本記事がどなたかの役に立てば幸いです。
アバター
こんにちは。SCSK 池田です。 まだ2月だというのに、少し気の早い春を感じるようになってきた今日この頃ですが皆様いかがお過ごしでしょうか。 早速本日のお題です。 少し時間が経ってしまいましたが、昨年9月にリリースされたLifeKeeper for Linux v9.9.0で新機能として提供を開始した「Disaster Recovery add-on」について、本機能が求められるようになった経緯を私なりに考察してみました。 Disaster Recovery add-onの振り返り Disaster Recovery add-onについては、データ同期処理にLinbit社のDRBDというソフトウェアを利用したLifeKeeperの災害対策ソリューションの名前です。こちらのSCSK技術ブログであるTech Harmonyの中でも何回か記事にさせていただきましたので、詳細のご説明はそちらに譲ります。 第10回 DRBD × LifeKeeperの高可用性リアルタイムレプリケーションを探る – TechHarmony AWSマルチリージョンにおける高可用性方式を実装してみる – TechHarmony 災害対策ということですので、地理的に離れた箇所にサーバを配置する必要があり、AWSの場合は、リージョンを跨いだレプリケーションを実現する必要があります。 データレプリケーションしたいならDataKeeperでは!? LifeKeeperを扱ったことのある人の中にはご存じのかたもいらっしゃるかと思いますが、サーバのローカルディスク間をデータレプリケーションするソリューションとして「DataKeeper」という製品があります。 オンプレミス環境においては、比較的高価な共有ディスクを買わずとも、サーバに内蔵されたディスクを安価にレプリケーションすることができる為、とても重宝されてきました。 AWSやAzureといったパブリッククラウド環境においては、そもそも共有ディスクを構成できなかったり、できたとしても様々な制約があり構成することが難しいケースの方が大半なことから、EC2インスタンスに紐づけたEBSボリュームをDataKeeperを用いてレプリケーションする構成が主流となっています。 それならば今回のようなAWSのリージョンを跨いだ災害対策目的の構成でも、DataKeeperが使えるのではと思った方も多いと思います。 結論からお伝えすると、 DataKeeperは、リージョンを跨いだデータレプリケーションには不向き なんです! その理由をご説明していきましょう。 DataKeeperがリージョン跨ぎ構成に向かない理由 非同期レプリケーションが使えない リージョン跨ぎの場合、どんなに低レイテンシの回線だと言われても、どうしても回線速度がネックになってしまいます。同期レプリケーション(リアルタイムレプリケーション)とした場合、DRサイト側のディスクに書き込みが完了しないと、稼働系で動作しているアプリケーションに対してI/O完了となりません。その為、回線速度の影響をまともに受けてしまい、レスポンスの低下に繋がってしまいます。 この問題は、非同期レプリケーションとすることで解決できるように感じますが、実は、Linuxのカーネルの問題により、レプリケーションタイプを非同期モード(Async mirror)に設定している場合、並列的に送られたデータの書き込み順序がまれに前後する可能性が不具合として報告されており、ファイルシステムの整合性に問題が生じる可能性があります。 簡単にイメージ化すると以下のようになります。 ■本来あって欲しい処理 ■カーネルの不具合により、非同期モードかつパラレルで同期した場合 ( [Linux]データレプリケーション構成においてスタンバイ側のファイルシステム(データ)に不整合が発生することがある – SIOS LifeKeeper/DataKeeper User Portal ) このカーネルの問題に対応する為に、並列処理が行われないようにLifeKeeper for Linux8.1.2以降では、nbd(正確にはnbd-server)のスレッド数を1ケにするよう改修が行われていますが、並列処理が行われなくなったことで、データレプリケーション処理速度を犠牲にせざるをえませんでした。 このことが、DataKeeperがリージョン跨ぎ構成に向かない理由となります。 DRBDを用いたDisaster Recovery add-on機能をサポート そこで、サイオステクノロジー社が目を付けたのが、LINBIT社製のDRBDでした。DRBDはWAN経由で遠隔地へ安定したデータレプリケーションを実現することができるソフトウェアとして、DataKeeperの代わりになると判断し、検証の末、2024年9月25日にリリースされたLifeKeeper for Linux xv9.9.0でサポートされることとなりました。   DataKeeperとDisaster Recovery add-onの棲み分けは? それでは、LifeKeeperにおいて、データレプリケーション構成を実現させるためにはどのような基準でDataKeeperとDisaster Recovery add-onを選択すれば良いでしょうか? 現在、サイオステクノロジー社でサポートしているDisaster Recovery add-on構成は、リージョン跨ぎの構成のみとなっています。その為、必然的に単一リージョン内の場合はDataKeeper、リージョン跨ぎの場合はDisaster Recovery add-onを選択するという棲み分けで覚えていただければと思います。   データレプリケーションソフト 備考 単一リージョン(マルチAZ) DataKeeper   リージョン跨ぎ Disaster Recovery add-on 3ノード構成   ※Disaster Recovery add-onの場合は、現時点で3ノード構成(単一リージョン内別AZに2ノード、別リージョンに1ノード)のみがメーカーからサポートされる構成となります。3ノード間のデータレプリケーションは全てDRBDを用いますのでご注意ください。 まとめ 今回は、『災害対策(DR)に対応した「Disaster Recovery add-on」の提供を開始した理由』と、同様のデータレプリケーションソフトであるDataKeeperとの違いや構成選択のポイントについて解説しました。 ・データレプリケーションには、DataKeeperとDisaster Recovery add-onの2種類がある ・リージョン跨ぎの構成では、Linuxのカーネルの問題があり、DataKeeperは不向きである ・単一リージョン内のAZ間レプリケーションではDataKeeperを、リージョン跨ぎの場合はDisaster Recovery add-onを選択する
アバター
SCSK 岩井です。 今回はRaspberry Piを2台使ってWebサーバの冗長化を行ってみたいと思います。 ただWebサーバを冗長化するだけではなく、WebサイトとLED点灯/消灯を連動させてみたいと思います。 ちょっとだけそれっぽくなる予定です。   下準備 使用するRaspberry Piは以下のものです。 【Raspberry Pi 5】 CPU: Broadcom BCM2712 quad-core Arm Cortex A76 processor @ 2.4GHz Memory: 8GB OS: Bookworm 【Raspberry Pi 3 Model B+】 CPU: Broadcom BCM2837B0, Cortex-A53 (ARMv8) 64-bit SoC @ 1.4GHz Memory: 1GB OS: Bookworm   この2台にflask(簡易的なWebアプリフレームワーク)をインストールしてWeb経由操作でLED点灯/消灯する構成を組んでみます。 【追加で用意するもの】 LED(赤)×1 LED(黄)×1 抵抗(330Ω)×2 ジャンパーケーブル×4 ブレッドボード×1 Webサーバアクセス用PC   ライブラリのインストール ラズパイに必要なライブラリ(Flask、GPIO(Rasberry Pi 3)、gpiozero(Rasberry Pi 5))をインストールします。 Raspberry Pi 5はGPIOが使えなくなってしまったので代わりにgpiozeroをインストールします。  # Raspberry Pi 5の場合  sudo apt update  sudo apt install python3-flask  sudo apt install gpiozero  # Raspberry Pi 3の場合  sudo apt update  sudo apt install python3-flask  sudo apt install python3-rpi.gpio    Pythonスクリプトの作成 PythonでFlaskアプリケーションを作成します。 ファイル名はapp.pyにしてみました。 Web画面上にLED点灯用とLED消灯用のリンクを張って、LEDを着けたり消したりできるようにします。 まずはviを起動します。  # Raspberry Pi 3/Raspberry Pi 5共通  sudo vi app.py 以下のコードを記述してファイルを保存します。Raspberry Pi 5はgpiozeroライブラリ、Raspberry Pi 3は GPIOライブラリなのでコードが異なります。同じ画面を作成していますが、Raspberry Pi 5はWebアクセスされると「Unit 1」、 Raspberry Pi 3はWebアクセスされると「Unit 2」と表示されるようにしています。  # Raspberry Pi 5の場合  from flask import Flask  from gpiozero import LED        app = Flask(__name__)    # GPIO 17を制御するLEDオブジェクトの作成    led = LED(17)    @app.route('/')  def index():  return '''    <h1>LED Control(Unit 1)</h1>  <p><a href="/led/on">Turn LED ON</a></p>  <p><a href="/led/off">Turn LED OFF</a></p>  '''    @app.route('/led/on')  def led_on():  led.on() # LEDを点灯  return '<h1>LED is ON</h1><p><a href="/">Back to home</a></p>'    @app.route('/led/off')  def led_off():  led.off() # LEDを消灯  return '<h1>LED is OFF</h1><p><a href="/">Back to home</a></p>'    if __name__ == '__main__':  app.run(host='0.0.0.0', port=5000)  # Raspberry Pi 3の場合  from flask import Flask  import RPi.GPIO as GPIO    app = Flask(__name__)    # GPIOピンの設定  LED_PIN = 17  GPIO.setmode(GPIO.BCM)  GPIO.setup(LED_PIN, GPIO.OUT)    @app.route('/')  def index():  return '''  <h1>LED Control(Unit 2)</h1>  <p><a href="/led/on">Turn LED ON</a></p>  <p><a href="/led/off">Turn LED OFF</a></p>  '''     @app.route('/led/on')  def led_on():  GPIO.output(LED_PIN, GPIO.HIGH)  return '<h1>LED is ON</h1><p><a href="/">Back to Home</a></p>'    @app.route('/led/off')  def led_off():  GPIO.output(LED_PIN, GPIO.LOW)  return '<h1>LED is OFF</h1><p><a href="/">Back to Home</a></p>'    if __name__ == '__main__':  try:  app.run(host='0.0.0.0', port=5000)  finally:  GPIO.cleanup()   LEDとRaspberry Piを接続する ブレッドボードにLEDと抵抗を接続します。 Raspberry Pi 5用とRaspberry Pi 3用に2セット回路を作ります。 イメージはこんなカンジで。(左側がRaspberry Pi 3用、右側がRaspberry Pi 5用) 次に回路とRaspberry Pi 5、Raspberry Pi 3を接続します。 なお、コードの中にあったGPIO 17は17ピンではなく、11ピンのGPIO17 です。 17ピンに刺すと3.3vの電圧がかかりっぱなしになるので、 ただLEDが光ってるだけの状態になります。(Webアプリと連動しません) 10分悩みました。 11ピンのGPIO17に+側(LED側のケーブル)、14ピンのGroundに-側(抵抗側のケーブル)を接続します。 【Raspberry Pi 5のピン配置】 Raspberry Pi 5 Pinouts including GPIO for the 40 Pin Header – element14 Community 【Raspberry Pi 3のピン配置】 Raspberry Pi 3 Model B GPIO 40 Pin Block Pinout – element14 Community   Webサーバの起動(Pythonスクリプトの実行) 各Raspberry Piのコンソールで、Webサーバを起動します。 まずはIPアドレス確認から。  # Raspberry Pi 5/Raspberry Pi 3共通  ip addr 次に各Raspberry PiでWebサーバを起動します。  # Raspberry Pi 5/Raspberry Pi 3共通  python3 app.py   WebブラウザからアクセスとLED操作 Webサイトアクセス用PCでブラウザを2つ起動し、それぞれ以下のURLにアクセスします。 http://Raspberry Pi 5のIPアドレス:5000 http://Raspberry Pi 3のIPアドレス:5000 次にブラウザ上の「Turn LED ON」のリンクをクリックします。       LED点灯。やりました。 「Back to Home」のリンクをクリックして前の画面に戻ります。 今度は「Turn LED OFF」のリンクをクリックします。 LED消灯。(写真撮り忘れました)   nginxのインストールとプロキシ設定 nginxを使用してFlaskアプリへのリクエストをプロキシできるようにするため、各Raspberry Piにnginxをインストールします。  # Raspberry Pi 5/Raspberry Pi 3共通  sudo apt install nginx プロキシを有効化するため、nginxの設定を変更します。  # Raspberry Pi 5/Raspberry Pi 3共通  sudo vi /etc/nginx/sites-available/default 設定内容は以下のとおり。  # Raspberry Pi 5/Raspberry Pi 3共通  server {   listen 80;   server_name localhost;     location / {   proxy_pass http://127.0.0.1:5000;   proxy_set_header Host $host;   proxy_set_header X-Real-IP $remote_addr;   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;   proxy_set_header X-Forwarded-Proto $scheme;   }  }   Keepalivedのインストールと設定 keepalivedインストールしてWebサイト(Flaskアプリ)の冗長化構成を組んでみます。 各Raspberry Piにkeepalivedをインストールします。  # Raspberry Pi 5/Raspberry Pi 3共通  sudo apt update  sudo apt install keepalived 次に各Raspberry Piでkeepalivedの設定ファイルを編集します。  # Raspberry Pi 5/Raspberry Pi 3共通  sudo vi /etc/keepalived/keepalived.conf Raspberry Pi 5をMaster、Raspberry Pi 3をBackupとします。 仮想IPアドレス(virtual_ipaddress)は192.168.11.110、Master側を優先させるため、priorityを100とします。 なお、interfaceは仮想IPアドレスを持たせるネットワークインターフェイスを指定します。  # Raspberry Pi 5(Master)  vrrp_instance VI_1 {   state MASTER   interface eth0   virtual_router_id 51 priority 100   advert_int 1   authentication {   auth_type PASS   auth_pass mypass   }   virtual_ipaddress {   192.168.11.110   }  } Backup側の仮想IPアドレス(virtual_ipaddress)はMaster側と同様の192.168.11.110、priorityを90とします。  # Raspberry Pi 3(Backup)  vrrp_instance VI_1 {   state BACKUP   interface eth0   virtual_router_id 51   priority 90   advert_int 1   authentication {   auth_type PASS   auth_pass mypass   }   virtual_ipaddress {   192.168.11.110   }  } 各Raspberry Piでkeepalivedを再起動します。  # Raspberry Pi 5/Raspberry Pi 3共通  sudo systemctl restart keepalived   動作確認 Webサイトアクセス用PC上のブラウザで仮想IPアドレスのURLにアクセスします。 http://192.168.11.110/ Unit 1の表記があり、Master側(Raspberry Pi 5)のWebサイトにアクセスしていることがわかります。 Raspberry Pi 5に接続したLEDの点灯/消灯も問題なくできます。 ここで、Raspberry Pi5をシャットダウンします。 その後上記URLにアクセスするとUnit 2の表記があり、Backup側(Rapberry Pi 3)のWebサイトにアクセスしています。 Raspberry Pi 3に接続したLEDの点灯/消灯も問題なくできます。 これでWebサイトを冗長化することができました。 GPIO側も冗長構成とする必要がありますが、片系に障害が発生してもサービスを継続させることができるようになります。 今回はLEDを取り付けましたが、カメラやセンサーなどを取り付けてみるのも面白そうです。
アバター
LifeKeeper記事では、前々回DRBD×LifeKeeperの組み合わせの製品と性能についてご紹介しました。 第10回 DRBD × LifeKeeperの高可用性リアルタイムレプリケーションを探る – TechHarmony   今回の記事では、DRBD×LifeKeeper製品であるDisaster Recovery Add-onの構築方法そして機能の検証をしていきたいと思います!   検証環境について 今回、Disaster Recovery Add-onの機能を確認する環境として AWS上の東京リージョンに2台、大阪リージョンに1台の計3ノードによる マルチリージョンでの高可用性構成にてDR環境を構築し、機能の検証を行っていきます。 以下 AWS での3ノード構成では、通常時の想定として 東京リージョンをメインサイトとして、東京1号機、東京2号機 が動作し、 大阪リージョンをバックアップサイトとして、大阪1号機が動作をしている想定の構成としております。 環境の設定値、ルーティング設定などは以下ブログに詳細を掲載しておりますので、気になる方はご参照下さい。 AWSマルチリージョンにおける高可用性方式を実装してみる – TechHarmony   LifeKeeperインストール/DRBD設定手順 DR環境でのLifeKeeperの設定手順について解説していきます。 まずは、LifeKeeperのインストールする際の手順となります。 LifeKeeperをインストールするタイミングでDisaster Recovery Add-onの機能を有するオプションを一緒にインストールすることが可能です。 インストール時以下のように、ARKと呼ばれるミドルウェア選択項目にて「DRBD Recovery Kit」を選択すればOKです。 インストール時の注意点として、「Quorum/Witness」の選択を忘れずにしましょう。 LifeKeeperが持つQuorum/Witness機能を使用することで、複数ノードが同時にActive状態になることを防止できます。   インストールが完了しましたら、 DRBDの設定作業を行っていくためのGUIをインストールする作業を行います。 設定に関しては、LifeKeeper GUIにて実行できますので、その事前準備を行います。 GUIはLKWMC[LifeKeeper Web Management Console]と呼ばれるWebアプリケーションを利用していきます。 LKWMCのインストールファイルを各ノードに配置し、インストールスクリプトを実行することで利用することができます。 LKWMC利用の注意点として、TCPポート5110を使用するため、通信許可の設定が必要です。   GUIの準備ができましたら、DRBDの設定を投入していきます。 設定内容は以下の5項目となります。 1 コミュニケーションパス 2 DRBDリソース作成/拡張 3 IPリソース作成/拡張 4 Generic ARK作成/拡張 5 Quorum設定   ■コミュニケーションパス LifeKeeper では、複数台のノードをお互いに接続することで、クラスターを構成していきます。 そのため、各ノード間で通信を行うための経路を事前に設定する必要があります。 この通信経路のことを LifeKeeper では「コミュニケーションパス」と呼び、こちらの設定を行います。 コミュニケーションパス>オペレーション にて各ノードの設定を入れていきます。作成が完了すると上記画像のような表記がされます。 コミュニケーションパスを作成する際の注意点なのですが、名前解決とTCPポートの穴あけが必要となります。 今回の環境では、/etc/hostsに各ノードの情報を記載し、コミュニケーションパスで使用する7365のポートの通信許可の設定を行いました。   ■DRBDリソース作成/拡張 コミュニケーションパスの作成が完了できましたら、次はDRBDリソースの作成と各ノードに拡張をしていく作業となります。 DRBDリソースは、共有ストレージを使用せずに可用性の高いクラスターを構築する機能を提供します。 まず各ノードにて、マウントポイントの対象となるデバイスの確認(ない場合は作成)、 対象デバイスにてパーティションの作成を行います。 上記作成ができましたら、GUIにてリソース作成を行っていきます。 リソースツリー>オペレーションからDRBDリソース作成を行えます。   作成が完了しますと、上記のように「drbd-mp」というマウントポイントの作成ができます。 このマウントポイントを各ノードへ拡張を行うと、以下のようにデータの同期先として利用できるようになります。   リソース作成/拡張の設定を投入する際に、切り替え/切り戻し動作の設定を行うことができます。 切り替えを手動 or 自動で行うのか。 切り戻しを手動 or 自動で行うのかを選択することが可能です。 今回の設定では切り替えを自動、切り戻しを手動の設定にて行いました。   ■IPリソース作成 続いてIPリソースの作成を行います。 IPリソースは各ノード間で切り替えることができる仮想IPアドレスの作成を行います。 今回は、「20.1.1.1」という仮想IPアドレスを用意し、設定していきます。   AWS上での仮想IPアドレスについては過去に解説記事を投稿していますので、こちらをご参照ください。 【LifeKeeper】AWSでは仮想IPアドレスが使えない!?をこうして解決する!! – TechHarmony   ■GenericARK作成/拡張 メインサイトである東京リージョンがDownした際に、自動で大阪リージョンへルートテーブルが切り替わるためのスクリプトを組み込みます。 詳細については別記事にて紹介予定となります。   ■Quorum設定 今回は自動切り替わりの動作とするため、Quorum設定を行います。 (手動切り替えの際は設定不要となります) GUI上での設定対応が終わりましたら、Quorum機能を有効にするためmajorityからstorageへ変更、 本番/災対サイト以外のリージョンにS3の作成を行い、その情報を設定へ追記します。 Disaster Recovery 構成ではメインサイトの2台が停止する想定のため、バックアップサイトのノード1台では Quorum を持つことができず、自動起動ができなくなります。 この場合、storage モードを使用することでメインサイトの2台が停止しても、バックアップサイトのノードにフェイルオーバーして、サービスをすばやく継続することができます。 storage としては、メインサイト/バックアップサイトとは別のリージョンに Amazon S3 を準備しておきます。Quorum設定なしでも自動で起動しますが、ネットワークが分断した場合(メインサイトは生きているが、クラスタノード間のネットワークが切断)はスプリットブレインになる可能性もあるので、Quorum/Witness の使用を推奨としています。   これにてLifeKeeperのインストール及びDRBD設定が完了となります。   シナリオ/機能検証 設定が完了したところで、Disaster Recovery Add-onの機能を検証していきたいと思います。 今回の確認観点としては、 ●フェイルオーバー時の動作 東京リージョンが使用不可になった際、大阪リージョンへのフェイルオーバーが問題なく行われるか。 ●データレプリケーションの整合性 DRBDリソースの作成時に指定したマウントポイントである”drbd-mp”が ファイルシステムとして稼働しているか、対象のファイルがレプリケーションされているか。 この2つの観点で機能を検証してみます。 テストシナリオとして、東京1、2号機(東京リージョン)をDownさせ、大阪1号機へフェイルオーバーを行います。 まず、通常時として以下のような状態となります。 東京1号機がActive、東京2号機/大阪1号機がStandbyの状態となります。 東京1号機に「drbd-mp」が存在し、test.txtがあります。東京2号機/大阪1号機にはありません。   今回のシナリオに沿って東京リージョンの1,2号機をDownさせていきます。 以下画像が東京東京1,2号機が落ちている状態となります。 GUIでリソースの状態を確認してみると無事フェイルオーバーされ、大阪1号機がPrimaryの状態となりました。 レプリケーションされているか確認したところ、drbd-mp(マウントポイント)が移動されており、 対象ファイルも確認できました。 切り替わり時間としては、東京リージョンをDownさせてから体感40~50秒ほどで切り替わりました。   東京1,2号機を復旧させると、大阪1号機がActiveで稼働している状態となります。 復旧後の切り戻しは手動 or 自動のどちらかを指定して戻すことができますが、 今回の切り戻し設定は、切り戻しのタイミングを管理者側のタイミングで復旧できるよう手動としておりますので、 東京が復旧してきても大阪1号機がActiveの状態のままとなります。   まとめ 今回AWS環境でのLifeKeeperを用いた災害対策としてDisaster Recovery Add-on の構築、機能検証を行い、 異なるリージョン間のデータレプリケーションの確認を行いました。 マルチAZ構成での可用性を高めるだけでなく、マルチリージョン構成をとることで、 遠隔地サイトへのスタンバイノードに対しても高速なレプリケーションと、障害発生時のフェールオーバーを可能とするため、自然災害によるシステム停止から素早くアプリケーションを回復することが可能となり、 よりシステムの信頼性と可用性を向上させ、企業のIT運用をさらに強固なものにすることができるなと感じました。 近年の災害対策としてぜひDisaster Recovery Add-onをご活用いただければと思います。
アバター
本記事は 新人ブログマラソン2024 の記事です 。 こんにちは。2024年度入社の野上です。 以前、MySQLの初心者ながらレプリケーション方式をまとめた記事を投稿させていただきました。 今回は、以前まとめた方式の中で一般的とされている「非同期レプリケーション」を構築してみました。 これは実際に私が携わった案件で経験した内容になります。 非同期レプリケーションの概要をわかっていても、実際に構築するとなると考慮しなければいけない事が多々ありました。 ブログや公式ドキュメントで書かれていない内容もありましたので、自分の知識のアウトプットもかねて皆さんにご紹介いたします。 非同期レプリケーションとは まず非同期レプリケーションとはどういう方式なのかという詳細に関しては、前回私が書いた記事を見ていただきたいです。 MySQL レプリケーションの方式をまとめてみた – TechHarmony ここで簡単に説明しますと、ソースサーバ(メインで使用するDBサーバ)でデータを更新する処理と、レプリカサーバ(複製されたDBサーバ)にデータの更新を反映させる処理が、非同期で行われます。 データベースのデータを更新するときには、トランザクション(データベースの操作を行う1つの処理単位)のコミットを実行し、コミットが完了することで、更新を確定させます。 非同期レプリケーションではレプリカサーバでの処理❶~❷の完了を待たずにコミットを完了します。   実際に構築してみた 非同期レプリケーションを構築する順序は以下の通りになります。 サーバの準備 データベースのコピー パラメータ設定 レプリケーション用ユーザーの作成 レプリケーション設定 各順序を説明しながら、レプリケーションを構築していきます。 サーバの準備 レプリケーションを構築するにあたり、レプリケーションを張る2つのサーバが必要となります。 Amazon EC2を用いて検証用のサーバを構築しました。 今回はMySQLのバージョンは 8.0.36 を使用しています。 MySQLはバージョンの違いによりコマンドが異なりますのでご注意ください 。 用途 ホストIPアドレス MySQLバージョン ソースサーバ 10.10.23.187 MySQL8.0.36 レプリカサーバ 10.10.23.19 MySQL8.0.36   データベースのコピー 対象のサーバを準備したら、ソースのデータベースをレプリカにコピーする必要があります。 ただレプリケーションを張るだけでは、レプリカのデータベースはソースと同じにはなりません。 レプリケーションは、ソースのバイナリログの内容をレプリカに転送するため、 バイナリログが残っている部分しかレプリケーションされません 。バイナリログの保持期間は、 「binlog_expire_logs_seconds」 というパラメータで設定されているため、my.cnfというパラメータの設定ファイルの内容を確認することをお勧めします。 データをコピーする方法は2通りあり、どちらかの方法でコピーを行います。 dmpファイルを用いてコピーする(MySQLの公式ドキュメントに記載されている) MySQLのデータディレクトリをコピーする dmpファイルを用いた方法が一般的ですが、今回は案件の中で私が勉強したことを共有したいこともあり、MySQLのデータディレクトリをコピーする方法で行います。 ①ソースでMySQLのデータディレクトリのパスを確認する データディレクトリのパスは、my.cnfに記述されているため以下のコマンドを実行します。     ②データディレクトリを圧縮する 以下のコマンドを実行することで、「datacopy.tar.gz」という圧縮ファイルが生成されます。     ③生成した圧縮ファイルをレプリカに送信する ファイルの送信方法もいくつかありますが、今回は2つのサーバで直接通信が可能なので、scpコマンドを利用してファイルの送信を行いました。     ④レプリカ側で圧縮したファイルを解凍してデータディレクトリを上書きする 以下のコマンドを実行することでデータディレクトリのコピーが完了します。 ※ここではmvコマンドにより元のデータディレクトリを退避させることで、安全にコマンドを実行しています。     ⑤auto.cnf の削除 以下のデータディレクトリ内に含まれる「auto.cnf」という名前のファイルの削除を行い、MySQLを再起動します。(※)       (※)auto.cnf とは、MySQL server のUUIDを格納したファイルです。 UUIDとは一意の識別子であり、UUIDが重複した状態ではレプリケーションを張ることができません 。そのため、ソースとレプリカで異なるUUIDを使用する必要があります。auto.cnf は自動で生成されるファイルであり、手動で削除してMySQLを再起動を実行することで自動作成されます。⑤の手順を行うことで、 ソースとレプリカで異なるUUIDとなるため、レプリケーションを張ることが可能になります 。 以上の手順により、データディレクトリのコピーが完了しました。   パラメータ設定 レプリケーションを張るためには、レプリケーションに必要なパラメータ設定をする必要があります。 MySQLのパラメータは「my.cnf」という設定ファイルに記述してあり、そのファイル内のパラメータ値を変更します。   ① 設定が必要なパラメータを以下の通りに設定する <ソースとレプリカ両方に設定> gtid_mode = ON GTID(グローバルトランザクションID)を有効化するパラメータです。 GTIDを有効化することによりソースとレプリカの一貫性の判断を容易にします。 enforce_gtid_consistency = ON GTIDを使用する際に、整合性に違反するトランザクションを禁止する設定です。 server_id = 1(ソース), 2(レプリカ) レプリケーションの構成内でソースとレプリカを一意に識別するために用います。 <レプリカのみに設定> read_only = ON レプリカ側でデータの更新を拒否します。 SUPER権限を持つアカウントは拒否しません。 super_read_only = ON レプリカ側でSUPER権限を持つアカウントからの更新を拒否します。   ② MySQLを再起動する my.cnf の内容を変更したときには、MySQLを再起動することで設定を反映することができます。 再起動には以下のコマンドを実行します。   以上の手順を実行することでMySQLのパラメータ変更が完了しました。   レプリケーション用ユーザーの作成 次に、レプリケーションを行うために必要なMySQLのユーザーを作成します。 レプリケーションを張る際には、レプリカがソースのMySQLユーザーとしてデータにアクセスします。そのため、ソース側にレプリケーションのユーザー権限を持ち、レプリカのIPアドレスからのログインを許可するユーザーが必要です。 ①ソース側でMySQLにログインし、以下のステートメントを実行してユーザーを作成する     ここでは、replica@10.10.23.19 というユーザーを作成しています。 MySQLではユーザー名とホスト名の組み合わせによってユーザーを作成します。   ②作成したユーザーにレプリケーション権限を与える 以下のステートメントを実行します。作成したユーザーがレプリケーション権限を持っていることが確認できます。         以上の手順でレプリケーション用のユーザーの作成が完了しました。   レプリケーション設定 パラメータの設定が完了したら、いよいよレプリケーションを張ります。 ①レプリカ側でMySQLにログインして、レプリケーションの設定を行う 以下のステートメントを実行します。       ここでは、先ほどソースで作成したユーザーのホストアドレス、ユーザー名、パスワードを指定します。 さらに、「source_auto_position」,「source_ssl」という2つのオプションを付けています。 「source_auto_position」はGTIDを使用するために必須のオプションです。 「source_ssl」 はレプリケーションの通信がSSL接続するというオプションです。(※) (※) MySQLはバージョン5.7以前と8.0以降でユーザー認証の方式が変更されています 。5.7以前はデフォルトの認証プラグインが「mysql_native_password」ですが、8.0以降は「caching_sha2_password」となっています。それにより、 セキュアな接続が必須 となりました。そのため 「source_ssl」のオプションを付けない場合にレプリケーション接続が失敗してしまいます 。 (参考) https://dev.mysql.com/doc/refman/8.0/ja/upgrading-from-previous-series.html#upgrade-caching-sha2-password   ②レプリケーションの開始 以下のステートメントを実行することでレプリケーション接続を開始します。     ③レプリケーションのステータスの確認 以下のステートメントを実行することでレプリケーションのステータスを表示します。                                             実行結果の赤枠で囲んだ項目は以下を意味します。 Replica_IO_Running=Yes ソースのバイナリログを読み込み、レプリカにログを転送するスレッドが動いていることを示します。 Replica_SQL_Running=Yes レプリカで、ソースから転送されたログからデータの更新を行うスレッドが動いていることを示します。 Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error:        エラーが発生していないことを示します。 上記のことから、 実行結果通りであればレプリケーション接続が成功 していることになります。 以上の手順を実行しレプリケーションの構築に成功しました。MySQLでは非同期レプリケーションがデフォルトであるため、非同期レプリケーションの構築ができたことになります。 まとめ 本記事では、非同期レプリケーションを実際に構築してみました。 私が案件の中で手を詰まらせた項目は紹介できたと思っています。 しかし、レプリケーションは要件によってパラメータの値などを変化させる必要があります。 そのため今回紹介したのはレプリケーションの構築の1つの例ですが、少しでも読んでくださった方の助けになるポイントがあれば嬉しいです。
アバター
前回、AWS Glue と Amazon Athena でS3上のデータを読み取ることができました。 Amazon Athena + AWS Glue で Amazon S3 上のデータを読み取る – TechHarmony 今回は Amazon Athena 上のデータを Amazon QuickSight で可視化したいと思います。 QuickSight の詳細については QuickSight公式ページ をご参照ください。   やってみた Amazon QuickSight AWSマネジメントコンソールからQuickSight画面を開きます。 初めてQuickSightを使用する際はアカウントの設定画面が開きますので、適宜設定してください。 「新しい分析」をクリックします。   「新しいデータセット」をクリックします。   データソースを選択します。今回はAthenaを選択します。   データソース名を入力し、「データソースを作成」をクリックします。 。   可視化したいデータがあるデータベースおよびテーブルを選択し、「選択」をクリックします。   「Visualize」をクリックします。   以上で、インポートが完了です。 自動で分析の画面に移ります。   最初にグラフが1つ用意されているので、フィールドリストから必要なものをドラッグ&ドロップで追加します。 X軸、Y軸に分析対象のデータを配置し、 分析対象のデータ形式に応じて、可視化のフォーマットもQuickSight側で色々準備されています。 今回は試しに積み上げ面折れ線グラフと円グラフでデータを可視化してみました。   おわりに Amazon S3 上のデータを Amazon Athena と Amazon QuickSight で可視化することができました。 今回は単純なデータでしたが、大まかに使い方を理解することができました。 サーバレスサービスなので手軽に利用開始でき、初心者でも使いやすそうだなという所感です。 データ分析をやってみたいけど難しそうだな、、、と思っている方に少しでもイメージが伝われば幸いです。
アバター
こんにちは。SCSK株式会社の上田です。 2024年12月10日に、ポイントリリースバージョンである Zabbix 7.2 がリリースされ、Zabbix7.0 LTSには無かった新機能が追加されました! 今回は、追加された新機能の中から、 役に立ちそうな機能を4個、ピックアップしてご紹介します! 前回リリースされたZabbix7.0 LTSの新機能については、こちらの記事をご参照ください。 Zabbix 7.0で追加された便利な新機能10選 Zabbix 7.0では様々な新機能が追加されました。その中から便利な機能を厳選してご紹介します。 blog.usize-tech.com 2024.07.05   新機能の紹介 それでは、新機能を紹介していきます。 ①新しいウィジェット Zabbix7.0でも新しいウィジェットが追加されましたが、今回も 新しいウィジェットがいくつかリリースされました 。 1つ目は、 Host card 。選択したホストの情報を見ることができます。 Host Cardの詳細 表示する内容は、カスタマイズ可能です。 Host Cardで選択可能な項目 2つ目は、 Top items 。ホストのアイテムや、値やグラフで一覧表示できます。 Top itemsの表示内容 3つ目は、 Sparkline 。こちらはウィジェットの種類というより、ウィジェット上に表示できるグラフです。アイテムデータや、先ほどのTop itemsウィジェットで、スパークラインを表示することができます。 SparkLine(アイテムの値ウィジェット) SparkLine(Top itemsウィジェット) ②設定ファイルの環境変数サポート Zabbixサーバやエージェントなどの設定ファイルで、 環境変数 が利用できる ようになりました。これにより、設定値をハードコーディングする必要がなくなり、Dockerなどのコンテナ環境での設定が容易になりました。 ※Docker用であり、通常の運用での利用は推奨されていないようです。 ③NETCONFの対応 SSHアイテムがサブシステムパラメータをサポートするようになり、 NETCONF を利用したネットワークデバイスの監視が可能になりました 。これにより、従来のSNMPによる監視に加え、NETCONFの機能を活用した監視を実現できます。 先日のZabbixカンファレンスで、Zabbixの代表から「 8.0ではnetconfによるコンフィグ管理が可能になる 」という発言がありました。それの準備として実装されたのだと思います。 Zabbixでコンフィグ管理ができるようになれば、ネットワーク監視の幅が大きく広がるので、今後の動向に注目です。 ④新しいマクロ 新しいマクロ がリリースされ、監視設定とアラート設定がより柔軟になります。 以下は新しいマクロの例です。 {*TIMESTAMP}  マクロ:  イベントのUnixタイムスタンプを取得可能。 {FUNCTION.VALUE}  マクロ:  トリガー式内で個々の関数結果を参照可能。 これらのマクロを活用することで、 より高度な監視ロジックを構築する ことができます。 まとめ Zabbix 7.2の新機能を紹介しました。今回紹介した機能以外にも様々な機能が追加されています。詳細は公式ドキュメントをご参照ください。 Zabbix 7.2の新機能 Zabbix 7.2 introduces new ways to visualize your data and host information, and improves upon various existing monitorin... www.zabbix.com 「 Zabbix 7.2を新規にインストールしてみたい 」「 既存のZabbixからバージョンアップしたい 」という方は、是非弊社までお声がけください!! 最後まで読んでいただき、ありがとうございました。 弊社ではZabbix関連サービスを展開しています。以下ページもご参照ください。 ★Zabbixの基礎をまとめたeBookを公開しております!★ FAQ・お問い合わせ|SCSK Plus サポート for Zabbix 導入をご検討される際、よくあるご質問と回答についてまとめております。 www.scsk.jp ★SCSK Plus サポート for Zabbix★ SCSK Plus サポート for Zabbix 世界で最も人気のあるオープンソース統合監視ツール「Zabbix」の導入構築から運用保守までSCSKが強力にサポートします www.scsk.jp ★YouTubeに、SCSK Zabbixチャンネルを開設しました!★ SCSK Zabbixチャンネル 本チャンネルでは、SCSK株式会社でのZabbixに関するトレンド/事例紹介などを動画にまとめて取り上げております。最新のトピックについては、以下の弊社HPもしくはツイッターアカウントをぜひ参照ください。ツイッターアカウント: www.youtube.com ★X(旧Twitter)に、SCSK Zabbixアカウントを開設しました!★ x.com x.com
アバター
SCSKの畑です。10回目の投稿です。 今回は 6回目 のエントリで記載した、aws-amplify のSSR (Nuxt3)対応の詳細もとい補足について記載します。小ネタです。 対応の背景などは同エントリに記載しているのでここでは省略します。   aws-amplify 用のコンフィグファイル実装 aws-amplify モジュールを使用するにあたり、AppSync や Cognito など、モジュールが使用する各種サービスの情報が必要となります。言い換えると、Amplify.configure の引数としてどのような内容を設定するべきか、です。 ところが、具体的にどのように設定すべきなのかというドキュメントや情報が相対的に乏しく、あったとしても aws-amplify のバージョンが異なっておりコピペだと動かないなど、ちゃんと動かすまで結構苦労したため備忘として残しておこうと思います。例えば、GraphQL のデフォルト認証モードが IAM な場合は値を 「iam」に設定するみたいな情報とか、少なくとも実装時点ではちょっと調べた程度だと出てこなかったりしたんですよね。公式ドキュメントでは以下 URL が対応すると思うのですが、一覧としての記載はありませんし。。 Configure Amplify categories - Vue - AWS Amplify Gen 1 Documentation Configuring the client. AWS Amplify Documentation docs.amplify.aws 色々と調べた限り、手動でコンフィグファイルを作成するより、Amplify CLI で経由で各種リソースを構成した際に生成されるコンフィグファイル (src/aws-exports や src/amplifyconfiguration.json) を使用するのが事例としては多いように思えました。そのことも、手動でコンフィグファイルを作成する情報が相対的に少ない原因に繋がっているように思えます。 ただ、今回は別エントリで説明した通りお客さん環境で Amplify を使用できないことが分かっていたこと、Amplify 経由で他サービスを構成した際の細かい設定が難しいことの2点より、Amplify 経由で構成したのは AppSync/DynamoDB のみでした。コンフィグファイル自体も最初から手動で作成する方針で進めていたので、その分時間もかかったような恰好です。 そもそも、コンフィグファイルの書式からして異なっているというのも、構築当初はかなり混乱しました。書式を参考にしようとしても参考にならず、しかしながら Amplify.configure の引数としてはどちらも使用できるというのが・・ What options can I pass to Amplify.configure()? There seem to be two different ways to configure Amplify. Some examples use a key-value properties format:import aws_exp... stackoverflow.com 実際、aws-amplify モジュールは UI 含めて色々と便利で、このモジュールだけを使用したいというケースも全然考えられると思うので、コンフィグ定義一覧程度は公式ドキュメントに載せてくれるとありがたいですね。 ということで、今回の場合の実装例を示してみます。対象は Amplify(GraphQL) 及び Cognito の2種類です。 {   "Auth": {       "Cognito": {           "region": "ap-northeast-1",           "userPoolId": "<Cognito_UserPool_Id>",           "userPoolClientId": "<Cognito_Application_Client_Id>",           "identityPoolId": "<Cognito_IdentityPool_Id>"       }   },   "API": {       "GraphQL": {           "endpoint": "<AppSync_GraphQL_Endpoint_URL>",           "region": "ap-northeast-1",           "defaultAuthMode": "iam"       }   } } なお、他サービスを含めたより詳細な内容については、以下広野さんのエントリも合わせてご参照ください。私も最初にこのエントリを見ていさえすれば、試行錯誤に数時間を費やすところまではいかなかったでしょう・・ aws-amplify と @aws-amplify/ui-react モジュールを v6 にアップデートしてみた aws-amplify と @aws-amplify/ui-react モジュールをバージョン 5 から 6 にアップグレードしたときの対応を紹介します。 blog.usize-tech.com 2023.12.19   Plugin 実装 こちらは、基本的には公式 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 なお、Plugin のファイル名で、client/server のどちらで実行するプラグインかどうかを指定しています。また、Plugin の読込み順序はファイル名(昇順)に従うため、ファイル名の先頭にナンバリングを付与して一番最初に読み込まれるようにしています。 前回のエントリ で記載した通り、他の Plugin 内で aws-amplify/auth (Cognito) や aws-amplify/data (AppSync/GraphQL) の機能を使用しているため、先にこちらの処理を実施しておく必要があるためです。公式 URL の実装例も正にそのようになっていますね。   01.amplifyApis.client.ts client 側で実行する plugin です。先般のエントリで記載した通り、aws-amplify 自体は client 側で実行する前提のモジュールとなっているため、実装内容自体はシンプルです。 今回のアプリケーションで使用するのは Auth/GraphQL の2種類のみであるため、その中から使用する機能やメソッドのみを返すようにしています。また、先に説明した aws-amplify 用の設定ファイルを Amplify.configure の引数で指定しています。 import { Amplify } from 'aws-amplify'; import { fetchAuthSession, fetchUserAttributes, getCurrentUser, signIn, signOut} from "aws-amplify/auth"; import { generateClient } from 'aws-amplify/data'; import { defineNuxtPlugin } from '#app'; import outputs from '../amplify_cognito_setting.json'; const client = generateClient(); export default defineNuxtPlugin({   name: 'AmplifyAPIs',   enforce: 'pre',   setup() {               Amplify.configure(outputs, { ssr: true });       return {           provide: {               Amplify: {                   Auth: {                       fetchAuthSession,                       fetchUserAttributes,                       getCurrentUser,                       signIn,                       signOut                   },                   GraphQL: {                       client                   }               }           }       }   } })   01.amplifyApis.server.ts server 側で実行する plugin です。こちらは率直に言うと、公式 URL の実装例から client の実装をベースに明らかに不要な項目を除外した程度の変更しかしていないため、正直あまり言及できる内容がありません。幸いにも公式 URL の実装例に記載されていた機能/メソッドしか使用しなかったためこのような対応で OK でしたが、他に使用したいものがあった場合はモジュールの実装内容そのものを読み解く必要があったかもしれないです。 認証用の情報を cookie に入れているようで、有効期限が実装例だと30日間となっているため、要件次第でこの点は見直した方が良さそうです。そもそも cookie を使用するかどうかを含めて検討すべきかもしれませんが・・ また、aws-amplify 用の設定ファイルを読み込んでいるのは一緒ですが、Amplify.configure は client 側のメソッドであるため、同設定ファイルから読み込んだ内容をベースに aws-amplify/auth 及び aws-amplify/data ごとに必要な情報をパースしています。 import type { CookieRef } from 'nuxt/app'; import type {   LibraryOptions,   FetchAuthSessionOptions } from '@aws-amplify/core'; import {   createKeyValueStorageFromCookieStorageAdapter,   createUserPoolsTokenProvider,   createAWSCredentialsAndIdentityIdProvider,   runWithAmplifyServerContext } from 'aws-amplify/adapter-core'; import { parseAmplifyConfig } from 'aws-amplify/utils'; import {   fetchAuthSession,   fetchUserAttributes,   getCurrentUser } from 'aws-amplify/auth/server'; import { generateClient } from 'aws-amplify/api/server'; import outputs from '@/amplify_cognito_setting.json'; // parse the content of `amplify_outputs.json` into the shape of ResourceConfig const amplifyConfig = parseAmplifyConfig(outputs); // create the Amplify used token cookies names array const userPoolClientId = amplifyConfig.Auth!.Cognito.userPoolClientId; const lastAuthUserCookieName = `CognitoIdentityServiceProvider.${userPoolClientId}.LastAuthUser`; // create a GraphQL client that can be used in a server context const gqlServerClient = generateClient({ config: amplifyConfig }); const getAmplifyAuthKeys = (lastAuthUser: string) =>   ['idToken', 'accessToken', 'refreshToken', 'clockDrift']       .map(           (key) =>               `CognitoIdentityServiceProvider.${userPoolClientId}.${lastAuthUser}.${key}`       )       .concat(lastAuthUserCookieName); // define the plugin export default defineNuxtPlugin({   name: 'AmplifyAPIs',   enforce: 'pre',   setup() {       // The Nuxt composable `useCookie` is capable of sending cookies to the       // client via the `SetCookie` header. If the `expires` option is left empty,       // it sets a cookie as a session cookie. If you need to persist the cookie       // on the client side after your end user closes your Web app, you need to       // specify an `expires` value.       //       // We use 30 days here as an example (the default Cognito refreshToken       // expiration time).       const expires = new Date();       expires.setDate(expires.getDate() + 30);         // Get the last auth user cookie value       //       // We use `sameSite: 'lax'` in this example, which allows the cookie to be       // sent to your Nuxt server when your end user gets redirected to your Web       // app from a different domain. You should choose an appropriate value for       // your own use cases.       const lastAuthUserCookie = useCookie(lastAuthUserCookieName, {           sameSite: 'lax',           expires,           secure: true       });         // Get all Amplify auth token cookie names       const authKeys = lastAuthUserCookie.value           ? getAmplifyAuthKeys(lastAuthUserCookie.value)           : [];       // Create a key-value map of cookie name => cookie ref       //       // Using the composable `useCookie` here in the plugin setup prevents       // cross-request pollution.       const amplifyCookies = authKeys           .map((name) => ({               name,               cookieRef: useCookie(name, { sameSite: 'lax', expires, secure: true })           }))           .reduce<Record<string, CookieRef<string | null | undefined>>>(               (result, current) => ({                   ...result,                   [current.name]: current.cookieRef               }),               {}           );       // Create a key value storage based on the cookies       //       // This key value storage is responsible for providing Amplify Auth tokens to       // the APIs that you are calling.       //       // If you implement the `set` method, when Amplify needed to refresh the Auth       // tokens on the server side, the new tokens would be sent back to the client       // side via `SetCookie` header in the response. Otherwise the refresh tokens       // would not be propagate to the client side, and Amplify would refresh       // the tokens when needed on the client side.       //       // In addition, if you decide not to implement the `set` method, you don't       // need to pass any `CookieOptions` to the `useCookie` composable.       const keyValueStorage = createKeyValueStorageFromCookieStorageAdapter({           get(name) {               const cookieRef = amplifyCookies[name];               if (cookieRef && cookieRef.value) {                   return { name, value: cookieRef.value };               }               return undefined;           },           getAll() {               return Object.entries(amplifyCookies).map(([name, cookieRef]) => {                   return { name, value: cookieRef.value ?? undefined };               });           },           set(name, value) {               const cookieRef = amplifyCookies[name];               if (cookieRef) {                   cookieRef.value = value;               }           },           delete(name) {               const cookieRef = amplifyCookies[name];               if (cookieRef) {                   cookieRef.value = null;               }           }       });       // Create a token provider       const tokenProvider = createUserPoolsTokenProvider(           amplifyConfig.Auth!,           keyValueStorage       );         // Create a credentials provider       const credentialsProvider = createAWSCredentialsAndIdentityIdProvider(           amplifyConfig.Auth!,           keyValueStorage       );         // Create the libraryOptions object       const libraryOptions: LibraryOptions = {           Auth: {               tokenProvider,               credentialsProvider           }       };         return {           provide: {               // You can add the Amplify APIs that you will use on the server side of               // your Nuxt app here. You must only use the APIs exported from the               // `aws-amplify/<category>/server` subpaths.               //               // You can call the API by via the composable `useNuxtApp()`. For example:               // `useNuxtApp().$Amplify.Auth.fetchAuthSession()`               //               // Recall that Amplify server APIs are required to be called in a isolated               // server context that is created by the `runWithAmplifyServerContext`               // function.               Amplify: {                   Auth: {                       fetchAuthSession: (options: FetchAuthSessionOptions) =>                           runWithAmplifyServerContext(                               amplifyConfig,                               libraryOptions,                               (contextSpec) => fetchAuthSession(contextSpec, options)                           ),                       fetchUserAttributes: () =>                           runWithAmplifyServerContext(                               amplifyConfig,                               libraryOptions,                               (contextSpec) => fetchUserAttributes(contextSpec)                           ),                       getCurrentUser: () =>                           runWithAmplifyServerContext(                               amplifyConfig,                               libraryOptions,                               (contextSpec) => getCurrentUser(contextSpec)                           )                   },                   GraphQL: {                       client: {                           // Follow this typing to ensure the`graphql` API return type can                           // be inferred correctly according to your queries and mutations                           graphql: <                               FALLBACK_TYPES = unknown,                               TYPED_GQL_STRING extends string = string                           >(                               options: GraphQLOptionsV6<FALLBACK_TYPES, TYPED_GQL_STRING>,                               additionalHeaders?: Record<string, string>                           ) =>                               runWithAmplifyServerContext<                                   GraphQLResponseV6<FALLBACK_TYPES, TYPED_GQL_STRING>                               >(amplifyConfig, libraryOptions, (contextSpec) =>                                   gqlServerClient.graphql(                                       contextSpec,                                       options,                                       additionalHeaders                                   )                               )                       }                   }               }           }       };   } });   まとめ コードの分量は多いですが、小ネタに相応しい内容でした。毎回気合いの入ったエントリを書くのがそろそろ難しくなってきたので、こういうちょっとした内容も増やしていければなと思います。(書きながらネタを増やしていけるように努力しているつもりですが・・)ともあれ2桁投稿することが当初の目標だったので、ひとまず達成できてよかったです。 本記事がどなたかの役に立てば幸いです。
アバター
こんにちは、SCSKの内ヶ島です。 オンプレミス環境から手軽にAWS Site-to-Site VPN接続の検証環境を構築する方法を紹介します。 この記事では、CloudFormationを使った環境を一括管理し、Amazon Linux 2023のリポジトリに含まれるLibreswanを用いてVPN接続を行います。 はじめに AWS Site-to-Site VPN接続は、オンプレミス環境とAWS環境を安全に接続するための重要な技術です。 しかし、VPN接続の検証には通常、実機のVPN機器が必要となりハードルが高いものでした。 そこで今回の検証では、以下の2点を主な目標として設定しました。 ソフトウェアVPNを用いてVPN接続が正常に機能すること 環境全体をCloudFormationで管理し、簡単に作成・削除できること 実機のVPN機器がなくても、AWSの環境内で完結した形でVPN接続の検証が可能になります。 また、CloudFormationを活用することで、環境の再現性と管理の容易さを実現しています。 環境構成 今回構築する環境は以下の通りです。 オンプレミス側(AWS上に疑似環境として構築)   – VPC (10.0.0.0/22)   – プライベートサブネット (10.0.0.0/24)   – パブリックサブネット (10.0.1.0/24)   – EC2インスタンス x2(VPN装置、クライアント想定) AWS側   – VPC (192.168.0.0/22)   – プライベートサブネット (192.168.0.0/24)   – EC2インスタンス(AWSサーバー想定)   – カスタマーゲートウェイ、仮想プライベートゲートウェイ CloudFormationテンプレート 今回の検証環境構築には、オンプレミス環境用とAWS環境用の2つのCloudFormationテンプレートを使用しています。 CloudFormationテンプレートの特徴 複雑なVPN検証環境を簡単に、そして再現性高く構築することができます。 両環境とも、インターネットに直接接続せずにパッケージ管理やシステム更新が可能な、セキュアな設計となっています。 以下の内容を生成AIを使ってCloudFormationテンプレートを作成しました。 長いテンプレートの記述ミスや整合性をある程度チェックできるので便利です。 共通の特徴 VPC構成 : 両テンプレートともVPCとサブネットを設定 EC2インスタンス : Amazon Linux 2023のEC2インスタンスを作成 セキュリティグループ : 必要最小限のトラフィックのみを許可するセキュリティグループを設定 IAMロールとVPCエンドポイント : Systems Manager Session Managerを使用してEC2インスタンスに接続できるよう、必要なIAMロールとVPCエンドポイント(SSM、EC2メッセージ、SSMメッセージ)を設定 S3 VPCエンドポイント : EC2インスタンスがインターネットを経由せずにS3にアクセスできるよう、S3用のVPCゲートウェイエンドポイントを設定。これにより、dnfやyumを使用したパッケージのインストールが可能となる。 タグ付け : すべてのリソースにタグを付け、管理を容易にしている。タグのプレフィックスはパラメータとして指定可能 オンプレミス環境用テンプレート(Onpre_resource.yaml)の固有の特徴 サブネット構成 : パブリックサブネットとプライベートサブネットの両方を作成 EC2インスタンス(パブリックサブネット) : VPN装置役のEC2インスタンス(Elastic IP付与)。ネットワークの送信元/送信先チェックをオフ EC2インスタンス(プライベートサブネット) : クライアント役のEC2インスタンス ルーティング : プライベートサブネットからのトラフィックをパブリックEC2インスタンス(疑似NATゲートウェイ)経由でルーティング インターネットゲートウェイ : パブリックサブネット用にインターネットゲートウェイを設定 AWS環境用テンプレート(AWS_resource.yaml)の固有の特徴 サブネット構成 : プライベートサブネットのみを作成します。 VPNリソース : カスタマーゲートウェイ、仮想プライベートゲートウェイ、VPN接続、VPN接続ルートを作成 パラメータ : カスタマーゲートウェイのIPアドレスをパラメータとして受け取る EC2インスタンス : プライベートサブネット内に1つのEC2インスタンスを作成(VPN経由でアクセスされるサーバー役) CloudFormationテンプレート オンプレミス環境用テンプレート(Onpre_resource.yaml) AWSTemplateFormatVersion: '2010-09-09' Description: 'VPC with Public and Private Subnets, EC2 Instances, SSM access, and S3 Endpoint' Parameters: TagPrefix:   Type: String   Default: '00000'   Description: 'Prefix for resource tags' AmazonLinux2023AMI:   Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>   Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64 Resources: VPC:   Type: AWS::EC2::VPC   Properties:     CidrBlock: 10.0.0.0/22     EnableDnsHostnames: true     EnableDnsSupport: true     Tags:       - Key: Name         Value: !Sub '${TagPrefix}-VPC'   InternetGateway:   Type: AWS::EC2::InternetGateway   Properties:     Tags:       - Key: Name         Value: !Sub '${TagPrefix}-IGW'   InternetGatewayAttachment:   Type: AWS::EC2::VPCGatewayAttachment   Properties:     InternetGatewayId: !Ref InternetGateway       VpcId: !Ref VPC PublicSubnet:   Type: AWS::EC2::Subnet   Properties:     VpcId: !Ref VPC     AvailabilityZone: !Select [0, !GetAZs '']     CidrBlock: 10.0.1.0/24     MapPublicIpOnLaunch: true     Tags:       - Key: Name           Value: !Sub '${TagPrefix}-Public-Subnet' PrivateSubnet:   Type: AWS::EC2::Subnet   Properties:     VpcId: !Ref VPC     AvailabilityZone: !Select [1, !GetAZs '']     CidrBlock: 10.0.0.0/24     MapPublicIpOnLaunch: false     Tags:       - Key: Name           Value: !Sub '${TagPrefix}-Private-Subnet' PublicRouteTable:   Type: AWS::EC2::RouteTable   Properties:     VpcId: !Ref VPC     Tags:       - Key: Name           Value: !Sub '${TagPrefix}-Public-RT' PrivateRouteTable:   Type: AWS::EC2::RouteTable   Properties:     VpcId: !Ref VPC     Tags:       - Key: Name           Value: !Sub '${TagPrefix}-Private-RT' PublicRoute:   Type: AWS::EC2::Route   DependsOn: InternetGatewayAttachment   Properties:     RouteTableId: !Ref PublicRouteTable     DestinationCidrBlock: 0.0.0.0/0       GatewayId: !Ref InternetGateway PrivateRoute:   Type: AWS::EC2::Route   Properties:     RouteTableId: !Ref PrivateRouteTable     DestinationCidrBlock: 0.0.0.0/0       InstanceId: !Ref PublicEC2Instance PublicSubnetRouteTableAssociation:   Type: AWS::EC2::SubnetRouteTableAssociation   Properties:     SubnetId: !Ref PublicSubnet       RouteTableId: !Ref PublicRouteTable PrivateSubnetRouteTableAssociation:   Type: AWS::EC2::SubnetRouteTableAssociation   Properties:     SubnetId: !Ref PrivateSubnet       RouteTableId: !Ref PrivateRouteTable PublicSecurityGroup:   Type: AWS::EC2::SecurityGroup   Properties:     GroupName: !Sub '${TagPrefix}-Public-SG'     GroupDescription: 'Security group for public EC2 instance'     VpcId: !Ref VPC     SecurityGroupIngress:       - IpProtocol: -1         FromPort: -1         ToPort: -1         CidrIp: 10.0.0.0/22     SecurityGroupEgress:       - IpProtocol: -1         FromPort: -1         ToPort: -1         CidrIp: 0.0.0.0/0     Tags:       - Key: Name           Value: !Sub '${TagPrefix}-Public-SG' PrivateSecurityGroup:   Type: AWS::EC2::SecurityGroup   Properties:     GroupName: !Sub '${TagPrefix}-Private-SG'     GroupDescription: 'Security group for private EC2 instance'     VpcId: !Ref VPC     SecurityGroupIngress:       - IpProtocol: -1         FromPort: -1         ToPort: -1         SourceSecurityGroupId: !Ref PublicSecurityGroup     SecurityGroupEgress:       - IpProtocol: -1         FromPort: -1         ToPort: -1         CidrIp: 0.0.0.0/0     Tags:       - Key: Name           Value: !Sub '${TagPrefix}-Private-SG' EndpointSecurityGroup:   Type: AWS::EC2::SecurityGroup   Properties:     GroupName: !Sub '${TagPrefix}-Endpoint-SG'     GroupDescription: 'Security group for VPC Endpoints'     VpcId: !Ref VPC     SecurityGroupIngress:       - IpProtocol: tcp         FromPort: 443         ToPort: 443         SourceSecurityGroupId: !Ref PrivateSecurityGroup       - IpProtocol: tcp         FromPort: 443         ToPort: 443         SourceSecurityGroupId: !Ref PublicSecurityGroup     Tags:       - Key: Name           Value: !Sub '${TagPrefix}-Endpoint-SG' PublicEC2Instance:   Type: AWS::EC2::Instance   Properties:     InstanceType: t3.micro     ImageId: !Ref AmazonLinux2023AMI     SubnetId: !Ref PublicSubnet     SecurityGroupIds:       - !Ref PublicSecurityGroup     IamInstanceProfile: !Ref EC2InstanceProfile     SourceDestCheck: false     Tags:       - Key: Name           Value: !Sub '${TagPrefix}-Onpre-Public-EC2' PrivateEC2Instance:   Type: AWS::EC2::Instance   Properties:     InstanceType: t3.micro     ImageId: !Ref AmazonLinux2023AMI     SubnetId: !Ref PrivateSubnet     SecurityGroupIds:       - !Ref PrivateSecurityGroup     IamInstanceProfile: !Ref EC2InstanceProfile     Tags:       - Key: Name           Value: !Sub '${TagPrefix}-Onpre-Private-EC2' PublicEIP:   Type: AWS::EC2::EIP   Properties:     Domain: vpc     InstanceId: !Ref PublicEC2Instance     Tags:       - Key: Name           Value: !Sub '${TagPrefix}-Public-EIP' EC2InstanceProfile:   Type: AWS::IAM::InstanceProfile   Properties:     Path: "/"     Roles:         - !Ref EC2SSMRole S3Endpoint:   Type: AWS::EC2::VPCEndpoint   Properties:     VpcId: !Ref VPC     ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'     VpcEndpointType: Gateway     RouteTableIds:         - !Ref PrivateRouteTable EC2SSMRole:   Type: AWS::IAM::Role   Properties:     AssumeRolePolicyDocument:       Version: "2012-10-17"       Statement:         - Effect: Allow           Principal:             Service:               - ec2.amazonaws.com           Action:             - sts:AssumeRole     ManagedPolicyArns:       - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore       - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess     Tags:       - Key: Name           Value: !Sub '${TagPrefix}-EC2-SSM-Role' SSMEndpoint:   Type: AWS::EC2::VPCEndpoint   Properties:     ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ssm'     VpcId: !Ref VPC     VpcEndpointType: Interface     PrivateDnsEnabled: true     SubnetIds:       - !Ref PrivateSubnet       - !Ref PublicSubnet     SecurityGroupIds:         - !Ref EndpointSecurityGroup SSMMessagesEndpoint:   Type: AWS::EC2::VPCEndpoint   Properties:     ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ssmmessages'     VpcId: !Ref VPC     VpcEndpointType: Interface     PrivateDnsEnabled: true     SubnetIds:       - !Ref PrivateSubnet       - !Ref PublicSubnet     SecurityGroupIds:         - !Ref EndpointSecurityGroup Outputs: PublicEC2InstanceId:   Description: 'Public EC2 Instance ID'   Value: !Ref PublicEC2Instance PrivateEC2InstanceId:   Description: 'Private EC2 Instance ID'   Value: !Ref PrivateEC2Instance PublicEIP:   Description: 'Elastic IP for Public EC2 Instance'   Value: !Ref PublicEIP AWS環境用テンプレート(AWS_resource.yaml) AWSTemplateFormatVersion: '2010-09-09' Description: 'VPC with Private Subnet, EC2 Instance, Site-to-Site VPN, and S3 Endpoint' Parameters: TagPrefix:   Type: String   Default: '00000'   Description: 'Prefix for resource tags' CustomerGatewayIp:   Type: String   Description: 'Public IP address of your Customer Gateway' AmazonLinux2023AMI:   Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>   Default: '/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64'   Description: 'Amazon Linux 2023 AMI ID' Resources: VPC:   Type: AWS::EC2::VPC   Properties:     CidrBlock: 192.168.0.0/22     EnableDnsHostnames: true       EnableDnsSupport: true     Tags:       - Key: Name         Value: !Sub '${TagPrefix}-VPC'   PrivateSubnet:   Type: AWS::EC2::Subnet   Properties:     VpcId: !Ref VPC     AvailabilityZone: !Select [0, !GetAZs '']     CidrBlock: 192.168.0.0/24     MapPublicIpOnLaunch: false     Tags:       - Key: Name         Value: !Sub '${TagPrefix}-Private-Subnet'   PrivateRouteTable:   Type: AWS::EC2::RouteTable   Properties:     VpcId: !Ref VPC     Tags:       - Key: Name         Value: !Sub '${TagPrefix}-Private-RT'   PrivateSubnetRouteTableAssociation:   Type: AWS::EC2::SubnetRouteTableAssociation   Properties:     SubnetId: !Ref PrivateSubnet     RouteTableId: !Ref PrivateRouteTable   VPNSecurityGroup:   Type: AWS::EC2::SecurityGroup   Properties:     GroupName: !Sub '${TagPrefix}-VPN-SG'     GroupDescription: 'Security group for VPN connection and SSM'     VpcId: !Ref VPC     SecurityGroupIngress:       - IpProtocol: -1         FromPort: -1         ToPort: -1         CidrIp: 10.0.0.0/22       - IpProtocol: tcp         FromPort: 443         ToPort: 443         CidrIp: 192.168.0.0/22     Tags:       - Key: Name         Value: !Sub '${TagPrefix}-VPN-SG'   EC2Instance:   Type: AWS::EC2::Instance   Properties:     InstanceType: t3.micro     ImageId: !Ref AmazonLinux2023AMI     SubnetId: !Ref PrivateSubnet     SecurityGroupIds:       - !Ref VPNSecurityGroup     IamInstanceProfile: !Ref EC2InstanceProfile     Tags:       - Key: Name         Value: !Sub '${TagPrefix}-AWS-Private-EC2'   EC2InstanceProfile:   Type: AWS::IAM::InstanceProfile   Properties:     Path: "/"     Roles:       - !Ref EC2SSMRole   S3Endpoint:   Type: AWS::EC2::VPCEndpoint   Properties:     VpcId: !Ref VPC     ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'     VpcEndpointType: Gateway     RouteTableIds:       - !Ref PrivateRouteTable   EC2SSMRole:   Type: AWS::IAM::Role   Properties:     AssumeRolePolicyDocument:       Version: "2012-10-17"       Statement:         - Effect: Allow           Principal:             Service:               - ec2.amazonaws.com           Action:             - sts:AssumeRole     ManagedPolicyArns:       - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore       - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess     Tags:       - Key: Name         Value: !Sub '${TagPrefix}-EC2-SSM-Role'   CustomerGateway:   Type: AWS::EC2::CustomerGateway   Properties:     Type: ipsec.1     BgpAsn: 65000     IpAddress: !Ref CustomerGatewayIp     Tags:       - Key: Name         Value: !Sub '${TagPrefix}-CustomerGateway'   VirtualPrivateGateway:   Type: AWS::EC2::VPNGateway   Properties:     Type: ipsec.1     Tags:       - Key: Name         Value: !Sub '${TagPrefix}-VPNGateway'   VPNGatewayAttachment:   Type: AWS::EC2::VPCGatewayAttachment   Properties:     VpcId: !Ref VPC     VpnGatewayId: !Ref VirtualPrivateGateway   VPNConnection:   Type: AWS::EC2::VPNConnection   DependsOn:     - VirtualPrivateGateway     - VPNGatewayAttachment   Properties:     Type: ipsec.1     CustomerGatewayId: !Ref CustomerGateway     VpnGatewayId: !Ref VirtualPrivateGateway     StaticRoutesOnly: true     Tags:       - Key: Name         Value: !Sub '${TagPrefix}-VPNConnection'   VPNConnectionRoute:   Type: AWS::EC2::VPNConnectionRoute   DependsOn: VPNConnection   Properties:     DestinationCidrBlock: 10.0.0.0/22     VpnConnectionId: !Ref VPNConnection   VPNRoute:   Type: AWS::EC2::Route   DependsOn: VPNGatewayAttachment   Properties:     RouteTableId: !Ref PrivateRouteTable     DestinationCidrBlock: 10.0.0.0/22     GatewayId: !Ref VirtualPrivateGateway   SSMEndpoint:   Type: AWS::EC2::VPCEndpoint   Properties:     VpcId: !Ref VPC     ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ssm'     VpcEndpointType: Interface     PrivateDnsEnabled: true     SubnetIds:       - !Ref PrivateSubnet     SecurityGroupIds:       - !Ref VPNSecurityGroup   EC2MessagesEndpoint:   Type: AWS::EC2::VPCEndpoint   Properties:     VpcId: !Ref VPC     ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ec2messages'     VpcEndpointType: Interface     PrivateDnsEnabled: true     SubnetIds:       - !Ref PrivateSubnet     SecurityGroupIds:       - !Ref VPNSecurityGroup   SSMMessagesEndpoint:   Type: AWS::EC2::VPCEndpoint   Properties:     VpcId: !Ref VPC     ServiceName: !Sub 'com.amazonaws.${AWS::Region}.ssmmessages'     VpcEndpointType: Interface     PrivateDnsEnabled: true     SubnetIds:       - !Ref PrivateSubnet     SecurityGroupIds:       - !Ref VPNSecurityGroup Outputs: EC2InstanceId:   Description: 'EC2 Instance ID'   Value: !Ref EC2Instance CustomerGatewayId:   Description: 'Customer Gateway ID'   Value: !Ref CustomerGateway VirtualPrivateGatewayId:   Description: 'Virtual Private Gateway ID'   Value: !Ref VirtualPrivateGateway VPNConnectionId:   Description: 'VPN Connection ID'   Value: !Ref VPNConnection 環境構築手順 なるべく少ない手数で再現性のある構築を進めるため、CloudShellでAWS CLIを用いて作業します。 AWSマネジメントコンソールの上部または左下の「>_」マークをクリックします。 立ち上がってきたCloudShell画面で「Open <リージョン名> environment」をクリックします。 Shellを打ち込める画面が出てきます。 # タグのプレフィックスを設定(任意の文字列を指定してください) TAGPREFIX=00000 # オンプレ側構築 aws cloudformation deploy \     --stack-name "VPNtest-Onpre-${TAGPREFIX}" \     --template-file Onpre_resource.yaml \     --capabilities CAPABILITY_IAM \     --parameter-overrides TagPrefix=${TAGPREFIX} # オンプレ側のVPN機器となるEC2インスタンスのGlobalIPを取得 GLOBALIP=`aws ec2 describe-addresses --query 'Addresses[].PublicIp' --filter "Name=tag:Name,Values=${TAGPREFIX}-Public-EIP" --output text` && echo ${GLOBALIP} # AWS側構築 aws cloudformation deploy \     --stack-name "VPNtest-AWS-${TAGPREFIX}" \     --template-file AWS_resource.yaml \     --capabilities CAPABILITY_IAM \   --parameter-overrides TagPrefix=${TAGPREFIX} CustomerGatewayIp=${GLOBALIP} VPN設定 AWS Site-to-Site VPNではオンプレ側VPN機器の設定サンプルをダウンロードできます。 オンプレ側ではソフトウェアVPNのLibreswanを使用しますが、該当するものがないので類似するOpenswanの設定サンプルをダウンロードします。 VPN設定ダウンロード [VPC] – [Site-to-Site VPN 接続] [設定をダウンロードする] [ベンダー] Openswan [プラットフォーム] Openswan [ソフトウェア] Openswan 2.6.38+ [IKEバージョン] ikev1 を指定し、[ダウンロード] ここでダウンロードした設定ファイルの一部を用いて、Libreswanの設定をしていきます。 Libreswan設定 LibreswanからAWS Site-to-Site VPNへの接続に利用するトンネルは1本のみとします。(2本用いるHA構成も可能だが「気軽な」検証にならないため) AWS Systems Manager Session Managerを用いてオンプレ側Public EC2インスタンスにログインします。 [EC2] – [インスタンス] より、ログインしたいインスタンスにチェックを入れ、[接続] をクリックします。 [セッションマネージャー] タブを選択し、[接続] をクリックします。 # Libreswanインストール sudo dnf install -y libreswan # カーネルパラメータ修正 echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.d/custom-ip-forwarding.conf sudo sysctl -p /etc/sysctl.d/custom-ip-forwarding.conf # Libreswan設定 sudo vi /etc/ipsec.conf config setup ブロックに下記設定のみを追記します。         protostack=netkey # ダウンロードしたVPN設定から接続部分を抜き出して貼り付け、内容を書き換える # Tunnel1の部分のみ。 sudo vi /etc/ipsec.d/aws.conf conn Tunnel1 authby=secret auto=start left=%defaultroute leftid= <オンプレ側のGlobalIP>   # 記載のものをそのまま入力 right= <AWS側のGlobalIP>   # 記載のものをそのまま入力 type=tunnel ikelifetime=8h keylife=1h phase2alg=aes128-sha1; modp2048  # 末尾のmodp1024をmodp2048に変更 ike=aes128-sha1; modp2048        # 末尾のmodp1024をmodp2048に変更 auth=esp                      # この行は削除 keyingtries=%forever keyexchange=ike leftsubnet=10.0.0.0/22          # leftがオンプレ側(今設定しているほう) rightsubnet=192.168.0.0/22      # rightがAWS側 dpddelay=10 dpdtimeout=30                   # この行は削除 retransmit-timeout=30s          # この行を追加 dpdaction=restart_by_peer # シークレット情報を張り付ける # ※Tunnel1もののみをそのまま貼り付け、Tunnel2のキーは貼り付けない sudo vi /etc/ipsec.d/aws.secrets <オンプレ側のGlobalIP> <AWS側のGlobalIP> : PSK " <キー情報> " sudo systemctl start ipsec sudo systemctl status ipsec exit VPNトンネル接続確認 [VPC] – [Site-to-Site VPN] 作成したVPN接続を選択 [トンネルの詳細] タブを選択 1つのトンネルが接続されていることを確認します。(リロードしながら少し待つ必要があります) もう一つはダウンのままで構いません。 接続確認 IPアドレスを確認 TAGPREFIX=00000 aws ec2 describe-instances \     --filters "Name=instance-state-name,Values=running" "Name=tag:Name,Values=*${TAGPREFIX}*" \     --query "Reservations[*].Instances[*].[PrivateIpAddress, Tags[?Key=='Name'].Value | [0]]" \   --output text 以下のようにIPアドレスが出力されます。このIPアドレスを用いて接続確認をしていきます。 10.0.0.148      00000-Onpre-Private-EC2 192.168.0.34   00000-AWS-Private-EC2 10.0.1.145      00000-Onpre-Public-EC2 通信確認 pingによる通信確認 AWS Systems Manager Session Managerを用いて オンプレ側プライベートEC2 へログインします。 # AWS側プライベートEC2のIPに向けて通信確認 ping 192.168.0.34 →オンプレ側からAWS側へ通信できることを確認します。 同じく、 AWS側プライベートEC2 へログインします。 ping 10.0.0.148 →AWS側からオンプレ側へ通信できることを確認します。 HTTPによる通信確認 AWS側プライベートEC2 で下記を実行します。 cd ~ touch iam_aws_private python3 -m http.server 8000 今度は、 オンプレ側プライベートEC2 で下記を実行します。 curl 192.168.0.34:8000 →ディレクトリの中身が見えることを確認します(VPN接続先のiam_aws_privateファイルが見える) AWS側プライベートEC2で、オンプレ側からアクセスがあったログを確認できます。 オンプレ側の通信確認 AWS側の通信確認 トラブルシューティング VPN接続で問題が発生した場合は、以下の点を確認してください セキュリティグループの設定 ルートテーブルの設定 Libreswanの設定ファイル VPNログ(sudo journalctl -u ipsec) クリーンアップ 検証が終わったら、以下のコマンドで環境を削除します # CloudFormationスタックを削除 aws cloudformation delete-stack --stack-name "VPNtest-Onpre-${TAGPREFIX}" aws cloudformation delete-stack --stack-name "VPNtest-AWS-${TAGPREFIX}" # スタック削除を待つ aws cloudformation wait stack-delete-complete --stack-name "VPNtest-Onpre-${TAGPREFIX}" & aws cloudformation wait stack-delete-complete --stack-name "VPNtest-AWS-${TAGPREFIX}" & wait # 2つのスタックが削除され、プロンプトが戻れば終了 CloudShellの終了は、CloudShell画面の [アクション] – [削除] を選択し、表示された確認画面で「delete」と入力し、[削除] ボタンを押します。 参考資料 ソフトウェア VPN から AWS Site-to-Site VPN https://docs.aws.amazon.com/ja_jp/whitepapers/latest/aws-vpc-connectivity-options/software-vpn-to-aws-site-to-site-vpn.html OS設定でNATインスタンスのチュートリアルを参考にしました(特にnet.ipv4.ip_forward=1の設定) https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/work-with-nat-instances.html SSMへの接続用VPCエンドポイントを作成の際、ec2messages:* エンドポイントは作成の必要がなくなりました。 https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-setting-up-messageAPIs.html SSM Agent のバージョン 3.3.40.0 以降、Systems Manager は、使用可能な場合には ec2messages:* エンドポイント (Amazon Message Delivery Service) の代わりに ssmmessages:* エンドポイント (Amazon Message Gateway Service) を使用するようになりました。   まとめ 今回の記事では、AWSでのSite-to-Site VPN接続の検証環境構築方法を紹介しました。CloudFormationを使うことで、複雑な環境も簡単に構築・管理できることがお分かりいただけたかと思います。 実際の本番環境では、セキュリティやパフォーマンスなどさらに考慮すべき点がありますが、この記事がVPN検証の参考になれば幸いです。
アバター
こんにちは、佐藤と申します! 今回が記念すべき初執筆! ということでつたない点もあるとは思いますが、少しでも面白いと思っていただける記事を書けるよう頑張ります! さて、今回は昨年末のAWS re:Inventにて、2025年からすべてのユーザーがクレジットカードなしで毎日無料で使えるようになるという素晴らしい発表があった PartyRock が主役。 PartyRockに理想のデートプランを考えさせてみた結果が……というお話です。ぜひ最後までお読みいただければ幸いです。   きっかけはこんな記事から… 56.4%――――。 これ何の数字かわかりますか? マイナビウーマンさんが『マイナビウーマン』にて2014年11月にとったWebアンケートによると、 56.4%の人がデートプランを前もって決めたい派 なんだそうです(参考 https://woman.mynavi.jp/article/141206-7/ )。皆さんはこの数字をどのように捉えるでしょうか? 確かにデートプランが事前にわかることでその日1日をどう過ごすのか、どんな服装でどこに集合すればいいのかを逆算することができますよね。 でも正直、 「デートプランを考えるの、めんどくさいんだよな…」 って方も少なくないのではないでしょうか? それならば!お手伝いしてもらいましょう! 誰に…? もちろんPartyRockにです!   ちなみにPartyRockってなに…? 初めて聞いた方のために、PartyRockについて簡単に補足を入れておきます。 PartyRockとは、 AWSが提供する生成AIアプリケーションをプロンプトのみ、ノーコードで作成することのできるサービス です。 こちらの公式ドキュメントから詳しい使い方等は確認いただけます。→  https://aws.amazon.com/jp/blogs/news/create-generative-ai-app-with-partyrock-amazon-bedrock/ PartyRockは、同じくAWSが提供する生成AIサービスであるAmazon Bedrockと統合されているため、 さまざまなAIモデルを使用することができ 、基本操作は テキスト入力 とUIをいじるための ドラッグアンドドロップ が主となんとも使いやすい仕様なのが特徴です。 2024年までは無料トライアル期間を設けており、その期間で限られたトークン分しか使用することができませんでしたが、上述の通り、 昨年末のAWS re:Inventでトライアル期間を気にせずに使用できることが発表されました。 →  https://aws.amazon.com/jp/about-aws/whats-new/2024/12/partyrock-app-discovery-upcoming-free-daily-use/ まさに触ってみなきゃ損!なサービスと言っても過言ではないのがPartyRockなんです。 PartyRockを最強のデートプランナーにしてみる PartyRockにログインしてみよう そうと決まれば、早速PartyRockにログインしてみましょう。 PartyRockへはこちらのURLからアクセスすることができます→  https://partyrock.aws/ 左上の 「Log in」 ボタンをクリックするとこのようなログイン画面が表示されます。 PartyRockへのログインは、 Googleアカウント 、 Appleアカウント からでも可能です! AWSアカウントをまだお持ちでない方でも試せるのはありがたいですよね。 早速ログインができたら、 「Generate app」 ボタンからアプリを作っていきましょう。   プロンプトからアプリを作成しよう するとアプリ構築のベースとなるプロンプトを入力する画面が表示されるので、大まかな目的や、ほしい機能を記載していきましょう。 今回はこんなプロンプトを作成してみました。 あなたは世界一のデートプランナーです。絶対に失敗できないデートがあります。相手の心をつかみ、二人の距離を一気に縮めるような絶対に成功するデートプランを作成してください。当日の詳細な流れ、おすすめスポット、会話のヒントを具体的かつ分かりやすく記述してください。 場所:@場所 予算:@予算 相手の趣味:@相手の趣味 キーワード:@キーワード(例:ロマンチック、アウトドア、サプライズ、音楽) 時間帯:@時間帯 ちなみに後半に一部入っている 「@」 があると思いますが、これは場所~時間帯までの要素が 変数(プレイスホルダー) であることをAIに明示するためのものです。 プロンプトを受け取るAI側は、これらがユーザーが定義した値が入るものとして結果を出力します。 これによりユーザーは変数に好きな条件を入れてデートプランを作成することができるようになるのです。 さて、30秒ほど待つと具体的なデートプランを考えるためのユーザー入力画面が出力されます。 先ほど指示した場所から時間帯まで5つの入力項目があるので、今回はこんな条件を入れてみました。 デートの場所:東京。できるだけ歩いて移動できる距離がいいかな。 予算:30000円くらい。 相手の趣味:音楽。音楽に関するデートができたら理想的。 キーワード:ロマンチック、サプライズ 時間帯:終日。お昼ごろから夜までで考えてみて。   予算30,000円はさすがにやりすぎましたかね…笑 私は普段からかなり音楽を聴くので、今回は音楽に関連するデートということでPartyRockにお願いしてみました。 果たしてその結果は…… 理想のデートプラン、その結果は…? PartyRock、只者ではないな……? まずは今回のデートの概要から。 音楽をテーマにした、ロマンチックで思い出に残る東京デートプランです。 昼から夜にかけて、音楽関連スポットを巡り、ライブ演奏を楽しみ、最後はサプライズで締めくくります。 お、なかなか悪くないですね。 これはかなり良い出力を期待してしまいます。 次が最も大切。1日のスケジュールです。 12:00 – 渋谷駅集合 12:15 – タワーレコード渋谷店でショッピング 13:30 – 代官山のカフェでランチ 15:00 – 恵比寿ガーデンプレイスでストリートミュージシャン鑑賞 16:30 – 目黒川沿いを散歩 18:00 – 中目黒のジャズバーでディナーとライブ 21:00 – 東京タワーで夜景鑑賞 22:30 – デート終了 思いのほかよさそうな気がしますね…笑 てっきり「いや、場所東京じゃないんかい!!」ってツッコミが必要かと思っていたのでこれは意外でした。 時間帯も昼から夜にかけてと完璧な出来です。 よくみると15時から恵比寿ガーデンプレイスで1時間近くストリートミュージシャンを眺める謎の時間発生してたりしますが…… しかもPartyRock、具体的な場所まで提案してくれているんです。 例えば13:30からの代官山のカフェでのランチ。 ここでは実際に渋谷に存在する「 茶亭 羽當」さんを紹介していました。 茶亭 羽當さんについてはこちらから→   https://tabelog.com/tokyo/A1303/A130301/13001169/ ちなみにPartyRockいわく、 おしゃれな雰囲気で 軽いランチを楽しめる BGMも洗練されており、 音楽好きにぴったり とのことで、音楽にもきちんと絡めてくれています。すごい…… しかし思いがけない落とし穴が… PartyRockに感心するのも束の間、1つ 大きな落とし穴 がありました。 それが、 移動距離 です。 今回のデートでは、「できるだけ歩いて行ける距離で」という条件を付けました。 実際にPartyRockが提案してくれたルートを調べてみると… 総移動距離、驚異の 14.2km 。 約3時間半 を徒歩移動に費やす計算になります。ちょっと歩けないかも…… 特に遠かったのが18時半から行く予定のジャズバー。 ということでPartyRockに場所を変えてほしいと要望を伝えてみると… なんと新たに3か所、候補となるジャズバーを紹介してくれました! ですが、なぜかすべて中目黒。なぜか渋谷方面には寄ってくれませんでした…。 PartyRockではこんな設定も可能なんです ちなみにPartyRockが最初に出力してくれたユーザー画面ですが、これも プロンプトを変更して出力を調整することも可能 です。 さらには 使用するAIモデルまで選択可能! デフォルトはClaudeになっていますが、Amazon Novaに変更して出力してみるとこんな感じになりました。 12:00 – 渋谷駅集合 12:15 – タワーレコード渋谷店でショッピング 13:30 – 代官山のカフェでランチ 15:00 – 恵比寿ガーデンプレイスで ウィンドウショッピングと散策 17:00 – 代官山蔦屋書店で音楽関連の本を探索 18:30 – 中目黒のイタリアンレストランでディナー 20:30 – 中目黒川沿いの散歩とサプライズライブ 何かあまり変わっていないような…笑 恵比寿ガーデンプレイスでショッピングを追加してくれたことは救いでしょうか…? また、 作ったアプリケーションはURLで共有することも可能 です。 URLで簡単に共有ができるお手軽さも素晴らしいです! 結論、PartyRockは… 実際にPartyRockを使ってみて、 生成AIサービス、特にアプリケーションを作成する類の中ではかなり使いやすい なというのが正直な感想です。 日本語対応 していて UIもかなり使いやすい 、 URLで簡単に共有 できる点も利用までの心理的ハードルが低くなるポイントだったと感じています。 そして何より一番の気づきは、 PartyRockは中目黒がおしゃれの頂点だと思っている説 …笑 何度か試しましたが渋谷・目黒エリアしか出してくれず、思いがけずPartyRockの好みまでわかってしまいました。 最後まで読んでいただきありがとうございました。 少しでも興味を持った方はぜひPartyRockでアイデアを形にする楽しさを味わってみてはいかがでしょうか?
アバター
こんにちは。SCSK株式会社の上田です。 Zabbixに関する、 深刻な脆弱性 が発表されました。 この脆弱性は、 Zabbixのバージョン5.0.45、6.0.33、6.4.18、7.0.3までのバージョン に影響します。 Zabbixをご利用の方は一度ご覧いただき、対象の場合はアップデート等のご検討お願い致します。 脆弱性情報 概要 HTTPリクエストオブジェクトのサーバーレスポンスヘッダー取得機能に脆弱性があり、受信したレスポンスのヘッダー内にある一部文字列の安全性チェックに不足があります。ヘッダー内に悪意のある文字列が入力されることにより、 Zabbixサーバ上でメモリの内容を読み取ったり、任意のコードを実行する恐れ があります。 対象コンポーネント Zabbixサーバ 対象バージョン 以下の Zabbix バージョンが影響を受けます。 Zabbix 5.0.0 – 5.0.45 Zabbix 6.0.0 – 6.0.33 Zabbix 6.4.0 – 6.4.18 Zabbix 7.0.0 – 7.0.3 影響 攻撃者が細工したHTTPヘッダーを送信することで、サーバー上の機密情報にアクセスしたり、任意のコードを実行したりする可能性があります。 回避方法 この脆弱性を回避するためには、Zabbixの最新バージョンにアップデートすることが推奨されます。 Zabbix 5.0.46以降 Zabbix 6.0.34以降 Zabbix 6.4.19以降 Zabbix 7.0.4移行 最後に 弊社では本件に関してのお問い合わせもお受けしております。 ご相談事項がございましたら、以下ページよりお問い合わせいただければと思います。 お問い合わせ 製品・サービスについて 入力 | SCSK株式会社 SCSK株式会社 製品・サービスについてご意見・ご質問をお受けしております。 www.scsk.jp また、弊社では Zabbixのバージョンアップの支援 も行っております。 ご興味のある方は、是非弊社までお問合せください。 最後まで読んでいただき、ありがとうございました。 弊社ではZabbix関連サービスを展開しています。以下ページもご参照ください。 ★Zabbixの基礎をまとめたeBookを公開しております!★ FAQ・お問い合わせ|SCSK Plus サポート for Zabbix 導入をご検討される際、よくあるご質問と回答についてまとめております。 www.scsk.jp ★SCSK Plus サポート for Zabbix★ SCSK Plus サポート for Zabbix 世界で最も人気のあるオープンソース統合監視ツール「Zabbix」の導入構築から運用保守までSCSKが強力にサポートします www.scsk.jp ★YouTubeに、SCSK Zabbixチャンネルを開設しました!★ SCSK Zabbixチャンネル 本チャンネルでは、SCSK株式会社でのZabbixに関するトレンド/事例紹介などを動画にまとめて取り上げております。最新のトピックについては、以下の弊社HPもしくはツイッターアカウントをぜひ参照ください。ツイッターアカウント: www.youtube.com ★X(旧Twitter)に、SCSK Zabbixアカウントを開設しました!★ x.com x.com
アバター