RM-BLOG

IT系技術職のおっさんがIT技術とかライブとか日常とか雑多に語るブログです。* 本ブログに書かれている内容は個人の意見・感想であり、特定の組織に属するものではありません。/All opinions are my own.*

【AWS】Private Subnetに配置したEC2×2にELBでロードバランシングした構成に独自ドメインを使ってアクセスしてみた(自己学習の記録)

タイトルの通りなのだが。。

AWSのElastic Load Balancer(Application Load Balancer)を使ってPrivate Subnetに配置した2台のEC2にロードバランシングする。
また、アクセスに際しては、Route 53で取得した独自サブドメインでアクセスする。
調べりゃ似たような記事はどこにでもあるので、あくまで自分の学習記録をまとめたものであり、自分用の備忘録である。
そういう意味では初学者用にはなるかもしれません。


はじめに

  • この記事は2020年4月時点のAWSマネジメントコンソールを使ってます。
    そのうち画面が一新される可能性は十分あるのでご注意ください。
  • この手順をそのまま実行すると金がかかります。
    私は12か月の無料トライアル期間でやったので基本ほとんど無料でしたが、それでも少なくともドメイン取得料だけは(たとえ12か月の無料期間中だとしても)発生します。
    一応注意事項として。
  • 以下のサービスを使いました。各サービスで必要になるポリシーは適宜操作ユーザーに付与してください。
    • Route 53
    • ACM(Amazon Certificate Manager)
    • VPC
    • EC2
    • ELB(Elastic Load Balancer)
  • 最終的に以下のような構成を目指します。(さくらインターネットさんのアイコン集を使わせていただきました)
    f:id:rmrmrmarmrmrm:20200502005723p:plain

ドメインの取得

独自ドメインを取って、それのサブドメインを登録する。

独自ドメインの取得

Route 53で独自ドメインを取得する。
AWS内で完結できる(いちいち他行って取らなきゃならない煩わしさがない)というのと、あと自分の勉強用にRoute 53にした、という程度であり、ドメイン取得できれば別にRoute 53である必要は全くない。
(Route 53以外で取った場合は後でDNSの切り替えが必要になるが)

Route 53のトップページにあるドメインチェックに使えそうなドメインいれて「チェック」を押す
f:id:rmrmrmarmrmrm:20200502005726p:plain

使えるドメインなら「ステータス」が「使用可能」になるので「カートに入れる」をクリック。
f:id:rmrmrmarmrmrm:20200502005730p:plain

このあとwhoisの管理情報の入力欄などがあるが割愛。
最後に「注文を完了する」的なボタン押すとドメイン購入完了になる。
ちなみにこの決済は通常のAWSの請求と違ってその場で引き落としかかるらしい。
一方、Budgetのアラーム等には当月の利用分として計上される。
以後、ここで取得したドメインを便宜上「aaa.com」と仮置きする。

サブドメインの取得

「aaa.com」のサブドメイン、ここでは便宜上「bbb.aaa.com」と仮置きするとして、これを取得する。

Route 53のトップページで「DNS管理」をクリックする
f:id:rmrmrmarmrmrm:20200502005735p:plain

この時点で恐らく「aaa.com」が一行だけ表示されていると思われる。
コメント欄に「HostedZone created by Route53 Registrar」と書かれている行がそれ。
この後「ホストゾーンの作成」をクリックする→右側に入力窓が出てくるので、「bbb.aaa.com」と入力して「作成」をクリック。
f:id:rmrmrmarmrmrm:20200502005739p:plain ※画像の一部(ドメイン名)にしょっぱい加工をかけています

NSレコードとSOAレコードが表示される。 NSレコードをコピーする。 f:id:rmrmrmarmrmrm:20200502005742p:plain

ホストゾーンに戻ると、今回登録した「bbb.aaa.com」が2行目として追加されている。
本体ドメイン「aaa.com」の行を選択する。→レコードセットの管理画面に行くので、「レコードセットの作成」をクリックする。
f:id:rmrmrmarmrmrm:20200502005748p:plain ※画像の一部(ドメイン名)にしょっぱい加工をかけています

項目
名前 bbb
タイプ NS-ネームサーバ-
上でコピーしたNSレコードの値をそのままべたっと貼り付ける

で、「作成」をクリックする。
ちょっとだけ待つ。(5~10分くらい?)

登録したサブドメイン「bbb.aaa.com」の名前解決をしてみて、引けるようになってたらOK

> nslookup -type=ANY bbb.aaa.com

bbb.aaa.com   nameserver = ns-9999.awsdns-99.co.uk
bbb.aaa.com   nameserver = ns-99.awsdns-99.com
bbb.aaa.com   nameserver = ns-999.awsdns-99.net
bbb.aaa.com   nameserver = ns-9999.awsdns-99.org

ns-9999.awsdns-99.org   internet address = xxx.xxx.xxx.xxx
ns-9999.awsdns-99.org   AAAA IPv6 address = xxxx:xxxx:xxxx:xxxx::1
ns-9999.awsdns-99.co.uk internet address = xxx.xxx.xxx.xxx
ns-9999.awsdns-99.co.uk AAAA IPv6 address = xxxx:xxxx:xxxx:xxxx::1
ns-99.awsdns-99.com     internet address = xxx.xxx.xxx.xxx
ns-99.awsdns-99.com     AAAA IPv6 address = xxxx:xxxx:xxxx:xxxx::1
ns-999.awsdns-99.net    internet address = xxx.xxx.xxx.xxx
ns-999.awsdns-99.net    AAAA IPv6 address = xxxx:xxxx:xxxx:xxxx::1

証明書の取得

Certificate managerに移る。
トップページで「証明書のリクエスト」ボタンをクリック
f:id:rmrmrmarmrmrm:20200502005751p:plain

f:id:rmrmrmarmrmrm:20200502005754p:plain

ドメイン名に「bbb.aaa.com」を入力して「次へ」
f:id:rmrmrmarmrmrm:20200502005758p:plain

DNSの検証」(初期値)で「次へ」
f:id:rmrmrmarmrmrm:20200502005802p:plain

タグはまあ、好きなものを入れる。
f:id:rmrmrmarmrmrm:20200502005806p:plain

bbb.aaa.comが表示されていることを確認し、「確定とリクエスト」をクリックする
f:id:rmrmrmarmrmrm:20200502005809p:plain
※画像の一部(ドメイン名)にしょっぱい加工をかけています

リクエストの画面が出て検証状態「検証保留中」になってると思われる。
CNAMEレコード情報が表示されているので、その下にある「Route 53でのレコード作成」ボタンをクリックする
f:id:rmrmrmarmrmrm:20200502005813p:plain ※画像の一部(ドメイン名)にしょっぱい加工をかけています

「作成」ボタンをクリックする
f:id:rmrmrmarmrmrm:20200502005816p:plain ※画像の一部(ドメイン名)にしょっぱい加工をかけています

この後、しらばく待つ。(15~30分くらい?)
しばらく待つと「状況」欄が「発行済み」になる。
「発行済み」になったら完了(証明書が出来上がっている)。
f:id:rmrmrmarmrmrm:20200502005821p:plain ※画像の一部(ドメイン名)にしょっぱい加工をかけています

なお、この作業の後、Route 53のホストゾーンに戻って、bbb.aaa.comの設定を見てみると、CNAMEレコードが追加されていることがわかる。
まあ、これは発行完了するまでの暇つぶしにでも見ておけばいいと思う(確認自体は一瞬で終わるが)

VPC、サブネット、インターネットゲートウェイ、ルートテーブル、とか諸々の作成

ネットワーク環境周りを作る。

VPCの作成

VPCを作る。
名前はまあ、わかりやすいものを適当につける。
IPv4 CIDRをとりあえず他の色んな例に倣って10.0.0.0/16にしておく。
f:id:rmrmrmarmrmrm:20200502005825p:plain

サブネットの作成

続いてSubnetを作っていく。
Public SubnetとPrivate Subnetをアベイラビリティゾーンをまたいでそれぞれ2つずつ、つまり合計4つ作る。
ただこの時点ではどれも「この後PublicないしPrivateにするSubnet」であって、この作成作業時点でそれぞれのSubnetにPublicとかPrivateとかいう特性は指示しない(そもそもそういう指示は存在しない)
逆に言えば後々のために名前をわかりやすくしておく(Public-1aとか)必要はあると思う。

アベイラビリティ・ゾーン CIDR Subnet用途
ap-northeast-1a 10.0.1.0/24 Public Subnet
ap-northeast-1c 10.0.2.0/24 Public Subnet
ap-northeast-1a 10.0.3.0/24 Private Subnet
ap-northeast-1c 10.0.4.0/24 Private Subnet

f:id:rmrmrmarmrmrm:20200502005828p:plain Public Subnet用、ap-northeast-1a参考画像。

Public Subnet2つ作るのは、ELB作成の際に、最低2つの異なるアベイラビリティ・ゾーンにあるサブネットを指定する必要があるから。
Private Subnetは2つなくてもいいのだが、ELBで負荷分散することを考えて、どうせならマルチAZにしてみる。

インターネットゲートウェイの作成

続いてインターネットゲートウェイを作る(Public Subnet用)
これの作成自体は単に名前を入力するだけである。
作成後、インターネットゲートウェイの一覧画面から、作成したインターネットゲートウェイを選んで、アクション→VPCにアタッチ、さっき作ったVPCを選択する。
f:id:rmrmrmarmrmrm:20200502005832p:plain

ルートテーブルの作成

Public用とPrivate用に2つつくる。
さっきつくったVPCを指定する。
f:id:rmrmrmarmrmrm:20200502005835p:plain

ルートテーブルを作ったら一覧画面に戻るので、2つつくったルートテーブルのうち、Private用にするsubnet(のために作ったルートテーブル)を選択する。
画面下部の「ルート」タブを選択し、「ルートの編集」をクリックする。
f:id:rmrmrmarmrmrm:20200502005838p:plain

0.0.0.0/0のルーティングを外す。(該当する行の×ボタンを押して行を消し、「ルートの保存」をクリック)
f:id:rmrmrmarmrmrm:20200502005842p:plain

要は0.0.0.0/0のルーティングがある→Public Subnet、ない→Private Subnet、ということらしい。
実際、0.0.0.0/0のないルートテーブル(に関連付けられたPrivate Subnetに配置されたEC2)に対しては、セキュリティグループでインバウンドルールをがら空きにしててもアクセスが通らない。(例えば0.0.0.0/0から22番を許可するルールをいれておいても、sshで中に入れない)
なお、ルートテーブルの作成前に、VPCにアタッチ済のインターネットゲートウェイがすでに存在していると、ルートテーブル作成に際して、インターネットゲートウェイに対するルーティングが自動で(勝手に?)追加されるようだ。
というかこういう状況では「インターネットゲートウェイを無視して作る」ということが逆にできなくなっているようだ(作成途中の画面で取り外せばいいんだが…逆に言えば毎回取り外さないとだめ)

続いてサブネットを関連付ける。
ルートテーブルの一覧からPublic用につくったルートテーブルを選択し、画面下部にある「サブネットの関連付け」タブを選択して「サブネットの関連付けの編集」ボタンをクリック
f:id:rmrmrmarmrmrm:20200502005845p:plain

0.0.0.0/0のルーティングがあるルートテーブルにはPublic(用に作った)Subnetを関連付ける。
0.0.0.0/0のルーティングがないルートテーブルにはPrivate(用に作った)Subnetを関連付ける。
下記はPublic Subnetを関連付けている例である
f:id:rmrmrmarmrmrm:20200502005849p:plain

EC2の作成

Publicに1台、Privateに2台つくる。
実際に動作させるアプリケーションはPrivateに配置するので、そういう意味ではPublicの1台はいらんのだが、Privateのサーバに入るために一度Publicを経由する必要がある。
ただこれは個人的な拘りというか実験によるところが強いので、単に動くものを乗せるだけならPublicに2台置いてhttpdとかnginxとか動かしてそこにロードバランシングさせれば事足りる。
なんとなくPublicとPrivateにモノを置きたかったのだ。(本当にそんだけ)
とりあえずEC2インスタンスを作っていく。

Public Subnetの踏み台サーバ

AMIはAmazon Linuxで。
f:id:rmrmrmarmrmrm:20200502005853p:plain

インスタンスタイプはt2.microを。(無料利用枠っていうのだけを理由にしている。特に意味はない)
f:id:rmrmrmarmrmrm:20200502005857p:plain

作成時にPublic Subnetを指定する。
アベイラビリティゾーンにこだわりはないので好きな方でいい。
f:id:rmrmrmarmrmrm:20200502005901p:plain

ストレージは初期値のまま。そんなに使わない。
余談だが、ここでサイズを8GB未満に指定すると、後の作成処理でエラーになる(最低でも8GBを指定しないとだめらしい。じゃあここでチェックしてくれよという感じもしないでもない。まあどうでもいいが)
f:id:rmrmrmarmrmrm:20200502005905p:plain

タグ。
なんか付けるのが慣習?のような雰囲気を感じている。
だからなんか付ける。
f:id:rmrmrmarmrmrm:20200502005909p:plain

セキュリティグループを作成する。
とりあえず22番(ssh)のアクセスを許可する。
個人的にはソースを0.0.0.0/0にするのがあまり好きではないので申し訳程度に自分のグローバルIPのCIDR指定してたりする(この辺で自分で調べられる)が、この辺は別に大きなこだわりはない。(下記は0.0.0.0/0を指定している例)
f:id:rmrmrmarmrmrm:20200502005914p:plain

鍵を指定して作成。
完全に出来上がるまで少し待つ。

Private Subnetのアプリケーション(もどき)サーバ

アベイラビリティ・ゾーンを跨いで2台つくる。
基本的にはPublic Subnetの踏み台サーバと同じ。(Amazon Linux、t2.micro、8GBのボリューム)
なお、今度はPrivate Subnetに設置するので、サブネットはPrivate Subnetを選択する。

また、新規にセキュリティグループを作成するにあたり、10.0.0.0/16から22番(SSH)及び4000番(カスタムTCP)の通信を許可する。

f:id:rmrmrmarmrmrm:20200502005919p:plain

前者は「VPC内(10.0.0.0/16)からのみsshでの接続を許可する」を意味する。
そういう意味だともっとキツくてもいいのだが(Public ServerのPrivate IPを直接指定、/32のほうが本当には望ましいんだと思われる)まあ、一旦これにしておく。
後者は「VPC内からの4000番の接続を許可する」を意味する(こっちのポートだと書き方が22番と違う雰囲気にしたくなるのは不思議だな、どうでもいいけどw)
4000番はPrivate Subnetに配置したこの「アプリケーションサーバ(もどき)」の上で動かすWebアプリ(もどき)のポートである。
別に4000に強い意味があるわけではないので好きなポートにしてよいと思う(ちなみにELBの初期値はHTTP 80番である)

アプリケーションの準備

Private Subnetに配置したサーバ2台に簡易なWebアプリケーションを用意する。
個人的にNode.jsがやりやすかったというだけでNode.jsを選んだが、これに深い意味はない。
極端な話、「動いてるところが見たい」というだけならhttpdやnginxで事足りる。
そんなわけで、各人好きなもの使えばいいと思う。

Node.jsのインストール

Amazon LinuxにはNode.jsは入ってないので、Node.jsの実行環境をまず用意する必要がある。
ただし、Private Subnetに配置したサーバからは、基本的に外(インターネット)に出れないので、yumもできない。
NATゲートウェイ設置して外との経路作れば行ける…のかな。(試してない)
まあとにかくここでは原始的なやり方でいくことにする。
Node.jsの公式ページからLinux用の実行ファイル群をダウンロードしてきて、それを踏み台サーバにscpしたのち、踏み台サーバからscpで各Privateサーバに送る。

(1)踏み台に送る

scp -i [秘密鍵ファイル] -4 [Node.jsダウンロードファイル(.tar.xz)] ec2-user@[踏み台サーバのPublic IP]:[踏み台サーバのどっか適当な場所]

(2)Tera Termとかで踏み台に入り、踏み台からさらにPrivateの各サーバに送る

scp -i [秘密鍵ファイル] -4 [Node.jsダウンロードファイル(.tar.xz)] ec2-user@[PrivateサーバのPrivate IP]:[Privateサーバのどっか適当な場所]

(3)踏み台から各Privateサーバにsshする

ssh -i [秘密鍵ファイル] -4 [PrivateサーバのPrivate IPアドレス]

(4)Node.jsダウンロードファイルを解凍

xz -d [Node.jsダウンロードファイル(.tar.xz)]
tar -xf [Node.jsダウンロードファイル(.tar)]

(5)適当なディレクトリを作って、expressをインストールする(npm initは飛ばす)

[Node.jsのbin]/npm install express

(6)以下のコードをviで貼り付け、index.jsで保存する。

const express = require('express');
const app = express();
const os = require('os');
const port = 4000;

app.get('/' , (req,res)=>{
    let hostname = os.hostname();
    res.send('hostname=' + hostname);
});

app.listen(port , (err)=>{
    if(err) {
        console.log('error happened');
        throw err;
    }
    console.log('server listened by port ' + String(port) + ' ...');
});

これが実際に動くアプリケーションの本体になる。
といっても中身は、ルート(/)にアクセスすると自分のホスト名(os.hostname())を出力するだけの非常に簡素なものだ。
要はELBで負荷分散された結果、「どのサーバに振られたか」というのを知りたいだけの実装である。

各Private上のサーバ上で起動(node index.js)したのち、curl http://localhost:4000/を実行して、自身のホスト名が表示されれば良し。
さっき設定したセキュリティグループでVPC内からの4000番を許可してるので、例えばPrivate1でnode実行してPublicからPrivate1のPrivate IP(ここでは例として10.0.3.100とする)に対してcurl http://10.0.3.100:4000/打てばPublicからも確認できる(同様にPrivate2からも確認ができる)
なお、Private Subneto上のアプリケーションサーバ(もどき)は2台用意してるので、(3)~(6)を2台分行う。
(こういうの、ユーザーデータとか使えば一発で済むんだろうな。もっといえばコンテナレジストリ使ってそこからpullするのもかっこいい気もする。まあいいか。)

ELBの作成

Elastic Load balancerを作成する。
f:id:rmrmrmarmrmrm:20200502005922p:plain

プロトコルHTTPSを選ぶ。
HTTPSを選択すると自動でポートも443に変わる。
また、VPCは↑でつくったPublic Subnetを2つ選択する。(上にも書いたが、ここでPublic Subnetを最低2つ選択しないと、エラーになって先に進めない)

f:id:rmrmrmarmrmrm:20200502005928p:plain

ちなみにこのとき(インターネット向けELBを作成することを前提で)Private Subnetを指定すると「インターネットゲートウェイがねえよ」と怒られる。
おお~よくできてるなあ~と感心した(?)

f:id:rmrmrmarmrmrm:20200502005931p:plain

Private×2でインターネット向けELB作ろうとしたら警告出たけど先には進めた(最終的に作成してないからどう動作するのかはわからないが)。
でもこのままだと結局リクエストが届かないで終わりになるんだろうな、コレ…
このあとでSubnetのルートテーブルにインターネットゲートウェイ追加したりするケース(それでも動くのかな?)に対してとりあえず作るだけは作っとくよ、ということなのかな。
まあいいか。

続いて証明書を設定する。
ACMから証明書を選択する(推奨)」→上で作った証明書を選択する。

f:id:rmrmrmarmrmrm:20200502005934p:plain

続いてセキュリティグループを作成する。
これは新しく作ってもいいし、もともと作成済みのやつ使ってもいい。
ただここまでの手順では、Public向けには22番のSSH許可するやつしか作ってないので、新しく作る。
ELBのポート443番に通る設定を作る。

f:id:rmrmrmarmrmrm:20200502005938p:plain

続いてルーティング(ターゲットグループ)を設定する。
これもこの場で新規作成できる。ので、「新しいターゲットグループ」を選択する。
プロトコルはHTTPで、ポートは4000番。この4000という数値は↑で作ったNode.jsのアプリがそのポートで動くからである。
同様にヘルスチェックも4000番にしておく。
しかし、これ、今さら思ったが、特にこのケースに限って言えば、別に「上書き」にする必要なさそうだな(「トラフィックポート」ってターゲットグループ設定のときに指定したポートのことか。。)

f:id:rmrmrmarmrmrm:20200502005943p:plain

最後にターゲットとなるインスタンスとポートを指定して追加する。
実際にアプリケーションとして動作しているのはPrivate Subnetのサーバ上なので、Private Subnet上で動いてるサーバ2台を選択して「登録済に追加」する

f:id:rmrmrmarmrmrm:20200502005947p:plain

登録出来てからちょっとすると、ELBのヘルスチェックが終わってルーティングされるようになっている。
「ターゲットグループ」のメニューを開くと、ELB作成に際して一緒に作成したターゲットグループが表示されるが、今回作ったターゲットグループを選択し、画面下部の「ターゲット」のタブを見ると、登録したインスタンス・ポートのヘルスチェック状態がわかる。

f:id:rmrmrmarmrmrm:20200502005951p:plain

次にRoute 53に移る。
ホストゾーンでbbb.aaa.comを選択し、「レコードセットの作成」をクリックして、「エイリアス」のラジオボタンを「はい」に選択する。
下にエイリアスの指定が出てくるので、「エイリアス先」にフォーカスする。
先ほど作成したELBのARNがあるので、それを選択する。

f:id:rmrmrmarmrmrm:20200502005955p:plain ※画像の一部(ドメイン名)にしょっぱい加工をかけています

試す

これで準備完了。
ブラウザで「https://bbb.aaa.com/」にアクセスしてみる。
以下のような表示が出れば成功だ!

f:id:rmrmrmarmrmrm:20200502005958p:plain

ちなみに何度かアクセスすると負荷分散で違うサーバに振られることも確認できる(ホスト名の表示が変わる)。

f:id:rmrmrmarmrmrm:20200502010002p:plain

おわりに

AWS初心者のため、勉強しながらいろいろ触ってみる、というのが目的だった。
結果、複数のサービスに触れたし、個々のサービスについても少なくとも基本的な使い方は学習できたので、よかったと思う。
また、プライベートの(趣味の)範疇で構成図を書くというのも面白い試みだった。
こういうのが手軽に、かつ基本無料で手元でできる、というのは良いことだなあと思う。
クラウド時代を感じました(小並感)
他にもちょこちょこ使ってみたいサービスはあるので、12か月以内で出来るところをいろいろ触ってみようと思う。