AWS ALBはWebDAVを通してくれるのか?

長いので先に結論

結論を先に書くと、ALBはWebDAVを通してくれます。よかったですね。

素朴な疑問

AWSの新しいロードバランサ、Application Load Balancer(ALB)がリリースされました1
プロトコル面ではWebSocketやHTTP/2のサポートなどが目新しいところですけども、果たしてWebDAVは通るんだろうか?
と云うわけで調べてみたものの、意外にも類例が見つからなかったので試してみました。

やってみた

テスト構成

今回は以下の構成でテストを行いました。

構成図

セキュリティグループ、ターゲットグループ、ACM(SSL)の設定は割愛します。

EC2インスタンスの設定

下記構成のEC2インスタンスを2つ作成し、各サブネットに1つずつ配置しました。

EC2インスタンスのOSはAmazon Linux2を使用しました。
インストールしたパッケージはhttpd24のみです。
http24の設定ファイルには手を加えず、/etc/httpd/conf.d/webdav.confファイルを作成し下記内容を追加しています。

webdav.conf
DavLockDB /tmp/DavLock

Alias /dav/ /var/www/dav/

<Directory "/var/www/dav/">
    Dav on

    Options None
    AllowOverride None

    AuthType Basic
    AuthName WebDAVoverALBTest
    AuthUserFile /var/www/.htpasswd

    <RequireAll>
        Require valid-user
    </RequireAll>
</Directory>

WebDAVのローカルディレクトリは/var/www/dav/、HTTP上のディレクトリは/dav/になるよう設定しています。また/healthcheckでヘルスチェックできるようにしています。

ALBの設定

次にELBの作成に移ります。
まずはロードバランサタイプの選択から。もちろんここはALBを選びます。
ロードバランサタイプの選択

ロードバランサの設定は前述の構成通りに行います。
ステップ1

SSL証明書の設定。ここでは事前にACMで作成したものを使用しました。
ステップ2

セキュリティグループの設定。HTTPSが開いているだけのものを事前に作成してあります。
ステップ3

ルーティングの設定。
ステップ4

ターゲットの登録。EC2インスタンスは事前にターゲットグループへ登録済みです。
ステップ5

作成の確認。
ステップ6

ALBを作成しましたので、続いて「ルールの表示/編集」に移ります。
ロードバランサ一覧

ルールを追加し、/dev/fuga/へのアクセスは2つ目のターゲットグループへフォワードするよう設定します。
ルールの編集
ルール保存完了

手元の環境では、上記の設定でWebDAVの読み書きが2つのEC2インスタンスに振り分けられることを確認しました。

あとがき

今どきWebDAVを採用する機会は少ないでしょうし、ましてや同一ホスト名で複数サーバに振り分ける用途がどれだけあるかは分かりませんが、参考になれば幸いです。

注釈


  1. 「新しい」と云っても、すでに発表から10ヶ月(本稿執筆時点)経ちましたが‥‥‥ 

  2. 本稿執筆時点では2017.03 

続きを読む

AWS ECS × New RelicによるコンテナのAPM監視

はじめに

『 ECS(AWSのコンテナマネジメントサービス)で起動したコンテナ上のアプリケーションをNew Relicで監視する 』をゴールとして、実施手順について解説したいと思います。ECSとALBの連携により、コンテナ起動に伴う各AWSサービスへの諸々の設定(サーバプールへの追加、ポートの払い出しetc..)を自動で行えるよう設定していきます。また、コンテナそれぞれにNew Relic Agentを手動でインストールするのは手間なので、起動時に自動でインストール実行できるよう、Dockerに仕込みたいと思います。

1. コンテナの作成

①. Dockerfileの作成

はじめにDockerfileを作成します。今回用意したDockerfileでは、起動時に下記2点を実行します。
1. HTTPサービスの起動及び監視対象PHPファイルの追加
2. New Relic APM Agentをインストールまで行う

Dockerfile
### CentOS Imageのダウンロード
FROM centos:centos6
### httpdのインストール、監視対象PHPファイルのアップロード##
RUN yum -y install httpd php
ADD phpinfo.php /var/www/html
### New Relic APM Agentのインストールおよびライセンスキーの設定
RUN rpm -Uvh http://yum.newrelic.com/pub/newrelic/el5/x86_64/newrelic-repo-5-3.noarch.rpm
RUN yum -y install newrelic-php5
RUN newrelic-install install
RUN sed -E -i 's/REPLACE_WITH_REAL_KEY/"Your New Relic APM License Key"/' /etc/php.d/newrelic.ini
### httpdの起動
CMD ["/usr/sbin/httpd", "-DFOREGROUND"]

②. コンテナイメージのビルド

次にコンテナイメージをビルドします。

# docker build -t nr-test-rp1 .
# docker images

コンテナを起動し、ビルドしたイメージが正常に動作することを確認します。

# docker run -d -p 8080:80 nr-test-rp1
# docker exec -it nr-test-rp1 bash

2. ALBの設定

AWS側でALBの設定を行います。
ALBと3で解説するECSの連携により、Auto Port Mapping機能が使用できます。
同機能により、ALB及びECSに対する下記設定を動的に行うことができます。

  • EC2ホストインスタンスの空きポートを動的にコンテナポートにマッピング
  • ECSが起動したコンテナをALBの負荷分散ターゲットとしてグルーピング
  • ALBに対してヘルスチェック/リスナー/ターゲットグループを自動設定

ALBの構成要素は下記のとおりです。それぞれ設定していきます。

  • リスナー
  • ターゲットグループ
  • ヘルスチェック

サービス > EC2 > ロードバランサーを選択し、『ロードバランサーの作成』を押下します。

SnapCrab_NoName_2017-4-27_15-53-34_No-00.png "ELB作成"

Application Load Balancerのラジオボタンを選択し、『次へ』を押下します。

SnapCrab_NoName_2017-4-27_15-54-44_No-00.png "ALB設定"

ステップ1: ロードバランサーの設定にて、下記の項目を設定します。

  • 名前
  • スキーマ
  • IPアドレスタイプ
  • リスナー
  • アベイラビリティゾーン

SnapCrab_NoName_2017-4-27_16-1-8_No-00.png "ALB設定"

ステップ2: セキュリティ設定の構成ではそのままページ下部の『次の手順』を押下します。

ステップ3: セキュリティグループの設定を行います。同ステップでは、ALB用のセキュリティグループを作成するため、HTTPの使用ポートである80番ポートを送信元:any(0.0.0.0/0)として設定します。(送信元については、今回テスト用としてany設定しておりますが、環境に適した任意の送信元を指定ください。)設定後、ページ下部の『次の手順』を押下します。
SnapCrab_NoName_2017-4-27_16-41-3_No-00.png "ALB設定"

SnapCrab_NoName_2017-4-27_16-4-7_No-00.png "ALB設定"

ステップ4: ルーティングの設定を行います。ターゲットグループ及びヘルスチェックの設定を行います。設定後、ページ下部の『次の手順』を押下します。

SnapCrab_NoName_2017-4-27_16-22-1_No-00.png "ALB設定"

ステップ5: ターゲットの登録ではターゲットインスタンスの登録を行わなず、そのままページ下部の『次の手順』を押下します。

ステップ6: 確認で設定内容を確認し、問題なければページ下部の『作成』を押下します。

これでALBの作成は完了です。

3. ECSの設定

ECSの構成要素は下記のとおりです。

  • ECRリポジトリ…コンテナイメージをアップロードするリポジトリ
  • クラスタ…コンテナのホストEC2インスタンスの指定&クラスタリング設定を行う
  • タスク定義…クラスタ上で起動するコンテナ=タスクに対して、リソース分配や使用ポートなどの起動設定を行う定義
  • サービス…サービス単位でコンテナを複数台起動することが可能、ALB/AutoScalingの設定が可能

①. リポジトリの設定 & Docker Imageのアップロード

はじめに、ローカルで作成したコンテナイメージをAWS上にアップロードするため、ECSリポジトリを作成します。
サービス > EC2 Container Service > リポジトリ を選択し、『リポジトリの作成』を押下します。

SnapCrab_NoName_2017-4-27_16-41-3_No-00.png "ECS-リポジトリ作成"

ステップ1: リポジトリの設定にて、リポジトリ名を設定し、ページ下部の『次のステップ』を押下します。

SnapCrab_NoName_2017-4-27_16-42-22_No-00.png "ECS-リポジトリ設定"

ステップ2: Docker イメージの構築、タグ付け、プッシュにて、ローカルから作成したECRリポジトリへのログイン、コンテナイメージのプッシュに関するコマンドが表示されるので、同表示に従い、コマンドを実行します。

SnapCrab_NoName_2017-4-27_16-43-33_No-00.png "ECS-リポジトリ設定"

コマンド実行後、レポジトリにイメージが追加されていることを確認します。

SnapCrab_NoName_2017-5-23_13-18-20_No-00.png "ECS-リポジトリ設定"

②. クラスタの設定

コンテナを搭載するホストEC2インスタンスの指定&クラスタリング設定を行います。

サービス > ECS > クラスターを選択し、『クラスターの作成』を押下します。

SnapCrab_NoName_2017-5-23_13-28-0_No-00.png "ECS-クラスタ設定"

クラスターの作成にて、下記の項目を設定し、『作成』を押下します。

  • クラスター名
  • EC2インスタンスタイプ
  • インスタンス数
  • リスナー
  • キーペア(ホストEC2にSSHアクセスしたい場合に設定)

  • VPC(ALBに設定したものと同じVPCを指定)

  • サブネット(同上)

  • セキュリティグループ(新規)

  • セキュリティグループのインバウンドルール(CIDR:0.0.0.0/0 , ポート:80)

  • IAMロール(ecsInstaceRoleポリシーが付与された既存のロール or 新しいロールの作成を選択。新しいロールの作成を選択した場合、ロールが自動作成される)

SnapCrab_NoName_2017-5-23_13-37-22_No-00.png  "ECS-クラスタ設定"
SnapCrab_NoName_2017-5-23_13-45-57_No-00.png  "ECS-クラスタ設定"

作成後、下記の通りクラスタが追加されていることを確認します。

SnapCrab_NoName_2017-5-23_13-53-41_No-00.png  "ECS-クラスタ設定"

動的ポートマッピング使用時には、ALB間の通信において、EC2ホストに割当られるポートレンジを全て許可しておく必要があるため、クラスタ配下のEC2ホストに設定されたセキュリティグループを編集します。

サービス > EC2 > インスタンスにて当該EC2ホストを選択、『説明』タブのセキュリティグループのリンクをクリックします。

当該セキュリティグループの『インバウンド』タブにある『編集』を押下し、ソースを2で設定したALBのセキュリティグループとした全ポート許可のルールを追加します。

SnapCrab_NoName_2017-5-23_15-2-55_No-00.png "ECS-クラスタ設定"

②. タスク定義の作成

コンテナの起動設定を定義するため、タスク定義を作成します。

サービス > ECS > タスク定義を選択し、『新しいタスクの定義』を押下します。

SnapCrab_NoName_2017-5-23_14-18-40_No-00.png "ECS-タスク定義"

タスク定義名を入力し、『コンテナの追加』を押下します。

コンテナの追加画面にて、下記の項目を設定し、『追加』を押下します。

  • コンテナ名
  • イメージ
  • メモリ制限 (割り当てるメモリ量)
  • ポートマッピング (Auto Port Mapping利用時はホストのポート番号を0に設定)

SnapCrab_NoName_2017-5-23_14-30-2_No-00.png "ECS-タスク定義"

コンテナの追加後、『作成』を押下します。

SnapCrab_NoName_2017-5-23_14-35-36_No-00.png "ECS-タスク定義"

③. サービスの作成

はじめにECSサービス用のIAMロールを作成します。
サービス > IAM > ロールを選択し、『新しいロールの作成』を押下します。
ロールタイプでAmazon EC2 Container Service Roleを選択します。

SnapCrab_NoName_2017-5-25_13-55-18_No-00.png "ECS-IAMロール作成"

ポリシーもそのままAmazonEC2ContainerServiceRoleをアタッチします。

SnapCrab_NoName_2017-5-25_13-56-29_No-00.png "ECS-IAMロール作成"

ロール名を入力し、『ロールの作成』を押下します。

SnapCrab_NoName_2017-5-25_14-0-0_No-00.png "ECS-IAMロール作成"

次にECSにてサービスを作成していきます。
サービス > ECS > クラスターを選択し、『サービス』タブの『作成』を押下します。

SnapCrab_NoName_2017-5-23_15-12-31_No-00![SnapCrab_NoName_2017-5-25_13-55-18_No-00.png "ECS-サービス作成"](https://qiita-image-store.s3.amazonaws.com/0/150903/745045c2-e48c-92eb-994b-4520d9cf97c3.png)<br>
.png

サービスの作成画面にて、下記の項目を設定し、『追加』を押下します。

  • タスク定義(3-②で作成したタスク定義を指定)
  • サービス名
  • タスクの数(起動するコンテナ数を指定)

SnapCrab_NoName_2017-5-23_15-16-50_No-00.png "ECS-サービス作成"

次に画面下部に移動し、『ELBの設定』を押下します。

SnapCrab_N![SnapCrab_NoName_2017-5-23_15-12-31_No-00.png "ECS-サービス作成"](https://qiita-image-store.s3.amazonaws.com/0/150903/3d7e0f3f-09db-993e-d1b5-d8e3b1de9e56.png)<br>
oName_2017-5-23_15-26-41_No-00.png "ECS-サービス作成"

下記項目を設定します。

  • ELBタイプ(Application Load Balancerを選択)
  • サービス用のIAMロール(作成したECSサービス用のIAMロールを指定)
  • ELB名(2で作成したALBを指定)

負荷分散用のコンテナという項目では、3-②で作成したコンテナを選択し、『ELBの追加』を押下します。

下記項目を設定し、『保存』を押下します。

  • リスナーポート(80:HTTPを指定)
  • ターゲットグループ(2で作成したターゲットグループを指定)

SnapCrab_NoName_2017-5-23_16-3-1_No-00.png "ECS-サービス作成"

作成したサービス画面でタスクが3台『Running』ステータスで起動していることを確認します。

SnapCrab_NoName_2017-5-23_16-9-10_No-00.png "ECS-サービス作成"

4. 挙動確認

①. アクセス確認

サービス > EC2 > ロードバランサ―の『説明』タブにてALBのDNSを確認します。
ブラウザにhttp://”ALBのDNS”/phpinfo.phpを入力し、アクセスできることを確認します。

②. New Relicからの監視確認

NewRelicのAPM画面でコンテナ上で起動するアプリケーションが追加されたことを確認します。

SnapCrab_NoName_2017-5-23_16-18-14_No-00.png  "New Relic画面"

当該アプリケーションを選択し、『Overview』画面下部に3台ホストが表示されることを確認します。

SnapCrab_NoName_2017-5-23_16-15-50_No-00.png

続きを読む

AWSのサービスでドメインを取得しALBでSSLで接続出来るようにする

概要

表題の通りですが、AWSのサービスのみで独自ドメインの取得を行いALBで公開を行います。
なお公開するWebサービスはSSLで接続可能にしますので、AWS Certificate Manager で証明書の取得も行います。

前提条件

ALB(Application Load Balancer) が作成されている必要があります。

※以前、ALB(Application Load Balancer)でWebサービスを冗長化する という記事を書きました。参考になれば幸いです。

ドメインの登録を行う

Route53のドメイン登録画面に遷移する

マネジメントコンソールからRoute53サービスの “Domain registration” に進みます。

domain-step1.png

“Register Domain”を選択します。

domain-step2.png

取得しようと思っているドメイン名が利用可能かどうかをチェックする

希望のドメイン名を入力し、”Check”を選択します。
トップドメインによって必要な登録料が変わってきます。
今回は検証用なので比較的易い.netを選択しました。

domain-step3.png

“Add to cart” を押して次の手続きに進みます。

必要事項を入力

必要事項を入力します。

domain-step4.png

デフォルトでチェックが入っていますが、”Hide contact information if the TLD registry, and the registrar, allow it” にチェックが入っているか確認を行います。

※ここにチェックが入っていると、whoisでドメインの管理情報が照会された時に、登録者であるAmazonの情報が表示されプライバシーを保護できます。

domain-step5.png

確認画面

“I have read and agree to the AWS Domain Name Registration Agreement” にチェックを入れて “Complete Purchase” をクリックします。

以上でドメインの申請は完了となります。

domain-step7.png

登録完了後しばらくすると、ドメイン登録時に入力したメールアドレスに「Verify your email address or your domain smsc-nsc.net will be suspended」 というタイトルのメールがAWSから届きます。

リンク内のURLをクリックしメールアドレスの検証を完了させましょう。

サブドメインの設定を行う

私が公開作成しようと思っているサービスは複数のサブドメインから成り立つ構成なので、サブドメインの登録を行い既に起動しているALBに対し任意のサブドメインを割り当てます。

Route53の「Hosted zones」を選択し先程登録したドメインを選択します。

subdomain-step1.png

「Create Record Set」を選択し新規レコードを作成します。

subdomain-step2.png

入力情報
Name: 登録したいサブドメイン名
Type: A - IPv4 addressを選択
Alias: Yesを選択
Alias Target: ALB(Application Load Balancer) を選択

subdomain-step3.png

「Create」を選択してから、しばらく待つと登録したサブドメインでALB(Application Load Balancer)にアクセス出来る事が確認出来るかと思います。

Certificate Managerで証明書を作成する

無料のSSL証明書を取得します。
証明書の更新が自動で料金も無料と魅力的ですが、いくつか制限があるので注意が必要です。
今回、私が利用する条件的には十分だったので問題はありませんでしたが、これらの条件のうちどれかが必須要件だった場合は別の手段で証明書を取得する必要があります。

  • AWS上の一部のサービスでしか使えない

  • EV SSL証明書としては利用出来ない

証明書の作成リクエストを送る

マネジメントコンソールから東京リージョンのCertificate Managerサービスにアクセスします。

CertificateManager-step1.png

「今すぐ始める」から証明書のリクエストを開始します。
今回は複数のサブドメインで運用を行うのでワイルドカード証明書を取得します。

*.hoge.net のように取得したドメインを入力します。

CertificateManager-step2.png

CertificateManager-step3.png

「確定とリクエスト」をクリックすると、ドメイン登録時に登録したメールアドレス宛に 「Certificate approval for Requestしたドメイン名」というタイトルのメールが送信されてきますので本文内のリンクから手続きに進みます。

CertificateManager-step5.png

“I Approve” をクリックすると手続きは完了となります。

CertificateManager-step5.png

CertificateManager-step6.png

ALB(Application Load Balancer)に作成した証明書を適応する

マネジメントコンソールからEC2サービス → ロードバランサーから証明書を適応するロードバランサーを選択します。

alb-step1.png

「リスナーの追加」から以下の内容を入力します。

プロトコル: HTTPS(セキュア HTTP)
デフォルトターゲットグループ: 作成済のターゲットグループ
AWS 証明書マネージャ (ACM) から、既存の証明書を選択する

alb-step2.png

しばらくすると対象のロードバランサーにHTTPSでアクセス出来る事が確認出来るハズです。

ちなみに私の場合はHTTPSアクセスが確認出来次第、HTTPのリスナーは削除するようにしました。

最後に

AWSのサービスを利用すると非常に簡単にSSLでWebサイトの公開が可能です。

EV SSLが必須の場合この方法は使えませんが、ちょっとしたサービスを作るのであれば良い選択肢だと思います。

最後まで読んで頂きありがとうございました。

続きを読む

ALB(Application Load Balancer)でWebサービスを冗長化する

概要

ALBを使ってアプリケーションを冗長化する手順です。

HTTPS接続でアプリケーションにアクセス出来るところまでをこの記事で紹介します。

前提条件

以下の事前条件が必要です。

  • VPCの作成を行っておく
  • 最低でも2台のWebサーバインスタンスを起動させておく事
  • ロードバランサー用サブネットの作成が行われている事(後で説明します。)

事前準備その1(ロードバランサー用サブネットの作成)

以下は公式サイトに書かれている内容です。

ロードバランサーのアベイラビリティーゾーンを指定します。ロードバランサーは、これらのアベイラビリティーゾーンにのみトラフィックをルーティングします。アベイラビリティーゾーンごとに 1 つだけサブネットを指定できます。ロードバランサーの可用性を高めるには、2 つ以上のアベイラビリティーゾーンからサブネットを指定する必要があります。

今回検証で利用している東京リージョンには ap-northeast-1aap-northeast-1c の2つのアベイラビリティーゾーンが存在するので、それぞれでサブネットの作成を行います。

サービス → VPC → サブネット → 「サブネットの作成」より作成を行います。

ap-northeast-1a で サブネットを作成します。
以下のように入力を行います。

  • ネームタグ

    • account_api_alb_1a
    • 開発環境アカウント用APIのALB用と分かる名前を付けています。分かりやすい名前であれば何でも構いません。
  • VPC

    • 利用対象となるVPCを選択します。
  • IPv4 CIRD block

    • 192.0.30.0/24
    • ネットワークの設計方針にもよりますが今回は 192.0.30.0/24 を割り当てます。

alb_subnet_step1.png

続いて ap-northeast-1c でも同じ要領でサブネットを作成します。
※先程とほとんど同じなので、入力内容に関しての詳細は省略します。

alb_subnet_step2.png

事前準備その2(SSLの証明書の用意)

SSLで接続を可能にするのでSSL証明書の用意が必要です。

今回は検証なので自己証明書を利用する事にします。

以前、LAMP 環境構築 PHP 7 MySQL 5.7(前編) という記事を書きました。

こちらに載っている手順を参考に自己証明書を用意します。

ALB(Application Load Balancer)の新規作成

ここからが本題になります。
サービス → EC2 → ロードバランサー → ロードバランサーの作成 を選択します。

alb_step1.png

Step1 ロードバランサーの設定

基本的な設定を行っていきます。
名前を入力します。(今回はaccount-api-alb)という名前を付けました。

インターネットに公開するサービスを想定しているので、スキーマは「インターネット向け」を選択します。

ロードバランサーのプロトコルにHTTPSを追加します。

alb_step2-1.png

アベイラビリティーゾーンに先程作成したサブネットを割り当てます。

alb_step2-2.png

Step2 セキュリティ設定の構成

SSL証明書の設定を行います。

alb_step2-3.png

証明書の名前は分かりやすい名前でOKです。

プライベートキーには事前準備で作成した、プライベートキーを入れます。
-----BEGIN RSA PRIVATE KEY----- から -----END RSA PRIVATE KEY----- までを全てコピーして下さい。

パブリックキー証明書には -----BEGIN CERTIFICATE----- から -----END CERTIFICATE----- までの内容を全てコピーして下さい。

セキュリティポリシーは ELBSecurityPolicy-2016-08 を選択します。

※2017-05-22 現在、この手順で問題なく証明書の追加が出来るハズなのですが Certificate not found というエラーが発生しロードバランサーの作成に失敗してしまいます。

証明書のアップロードを aws-cli を使って事前に実施するようにしたら上手く行きました。

証明書のアップロード
aws iam upload-server-certificate --server-certificate-name self-certificate --certificate-body file://crt.crt --private-key file://private.key

file:// を付けるのがポイントです。これがないと上手くアップロード出来ませんでした。

--server-certificate-name には任意の名前を入力して下さい。

上手く行くと下記のようなレスポンスが返ってきます。

証明書アップロードのレスポンス
{
    "ServerCertificateMetadata": {
        "ServerCertificateId": "XXXXXXXXXXXXXXXXXXXXX",
        "ServerCertificateName": "self-certificate",
        "Expiration": "2018-05-22T04:14:02Z",
        "Path": "/",
        "Arn": "arn:aws:iam::999999999999:server-certificate/self-certificate",
        "UploadDate": "2017-05-22T05:58:44.754Z"
    }
}

アップロード完了後に「AWS Identity and Access Management(IAM)から、既存の証明書を選択する」を選んで先程アップロードした証明書を選択して下さい。

alb_step2-3.1.png

この問題については 既存の ELB に SSL 証明書を追加しようとすると Server Certificate not found for the key というエラーになる件の解決方法 を参考にさせて頂きました。

Step3 セキュリティグループの設定

セキュリティグループの設定を行います。

alb_step2-4.png

Step4 ルーティングの設定

ターゲットグループの新規作成を行います。

alb_step2-5.png

名前、プロトコル、ヘルスチェック用のURLの設定等を行います。

Step5 ターゲットの登録

ロードバランサーの配下で起動するインスタンスを選択します。

alb_step2-6.png

作成に必要な情報入力は以上となります。

確認画面に進み作成を行いしばらくすると、ロードバランサーが作成され利用可能な状態となります。

※サービス → EC2 → ロードバランサー より確認が出来ます。

alb_step3.png

動作確認

サービス → EC2 → ロードバランサー よりDNSが確認出来るので、動作確認を行います。

curl -kv https://account-api-alb-000000000.ap-northeast-1.elb.amazonaws.com/
*   Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to account-api-alb-000000000.ap-northeast-1.elb.amazonaws.com (0.0.0.0) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: system
> GET / HTTP/1.1
> Host: account-api-alb-000000000.ap-northeast-1.elb.amazonaws.com
> User-Agent: curl/7.51.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Date: Mon, 22 May 2017 07:26:02 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< Server: nginx/1.12.0
< X-Request-Id: 76c7e41f-1a4e-4328-972c-b98055e84395
< Cache-Control: no-cache, private
<
* Curl_http_done: called premature == 0
* Connection #0 to host account-api-alb-000000000.ap-northeast-1.elb.amazonaws.com left intact
{"code":404,"message":"Not Found"}

各Webサーバのログを確認すると、処理が振り分けられているのが、確認出来ます。

本番環境での運用に向けて

ここまで簡単に作成が出来ましたが実環境で運用を行うにはまだまだ考慮が必要な点が多いです。

  • SSL証明書を正式な物にする(自己証明書で運用とかはさすがに厳しいと思います)
  • 独自ドメインでのアクセスを可能にする
  • 各EC2のログに記載されているIPがロードバランサーの物になっている

※これらの手順は順次行っていく予定ですので、準備が出来次第記事を書く予定です。

最後まで読んで頂きありがとうございました。

続きを読む

IPv6でアクセスすると"via IPv6"って出るやつ

IPv6でアクセスすると”via IPv6″って出る例のやつ作りました。
(HTMLタグ貼るだけのやつが見つからなかったので)

表示してみる

IPv6から繋ぐと
Screen Shot 2017-05-22 at 3.19.22.png
が表示されます。

IPv4から繋ぐと
Screen Shot 2017-05-22 at 3.19.41.png
が表示されます。

使い方

<span id="kibousoft-viav6"></span>
<script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://viav6.kibousoft.co.jp/', true);
xhr.onreadystatechange = function(){
if (xhr.readyState === 4 && xhr.status === 200){
   var dom = document.getElementById('kibousoft-viav6');
   dom.innerHTML = xhr.responseText;
 }
};
xhr.send(null);
</script>

ソースコード

汚いですが直書きです。大したことしてない。

index.php
<a href="https://github.com/kibousoft/viav6_web/" style="text-decoration: none; color: white;">
<?php
$ip = $_SERVER['REMOTE_ADDR'];
$headers = apache_request_headers();
if ($headers['X-Forwarded-For']) {
    $ip = $headers['X-Forwarded-For'];
}

if (preg_match('/^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/', $ip)) {
    echo '<div style="background: linear-gradient(#FF0000, #FF99CC); padding: 5px; border: 1px solid #333333; border-radius: 3px; font-size: 13px; width: 50px; text-align: center; font-family: sans-serif;">via IPv4</div>';
} else {
    echo '<div style="background: linear-gradient(#0000FF, #99CCFF); padding: 5px; border: 1px solid #333333; border-radius: 3px; font-size: 13px; width: 50px; text-align: center; font-family: sans-serif;">via IPv6</div>';
}
?>
</a>

CORSの話

外部からXHRで取得される可能性のあるサイトでは、
Access-Control-Allow-Origin , Access-Control-Allow-Methods ヘッダーを返す必要があります。
.htaccessで以下を設定しました。

.htaccess
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET"

インフラの話

最初Amazon API Gatewayでやろうとしたんですが、API GatewayはIPv6対応していませんでした。
なので、OpsWorksでPHP App Serverを立てて動かしています。
OpsWorksにも以下の問題がありました。

  • Application Load Balancer(IPv6対応)には対応していない
  • EC2へのIPv6アドレスのアタッチには対応していない
  • セキュリティグループでIPv6のTCP 80番が許可されていない

そのため、上記の設定は手動で行いました。

備考

  • Happy Eyeballsの関係で、サイトにはIPv4で繋がって、XHRはIPv6で繋がるケースもあるよねとか細かい話はなしで。

続きを読む

Amazon ECSを用いたDocker本番運用の実現

はじめに

現在お手伝いしているアカウンティング・サース・ジャパンにて、ECSを使ったDockerの本番運用を始めたので、その一連の流れについてまとめました。

税理士向け会計システムを扱うアカウンティング・サース・ジャパンでは最近Scalaでの新規プロジェクトが立ち上がってきており、既存のプロジェクトはJavaであったり、Erlangであったりと様々な言語が用いられていますが、インフラ人員が少ないということもあり、なるべくシンプルなインフラ構成を実現する必要がありました。

そういった中、各アプリケーションをDocker化することでインフラとしては共通基盤としてのDockerクラスタのみの管理になり、運用コストが下がるのではないかという仮説からDocker化を進めることになりました。クラスタを実現するに辺りKubenatesなどの選択肢もありましたが、今回はECSを選択し、下記のようにAWSのマネージドサービスを最大限に活用しています。

  • オーケストレーションツール: Amazon EC2 Container Service (ECS)
  • サービスディスカバリ: Application Load Balancer (ALB)
  • Dockerレジストリ: Amazon ECR
  • ログ、メトリクス収集: CloudWatch, CloudWatch Logs
  • 監視: CloudWatch Alarms
  • Infrastructure as Code: CloudFormation
  • CIツール: Jenkins

各技術の選定理由

今回Docker化を行うに辺り、下記を優先的に技術選定を行いました。

  • 運用が楽であること
  • 構成がシンプルで、技術の学習コストが低いこと

まずは、オーケストレーションツールの選定です。候補に上がったのは、Docker Swarm、Kubernetes、ECSです。

DockerのSwarm modeは本番での運用例が技術選定時点であまり見当たらなかったので候補から落としました。次にKubernetesとECSですが、海外の事例などではどちらも多く使われているようです。

今回は多機能さよりも運用に手間がかからない方が良いと考え、マネージドサービスであるECSが第一候補にあがりました。ここは詳細に調査したというよりも、ある種勢いで決めています。その上でやりたいことが実現できるかどうか一つ一つ技術検証を行った上で導入判断を行いました。

同じようにマネージドサービスを優先的に使ったほうが良いという考えで、ログなどでもCloudWatchを使っています。

AWSインフラをコードで記述するものとしてはTerraformが良く取り上げられている気がしますが、個人的にはいくつかの理由でCloudFormationを推しているのでこちらを使っています。

CIツールですが、社内の標準であるJenkinsをそのまま使うことにしました。

全体構成

下記のような構成になっています。

スクリーンショット 2017-05-21 12.46.39.png

ざっくりと説明すると、developmentブランチにプッシュするとGithub HookでJenkinsがDockerイメージをビルドして、ECRにPushします。ユーザはJenkinsでDeployジョブを実行(あるいはBuildの後続ジョブとして自動実行)し、CloudFormationにyamlファイルを適用することでTask, Service, ALB, Route53設定, CloudWatch設定を一通り実行します。またECSのClusterはあらかじめCloudFormationテンプレートを作成して作っておきます。

Task/Serviceの更新についてはCloudFormationを経由しない方がシンプルかとは思いまいしたが、Service毎に管理するRoute53やCloudWatchと合わせて一つのテンプレートにしてしまうのが良いと判断しました。

ここまでやるなら専用のデプロイ管理ツールを作った方がとも思ったのですが、業務委託という立場で自分しかメンテができないものを残すものは躊躇されたため、あくまでAWSとJenkinsの標準的な機能を組み合わせて実現しています。

CloudFormationテンプレートの解説

上記の流れが全てなので理解は難しくないと思いますが、一連の処理で重要なポイントとなるのはCloudFormationテンプレートなのでこれについてだけ触れておきます。長いテンプレートなのでざっくりとだけ雰囲気を掴んでもらえればと思います。

ECSクラスタのテンプレート

cluster作成用のCloudFormationテンプレートは下記のようになっています。

gist:cluster.yaml

一見複雑に見えますが、Amazon EC2 Container Service テンプレートスニペットを参考に作ると簡単に作成できると思います。

(あまりそのまま書くと会社に怒られそうなため)省略していますが、実際にはここにECSクラスタの監視を行うCloudWatch Alarmなどを設定することで、監視設定までこのテンプレートだけで完了します。

ECSクラスタはインフラチーム側であらかじめ用意しておき、リソースが足りなくなったときなどには適宜インスタンス数を変更したりクラスタ自体を別途作ったりしていきます。オートスケーリングを導入すればそれすら必要なくなります(今回はDocker運用が初めてだったので知見がたまるまで手動での対応にしています)。

インフラ側としての責務はここまでで、下記のテンプレートで定義される個別のサービスについてはアプリ開発者側の責務として明確に責任境界を分けました。(もちろん実際にはサポートはかなりの部分でしています。)

これにより全員が今までよりインフラに近い領域まで意識するように個人の意識が変わっていくことを期待しています。

個別サービス用テンプレート

開発環境、ステージング環境、プロダクション環境などそれぞれで同一のテンプレートを使うようにし、パラメータを使用します。そのパラメータをJenkinsのジョブ内で注入することで実現します。VPCなどの環境で決まる値はJenkinsジョブで実行するスクリプト内で定義し、アプリケーションごとの値は environment.yaml というファイルを用意してスクリプトから読み込みます。

environment.yamlは例えば下記のようになっています。アプリケーション開発者は、特殊なことをしない限りは service.yaml をインフラチームが用意したservice.yamlをコピーして、environment.yamlだけ編集すれば良い形になっています。DSLですら無いのでアプリ側のメンバーも心理的な抵抗が少ないようで良かったです。

environment.yaml
images:
- xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-image
parameters:
  default:
    TaskMemory: 512
    TaskMaxMemory: 990
    ImageRepositoryUrl: xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-image
    ServiceDesiredCount: 1
  dev:
    ClusterName: dev-default
    JavaOpts: "-Xmx256MB"
  stg:
    ClusterName: stg-default
    JavaOpts: "-Xmx256MB"
  prod:
    ClusterName: default
    JavaOpts: "-Xmx1500MB -Xms1500MB"
    TaskMemory: 1990
    TaskMaxMemory: 1990
    ServiceDesiredCount: 2

そして service.yaml は下記のようなファイルです。

gist:service.yaml

これもAmazon EC2 Container Service テンプレートスニペットから作ればすぐにできるのではないかと思います。(もちろん全てのパラメータは一つ一つ値を検討します。)

こちらもCloudWatch周りや重要でないところは削除しています。色々と手で削ってるのでコピペだと動かない可能性大ですが雰囲気だけ掴んで貰えればと思います。

このファイルは全アプリケーションで同一ファイルを使うのではなく、アプリケーションごとにコピー/編集して利用します。全体の変更を行うときには全プロジェクトのファイルを更新しなければいけませんが、共通基盤がアプリケーション側を制約しないように、プロジェクト毎のyamlファイル管理としています。ファイルの配置場所は各Gitリポジトリに配置するのが理想ですが、現状ではDocker運用になれてくるまで全てのyamlファイルを管理するリポジトリを作成してインフラチーム側が主に編集する形を取っています。

デプロイ

あとは、このservice.yamlとenvironment.yamlを組み合わせてデプロイするRubyスクリプトでもJenkinsのPipelineのコードでも適当に書いてJenkinsのJobを登録すれば完了です。(environment.yamlファイルを読み込んで aws cloudformation create-stack でservice.yamlと共にパラメータとして渡すだけなので簡単です!)

新規アプリ開発時も社内標準のservice.yamlとenvironment.yamlをファイルを持ってきて、environment.yamlを修正した上で、Jenkinsにジョブを登録すればすぐにDockerクラスタへのデプロイ準備が整います。しかも、上記のテンプレート例では割愛していますが、テンプレートには監視項目/通知設定まで書かれているので、インフラ側で設定を行う必要もなく監視が開始されます。CloudFormation最高ですね。

おわりに

実際の運用ではミッションクリティカルなアプリケーションならではの品質管理のために、JenkinsのPipeline機能を利用して開発→検証→リリースまでのデプロイメントパイプラインを実現しています。

アプリケーションのSECRETなどコミットしない情報をどう管理するかも検討する必要がありますが、これは管理の仕方はチームによって異なると思ったため割愛しています。

また、ログ解析としてはS3に出されたALBのログをRedash+Amazon Athenaでエラー率やアクセス数を分析できるようにし、CPU使用率やメモリ使用率などのパフォーマンス状況をCloudWatchの内容をGrafanaで可視化しています。これによりログ収集の基盤などを作らずに必要な可視化を実現することができました。ベンチャーでは分析基盤の運用も大きなコストになってしまうため、こういった工夫も必要です。(もちろん重要なKPIについては別途分析する仕組みが整っています。)

今回の構成が最高とは思いませんが、ある程度満足行くところまではできたかなと思います。もっとよくできるよ!とか一緒にやりたいな!とかもっと詳細聞きたいな!いう方はぜひ @miyasakura_ までご一報ください。

続きを読む

【AWS】ELB(CLB)

はじめに

タダです。
AWS認定資格の試験勉強のためにELBの情報をまとめた自分用メモになります。
あくまで個人用のメモになるので、その点はご了承ください。
※違う内容書いているなどありましたらご指摘いただけると幸いです。
※本記事では、Classic Load Balancerの情報をまとめていきます。
※随時アップデートがあれば更新していきます。

サービス概要

ELBは2つのタイプに分けられる

  • Classic Load Balancer

    • アプリまたはネットワークレベルのいずれかの情報に基づいてルーティングする
    • 複数のEC2インスタンス間でシンプルなトラフィックのルーティングに適する
  • Application Load Balancer
    • リクエストパスまたはホストベースでルーティングする
    • 複数のサービスにルーティングしたり、同じEC2インスタンスの複数のポート間で負荷分散するのに適する

特徴

  • スケーラブル

    • ELB自体も負荷に応じてスケールアウト
  • 可用性向上
    • 複数AZへの振り分け、インスタンスのヘルスチェック
  • マネージドサービス
  • ルーティング方式
    • 基本はラウンドロビン
  • コネクションタイムアウト
    • クライアントからの無通信状態が続くと接続を自動で切断する(デフォルト60秒)
  • ELB自体のスケーリング
    • じんわりとしたトラフィックの増加には対応できる

      • 逆に急激なトラフィックの増加にはスケーリング対応できないため、事前にPre-Warming(暖機運転)の申請をAWSに行う
  • スティッキーセッション
    • 同じユーザーから来たリクエストをすべて同じEC2に送信
    • アプリケーションでのセッション情報、一時ファイルなどをEC2が保持する構成の場合に必要
    • HTTP/HTTPSでのみ利用可能
    • EC2の増減を柔軟に管理できるようセッション情報は別のDBやキャッシュサーバに保持させるのが好ましい
  • Connection Draining
    • EC2インスタンスとELB間の登録解除、ヘルスチェック失敗した時にルーティングを中止して、処理中のリクエスト終わるまで一定期間まつ
  • X-Forwarded-For
    • HTTPまたはHTTPSロードバランサーを使用する場合に、クライアントの IP アドレスを識別するのに役立つ
  • Proxy Protocol
    • ELBはProxy Protocolバージョン1をサポートしている
    • Proxy Protocolヘッダーはバックエンド接続用にTCPを使う場合、クライアントのIPアドレスを識別するのに役立つ
    • 有効化する場合の前提条件は以下の通り
      • ELBの背後にプロキシサーバがいないこと
      • インスタンスでProxy Protocol情報を処理できるか確認する
      • リスナー設定でProxy Protocloがサポートされているかを確認する
  • 他のサービスとの連携機能
    • AutoScaling
    • Route53
    • CloudFormation
    • OpsWorks
    • Elastic Beanstalk
    • CloudWatch
    • EC2
    • S3など

サポートプロトコル

Classic Load Balancerのサポートプロトコルは、以下の通り

  • HTTP
  • HTTPS
  • SSL(セキュアTCP)
  • TCP

ロードバランサーのタイプ

ロードバランサーのタイプとして、外向けロードバランサーと内向けロードバランサーがある

  • 外向けロードバランサー : インターネットからアクセスできるELB
  • 内向けロードバランサー : VPC内やオンプレ環境からのみアクセスできるELB

モニタリング

ELBの監視を行う場合は以下の方法がある

  • CloudWatchメトリクス
  • ELBのアクセスログ
    • ELBへのリクエストの詳細情報をキャプチャしているので、確認する
  • CloudTrailログ

トラブルシューティング

よくELBのトラブルシューティングでHTTPステータスから設定不足を確認することがあるので、ドキュメントの情報をまとめる

  • HTTP 400: BAD_REQUEST

    • 原因:クライアントが誤ったリクエストをしてしまったため発生
    • 解決策:直接インスタンスに接続し、クライアントリクエストの詳細をキャプチャする
  • HTTP 405: METHOD_NOT_ALLOWED
    • 原因:リクエストヘッダー内のメソッドの長さが127文字を超える
    • 解決策:メソッドの長さを確認する
  • HTTP 408: Request Timeout
    • 原因:指定されたコンテンツのサイズが実際に創始されたコンテンツサイズと一致しないや、クライアントとの接続が閉じているため発生
    • 解決策:リクエストを生成しているコードを調べ、リクエストをより詳細に調べることのできる登録済みインスタンスに直接送信する、レスポンスが送信される前にクライアントが接続を閉じないようにする
  • HTTP 502: Bad Gateway
    • 原因:インスタンスからの応答の形式が適切でないか、ロードバランサーに問題がある
    • 解決策:インスタンスから送信された応答が HTTP 仕様に準拠していることを確認する
  • HTTP 503: Service Unavailable
    • 原因:ロードバランサーにリクエストを処理する能力が不足または、登録されたインスタンス(正常なインスタンス)が存在しない
    • 解決方法:前者の場合は一時的な問題のため、一旦放置氏それでも解決しない場合はAmazon Web Servicesサポートへ問い合わせ
    • 後者の場合、正常なインスタンスを登録する
  • HTTP 504: Gateway Timeout
    • 原因:アプリケーションの応答が、設定されているアイドルタイムアウトよりも長くかかっているまたは、録されたインスタンスがELBへの接続を終了させている
    • 解決策:前者は、ELB側のアイドル接続のタイムアウト設定を伸ばす
    • 後者の場合は、MWのKeepAlive設定を有効にしてELBのタイムアウト設定よりも大きい値を定義する

参考

更新日時

2017/05/01 初回投稿

続きを読む

Elastic Beanstalkトラブルシューティング集

公式のトラブルシューティングに載ってないものをまとめてみました。
いろいろハマった気もしますが、覚えているやつだけ記載しています。

と、その前に

基本的なデバッグ方法としては以下の通りです。

  1. eb create をするときに –debug をつける
  2. eb ssh して直接インスタンスの中を確認する
  3. eb logs する

インスタンスが生成できていなければ 1. 、インスタンスが生成できていれば 2. 、pingで疎通できれば 3. で探っていく感じになります。

Q1. Application Load Balancer を選択するとエラーになる

eb create するときに Select a load balancer type で application を選択すると、こんな感じのエラーが出る。

eb : Configuration validation exception: Invalid option value: 'null' (Namespace: 'aws:ec2:vpc', OptionName: 'Subnets'): Specify the subnets for the VPC for load balancer type application.

A1. コマンドオプションで指定する

VPC が複数ある環境の場合、Elastic Beanstalk がどの VPC を選んだらいいか判断できないようで、このエラーが発生するみたいです。
なので、 eb create のコマンドオプションで VPC 関連の情報を指定すれば OK です。

eb create xxxxxxxx \
    --elb-type application \
    --vpc.id vpc-xxxxxxxx \
    --vpc.elbsubnets subnet-xxxxxxxx,subnet-xxxxxxxx \
    --vpc.ec2subnets subnet-xxxxxxxx,subnet-xxxxxxxx \
    --vpc.elbpublic \
    --vpc.publicip

Q2. リソースが作成できない

以下のようなエラーが出てリソースの作成に失敗する。

Stack named 'awseb-e-xxxxxxxxxx-stack' aborted operation. Current state: 'CREATE_FAILED' Reason: The following resource(s) failed to create: [ALB, AWSEBV2LoadBalancerTargetGroup, AWSEBBeanstalkMetadata, AWSEBLoadBalancerSecurityGroup].

A2. 権限を付与する

実行ユーザーに AWSElasticBeanstalkFullAccess というポリシーをアタッチすれば OK です。
ここでいう実行ユーザーとは ~/.aws/config で指定している IAM ユーザーのことです。

Q3. composer / bundler などで落ちる

hooks/appdeploy/pre にある shell を実行するタイミングで落ちる。
例えばこんなエラーです。

/var/log/eb-activity.log を見てもだいたい解決しないのはお約束です。

ERROR: [Instance: i-xxxxxxxx] Command failed on instance. Return code: 1 Output: [CMD-AppDeploy/AppDeployStage0/AppDeployPreHook/10_composer_install.sh] command failed with error code 1: /opt/elasticbeanstalk/hooks/appdeploy/pre/10_composer_install.sh
++ /opt/elasticbeanstalk/bin/get-config container -k app_staging_dir
+ EB_APP_STAGING_DIR=/var/app/ondeck
+ cd /var/app/ondeck
+ '[' -f composer.json ']'
+ export COMPOSER_HOME=/root
+ COMPOSER_HOME=/root
+ '[' -d vendor ']'
++ /opt/elasticbeanstalk/bin/get-config optionsettings -n aws:elasticbeanstalk:container:php:phpini -o composer_options
+ PHP_COMPOSER_OPTIONS=--no-dev
+ echo 'Found composer.json file. Attempting to install vendors.'
Found composer.json file. Attempting to install vendors.
+ composer.phar install --no-ansi --no-interaction --no-dev

なんとかかんとか

Hook //opt/elasticbeanstalk/hooks/appdeploy/pre/10_composer_install.sh failed. For more detail, check /var/log/eb-activity.log using console or EB CLI.

この場合は出ているエラーのうち、なんとかかんとかの部分で判断できます。

A3-1. Cannot allocate memory

Cannot allocate memory と出ているなら composer の実行時にメモリが足りていない可能性が高いので、.ebextensions でメモリを増やす設定を追加します。

.ebextensions
option_settings:
  aws:elasticbeanstalk:container:php:phpini:
    memory_limit: 1024M

インスタンスを大きくするとかできるなら楽ちんです。
(swap ファイル作るでも解決できるはずだが調べてない)

A3-2. ErrorException

[ErrorException] Undefined index: hash

ERROR: bundle install failed!
の場合、lock ファイルおかしい可能性があります。

eb ssh をしてみて、lock ファイルを消してライブラリをインストールすると分かったりします。

# ruby なら
cd /var/app/ondeck/
sudo rm Gemfile.lock
bundle install

# php なら
cd /var/app/ondeck/
sudo rm composer.lock
composer.phar install

僕の場合は composer 自体のバージョンがローカル環境とあっていないためにエラーになっていました。
なので、.ebextensions でバージョンを指定して対応しました。

.ebextensions
commands:
  01updateComposer:
    command: export HOME=/root && /usr/bin/composer.phar self-update 1.5-dev

Q4. DB に繋がらない

エラーログをみると DB に繋げないっていうのです。

A4. ローカル IP からのアクセスを変更する

原因としては Application Load Balancer を選択したせいで subnet の IPv4 CIDR が複数のブロックになった可能性が疑われます。

なので、MySQL にログインして該当のブロックアドレスに GRANT してあげれば OK です。

use mysql;
SELECT Host, User, Password, Select_priv, Insert_priv,Update_priv, Delete_priv FROM user; -- 権限を調べてみる

GRANT ALL PRIVILEGES ON database.* to hoge@"ブロックアドレス%" IDENTIFIED BY 'password' WITH GRANT OPTION;

Q5. 環境を削除しても消えない

Elastic Beanstalk のイベントでこんな感じになって環境が消せないという症状です。

The environment termination step failed because at least one of the environment termination workflows failed.

A5. 問い合わせたら消してくれるっぽい

まあ影響ないし置いといたらいいんじゃないかな。。。

続きを読む