Vapor Trail

明るく楽しく元気よく

手を動かしながら2週間で学ぶ AWS 基本から応用まで Day14

Day14 CloudFormationを用いた環境構築の自動化

CloudFormationについて

Infrastructure as Code

  • テンプレート
    AWSリソースの構成をYAML or JSONで記載したドキュメント

  • スタック
    テンプレートから自動構築されたAWSリソースの集合

1. シンプルなテンプレートでVPCを作る

一般的なテンプレートスニペット - AWS CloudFormation

template.yml

AWSTemplateFormatVersion: '2010-09-09'
Description: Test cfn Template
AWSTemplateFormatVersion: '2010-09-09'
Description: Test cfn Template

Resources:
  cfnVpc:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: '192.168.0.0/16'
      Tags:
        - Key: 'Name'
          Value: 'cfn-vpc'

CloudFormation→新しいスタックの作成
作成したテンプレートファイルをアップロード
作成

2. Ref関数を使ってSubnetを作る

サブネットで作成したVPCを参照するようにする

AWSTemplateFormatVersion: '2010-09-09'
Description: Test cfn Template

Resources:
  cfnVpc:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: '192.168.0.0/16'
      Tags:
        - Key: 'Name'
          Value: 'cfn-vpc'
  cfnSubnet:
    Type: 'AWS::EC2::Subnet'
    Properties:
      CidrBlock: '192.168.1.0/24'
      MapPublicIpOnLaunch: true
      Tags:
        - Key: 'Name'
          Value: 'cfn-subnet'
      VpcId: !Ref cfnVpc

スタック→アクション→スタックの更新

3. EC2インスタンスの雛形を作成する

EC2のパラメータを作成

Parameters:
  InstanceType:
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.micro
      - t2.small
      - t2.medium
    Description: Select EC2 instance type.

    InstanceType: !Ref InstanceType

EC2にキーペアを入れる

  KeyPair:
    Description: Select KeyPair Name.
    Type: AWS::EC2::KeyPair::KeyName

Resources:
  cfnEC2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      KeyName: !Ref KeyPair

リージョンによってAMIを選択する

Mappings:
  RegionMap:
    us-east-1:
      hvm: "ami-a4c7edb2"
    ap-northeast-1:
      hvm: "ami-3bd3c45c"

Resources:
  cfnEC2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      ImageId: !FindInMap [ RegionMap, !Ref "AWS::Region", hvm ]

CloudFormationベストプラクティス

  1. 組み込み関数を使って環境に依存しないテンプレートを目指す
    FindInMapなどの組み込み関数を使用してリージョンやアカウントによる変化に対応できるようなテンプレートを作成する。

  2. リソースの追加・変更は必ずCloudFormationで行う
    手動で追加・削除されるとテンプレートと環境の整合性が取れなくなる。

  3. テンプレートをバージョン管理する
    テンプレートを変更した際に、古いバージョンに戻すことでロールバックできる
  4. システムの規模やライフサイクルによってテンプレートを分割する
    VPCやEC2などの変更が多いものと、変更が少ないものなどで使い分ける

手を動かしながら2週間で学ぶ AWS 基本から応用まで Day12 Day13

Day12 Eメール送受信サービスSESとキューイングサービスSQS

Amazon SES

  1. ドメインを登録する
  2. Route53に紐付ける
  3. メールアドレスを事前登録する
  4. マネージメントコンソールから送信
  5. SDK for PHPでメッセージを送信する

1. ドメインを登録する

Amazon SES→Domains→Verify a New Domain

2. Route 53に紐づけする

Domain:ドメイン名を入力 Generate DKIM Settingsにチェック

Route 53に登録しているドメインなので、Use Route 53→Create Record Sets Route 53のドメインのホストゾーンにレコードが追加される

3. メールアドレスを事前登録する

Amazon SES→Email Addresses→Verify a New Email Address
上限緩和申請をしないと、事前に登録したメールアドレスにしかメールを送信できない。

4. マネージメントコンソールから送信

Domains→Send Test Email

事前登録していないアドレスに送ろうとすると、

Email address is not verified. The following identities failed the check in region US-EAST-1

と出る。

5. SDK for PHPでメッセージを送信する

batchインスタンスに接続

<?php
require '../vendor/autoload.php';

use Aws\Ses\SesClient;

// バージニア北部リージョン以外を利用している場合は、region を変更すること
$ses = SesClient::factory(array(
  'version'=> 'latest',
  'region' => 'us-east-1',
));

try {
  $result = $ses->sendEmail([
    // TODO: 送信元メールアドレスの入力
    'Source' => '***@***.***',
    'Destination' => [
      'ToAddresses' => [
        // TODO: 送信先メールアドレスの入力
        '***@***.***',
      ],
    ],
    'Message' => [
      'Subject' => [
        'Charset' => 'UTF-8',
        'Data' => 'Hello SES World!!',
      ],
      'Body' => [
        'Text' => [
          'Charset' => 'UTF-8',
          'Data' => 'AWS SDK for PHP を使った SES 送信テストです。',
        ],
      ],
    ],
  ]);

  $messageId = $result->get('MessageId');
  echo("Email sent! Message ID: $messageId"."\n");

} catch (SesException $error) {
  echo("The email was not sent. Error message: ".$error->getAwsErrorMessage()."\n");
}

> php SendEmail.php
Email sent! Message ID: xxxxxx-xxxxxx-xxxx-xxxxx

[ec2-user@udemy-aws-14days-batch src]$ php SendEmail.php
PHP Fatal error:  Uncaught exception 'Aws\Ses\Exception\SesException' with message 'Error executing "SendEmail" on "https://email.us-east-1.amazonaws.com"; AWS HTTP error: Client error: `POST https://email.us-east-1.amazonaws.com` resulted in a `403 Forbidden` response:
<ErrorResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
  <Error>
    <Type>Sender</Type>
    <Code>AccessDenie (truncated...)
 AccessDenied (client): User ' is not authorized to perform ses:SendEmail' on resource `arn:aws:ses:us-east-1:303167122504:identity/' - <ErrorResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
  <Error>
    <Type>Sender</Type>
    <Code>AccessDenied</Code>
    <Message>User ' is not authorized to perform ses:SendEmail' on resource `'</Message>
  </Error>
  <RequestId>4aa5cba6-21d9-11e9-96 in /home/ec2-user/vendor/aws/aws-sdk-php/src/WrappedHttpHandler.php on line 191

アクセス権限のエラーが出る場合は、Batchに割り当てられているIAMロールにAmazon SESのアクセス権を追加して、ポリシーを一度デタッチして、もう1度アタッチするとメールが送れるようになる。

バウンス処理について

SNSと組み合わせることでメールが正しく送信できたかを取得することができる、

もしメールが送信できなかった場合、該当するメールアドレスを削除することができる

  • バウンスの例

DBから送信先を取得→メール送信→メール送信に失敗→トリガ→DBから送信先のメールアドレスを削除

Amazon SQS

処理の間にQueueを噛ませることで、非同期処理にできる。

1度に複数件のメッセージを登録/取得できる
Long Polling: キューが空の場合はタイム・アウトするまで待ち続ける
Dead Letter Queue: いつまでも残っているメッセージを別のキューに移動する
Delay Queue(Message Timer): 新しいメッセージを指定した時間見えなくする

注意点:
順序は保証されない
同じメッセージを複数回受信する可能性がある(標準キューとFIFOキュー)
aws.amazon.com

1. キューを作成する

Amazon SQS→新しいキューの作成 デフォルトで標準キューで作成する

キューの操作→メッセージの送信 利用可能なメッセージの数が増えている。

キューの操作→メッセージの表示/削除 メッセージのポーリングを開始で受信できる

2. マネジメントコンソールからメッセージを送受信する

メッセージの送信

<?php
require '../vendor/autoload.php';

use Aws\Sqs\SqsClient;
use Aws\Exception\AwsException;

date_default_timezone_set('Asia/Tokyo');

$client = new SqsClient([
    'region' => 'ap-northeast-1',
    'version' => '2012-11-05'
]);

$params = [
    'MessageAttributes' => [
        "Sender" => [
            'DataType' => "String",
            'StringValue' => "AmazonSQS test"
        ]
    ],
    'MessageBody' => date('YmdHis'),
    //TODO: Queue の URL を記載する
    'QueueUrl' => '***'
];

try {
    $result = $client->sendMessage($params);
    var_dump($result);
} catch (AwsException $e) {
    error_log($e->getMessage());
}

作成したキューのURLを入れる。

> php SendQueue.php

object(Aws\Result)#142 (2) {
  ["data":"Aws\Result":private]=>
  array(4) {
    ["MD5OfMessageBody"]=>
    string(32) "xxxx"
    ["MD5OfMessageAttributes"]=>
    string(32) "xxxx"
    ["MessageId"]=>
    string(36) "xxxx"
    ["@metadata"]=>
    array(4) {
      ["statusCode"]=>
      int(200)
      ["effectiveUri"]=>
      string(62) "https://sqs.ap-northeast-1.amazonaws.com/xxxx/キュー名"
      ["headers"]=>
      array(4) {
        ["x-amzn-requestid"]=>
        string(36) "xxxx"
        ["date"]=>
        string(29) "Sun, 27 Jan 2019 06:51:48 GMT"
        ["content-type"]=>
        string(8) "text/xml"
        ["content-length"]=>
        string(3) "459"
      }
      ["transferStats"]=>
      array(1) {
        ["http"]=>
        array(1) {
          [0]=>
          array(0) {
          }
        }
      }
    }
  }
  ["monitoringEvents":"Aws\Result":private]=>
  array(0) {
  }
}

メッセージが送信される。

メッセージの受信

<?php
require '../vendor/autoload.php';

use Aws\Sqs\SqsClient;
use Aws\Exception\AwsException;

//TODO: Queue の URL を記載する
$queueUrl = "****";

$client = new SqsClient([
    'region' => 'ap-northeast-1',
    'version' => '2012-11-05'
]);

try {
    $result = $client->receiveMessage(array(
        'AttributeNames' => ['SentTimestamp'],
        'MaxNumberOfMessages' => 1,
        'MessageAttributeNames' => ['All'],
        'QueueUrl' => $queueUrl, // REQUIRED
        'WaitTimeSeconds' => 0,
    ));
    if (count($result->get('Messages')) > 0) {
        var_dump($result->get('Messages')[0]);
        $result = $client->deleteMessage([
            'QueueUrl' => $queueUrl, // REQUIRED
            'ReceiptHandle' => $result->get('Messages')[0]['ReceiptHandle'] // REQUIRED
        ]);
    } else {
        echo "No messages in queue. \n";
    }
} catch (AwsException $e) {
    // output error message if fails
    error_log($e->getMessage());
}
> php ReceiveQueue.php

array(7) {
  ["MessageId"]=>
  string(36) "xxxx"
  ["ReceiptHandle"]=>
  string(412) "xxxx"
  ["MD5OfBody"]=>
  string(32) "xxxx"
  ["Body"]=>
  string(14) "20190127155425"
  ["Attributes"]=>
  array(1) {
    ["SentTimestamp"]=>
    string(13) "1548572065941"
  }
  ["MD5OfMessageAttributes"]=>
  string(32) "xxxx"
  ["MessageAttributes"]=>
  array(1) {
    ["Sender"]=>
    array(2) {
      ["StringValue"]=>
      string(20) "Amazon SQS Send Test"
      ["DataType"]=>
      string(6) "String"
    }
  }
}

Day13 アプリケーション開発を支援するCodeシリーズ

f:id:kyamashiro:20190127222141p:plain

CodeCommit

Gitリポジトリをホストする

> mkdir work
> cp -r udemy-14days-aws/Day13/* work/

IAMユーザの権限を使ってgitの設定をするので一応、aws configureで権限を確認する。

gitの設定

git config --global credential.helper '!aws --region ap-northeast-1 codecommit credential-helper $@'
git config --global credential.UseHttpPath true

git config --global user.email "user_email"
git config --global user.name "user_name"

git clone

git init
git remote add origin 作成したリポジトリのURL
// ステージに追加
git add -A
// コミット
git commit -m "first commit"
// プッシュ
git push origin master

CodeBuild

CodeBuild上で行う作業については、buildspec.ymlに定義する

1. Build後のファイルを格納するためのS3を作成する

バケットを作成、バージョニングを有効にする

2. buildspec.ymlの編集

アプリケーション名・作ったS3のバケット名を入力。

version: 0.1

phases:
  build:
    commands:
      - aws deploy push --region ap-northeast-1 --application-name application-name --s3-location s3://S3-bucket-name/sample.zip --source src
artifacts:
  files:
    - '**/*'
  base-directory: src
  name: sample.zip

3. CodeBuildの作成

CodeBuild→ビルドプロジェクトを作成する プロジェクト名: buildspec.ymlで設定したアプリケーション名

ビルドプロジェクトを作成する

  • CodeBuildで作成したIAMロールの設定
    ポリシーをアタッチする
    AWSCodeDeployDeployerAccess
    AmazonS3FullAccess

CodeDeploy

  • EC2インスタンス用のIAMロールを作成する ロールの作成→EC2
    S3のアクセス権を設定
    Web-1a, Web-1cに作成したDeploy用のロールを割り当て

  • 各EC2インスタンスにCodeDeployエージェントをインストール

wget https://aws-codedeploy-ap-northeast-1.s3.amazonaws.com/latest/install
chmod +x install
sudo ./install auto

CodeDeploy→アプリケーションの作成 アプリケーション名: buildspec.ymlのアプリケーション名 コンピューティングプラットフォーム: EC2/オンプレミス

  • デプロイタイプの設定 アプリケーション→デプロイグループの作成 デプロイタイプ:インプレース

  • 環境設定 EC2インスタンス Web-1aとWeb-1cを登録

  • ロードバランサー ロードバランシングを有効にするにチェック 片方のEC2インスタンスがデプロイ中は切り離して、終わるともう片方のインスタンスをデプロイするという処理にできる

実際にビルドしてデプロイする

batchインスタンスに接続

cd work
git status
modified buildspec.yml
git add -A
git commit -m 'update buildspec.yml'
git push origin master
  • ビルド CodeBuild→ビルド→ビルドプロジェクト→ビルドの開始

  • デプロイ CodeDeploy→デプロイ→アプリケーション→デプロイの作成 デプロイグループ・リビジョンの場所を選択→デプロイ

デプロイ→イベント

BlockTraffic 5 分 45 秒

EC2のターゲットグループの登録解除の遅延を300秒から短くすると、デプロイにかかる時間が短くなる

CodePipeline

パイプラインの作成

  • パイプライン設定の選択
    サービスロール:新しいサービスロール
    アーティファクトストア:カスタムの場所 デプロイ用のバケットを選択

  • ソースステージの追加
    ソースプロバイダ:AWS CodeCommit
    変更検出オプション: AWS CodePipeline

  • ビルドステージの追加
    ビルドプロバイダ: AWS CodeBuild
    プロジェクト名を選択:ビルドプロジェクト名を選択

  • デプロイステージの追加
    デプロイプロバイダ: AWS CodeDeploy
    アプリケーション名:
    デプロイグループの選択

パイプラインの作成
f:id:kyamashiro:20190127222146p:plain git pushすると自動でデプロイされるようになる!

手を動かしながら2週間で学ぶ AWS 基本から応用まで Day10 Day11

Day10 CLIによるAWSの操作とシステム監視

マネージメントコンソールからの操作をターミナルでできる

> aws --v
aws-cli/1.16.96 Python/2.7.15 Windows/10 botocore/1.12.86

batchインスタンスを起動

> cd .aws
> more config

[default]
output = json
region = ap-northrast-1

IAM→ユーザ
アクセスキー シークレットキーを控えて入力する

> aws configure

AWS Access Key ID : xxx
AWS Secret Access Key : xxx
Default region name : ap-northeast-1
Default output format : json

Windows PowerShellでコマンドを叩くと、S3のバケット名を取得できるが変なエラーが出る

> aws s3 ls
C:\Program Files\Amazon\AWSCLI\.\dateutil\parser\_parser.py:1175: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal

dev.classmethod.jp

Set-Item env:tz -Value jst

でUnicodeWarningが出なくなる。

他の権限で操作したいとき、--profileオプションを付けて、切り替える

> aws configure --profile 設定名
> aws s3 ls --profile 設定名
  • コマンドリファレンス

aws — AWS CLI 1.16.96 Command Reference

S3バケットを作って、ファイルをアップロードする

// make bucket
// aws-cli-cmd-testというバケット名で作成
> aws s3 mb s3://aws-cli-cmd-test aws s3 ls

> aws s3 cp ファイル名 s3://aws-cli-cmd-test

CloudWatchについて

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

AWSリソースの状況を監視する

メトリクス AWSが標準で提供するメトリクス Disk,Network,CPU,Statusなど メトリクスを独自にカスタムもできる

CloudWatchの設定

  1. メトリクスとしきい値を設定
  2. SNSの通知先を設定
    EC2 ⇔ CloudWatch → SNS → 通知

ClowdWatch→アラームの作成

  • メトリクスの選択
    メトリクスの選択→EC2
    CPUUtilizationを選択

    割り当てられた EC2 コンピュートユニットのうち、現在インスタンス上で使用されているものの比率。このメトリクスは、選択されたインスタンス上でアプリケーションを実行するのに必要な処理能力を表します。
    パーセンタイル統計を使用するには、詳細モニタリングを有効にする必要があります。
    インスタンスタイプによっては、インスタンスがフルプロセッサコアに割り当てられていない場合に、オペレーティングシステムのツールが CloudWatch よりも低い比率を示す場合があります。
    単位: パーセント
    https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html

  • アラームの定義
    名前:BatchCPUMoreThan10percent
    説明:BatchCPUMoreThan10percent
    CPU使用率が10%を超えたときにアラームを出すように設定

一旦アラームの作成

  • 通知先の設定
    アクション→変更→通知
    +新しいリスト
    通知の送信先:CloudWatchAlert
    メールリスト:通知先のメールアドレス

Batchインスタンスにログインして、負荷を上げる

yes > /dev/null &

設定値を超えるとアラームが出る

Day11 コンテンツキャッシュとインメモリキャッシュ

CloudFrontについて

CDN(Contents Delivery Network)を配置して、CDNからのキャッシュを利用することでサーバの負荷を下げる。 世界中に100を超えるエッジロケーションが存在する。

  • CloudFrontの導入
    ELBの前段に配置する
  • オリジンサーバの指定
  • キャッシュのふるまいの設定
  • キャッシュTTLの設定
  • その他の設定

CloudFront→Create Distribution→Web Get Started

  • Origin Settings
    Origin Domain Name:ELBのDNS
    基本的にデフォルト

  • Default Cache Behavior Settings

  • Object Caching: Customize
    Minimum TTL:20
    Maximum TTL:20
    Default TTL:20

TTL(Time to live)=コンテンツがエッジキャッシュに保持される期間の管理 (有効期限)
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/Expiration.html

作成まで10分ほどかかる。

IDをクリック、Domain Name:xxx.cloudfront.net
にアクセスするとページが見れる。

xxx.cloudfront.netにアクセスしても変わっていないが、20秒後にアクセスすると変更が適用されている。
20秒毎にTTLを見に行くので、データが更新されるまで最大20秒、時間がかかる。

Curlコマンドでのキャッシュを確認する

curl -v http://xxx.cloudfront.net/  > /dev/null

< Age: 18
< X-Cache: Hit from cloudfront

CloudFrontを使う際のポイント

  1. キャッシュTTLの設計
    短すぎるとキャッシュしている意味が薄れてしまうし、長すぎるとコンテンツのアップロードが更新されない。バランスを考慮する必要がある。リアルタイム性が高い場合は、デプロイ時にキャッシュをクリアするなどのフローを考える。
  2. CloudWatchで監視する
    CloudFrontのメトリクスがある。

    CloudWatch を使用した CloudFront アクティビティの監視 https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/monitoring-using-cloudwatch.html

デフォルトでは、オリジンから HTTP 4xx または 5xx ステータスコードが返されると、CloudFront はこれらのエラーレスポンスを 5 分間キャッシュします。 https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/custom-error-pages-expiration.html

  1. Popular Object Reportをもとに設定を定期的に見直す キャッシュのヒット率などをもとにTTLを見直す

ElasticCacheについて

CDNのデータベース版。
頻繁にアクセスするデータをデータベースではなくてインメモリキャッシュに格納することでデータベースの負荷を軽減する設計パターン。

ElasticCacheの導入 プライベートサブネットに配置 1. サブネットグループの作成 2. パラメータグループの作成 3. ノードタイプの設定 4. レプリケーションの設定 5. セキュリティグループの設定

今回はRedisを使う ElasticCache→パラメータグループ default.Redisはパラメータグループを変更できないので、パラメータグループの作成を行う。

  • パラメータグループの作成
    ファミリー:Redis 4.0

  • サブネットグループの作成 アベイラビリティーゾーン:ap-northeast-1a
    サブネットid:private-1a
    追加

  • セキュリティグループの作成 EC2→セキュリティグループの作成 インバウンド
    カスタムTCP ポート:6379 ソース:batch-sg,web-sgからの接続を許可する 作成

ElasticCache→Redis→作成 クラスターエンジン:Redis エンジンバージョンの互換性:4.0.10 パラメータグループ:先程作ったパラメータグループ ノード:t2.micro レプリケーション数:0

サブネットグループ:先程作ったサブネットグループ 優先アベイラビリティーゾーン:指定なし

セキュリティグループ:先程作ったセキュリティグループを追加

> sudo yum install -y redis --enablerepo=epel
// redisのプライマリエンドポイント
> redis-cli -h redis.xxx.xxx.apne1.cache.amazonaws.com

ElasticCacheを使う際のポイント

  1. キャッシュするデータの選定
    頻繁に更新するようなデータはキャッシュヒットしにくいので意味がない。更新は少ないけど、頻繁に参照されるものは効果が高い。

  2. 耐障害性への考慮
    マルチノードで構築する。RDSと同じようにマルチAZ構成になるようにする。単一AZの障害で止まらないようにする。

  3. 負荷テストを実施して、ノードのタイプを決定する