それでは前回宣言した通り、Elastic Beanstalkの設定ファイルを用いた環境構築の自動化をご紹介しようと思います。
はじめに
今回の大きなテーマは**.ebextensions**を使った環境構築の自動化です。
そのための題材として、ナウいWebアプリのHTTPS対応をやってみたいと思います。
SSL証明書はACMに登録しているものを使う前提です。
なので今回の内容としては
- .ebextensionsの作成
- Elastic Beanstalkへのデプロイ
- Route53での名前解決
となります。
ナウい環境って?
こんなの
事前準備
ACMへの証明書の登録
まず、「そもそもドメイン持ってないよ」という方は、Route53のコンソールから適当なドメインを取得してください。
手順はこの記事がわかりやすいかと思います。
実践
では、色々と設定を.ebextensionsに追記して行きますが、
例によって適宜公式ドキュメントを確認すると捗ります。
そういえば.ebextensionsって何?
.ebextensionsはElastic Beanstalkの設定ファイル...ではなく設定ファイル群を格納するディレクトリです。
プロジェクトルートの直下に.ebextensionsディレクトリを作り、その中に拡張子が**.configのyaml形式かjson形式の**ファイルを置くことで、自動的に読み取って設定してくれます。
jsonでも大丈夫らしいですが、公式のドキュメントのサンプルとかでも基本的にyamlで記載しているので、
ここでもyamlで書きます。
HTTPS対応
HTTPS対応と一言で表してましたが、まずは前回つくった環境をベースにやるべきことを列挙します。
- ロードバランサーの443ポートを開けてHTTPSを待ち受けるようにする
- ロードバランサーが443で受けたリクエストをEC2のインスタンスに流す
- ロードバランサーにACMにある証明書を紐づける
まず思いつくのはこれぐらいですね。
しかし、これだけだとロードバランサーがポート80で受け取ったHTTPがそのままDockerコンテナまで流れてしまうので、HTTPSの意味がありません。
よって、
- ロードバランサーがHTTPで受けたリクエストをHTTPSにリダイレクトする
ということも必要になります。
ロードバランサーのポートを開けるぞ!
って時にはaws:elbv2:listener:<port_number>:
という名前空間を使います。
この場合だとaws:elbv2:listener:443:
ですね。
それでは.ebextensionsの下にload-balancer.configとか作って、設定を書いてみましょう。
option_settings:
aws:elbv2:listener:443:
DefaultProcess: default
ListenerEnabled: true
Protocol: HTTPS
最初に目をひくoption_settings
というのは、Elastic Beanstalkの設定を書くときの約束事です。
なので、その気になればCloud Formationのテンプレートとかぶち込んでもっと色々できるのですが、今回は割愛します。
この中だとDefaultProcess
ってのが謎だと思いますが、プロセスというのは「ロードバランサーからEC2への通信の定義」(プロトコルとかポートとか、ヘルスチェックの仕方とか)ぐらいに捉えればいいかと思います。
なので、ここでは「443ポートでリクエストを受けたらdefaultって名前のプロセスを実行するよ」みたいな意味です。
プロセス自体の設定は後述します。
ACMの証明書を使うんだ!
証明書の紐付けはaws:elbv2:listener:<port_number>:
の下にSSLCertificateArns
という項目を作り、そこにACMのARNを記載します。
こんな感じで。
option_settings:
aws:elbv2:listener:443:
DefaultProcess: default
ListenerEnabled: true
SSLCertificateArns: arn:aws:acm:ap-northeast-1:<account-id>:certificate/<certificate>
Protocol: HTTPS
楽やのう。
EC2にリクエストを流すぜ!
さてさて、ここで先ほど話題になったプロセスを定義します。
プロセスを定義する名前空間はaws:elasticbeanstalk:environment:process:<process_name>:
です。
そこにEC2のどのポートにどのプロトコルで通信するかを定義します。
option_settings:
aws:elbv2:listener:443:
DefaultProcess: default
ListenerEnabled: true
SSLCertificateArns: arn:aws:acm:ap-northeast-1:<account-id>:certificate/<certificate>
Protocol: HTTPS
aws:elasticbeanstalk:environment:process:default:
Port: 80
Protocol: HTTP
StickinessEnabled: true # スティッキーセッションの有効化
HealthCheckPath: / # ヘルスチェック対象のパス
HealthCheckTimeout: 30 # ヘルスチェックのタイムアウト時間(秒)
HealthCheckInterval: 60 # ヘルスチェックの間隔(秒)
MatcherHTTPCode: 200 # 「このコードが帰ってきたらヘルスチェック成功!」と言えるHTTPのステータスコード
上記の設定の意味合いをざっくり口語体で説明すると、
「ロードバランサーの443ポートでHTTPSの待受をするよ!リクエストがきたらdefaultっていうプロセスを始めるんだ!defaultプロセスってのはEC2の80ポートにHTTPでリクエストを流すんだよ!60秒ごとにヘルスチェックもしちゃうよ!」って感じです。
(ざっくりしすぎやろなぁ...)
HTTPで来たリクエストはHTTPSでリダイレクト!
さて、今度はHTTPでアクセスされたらHTTPSにリダイレクトさせる設定をしていくわけですが、
残念ながらロードバランサーにはそのような機能は無いため、EC2に頑張ってもらいます。
しかし、HTTPSの証明書のあれこれなどはロードバランサーで終了し、そこから先の通信はHTTPなのでEC2でプロトコルの種類を判別させることはできません。
というわけで↓のような方針を取ります。
ロードバランサーがリクエストを
- 443で受け取る -> EC2の80に流す
- 80で -> EC2の80以外の適当なポート(今回は81を使用)に流す
EC2のnginxがリクエストを
- 80で受け取る -> Dockerコンテナへ
- 81で受け取る -> HTTPSでリダイレクトさせる
まずはnginxの設定を書くため、.ebextensions/nginx.configを作成します。
files:
/etc/nginx/conf.d/redirect.conf:
mode: "000644"
owner: root
group: root
content: |
# Redirect HTTP To HTTPS
server {
listen 81;
rewrite ^ https://$host$request_uri permanent;
}
files
という名前空間を使うことで、Elastic Beanstalkが作ったEC2インスタンスにファイルを置くことができます。
あとは、先ほどの要領でロードバランサーの設定も追記します。
option_settings:
aws:elasticbeanstalk:environment:
LoadBalancerType: application
aws:elbv2:listener:443:
DefaultProcess: default
ListenerEnabled: true
SSLCertificateArns: arn:aws:acm:ap-northeast-1:<account-id>:certificate/<certificate>
Protocol: HTTPS
aws:elasticbeanstalk:environment:process:default:
Port: 80
Protocol: HTTP
StickinessEnabled: true # スティッキーセッションの有効化
HealthCheckPath: / # ヘルスチェック対象のパス
HealthCheckTimeout: 30 # ヘルスチェックのタイムアウト時間(秒)
HealthCheckInterval: 60 # ヘルスチェックの間隔(秒)
MatcherHTTPCode: 200 # 「このコードが帰ってきたらヘルスチェック成功!」と言えるHTTPのステータスコード
aws:elbv2:listener:80: # ロードバランサーが80ポートで受けた時の定義
DefaultProcess: http
ListenerEnabled: true
Protocol: HTTP
aws:elasticbeanstalk:environment:process:http:
Port: 81
Protocol: HTTP
HealthCheckPath: /
HealthCheckTimeout: 30
HealthCheckInterval: 60
MatcherHTTPCode: 301 # リダイレクトされるのでコードは301が帰ってくるはず
さあ、デプロイしようか
「すぐに環境が作れるよ」がうたい文句なので、エンバイロメントを新しく作ることにします。
VPCの設定とかをまたコマンドで打つのはだるいので、その辺も設定に書いておきます。
新しくec2.configというファイルを作ります。
option_settings:
aws:ec2:vpc:
VPCId: vpc-5920d83d # VPCのID
Subnets: subnet-9ce850ea,subnet-8128ead9 # EC2のサブネットのID
ELBSubnets: subnet-9ce850ea,subnet-8128ead9 # ELBのサブネットのID
AssociatePublicIpAddress: true
aws:autoscaling:asg: # オートスケーリングの設定
MinSize: 1
MaxSize: 2
aws:autoscaling:launchconfiguration:
EC2KeyName: <key_name> # SSHキー
SSHSourceRestriction: tcp, 22, 22, <my_ip_address> # SSHのアクセス権限 お使いのIPアドレスを
さて、これでeb create
と行きたいところですが、ソースと思しきファイルがないとElastic Beanstalkはサンプルアプリでエンバイロメントを構築するのですが、
その場合ファイルのアップロードが行われないためせっかく書いた.ebextensions以下の設定がガン無視されます。
なので適当なDockerfileを用意しておきます。
FROM ubuntu:12.04
RUN apt-get update
RUN apt-get install -y nginx zip curl
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
RUN curl -o /usr/share/nginx/www/master.zip -L https://codeload.github.com/gabrielecirulli/2048/zip/master
RUN cd /usr/share/nginx/www/ && unzip master.zip && mv 2048-master/* . && rm -rf 2048-master master.zip
EXPOSE 80
CMD ["/usr/sbin/nginx", "-c", "/etc/nginx/nginx.conf"]
これでeb create
してみましょう。
ロードバランサーのタイプは設定に書いてても聞かれるので、必ずapplicationと答えましょう。
エンバイロメントが出来上がってからElastic Beanstalkのコンソールに記載されているURLにアクセスすると、
こんな画面になります。
ただし、この状態ではSSL証明書のドメインとリクエストURLのドメインが異なるため証明書のエラーが発生します。
仕上げはRoute53!
さあ、あとはRoute53をElastic Beanstalkに向けるだけです。
Route53のコンソールからまず取得しているドメインでHosted zoneを作成し、適当なサブドメインでレコードセットを作ります。
タイプをAレコードのIPv4にすると候補にElastic Beanstalkのアプリが候補に出ますので、そいつを選んでやりましょう。
今回私はwww.mic-u-neko-masshigura.netというドメインを使います。
こんなしょぼいサンプルアプリのために.netを使うとは、なんて贅沢なんでしょう。
レコードセットを作ったらwww.mic-u-neko-masshigura.netにアクセスします。
アドレスバー左の鍵のマークが緑色になっているので、SSLが成功していることがわかります。
あと、画像からじゃわかりませんが、HTTPでアクセスしてもHTTPSにリダイレクトされます。
終わりに
長い戦いを経て、ナウい環境を設定ファイルに落とし込むことができました。
ここまで長い道のりでしたが、あとはこの.enextensionsさえあれば同様の環境がコマンド一発で量産できます。
オンプレで用意しようとしたら考えるのも嫌なぐらい面倒で、AWSでも手動で毎回構築するのは結構面倒臭い環境を(最初に設定ファイルを用意するのが面倒とはいえ)簡単に構築して量産できるのは魅力ではないでしょうか?
個人的な印象ではElastic Beanstalkってまだまだあまり使われていないような気がしますが、
上手く使えば本当に便利ですのでこれを機に皆さんどんどんElastic Beanstalkに触れてみてください!