【未経験向け】CCNA(200-301)とは? NW未経験が1ヶ月で合格した際に使用した魔法の学習教材、サイトを紹介!!!

【AWS】PrivateLinkをCloudFormationで構築する

AWS

職場でPrivateLinkという単語を聞き、「PrivateLinkってエンドポイント貼るやつっすよね!」って発言したところ、そんな単純な概念ではないと冷静に返されました。。

考えてみたら、今までエンドポイントを強く意識して環境構築をしたことって無かったので、改めてエンドポイントを意識したハンズオンでもやってみるかと本記事を執筆した次第です。

スポンサーリンク

概要

本記事の目的

  1. PrivateLinkを利用したプライベートなWeb環境を構築する。
    • 環境構築にはCloudFormationを使用する。
  2. クライアントクラスターのEC2からプロパイダークラスターのEC2(Webサーバ)に対して、プライベートリンクを経由したcurlコマンドによる疎通確認を行う。

インフラ構成図

  • 本記事で構築するインフラ構成を下図の通りとする。

構築リソース

  • プロパイダークラスター(アクセス先)
    • ネットワーク環境を構築(VPC,Subnet,Routetable)
    • サーバ環境を構築(SG,EC2,NLB,EndPointService)
  • クライアントクラスター(アクセス元)
    • ネットワーク環境を構築(VPC,Subnet,Routetable)
    • サーバ環境を構築(SG,EC2,EndPoint)

PrivateLinkについて

PrivateLinkとは?

PrvateLinkとは、分かりやすく言うと「AWSへのAPIアクセスをインターネットを経由せずに行えるインターフェースタイプのVPCエンドポイント」です。

EC2からAWS CLIを使用してAWSのAPIを叩く際、APIのリクエストのトラフィックはインターネットを通ります。そのため、インターネットゲートウェイがないとインターネットに出られず、APIが叩けない事になります。
しかし、PrivateLinkを使えば、インターネットに出ずにEC2のAPIを叩くことができるようになります。

どういう時に使うの?

インターネットを経由せずにAWSのAPIを叩くリソースを手軽に構築する際に利用します。

PrivateLinkの実態はエンドポイントなので、VPCピアリングのようにNWセグメント同士で繋げるわけではないので、余計な通信経路が発生することはありません。

ハンズオン

プロバイダークラスターの構築

以下のCFnテンプレートを使用し、環境構築を行います。

  • CloudFormationテンプレート
    • network.yml(ネットワーク環境を構築)
    • server.yml(サーバ環境を構築)

CloudFormationテンプレート

AWSTemplateFormatVersion: "2010-09-09"
Description: "Create Provider-Network-Env to PrivateLink-PrivateWeb-dev-Env"
    
# ------------------------------------------------------------
#  Metadate
# ------------------------------------------------------------
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Parameters:
          - PJPrefix
          - VPCCIDR
          - PrivateSubnetCIDR
          
          
# ------------------------------------------------------------
# Input Parameters
# ------------------------------------------------------------
Parameters:
### Project Prefix ###
  PJPrefix:
    Type: String
### VPC ###
  VPCCIDR:
    Type: String
    Default: "192.168.0.0/20"
### Private Subnet ###
  PrivateSubnetCIDR:
    Type: String
    Default: "192.168.1.0/24"
    
    
### Resources ###
Resources: 
# ------------------------------------------------------------
# VPC
# ------------------------------------------------------------
  VPC: 
    Type: "AWS::EC2::VPC"
    Properties: 
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: "true"
      EnableDnsHostnames: "true"
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-Provider-VPC"
          
          
# ------------------------------------------------------------
# Subnet
# ------------------------------------------------------------
### Private Subnet ###
  PrivateSubnet: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PrivateSubnetCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-Provider-PrivateSubnet"
          
          
# ------------------------------------------------------------
# RouteTable
# ------------------------------------------------------------
### Privatec Subnet Routing ###
  PrivateRTB: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-Provider-PrivateRoute"

  PrivateSubnetRTBAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PrivateSubnet 
      RouteTableId: !Ref PrivateRTB
      
      
# ------------------------------------------------------------
# Output Parameter
# ------------------------------------------------------------
Outputs:
### VPC ###
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${PJPrefix}-Provider-VPC"
      
### Private Subnet ###
  PrivateSubnet:
    Value: !Ref PrivateSubnet
    Export:
      Name: !Sub "${PJPrefix}-Provider-PrivateSubnet"
AWSTemplateFormatVersion: "2010-09-09"
Description: "Create Provider-Server-Env to PrivateLink-PrivateWeb-dev-Env"
    
# ------------------------------------------------------------
#  Metadate
# ------------------------------------------------------------
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Parameters:
          - PJPrefix
          - EC2AMI
          
          
# ------------------------------------------------------------
# Input Parameters
# ------------------------------------------------------------
Parameters:
### Project Prefix ###
  PJPrefix:
    Type: 'String'
  EC2AMI:
    Type: 'String'
  InstanceType:
    Type: 'String'
    
    
### Resources ###
Resources:
# ------------------------------------------------------------
# SecurityGroup
# ------------------------------------------------------------
### WebServer Security Group ###
  WebSG:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "http"
      GroupName: !Sub "${PJPrefix}-Web-SG"
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-Provider-VPC" }
      Tags: 
         - Key: Name
           Value: !Sub "${PJPrefix}-Provider-Web-SG"
      SecurityGroupIngress:
         - IpProtocol: tcp
           FromPort : 80
           ToPort : 80
           CidrIp: 0.0.0.0/0
                
                
# ------------------------------------------------------------
# EC2
# ------------------------------------------------------------
### WebServer ###
  WebServerA:
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: !Ref EC2AMI
      InstanceType: !Ref InstanceType 
      NetworkInterfaces: 
        - AssociatePublicIpAddress: "false"
          DeviceIndex: "0"
          SubnetId: { "Fn::ImportValue": !Sub "${PJPrefix}-Provider-PrivateSubnet" }
          GroupSet:
            - !Ref WebSG
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-Provider-WebServerA"
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          sudo yum -y update
          sudo yum -y install httpd
          sudo systemctl start httpd
          sudo systemctl enable httpd
          sudo echo "chibiharu's Qiita Apache Test Page For Success" > /var/www/html/index.html
          
            
# ------------------------------------------------------------
# TargetGroup
# ------------------------------------------------------------
  TargetGroup: 
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties: 
      VpcId:  { "Fn::ImportValue": !Sub "${PJPrefix}-Provider-VPC" }
      Name: !Sub "${PJPrefix}-Provider-TG"
      Protocol: TCP
      Port: 80
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-Provider-TG"
      Targets: 
        - Id: !Ref WebServerA
          Port: 80
          
          
# ------------------------------------------------------------
# NetworkLoadBalancer
# ------------------------------------------------------------
  NLB: 
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties: 
      Name: !Sub "${PJPrefix}-Provider-NLB"
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-Provider-NLB"
      Scheme: "internal"
      LoadBalancerAttributes: 
        - Key: "deletion_protection.enabled"
          Value: false
      Subnets: 
        -  { "Fn::ImportValue": !Sub "${PJPrefix}-Provider-PrivateSubnet" }
      Type: network
      
  NLBListenerHTTP: 
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties: 
      DefaultActions: 
        - TargetGroupArn: !Ref TargetGroup
          Type: forward
      LoadBalancerArn: !Ref NLB
      Port: 80
      Protocol: TCP
      
      
# ------------------------------------------------------------
# EndPointService
# ------------------------------------------------------------
  VPCEndpointService:
    Type: AWS::EC2::VPCEndpointService
    Properties: 
      AcceptanceRequired: true
      NetworkLoadBalancerArns:
        - !Ref NLB
          
          
# ------------------------------------------------------------
# Output Parameters
# ------------------------------------------------------------
Outputs:
  VPCEndpointService:
    Value: { "Fn::Sub": "com.amazonaws.vpce.${AWS::Region}.${VPCEndpointService}" }
    Export:
      Name: !Sub "${PJPrefix}-VPCEndpointService"

クライアントクラスターの構築

以下のCFnテンプレートを使用し、環境構築を行います。

  • CloudFormationテンプレート
    • network.yml(ネットワーク環境を構築)
    • server.yml(サーバ環境を構築)

CloudFormationテンプレート

AWSTemplateFormatVersion: "2010-09-09"
Description: "Create Client-Network-Env to PrivateLink-PrivateWeb-dev-Env"
    
# ------------------------------------------------------------
#  Metadate
# ------------------------------------------------------------
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Parameters:
          - PJPrefix
          - VPCCIDR
          - PrivateSubnetCIDR
          
          
# ------------------------------------------------------------
# Input Parameters
# ------------------------------------------------------------
Parameters:
### Project Prefix ###
  PJPrefix:
    Type: String
### VPC ###
  VPCCIDR:
    Type: String
    Default: "192.168.0.0/20"
### Private Subnet ###
  PrivateSubnetCIDR:
    Type: String
    Default: "192.168.1.0/24"
    
    
### Resources ###
Resources: 
# ------------------------------------------------------------
# VPC
# ------------------------------------------------------------
  VPC: 
    Type: "AWS::EC2::VPC"
    Properties: 
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: "true"
      EnableDnsHostnames: "true"
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-Client-VPC"
      
      
# ------------------------------------------------------------
# Subnet
# ------------------------------------------------------------
### Private Subnet ###
  PrivateSubnet: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PrivateSubnetCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-Client-PrivateSubnet"
          
          
# ------------------------------------------------------------
# RouteTable
# ------------------------------------------------------------
### Privatec Subnet Routing ###
  PrivateRTB: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-Client-PrivateRoute"

  PrivateSubnetRTBAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PrivateSubnet 
      RouteTableId: !Ref PrivateRTB
      
      
# ------------------------------------------------------------
# Output Parameter
# ------------------------------------------------------------
Outputs:
### VPC ###
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${PJPrefix}-Client-VPC"
      
### Private Subnet ###
  PrivateSubnet:
    Value: !Ref PrivateSubnet
    Export:
      Name: !Sub "${PJPrefix}-Client-PrivateSubnet"
AWSTemplateFormatVersion: "2010-09-09"
Description: "Create Client-Server-Env to PrivateLink-PrivateWeb-dev-Env"
    
# ------------------------------------------------------------
#  Metadate
# ------------------------------------------------------------
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Parameters:
          - PJPrefix
          - EC2AMI
          - InstanceType
          - KeyName
          
          
# ------------------------------------------------------------
# Input Parameters
# ------------------------------------------------------------
Parameters:
### Project Prefix ###
  PJPrefix:
    Type: 'String'
  EC2AMI:
    Type: 'String'
  InstanceType:
    Type: 'String'
  KeyName:
    Type: 'AWS::EC2::KeyPair::KeyName'
    
    
### Resources ###
Resources:
# ------------------------------------------------------------
# SecurityGroup
# ------------------------------------------------------------
### WebServer Security Group ###
  ClientSG:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "http"
      GroupName: !Sub "${PJPrefix}-Client-SG"
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-Client-VPC" }
      Tags: 
         - Key: Name
           Value: !Sub "${PJPrefix}-Client-SG"
      SecurityGroupIngress:
         - IpProtocol: tcp
           FromPort : 22
           ToPort : 22
           CidrIp: 0.0.0.0/0
                
                
# ------------------------------------------------------------
# EC2
# ------------------------------------------------------------
### ClientServer ###
  ClientServer:
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: !Ref EC2AMI
      InstanceType: !Ref InstanceType 
      KeyName: !Ref KeyName
      NetworkInterfaces: 
        - AssociatePublicIpAddress: "false"
          DeviceIndex: "0"
          SubnetId: { "Fn::ImportValue": !Sub "${PJPrefix}-Client-PrivateSubnet" }
          GroupSet:
            - !Ref ClientSG
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-ClientServer"
            
            
# ------------------------------------------------------------
# EndPoint
# ------------------------------------------------------------
  VpcEndpoint:
    Type: "AWS::EC2::VPCEndpoint"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-Client-VPC" }
      ServiceName: { "Fn::ImportValue": !Sub "${PJPrefix}-VPCEndpointService" }
      VpcEndpointType: "Interface"
      SubnetIds: 
         - { "Fn::ImportValue": !Sub "${PJPrefix}-Client-PrivateSubnet" }
      SecurityGroupIds:
        - !Ref ClientSG

動作確認

動作確認にはcurlコマンドを使用します。
ClientServerからClient側で作成したエンドポイントのDNS名宛にcurlコマンドを実行し、Provider側への接続試験を行います。

エンドポイント経由での接続試験

ClientServerのコンソール上へ接続します。
以下のコマンドを実行し、WebServerのテストページが返ってくることを確認します。

# Client側で作成したエンドポイントのDNS名宛にcurlコマンドを実行
$ curl vpce-xxxxxxxxxxx.vpce-svc-xxxxxxxxxxxxxxx.ap-northeast-1.vpce.amazonaws.com 
chibiharu's Qiita Apache Test Page For Success WebServerA

まとめ

PrivateLinkは非常に便利で手軽に構築できる反面、作り過ぎてPrivateLink(VPC EndPointとNLB)まみれになってしまうことがあります。

ご利用は計画的に..ってね。

コメント

タイトルとURLをコピーしました