実用的なALBアクセスログ用のAthenaDDL

リクエストURIの部分をHTTPメソッド、ホスト名、パス、QueryStringに分割しました。
Athenaの?の認識が怪しかったので、?ですむところをわざわざ[?]にしたりしています。

CREATE EXTERNAL TABLE IF NOT EXISTS table_name (
    type string,
    request_timestamp string,
    elb_name string,
    client_addr string,
    client_port int,
    target_addrport string,
    target_addr string,
    target_port int,
    request_processing_time double,
    target_processing_time double,
    response_processing_time double,
    elb_status_code int,
    target_status_code string,
    received_bytes int,
    sent_bytes int,
    request_method string,
    request_uri string,
    request_host string,
    request_path string,
    query_string string,
    request_proto string,
    user_agent string,
    ssl_cipher string,
    ssl_protocol string,
    target_group_arn string,
    trace_id string
)
PARTITIONED BY (ymd string)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
    'serialization.format' = '1',
    'input.regex' = '^(https?|h2|wss?) ([^ ]+) ([^ ]+) ([^:]+):([0-9]+) (([^:]+):([0-9]+)|-) ([0-9.]+|-1) ([0-9.]+|-1) ([0-9.]+|-1) ([0-9]+) ([0-9]+|-) ([0-9]+) ([0-9]+) "([^ ]+) ([^:]+://([^:]+):[0-9]+(/[^ ?]+)?[?]?([^ ]+)?) ([^ ]+)" "([^"]+)" ([^ ]+|-) ([^ ]+|-) ([^ ]+) (.+)$'
)
LOCATION 's3://[your-bucket]/[your-prefix]/AWSLogs/[your-account-id]/elasticloadbalancing/[your-region]/'
TBLPROPERTIES ('has_encrypted_data' = 'false')

続きを読む

awsrmを使ってALB配下のEC2インスタンスIDを取得する

ALBになってから ALB -> ( Listener ) -> Target group -> EC2インスタンス というつながりになりました。

「ALB配下のEC2インスタンスについてテストをしたい」というときに少し遠いです。

こういうときに awsrm を使うと直感的にEC2のインスタンスIDを取得できます。

require 'awsrm'

instance_ids = Awsrm::AlbTargetGroup.all(alb: 'my-alb-load-balancer-name').map do |target|
  target.instance_ids
end.flatten

これで、ALB配下の複数のTarget groupの下に紐付いているEC2インスタンスのIDを取得できます。

「ALBはTagで管理している」というときにも、

require 'awsrm'

alb_id = Awsrm::Alb.one(tags: {Role: 'web'}).id
instance_ids = Awsrm::AlbTargetGroup.all(alb: alb_id).map do |target|
  target.instance_ids
end.flatten

「複数のALB(VPC内のALB全て、など)」というときにも、

require 'awsrm'

instance_ids = Awsrm::Alb.all(vpc: 'my-vpc').map do |alb|
  Awsrm::AlbTargetGroup.all(alb: alb.id).map do |target|
    target.instance_ids
  end.flatten
end.flatten

と、直感的に記述できます。

インスタンスIDが取得できれば、awspecで「ALB配下のEC2インスタンスにスケジュールイベントがないこと」というテストを書くのも簡単です。

require 'awspec'
require 'awsrm'

instance_ids = Awsrm::AlbTargetGroup.all(alb: 'my-alb-load-balancer-name').map do |target|
  target.instance_ids
end.flatten

instance_ids.each do |id|
  describe ec2(id) do
    it { should be_running }
    it { should_not have_events }
  end
end

awsrmはawspecとは独立していますので、テスト以外にも様々な用途で活用できると思います。

もし、awsrmに実装して欲しいリソースがありましたら、PR/IssueやTwitterでのメンションなどよろしくお願いします。

続きを読む

ターゲットトラッキングポリシーを利用した、EC2のオートスケール設定を実施してみた

はじめに AWSチームのすずきです。 2017年7月のアップデートにより、「ターゲットトラッキングポリシー」を利用したEC2のオートスケール設定が可能になりました。 新機能 – EC2 Auto Scalingのターゲッ […] 続きを読む

ALB+WAF利用時のCloudWatchアラーム設定

概要

  • Classic ELB から ALB + WAF へ移行するに辺り、CloudWatch アラーム設定をどうしようか考えてみる(結局、結論は出ず。。)

bad behavior なリクエストが来た場合

※ 例えば、PHPMyAdminを狙った /PMA/

Classic ELB (WAFなし) のとき

  • CloudWatch HTTPCode_Backend_4XX は、作業ミスやコードの不具合・バグ検知用に入れたはずが、クローラー/愉快犯/画像泥棒/攻撃事前調査(Probe)などによるアラートの方が多い。
  • PHPMyAdmin を狙った攻撃事前調査系の bad behavior も、PHPMyAdmin なんて入れていないので HTTPCode_Backend_4XX (=404) アラートが飛ぶ (最近特に多い)。
  • そこで、AWS WAF を導入したら HTTPCode_Backend_4XX アラートが減るんじゃないかなと期待してみた(けど。。)

AWS ALB + AWS WAF

うーむ

とりあえず?

  • ALB CloudWatach HTTPCode_ELB_4XX_Count と WAF CloudWatch BlockedRequests を設定する。
  • で、
    ALB の CloudWatach HTTPCode_ELB_4XX_Count アラートが飛ぶ 

    WAF の CloudWatch BlockedRequests アラートが来なかったら、Kibanaなどで状況を確認する

メモ

AWS ALB のみの場合

構成

ALB —> httpd on EC2

bad behavior request が来た

http://ALB-DNS-Name/PMA/

① ALB が受ける
② ターゲットグループの httpd on EC2 が受ける

アクセスログ

① ALBアクセスログ

  • elb_status_code: 404
  • target_status_code: 404

② httpd on EC2 アクセスログ

  • GET /PMA/ HTTP/1.1″ 404

Amazon CloudWatch メトリクス

ALB

  • elb_status_code: 404 ⇒ HTTPCode_ELB_4XX_Count (統計: 最も有用な統計は Sum)
  • target_status_code: 404 ⇒ HTTPCode_Target_4XX_Count (統計: 最も有用な統計は Sum)

AWS ALB + AWS WAF の場合

構成

ALB —> httpd on EC2
+–> WAF (rule:bad-behaviorをブロック)

bad behavior request が来た

http://ALB-DNS-Name/PMA/

① ALB が受ける
② AWS WAF へ転送
③ AWS WAF が ルールに基いて検査
④ AWS WAF が ALB へリクエストのブロックを指示
⑤ ALB がブロックする (403 Forbidden)

アクセスログ

① ALBアクセスログ

  • elb_status_code: 403
  • target_status_code: –

② httpd on EC2 アクセスログ

  • なし

Amazon CloudWatch メトリクス

ALB

  • elb_status_code: 403 ⇒ HTTPCode_ELB_4XX_Count (統計: 最も有用な統計は Sum)

WAF

  • BlockedRequests (有効な統計: Sum)

続きを読む

TerraformでAWSの構成を読み取るぞ(Terraform import)

前書き

Terraform import については、Qiitaでお2人も記載してくれていますので、参照ください。

tyasuさん:terraform importの使い方メモ
kt_higaさん:terraform importを試してみた

同じスタンスで記載してもしょうがないので、勉強している最中に違う観点でいくつかTerraform import について記載します。

環境

Windows にTerraform(2017/7/18時点:v0.9.11)をインストール
AWSには、EC2,ELB,RDS,S3 など以前手作業で作った環境があります。

準備

まずは、最低限Terraformの利用には、access_key, secret_keyが必要です。
こちらを参照してキーを確認しましょう。
Windowsの作業フォルダにファイルを作成して、確認したキーを記載します。
※ ファイル名は、<任意の名前>.tf としてください。
ここでは、c:work フォルダを作業フォルダとします。

provider "aws" {                                            ・・・・決め打ちです。
    region      = "ap-northeast-1"                          ・・・・リージョンを記載(ap-notheast-1:日本リージョン)
    access_key  = "XXXXXXXXXXXXXXXXX"                       ・・・・access_keyを登録
    secret_key  = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY" ・・・・secret_keyを登録
}

※ ほとんどが決め打ちです。
   今回は、AWS環境のため"aws" となります。

インポート!

叩くコマンドは、簡単!

AWSのマネジメントコンソールから、インスタンスID(赤枠部分)を確認して、コマンドのオプションに指定します。
C:> cd work
C:work> terraform import aws_instance.<任意の名前> <インスタンスID>

題.png

ここでは、こんな感じでコマンドを叩いてみました。
もともとの環境では、タグ名を「batchserver01」という名前を付けていましたが、ここではわざと「batch01」に変更しています。
タグ名と合わせても勿論OKです。

import02.png

無事、terraform.tfstate ファイルが作成されました。

※ <> 部分は、修正しています。

{
    "version": 3,
    "terraform_version": "0.9.11",
    "serial": 0,
    "lineage": "XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {
                "aws_instance.batch01": {
                    "type": "aws_instance",
                    "depends_on": [],
                    "primary": {
                        "id": "<INSTANCE_ID>",
                        "attributes": {
                            "ami": "<AMI_ID>",
                            "associate_public_ip_address": "false",
                            "availability_zone": "<AvailabilityZone_ID>",
                            "disable_api_termination": "false",
                            "ebs_block_device.#": "0",
                            "ebs_optimized": "false",
                            "ephemeral_block_device.#": "0",
                            "iam_instance_profile": "",
                            "id": "<INSTANCE ID>",
                            "instance_state": "running",
                            "instance_type": "<INSTANCE_TYPE>",
                            "ipv6_addresses.#": "0",
                            "key_name": "<KEY_NAME>",
                            "monitoring": "false",
                            "network_interface.#": "0",
                            "network_interface_id": "<NW_INTERFACE_ID>",
                            "primary_network_interface_id": "<NW_INTERFACE_ID>",
                            "private_dns": "<DNS_NAME>",
                            "private_ip": "<PRIVATE_IP>",
                            "public_dns": "",
                            "public_ip": "",
                            "root_block_device.#": "1",
                            "root_block_device.0.delete_on_termination": "false",
                            "root_block_device.0.iops": "100",
                            "root_block_device.0.volume_size": "10",
                            "root_block_device.0.volume_type": "gp2",
                            "security_groups.#": "0",
                            "source_dest_check": "true",
                            "subnet_id": "<SUBNET_ID>",
                            "tags.%": "1",
                            "tags.Name": "batchserver01",
                            "tenancy": "default",
                            "volume_tags.%": "0",
                            "vpc_security_group_ids.#": "1",
                            "vpc_security_group_ids.2148762998": "<SECURITY_GROUP_ID>"
                        },
                        "meta": {
                            "schema_version": "1"
                        },
                        "tainted": false
                    },
                    "deposed": [],
                    "provider": "aws"
                }
            },
            "depends_on": []
        }
    ]
}

ここから本題

★ EC2を1台しかimportしていないので、もう1台importしたらどうなるんだ?

import01.png

別に問題なくコマンド終了。
先ほど作成されたterraform.tfstate は、terraform.tfstate.backup にCOPYされ、terraform.tfstate に2台目が追記されました。
(省略)

★ S3をimport しても大丈夫だよね?

C:work> terraform import aws_s3_bucket.<任意の名前> <バケット名>

わざとバケット名を間違えて見ました。

import03.png

★ ALBをimport !!

C:work> terraform import aws_alb.<任意の名前> <ARN>

import04.png

結果、terraform.tfstate ファイルにはどんどんimport すればしただけ、追記されていきました。
どんどん下に追記されていくわけではなく、DataSource(aws_s3_bucket, aws_instance, aws_albなど)の名前で順に並んでいくようです。

参考サイト

対応しているDataSource をはじめ、オプション確認にはやっぱりここは外せません。

本家HashiCorp:AWS Provider

続きを読む

AWSクラウド環境の構築からSpring Bootアプリのデプロイまで(初心者向け)

アプリケーションエンジニアとして、普段の仕事でインフラ設計をなかなか経験したことがないですが、AWSを通じてインフラ知識がほぼZeroの私でもインフラ基盤を気軽に構築できて感動しました。今回は、私と同じの初心者に向け、AWSクラウド環境の構築からSpring Bootアプリのデプロイまでの手順を共有します。

環境構成図

AWS構成について、東京リージョン内で2つのアベイラビリティゾーン(AZ)を使用した冗長構成を採用します。EC2インスタンスはその2つのAZに分散配置し、ALB(ロードバランサ)経由でアクセスを分散する構成とします。また、RDSインスタンスはEC2と同様に2つのAZに分散配置するようMulti-AZの構成とします。
この構成はAWSの無料利用枠を超えているため、料金がかかります!ご注意ください。
全体構成図.jpg

まず、VPCを構築します!

外部ネットワークからAWS内のインスタンスに到達できるように、AWS内の各インスタンスにIPアドレスが割り振られ、適切にルーティングされる必要があります。このような仮想ネットワークを提供しているサービスをAmazon Virtual Private Cloud(VPC)と言います。

VPCの構成について

練習とは言え、実戦に近いVPC環境を目指しています。今回のVPCは、インターネット通信用のパブリックサブネットとインターネットから遮断されるプライベートサブネットの2種類で構成されます。その2種類のサブネットへのアクセスを制御するために、それぞれに異なるセキュリティグループを適用します。APサーバーはパブリックサブネット上に構築し、DBサーバーはプライベートサブネット上に構築します。
VPC構成詳細.jpg

VPC作成

AWSアカウントを新規登録した後に、デフォルトのVPC環境が既に作られていましたが、今回はそれを利用せずに、一から以下のVPCを新規構築します。
vpc構成図.jpg

1.AWSマネジメントコンソールVPCをクリック⇒左のメニューからVPCを選択⇒VPCの作成ボタンを押下します。
2.VPCの作成画面に適当な名前を入力し、CIDRブロック欄にIPアドレス範囲に入力します。(今回は「10.0.0.0/16」を入力します。)
3.はい、作成するボタンを押下します。
VPC作成.jpg

サブネット作成

上記のVPCの中にサブネットを作成します。サブネットは複数のAZに跨って作成することはできないので、必ず1つのAZを指定して作成します。負荷分散と冗長化のために、APサーバー用サブネットとDBサーバー用サブネットをそれぞれ2つずつ構築します。
subnet構成.jpg

1.左のメニューからサブネットを選択⇒サブネットの作成ボタンを押下します。
2.サブネットの作成画面に、適当な名前入力し、上記作成されたVPCを選んでCIDRブロックを入力します。(Subnet1は「10.0.0.0/24」とします。)
subnet1作成.jpg
3.上記と同じの手順で、Subnet2、Subnet3、Subnet4を構築します。

Subnet AZ IPv4 CIDRブロック
public-subnet1 ap-northeast-1a 10.0.0.0/24
public-subnet2 ap-northeast-1c 10.0.1.0/24
private-subnet1 ap-northeast-1a 10.0.2.0/24
private-subnet2 ap-northeast-1c 10.0.3.0/24

完成後サブネット一覧画面
subnetList.jpg

インターネットゲートウェイ(IGW)とルートテーブルの作成

インターネットゲートウェイ(IGW)は、名前の通りにインターネットとの出入口であり、VPCと外部ネットワークの間で通信を行うために設置します。
また、上記作成されたサブネットがパブリックサブネットなのか、あるいはプライベートサブネットなのかは、そのサブネットに適用されているルートテーブルによって決まります。
送信先:0.0.0.0/0のターゲットとしてIGWが設定されているルートテーブルが適用されているサブネットはパブリックサブネットです。一方、送信先:0.0.0.0/0のターゲットとしてIGWが設定されていないルートテーブル(デフォルトのまま)が適用されているサブネットはプライベートサブネットです。
igw&rtb.jpg

1.左のメニューからインターネットゲートウェイを選択⇒インターネットゲートウェイの作成ボタンを押下します。
2.適当な名前を入力し、はい、作成するのボタンを押下します。
igw.jpg
3.VPCにアタッチのボタンを押下し、VPCとの紐付けを行います。
igw attache.jpg
4.左のメニューからルートテーブルを選択⇒ルートテーブルの作成ボタンを押下します。
5.パブリックサブネットであるSubnet1のルートテーブルを作成するために、適当な名前を入力し、VPCとの紐付けを行い、はい、作成するのボタンを押下します。
rtb1.jpg
6.上記と同様の手順で、public-rtb2を構築します。今回はプライベートサブネット用ルートテーブルを作成せずに、デフォルトのルートテーブルを使用します。
7.パブリックサブネット用のルートテーブルに、デフォルトゲートウェイ(送信先0.0.0.0/0)のターゲットにIGWを登録します。
route.jpg
ルートテーブル内の「10.0.0.0/16 local」というルート情報は、デフォルトの設定で変更・削除することができません。このデフォルト設定は、VPC内の通信はルートテーブルでは制御できないということで、同じVPC内のサブネットであればサブネット間の通信が可能なっていることを意味しています。

セキュリティグループの作成

セキュリティグループは、AWS内各インスタンスごとのファイアウォールで、受信(インバウンド)と送信(アウトバウンド)のアクセス制御ができます。各インスタンスには少なくとも1つのセキュリティグループを適用する必要があります。
VPC構成詳細.jpg
1.左のメニューからセキュリティグループを選択⇒セキュリティグループの作成ボタンを押下する。
2.APサーバー用セキュリティグループを作成するために、適当な名前を入力し、VPCへの紐付けを行い、はい、作成するのボタンを押下します。
sg作成.jpg
3.上記と同じの手順でDBサーバー用のセキュリティグループを作成します。
private sg.jpg
4.それぞれのセキュリティグループに受信(インバウンド)と送信(アウトバウンド)のルールを作成します。デフォルトでインバウンドは許可されているルールがないため、どこからのアクセスも受け付けません。一方、アウトバウンドは、デフォルトで全ての宛先/ポート番号に対するアクセスを許可するルールが設定されています。
外部からアクセスできように、APサーバー用セキュリティグループでSSHの22ポートとウェブアプリの8085ポートを開けておきます。
public inbound.jpg
一方、DBサーバー用セキュリティグループは、APサーバーからDBアクセスのみを許可するために、Auroraの3306ポートを開けておきます。
private sg rule.jpg

以上でVPCの構築が完了しました。

RDSインスタンスの構築

RDSは、リレーショナルデータベースのマネージャーサービスのことです。RDSで選択できるデータベースエンジンは、以下の6種類です。
・Amazon Aurora
・MySQL
・MariaDB
・PostgreSQL
・Oracle
・MS SQL Server
今回はAurora DBを構築します。Auroraは、MySQLと互換性のあるAWS独自のリレーショナルDBエンジンで、最大で MySQL の5倍のスループットおよび3倍の PostgreSQL スループットのパフォーマンスを持つと言われます。
RDS.jpg

サブネットグループの作成

DBインスタンス作成の前提条件として、VPC内でDBサブネットグループを指定する必要があります。
DB サブネットグループには、特定のリージョン内の少なくとも 2 つのアベイラビリティーゾーンにサブネットが必要です。 VPC に DB インスタンスを作成するときに、DB サブネットグループを選択する必要があります。Amazon RDS は、その DB サブネットグループと優先アベイラビリティーゾーンを使用し、サブネットとそのサブネット内の IP アドレスを選択して DB インスタンスに関連付けます。
1.AWSマネジメントコンソールRDSをクリック⇒左のメニューからサブネットグループを選択⇒サブネットグループの作成ボタンを押下します。
2.名前等を適当に入力し、2つのDB用サブネット(private subnet1、private subnet2)を追加し、作成ボタンを押下します。
db subnet group1.jpg

DBインスタンスの作成

1.左のメニューからインスタンスを選択⇒DBインスタンスの起動ボタンを押下します。
2.エンジンの選択画面にAmazon Auroraを選択します。
db1.jpg
3.DB詳細画面にて、DBインスタンスクラスなどを指定し、次のステップボタンを押下します。
※マルチAZ配置を選んだ場合、料金2倍かかります!
db2.jpg
4.VPC、DBサブネットグループ、優先AZ、およびセキュリティグループを指定します。
db3.jpg
5.設定完了後にDBインスタンスが作成中の状態であることを確認できます。
db4.jpg

EC2インスタンスの構築

やっと、EC2まで辿り着きました。Amazon Elastic Compute Cloud(EC2)は、AWSにおける仮想サーバのことです。今回負荷分散のために、2つのインスタンスを構築します。
EC2.jpg

EC2インスタンスの作成

1.AWSマネジメントコンソールEC2をクリック⇒左のメニューからインスタンスを選択⇒インスタンスの作成ボタンを押下します。
2.インスタンスの種類にAmazon Linuxを選択します。
ec21.jpg
3.インスタンスタイプ選択画面に、無料利用枠対象のタイプを選択します。
ec22.jpg
4.詳細設定画面に、VPCとサブネットを指定します。
ec23.jpg
5.ストレージを追加します。
ec24.jpg
6.APサーバー用セキュリティグループを指定します。
ec25.jpg
7.最後に、EC2にログインするためのキーペアをダウンロードし、インスタンスの作成ボタンを押下し、インスタンス作成が完了です。
ec26.jpg

ELASTIC IPの関連付け

上記のEC2インスタンスに対して、静的なパブリックIPアドレスを付与するために、ELASTIC IPの割り当てが必要です。
1.左のメニューからELASTIC IPを選択⇒新しいアドレスの割り当てボタンを押下します。
eip1.jpg
2.EC2インスタンスに関連付けを行います。
eip2.jpg

EC2環境の初期設定

Tera TermなどのSSHクライアントを使って上記のELASTIC IPを入力し、EC2インスタンスにアクセスします。
ec27.jpg
「ec2-user」ユーザーを使って、先ほどダウンロードしたキーでログインします。
ec28.jpg

tera term.jpg

無事にログインできたら、EC2の初期設定を行います。

# 最新のソフトウェアにアップデート
$ sudo yum update -y
# ホスト名変更
$ sudo hostname ec2-1-cinpo1
$ sudo vim /etc/sysconfig/network
HOSTNAME=ec2-cinpo1;
# ホストファイルを編集し、AWSから払い出された<Private IP>を書く。
$ echo "17X.XX.X.X30 ec2-cinpo1" |sudo tee -a /etc/hosts
# ホスト名確認
$ hostname -f
# タイムゾーン変更
# /etc/sysconfig/clockの編集
$ echo -e 'ZONE="Asia/Tokyo"nUTC=false' | sudo tee /etc/sysconfig/clock
# タイムゾーンファイルの変更
$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
# 結果確認
$ date
# Java 8のインストール
$ sudo yum install java-1.8.0-openjdk.x86_64
# Java 8の選択
$ sudo alternatives --config java
# 結果確認
$ java -version

2台目のEC2を構築します。

上記と同じの手順で、2台目のEC2インスタンスを構築します。

Aurora環境へのデータ移植

# Auroraサーバーに接続するために、MySQLクライアントをインストールします。
$ sudo yum install mysql
# Auroraサーバーに接続し、データベースの新規作成やデータの移植を行います。
$ mysql -h <RDSインスタンスのエンドポイント> -u username -p
$ create database sampleDB

...下記省略...

mysql.jpg

Spring Bootアプリのデプロイ

1.Tera TermのSSH SCP転送を利用し、Spring Bootアプリをローカルから上記2つのEC2インスタンスにアップロードします。
2.完了後に、従来通りにSpring Bootアプリを起動します。

$ java -jar XXXXXXXX.jar

boot.jpg

3.この時点で、ELASTIC IP:8085 にアクセスしてみれば、アプリ画面が表示されるはずです。

ロードバランサーの作成

最後に、ロードバランサー(Application Load Balancer [ALB])を適用し、APサーバーの負荷分散を実現します。
ALB.jpg

ターゲットグループの構築

ALB適用の前提条件として、EC2インスタンスをターゲットとしてターゲットグループに登録する必要があります。ALBは、クライアントにとって単一の通信先として機能し、登録済みターゲットに受信トラフィックを分散します。
1.AWSマネジメントコンソールEC2をクリック⇒左のメニューからターゲットグループを選択⇒ターゲットグループの作成ボタンを押下します。
ALB1.jpg
2.ターゲットグループ一覧画面にて選択済みのターゲットグループにターゲットの登録を行います。
ALB2.jpg
3.EC2インスタンスをターゲットとしてターゲットグループに登録します。
ALB3.jpg

ALBの構築

1.左のメニューからロードバランサーを選択⇒ロードバランサーの作成ボタンを押下し、Application Load Balancerを選択します。
ALB4.jpg
2.名前を適当に指定し、リスナーとサブネット等を指定します。
ALB5.jpg
3.セキュリティグループを指定します。
ALB6.jpg
4.先ほど作成されたターゲットグループを指定します。
ALB7.jpg
5.作成中のステータスとなり、1~2分後に利用可能となります。
ALB8.jpg

動作確認

以上、AWS環境の構築からアプリのデプロイまで完成しました。
http://:ポート/にアクセスすれば、アプリ画面が表示されたら完成です。
final1.jpg

続きを読む

Tomcat 8でBackupManagerを使ってユニキャスト通信でセッションレプリケーション

Tomcat 8でDeltaManagerを使ってユニキャスト通信でセッションレプリケーションするときのメモではDeltaManagerでのセッションレプリケーション方法を扱いましたが、こちらはBackupManagerでのセッションレプリケーションです。

結論から言うと、ユニキャスト通信でも、DeltaManagerと同様にBackupManagerでセッションレプリケーション可能です。

※ある意味時代遅れの方法なので、DeltaManagerの記事はそれほど読まれないかと思っていましたが、思っていたよりも読まれることが多いようなので(レガシーなアプリケーションのクラウド移行を進めている方が多いのでしょうか?)、BackupManagerについても取り上げることにしました。

DeltaManager用設定からの変更箇所

Tomcat 8でDeltaManagerを使ってユニキャスト通信でセッションレプリケーションするときのメモの設定のうち、server.xmlのManagerエレメント部分を書き換えます。

server.xml(Managerエレメント部分・変更前)
          <Manager className="org.apache.catalina.ha.session.DeltaManager"
                   expireSessionsOnShutdown="false"
                   notifyListenersOnReplication="true"/>
server.xml(Managerエレメント部分・変更後)
          <Manager className="org.apache.catalina.ha.session.BackupManager"
                   notifyListenersOnReplication="true"/>

BackupManagerにはexpireSessionsOnShutdown属性がないので削除します。
なお、DeltaManagerのexpireSessionsOnShutdown=”false”、両ManagerのnotifyListenersOnReplication=”true”はデフォルト値通りなので記述しなくても構いません。

他の部分は元のまま変更しなくてOKです。

DeltaManagerとBackupManagerの違い

詳細については、以下の書籍をご覧ください。

詳解 Tomcat(オライリー・ジャパン)
※5章 5.2.3でセッションレプリケーションについて解説されています。

DeltaManagerはクラスタ中の全ノード(サーバ)間でセッション情報(ID・オブジェクトなど)をAll-to-Allでレプリケートします。
クラスタ内のノードが2台の場合、例えば各ノードをAとBと表現するならば、

  • A→B
  • B→A

の2通りの通信しか行われませんが、ノードが3台(A・B・C)の場合、

  • A→B
  • A→C
  • B→A
  • B→C
  • C→A
  • C→B

の6通りに増えます。4台の場合は12通り、5台の場合は20通りです。
マルチキャスト通信を使う場合はまだいいのですが、ユニキャスト通信ではノードが多くなるとレプリケーション負荷が高くなることがイメージできると思います。

BackupManagerは、セッションの生成・変更・削除そのものについてはAll-to-Allで通信しますが、セッションに保存されたオブジェクトについては、

  • セッションの生成を行った(受け付けた)ノード(Primaryノード)
  • Primary以外から選ばれた1つのノード(Backupノード)

の2台の間で保持(レプリケート)するだけ、という形でレプリケーション負荷を抑えています。
これら以外のノード(Proxyノード)では、セッションIDと、当該セッションを保持するPrimary/Backupノードの情報のみを保持します。

ノード障害があった場合、

  • そのノードがPrimaryノードになっているセッションについて、Backupノードが一旦新たなPrimaryノードとなり、Proxyノードの中から新たなBackupノードが選出されます。
  • そのノードがBackupノードになっているセッションについて、Proxyノードの中から新たなBackupノードが選出されます。
  • 新たなPrimary/Backupノードが対象のセッションオブジェクトを保持していない場合は、保持しているノードからレプリケートします。

但し、Tomcatより前段(クライアント側)にあるロードバランサーが、必ずしも新Primaryノードにリクエストを振り分けてくれるとは限りません。ロードバランサーが違うノードにリクエストを振り分けた場合は、振り分けられたノードがPrimaryノード、(振り分けられたノードが新Backupノードであれば)それ以外のノードがBackupノードになるように再構成が行われます(上で「一旦」と表現したのはこのためです)。

なお、ノード障害ではなく、メンテナンスのためにロードバランサーで特定のノードをクラスタから除外することもあると思います。ノード障害が検出されていない状態でPrimaryノードではないノードにリクエストが振り分けられた場合にも、新たなPrimaryノード(=リクエストが振り分けられたノード)/Backupノードが選出されます。
ノードをシャットダウンした場合は、そのノードがPrimary/Backupとなっているセッションオブジェクトについて、新Primary/Backupノードを選出するとともにレプリケートします。

使用上の注意

BackupManagerの場合、平時にはDeltaManagerよりセッションレプリケーション負荷を抑えられますが、障害などでクラスタ内のノード構成が変化すると、そのタイミングでレプリケーションが行われるため、かえって(瞬間的な)負荷が高まる可能性があります。
また、セッションオブジェクトを2台のノードのみで保持するため、AWSのAZ障害時には(同じAZにある2台で保持していることが原因で)一部のセッションオブジェクトが失われる可能性があります。

それから、DeltaManager同様、ユニキャスト通信の場合はクラスタ設定(server.xml内)にノード情報をあらかじめ列記しておかないといけませんので、AWSのAuto Scalingのような仕組みとは相性が悪いです(1台ずつ個別にスケーリングを指定して「Time-based Scaling」として使うことはできると思いますが)。

ちなみに、これはTomcatやDeltaManager/BackupManagerに限った話ではないのですが、AWSのALBはDraining時の処理に問題を抱えているため(Auto Scalingの縮退時に問題になります)、セッションレプリケーションしないといけないような、信頼性を求められるWebアプリケーションとは相性が悪い点にも注意が必要です(CLBでは同じ問題は発生しないらしいですが)。

なお、Tomcatのサービスを停止する際、前述の通りそのノードがPrimary/Backupとなっているセッションオブジェクトについて、新Primary/Backupノードを選出するとともにレプリケートします。その関係で、クラスタ内に3台以上のノードが残る場合、シャットダウンに長い時間が掛かり、Linuxの起動スクリプトの処理がシャットダウンの途中でタイムアウトしてしまうことがあります。

続きを読む

Amazon API Gateway にカスタムドメインを設定する

概要

Amazon API Gateway に独自ドメインを設定しアクセス可能にします。
SSL証明書は AWS Certificate Manager を利用します。

前提条件

この記事の内容を実践する為に必要な前提条件です。

  • 公開可能済のAPI Gatewayが存在している事
  • 利用可能なドメインを取得している事
  • AWS Certificate ManagerでSSL証明書が準備されている事

前提条件1 API Gatewayの準備

API Gatewayの準備ですが、Serverless Framework を使うと簡単に用意出来るのでオススメです。
以前、 Serverless Frameworkのインストールと初期設定 という記事を書きましたので参考になると幸いです。

前提条件2 利用可能なドメインを取得する

Route 53でドメインを購入すると全てがAWSのサービスで管理出来る事になるのでオススメです。
ドメインの購入方法等は下記の記事に載せてありますので参考にして頂けると幸いです。

前提条件3 AWS Certificate ManagerでSSL証明書を準備する

手順に関しては難しくありません、 AWSのサービスでドメインを取得しALBでSSLで接続出来るようにする に取得方法を記載してあります。

1点だけ注意点があります。それは証明書を配置する region は必ず us-east-1 米国東部(バージニア北部) にする事です。

2017-06-15 現在の時点では AWS Certificate Manager の証明書を利用する為には、証明書が us-east-1 米国東部(バージニア北部) に配置されている必要があります。

API Gatewayにカスタムドメインの設定を行う

マネジメントコンソール → Amazon API Gateway → +カスタムドメイン名の作成 より設定を行います。

以下の情報を入力します。

  • ドメイン名: 任意のドメイン名を入力します
  • ACM 証明書: 作成した証明書を選択します
  • ベースマッピング: 作成したどのAPI Gatewayにマッピングさせるかを選択します

custom-domain1.png

「保存」を押すと作成が開始されます。(かなり時間がかかります)

しばらくすると下記のような形になります。
この「ディストリビューションドメイン名」という箇所をコピーしましょう。

custom-domain2.png

ディストリビューションドメイン名をRoute 53に設定する

DNSレコードの設定を行っていきます。

私の場合はAPI Gatewayに設定したドメインは 元々持っていたドメインのサブドメインとして設定を行いました。

その為、元々のHosted zoneにAレコードとして登録を行います。

Name: 設定したドメイン名を入力
Alias Target: 先程コピーしたディストリビューションドメイン名

custom-domain3.png

このあたりの方法に関しては AWSのサービスでドメインを取得しALBでSSLで接続出来るようにする でも方法を紹介しています。

動作確認

最後に動作確認を行います。

自動で割り当てられるURLで接続
curl -v https://i99999999i.execute-api.ap-northeast-1.amazonaws.com/development/other
設定したカスタムドメインで接続
curl -v https://api-gateway.hoge.org/other

両方で同じ内容が返ってくれば成功になります。

以上になります。最後まで読んで頂きありがとうございました。

続きを読む