1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【AWS】CloudFormationでWebサービスを構築①(NW編)

Last updated at Posted at 2022-01-16

#1. はじめに
最近はインフラ構築もコードで行われるようになりました。
その一例がAWSの提供するリソースプロビジョニングサービス「CloudFormation(以下、CFnと表す)」です。
今回はCFnを使用して、一般的な2階層Webサービスを構築しようと思います。

また、CFnに関する記事では、マネコン上でビルドする記事が多いですが、CFnの本質はIaC、つまりはコードでのインフラ構築ですので、本記事ではテンプレートのビルドはIDE上(Cloud9)でAWS CLIを使用して行うものとします。

記事は以下3編に分けて公開します。
①【AWS】CloudFormationでWebサービスを構築①(NW編)←本記事
②【AWS】CloudFormationでWebサービスを構築②(EC2編)
③【AWS】CloudFormationでWebサービスを構築③(RDS編)

#2. 概要
本記事の概要を以下に記載します。

##2.1 本記事の目的
CFnを利用し、IDE上(Cloud9)からWebサーバ環境を構築する。

##2.2 本記事の流れ
① NW環境、及びセキュリティテンプレートの作成 ⇒ ビルド
② サーバテンプレートの作成 ⇒ ビルド
③ データベーステンプレートの作成 ⇒ ビルド
④ 動作確認

##2.3 機能要件
・テンプレートの形式は『yaml』を採用する
・テンプレートファイルは各ロール毎に分割し、クロススタック方式を採用する
・作成するテンプレートを以下の通りとする
 ・「network.yml」
 ・「security.yml」
 ・「server.yml」
 ・「database.yml」
・リソース作成からビルドまでを全てコマンドライン上で行う
・IDEはCloud9を使用する ※皆さんはVSCode等のお好きなIDEを使用して下さい
・完了要件を以下2点とする
 ・Webサーバのテストページの閲覧
 ・RDSで作成したDBレコードのブラウザ閲覧

##2.4 構成図
chibiharu-cfn-qiita-web-env.png

#3. 事前準備
事前準備として、以下の準備をお願いします。
・IDE(本記事ではCloud9を使用します)
・AWS CLIが使用可能であること
・独自ドメインを取得していること ※投稿主は「お名前.com」にてドメインを取得しました

また、テンプレートファイルの保管、及びビルド用のディレクトリ(以下、「作業用ディレクトリ」を表す)を任意の場所に作成してください。
ディレクトリの作成コマンドは以下の通りとなります。

作業ディレクトリの作成
$ mkdir cfn-dev-dir
$ cd cfn-dev-dir

以降、作成するテンプレートは全てこの作業用ディレクトリの直下に保存して下さい。

#4. NW編の概要
今回の『【AWS】CloudFormationでWebサーバを構築①(NW編)』では、Web環境周りのNW部分のテンプレートを作成します。
具体的なサービスとしては以下の通りとなります。

##4.1 「network.yml」で登場するサービス
・Amazon VPC
・Internet Gateway
・VPC Subnet
・VPC RouteTable

##4.2 「security.yml」で登場するサービス
・SecurityGroup

##4.3 NW編構成図
NW編で作成するテンプレート環境の構成図を下図の通りとします。
chibiharu-cfn-qiita-web-env.png

#5. NWテンプレートの作成
早速ですが、以下がNW部分のテンプレートになります。
命名規則等、必要であればご自身の環境に合わせて修正してください。
修正が完了したら、作業用ディレクトリに「network.yml」というファイル名で保存してください。

network.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: Web2tier-Network-Template
    

# ------------------------------------------------------------
# Input Parameters
# ------------------------------------------------------------
Parameters:
### Project Prefix ###
  PJPrefix:
    Type: String
### VPC ###
  VPCCIDR:
    Type: String
    Default: "192.168.0.0/20"
### Public Subnet ###
  PublicSubnetACIDR:
    Type: String
    Default: "192.168.1.0/24"
  PublicSubnetCCIDR:
    Type: String
    Default: "192.168.2.0/24"
### Private Subnet ###
  PrivateSubnetACIDR:
    Type: String
    Default: "192.168.3.0/24"
  PrivateSubnetCCIDR:
    Type: String
    Default: "192.168.4.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}-vpc"
          
          
# ------------------------------------------------------------
# InternetGateway:
# ------------------------------------------------------------
  InternetGateway: 
    Type: "AWS::EC2::InternetGateway"
    Properties: 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-igw"
          
  IGWAttachment: 
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties: 
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC
      
      
# ------------------------------------------------------------
# NatGateway
# ------------------------------------------------------------
### NAT Gateway ###
  NatGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
          - NatGatewayEIP
          - AllocationId
      SubnetId: !Ref PublicSubnetA
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-NAT-Gateway"
          
### NAT Gateway EIP ###
  NatGatewayEIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-NGW-EIP" 
          
          
# ------------------------------------------------------------
# Subnet
# ------------------------------------------------------------
### Public Subnet ###
  PublicSubnetA: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PublicSubnetACIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-public-subnet-a"
          
  PublicSubnetC: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PublicSubnetCCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-public-subnet-c"

### Private Subnet ###
  PrivateSubnetA: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PrivateSubnetACIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-private-subnet-a"
  
  PrivateSubnetC: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PrivateSubnetCCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-private-subnet-c"
          
          
# ------------------------------------------------------------
# RouteTable
# ------------------------------------------------------------
### Public Subnet A Routing ###
  PublicARTB: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-public-route-a"

  PublicASubnetRTBAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PublicSubnetA 
      RouteTableId: !Ref PublicARTB

  PublicARoute: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PublicARTB
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway
      
### Public Subnet C Routing ###
  PublicCRTB: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-public-route-c"

  PublicCSubnetRTBAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PublicSubnetC
      RouteTableId: !Ref PublicCRTB

  PublicCRoute: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PublicCRTB
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway 

### Private Subnet A Routing ###
  PrivateARTB: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-private-route-a"

  PrivateSubnetRTBAAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PrivateSubnetA
      RouteTableId: !Ref PrivateARTB
      
  PrivateRouteA01: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PrivateARTB
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref NatGateway
  
### Private Subnet C Routing ###
  PrivateCRTB: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-private-route-c"
          
  PrivateSubnetRTBCAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PrivateSubnetC
      RouteTableId: !Ref PrivateCRTB
      
  PrivateRouteC01: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PrivateCRTB
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref NatGateway
      
      
# ------------------------------------------------------------
# Output Parameter
# ------------------------------------------------------------
Outputs:
### VPC ###
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${PJPrefix}-vpc"
      
### Subnet ###
## Public Subnet A ##
  PublicSubnetA:
    Value: !Ref PublicSubnetA
    Export:
      Name: !Sub "${PJPrefix}-public-subnet-a"
## Public Subnet C ##
  PublicSubnetC:
    Value: !Ref PublicSubnetC
    Export:
      Name: !Sub "${PJPrefix}-public-subnet-c"
## Private Subnet A ##
  PrivateSubnetA:
    Value: !Ref PrivateSubnetA
    Export:
      Name: !Sub "${PJPrefix}-private-subnet-a"
## Private Subnet C ##
  PrivateSubnetC:
    Value: !Ref PrivateSubnetC
    Export:
      Name: !Sub "${PJPrefix}-private-subnet-c"

##5.1 NWテンプレートのビルド
では作成したCFnのビルドを行います。
概要でも説明しましたが、ビルドはAWS CLIコマンドを使用して行います。
投稿主はCloud9を使用しているので、Cloud9上でBashを開き、そこでビルドを行っていきます。

AWS CLIを使用してのCFnテンプレートのビルド方法は非常に単純で、ワンライナーで完結します。
以下コマンドを実行してください。
※ スタック名やプレフィックス、また追加で指定したいパラメータ等があればご自身の環境に合わせ修正して下さい。

NWテンプレートのビルドコマンド
$ aws cloudformation create-stack \
--stack-name web2tier-Network-stack \
--template-body file://./network.yml \
--parameters ParameterKey=PJPrefix,ParameterValue=web2tier

↓ 構文エラーが無ければ、以下のような結果出力がされます。

実行結果
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxx:stack/<スタックネーム>/<メタ番号>"
}

#6. セキュリティテンプレートの作成
以下はSecurity部分のテンプレートになります。
こちらも命名規則等、必要であればご自身の環境に合わせて修正してください。
修正が完了したら、作業用ディレクトリに「security.yml」というファイル名で保存してください。

security.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: Web2tier-Security-Template


# ------------------------------------------------------------
# Input Parameters
# ------------------------------------------------------------
Parameters:
### Project Prefix ###
  PJPrefix:
    Type: String
    

### Resources ###
Resources:
# ------------------------------------------------------------
# SecurityGroup
# ------------------------------------------------------------
### ALB Server Security Group ###
    ALBSG:
        Type: "AWS::EC2::SecurityGroup"
        Properties:
            GroupDescription: "https"
            GroupName: cfn-dev-alb-sg
            VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
            Tags: 
                - Key: Name
                  Value: !Sub "${PJPrefix}-alb-sg"
            SecurityGroupIngress:
                IpProtocol: tcp
                FromPort : 443
                ToPort : 443
                CidrIp: 0.0.0.0/0
                
### Web Server Security Group ###
    WebSG:
        Type: "AWS::EC2::SecurityGroup"
        Properties:
            GroupDescription: "http"
            GroupName: cfn-dev-web-sg
            VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
            Tags: 
                - Key: Name
                  Value: !Sub "${PJPrefix}-web-sg"
            SecurityGroupIngress:
                IpProtocol: tcp
                FromPort : 80
                ToPort : 80
                SourceSecurityGroupId: !Ref ALBSG
                
### RDS Security Group ###            
    RDSSG:
        Type: "AWS::EC2::SecurityGroup"
        Properties:
            GroupDescription: "mysql"
            GroupName: cfn-dev-rds-sg
            VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
            Tags: 
                - Key: Name
                  Value: !Sub "${PJPrefix}-RDS-sg"
            SecurityGroupIngress:
                IpProtocol: tcp
                FromPort : 3306
                ToPort : 3306
                SourceSecurityGroupId: !Ref WebSG
                
                
# ------------------------------------------------------------
# Output Parameter
# ------------------------------------------------------------
Outputs:
### SecurityGroup ###
    ALBSG:
        Value: !Ref ALBSG
        Export:
            Name: !Sub "${PJPrefix}-alb-sg"
    WebSG:
        Value: !Ref WebSG
        Export:
            Name: !Sub "${PJPrefix}-web-sg"
    RDSSG:
        Value: !Ref RDSSG
        Export:
            Name: !Sub "${PJPrefix}-rds-sg"

##6.1 NWテンプレートのビルド
ではNWと同じように作成したCFnテンプレートのビルドを行います。

以下コマンドを実行してください。
※ スタック名やプレフィックス、また追加で指定したいパラメータ等があればご自身の環境に合わせ修正して下さい。

セキュリティテンプレートのビルドコマンド
$ aws cloudformation create-stack \
--stack-name web2tier-Security-stack \
--template-body file://./security.yml \
--parameters ParameterKey=PJPrefix,ParameterValue=web2tier

↓ 構文エラーが無ければ、以下のような結果出力がされます。

実行結果
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxx:stack/<スタックネーム>/<メタ番号>"
}

#7. まとめ
本記事は以上になります。
次回はサーバー部分のテンプレート作成「【AWS】CloudFormationでWebサーバを構築②(EC2編)」になります。

やはりCUIよりGUIの方が直感的に分かりやすく、ミスオペも無く簡単ですが、慣れればCUIのが簡易にインフラ構築が可能になります。

また、コード化することによって、GitHub等のバージョン管理システム上でコード管理を行えるようになり、大規模なインフラ構築における可視性の向上、及び迅速なプロビジョニングが可能になります。

本記事を足がかりに、是非この際にIaCに挑戦して貰えればと思います。

--- 記事一覧 ---
①【AWS】CloudFormationでWebサービスを構築①(NW編)
②【AWS】CloudFormationでWebサービスを構築②(EC2編)
③【AWS】CloudFormationでWebサービスを構築③(RDS編)

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?