HA構成プロキシ環境を NLBを使って構築してみる

2019.11.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

前回のブログ では プロキシサーバー シングル構成の検証環境を構築しました。

今回は 高可用性(HA)を目的としたプロキシサーバー冗長化 を検証してみます。 想定する本番環境は下記の通り。

そもそもプロキシサーバーを使用している理由 などは前回のブログを参照ください

目次

  1. 環境構築
    1. NLB
    2. セキュリティグループ
    3. プロキシサーバーのAMI
    4. Auto Scaling Group
  2. 検証
    1. 接続・ステータス確認
    2. インスタンスを止めてみる
  3. おわりに

環境構築

検証環境は以下のとおりです。

  • 2つの Availability Zone(AZ) にそれぞれサブネットを作成
  • プロキシサーバーを 2サブネットに配置する Auto Scaling Group を作成
  • Network Load Balancer (NLB) を使って ターゲットを分散

APPサーバーから NLB → Proxyサーバー経由で Serviceを利用できるか確かめてみます。 構築は勉強も兼ねて CloudFormation(CFn)で行います。

github に今回作成した CFnテンプレートを上げています。

  • 00-network.yaml: ネットワークリソース作成用
  • 01-sg.yaml: セキュリティグループ作成用
  • 02-nlb.yaml: NLB 作成用
  • 03-as.yaml: Auto Scaling Group 作成用
  • 04-ec2-service.yaml: サービスサーバー (Linux) 作成用
  • 05-ec2-app.yaml: アプリサーバー (Windows) 作成用

以降、Proxy-VPC内の システム周りを中心に説明します。

NLB

下記 3リソースを作成します。

▼— LoadBalancer ---

Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties: 
  Name: !Sub ${Prefix}-nlb
  Type: network
  Scheme: internal
  IpAddressType: ipv4
  Subnets: 
    - Fn::ImportValue: !Sub ${Prefix}-subnet2-a
    - Fn::ImportValue: !Sub ${Prefix}-subnet2-c
  • NLBを作成するので Type: network とします
  • 内部向けを想定しているので Scheme: internal とします
  • NLBを 2サブネットに設置するための設定を Subnets: に記載します

▼— TargetGroup ---

Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties: 
  Name: !Sub ${Prefix}-nlb-tg
  TargetType: instance
  Protocol: TCP
  Port: 3128 # squid
  VpcId:
    Fn::ImportValue: !Sub ${Prefix}-vpc2

後ほどこのターゲットグループが Auto Scaling Group と関連付けられます。 NLBは ターゲットグループに対して Protocol: TCPPort: 3128 (squidで使うポート番号) のリクエストを送信します。

TargetType:instance もしくは ip が選択できます。今回は instance を選択しますが、 留意点は TargetType: によって NLBから送られる パケットの送信元IPアドレス が変わる ことです。

インスタンス ID を使用してターゲットを指定すると、 クライアントの送信元 IP アドレス が保持 され、アプリケーションに提供されます。

ターゲットを IP アドレスで指定する場合、送信元 IP アドレスは ロードーバランサノードのプライベート IP アドレス となります。

(引用: AWS: Network Load Balancer のターゲットグループ)

▼— Listener ---

Type: AWS::ElasticLoadBalancingV2::Listener
Properties: 
  Port: 3128
  Protocol: TCP
  DefaultActions: 
    - Type: forward
      TargetGroupArn: !Ref NLBTargetGroup
  LoadBalancerArn: !Ref NLB
  • クライアントからのリクエストを Port: 3128Protocol: TCP で受け付けます
  • DefaultActions: でアクションを設定します。 リクエストを指定した TargetGroup へルーティング ( forward ) します。

セキュリティグループ

NLB構成としたことでプロキシサーバーに設定するセキュリティグループの設定内容が少々変わってきます。

Type: AWS::EC2::SecurityGroup
Properties: 
  GroupDescription: SG for Proxy Server
  GroupName: !Sub ${Prefix}-proxy-sg
  VpcId:
    Fn::ImportValue: !Sub ${Prefix}-vpc2
  SecurityGroupIngress:
    - IpProtocol: tcp
      FromPort: 3128
      ToPort: 3128
      CidrIp: 10.0.0.0/24 # from Proxy-VPC (for healthcheck)
    - IpProtocol: tcp
      FromPort: 3128
      ToPort: 3128
      CidrIp: 192.168.0.0/24 # from APP-VPC
    # - IpProtocol: tcp
    #   FromPort: 3128
    #   ToPort: 3128
    #   SourceSecurityGroupId: !Ref AppSG

コメントアウトしている部分が前回の シングル構成時のルール です (クライアントSGからの ポート3128を許可)。

今回は NLB経由の接続ですが、 NLBは セキュリティグループを持ちません 。 また、 TargetType: instance としているため、 NLBで送信元IPをクライアントのIPに置き換えています

そのため、 CIDR形式で 以下の通信を許可する必要があります。

  • NLBを配置しているネットワークCIDR からの通信 (ヘルスチェックのため)
  • クライアントを配置しているネットワークCIDR からの通信 (メインの通信のため)

プロキシサーバーのAMI

Auto Scaling で使用する AMIを事前に作成します。

Amazon Linux2 EC2インスタンスを起動して squid のインストール・自動起動の設定をします。 設定ファイル /etc/squid/squid.conf の修正内容としては大きく 2つ。

acl localnet src 192.168.0.0/24	# VPC Network (Application side)
acl localnet src 10.0.0.0/24	# VPC Network (Proxy side: for NLB health check)
...
# disable cache
acl NOCACHE src all
cache deny NOCACHE

前 2行で アクセス制御の設定、後ろ2行でキャッシュの無効化を行っています ( 参考: Qiita: Squidのキャッシュ機能を無効化する )。

設定後、AMIを作成します。

Auto Scaling Group

下記 2リソースを作成します。

▼— Launch Configuration ---

Type: AWS::AutoScaling::LaunchConfiguration
Properties:
  LaunchConfigurationName: !Sub ${Prefix}-launch-configuration
  ImageId: !Ref ProxyServerImageId
  InstanceType: t3.micro
  SecurityGroups: 
    - Fn::ImportValue: !Sub ${Prefix}-proxy-sg
  BlockDeviceMappings: 
    - DeviceName: /dev/xvda
      Ebs:
        DeleteOnTermination: True
        VolumeType: gp2
        VolumeSize: 8
  KeyName: !Ref Ec2KeyName
  # ... 略

ImageId: に事前に作成したAMIの IDを入れます。 他は EC2インスタンスを作成するときと同じように設定していきます。

▼— Auto Scaling Group ---

Type: AWS::AutoScaling::AutoScalingGroup
Properties: 
  AutoScalingGroupName: !Sub ${Prefix}-as-group
  LaunchConfigurationName: !Ref LaunchConfiguration
  DesiredCapacity: 2
  MaxSize: 2
  MinSize: 2
  VPCZoneIdentifier:
    - Fn::ImportValue: !Sub ${Prefix}-subnet2-a
    - Fn::ImportValue: !Sub ${Prefix}-subnet2-c
  TargetGroupARNs: 
    - Fn::ImportValue: !Sub ${Prefix}-nlb-tg
  HealthCheckType: ELB
  # ... 略
  • LaunchConfigurationName: に前述の LaunchConfigurationを指定します。
  • 常に 2台を維持する設定とするため、 DesiredCapacity:MaxSize:MinSize:2 とします。
  • TargetGroupARNs: に NLBの TargetGroup を指定します

検証

接続・ステータス確認

APPサーバーにログインして、Service-VPCにある サービスを利用できるか検証します。 (Serviceの EC2インスタンスには httpd を入れておきます)

※APPサーバーのプロキシ設定は以下の通り。

APPサーバーにログインして、ServiceのローカルIPにアクセスしてみます。

Serviceの httpd ログを見てみます。APPサーバーを 2台立てて交互にアクセスしてみました。 2台のプロキシサーバーからアクセスされていることが確認できます。

ターゲットグループを見てみます。 2台ともヘルスチェックに合格しています。

Auto Scaling Group のステータスは以下の通り。

インスタンスを止めてみる

1つの プロキシサーバーを止めてみます。

止めたインスタンスが Unhealthy となりました。

ライフサイクルが 終了中 に変わります。

新しい命が作られそうです。

無事に入れ替わりました。

Service のログにも新しくできたプロキシサーバーからのアクセスが確認できます。

(参考) 一連のアクティビティ履歴は以下の通り。

おわりに

HA構成プロキシ環境を NLBを使って構築してみました。 NLB, Auto Scaling 周りの 動作は勉強はしていたものの実際に見てはいなかったので、 確認できてよかったです。 CFnテンプレートを書く筋肉も付いてきた気がします。

今後は下記 2点あたりを考えて、まとめていけたらなと思っています。

  • NLB, Auto Scaling のパラメータチューニング (今回はほとんどデフォルト)
  • 運用・ログ監視周り

(追記) 通信要件によっては、そもそも ALB(IPターゲット) をリバースプロキシとして使えば実装できるみたいです。そちらの検証を今後行ってみます。