Tech Hotoke Blog

IT観音とは私のことです。

Vue×SpringでSPA作成7【AWSにアプリケーションのデプロイ】

f:id:TechHotoke:20211226002024p:plain

まえがき

こちらの記事の続編です。 techhotoke.hatenablog.com

目的

VueとSpringで作成したプロジェクトの構築手順の備忘録。 備忘録のため、詳細な説明を省略している部分があります。

前提

  • 基本的なJavaの知識やSpring、Vueの知識があること。
  • AWSに関する基本的な知識があること
  • ネットワークに関する基本的な知識があること

環境

  • Java 11
  • Spring Boot2.5.6
  • Gradle 7.1.1
  • Vue2.6
  • IDESTS
  • AWS(EC2)

やること

  • 踏み台サーバーの構築
  • AWIの取得
  • NATGatewayの作成
  • ELBの作成

やらないこと

構成

f:id:TechHotoke:20220103064131p:plain

踏み台サーバーの作成

踏み台サーバーとは?

  • プライベートサブネットに配置したサーバーにアクセスするための手法 メリット
  • 外部から各サーバーに直接アクセスできないので、セキュリティの向上につながる
  • 外部通信に対するアクセス制御の対象を踏み台サーバーに限定できるため運用負荷を軽減できる デメリット
  • サーバーが一台増えるので管理コストが上がる

構築

  • EC2インスタンスをpublic-subnet-aに新たに作成(手順省略)
  • ElasticIPアドレスを新規作成し、上記手順で作成したEC2インスタンスに関連づける
  • 既存のセキュリティグループのSSH接続で割り当てられているIPアドレスを上記で作成したものに変更する
  • ここまで設定したら、確認として既存のEC2インスタンスに直接ログインできないことを確認する
  • SSH ポートフォワーディングを使って踏み台サーバーを経由してWebサーバーにログインする
  • 新規ウィンドウでターミナルを開き、ssh -i ~/.ssh/tms-dev-keypair.pem ec2-user@localhost -p 10022コマンドでWebサーバーにアクセスする
  • lsコマンドなどアプリケーションのソースが存在することを確認できたら完了 f:id:TechHotoke:20220103154439p:plain
SSH ポートフォワーディングとは?

SSHコネクション上で任意のポートへの通信を特定ポートへ転送する機能です。これはSSHの機能で、sshコマンドにポートを転送するオプションがデフォルトで備わっています。異なるサーバー間でセキュリティやネットワークの設定によって直接アクセスができない場合に利用されます。

代表的な2つのSSHポートフォワーディング

  • ローカルポートフォワード: クライアントユーザーや稼働するソフトウェアから見た、自らのコンピュータまたは端末 リモートホスト…ローカルのクライアントから見た、ネットワークで接続する先のコンピュータまたは端末 サーバーを中継して、クライアントのポートをリモートホストのリモートポートに転送します。

ssh -L {port}:{remotehost}:{remoteport} {server}

  • リモートポートフォワード: クライアント…ユーザーや稼働するソフトウェアから見た、自らのコンピュータまたは端末 リモートホスト…クライアントから接続するホストから見た、ネットワークで接続する先のコンピュータまたは端末 クライアントを中継して、サーバーのポートをリモートホストのリモートポートに転送します。

ssh -R {port}:{remotehost}:{remoteport} {server}

パブリックサブネットに配置されているEC2インスタンスを別サブネットに移行する

今回はAMIを作成して、そこからEC2インスタンス立ち上げていきます。

AMIとは?

  • ソフトウェア構成 (オペレーティングシステムアプリケーションサーバー、アプリケーションなど) を記録したテンプレート。EC2の元となるイメージ的なもの
  • 独自でカスタムしたAMIを書き出し、それを利用することで、冗長構成の構築、復旧作業などが比較的簡単に行える

AMIの作成

  • 該当のWebサーバーからイメージを作成を選択 f:id:TechHotoke:20220103155442p:plain
  • 適当な名前とタグをつけて(現在日時を入れると後々混乱しなくて良いです)作成
  • リンクをクリック
  • イメージからインスタンスを起動を選択
  • Protected-aのサブネットに配置
  • 既存のセキュリティグループからweb-appを選択
  • インスタンスを作成
  • 作成したインスタンスに踏み台サーバーの作成で行ったことと同じ設定を施す
  • Webサーバーにアクセスできることを確認する
  • Webサーバーにアクセスしたら、下記コマンドなどを使って外部のネットワークに接続できなくなっていることを確認する wget https://ja.wordpress.org/latest-ja.tar.gz
  • 処理が行われずタイムアウトすると思われるので、そうなれば完了です

添付画像のようなWARNINGが発生したので、対処方法をまとめておきます

f:id:TechHotoke:20220104164438p:plain

  • SSHでは初回接続時に接続先ホストの公開鍵を保存しておき、次回接続時にホスト鍵を比較して前回と同じホストに接続したかを確認するような仕組みになっています。そのため、IPアドレスの振り直しやOS再インストールなどでホスト鍵が変わってしまった場合、エラーメッセージが出てSSH接続が失敗してしまいます。
  • 中間者攻撃対策に備わっている機能だと思います

中間者攻撃とは二者間の通信を特別なソフトウェアなどの不正な手段を用いて傍受、盗聴して内容を取得するといったもの

  • 対処

    • $HOME/.ssh/known_hostsの該当行を削除する

    • またはssh-keygenコマンドの-Rオプションで消すこともできるらしいです

      $ ssh-keygen -R example.com

    • 次のような形式でホスト名に加えてポート番号を指定することもできます。[]はシェルのワイルドカード文字ですので、クォーティングは必須です

      $ ssh-keygen -R '[example.com]:10022'

NATゲートウェイの作成

STSのエラーが発生し、あまりにもイライラしたのでInteliJに乗り換えました。なのでここからInteliJを使用して開発していきます。

NATゲートウェイとは?

  • NAT(Network Address Transration):プライベートIPアドレスグローバルIPアドレスに変換する技術
  • インターネットからプライベートサブネットへのアクセスはできなくなる f:id:TechHotoke:20220105171442p:plain
  • NATGateWayはAWSが提供するNATの機能を実現するためのサービス
  • サブネットの設定、ElasticIPアドレスの設定によってNATの機能を簡単に実現することが可能 f:id:TechHotoke:20220105171909p:plain
  • ベストプラクティスとしては、異なるアベイラビリティゾーンごとに NATGateWayを設置して、冗長構成を実現する(NATGateWayが設置されたアベイラビリティゾーンに障害が発生した場合のセーフティネットとして) f:id:TechHotoke:20220105172508p:plain

構築

NATゲートウェイ

  • VPCを検索
  • ナビゲーションペインからNATゲートウェイを選択
  • NATゲートウェイを作成ボタンを押下
  • public-aサブネットを選択する(protectedにしないように注意)

NATゲートウェイ用のサブネットを作成する

  • ルートテーブルを作成
  • ルートテーブルにルート0.0.0.0/0、NAtゲートウェイを選択して作成
  • protectedサブネットの関連づけを行う f:id:TechHotoke:20220105174327p:plain
  • 踏み台サーバー経由でインスタンスにログインする
  • wget https://ja.wordpress.org/latest-ja.tar.gzコマンドなどを実行してインターネットにアクセスできることを確認 f:id:TechHotoke:20220105184006p:plain

ELBの作成

ELBとは?

  • 負荷分散のサービス。ロードバランサーの役割を担うサービス
  • ELBは自動でスケーリングされる
  • ひとつのELBを設置することで、アベイラビリティーゾーンごとにELBが配置されているような構成になる
  • ELBの種類 f:id:TechHotoke:20220106075349p:plain
    • ALB:アプリケーション層で使用。パスベースルーティングを行う(URL含まれるパスに応じて接続するサーバーを切り替える)
    • NLB: トランスポート層で使用。Webサーバー以外の負荷分散に使われる。
    • CLB:旧世代なので使用は非推奨
ELBについて
  • インターネット向けと内部
    • インターネット向け:インターネットから通信が可能
    • 内部:インターネットから通信不可
    • 後から変更できないので注意
  • リスナー
    • どのような通信を受付け、どこに転送するかの設定
    • パスベースルーティングはこの転送先の設定で行うことができる
  • アベイラビリティゾーンとサブネット
    • インターネット向けを選択した場合はパブリックサブネットに配置しないと通信できないので注意
    • 自動でスケールアウトした場合にサブネットのIPアドレスを使用する(ベストプラクティスはサブネットに最低8つのI Pアドレスを作成しておくこと)
  • セキュリティグループ
  • ターゲットグループ
    • 負荷分散の大賞サーバーをまとめたグループ
  • ヘルスチェック f:id:TechHotoke:20220106152330p:plain
  • スティッキーセッション
    • 同じユーザーからきたリクエストを同じターゲット(サーバー)に転送する
    • クッキーを使用して、同じターゲットに転送する

構築

  • ELBを検索し、機能からロードバランサーを選択
  • ロードバランサーを作成を選択
  • Application Loadbalancerを選択
  • Internet向けを選択(記事執筆時では、UIが英語な感じになっていたので、画像を添付します) f:id:TechHotoke:20220106161807p:plain
  • 該当のVPCを選択
  • セキュリティグループを新規作成(HTTP通信ポート80と8080[おそらくTomcatの接続で使うので]をインバウンドルールに設定) f:id:TechHotoke:20220106161930p:plain
  • public-subnet-a及びcをMappingする f:id:TechHotoke:20220106162148p:plain
  • HTTPポート80/8080をリスナーに追加
  • ターゲットグループを新規作成し、適当な名称をつけてデフォルト設定のままターゲットの作成に進む
  • web-app-protectedをターゲットに選択して作成
  • 画像のようにセッションの維持設定を行う f:id:TechHotoke:20220106170940p:plain
  • ロードバランサー作成画面に戻り、リスナーのfoward先に作成したターゲットを選択
  • ロードバランサーの作成を選択
  • 踏み台サーバ経由でインスタンスにログインした状態で、./gradlew bootRunを実行してアプリケーションを起動
  • ロードバランサーに割り当てられたDNSとアプリケーションのポート番号でアクセス
  • こうなった。Damn... f:id:TechHotoke:20220106182628p:plain

502BadGatewayの対処

  • 公式が主にこのような原因が考えられると教えてくれてます(親切)

f:id:TechHotoke:20220106184215p:plain

引用: Application Load Balancer のトラブルシューティング - Elastic Load Balancing

まずはEC2に原因があるのかALBに原因があるのか切り分けたいので、ALBのログを取得できるようにしていきたいと思います。

ALBのアクセスログを有効化して、 f:id:TechHotoke:20220106185849p:plain

S3の添付画像のパスの形式でログが保存されているので、そちらをダウンロードします f:id:TechHotoke:20220109224648p:plain

ログには以下のような情報が記載されているようです。

http 
2022-01-09T13:33:03.219781Z 
app/tms-dev-alb-public/af018ae04d93b735 
14.11.32.1:16804 
10.0.10.54:80 
-1 
-1 
-1 
502 
- 
637 
1077 
"GET http://tms-dev-alb-public-1362974723.ap-northeast-1.elb.amazonaws.com:8080/favicon.ico HTTP/1.1" 
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36" 
- 
- 
arn:aws:elasticloadbalancing:ap-northeast-1:187609072069:targetgroup/tms-dev-tg-public/f25101ace9787ef1 
"Root=1-61dae40f-4edfd59f2cd80e0a78d44d6c"
"-"
"-" 
0 
2022-01-09T13:33:03.192000Z 
"forward" 
"-" 
"-" 
"10.0.10.54:80" 
"-" 
"-" 
"-"

f:id:TechHotoke:20220109232859j:plain

ログを見ると、ロードバランサーが502を返し、ターゲットからの応答が無いようなので、ロードバランサーの問題ということが分かりました。 また、バックエンドからではなく、ELBから502が返ってきていることが分かりました。

つまり、ELBのどこかしらの設定に問題がありそうなので、探してみると

セキュリティグループはpublicなので問題なさそう。

f:id:TechHotoke:20220110121252p:plain

ターゲットグループはHTTP80番ポートを受けて、ターゲットには対象のprotectedサブネットに配置したEC2インスタンスが指定されているので問題なさそう(EC2インスタンス接続の際に80ポートは使わないので削除しました)

f:id:TechHotoke:20220110121600p:plain

リスナーのポートを確認すると。。。8080が登録されていなかったので、8080を以下のように追加。

f:id:TechHotoke:20220110121156p:plain

ELBのDNS名:ポート/アプリケーションのルートパスでアクセスすると、アクセスできました!

f:id:TechHotoke:20220110121637p:plain

今回はここまでとなります。

お付き合い頂きありがとうございます。 次回はDBをRDSに移行していきます。