Author: AWS Japan Staff


【変更版】11 月の AWS Black Belt オンラインセミナーのご案内

※11/20追記

直前のお知らせとなり申し訳ありません。11/21(火) 12:00-13:00に開催を予定しておりました「AWS上の位置情報」ですが、講師の都合により延期となりました。事前登録いただいておりました皆様、申し訳ございませんでした。また、改めて開催予定日を案内させていただきます。

 

こんにちは。ソリューションアーキテクトの岡本です。AWS Black Belt オンラインセミナー11月の配信についてご案内させて頂きます。初めてご紹介するNLB (Network Load Balancer) を中心としたELBのセッションをはじめ、今月も様々なテーマを取り扱います。

また今年もAWS re:Inventの開催期間中に現地からの生中継で最新アップデートをお伝えする回を予定しておりますので、こちらもぜひご登録いただければと思います。

 

 

 

 

 

 

 

 

 

11月の開催予定

サービスカット
11/1(水) 18:00-19:00 Amazon EMR
11/15(水) 18:00-19:00 ELB Update – Network Load Balancer(NLB)と関連サービス
11/22(水) 18:00-19:00 AWS WAF – OWASP Top10脆弱性緩和策 –

ソリューションカット
11/9(木) 12:00-13:00 Amazon Pinpoint で始めるモバイルアプリのグロースハック  ※ 通常の開催曜日と異なりますのでご注意ください
12/1(金) 12:00-13:00 AWS re:Invent 2017 Report  ※ 通常の開催曜日と異なりますのでご注意ください。また12月開催ですが先行告知させて頂きます

お申し込みは、それぞれ上記のリンクより行って頂けます。キャンセルの際も連絡不要ですので是非お早めにご登録ください。Speaker、Staff 一同みなさまのご参加をお待ちしております。

 

Amazon DynamoDB からのデータストリームを AWS Lambda と Amazon Kinesis Firehose を活用して Amazon Aurora に格納する

Aravind Kodandaramaiah は AWS パートナープログラムのパートナーソリューションアーキテクトです。

はじめに

AWS ワークロードを実行するお客様は Amazon DynamoDBAmazon Aurora の両方を使用していることがよくあります。Amazon DynamoDB は、どのような規模でも、一貫した、数ミリ秒台にレイテンシーを抑える必要のあるアプリケーションに適した、高速で柔軟性の高い NoSQL データベースサービスです。データモデルの柔軟性が高く、パフォーマンスが信頼できるため、モバイル、ウェブ、ゲーム、広告、IoT、他の多くのアプリケーションに最適です。

Amazon Aurora は、MySQL と互換性のあるリレーショナルデータベースエンジンで、オープンソースデータベースのコスト効率性と簡素性を備えた、高性能の商用データベースの可用性とスピードをあわせもったエンジンです。Amazon Aurora は、MySQL よりも最大 5 倍のパフォーマンスを発揮するだけでなく、商用データベースのセキュリティ、可用性、および信頼性を 10 分の 1 のコストで実現します。

DynamoDB と Aurora を連携させるために、カスタムウェブ解析エンジンを構築して、毎秒数百万のウェブクリックが DynamoDB に登録されるようにしたとします。Amazon DynamoDB はこの規模で動作し、データを高速に取り込むことができます。また、このクリックストリームデータを Amazon Aurora などのリレーショナルデータベース管理システム (RDBMS) にレプリケートする必要があるとします。さらに、ストアドプロシージャまたは関数内で SQL の機能を使用して、このデータに対してスライスアンドダイスや、さまざまな方法でのプロジェクションを行ったり、他のトランザクション目的で使用したりするとします。

DynamoDB から Aurora に効率的にデータをレプリケートするには、信頼性の高いスケーラブルなデータレプリケーション (ETL) プロセスを構築する必要があります。この記事では、AWS Lambda Amazon Kinesis Firehose によるサーバーレスアーキテクチャを使用して、このようなプロセスを構築する方法について説明します。

ソリューションの概要

以下の図に示しているのは、このソリューションのアーキテクチャです。このアーキテクチャの背後には、次のような動機があります。

  1. サーバーレス – インフラストラクチャ管理を AWS にオフロードすることで、メンテナンスゼロのインフラストラクチャを実現します。ソリューションのセキュリティ管理を簡素化します。これは、キーやパスワードを使用する必要がないためです。また、コストを最適化します。さらに、DynamoDB Streams のシャードイテレーターに基づいた Lambda 関数の同時実行により、スケーリングを自動化します。
  2. エラー発生時に再試行可能 – データ移動プロセスには高い信頼性が必要であるため、プロセスは各ステップでエラーを処理し、エラーが発生したステップを再試行できる必要があります。このアーキテクチャではそれが可能です。
  3. 同時データベース接続の最適化 – 間隔またはバッファサイズに基づいてレコードをバッファすることで、Amazon Aurora への同時接続の数を減らすことができます。この方法は、接続タイムアウトを回避するのに役立ちます。
  4. 懸念部分の分離 – AWS Lambda を使用すると、データレプリケーションプロセスの各懸念部分を分離できます。たとえば、抽出フェーズを DynamoDB ストリームの処理として、変換フェーズを Firehose-Lambda 変換として、ロードフェーズを Aurora への一括挿入として分離できます。

以下に示しているのは、このソリューションのしくみです。

  1. DynamoDB Streams がデータソースです。DynamoDB Streams を使用すると、DynamoDB テーブル内の項目が変更されたときに、その変更を取得できます。AWS Lambda は、新しいストリームレコードを検出すると、Lambda 関数を同期的に呼び出します。
  2. Lambda 関数は、DynamoDB テーブルに新たに追加された項目をバッファし、これらの項目のバッチを Amazon Kinesis Firehose に送ります。
  3. Firehose は、受け取ったデータを Lambda 関数により変換して、Amazon S3 に配信します。Firehose に対してデータ変換を有効にしていると、Firehose は受け取ったデータをバッファし、バッファしたデータのバッチごとに、指定された Lambda 関数を非同期的に呼び出します。変換されたデータは Lambda から Firehose に返されてバッファされます。
  4. Firehose は変換されたすべてのレコードを S3 バケットに配信します。
  5. Firehose は変換されないすべてのレコードも S3 バケットに配信します。ステップ 4 と 5 は同時に実行されます。Amazon SNS トピックをこの S3 バケットに登録して、以降の通知、修復、再処理に使用できます (通知に関する詳細はこのブログ記事では取り上げません)。
  6. Firehose が変換されたデータを S3 に正常に配信するたびに、S3 はイベントを発行することで Lambda 関数を呼び出します。この Lambda 関数は VPC 内で実行されます。
  7. Lambda 関数は Aurora データベースに接続し、SQL 式を実行して、S3 から直接テキストファイルにデータをインポートします。
  8. Aurora (VPC プライベートサブネット内で実行) は、S3 VPC エンドポイントを使用して S3 からデータをインポートします。

ソリューションの実装とデプロイ
次に、このソリューションを機能させるために必要な手順について説明します。以下の手順では、AWS CloudFormation スタックを起動して一連の AWS CLI コマンドを実行することで、VPC 環境を作成する必要があります。

AWS サービスを使用してこれらの手順を実行している間、AWS サービスの料金が適用されることがあります。

ステップ 1: ソリューションのソースコードをダウンロードする
このブログ記事で概説したソリューションでは、多くの Lambda 関数を使用し、また、多くの AWS Identity and Access Management (IAM) ポリシーおよびロールを作成します。このソリューションのソースコードは以下の場所からダウンロードします。

git clone https://github.com/awslabs/dynamoDB-data-replication-to-aurora.git

このリポジトリには、以下のフォルダ構造があります。このブログ記事の後続の手順を実行するために、lambda_iam フォルダに移動します。

ステップ 2: Firehose 配信用の S3 バケットを作成する
Amazon Kinesis Firehose を使用すると、Amazon S3 にリアルタイムのストリーミングデータを配信できます。そのためには、まず S3 バケットを作成します。次に、レコードの処理に失敗した場合に備えて、変換された最終のレコードとデータバックアップを保存するフォルダを作成します。

aws s3api create-bucket --bucket bucket_name

aws s3api put-object \
--bucket bucket_name \
--key processed/

aws s3api put-object \
--bucket bucket_name
--key tranformation_failed_data_backup/

 

ステップ 3: IAM ポリシー、S3 イベント通知、Firehose-S3 配信設定ファイルを変更する
次に、以下のファイルで、プレースホルダー AWS_REGION、

AWS_ACCOUNT_NUMBER、BUCKET_NAME をそれぞれ、お客様の AWS リージョン ID、AWS アカウント番号、ステップ 2 で作成した S3 バケットの名前に置き換えます。

 

·         aurora-s3-access-Policy.json

·         DynamoDb-Stream-lambda-AccessPolicy.json

·         firehose_delivery_AccessPolicy.json

·         lambda-s3-notification-config.json

·         s3-destination-configuration.json

·         firehose_delivery_trust_policy.json

ステップ 4: CloudFormation を使用して Aurora クラスターを設定する
次に、[Launch Stack] ボタンをクリックして AWS CloudFormation スタックを起動します。CloudFormation テンプレートは、VPC を作成し、その VPC のパブリックおよびプライベートサブネットを設定します。また、このテンプレートは、プライベートサブネット内で Amazon Aurora データベースクラスターを起動し、パブリックサブネット内でパブリック IP を割り当てた踏み台ホストも起動します。


ステップ 5: Aurora DB クラスターを設定する
CloudFormation スタックが完成したら、S3 バケット内のテキストファイルから DB クラスターにデータをロードするように、Aurora クラスターを変更する必要があります。以下に示しているのは、そのための手順です。

Amazon Aurora が Amazon S3 にアクセスできるようにします。そのためには、IAM ロールを作成し、先ほど作成した信頼およびアクセスポリシーをそのロールにアタッチします。

auroraS3Arn=$(aws iam create-role \
    --role-name aurora_s3_access_role \
    --assume-role-policy-document file://aurora-s3-Trust-Policy.json \
    --query 'Role.Arn' \
    --output text)

aws iam put-role-policy \
    --role-name aurora_s3_access_role \
    --policy-name aurora-s3-access-Policy \
    --policy-document file://aurora-s3-access-Policy.json

 

その IAM ロールを Aurora DB クラスターに関連付けます。そのためには、新しい DB クラスターパラメータグループを作成し、その DB クラスターに関連付けます。

aws rds add-role-to-db-cluster \
    --db-cluster-identifier Output AuroraClusterId from CloudFormation Stack \
    --role-arn $auroraS3Arn

aws rds create-db-cluster-parameter-group \
    --db-cluster-parameter-group-name webAnayticsclusterParamGroup \
    --db-parameter-group-family aurora5.6 \
    --description 'Aurora cluster parameter group - Allow access to Amazon S3'

aws rds modify-db-cluster-parameter-group \
    --db-cluster-parameter-group-name webAnayticsclusterParamGroup \
    --parameters "ParameterName=aws_default_s3_role,ParameterValue= $auroraS3Arn,ApplyMethod=pending-reboot"

aws rds modify-db-cluster \
	--db-cluster-identifier Output AuroraClusterId from CloudFormation Stack \
	--db-cluster-parameter-group-name webAnayticsclusterParamGroup

 

プライマリ DB インスタンスを再起動します。

aws rds reboot-db-instance \
--db-instance-identifier Output PrimaryInstanceId from CloudFormationF Stack 

ステップ 6: DynamoDB ストリームと、そのストリームを処理する Lambda 関数を設定する

1.    ストリームを有効にして新しい DynamoDB テーブルを作成します。以降の手順では、AWS Lambda 関数をストリームに関連付けることで、トリガーを作成します。

aws dynamodb create-table \
    --table-name web_analytics \
    --attribute-definitions AttributeName=page_id,AttributeType=S AttributeName=activity_dt,AttributeType=S \
    --key-schema AttributeName=page_id,KeyType=HASH AttributeName=activity_dt,KeyType=RANGE \
    --provisioned-throughput ReadCapacityUnits=50,WriteCapacityUnits=50 \
    --stream-specification StreamEnabled=true,StreamViewType=NEW_IMAGE

2.    Lambda 実行ロールを作成します。

DdbStreamLambdaRole=$(aws iam create-role \
    --role-name DdbStreamLambdaRole \
    --assume-role-policy-document file://DynamoDB-Stream-lambda-Trust-Policy.json \
    --query 'Role.Arn' \
    --output text)


aws iam put-role-policy \
    --role-name DdbStreamLambdaRole \
    --policy-name DdbStreamProcessingAccessPolicy \
    --policy-document file://DynamoDb-Stream-lambda-AccessPolicy.json

3.  DynamoDB ストリームを処理する Lambda 関数を作成します。

aws lambda create-function \
    --function-name WebAnalyticsDdbStreamFunction \
    --zip-file fileb://ddbStreamProcessor.zip \
    --role $DdbStreamLambdaRole \
    --handler ddbStreamProcessor.handler \
    --timeout 300 \
    --runtime nodejs4.3

 

4.  その Lambda 関数を DynamoDB ストリームに関連付けることで、トリガーを作成します。

tableStreamArn=$(aws dynamodb describe-table --table-name web_analytics --query 'Table.LatestStreamArn' --output text)

aws lambda create-event-source-mapping \
    --function-name WebAnalyticsDdbStreamFunction \
    --event-source-arn $tableStreamArn \
    --batch-size 100 \
    --starting-position LATEST

ステップ 7: Firehose のデータ変換 Lambda 関数を作成して設定する

Lambda 実行ロールを作成します。

transRole=$(aws iam create-role \
            --role-name firehose_delivery_lambda_transformation_role \
            --assume-role-policy-document  file://firehose_lambda_transformation_trust_policy.json \
            --query 'Role.Arn' --output text)

aws iam put-role-policy \
        --role-name firehose_delivery_lambda_transformation_role \
        --policy-name firehose_lambda_transformation_AccessPolicy \
        --policy-document file://firehose_lambda_transformation_AccessPolicy.json

2.  データ変換 Lambda 関数を作成します。

aws lambda create-function \
    --function-name firehoseDeliveryTransformationFunction \
    --zip-file fileb://firehose_delivery_transformation.zip \
    --role $transRole \
    --handler firehose_delivery_transformation.handler \
    --timeout 300 \
    --runtime nodejs4.3 \
    --memory-size 1024

この関数は、受け取ったストリームのレコードを JSON スキーマに対して検証します。スキーマに一致したら、受け取った JSON レコードを解析し、カンマ区切り値 (CSV) 形式に変換します。

'use strict';

var jsonSchema = require('jsonschema');

var schema = { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "Hits": { "type": "integer" }, "device": { "type": "object", "properties": { "make": { "type": "string" }, "platform": { "type": "object", "properties": { "name": { "type": "string" }, "version": { "type": "string" } }, "required": ["name", "version"] }, "location": { "type": "object", "properties": { "latitude": { "type": "string" }, "longitude": { "type": "string" }, "country": { "type": "string" } }, "required": ["latitude", "longitude", "country"] } }, "required": ["make", "platform", "location"] }, "session": { "type": "object", "properties": { "session_id": { "type": "string" }, "start_timestamp": { "type": "string" }, "stop_timestamp": { "type": "string" } }, "required": ["session_id", "start_timestamp", "stop_timestamp"] } }, "required": ["Hits", "device", "session"] };

exports.handler = (event, context, callback) => {
    let success = 0; // Number of valid entries found
    let failure = 0; // Number of invalid entries found

    const output = event.records.map((record) => {

        const entry = (new Buffer(record.data, 'base64')).toString('utf8');

        var rec = JSON.parse(entry);
        console.log('Decoded payload:', entry);
        var milliseconds = new Date().getTime();

        var payl = JSON.parse(rec.payload.S);

        var jsonValidator = new jsonSchema.Validator();
        var validationResult = jsonValidator.validate(payl, schema);
        console.log('Json Schema Validation result = ' + validationResult.errors);

        if (validationResult.errors.length === 0) {

            const result = `${milliseconds},${rec.page_id.S},${payl.Hits},
				${payl.session.start_timestamp},
				${payl.session.stop_timestamp},${payl.device.location.country}` + "\n";

            const payload = (new Buffer(result, 'utf8')).toString('base64');
            console.log(payload);
            success++;

            return {
                recordId: record.recordId,
                result: 'Ok',
                data: payload,
            };
        }
        else {
            failure++;
            return {
                recordId: record.recordId,
                result: 'ProcessingFailed',
                data: record.data,
            }
        }

    });

    console.log(`Processing completed.  Successful records ${success}, Failed records ${failure}.`);
    callback(null, { records: output });

};

ステップ 8: Firehose 配信ストリームを作成し、S3 にデータを配信するように設定する

Amazon S3 ターゲットを使用するとき、Firehose は S3 バケットにデータを配信します。配信ストリームを作成するには、IAM ロールが必要です。Firehose はその IAM ロールを引き受けることで、指定したバケットとキーにアクセスする権限を取得します。Firehose はまた、その IAM ロールを使用することで、Amazon CloudWatch ロググループにアクセスし、データ変換 Lambda 関数を呼び出す権限を取得します。

1.    S3 バケット、キー、CloudWatch ロググループ、データ変換 Lambda 関数にアクセスする権限を付与する IAM ロールを作成します。

aws iam create-role \
    --role-name firehose_delivery_role \
    --assume-role-policy-document  file://firehose_delivery_trust_policy.json

aws iam put-role-policy \
    --role-name firehose_delivery_role \
    --policy-name firehose_delivery_AccessPolicy \
    --policy-document file://firehose_delivery_AccessPolicy.json

 

2.  S3 ターゲット設定を指定して、Firehose 配信ストリームを作成します。

aws firehose create-delivery-stream \
    --delivery-stream-name webAnalytics \
    --extended-s3-destination-configuration='CONTENTS OF s3-destination-configuration.json file' 

3.  AWS マネジメントコンソールにサインインし、Firehose コンソールに移動します。webAnalytics という名前の配信ストリームを選択します。[Details] タブで、[Edit] を選択します。[Data transformation][Enabled] を、[IAM role] [firehose_delivery_role] を選択します。[Lambda function] で、[firehoseDeliveryTransformationFunction] を選択します。次に、[Save] を選択してこの設定を保存します。

ステップ 8: Lambda 関数を作成して、VPC リソースにアクセスするように設定する
S3 バケットから Amazon Aurora にデータをインポートするには、VPC 内のリソースにアクセスするように Lambda 関数を設定します。

1.    Lambda 関数の IAM 実行ロールを作成します。

auroraLambdaRole=$(aws iam create-role \
                --role-name lambda_aurora_role \
                --assume-role-policy-document file://lambda-aurora-Trust-Policy.json \
                --query 'Role.Arn' --output text)

aws iam put-role-policy \
    --role-name lambda_aurora_role \
    --policy-name lambda-aurora-AccessPolicy \
    --policy-document file://lambda-aurora-AccessPolicy.json

2.  プライベートサブネットやセキュリティグループなどの VPC 設定を指定する Lambda 関数を作成します。CLI の実行中に渡される環境変数 AuroraEndpoint、dbUser (データベースユーザー)、dbPassword (データベースパスワード) に正しい値を設定していることを確認します。これらの値については、CloudFormation スタック出力を参照してください。

aws lambda create-function \
    --function-name AuroraDataManagementFunction \
    --zip-file fileb://AuroraDataMgr.zip \
    --role $auroraLambdaRole \
    --handler dbMgr.handler \
    --timeout 300 \
    --runtime python2.7 \
    --vpc-config SubnetIds='Output PrivateSubnets from CloudFormation stack',SecurityGroupIds='Output DefaultSecurityGroupId from CloudFormation stack' \
    --memory-size 1024 \    
    --environment='    
                    {
                        "Variables": {
                            "AuroraEndpoint": "Output AuroraEndpoint from CloudFormation stack",
                            "dbUser": "Database User Name",
                            "dbPassword": "Database Password"
                        }
                    }'

 

Lambda 関数は Aurora データベースに接続します。この関数は、LOAD DATA FROM S3 SQL コマンドを実行して、S3 バケット内のテキストファイルから Aurora DB クラスターにデータをロードします。

import logging import pymysql import boto3 import sys import os
# rds settings
db_name = "Demo"
dbUser = os.environ['dbUser']
dbPassword = os.environ['dbPassword']
AuroraEndpoint = os.environ['AuroraEndpoint']

logger = logging.getLogger()
logger.setLevel(logging.INFO)

s3_client = boto3.client('s3')
try:
    conn = pymysql.connect(AuroraEndpoint, user=dbUser, passwd=dbPassword, db=db_name, connect_timeout=5)
except:
    logger.error("ERROR: Unexpected error: Could not connect to Aurora instance.")
    sys.exit()

logger.info("SUCCESS: Connection to RDS Aurora instance succeeded")

def handler(event, context):

    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = record['s3']['object']['key']
        s3location = 's3://' + bucket + '/' + key
        logger.info(s3location)

        sql = "LOAD DATA FROM S3 '" + s3location + "' INTO TABLE Demo.WebAnalytics FIELDS TERMINATED BY ',' " \
              "LINES TERMINATED BY '\\n' (timestarted, page_id, hits, start_time, end_time, country_code);"

        logger.info(sql)

        with conn.cursor() as cur:
            cur.execute(sql)
            conn.commit()
            logger.info('Data loaded from S3 into Aurora')

ステップ 9: S3 イベント通知を設定する
最後に、S3 がイベントを発行することで、前のステップで作成した Lambda 関数を呼び出すように、その関数を設定します。このプロセスの最初の手順は、Lambda 関数を呼び出すアクセス権限を S3 に付与することです。

1.    Lambda 関数を呼び出すアクセス権限を S3 に付与します。

aws lambda add-permission \
    --function-name AuroraDataManagementFunction \
    --action “lambda:InvokeFunction” \
    --statement-id Demo1234 \
    --principal s3.amazonaws.com \
    --source-arn ‘ARN of S3 Bucket created in Step 2’ \
    --source-account ‘AWS Account Id

2.    S3 バケット通知を設定します。

aws s3api put-bucket-notification-configuration \
    --bucket 'Name of S3 Bucket created in step 2' \
    --notification-configuration=' CONTENTS OF lambda-s3-notification-config.json ' 

 

ステップ 10: ソリューションをテストする
最後のステップは、ソリューションをテストすることです。

  1. このブログ記事のソースコードの TestHarness フォルダには、テストハーネスがあります。このテストハーネスは DynamoDB テーブルにデータを読み込みます。まず、TestHarness フォルダに移動し、コマンドノード loadDataToDDb.js を実行します。
  2. Secure Shell (SSH) を使用して、踏み台ホストに接続します。SSH を使用した接続の詳細については、EC2 のドキュメントを参照してください。
  3. 踏み台ホストのブートストラッププロセス中に MySQL クライアントがインストールされたため、以下のコマンドを使用して Aurora データベースに接続できます。パラメータ値を適切に変更していることを確認します。
/usr/bin/mysql -h DatabaseEndpoint -u DatabaseUsername --password=DatabasePassword 

4.    コネクションが成功したら、以下のコマンドを実行します。

 mysql> select count(*) from Demo.WebAnalytics;

このコマンドを実行した後、テーブルにレコードが読み込まれています。

テーブルにレコードが読み込まれていない場合、Firehose はそれらのレコードを S3 に送信する前にバッファしている可能性があります。これを回避するには、1 分くらい後に同じ SQL コードを再試行してください。この間隔は、現在設定されている S3 バッファ間隔の値に基づきます。このコードを再試行した後、Amazon Aurora にレコードが挿入されています。

 

まとめ

DynamoDB Streams と、Amazon Kinesis Firehose のデータ変換関数を使用すると、DynamoDB から Amazon Aurora などのデータソースにデータをレプリケートする強力でスケーラブルな方法を得られます。このブログ記事では、DynamoDB から Aurora へのデータのレプリケートを取り上げましたが、同じ一般的なアーキテクチャパターンを使用すれば、他のストリーミングデータを変換して、Amazon Aurora に取り込むことができます。

さらに、以下の関連するブログ記事を参照してください。

 

日本準拠法に関する AWS カスタマーアグリーメントの変更: AWS Artifact

–この記事はAWS セキュリティ・アシュアランス本部の寄稿です–

2016年12月に AWS Artifact のサービスが開始され監査レポート等コンプライアンスに関する重要な情報を提供してきましたが、2017年11月より同サービスにおいて、日本のお客様に向けて「日本準拠法に関する AWS カスタマーアグリーメント変更契約」の手続きが可能な新機能の提供を開始しました。これにより、AWS Artifact を通じて日本準拠法に関する AWS カスタマーアグリーメント変更契約をリアルタイムに締結または終了することが可能となっています。

日本準拠法に関する AWS カスタマーアグリーメント変更契約とは、現在お客様がご利用中の AWS アカウントに適用されている、 AWS カスタマーアグリーメントの準拠法および管轄裁判所を変更する契約を指します。この契約を有効にすることで、 AWS カスタマーアグリーメントの準拠法を日本法に変更し、更に、同契約に関するあらゆる紛争に関する第一審裁判所を東京地方裁判所に変更することができます。

従来、AWSカスタマーアグリーメントの準拠法および管轄裁判所を変更する際に、その都度、書面で契約を締結して頂く必要がありましたが、AWSアカウントのマネジメントコンソールからお客様ご自身で受諾(有効に)することで、お客様の手間を省略することが可能となっています。

AWSでは今後も日本のお客様の声に耳を傾け、サービスの拡充に努めていきます。

【AWS Artifactの操作方法】

AWS Artifactを使用した日本準拠法に関するAWSカスタマーアグリーメントの変更方法、操作方法については下記のリンクから動画をご参照ください。より詳細なAWS Artifactの情報については以下のartifactのページをご参照ください。

https://aws.amazon.com/jp/artifact/

 

【留意事項】

  •  日本準拠法に関する AWS カスタマーアグリーメントは、請求連絡先アドレスが日本国内にあるアカウントに対してのみ提供されるものです。
  • 複数のアカウントをお持ちのお客様の場合、アカウント毎に有効にして頂く必要があります。
  • 既に個別に書面でカスタマーアグリーメントの準拠法および裁判管轄を変更する契約を取り交わしているお客様については、再度締結いただく必要はございません。

その他の詳細については下記のページをご参照ください。

https://aws.amazon.com/jp/artifact/faq/

 

– AWS セキュリティ・アシュアランス本部

 

 

SAP on AWSにおけるVPCサブネットのゾーニングパターン 第3回:内部および外部接続

この記事は、Amazon Web Services(AWS)のソリューション アーキテクト、Harpreet SinghとDerek Ewellによるものです。

VPCサブネットのゾーニングパターンに関するシリーズ記事の第1回目では、SAPアプリケーションへのいくつかの接続方法を紹介し、内部専用接続のためのAmazon Virtual Private Cloud(Amazon VPC) サブネットのゾーニングパターンについて詳細を説明しました。第2回目では、従来のアプリケーションにおけるネットワークのゾーニングをAWS上でどのように実装できるか説明しました。このシリーズの最後の記事として、内部接続と外部接続の両方を必要とするSAPアプリケーションの接続パターンを説明します。外部ソースからの接続は管理されていてもよく(つまり、既知の外部の関係者を含む)、管理されていない(つまり、公開されていてもよい)場合もあります。両方のシナリオについて説明します。

内部および管理された外部接続

外部向けインターフェースに対する信頼された外部の関係者からの接続と、内部向けインターフェースに対するSAPと非SAPシステム間またはどちらかの間の接続が必要となる、SAP Process Orchestration(PO)とSAP Process Integration(PI)がこのシナリオの典型的な例です。内部向けインターフェースは簡単に管理できます。基本的には、ルートテーブル、ネットワークアクセスコントロールリスト(ACL)、およびセキュリティグループに適切なトラフィックを定義します。しかしながら、外部接続を安全に提供することには課題があります。そこで、信頼できる外部の関係者からのトラフィックの入口と出口に着目しましょう。典型的な選択肢が4つあります。

  • 入口と出口の両方のための仮想プライベートネットワーク(VPN)接続
  • 入口用のElastic Load Balancing、および出口用のネットワークアドレス変換(NAT)ゲートウェイ
  • NATデバイス(NATインスタンスまたはNATゲートウェイ)
  • リバースプロキシ

VPN接続 (入口と出口)

信頼できる外部の関係者と専用の仮想接続を作成する場合は、VPCでVirtual Private Gateway(VGW)を使用するか、パブリックサブネット内にAWS Marketplaceで公開されているような独自のソフトウェアVPNサーバを使用することで、サイト間IPsec VPN接続を確立できます。

注釈   この記事のアーキテクチャ図では、簡単にするために単一のアベイラビリティゾーンで示しています。実際には、高可用性を実現するために、少なくとも2つのアベイラビリティゾーンにわたってソリューションを構成することをお勧めします。

図 1: 管理された外部接続のためのVPNコネクション

Elastic Load Balancing (入口) / NATゲートウェイ (出口)

Elastic Load Balancingには、次の3つのタイプがあります。

  • Classic Load Balancer
  • Network Load Balancer
  • Application Load Balancer

Classic Load Balancerは、 EC2-Classicプラットフォーム上で構築されたアプリケーションに向いています。VPCをお使いであれば、Network Load BalancerあるいはApplication Load Balancerをお勧めします。Network Load Balancerは、Open Systems Interconnection(OSI)モデルの接続レベル(レイヤー4)で動作し、TCPトラフィックの負荷分散に最適です。Application Load Balancerは、接続レベル(レイヤー7)で動作し、HTTPまたはHTTPSの負荷分散に最適です。

Secure File Transfer Protocol(SFTP)とHTTPSの2つの例を考えてみましょう。

  • SFTP: プライベートサブネットにSFTPサーバがあり、信頼できる外部の関係者から接続できる必要があるとします。このシナリオでは、パブリックサブネット内にインターネットに面するNetwork Load Balancerを配置し、プライベートサブネット内のSFTPサーバに関連付けられているセキュリティグループによって、信頼できる外部の関係者からの接続を管理できます(Network Load Balancerに関連付けられたセキュリティグループはありません)。SAP PI / POから信頼できる外部の関係者が持つSFTPサーバへのアウトバウンド(出口)トラフィックの場合には、NATゲートウェイが必要です。Amazon Route 53を使用して、組織の外部ドメイン名を登録し、ロードバランサーを完全修飾ドメイン名(FQDN)で名前解決できます。

    図 2: 外部接続のためのNetwork Load Balancer

  • HTTPS: この2つ目の例では、外部から利用可能なSAP PI / POへのWebサービスベースのインターフェイスがあるとします。このシナリオでは、接続はSSL(HTTPS)に基づいて行われるため、Application Load Balancerが入口トラフィックに最適です。既知のIPからの接続は、ロードバランサーに関連付けられたセキュリティグループによって管理されます。一方、SAP PI / POが信頼できる外部の関係者によって公開されたWebサービスを利用する必要がある場合は、NATゲートウェイが必要です。Amazon Route 53は、ドメイン名の登録とFQDN解決にも使用できます。

    図 3: 外部接続のためのApplication Load Balancer

その他の選択肢

Elastic Load Balancingを使用する代わりに、NATインスタンスとリバースプロキシを使用できます。ただし、NATインスタンスとリバースプロキシを管理する負担を取り除くため、インターネットに面する構成ではNetwork Load BalancerあるいはApplication Load Balancerを使用することをお勧めします。

  • NATデバイス: AWSでは、2種類のNATデバイス、つまりNATゲートウェイとNATインスタンスを提供しています。NATインスタンスはAmazon Machine Image(AMI)に基づいていますが、NATゲートウェイはAWSサービスとして管理されています。これらのデバイスはいずれも、プライベートサブネット内のEC2インスタンスにインターネット接続を提供します。ポート転送のNATインスタンスを有効にすることで、外部の関係者がプライベートサブネット内のEC2インスタンス上で実行されているアプリケーションに接続することもできます。前述のSFTPの例をもう一度見てみましょう。プライベートサブネット内のSFTPサーバには、既知の外部の関係者からはSAP PI / POの持つファイルベースのインターフェイスを経由して接続される必要があります。NATインスタンス(ポート転送を有効にした後)は、SFTPサーバ(プライベートサブネット内に存在)を外部からの直接接続から保護しながら、外部の関係者が接続できるようにします。NATインスタンスに関連付けられたセキュリティグループを設定すると、管理された接続として既知の外部IPからのトラフィックだけを許可できます。すべてのインターフェイスがSAP PI / POからのアウトバウンド接続に基づいている場合、NATゲートウェイが最適です。

    図 4: ポート転送のためのNATインスタンス

  • リバースプロキシ (入口) / NATゲートウェイ (出口): HTTP / HTTPSトラフィックの入口にはリバースプロキシが使用されます。パブリックサブネットのリバースプロキシは、外部の関係者からプライベートサブネット内のSAP PO / PIサーバにHTTP / HTTPS接続を確立できるようにします。入口トラフィックのリバースプロキシとしてSAP Web Dispatcherを使用することも、F5 BIG-IPのようなサードパーティ製品を使用することもできます。SAP Web Dispatcherを使用している場合は、Reverse invokeによる接続を構成することをお奨めします。前述のシナリオのように、出口トラフィックに対してはNATゲートウェイを使用できます。

    図 5: 外部接続のためのリバースプロキシ

これらの両方のシナリオ(NATおよびリバースプロキシ)では、静的パブリックIPアドレスを保持するために、Elastic IPアドレスを使用する必要があります。さらに、Amazon Route 53を使用して、ドメイン名の登録とFQDN解決を行います。

SAP Cloud Connectorなど、他のアプリケーション固有の選択肢も利用することができます。クラウドコネクタは、HTTPS経由でSAP Cloud Platformとの接続を確立します。接続は常にクラウドコネクタによって呼び出されますが、セッションが確立された後、データは双方向に(逆引き接続を介して)送信されます。クラウドコネクタはエクストラネットゾーンに配置し、NATゲートウェイ経由でインターネットに接続することをお勧めします。

図 6: SAP Cloud Platformとの連携

内部および管理されていない外部接続

この領域のアプリケーションへの内部接続は、前述のシナリオ(内部および管理された外部接続)と同様の方法で、つまり適切なルートテーブル、ネットワークACL、およびセキュリティグループを定義することによって管理する必要があります。したがって、このセクションでは、管理されていない外部接続に焦点を当てます。

SAP Fioriは、VPN接続および内部接続なしで外部接続が必要なSAPアプリケーションの一般的な例です。その他の例としては、SAP Hybrisプラットフォーム、外部から接続可能なSAP Enterprise Portal(EP)、あるいはSAP Supplier Relationship Management(SRM)などがあります。ただし、ほとんどの場合、SAPシステムへの管理されていない外部接続はHTTP / HTTPSに制限されています。SAP NetWeaver Gateway上で実行されるセントラルハブ構成のSAP Fioriフロントエンドサーバの例を考えてみましょう。

図 7: 外部接続のためのApplication Load Balancer

外部ゾーンのApplication Load Balancerは、HTTP / HTTPS要求のエントリーポイントとして機能します。ロードバランサーはSAP Web Dispatcherに要求を送信します。AWS Shieldは、特にロードバランサーとAWS WAFを組み合わせて使用​​する場合、一般的なWebエクスプロイトからSAP NetWeaver Gatewayを保護します。AWS Shieldは、AWS上で動作するWebアプリケーションを保護する、マネージド型の分散サービス妨害(DDoS)の保護サービスです。AWS Shieldには、StandardとAdvancedの2つの階層があります。AWS Shield Standardには追加料金はかかりません。

今後について
この記事では、SAPアプリケーションに内部および外部から接続するためのシナリオについて説明しました。特定のシナリオについて議論したい場合、またはこのブログの内容についてご質問やご提案がありましたら、お気軽にお問い合わせください。

翻訳はPartner SA 河原が担当しました。原文はこちらです。

OracleDBからPostgreSQLへの移行

 

Knievel Co は、アマゾン ウェブ サービスのデータベースエンジニアです。

このブログ記事では、Oracle データベースを PostgreSQL に移行する方法の概要について説明します。データベース移行の2つの主要部分は、スキーマの変換とデータの複製です。)AWS スキーマ変換ツール (AWS SCT) と AWS Database Migration Service (AWS DMS) を使用して、これら 2 つの部分に取り組む方法について説明します。

SCT と DMSについて説明する前に、予備的な手順を実行する必要があります。これらは、すべての移行に役立つことが判明しています。移行を容易にする方法の 1 つは、移行の前に、通常更新フェーズと呼ばれるものを行うことです。このフェーズでは、Oracle データベース内のオブジェクトのインベントリを作成し、いくつかの決定を下します。

最初に、不要になったオブジェクトを削除します。オブジェクトの移行にかかる時間は誰も気にかけませんが、無駄にしないでください。また、不要になった履歴データを削除することもできます。一時的なテーブルや過去のメンテナンス時のテーブルのバックアップコピーなど、不要なデータを複製するために時間を無駄にすることはありません。次に、LOB、CLOB、LONG などに保存されているフラットファイルおよび長い文字列を Amazon S3 または Amazon Dynamo DB に移動します。このプロセスではクライアントソフトウェアの変更が必要となりますが、データベースが簡素化されサイズが削減されることで、システム全体がより効率的になります。最後に、PL/SQL パッケージとプロシージャを移動します。特にビジネスロジックを含むものをクライアントソフトウェアに戻してみます。これらのオブジェクトは、SCT が変換できない場合は手動で変更する必要があります。

次の手順は、異なるデータベースエンジン (この場合は Oracle から PostgreSQL へ) に移行するための手順です。別のプラットフォームに移動していない場合は、データベースを移動するためのより適切なネイティブツールやその他のテクニックがあります。

  1. ターゲットデータベースでスキーマを作成します。
  2. ターゲットデータベースの外部キーとセカンダリインデックスを削除し、トリガーを無効にします。
  3. データを複製するためのDMSタスクを設定します (全ロードと変更データキャプチャ (CDC))。
  4. 全ロードフェーズが完了したらタスクを停止し、外部キーとセカンダリインデックスを再作成します。
  5. DMS タスクを有効にします。
  6. ツールとソフトウェアを移行し、トリガーを有効にします。

ターゲットデータベースでスキーマを作成します。

移行するスキーマを確認して、移行を開始します。このケースでは、AWS スキーマ変換ツール (AWS SCT) を使用して分析を実行します。アプリケーションを起動するときは、ソースが Oracle でターゲットが PostgreSQL となる新しいプロジェクトを作成する必要があります。再接続したら、左側で移行するスキーマの名前を選択します。スキーマ名を右クリックし、[スキーマの変換] を選択します。次に、[View / Assessment Report View] を選択します。

AWS SCT 評価レポートは、Oracle データベースを PostgreSQL に変換するために必要な作業の高レベルな概要を示します。以下に示しているのは、評価レポートの具体的な例です。

 

このレポートは、各オブジェクトタイプごとに手動で変換するために必要な手作業を示しています。一般的に、パッケージ、プロシージャ、関数には解決すべきいくつかの問題があります。AWS SCT では、これらのオブジェクトを修正する理由を説明し、その方法のヒントを示します。

スキーマが自動的に変換されない場合の、問題を解決するためのヒントを次に示します。

  • ソースの Oracle データベースのオブジェクトを変更して、AWS SCT がそれらをターゲットの PostgreSQL に変換できるようにします。
  • スキーマをそのまま変換し、AWT SCT によって生成されたスクリプトを手動で変更してから、それらをターゲットの PostgreSQL データベースに適用してみてください。
  • 変換できないオブジェクトを無視して、機能を別の AWS サービスまたは同等のものに置き換えます。

スキーマの変換機能を改善すると、反復プロセスを経てレポートとスキーマを再生成できます。[Action Items] ビューには、変換プロセスを実行する際に生じる問題のリストが表示されます。変換されたスキーマの結果が満足のいくものであれば、それらをターゲットの PostgreSQL データベースに適用することができます。

ターゲットデータベースのスキーマを参照し、列のデータ型、オブジェクト名などを簡単ににチェックすることをお勧めします。ソースおよびターゲットのデータ型の詳細については、「AWS Database Migration Service リファレンス」を参照してください。 また、Oracle から PostgreSQL への変換であるため、オブジェクト名が Oracle では大文字で、PostgreSQL では小文字であることが問題となります。

外部キー制約とセカンダリインデックスを削除する。トリガーを無効にする

ターゲット上に必要なスキーマを揃えるするには、ソースから実際のデータを移行するためにスキーマを準備する必要があります。ここでは、AWS Database Migration Service (AWS DMS) を使用します。DMSには、全ロードと変更データキャプチャ (CDC) の 2 つのフェーズがあります。全ロードフェーズでは、テーブルは順不同でロードされます。したがって、ターゲットで制約を有効にすると、いくつかの外部キーで制約違反が発生します。また、全ロード時には表のレプリケーションが遅くなる可能性があるため、セカンダリインデックスを無効にする必要があります。これは、レコードがロードされるときにインデックスを維持する必要があるためです。

ターゲットの PostgreSQL データベースで、クエリを実行してデータベーステーブルの外部キー制約に DDL を生成し、出力を保存します。これを行うための多くのサンプルクエリをオンラインで見つけることができます。次のような情報が表示されます。これを実行すると、後で外部キー制約を再作成するための DDL が提供されます。

ALTER TABLE <テーブル名> ADD CONSTRAINT <制約名> FOREIGN KEY(キー列)REFERENCES <親テーブル名>(キー列)MATCH FULL;

同様に、DDL 生成クエリを実行して、ターゲットデータベース上のすべての外部キー制約を削除します。

ALTER TABLE <テーブル名> DROP CONSTRAINT <制約名>;

ここで、セカンダリインデックスについても同じことを行います。つまり、コマンドを作成し結果を生成してから、セカンダリインデックスを削除します。

次に、トリガーを無効にします。

ALTER TABLE <テーブル名> DISABLE TRIGGER ALL;

ID 列でシーケンスを使用する場合は、ターゲットでシーケンスを作成するときに、次の値をソースデータベースよりも高く設定することをお勧めします。十分なギャップを残して、移行カットオーバーの日付で値がソースのデータベースの値よりも高くなっていることを確認してください。このアプローチによって、移行後のシーケンス ID の競合が回避されます。

DMS タスクを設定してデータをレプリケートする

ターゲットの PostgreSQL データベースでスキーマの準備ができました。これでデータをレプリケートする準備が整いました。これは DMS が入る場所となります。DMS の素晴らしい点は、各テーブルのデータをレプリケートするのではなく、移行する準備ができるまで CDC モードでデータを最新の状態で保持することです。

ソース Oracle データベースの準備:

  • Oracle ログインに必要な権限を確認します。
  • DMS が Oracle ソースデータベースから変更を取得するために必要なサプリメンタルロギングを設定します。
  • ソース Oracle データベースの準備の詳細については、DMS ドキュメントを参照してください。

AWS コンソールで DMS を起動します。最初に、レプリケーションインスタンスを作成する必要があります。レプリケーションインスタンスが DMS タスクを実行します。このインスタンスは、ソース Oracle データベースとターゲット PostgreSQL データベースの両方に接続する中間サーバーです。適切なサイズのサーバーを選択します。特に、複数のタスクの作成、多数のテーブルの移行、またはその両方を行う場合は、適切なサーバーを選択します。

次に、ソースデータベースのエンドポイントとターゲットデータベースのエンドポイントを作成します。Oracle データベースと PostgreSQL データベースの適切な接続情報をすべて入力します。各エンドポイントの作成が完了する前に、接続テストが成功した後で必ずスキーマの更新オプションを選択し、テストを実行 してください。

これで、タスクを作成する準備ができました。タスク名を入力し、作成したレプリケーションインスタンス、およびソースおよびターゲットエンドポイントを選択します。[移行タイプ] で、[既存データの移行と進行中の変更のレプリケート] を使用します。スキーマを事前に作成するためにAWS SCTを使用しているため、[ターゲットテーブル準備モード][何もしない] または [切り詰め] を選択します。

オプション [全ロードの完了後にタスクを停止する] で、[キャッシュされた変更の適用後に停止] を選択します。完全ロードが完了し、キャッシュされた変更が適用された後で、タスクを一時的に停止します。キャッシュされた変更は、テーブル全体のロードプロセスが実行されている間に発生し蓄積された変更です。これは、CDC が適用される直前のステップです。

できれば、ソース Oracle データベースを更新して、LOB を S3、DynamoDB、または別の同様のサービスに移動します。そうでない場合は、LOB を処理する方法についていくつかのオプションがあります。すべてのテーブルの LOB 全体をレプリケートする場合は、[レプリケーションに LOB 列を含める] で、[完全 LOB モード] を選択します。LOB を特定の長さまでレプリケートするのみの場合は、[制限付き LOB モード] を選択します。移行する LOB の長さは、[最大 LOB サイズ (KB)] テキストボックスで指定します。

最後に、[ログ作成の有効化] を選択して、タスクで発生したエラーや警告を確認し、問題のトラブルシューティングを行えるようにすることをお勧めします。[タスクの作成] を選択します。

次に、[テーブルマッピング] で移行するスキーマを選択し、[選択ルールの追加] を選択します。[JSON] タブを選択します。[JSON の編集を有効にする] チェックボックスを選択します。次に、次の JSON 文字列を入力し、スキーマ名 DMS_SAMPLE を使用するスキーマ名に置き換えます。

 

{

 "rules": [

  {

   "rule-type": "selection",

   "rule-id": "1",

   "rule-name": "1",

   "object-locator": {

    "schema-name": "DMS_SAMPLE",

    "table-name": "%"

   },

   "rule-action": "include"

  },

  {

   "rule-type": "transformation",

   "rule-id": "6",

   "rule-name": "6",

   "rule-action": "convert-lowercase",

   "rule-target": "schema",

   "object-locator": {

    "schema-name": "%"

   }

  },

  {

   "rule-type": "transformation",

   "rule-id": "7",

   "rule-name": "7",

   "rule-action": "convert-lowercase",

   "rule-target": "table",

   "object-locator": {

    "schema-name": "%",

    "table-name": "%"

   }

  },

  {

   "rule-type": "transformation",

   "rule-id": "8",

   "rule-name": "8",

   "rule-action": "convert-lowercase",

   "rule-target": "column",

   "object-locator": {

    "schema-name": "%",

    "table-name": "%",

    "column-name": "%"

   }

  }

 ]

}

この JSON 文字列は、PostgreSQL の場合、スキーマ名、テーブル名、列名を小文字に変換します。

タスクが作成されると、自動的に開始されます。タスクを選択して[ステータス] タブをクリックすると、DMS コンソールを使用して進行状況を監視できます。全ロードが完了しキャッシュされた変更が適用されると、タスクは自動的に停止します。

全ロードフェーズが完了したらタスクを停止し、外部キーとセカンダリインデックスを再作成します。

テーブルのロードが完了しました。今度は、ログを確認してタスクにエラーがないことを確認するのがよいでしょう。

タスクの次のフェーズは、ソースデータベースで発生した順序で変更を適用する CDC です。このアプローチは、親テーブルがターゲットデータベース上の子テーブルの前に更新されるため、外部キーを再作成できることを意味します。

必要に応じて生成されたスクリプトを調整して、以前に削除された外部キーとセカンダリインデックスを再作成します。セカンダリインデックスはタスクのこのフェーズで重要となります。このフェーズは重要です。なぜなら、where 句を使用してソースデータベースに対して行われた更新は、ターゲットデータベース上のインデックスのルックアップでもあるからです。更新に欠落しているインデックスがある場合、これらの更新は全テーブルスキャンとして実行されます。

ソースからのデータを更新できるため、移行の切り替えまでトリガーを有効にしないでください。

DMS タスクを有効にする

これで外部キーとセカンダリインデックスが戻ったので、DMS タスクを有効にすることができます。DMS コンソールに移動して、[タスク] を選択します。リストでタスクを選択し、[開始/再開] を選択します。[開始] オプションを選択し、[タスクの開始] を選択します。

ツールとソフトウェアを移行し、トリガーを有効にします。

最後に、カットオーバーポイントがここにあります。ツールとソフトウェアの接続がソースデータベースへのアクセスを停止し、DMS タスクが最後のデータ変更をターゲットデータベースにレプリケートしたら、DMS コンソールで DMS タスクを停止します。次に、ターゲットデータベースでトリガーを有効にします。

ALTER TABLE <テーブル名> ENABLE TRIGGER ALL;

最後のステップは、アプリケーションを新しいターゲット PostgreSQL データベースに再配置して再起動することです。完了です。

役立つヒント

スキーマを変換しやすくするには、AWS SCT を使用して評価レポートを取得し、アクション項目を反復処理します。ターゲット PostgreSQL スキーマの最終バージョンに到達するまで、ターゲットスキーマを複数回生成する必要があります。
新しいスキーマのクエリが新しいプラットフォームで機能するようにするには、ターゲットシステムでアプリケーションをテストします。AWS SCT は、アプリケーションクエリを PostgreSQL に変換することもできます。詳細については、AWS SCT ドキュメントを参照してください。また、ターゲットシステムの負荷テストを実装して、ターゲットサーバーのサイズとデータベース設定が正しいことを確認します。
前に概要を説明した実際の移行手順を実践し、プロセスを合理化します。上記の手順は単なる出発点に過ぎず、各データベースは一意のものです。

詳細情報

また、次を検討することをお勧めします。

AWS SCT および AWS DMS のドキュメント
DMS のステップバイステップチュートリアル
DMS のベストプラクティス
GitHub の DMS サンプルデータベース
関連するブログ記事: SQL を使用して Oracle から PostgreSQL へユーザー、ロール、および許可をマップする

詳解: Amazon ECSのタスクネットワーク

この記事はECSのSr. Software Dev EngineerのAnirudh Aithalの寄稿です。

2017年11月14日に、AWSはコンテナにElastic Network InterfaceをアタッチできるようにするAmazon ECSのTask Networkingを発表しました。

この記事では、ECSが管理するインスタンス(コンテナインスタンスと呼ばれます)上でContainer Networking Interfaceプラグインを使って、この新しいコンテナネイティブなawsvpcネットワークモードがどのように実装されているかを詳しくご紹介したいと思います。

こちらはAmazon ECSでタスクネットワークが動作するかにdeep diveしたものです。もし自身のコンテナ化したアプリケーションでどうやってタスクネットワークを使い始めれば良いかについて学びたい時には、Amazon ECSコンテナにCloud Native Networkingが登場をご覧下さい。Cloud Native Computing Foundation (CNCF)がContainer Networking Interface (CNI)プロジェクトをホストしており、Linuxコンテナでネットワークインターフェースを設定するためのプラグインを書くための仕様やライブラリが含まれています。AWSのクラウドネイティブコンピューティングについての詳細は、Adrian CockcroftのCloud Native Computingについての投稿をご覧下さい。

コンテナインスタンスのセットアップ

コンテナインスタンス上でのタスクネットワーク有効化の詳細をご説明する前に、ECSの典型的なインスタンスがどのようになっているかを見てみましょう。

上の図は典型的なコンテナインスタンスを示しています。ECS agentは自身もコンテナとして実行されているのですが、以下のような責任を負っています:

  • EC2インスタンスをECSのバックエンドに登録
  • コンテナインスタンスに対してECSバックエンドが発生させたタスク状態の変化を、正しく適応
  • Dockerデーモンと会話しながら、コンテナの作成、開始、停止、監視
  • コンテナの状態とタスクの状態の遷移をECSバックエンドにリレー

ECS agentはその管理下のコンテナのスーパーバイザーの様に動作するので、Dockerデーモン(Dockerのデフォルトネットワークモードで設定されたコンテナ用)、又はCNIプラグイン達(ネットワークモードがawsvpcで設定されたタスク内のコンテナ)のための、ネットワーク設定をする難しさをオフロードしてくれます。

いずれの場合にも、コンテナのネットワークスタックは、ネットワークのnamespaceを通じて設定されます。ip-netns(8)のマニュアルによると「ネットワークnamespaceは論理的なネットワークスタックのコピーで、自身のルーティング、ファイアウォールルール、ネットワークデバイスを持っています。」 とあります。ネットワークnamespaceの構成によって、ホスト上で動いているプロセスやコンテナ間でのネットワークスタックの隔離を可能としてくれます。

ネットワークnamespaceとCNIプラグイン

CNIプラグインとは、CNI仕様を満たしコンテナのネットワーク接続性の設定を行う実行ファイル群です。CNIプロジェクトではプラグインの仕様を定義し、プラグインが利用するライブラリを提供することで、一貫していて信頼でき、かつ簡素なプラグイン用のインタフェースを提供してくれます。

コンテナやネットワークnamespaceを指定してプラグインを呼び出す時に、ADDコマンドでコンテナにネットワークインターフェースを追加したり、DELコマンドでそれを落としたりします。例えばリファレンスのBridgeプラグインは、ホストネットワークnamespaceの中にいるブリッジに対してホスト上の全てのコンテナを追加します。

このプラグインのモデルはECS agentの「コンテナのライフサイクルへの最小限の介入」というモデルと相性が良く、agentはコンテナのネットワーク設定の詳細について考慮する必要がなくなります。また拡張性の高いモデルなので、将来必要になった時には、agentが異なるプラグイン群を利用できるようにスイッチさせることもできます。最後に、これらプラグインは必要な時に呼び出されるだけなので、その死活監視をECS agentがする必要はありません。

ECS agentからCNIプラグインを呼び出す

ECSがElastic Network Interfaceをインスタンスにアタッチし、agentに対しそのElastic Network Interfaceをタスク内のコンテナに対してプロビジョンするようにメッセージを送った時には、(任意のネットワークデバイスを使う) そのElastic Network Interfaceはホストのグローバルデフォルトネットワークnamespaceに現れてきます。ECS agentは複数のCNIプラグインを順番に呼び出すことで、そのElastic Network Interfaceがコンテナのネットワークnamespaceに正しく設定されていることを保証してくれます。これらのプラグインはamazon-ecs-cni-pluginsのGitHubレポジトリで見ることができます。

最初に呼び出されるものはecs-eniプラグインで、Elastic Network Interfaceがコンテナのネットワークnamespaceにアタッチされ、VPCから割り当てられたIPアドレスとサブネットのゲートウェイに対するデフォルトルートが設定されていることを保証します。コンテナは更にIAMロールの認証情報を得るために、(ECS agentが提供している) 認証情報エンドポイントにHTTPリクエストできるようになる必要があります。これはその次に実行されるecs-bridgeecs-ipamプラグインによって処理されます。CNIのライブラリはこれらのプラグインの実行結果を解釈する仕組みを提供しているので、agent上で効果的にエラーをハンドリングすることができます。以下の図は、このプロセスのそれぞれのステップを図解したものです:

ネットワークスタックの設定とアプリケーションコンテナの中で実行されるコマンドのレースコンディションを避けるために、ECS agentはタスク毎にそのタスク定義内にあるコンテナを開始する前に、pauseコンテナを作成します。その後、前述のCNIプラグイン群を実行することで、pauseコンテナのネットワークnamespaceをセットアップします。それからタスクにある残りのコンテナ達を開始するので、pauseコンテナのネットワークスタックを共有します。これは、タスク内の全てのコンテナはElastic Network InterfaceのIPアドレスによって到達可能であり、かつlocalhostインタフェースを通じて互いに通信することができることを意味しています。

このセットアップ例では、1つのElastic Network Interfaceの後ろに2つのコンテナを持った1つのタスクがあります。以下のコマンドでそれらが同じネットワークスタックを持っていて、localhostインタフェースを通して互いに会話可能であることを見ていきましょう。

ホスト上で実行している最新3つのコンテナをリストする (タスクで起動した2つのコンテナと、ECS agentが追加で起動したネットワークnamespaceの設定用コンテナ):

$ docker ps -n 3 --format "{{.ID}}\t{{.Names}}\t{{.Command}}\t{{.Status}}"
7d7b7fbc30b9	ecs-front-envoy-5-envoy-sds-ecs-ce8bd9eca6dd81a8d101	"/bin/sh -c '/usr/..."	Up 3 days
dfdcb2acfc91	ecs-front-envoy-5-front-envoy-faeae686adf9c1d91000	"/bin/sh -c '/usr/..."	Up 3 days
f731f6dbb81c	ecs-front-envoy-5-internalecspause-a8e6e19e909fa9c9e901	"./pause"	Up 3 days

3つのコンテナのインタフェースをリストし、それらが同じであることを確認する:

$ for id in `docker ps -n 3 -q`; do pid=`docker inspect $id -f '{{.State.Pid}}'`; echo container $id; sudo nsenter -t $pid -n ip link show; done
container 7d7b7fbc30b9
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: ecs-eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 0a:58:a9:fe:ac:0c brd ff:ff:ff:ff:ff:ff link-netnsid 0
27: eth12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 02:5a:a1:1a:43:42 brd ff:ff:ff:ff:ff:ff

container dfdcb2acfc91
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: ecs-eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 0a:58:a9:fe:ac:0c brd ff:ff:ff:ff:ff:ff link-netnsid 0
27: eth12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 02:5a:a1:1a:43:42 brd ff:ff:ff:ff:ff:ff

container f731f6dbb81c
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: ecs-eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 0a:58:a9:fe:ac:0c brd ff:ff:ff:ff:ff:ff link-netnsid 0
27: eth12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 02:5a:a1:1a:43:42 brd ff:ff:ff:ff:ff:ff

まとめ

以上の仕組みによって、新しいawsvpcネットワークモードを使うことができ、コンテナに対してネイティブなネットワークサポートによる利便性を享受することができます。awsvpcモードの詳しい使い方は、Amazon ECSコンテナにCloud Native Networkingが登場ECSドキュメントをご覧下さい。

GitHubのECS CNIプラグインECS Agentのレポジトリで私にフィードバック頂けると幸いです。

原文: Under the Hood: Task Networking for Amazon ECS (翻訳: SA岩永)

Amazon ECSコンテナにCloud Native Networkingが登場

この記事はECSのSr. Software Dev EngineerのAnirudh Aithalの寄稿です。

2017年11月14日に、AWSはAmazon ECSのTask Networkingを発表しました。これによって、Elastic Network Interfaceを使ったAmazon EC2のネットワーク機能をタスクに持ち込むことができるようになります。

Elastic Network InterfaceはVPC内のインスタンスにアタッチすることができる仮想的なネットワークインタフェースです。EC2の仮想マシンを起動する時には、インスタンスにネットワークの機能を提供するために自動的に1つのElastic Network Interfaceがプロビジョンされます。

タスクは実行されるコンテナの論理的なグループです。これまでは、Amazon ECSで実行されるタスクはそれが動くEC2ホストのElastic Network Interfaceを共有していました。これからは、新しいawsvpcというネットワークモードを使うことで、Elastic Network Interfaceが直接タスクにアタッチされます。

これによってネットワークの設定を簡略化することができ、VPCが持っているネットワークの全機能、隔離性、そしてセキュリティの制御を各コンテナにEC2インスタンスと同様のレベルで利用することができます。

この記事では、awsvpcモードがどのように動作し、ECSのタスクでどのようにElastic Network Interfaceを使い始めることができるかをご紹介します。

背景: EC2のElastic Network Interface

VPC内にEC2インスタンスを起動する時には、各インスタンスが互いに通信できるようにするために、追加のオーバーレイネットワークを設定する必要はありません。標準で、VPCのルーティングテーブルがインスタンスや他のエンドポイント間での通信をシームレスに実現してくれます。これは、Elastic Network Interfaceと呼ばれるVPC内の仮想的なネットワーク・インタフェースによって可能となっています。全ての起動されるEC2インスタンスは自動的に1つのElastic Network Interface(プライマリネットワークインタフェース)がアサインされます。サブネット、セキュリティグループといった全てのネットワークパラメータは、このプライマリネットワークインタフェースの属性として扱われます。

さらに、各Elastic Network Interfaceは作成時にVPCによってIPv4アドレス(プライマリIPv4アドレス)が割当られます。このプライマリアドレスはユニークでVPC内でルーティング可能なものです。これによって、VPCは実際はフラットなネットワークとなり、ネットワークトポロジを簡潔なものとしてくれます。

Elastic Network InterfaceはVPC上で多様なエンドポイントとの接続を実現するための基本的なビルディングブロックとみなすことができ、そのうえでより高レベルな抽象レイヤを構築することができます。これによってElastic Network Interfacdeは以下の機能を利用することが可能となっています:

  • VPCネイティブなIPv4アドレスとルーティング (VPC上でのインスタンス間や他のエンドポイントとの間)
  • ネットワークトラフィックの隔離
  • ACLとファイアウォールルール(セキュリティグループ)を使ったネットワークポリシーの強制
  • (サブネットのCIDRを通じた)IPv4アドレスレンジの強制

なぜawsvpcを使うのか?

以前はECSはDockerが提供する標準のネットワークの挙動が提供するネットワーク機能に依存した形でコンテナ向けのネットワークスタックを構成していました。デフォルトのBridgeネットワークモードでは、インスタンス上のコンテナ達はdocker0ブリッジを使って互いにつながっています。インスタンス外のエンドポイントと通信する時には、コンテナはこのブリッジを利用し、それが実行されているインスタンスのプライマリネットワークインタフェースを使います。コンテナは、ファイアウォールルール(セキュリティグループ)やIPアドレスは、そのプライマリElastic Network Interfaceのネットワーク属性を共有しまた依存しています。

これは、Dockerによって割当られたIPアドレス(ローカルスコープのアドレスプールから割当られます)を使ってもこれらのコンテナに到達できないことと、細かいNetwork ACLやファイアウォールルールを強制できないことを意味します。代わりに、VPCからはインスタンスのプライマリElastic Network InterfaceのIPアドレスと、マップされたホスト側のポート番号(静的か動的なポートマッピング)の組み合わせによって、コンテナに到達することができます。また、1つのElastic Network Interfaceが複数のコンテナで共有されているので、各コンテナに最適なネットワークポリシーを簡単に作成することが難しくなっています。

awsvpcネットワークモードでは、タスク毎にElastic Network Interfaceをプロビジョンすることによって、これらの課題を解決しています。従って、コンテナ達はこれらのリソースを共有もせず干渉することももはやありません。これによって、以下のことが可能となります:

  • 複数のコンテナのコピーを同じインスタンス上で同じコンテナポートを使っていても、ポートマッピングの様なことをせずに実行することができ、アプリケーションの構成を簡素化できます。
  • 共有ブリッジで帯域が干渉することがないので、アプリケーションのネットワークパフォーマンスを向上させられます。
  • 各Amazon ECS タスクにセキュリティグループのルールを紐付けることで、コンテナ化したアプリケーションに緻密なアクセス制御を強制することができるので、アプリケーションのセキュリティを向上できます。

タスク内のコンテナにセキュリティグループを紐付けることで、送信元のポートとIPアドレスで受け付けるネットワークトラフィックを制限することができます。例えば、インスタンスにはSSHアクセスを許可するが、コンテナにはそれを許可しないということが可能です。あるいは、HTTPのトラフィックを80番ポートでコンテナは受付可能とするポリシーを強制するが、インスタンスではブロックするといったことができます。このようなセキュリティグループのルールを強制することによって、インスタンスもコンテナも攻撃にさらされる範囲を限りなく狭めることができます。

ECSはタスクのためにプロビジョンされるElastic Network Interfaceのライフサイクルを管理しており、オンデマンドで作成し、タスクが停止したら削除します。EC2インスタンスを起動する時と全く同じプロパティをタスクに指定することができます。こうしたタスクの中にあるコンテナは以下のような特徴を持ちます:

  • Elastic Network InterfaceのIPアドレスとDNS名で到達可能
  • Application Load BalancerとNetwork Load BalancerにIPターゲットとして登録可能
  • VPC Flow Logで観測可能
  • セキュリティグループでアクセス制御可能

これはまた、全く同じタスク定義から複数のタスクを同じインスタンス上で実行するときに、ポートの衝突を心配する必要をなくしてくれます。Bridgeネットワークモードの時にはあった、共有のdocker0ブリッジでのポートの変換や帯域の干渉が不要となるので、より高いパフォーマンスを得ることもできます。

始めてみよう

まだECSクラスタを持っていなければ、クラスタ作成ウィザードを使って1つ作成することができます。この記事ではawsvpc-demoをクラスタ名として使います。また、コマンドラインの手順を行うのであれば、最新バージョンのAWS CLISDKをインストールしていることを確認してください。

タスク定義を登録する

タスクネットワークの為にタスク定義で変更する必要がある唯一のものは、ネットワークモードのパラメータをawsvpcにするだけです。ECSコンソールでは、この値はNetwork Modeに入力します。

もしこのタスク定義のコンテナをECSサービスへ登録しようと思っている場合には、コンテナポートをタスク定義で指定します。この例ではNGINXコンテナは80番ポートを開放するように指定しています:

これでnginx-awsvpcという名前のタスク定義が作成され、ネットワークモードがawsvpcに設定されています。以下のコマンド群は、コマンドラインでタスク定義を登録する方法を示しています:

$ cat nginx-awsvpc.json
{
        "family": "nginx-awsvpc",
        "networkMode": "awsvpc",
        "containerDefinitions": [
            {
                "name": "nginx",
                "image": "nginx:latest",
                "cpu": 100,
                "memory": 512,
                "essential": true,
                "portMappings": [
                  {
                    "containerPort": 80,
                    "protocol": "tcp"
                  }
                ]
            }
        ]
}

$ aws ecs register-task-definition --cli-input-json file://./nginx-awsvpc.json

タスクを実行する

このタスク定義でタスクを実行するために、Amazon ECSコンソールのクラスタ画面に行き、Run new taskを選択します。nginx-awsvpcのタスク定義を指定します。次に、このタスクを実行するサブネットの集合を指定します。これらサブネットの少なくとも1つにはECSに登録されたインスタンスが必要です。そうしないと、ECSはElatic Network Interfaceをアタッチする候補を見つけることができません。

コンソールを使うと、Cluster VPCの値を選ぶことで、サブネットの候補を絞り込むことができます:

次に、タスクのセキュリティグループを選択します。この例では、新しいセキュリティグループを作成し、80番ポートの内向きのみを許可します。もしくは、既に作成済のセキュリティグループを選択することもできます。

次に、Run Taskをクリックしてタスクを実行します。

これで実行中のタスクができました。タスクの詳細画面をみると、Elastic Network Interfaceが割り当てられていることと、そのElatic Network Interfaceに紐付いたIPアドレスを確認することができます:

コマンドラインを使って同じことができます:

$ aws ecs run-task --cluster awsvpc-ecs-demo --network-configuration "awsvpcConfiguration={subnets=["subnet-c070009b"],securityGroups=["sg-9effe8e4"]}" nginx-awsvpc $ aws ecs describe-tasks --cluster awsvpc-ecs-demo --task $ECS_TASK_ARN --query tasks[0]
{
    "taskArn": "arn:aws:ecs:us-west-2:xx..x:task/f5xx-...",
    "group": "family:nginx-awsvpc",
    "attachments": [
        {
            "status": "ATTACHED",
            "type": "ElasticNetworkInterface",
            "id": "xx..",
            "details": [
                {
                    "name": "subnetId",
                    "value": "subnet-c070009b"
                },
                {
                    "name": "networkInterfaceId",
                    "value": "eni-b0aaa4b2"
                },
                {
                    "name": "macAddress",
                    "value": "0a:47:e4:7a:2b:02"
                },
                {
                    "name": "privateIPv4Address",
                    "value": "10.0.0.35"
                }
            ]
        }
    ],
    ...
    "desiredStatus": "RUNNING",
    "taskDefinitionArn": "arn:aws:ecs:us-west-2:xx..x:task-definition/nginx-awsvpc:2",
    "containers": [
        {
            "containerArn": "arn:aws:ecs:us-west-2:xx..x:container/62xx-...",
            "taskArn": "arn:aws:ecs:us-west-2:xx..x:task/f5x-...",
            "name": "nginx",
            "networkBindings": [],
            "lastStatus": "RUNNING",
            "networkInterfaces": [
                {
                    "privateIpv4Address": "10.0.0.35",
                    "attachmentId": "xx.."
                }
            ]
        }
    ]
}

awsvpcのタスクの詳細を見ると、Elastic Network Interfaceの詳細についてはattachmentsオブジェクトとして値が返ってきます。containersオブジェクトの中でもこの情報を見ることができます。例:

$ aws ecs describe-tasks --cluster awsvpc-ecs-demo --task $ECS_TASK_ARN --query tasks[0].containers[0].networkInterfaces[0].privateIpv4Address
"10.0.0.35"

まとめ

nginxコンテナはVPC上で10.0.0.35というIPv4アドレスでアクセスすることが可能です。80番ポートでリクエストを受け付けるために、インスタンスのセキュリティグループを編集する必要はありませんので、インスタンスのセキュリティを向上できます。また、アプリケーションの改修を全くせずに、80番ポート以外の全てのポートがブロックされていることも保証できるので、ネットワーク上でタスクの管理が簡単になります。Elastic Network InterfaceのAPI操作はECSが全て面倒を見てくれるので、何もする必要がありません。

タスクネットワークの機能に関するより詳しい情報はECSのドキュメントをご覧下さい。この新しいネットワークモードがインスタンス上でどのように実装されているかの詳細は、詳説: Amazon ECSのタスクネットワークをご覧下さい。

原文: Introducing Cloud Native Networking for Amazon ECS Containers (翻訳: SA岩永)

Amazon ElastiCache for Redis を使ったChatアプリの開発

Sam Dengler は、アマゾン ウェブ サービスのソリューションアーキテクトです。

このブログ記事では、チャットアプリケーションに関連する概念とアーキテクチャのパターンについて説明します。また、チャットクライアントとサーバーの実装の詳細、サンプルのチャットアプリケーションを AWS アカウントに展開する方法についても説明します。

背景情報

チャットアプリケーションを構築するには、クライアントがチャットルームの他の参加者に再配信されるメッセージを送信できる通信チャネルが必要となります。この通信は、一般に publish-subscribe パターン (PubSub) を使用して実装されます。このパターンでは、メッセージが中央トピックチャネルに送信されます。関係者は、このチャンネルをサブスクライブして更新の通知を受けることができます。このパターンでは、発行者の知識なしに受信者のグループを拡大または縮小できるように、発行者と受信者を切り離しています。

PubSubは、クライアントが WebSockets を使用して通信するバックエンドサーバーに実装されます。WebSockets は、クライアントとサーバー間で双方向にストリーミングされるデータのチャネルを提供する永続的な TCP 接続です。単一サーバアーキテクチャでは、1 つの PubSub アプリケーションが発行者と受信者の状態を管理し、WebSocket を介してクライアントにメッセージを再配布することもできます。次の図は、単一サーバー PubSub アーキテクチャ上の 2 つのクライアント間でメッセージが WebSocket を通過するパスを示しています。

単一サーバーアーキテクチャは、通信フローを説明するのに役立ちます。しかし、ほとんどのソリューションビルダーはマルチサーバーアーキテクチャで設計したいと考えています。マルチサーバーアーキテクチャは、信頼性を高め、伸縮性を高め、クライアントの数が増えるにつれてアプリケーションを水平的に拡大するのに役立ちます。

マルチサーバーアーキテクチャでは、クライアントはサーバープールにトラフィックを転送するロードバランサーに対して WebSocket 接続を行います。これらのサーバーは、WebSocket 接続とそれを経由してストリーミングされるデータを管理します。WebSocket 接続が PubSub アプリケーションサーバーとの間で確立されると、その接続は永続化され、データは両方向のアプリケーションにストリームされます。ロードバランサーは、WebSocket 接続のリクエストを健全なサーバーに配信します。つまり、2 つのクライアントが異なるアプリケーションサーバーに WebSocket 接続を確立できます。

 

複数のアプリケーションがクライアントの WebSocket 接続を管理するため、アプリケーションはメッセージを再配布するためにそれらの間で通信する必要があります。この通信が必要なのは、メッセージが WebSocket を介して 1 つのアプリケーションサーバーにストリームアップされ、別のアプリケーションサーバーに接続されたクライアントにストリームダウンされる必要があるためです。クライアント接続を管理しているアプリケーションから PubSub ソリューションを外部に出すことで、アプリケーション間の共有通信の要件を満たすことができます。

 

次の図は、マルチサーバー PubSub アーキテクチャ上の 2 つのクライアント間でメッセージが WebSocket を通過するパスを示しています。永続的な接続は、各クライアントと WebSocket サーバー間のロードバランサーを通じて確立されます。また、永続的な接続は、WebSocket サーバーと PubSub サーバー間で、すべてのクライアント間で共有されるサブスクリプショントピックごとに確立されます。

 

 

カスタムの PubSub ソリューションも可能ですが、既存のソフトウェアアプリケーションを使用してこの機能を提供することもできます。Redis は、高速なオープンソースのインメモリ型データストアおよびキャッシュで、PubSub をサポートしています。Amazon ElastiCache for Redis は、Redis 対応のインメモリサービスです。使いやすく、Redis の性能を利用でき、もっとも要求の厳しいアプリケーションに対応できる可用性、信頼性、パフォーマンスを提供します。

Using ElastiCache for Redis and the WebSocket support found in the that is part of Elastic Load Balancing の一部である Application Load Balancer にある ElastiCache for Redis と WebSocket サポートを使用して、サンプルのチャットアプリケーションを構築する方法を説明します。アプリケーションには、Node.js と AWS Elastic Beanstalk に基づくバックエンドと、Vue.js ウェブクライアントがあります。サンプルアプリケーションのすべてのコードは、すべて elasticache-redis-chatapp GitHub リポジトリにあります。

アーキテクチャ

次の図は、Redis、Application Load Balancer、Node.js Elastic Beanstalk アプリケーション、および Vue.js ウェブクライアント用の ElastiCache を使用した AWS の最終的なアーキテクチャを示しています。

チャットアプリケーションの実装を高いレベルで見てみましょう。

実装の概要

サンプルチャットアプリケーションは、以下のスクリーンショットに示す共有チャットルームで通信するメンバーとメッセージで構成されています。

このサンプルアプリケーションでは、メンバー登録、プロフィール管理、ログインを拒否しています。代わりに、ブラウザでチャットアプリケーションを開くと、ユーザーの代わりにランダムなユーザー名とアバターでメンバーが生成されます。この名前とアバターは、左側のメンバーリストに表示されます。他のメンバーがブラウザでアプリケーションを開いて参加したり離したりすると、その名前がウェブアプリケーションに表示されます。メンバーは、他のウェブクライアントに再配信されメインのチャットウィンドウに表示されるメッセージを送信できます。

次に、Vue.js ウェブクライアントの詳細を調べ、Node.js バックエンドアプリケーションを確認します。

Vue.js ウェブクライアント

ウェブクライアントは、ビューレイヤを管理する Vue.js、UI 用の Bootstrap、および WebSocket 通信用の Socket.io を使用して実装されています。初心者向けに複雑さを軽減するために、JavaScript バンドルは使用していません。ただし、本稼働アプリケーションでは webpack または類似のソフトウェアを検討する必要があります。Vue.js は、基礎となるデータモデルの更新に基づいて UI の変更をレンダリングします。これらのフレームワークとライブラリを、最新の単一ページの代表的なウェブアプリケーションとして選択しました。ただし、コミュニティには多くの類似した選択肢があり、毎日多くのものが出現しています。

次に、Vue.js アプリケーションコンポーネントを設定する HTML マークアップのコードスニペットと、メンバーを表示するイテレーターを示します。機能に焦点を当てるために、いくつかの中間的なマークアップと CSS スタイルを削除しています。。完全なサンプルは GitHub リポジトリにあります。

<html>
<body>
    <div id=”app”>
        <li v-for="(value, key) in members">
            <img v-bind:src="value.avatar">
            <small>{{ value.username }}</small>
        </li>
    </div>

v-for パラメータは、イテレーターを定義するために使用されます。この場合は、この後で説明するメンバーオブジェクトデータモデルのキー値タプルです。反復されるループ内で Mustache テンプレートを使用して、各メンバーオブジェクトにアクセスし、ユーザー名を表示します。Mustache テンプレートは HTML 属性内では機能しないため、メンバーのアバター画像 URL を解決するためには v-bind 引数を使用する必要があります。

Vue.js は、スマート DOM の差の計算に基づいて UI の変更を最小限にレンダリングします。このアプローチにより、基になる Vue.js データモデルの状態変更に専念できます。サンプルアプリケーションでは、HTML 内で JavaScript コードをインライン展開しています。しかし、本番稼働用システムでは、外部の .vue ファイルを使用して UI コンポーネントをモジュール化し、ビルド時に webpack を使用して変換する可能性があります。Socket.io ライブラリも初期化されており、多少カバーされています。

<script src="js/vue/2.1.10/vue.min.js"></script>
<script src="js/socket.io/1.7.2/socket.io.min.js"></script>
<script>
    var socket = io();

    new Vue({
        el: '#app',
        data: {
            message: '',
            messages: [],
            members: {}
        }

ここでは、Vue.js アプリケーションを宣言し、アプリケーション ID で HTML div 要素にバインドしました。また、次の 3 つのデータモデルを宣言しました。

  • message: フォームに入力されたメッセージテキスト
  • messages: メッセージのリスト。メッセージを追加するだけなので、配列が使用されます。
  • messages: メンバーのリスト。メンバーがチャットルームを離れたときにメンバーを見つけて削除できるように、オブジェクトが使用されています。

マークアップおよび Vue.js アプリケーション宣言に加えて、ウェブクライアントは WebSocket 接続を確立し、サブスクライブするトピックを宣言し、それらのトピックに発行されたメッセージが前に宣言されたデータモデルをどのように変更するかを確立します。このアプリケーションでは、コミュニケーションのための 5 つのトピックを確立します。それぞれのトピックが、トリガーイベントと対応するアクションとともに示されます。

[メッセージ]

トリガー: メッセージがチャットルームに送信される。

アクション: メッセージテキストおよびメンバーメタデータを使用して、メッセージのリストを更新します。

[member_add]

トリガー: メンバーがチャットルームに参加する。

アクション: メンバーのユーザー名とパスワードをメンバーのリストに追加します。

[member_delete]

トリガー: メンバーがチャットルームを離れる。

アクション: メンバーの一覧からメンバーを削除します。

[message_history]

トリガー: クライアントがメッセージのリストを初期化。

アクション: メッセージのリストを、最近の履歴メッセージの切り詰められたリストとして設定します。

[member_history]

トリガー: クライアントがメンバーのリストを初期化。

アクション: メンバーリストをチャットルームに参加しているメンバーのリストとして設定します。

 

以下に、これらのメソッドを実装するための JavaScript コードを示します。以前の Vue.js コードをリファレンスポイントとして維持しています。

new Vue({
    el: '#app',
    data: {
        message: '',
        messages: [],
        members: {}
    },
    methods: {
        send: function() {
            socket.emit('send', this.message);
            this.message = '';
        },
    mounted: function() {
        socket.on('messages', function(message) {
            this.messages.push(message);
        }.bind(this));

        socket.on('member_add', function(member) {
            Vue.set(this.members, member.socket, member);
        }.bind(this));

        socket.on('member_delete', function(socket_id) {
            Vue.delete(this.members, socket_id);
        }.bind(this));

        socket.on('message_history', function(messages) {
            this.messages = messages;
        }.bind(this));

        socket.on('member_history', function(members) {
                    this.members = members;
        }.bind(this));
    }

以前のコードスニペットで宣言された Socket.io オブジェクトは、前述のトピックごとに 1 つずつ、socket.on を使用してデータトピックにサブスクライブします。メッセージがトピックに発行されると、コールバック関数が実行されます。データモデルは、アクション (セット、追加、削除) とターゲットデータモデル (配列、オブジェクト) に従って更新されます。バインド (this) 文が追加され、Vue.js データモデルがコールバック関数スコープに挿入されます (詳細については、Function.prototype.bind を参照してください)。

最後は、メッセージフォームの送信を処理する Vue.jsメソッドです。Vue.js は、フォーム提出をメソッドにバインドする便利なメソッドを提供します。このメソッドは、WebSocket 上にメッセージテキストを発行し、メッセージを空の文字列に設定します。この文字列は、Vue.js バインディングを使用して UI を更新します。

Node.js バックエンドアプリケーション

ここでは、ウェブクライアントの基本について説明しました。次に、Node.js バックエンドアプリケーションについて見ていきましょう。PubSub を使ってデータを保持し、WebSocket メッセージを再配布するために Redis がどのように使用されるかを見ていきます。

Redis と WebSockets の設定

ウェブクライアントがブラウザで開かれると、PubSub アプリケーションで WebSocket が確立されます。接続すると、アプリケーションは既存のメンバーとメッセージを新しいクライアントに発行するために、いくつかのデータをアセンブルする必要があります。また、新しいチャットルーム参加者について他のクライアントを更新する必要があります。次に、HTTP アプリケーションと WebSocket の宣言を示すコードスニペットを示します。

var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var port = process.env.PORT || 3000;

WebSocket リスナーの作成に加えて、アプリケーションは Redis クラスタへの複数の接続も確立する必要があります。 Redis データモデルを更新してトピックにメッセージを発行するには、1 つの接続が必要です。 トピックのサブスクリプションごとに追加の接続が必要になります。

var Redis = require('ioredis');
var redis_address = process.env.REDIS_ADDRESS || 'redis://127.0.0.1:6379';

var redis = new Redis(redis_address);
var redis_subscribers = {};

function add_redis_subscriber(subscriber_key) {
    var client = new Redis(redis_address);

    client.subscribe(subscriber_key);
    client.on('message', function(channel, message) {
        io.emit(subscriber_key, JSON.parse(message));
    });

    redis_subscribers[subscriber_key] = client;
}
add_redis_subscriber('messages');
add_redis_subscriber('member_add');
add_redis_subscriber('member_delete');

このコードスニペットでは、
Redis コマンドチャネルは ioredis JavaScript クライアントを使用して確立されています。
また関数は、新しいトピックのチャンネルを初期化するためにも定義され、
チャンルのトピックについてキー入力されたすべての受信者のハッシュに追加されます。
各サブスクリプションチャネルは同じように機能します。

  • Redis サブスクリプションチャンネルで JSON 文字列メッセージを受信します。
  • JSON 文字列を JavaScript オブジェクトに解析します。
  • Redis PubSub と同じトピックを使用して、JavaScript オブジェクトを WebSocket 接続に発行します。

この後で説明するように、JavaScript オブジェクトは、Redis データ型の値として保存され、PubSub トピックに発行されるときに JSON 文字列にシリアル化されることが重要です。WebSocket を介して発行する前に、JSON 文字列を JavaScript オブジェクトに逆シリアル化して戻す必要があります。Socket.io ライブラリがクライアントと通信するときには、オブジェクトをシリアル化したり逆シリアル化したりするため、この逆シリアル化が生じる必要があります。

ウェブクライアントがブラウザのチャットルームに参加すると、クライアントは WebSocket への新しい接続を確立します。次のような関数を定義することによって、この接続が確立されたときにアクションを実行できます。

io.on('connection', function(socket) {

... application business logic ... 

}

ソケットは、クライアントへの各 WebSocket 接続を識別するために使用される ID プロパティを含むオブジェクトです。socket.id 値を使用して、メンバーを識別します。この識別により、Redis データモデルからメンバーを見つけて削除することができます。また、member_delete トピックを使用しているすべてのチャットルームクライアントにメンバーの削除を伝えることもできます。以下で説明する他の関数は、このコールバック関数のコンテキストにあります。

次のセクションでは、新しいクライアントが WebSocket を介して Node.js バックアップアプリケーションに接続すると何が起こるかを見ていきます。

新しいクライアント接続の初期化

新しいクライアントがチャットルームに参加すると、いくつかのことが起こります。

現在のメンバーリストが取得されます。
これがクライアントの再接続でない限り、ランダムなユーザー名とアバター URL で新しいメンバーが作成され、Redis Hash に保存されます。
最近の履歴メッセージの切り詰められたリストが検索されます。
これらのタスクを達成するためのコードを見てみましょう。まず、次のコードがあります。

var get_members = redis.hgetall('members').then(function(redis_members) {
    var members = {};
    for (var key in redis_members) {
        members[key] = JSON.parse(redis_members[key]);
    }
    return members;
});

ioredis JavaScript クライアントは、非同期実行処理で Promises を使用します。HGETALL (‘members’) 呼び出しは、キー ‘members’に格納されているハッシュのすべてのキーと値を返します。Redis はハッシュデータ型をサポートしていますが、1 レベルの深さしかありません。ハッシュの値は文字列でなければなりません。コールバック関数は、次のチェーンで逆シリアル化されたハッシュのキーと値のペアを反復して、メンバーを初期化します。

var initialize_member = get_members.then(function(members) {
    if (members[socket.id]) {
        return members[socket.id];
    }

    var username = faker.fake("{{name.firstName}} {{name.lastName}}");
    var member = {
        socket: socket.id,
        username: username,
        avatar: "//api.adorable.io/avatars/30/" + username + '.png'
    };
    
    return redis.hset('members', socket.id, JSON.stringify(member)).then(function() {
        return member;
    });
});

initialize_member Promise 関数は、メンバーが再接続ソケットであるかどうかをまずチェックします。再接続ソケットでない場合は、Faker を使用してランダムなユーザー名で新しいメンバーが生成されますこのユーザー名から、Adorable Avatars サービスを使用してランダムなアバター URL が生成されます。

クライアント初期化の最後のステップは、過去の最近のメッセージの切り捨てられたリストを取得することです。これを行うには、ソート対象セットと呼ばれる別の Redis データ型を利用することができます。このタイプは Redis セットに似ていますが、セット内の各要素のランクを含みます。ソート対象セットは、リーダーボードの一般的なデータ型です。タイムスタンプがランクとして使用されている場合は、時間順に並べられた要素のコレクションを格納するためにも使用できます。

var get_messages = redis.zrange('messages', -1 * channel_history_max, -1).then(function(result) {
    return result.map(function(x) {
       return JSON.parse(x);
    });
});

We use a Redis method on the Sorted Set, called ZRANGE というソート対象セットでは、ランクに基づいて要素のリストを返す Redis メソッドを使用します。要素は最低スコアから最高スコアまで並べられます。したがって、初期化時に取得するメッセージの最大数 (-1 * channel_history_max) まで最後の要素 (-1) を取得する必要があります。繰り返しになりますが、各要素は JSON 文字列としてシリアル化されていますので、要素を JavaScript オブジェクトに対して逆シリアル化する必要があります。

要約すると、新しいクライアントがチャットルームに参加すると、いくつかのことが起こります。

  1. 現在のメンバーリストが取得されます。
  2. これがクライアントの再接続でない限り、ランダムなユーザー名とアバター URL で新しいメンバーが作成され、Redis Hash に保存されます。
  3. 最近の履歴メッセージの切り詰められたリストが検索されます。

これらの手順をそれぞれを確認しました。ここで、初期化とクライアントへのストリームデータの完了方法を見ていきましょう。ioredis は Promise を使用するため、非同期の実行を連鎖させて、すべてが完了するのを待ってから Promise.all を使用して結果を処理できます。

Promise.all([get_members, initialize_member, get_messages])
    .then(function(values) {
        var members = values[0];
        var member = values[1];
        var messages = values[2];

...

)};

これですべての必要なデータが得られたので、WebSocket 接続を使用してデータをストリーミングして新しいクライアントを初期化し、新しいメンバーがチャットルームに参加したことをすべてのメンバーに伝える必要があります。

io.emit('member_history', members);
io.emit('message_history', messages);
redis.publish('member_add', JSON.stringify(member));

Socket.io の emit メソッドを使用して、初期化中のクライアントにメッセージとメンバーのリストをストリーミングします。1 つの WebSocket を使用して複数のメッセージを送信できます。ここでは、トピック (member_history, message_history) は先ほどのクライアントコードでレビューしたトピックリスナーに対応しています。新しいメンバーはすべての参加者に伝えなければなりません。これを行うには、Redis コマンドチャネルを使用して、シリアル化された JSON 文字列を member_add トピックに発行します。すでに行っているように、WebSocket を使用して同じトピックをリッスンしているクライアントにメッセージを再配布するため、3 つの Redis トピックを設定しました。

次のセクションでは、チャットルームで送信されたメッセージを処理するハンドラを設定する方法を見ていきましょう。

メッセージの処理

新しいクライアントが初期 WebSocket 接続を完了すると、新しいクライアントによって送信されるメッセージ用のメッセージハンドラも定義する必要があります。メッセージは、メッセージテキスト、メンバーのユーザー名、メンバーのアバター、メッセージの作成タイムスタンプで構成されます。

socket.on('send', function(message_text) {
    var date = moment.now();
    var message = JSON.stringify({
        date: date,
        username: member['username'],
        avatar: member['avatar'],
        message: message_text
    });

    redis.zadd('messages', date, message);
    redis.publish('messages', message);
});

ZADD コマンドとメッセージ作成タイムスタンプをランクとして使用して、ソート対象セットに格納されたメッセージ履歴にメッセージを追加します。最後に、Redis コマンドチャネルを使用してメッセージトピックを発行しました。Redis/WebSockets の再配布は以前に定義しています。

クライアントを初期化し、チャットルームで送信されたメッセージを処理する方法について説明しました。最後に、クライアントがチャットルームを離れるときの対処方法を見てみましょう。

切断処理

Socket.io は、クライアントがサーバーに接続するときに確立された WebSocket のハートビートを作成します。ハートビートが失敗すると、クライアントで切断イベントが発生します。

socket.on('disconnect', function() {
    redis.hdel('members', socket.id);
    redis.publish('member_delete', JSON.stringify(socket.id));
});

クライアントが切断すると、メンバーの初期化中に実行されたアクションが取り消されます。まず、Redis HDEL メソッドを使用して、クライアントの WebSocket ソケット ID を使用するメンバーハッシュ Redis データ型からクライアントを削除します。同じメソッドをクライアントをハッシュに追加するのに使用します。すべての参加者に対してチャットルームに加わった新しいメンバーを通知するように、メンバーがチャットルームを離れることをすべての参加者に通知する必要があります。これは、member_delete Redis トピックを使用して行います。このトピックは、WebSocket を使用して残りのクライアントに再配布されます。

これでコードの確認は完了です。次に、AWS CloudFormation を使用してアプリケーションスタックを AWS にデプロイする方法を確認します

AWS CloudFormation を使用したアプリケーションスタックのデプロイ

CloudFormation は、開発者やシステム管理者は、関連する AWS リソースのコレクションを簡単に作成および管理する方法を提供します。CloudFormation は、整った予測可能な方法でリソースを提供し、更新します。チャットアプリケーションの CloudFormation スタックを起動するには、次のボタンをクリックします。

CloudFormation スクリプトは、Elastic Beanstalk 環境、アプリケーション、および構成テンプレートを作成します。Redis の ElastiCache クラスタと、ロードバランサ、アプリケーションサーバー、Redis クラスタの Amazon EC2 セキュリティグループも作成します。このようにして、アーキテクチャレイヤ間の最小権限セキュリティ構成にベストプラクティスを使用します。

ElisiCache for Redis の設定スニペットに関する 1 つの注意点AWS::EC2::SecurityGroup では進入セキュリティルールのインライン指定が可能ですが、そうすることで、CacheCluster と SecurityGroup の間に循環参照が作成されてしまいます。次のスニペットに示すように、進入ルールを別の AWS::EC2::SecurityGroupIngress に分割して循環参照を破棄する必要があります。

リソース:

RedisCluster:
    Type: AWS::ElastiCache::CacheCluster
    Properties:
      CacheNodeType:
        Ref: ClusterNodeType
      VpcSecurityGroupIds:
        - !GetAtt CacheSecurityGroup.GroupId
      Engine: redis
      NumCacheNodes: 1
  CacheSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Cache security group
  CacheSecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !GetAtt CacheSecurityGroup.GroupId
      IpProtocol: tcp
      FromPort: !GetAtt RedisCluster.RedisEndpoint.Port
      ToPort: !GetAtt RedisCluster.RedisEndpoint.Port
      SourceSecurityGroupId: !GetAtt ApplicationSecurityGroup.GroupId

次に、WebSocket をサポートするための Elastic Beanstalk Nginx プロキシ設定の設定を変更する方法を確認してみましょう。

WebSocket サポートのための AWS Elastic Beanstalk の Nginx 設定

AWS Elastic Beanstalk は、Java、.NET、PHP、Node.js、Python、Ruby、Go および Docker を使用して開発されたウェブアプリケーションやサービスを、Apache、Nginx、Passenger、IIS など使い慣れたサーバーでデプロイおよびスケーリングするための、使いやすいサービスです。

Elastic Beanstalk は、Elastic Load Balancer (ELB) と Application Load Balancer (ALB) の両方をサポートします。弊社のクライアントとサーバーは WebSocket を使用して通信するので、WebSockets サポートのための ALB を構成しますサンプルの Node.js バックエンドアプリケーションでは、Node.js ベースの Elastic Beanstalk の事前構成済みアプリケーションスタックを選択します。アプリケーションコードの前で、ウェブ層プロキシとして Nginx を使用します。

WebSocket がサポートされていない場合、Socket.io にはポーリング戦略に戻る手段があります。ただし、単純に ALB と Nginx に設定を変更することで、WebSocket のサポートを有効にし、サーバーからクライアントへのプッシュベースのデータストリームを使用することができます。ALB で WebSocket サポートを有効にするには、クライアントが 2 つの連続した HTTP リクエストを行って接続をアップグレードして Websocket を使用したときに、同じインスタンスが応答するように、スティッキーセッションを有効化する必要があります。Nginx で WebSocket サポートを有効にするには、Elastic Beanstalk の .ebextensions メカニズムを使用して、Nginx の設定を少し変更する必要があります。コンテナコマンドは、アプリケーションアーカイブが展開された後、アクティブアプリケーションとしてインストールされる前に、アプリケーションに変更を導入する方法を提供します。

container_commands:
  enable_websockets:
    command: |
      sed -i '/\s*proxy_set_header\s*Connection/c \
              proxy_set_header Upgrade $http_upgrade;\
              proxy_set_header Connection "upgrade";\
      ' /tmp/deployment/config/#etc#nginx#conf.d#00_elastic_beanstalk_proxy.conf

前述のコードスニペットは、Nginx 設定ファイルを所定の位置で変更します。/tmp/deployment/config/#etc#nginx#conf.d#00_elastic_beanstalk_proxy.conf。これは、sed コマンドを使用して「プロキシセットヘッダー」行を検索し、WebSocket をサポートする設定に置き換えます。アプリケーションがインストールされると、Elastic Beanstalk は設定ファイルを /etc/nginx/conf.d/00_elastic_beanstalk_proxy.conf にコピーします。この手順を実行すると、Nginx サービスが再起動され変更がアクティブになります。

まとめ

このブログ記事では、publish-subscribe パターンについて見てきました。また、それを ElastiCache for Redis 内で使用して、チャットアプリケーションの複数のクライアントの双方向ストリーミング通信をサポートする方法についても説明しました。

リマインダーとして、awslabs GitHub リポジトリにこのサンプルアプリケーションの完全なソースがあります。このサンプルアプリケーションを起動したら、ユーザー認証、添付ファイル、または自分のチャットや PubSub アプリケーションに役立つその他の機能を追加して、チャットアプリケーションに独自のアイデアを組み込んで拡張することをお勧めします。

12 月の AWS Black Belt オンラインセミナーのご案内

こんにちは。プロフェッショナル サービスの宮本です。AWS Black Belt オンラインセミナー12月の配信についてご案内させて頂きます。サービスカットは、10/24 に Generally Available を迎えた Amazon Aurora with PostgreSQL Compatibility をはじめ、今月も様々なテーマを取り扱います。また、ソリューションカットは、「AWSサービスを利用したアプリケーション開発を始めよう」と題して、AWSにおけるアプリケーション開発に活用できる様々なサービスについてご紹介や、AWSにおけるIPv6のサポート状況についてご紹介します。

 

 

 

 

 

 

 

 

 

 

 

 

12月の開催予定

サービスカット
12/6(水) 18:00-19:00 Amazon Elasticsearch Service
12/14(木) 18:00-19:00 Amazon ElastiCache ※ 通常の開催曜日と異なりますのでご注意ください。
12/20(水) 18:00-19:00 Aurora PostgreSQL

ソリューションカット
12/1(金) 12:00-13:00 AWS re:Invent 2017 Report  ※ 通常の開催曜日と異なりますのでご注意ください。
12/5(火) 12:00-13:00 AWSのIPv6対応
12/12(火) 12:00-13:00 AWSサービスを利用したアプリケーション開発を始めよう

お申し込みは、それぞれ上記のリンクより行って頂けます。キャンセルの際も連絡不要ですので是非お早めにご登録ください。Speaker、Staff 一同みなさまのご参加をお待ちしております。

今すぐご利用可能 – Amazon EC2 コンピューティング最適化インスタンス C5

新しくコンピューティングに最適化されたC5インスタンスが、3つのAWSリージョン、6つのインスタンスサイズでリリースされ、今日から利用可能であることを発表することに興奮しています!

これらのインスタンスは、バッチ処理、分散解析処理、高性能コンピューティング(HPC)、広告配信、スケーラブルなマルチプレーヤゲーミング、ビデオエンコーディングなどのコンピューティング重視のアプリケーション用に設計されています。 新しいインスタンスは、C4インスタンスに対して25%の価格/パフォーマンスの向上をもたらし、一部のワークロードでは50%を上回ります。 また、vCPUあたりの追加メモリ(新しいAVX-512命令を使用できるコードにおいて)は、2倍のベクターおよび浮動小数点演算のパフォーマンスを備えています。

AWSによって設計、構築された専用ハードウェアにさまざまな種類の作業をオフロードすることに長期的に視点を置き、最高のネットワーク、ストレージ、およびコンピューティングパフォーマンスを顧客に提供するために、私たちは長年にわたりノンストップで取り組んできました。 C5インスタンスタイプには、最新世代のハードウェアオフロードが組み込まれており、ハードウェアに手を加えて新しいハイパーバイザーを追加することで、さらに大きな前進を遂げています。新しいハイパーバイザーを使用すると、ホストハードウェアが提供するすべての処理能力にアクセスすることができます。同時に、パフォーマンスをより一貫して強化し、さらにセキュリティを強化します。 私たちはAWS re:Inventで、それに関する多くの技術的な詳細を共有します。

新しいインスタンス
C5インスタンスは6つのサイズが利用可能です:

Instance Name vCPUs
RAM
EBS Bandwidth Network Bandwidth
c5.large 2 4 GiB Up to 2.25 Gbps Up to 10 Gbps
c5.xlarge 4 8 GiB Up to 2.25 Gbps Up to 10 Gbps
c5.2xlarge 8 16 GiB Up to 2.25 Gbps Up to 10 Gbps
c5.4xlarge 16 32 GiB 2.25 Gbps Up to 10 Gbps
c5.9xlarge 36 72 GiB 4.5 Gbps 10 Gbps
c5.18xlarge 72 144 GiB 9 Gbps 25 Gbps

各vCPUは、3.0 GHz Intel Xeon Platinum 8000シリーズプロセッサのハードウェアハイパースレッドコアです。 EC2用に最適化されたこのカスタムプロセッサは、2つの最大サイズのC-stateを完全に制御できるため、Intel Turbo Boost Technologyを使用して単一コアで最大3.5GHzで動作できます。

この表からわかるように、4つの最小インスタンスサイズは、前世代のコンピューティング最適化インスタンスよりも大幅に拡張されたEBSならびにネットワーク帯域幅を提供します。

すべてのネットワーキングおよびストレージ機能はハードウェアで実装されているため、C5インスタンスにはElastic Network Adapter(ENA) およびNVMe用のドライバを含むHVM AMIが必要です。 最新のAmazon Linux、Microsoft Windows、Ubuntu、RHEL、CentOS、SLES、Debian、およびFreeBSD AMIはすべてC5インスタンスをサポートしています。 もし機械学習の推論処理やその他のコンピューティングが重視される作業を行っている場合は、最新のバージョンのIntel Math Kernel Libraryを確認してください。 インテル®Xeon®Platinumプロセッサー用に最適化されており、作業を大幅に高速化する可能性があります。

Xenハイパーバイザーを使用するインスタンスとの互換性を維持するために、EBSボリュームのデバイス名は、引き続き既存の/dev/sdおよび/dev/xvd のプレフィックスを使用します。 NVMeドライバが独自のデバイス名を割り当てるため、インスタンスにボリュームを接続するときに指定したデバイス名は使用されません(詳しくは、Amazon EBSおよびNVMeを参照してください)。

nvmeコマンドは、各ボリュームに関する追加情報を表示します (必要に応じてsudo yum -y install nvme-cliを使用してインストールします)。

出力のSNフィールドは、 “vol”プレフィックスの後に ” – “を挿入することでEBSボリュームIDにマッピングできます (悲しいことに、NVMe SNフィールドがID全体を格納するのに十分な長さではありません)。 この情報を使用して、接続されている各ボリュームのEBSスナップショットを作成する簡単なスクリプトを次に示します。

$ sudo nvme list | \
  awk '/dev/ {print(gensub("vol", "vol-", 1, $2))}' | \
  xargs -n 1 aws ec2 create-snapshot --volume-id

もう少し作業 (そして多くのテスト) をすれば、一杯になっているEBSボリュームを拡張するスクリプトを作成できます。

Getting to C5

先ほどお話したように、私たちはハードウェアアクセラレータに処理をオフロードする努力をかなり前から行なっています。 ここに要約します:

CC12010年ローンチ、CC1はスケールアウトHPCアプリケーションをサポートするように設計されています。 10 Gbpsネットワーキングをサポートする最初のEC2インスタンスと、HVM仮想化をサポートする最初のEC2インスタンスでした。 CC1用に設計したネットワークファブリック(独自のスイッチハードウェアに基づく)は、すべてのAWSデータセンターの標準となっています。

C32013年ローンチ、C3ではEnhanced Networkingが導入され、各仮想プライベートクラウド(VPC)内のソフトウェア定義ネットワークをサポートする専用のハードウェアアクセラレータが使用されています。 ハードウェア仮想化は、ゲストOSによる直接アクセスを優先してI / Oスタックをハイパーバイザから削除し、より高いパフォーマンスと変動性の低減をもたらします。

C42015年ローンチ、C4インスタンスはデフォルトで専用ネットワーク接続によってEBS最適化され、EBSの処理(暗号化されたEBSボリュームに対するCPUでの暗号化処理を含む)をハードウェアアクセラレータにオフロードします。

C5 – 本日ローンチ、C5インスタンスに動力を与えるハイパーバイザーは、ホストCPUのほとんどのリソースをお客様のインスタンスに費やすことを可能にします。 ENAネットワーキングとEBSへのNVMeインターフェイスはどちらもハードウェアアクセラレータによって駆動されます。 インスタンスは、効率を高めるために除去されたXen準仮想化ネットワーキングまたはブロックデバイスドライバを必要としません(またはサポートしません)。

今後はこのハイパーバイザーを使用して他のインスタンスタイプを強化します、そしてAWS re:Inventのセッションで追加の技術的な詳細を共有する予定です。

本日C5を発表

オンデマンドとスポット(リザーブドインスタンスも利用可能)で、米国東部(バージニア)、米国西部(オレゴン)、EU(アイルランド) リージョンでC5インスタンスを今すぐ起動できます。なお対象リージョンの追加作業を行なっています。

使用前の簡単な注意点:現在のNVMeドライバは高性能シーケンシャルワークロード用に最適化されておらず、sc1またはst1ボリュームと組み合わせてC5インスタンスを使用することはお勧めしません。 私たちはこの問題を認識しており、この重要な利用ケースのためにドライバを最適化するよう努めています。

Jeff;

(翻訳:SA小川、元の記事はこちらです)