Amazon Web Services ブログ

App Runner VPCネットワーク接続時における可観測性

AWS App Runner を利用すると任意の規模で web アプリケーションや API を素早くデプロイできます。ソースコードまたはコンテナイメージがあれば始められ、App Runner はアプリケーションに必要なサーバ、ネットワーク、ロードバランサをフルマネージドで提供します。もし必要があれば、App Runner でデプロイパイプラインの設定もできます。

2022/02 に App Runner が Amazon Virtual Private Cloud ( Amazon VPC ) をサポート、2022/04 に AWS X-Ray をサポートしたことを AWS はアナウンスしました。

VPC サポートによって、アプリケーションは自身が管理する Amazon VPC 内のプライベートなリソース、例えば Amazon Relational Database Service ( Amazon RDS )Amazon ElasticCache for Redis、Amazon ElasticCache for Memcached 等に接続できるようになりました。

X-Ray サポートによって、サイドカーやエージェントの構成及びセットアップをすることなく App Runner サービスにトレースを追加できるようになりました。AWS Distro for OpenTelemetry ( 以下、ADOT ) を利用してアプリケーションを計装することで、AWS X-Ray でコンテナ化されたアプリケーションをトレースできます。

このブログでは、Node.js ウェブアプリケーションから Amazon RDS データベースへ接続するようなケースで、2 つの機能がどのように働くか学ぶことができます。

アーキテクチャー

ウォークスルー

前提条件

作業者は、以下の前提条件を満たす必要があります。

  • 以下のリソースに対する作成権限 及び アクセス権限をもつ AWS アカウントを有している
    • VPC
    • Subnets
    • Internet gateway
    • NAT gateway
    • Security group
    • IAM role
    • Amazon RDS database
    • X-Ray
    • Amazon CloudWatch
    • App Runner
  • コンテナの基礎知識を有している

ステップ 1: Amazon VPC and subnets の準備

以下のステップでは、データベース 及び アプリケーションをホストするサブネットを含む hotel-app-vpc を作成します。

  1. マネジメントコンソールで Amazon VPC の画面へ遷移します。
  2. VPC ダッシュボードで Launch VPC Wizard を選択します。
  3. 以下の情報をウィザードに入力し、Create VPC を選択します。
    1. Name tag auto-generation: “hotel-app” を入力します。
    2. Availability Zones: 3 を選択します。
    3. NAT gateways: In 1AZ を選択します。
    4. VPC endpoints: None を選択します。
    5. All other settings: デフォルトのままにします。

VPC、サブネット及びその他の構成リソースを作成するために数分かかります。それらが作成されると、ウィザードの最後にある View VPC ボタンを押すことで詳細を確認できます。VPC とサブネットの名前を書き留めます。これらは後の作業で利用します。

ステップ 2: セキュリティグループの作成

VPC へのアウトバウンドアクセス可能なアプリケーションを App Runner 上にデプロイするために、まずアプリケーションに関連づける 1 つ以上のサブネット及びセキュリティグループを指定して App Runner VPC connector を作成する必要があります。

セキュリティグループに関しては、App Runner VPC connector はアプリケーションからのアウトバウンド通信にのみ使用されることに注意してください。従って、セキュリティグループのインバウンドルールは関連づけられず、無視されます。重要なのはアウトバウンドルールであり、目的の宛先エンドポイントへの通信を許可する必要があります。もしより詳細を知りたい方は、Deep Dive on AWS App Runner VPC Networking を参照ください。

ここでは、ステップ 1 で作成したVPC内に2つのセキュリティグループを作成します。

  • hotel-app-svc-sg: App Runner VPC connector 用のセキュリティグループ
Rule IP version Type Protocol Port range Destination
Inbound N/A N/A N/A N/A N/A
Outbound IPv4 All Traffic All All 0.0.0.0/0
  • hotel-app-rds-sg: アプリケーションからアクセスされる Amaozn RDS データベース用のセキュリティグループ
Rule IP version Type Protocol Port range Source
Inbound N/A MYSQL/Aurora TCP 3306 sg-0f7ff683254111fb2 / hotel-app-svc-sg

宛先リソースに関連づけられた hotel-app-rds-sg が、VPC connector セキュリティグループである hotel-app-svc-sg からの通信を許可するために適切なインバウンドルールをもつか確認する必要があります。

コンソールでセキュリティグループを作成するため次の手順を利用します。

  1. Amazon VPC コンソールに遷移します。ナビゲーションペインで Security Groups を選択し、次に Create security group を選択します。
  2. セキュリティグループ名に hotel-app-svc-sg と入力し、セキュリティグループの説明も記載する(推奨されるセキュリティグループ名は、表を参考にしてください)。作成後にセキュリティグループ名と説明を変更することはできません。
  3. VPC の選択では、hotel-app-vpc を選んでください。ここでは、セキュリティグループルールは空白のままにしてください。後で設定します。
  4. タグを追加できます。また、タグは後で追加することもできます。タグを追加する際は、Add new tag を選び、タグのキーとバリューを入力します。Create security group を選んでください。
  5. hotel-app-rds-sg を作成するために、再度上記 1-3 を実施します。
  6. 上記の表を参照し、セキュリティグループルールを追加します。
    1. Type では、MYSQL/Aurora を選択します。
    2. Destination では、hotel-app-svc-sg を選択します。
  7. Create security group を選択します。
  8. Amazon VPC コンソールに遷移します。ナビゲーションペインで Security Groups を選択します。上記で作成した2つのセキュリティグループ hotel-app-svc-sg 及び hotel-app-rds-sg が確認できます。
  9. hotel-app-svc-sg セキュリティグループを選択します。
  10. Actions, Edit outbound rules を選択します。
  11. Add rule を選択し、上記の表を参考に以下のルールを設定します。
    1. Type では、All trafic を選択します。
    2. Destination では、Anywhere-IPv4 を選択します。
  12. Save rules を選択します。

セキュリティグループの名前を書き留めます。これは Amazon RDS データベース及び App Runner サービスを作成するときに使用します。

ステップ 3: Amazon RDS データベースの準備

自身の VPC 内にホストされる Amazon RDS データベースのセットアップします。

  1. Amazon RDS コンソールに遷移します。
  2. ナビゲーションペインで Databases を選択します。
  3. Create database を選択し、Standard Create が選択されていることを確認してください。
  4. Engine type では、Amazon Aurora を選択します。
  5. Edition では、Amazon Aurora with MySQL compatibility を選択します。
  6. Hide filters を開き、Show versions that support Serverless v2 を選択し、使用可能なオプションを選択します。
  7. Templates では、Dev/Test を選択します。
  8. DB cluster identifier では、データベースクラスター名を入力、またはデフォルトのままにします。ここでは、hotel-app-database とします。
  9. Master username では、admin のままにします。
  10. DB クラスター用の自動生成パスワードを使用するために、Auto generate a password のチェックボックスがオンになっていること確認してください。パスワードを入力する場合は、 Auto generate a password をオフにして、Master password 及び Confirm password に同じ値が入力してください。
  11. DB instance class では、Serverless を選択してください。
  12. Connectivity では、ステップ 1,2 で作成した hotel-app-vpc 及び hotel-app-rds-sg を使用します。データベースを公開する必要はありません。それは、プライベート通信を使いデータベースへ接続するからです。
  13. 他の設定値はデフォルトのままにします。
  14. Create database を選択します。

RDS データベースを作成するために数分かかります。Amazon RDS Database のリストから、作成したデータベースを確認できます。hotel-app-database を選択して、データベースの詳細を確認します。

ライターインスタンスのエンドポイント名、パスワード、ユーザー情報を書き留めてください。後で利用します。

ステップ 4: アプリケーションソースコードリポジトリの準備

App Runner では、ソースコードリポジトリにホストされたソースコードまたはコンテナイメージを利用して新たなサービスをデプロイすることができます。この例では、GitHub 内のプライベートプロジェクトを使用しています。

下記は、データベースに接続するシンプルな Node.js ウェブアプリケーションです。このアプリケーションでは、Amazon RDS でホストされた MySQL/Aurora へホテルの部屋情報を格納します。アプリケーションへ HTTP(S) の GET 及び POST リクエストを送信し、部屋情報を取得及び更新することができます。Amazon RDS データベースは、VPC 内からのみアクセス可能です。テーブルを作成し、アイテムやリストアイテムを追加できます。下記は、hotel-app/app.js のソースコードです。

const my_tracer = require('./create-a-tracer');
const AWS = require('aws-sdk');

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var createRouter = require('./routes/create');
var addRouter = require('./routes/add');
var roomsRouter = require('./routes/rooms');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/create', createRouter);
app.use('/add', addRouter);
app.use('/rooms', roomsRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

アプリケーションの計装

OpenTelemetry を使用してアプリケーションを計装します。使用する ADOT SDK に応じて、ADOT は自動計装と手動計装をサポートします。手順については、Getting Started with the OpenTelemetry JavaScript SDK on Traces Instrumentation をご覧ください。今回は、手動計装を利用します。もし自動計装の動作が知りたい場合は、こちらのブログを参照ください。

X-Ray を通してアプリケーションをトレースするために、アプリケーションのメインディレクトリに下記の OpenTelemetry JavaScript パッケージをインストールする必要があります。

OpenTelemetry JavaScript package のインストール

{
  "name": "hotel-app",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "jade": "~1.11.0",
    "morgan": "~1.9.1",
    "mysql": "^2.18.1",
    "postcss": "^8.4.6",
    "postcss-cssnext": "^3.1.1",
    "postcss-nested": "^5.0.6",
    "@opentelemetry/api": "^1.0.3",
    "@opentelemetry/sdk-trace-node": "^0.25.0",
    "@opentelemetry/sdk-metrics-base": "^0.25.0",
    "@opentelemetry/exporter-collector-grpc": "^0.25.0",
    "@opentelemetry/instrumentation": "^0.25.0",
    "@opentelemetry/instrumentation-http": "^0.25.0",
    "aws-sdk": "^2.875.0",
    "@opentelemetry/id-generator-aws-xray": "^0.25.0",
    "@opentelemetry/propagator-aws-xray": "^0.25.0",
    "opentelemetry-instrumentation-aws-sdk": "^0.24.2",
    "@opentelemetry/instrumentation-mysql": "^0.23.0"
  }
}

トレースデータを X-Ray に送信

ADOT を通して X-Ray にトレースデータを送信するために、グローバルトレースプロバイダに X-Ray ID ジェネレータ、X-Ray プロパゲータ及び gRPC エクスポータを設定する必要があります。

hotel-app/create-a-tracer.js:

'use strict'

// OTel JS - API
const { trace } = require('@opentelemetry/api');

// OTel JS - Core
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
const { SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-base');

// OTel JS - Core - Exporters
const { CollectorTraceExporter } = require('@opentelemetry/exporter-collector-grpc');

// OTel JS - Core - Instrumentations
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const { MySQLInstrumentation } = require('@opentelemetry/instrumentation-mysql');
const { AwsInstrumentation } = require('opentelemetry-instrumentation-aws-sdk');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions')

// OTel JS - Contrib - AWS X-Ray
const { AWSXRayIdGenerator } = require('@opentelemetry/id-generator-aws-xray');
const { AWSXRayPropagator } = require('@opentelemetry/propagator-aws-xray');


const tracerProvider = new NodeTracerProvider({
  resource: Resource.default().merge(new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: "hotel-app"
  })),
  idGenerator: new AWSXRayIdGenerator(),
  instrumentations: [
    new HttpInstrumentation(),
    new MySQLInstrumentation(),
    new AwsInstrumentation({
      suppressInternalInstrumentation: true
    }),
  ]
});

// Expects Collector at env variable `OTEL_EXPORTER_OTLP_ENDPOINT`, otherwise, http://localhost:4317
tracerProvider.addSpanProcessor(new SimpleSpanProcessor(new CollectorTraceExporter()));

tracerProvider.register({
  propagator: new AWSXRayPropagator()
});

module.exports = trace.getTracer("hotel-app");

アプリケーションのメインコードにトレーサーを挿入します ( hotel-app/app.js )。

const my_tracer = require('./create-a-tracer');

Amazon RDS データベースへの接続設定

ステップ 3 を確認し、下記の hotel-app/rds.js の rdsUrl、password、user を自身のRDSデータベースのエンドポイント、パスワード、ユーザー情報に書き換えてください。

var mysql = require('mysql');

var rdsUrl = 'replace with endpoint to the RDS';
var password =  'replace with password to the RDS';
var user = 'replace with user to the RDS';

// mysql connection pool
var rdsPool = mysql.createPool({
    connectionLimit : 12,
    host: rdsUrl,
    password: password,
    user: user
});

module.exports.pool = rdsPool;
module.exports.url = rdsUrl;

ステップ 5: IAM ロールの設定

App Runner は、他の AWS サービスにアクセスできるように AWS Identity and Access Management (IAM) ロールを用います。この場合、App Runner はアプリケーションがトレーシングデータを X-Ray に送信するためにこのロールを利用します。

  1. IAM のコンソールに遷移します。
  2. IAM のナビゲーションペインで Role を選択し、その後 Create role を選択します。
  3. Select trusted entityCustom trust policy を選択します。 How App Runner works with IAM in the App Runner Developer Guide に従いロールの信頼ポリシーを入力してください。
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "tasks.apprunner.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
  1. Next を選択します。
  2. Add permissions では
    1. App Runner がトレーシングデータを X-Ray に送信するために AWS マネージドポリシーである AWSXRayDaemonWriteAccess を付与します。
    2. App Runner が AWS RDS と連携できるように AWS マネージドポリシーである AmazonRDSDataFullAccess を付与します。
  3. Next を選択します。
  4. ロール名では、hotel-app-instance-role を入力します。
  5. 設定項目を確認し、Create role を選択します。

設定したIAM Roleは次のスクリーンショットのようになります。

ステップ 6: VPC への接続と X-Ray によるトレーシングが可能な App Runner サービスの設定/構築

コードが計装可能になり、必要な IAM ロール及び VPC リソースを作成したので、App Runner サービスを作成し、VPC への接続と X-Ray によるトレーシングができるように設定します。

  1. App Runner コンソールに遷移します。
  2. App Runner Services に遷移します。
  3. Create service を選択します。
  4. リポジトリタイプでは、Source code repository を選択します。
  5. Connect to GitHub では、自身の GitHub リポジトリ及び今回利用するブランチを選択します。
  6. Deployment settings では、 Manual を選択します。オプションで、Automatic デプロイメントトリガーを選択すると、このブランチへソースコードをプッシュするたびに新しいバージョンのサービスがデプロイされます。
  7. Next を選択します。
  8. 次にビルド設定をします。今回はとてもシンプルなアプリケーションなので、ビルドコマンド及びアプリケーションコマンドを次のように設定します。
    1. Runtime: Nodejs 14
    2. Build command: npm install
    3. Start command: npm start
      より高度なユースケースの場合、このサンプルアプリケーションのように設定ファイルである apprunner.yaml をリポジトリ内に追加することができます。
  9. Next を選択します。
  10. Service settings のセクションでは、Service namehotel-appを入力します。
  11. Security のセクションでは、Instance role で先ほど作成した IAM ロール hotel-app-instance-role を選択します。
  12. AWS KMS key では、Use an AWS-owned key を選択します。
  13. Networking では、新しいオプションである Custom VPC を使用し、VPC connector を追加します。Add new VPC connector では、VPC connector namehotel-app-svc-vpc を入力します。新しい VPC connector を追加するために、ステップ 1,2 で作成したVPC、サブネット、 hotel-app-svc-sg セキュリティグループを選択します。
  14. 新しく導入された Observability セクションでは、 Tracing with AWS X-Ray を有効化します。
  15. 残りはデフォルト設定のままにしてください。
  16. Next を選択します。
  17. 全ての設定を確認し、Create & deploy を選択します。
  18. サービスダッシュボードページの Service overview セクションでサービスのステータスが確認できます。ステータスが Running に変わると、サービスは稼働し、リクエストを処理する準備が整います。

ステップ 7: アプリケーションの実行

数分後、サービスは実行され、Observability タブに Tracing On と表示されます。

Default domain 欄に記載の URL をクリックするとブラウザの新しいタブが開きます。アプリケーションが実行され VPC 内の Amazon RDS データベースに接続します。

アプリケーション利用者は

  • テーブルが存在しない場合、/create を選択してデータベース内にテーブルを作成できます ( アプリケーションを初めて使用する際には、最初にこのAPIを呼び出す必要があります。)
  • /add を選択してデータベースへ新しい部屋情報を挿入できます。
  • /rooms を選択して部屋情報を確認できます。

できましたー!!

ステップ 8: X-Ray でトレーシングデータの表示

  1. Observability タブで View service map を選択します。

CloudWatch のコンソールにリダイレクトます。HTTP リクエストと AWS SDK リクエストが計装されていることに気付くでしょう。下記の図は、アプリケーションから収集されたトレースデータを X-Ray のサービスマップで表示しています。

  1. View traces を選択します。
    CloudWatch の Traces ページが表示されます。

何回か API コールをしたとします。そのうちのいくつかで 4xx エラーが発生しました。4xx レスポンスの原因となったリクエストのグループを特定するために、X-Ray のトレースを確認し、HTTP ステータスコードでフィルタリングします。エラーの原因を調べるには、トレースリストで、HTTP メソッドによる不正なリクエストのグループに結果を絞り込みます。次に、各トレースセグメントの詳細を確認します。この情報は、問題のトラブルシューティングに役立ちます。

クリーンアップ

  • App Runner サービス hotel-app を削除します。
  • App Runner VPC connector hotel-app-svc-vpc を削除します。
  • IAM ロール hotel-app-instance-role を削除します。
  • Amazon RDS データベース hotel-app-datastore を削除します。
  • セキュリティグループ hotel-app-svc-sg 及び hotel-app-rds-sg を削除します。
  • VPC 内の Nat Gateway hotel-app-vpc を削除します。
  • hotel-app-vpc から Internet Gateway をデタッチします。
  • Internet Gateway を削除します。
  • VPC hotel-app-vpc を削除します。

もっと学ぶために

AWS App Runner の VPC サポート及び X-Ray サポートをより学ぶためのコンテンツを以下に掲載します。是非チェックしてみてください!

読んでくれてありがとう!
Follow me on Twitter @pymhq

翻訳はソリューションアーキテクト祖父江が担当しました。原文はこちらです。