クラスタリング付き Express アプリケーションを Elastic Beanstalk にデプロイする - AWS Elastic Beanstalk

クラスタリング付き Express アプリケーションを Elastic Beanstalk にデプロイする

このチュートリアルでは、Elastic Beanstalk コマンドラインインターフェイス (EB CLI) を使用してサンプルアプリケーションを Elastic Beanstalk にデプロイした後、Express フレームワーク、Amazon ElastiCache、およびクラスタリングを使用するようアプリケーションを更新する手順を示します。クラスタリングはウェブアプリケーションの高可用性、パフォーマンス、セキュリティを拡張します。Amazon ElastiCache の詳細については、『Amazon ElastiCache for Memcached ユーザーガイド』の「Amazon ElastiCache for Memcached とは」を参照してください。

注記

この例では、課金対象となる可能性のある AWS リソースを作成します。AWS 料金の詳細については、「https://aws.amazon.com/pricing/」を参照してください。一部のサービスは、AWS 無料利用枠の対象です。新規のお客様は、無料でこれらのサービスをテストできる場合があります。詳細については、「https://aws.amazon.com/free/」を参照してください。

前提条件

このチュートリアルでは、Node.js 言語とそのパッケージマネージャである npm、Express ウェブアプリケーションフレームワークが必要です。これらのコンポーネントをインストールし、ローカル開発環境を設定するための詳細については、「Node.js 開発環境をセットアップする」を参照してください。

注記

このチュートリアルでは、AWS SDK for Node.js をインストールする必要はありません。これは「Node.js 開発環境をセットアップする」でも説明されています。

また、このチュートリアルでは、Elastic Beanstalk コマンドラインインターフェイス (EB CLI) も必要です。EB CLI をインストールおよび設定する詳しい手順については、EB CLI のインストール および EB CLI の設定 を参照してください。

Elastic Beanstalk 環境の作成

アプリケーションの EB CLI リポジトリを設定し、Node.js プラットフォームを実行する Elastic Beanstalk 環境を作成します。

  1. eb init コマンドを使用してリポジトリを作成します。

    ~/node-express$ eb init --platform node.js --region us-east-2 Application node-express has been created.

    このコマンドは、.elasticbeanstalk という名前のフォルダに、アプリケーションの環境作成用の設定ファイルを作成し、現在のフォルダに基づいた名前で Elastic Beanstalk アプリケーションを作成します。

  2. eb create コマンドを使用して、サンプルアプリケーションを実行する環境を作成します。

    ~/node-express$ eb create --sample node-express-env

    このコマンドは、Node.js プラットフォームと以下のリソース用にデフォルト設定で負荷分散環境を作成します。

    • EC2 インスタンス – 選択したプラットフォームでウェブアプリケーションを実行するように設定された Amazon Elastic Compute Cloud (Amazon EC2) 仮想マシン。

      各プラットフォームは、それぞれ特定の言語バージョン、フレームワーク、ウェブコンテナ、またはそれらの組み合わせをサポートするための、特定のソフトウェア、設定ファイル、スクリプトを実行します。ほとんどのプラットフォームでは、Apache または nginx のいずれかをウェブアプリケーションの前にリバースプロキシとして配置します。そのプロキシがリクエストをアプリケーションに転送し、静的アセットを提供し、アクセスログとエラーログを生成します。

    • インスタンスセキュリティグループ – ポート 80 上のインバウンドトラフィックを許可するように設定された Amazon EC2 セキュリティグループ。このリソースでは、ロードバランサーからの HTTP トラフィックが、ウェブアプリケーションを実行している EC2 インスタンスに達することができます。デフォルトでは、トラフィックは他のポート上で許可されません。

    • ロードバランサー – アプリケーションを実行するインスタンスにリクエストを分散するように設定された Elastic Load Balancing ロードバランサー。ロードバランサーにより、インスタンスを直接インターネットに公開する必要もなくなります。

    • ロードバランサーセキュリティグループ – ポート 80 上のインバウンドトラフィックを許可するように設定された Amazon EC2 セキュリティグループ。このリソースでは、インターネットからの HTTP トラフィックが、ロードバランサーに達することができます。デフォルトでは、トラフィックは他のポート上で許可されません。

    • Auto Scaling グループ – インスタンスが終了されたか利用不可になった場合にそのインスタンスを置き換えるように設定された Auto Scaling グループ。

    • Amazon S3 バケット – Elastic Beanstalk の使用時に作成されるソースコード、ログ、その他のアーティファクトの保存場所。

    • Amazon CloudWatch アラーム – 環境内のインスタンスの負荷をモニタリングし、負荷が高すぎたり低すぎたりするとトリガーする 2 つの CloudWatch アラーム。アラームがトリガーされると、Auto Scaling グループはレスポンスとしてスケールアップまたはダウンを行います。

    • AWS CloudFormation スタック – Elastic Beanstalk はAWS CloudFormation を使用して環境内のリソースを起動し、設定の変更を伝達します。リソースは、AWS CloudFormation コンソールに表示できるテンプレートで定義されます。

    • ドメイン名 – ウェブアプリケーションまでのルートとなるドメイン名であり、subdomain.region.elasticbeanstalk.com の形式です。

  3. 環境の作成が完了したら、eb open コマンドを使用して、デフォルトのブラウザーでその環境の URL を開きます。

    ~/node-express$ eb open

アプリケーションの更新

Elastic Beanstalk 環境にサンプルアプリケーションを更新して、Express フレームワークを使用するようにします。

最終的なソースコードは、nodejs-example-express-elasticache.zip からダウンロードすることができます。

注記

前提条件となる開発環境の設定は、node-express フォルダーの Express プロジェクト構造にあります。Express プロジェクトを生成していない場合は、次のコマンドを実行します。詳細については、「Express のインストール」を参照してください。

~/node-express$ express && npm install

Express を使用するようアプリケーションを更新するには

  1. node-express/app.jsnode-express/express-app.js に名前変更します。

    node-express$ mv app.js express-app.js
  2. node-express/express-app.jsvar app = express(); 行を次に更新します。

    var app = module.exports = express();
  3. ローカルコンピュータで、以下のコードを含む node-express/app.js という名前のファイルを作成します。

    var cluster = require('cluster'), app = require('./express-app'); var workers = {}, count = require('os').cpus().length; function spawn(){ var worker = cluster.fork(); workers[worker.pid] = worker; return worker; } if (cluster.isMaster) { for (var i = 0; i < count; i++) { spawn(); } cluster.on('death', function(worker) { console.log('worker ' + worker.pid + ' died. spawning a new process...'); delete workers[worker.pid]; spawn(); }); } else { app.listen(process.env.PORT || 5000); }
  4. 更新したアプリケーションをデプロイします。

    node-express$ eb deploy
  5. 数分後、環境が更新されます。環境が緑色で示されていて準備完了したら、URL を再表示して正しく動作することを確認します。ウェブページに "Welcome to Express" が表示されます。

アプリケーションを実行している EC2 インスタンスのログにアクセスできます。ログのアクセス手順については、「Elastic Beanstalk 環境の Amazon EC2 インスタンスからのログの表示」を参照してください。

次に、Amazon ElastiCache を使用するよう Express アプリケーションを更新します。

Amazon ElastiCache を使用するよう Express アプリケーションを更新するには

  1. ローカルコンピュータで、ソースバンドルの最上位ディレクトリに .ebextensions ディレクトリを作成します。この例では、node-express/.ebextensions を使用します。

  2. 次のスニペットを使用して、設定ファイル node-express/.ebextensions/elasticache-iam-with-script.config を作成します。設定ファイルの詳細については、「Node.js 設定の名前空間」を参照してください。elasticache ノードの検出に必要な権限を持つ IAM ユーザーが作成され、キャッシュが変更されると常にファイルに書き込まれます。nodejs-example-express-elasticache.zip からファイルをコピーすることもできます。ElastiCache プロパティの詳細については、「例: ElastiCache」を参照してください。

    注記

    YAML は、一貫したインデントに依存します。設定ファイルの例でコンテンツを置き換える際はインデントレベルを一致させ、テキストエディタがインデントにタブ文字ではなくスペースを使用していることを確認します。

    Resources: MyCacheSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: GroupDescription: "Lock cache down to webserver access only" SecurityGroupIngress: - IpProtocol: tcp FromPort: Fn::GetOptionSetting: OptionName: CachePort DefaultValue: 11211 ToPort: Fn::GetOptionSetting: OptionName: CachePort DefaultValue: 11211 SourceSecurityGroupName: Ref: AWSEBSecurityGroup MyElastiCache: Type: 'AWS::ElastiCache::CacheCluster' Properties: CacheNodeType: Fn::GetOptionSetting: OptionName: CacheNodeType DefaultValue: cache.t2.micro NumCacheNodes: Fn::GetOptionSetting: OptionName: NumCacheNodes DefaultValue: 1 Engine: Fn::GetOptionSetting: OptionName: Engine DefaultValue: redis VpcSecurityGroupIds: - Fn::GetAtt: - MyCacheSecurityGroup - GroupId AWSEBAutoScalingGroup : Metadata : ElastiCacheConfig : CacheName : Ref : MyElastiCache CacheSize : Fn::GetOptionSetting: OptionName : NumCacheNodes DefaultValue: 1 WebServerUser : Type : AWS::IAM::User Properties : Path : "/" Policies: - PolicyName: root PolicyDocument : Statement : - Effect : Allow Action : - cloudformation:DescribeStackResource - cloudformation:ListStackResources - elasticache:DescribeCacheClusters Resource : "*" WebServerKeys : Type : AWS::IAM::AccessKey Properties : UserName : Ref: WebServerUser Outputs: WebsiteURL: Description: sample output only here to show inline string function parsing Value: | http://`{ "Fn::GetAtt" : [ "AWSEBLoadBalancer", "DNSName" ] }` MyElastiCacheName: Description: Name of the elasticache Value: Ref : MyElastiCache NumCacheNodes: Description: Number of cache nodes in MyElastiCache Value: Fn::GetOptionSetting: OptionName : NumCacheNodes DefaultValue: 1 files: "/etc/cfn/cfn-credentials" : content : | AWSAccessKeyId=`{ "Ref" : "WebServerKeys" }` AWSSecretKey=`{ "Fn::GetAtt" : ["WebServerKeys", "SecretAccessKey"] }` mode : "000400" owner : root group : root "/etc/cfn/get-cache-nodes" : content : | # Define environment variables for command line tools export AWS_ELASTICACHE_HOME="/home/ec2-user/elasticache/$(ls /home/ec2-user/elasticache/)" export AWS_CLOUDFORMATION_HOME=/opt/aws/apitools/cfn export PATH=$AWS_CLOUDFORMATION_HOME/bin:$AWS_ELASTICACHE_HOME/bin:$PATH export AWS_CREDENTIAL_FILE=/etc/cfn/cfn-credentials export JAVA_HOME=/usr/lib/jvm/jre # Grab the Cache node names and configure the PHP page aws cloudformation list-stack-resources --stack `{ "Ref" : "AWS::StackName" }` --region `{ "Ref" : "AWS::Region" }` --output text | grep MyElastiCache | awk '{print $4}' | xargs -I {} aws elasticache describe-cache-clusters --cache-cluster-id {} --region `{ "Ref" : "AWS::Region" }` --show-cache-node-info --output text | grep '^ENDPOINT' | awk '{print $2 ":" $3}' > `{ "Fn::GetOptionSetting" : { "OptionName" : "NodeListPath", "DefaultValue" : "/var/www/html/nodelist" } }` mode : "000500" owner : root group : root "/etc/cfn/hooks.d/cfn-cache-change.conf" : "content": | [cfn-cache-size-change] triggers=post.update path=Resources.AWSEBAutoScalingGroup.Metadata.ElastiCacheConfig action=/etc/cfn/get-cache-nodes runas=root sources : "/home/ec2-user/elasticache" : "https://elasticache-downloads.s3.amazonaws.com/AmazonElastiCacheCli-latest.zip" commands: make-elasticache-executable: command: chmod -R ugo+x /home/ec2-user/elasticache/*/bin/* packages : "yum" : "aws-apitools-cfn" : [] container_commands: initial_cache_nodes: command: /etc/cfn/get-cache-nodes
  3. ローカルコンピュータに次のスニペットを使用して設定ファイル node-express/.ebextensions/elasticache_settings.config を作成し、ElastiCache を設定します。

    option_settings: "aws:elasticbeanstalk:customoption": CacheNodeType: cache.t2.micro NumCacheNodes: 1 Engine: memcached NodeListPath: /var/nodelist
  4. ローカルコンピュータで、node-express/express-app.js を次のスニペットに置き換えます。このファイルでは、ディスクからノードリストを読み取り(/var/nodelist)、ノードが存在する場合は memcached をセッションストアとして使用するよう Express を設定します。ファイルは次のようになります。

    /** * Module dependencies. */ var express = require('express'), session = require('express-session'), bodyParser = require('body-parser'), methodOverride = require('method-override'), cookieParser = require('cookie-parser'), fs = require('fs'), filename = '/var/nodelist', app = module.exports = express(); var MemcachedStore = require('connect-memcached')(session); function setup(cacheNodes) { app.use(bodyParser.raw()); app.use(methodOverride()); if (cacheNodes) { app.use(cookieParser()); console.log('Using memcached store nodes:'); console.log(cacheNodes); app.use(session({ secret: 'your secret here', resave: false, saveUninitialized: false, store: new MemcachedStore({'hosts': cacheNodes}) })); } else { console.log('Not using memcached store.'); app.use(cookieParser('your secret here')); app.use(session()); } app.get('/', function(req, resp){ if (req.session.views) { req.session.views++ resp.setHeader('Content-Type', 'text/html') resp.write('Views: ' + req.session.views) resp.end() } else { req.session.views = 1 resp.end('Refresh the page!') } }); if (!module.parent) { console.log('Running express without cluster.'); app.listen(process.env.PORT || 5000); } } // Load elasticache configuration. fs.readFile(filename, 'UTF8', function(err, data) { if (err) throw err; var cacheNodes = []; if (data) { var lines = data.split('\n'); for (var i = 0 ; i < lines.length ; i++) { if (lines[i].length > 0) { cacheNodes.push(lines[i]); } } } setup(cacheNodes); });
  5. ローカルコンピュータで、node-express/package.json を更新してを 4 つの依存関係を追加します。

    { "name": "node-express", "version": "0.0.0", "private": true, "scripts": { "start": "node ./bin/www" }, "dependencies": { "cookie-parser": "*", "debug": "~2.6.9", "express": "~4.16.0", "http-errors": "~1.6.2", "jade": "~1.11.0", "morgan": "~1.9.0", "connect-memcached": "*", "express-session": "*", "body-parser": "*", "method-override": "*" } }
  6. 更新したアプリケーションをデプロイします。

    node-express$ eb deploy
  7. 数分後、環境が更新されます。環境が緑色で示され準備が完了したら、コードが機能することを確認します。

    1. Amazon CloudWatch コンソールで ElastiCache メトリクスを表示します。ElastiCache メトリックスを表示するには、左ペインで [Metrics (メトリクス)] を選択し、[CurrItems] を検索します。[ElastiCache> Cache Node Metrics] を選択したら、キャッシュノードを選択してキャッシュ内の項目数を表示します。

      注記

      アプリケーションのデプロイ先と同じリージョンを調べていることを確認してください。

      アプリケーション URL をコピーして別のウェブブラウザに貼り付け、ページを更新した場合、5 分後に CurrItem カウントが上がります。

    2. ログのスナップショットを取得します。ログの取得についての詳細は、「Elastic Beanstalk 環境の Amazon EC2 インスタンスからのログの表示」を参照してください。

    3. ログバンドルの /var/log/nodejs/nodejs.log ファイルを確認します。次のような結果が表示されます。

      Using memcached store nodes: [ 'aws-my-1oys9co8zt1uo.1iwtrn.0001.use1.cache.amazonaws.com:11211' ]

クリーンアップ

アプリケーションを実行したくない場合は、環境を終了し、アプリケーションを削除してクリーンアップできます。

環境を終了するには eb terminate コマンドを、アプリケーションを削除するには eb delete コマンドを使用します。

環境を終了するには

ローカルリポジトリを作成したディレクトリから、eb terminate を実行します。

$ eb terminate

このプロセスには数分かかることがあります。環境が正常に終了すると、Elastic Beanstalk にメッセージが表示されます。