APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

【初心者向け】AWS CloudFormationでWeb3層構造のWordPress環境を構築(Session Manager接続可)

目次

はじめに

こんにちは。クラウド事業部の西川です。

今回はELB(ALB)とEC2、RDSを使用したWeb3層構造のWordPress環境のCloudFormationテンプレートとデプロイ方法をご紹介します。

EC2インスタンスへはSession Managerで接続できるようにしています。

構成図

テンプレート

AWSTemplateFormatVersion: '2010-09-09'

Description:
  Single-AZ WordPress Blog Site

Parameters:
  DatabaseMasterName:
    Description: Database Master User Name
    Type : String
    Default: admin
  DatabaseMasterPassword:
    Description: Database Master User Password
    Type : String
  DatabaseName:
    Description: Database Name
    Type : String
    Default: wordpress
  EngineVersion:
    Description: Engine Version
    Type : String
    Default: 8.0.35
  EC2AMIId:
    Description: AMI ID
    Type : String
    Default: ami-0d0150aa305b7226d
  EnvironmentName:
    Type: String
    Default: Test-CFn-WP
  KeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: "AWS::EC2::KeyPair::KeyName"

Resources:
  # ------------------------------------------------------------#
  #  VPC
  # ------------------------------------------------------------#
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      InstanceTenancy: default
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
      - Key: Name
        Value: !Sub ${EnvironmentName}-VPC

  # ------------------------------------------------------------#
  #  Internet Gateway
  # ------------------------------------------------------------#
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: !Sub ${EnvironmentName}-VPC-IGW
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  # ------------------------------------------------------------#
  #  Route Table
  # ------------------------------------------------------------#
  RouteTable:
    Type: AWS::EC2::RouteTable
    DependsOn: AttachGateway
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${EnvironmentName}-VPC-RTB
  Route:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  # ------------------------------------------------------------#
  #  Public Sunbet A
  # ------------------------------------------------------------#
  PublicSubnetA:
    Type: AWS::EC2::Subnet
    DependsOn: AttachGateway
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: 'true'
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${EnvironmentName}-VPC-PublicSubnet-A
  PublicRouteTableAssociationA:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetA
      RouteTableId: !Ref RouteTable

  # ------------------------------------------------------------#
  #  Private Sunbet A
  # ------------------------------------------------------------#
  PrivateSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: 10.0.2.0/24
      MapPublicIpOnLaunch: 'false'
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${EnvironmentName}-VPC-PrivateSubnet-A

  # ------------------------------------------------------------#
  #  Public Sunbet C
  # ------------------------------------------------------------#
  PublicSubnetC:
    Type: AWS::EC2::Subnet
    DependsOn: AttachGateway
    Properties:
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: 10.0.3.0/24
      MapPublicIpOnLaunch: 'true'
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${EnvironmentName}-VPC-PublicSubnet-C
  PublicRouteTableAssociationC:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetC
      RouteTableId: !Ref RouteTable

  # ------------------------------------------------------------#
  #  Private Sunbet C
  # ------------------------------------------------------------#
  PrivateSubnetC:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: 10.0.4.0/24
      MapPublicIpOnLaunch: 'false'
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${EnvironmentName}-VPC-PrivateSubnet-C

  # ------------------------------------------------------------#
  #  ALB Security Group
  # ------------------------------------------------------------#
  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub ${EnvironmentName}-ALB-SG
      GroupDescription: Allow HTTP and HTTPS access from internet
      VpcId: !Ref VPC
      SecurityGroupIngress:
        # http
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"
        # https
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: "0.0.0.0/0"
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-ALB-SG

  # ------------------------------------------------------------#
  #  ALB
  # ------------------------------------------------------------#
  ApplicationLoadBalancer:
    Type : AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub ${EnvironmentName}-ALB
      Scheme: "internet-facing"
      SecurityGroups:
        - !Ref ALBSecurityGroup
      Subnets:
        - !Ref PublicSubnetA
        - !Ref PublicSubnetC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-ALB

  # ------------------------------------------------------------#
  #  Target Group
  # ------------------------------------------------------------#
  ALBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckPath: /healths/wp-check
      HealthCheckEnabled: True
      Name: !Sub ${EnvironmentName}-ALB-TG
      Port: 80
      Protocol: HTTP
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-ALB-TG
      Targets:
        - Id: !Ref EC2Instance
          Port: 80
      VpcId: !Ref VPC

  # ------------------------------------------------------------#
  #  ALB Listner
  # ------------------------------------------------------------#
  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref ALBTargetGroup
          Type: forward
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: 80
      Protocol: HTTP

  # ------------------------------------------------------------#
  #  EC2 Security Group
  # ------------------------------------------------------------#
  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    DependsOn: ALBSecurityGroup
    Properties:
      GroupName: !Sub ${EnvironmentName}-EC2-SG
      GroupDescription: Allow HTTP and HTTPS from ALBSecurityGroup
      VpcId: !Ref VPC
      SecurityGroupIngress:
        # HTTP
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref ALBSecurityGroup
        # HTTPS
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          SourceSecurityGroupId: !Ref ALBSecurityGroup
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-EC2-SG

  # ------------------------------------------------------------#
  #  EC2 Role for SSM
  # ------------------------------------------------------------#
  EC2RoleforSSM:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${EnvironmentName}-SSM-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

  # ------------------------------------------------------------#
  #  EC2 Instance Profile
  # ------------------------------------------------------------#
  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref EC2RoleforSSM

  # ------------------------------------------------------------#
  #  EC2
  # ------------------------------------------------------------#
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize: 8
            VolumeType: gp2
      ImageId: !Ref EC2AMIId
      InstanceInitiatedShutdownBehavior: 'stop'
      InstanceType: t2.micro
      IamInstanceProfile: !Ref EC2InstanceProfile
      KeyName: !Ref KeyName
      SecurityGroupIds:
        - !Ref EC2SecurityGroup
      SubnetId: !Ref PublicSubnetA
      Tenancy: default
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-EC2-Instance
      UserData:
        Fn::Base64: |
          #!/bin/bash
          yum -y update
          amazon-linux-extras install php7.2 -y
          yum -y install mysql httpd php-mbstring php-xml gd php-gd
          systemctl enable httpd.service
          systemctl start httpd.service
          wget http://ja.wordpress.org/latest-ja.tar.gz
          tar zxvf latest-ja.tar.gz
          cp -r wordpress/* /var/www/html/
          chown apache:apache -R /var/www/html
          cd /var/www/html/
          mkdir healths
          cd healths
          touch wp-check

  # ------------------------------------------------------------#
  #  Database Subnet Group
  # ------------------------------------------------------------#
  RDSDBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: RDS-SubnetGroup
      DBSubnetGroupName: !Sub ${EnvironmentName}-RDS-SubnetGroup
      SubnetIds:
        - !Ref PrivateSubnetA
        - !Ref PrivateSubnetC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-RDS-SubnetGroup

  # ------------------------------------------------------------#
  #  RDS Security Group
  # ------------------------------------------------------------#
  RDSSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub ${EnvironmentName}-RDS-SG
      GroupDescription: Allow Request from WebServer
      VpcId: !Ref VPC
      SecurityGroupIngress:
        # http
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !Ref EC2SecurityGroup
      Tags:
        - Key: !Sub ${EnvironmentName}-Name
          Value: !Sub ${EnvironmentName}-RDS-SG

  # ------------------------------------------------------------#
  #  RDS
  # ------------------------------------------------------------#
  RDSDatabase:
    Type: AWS::RDS::DBInstance
    Properties:
      AllocatedStorage: 20
      AllowMajorVersionUpgrade: false
      AutoMinorVersionUpgrade: false
      AvailabilityZone: ap-northeast-1a
      BackupRetentionPeriod: 0
      DBInstanceClass: db.t3.micro
      DBInstanceIdentifier: !Sub ${EnvironmentName}-RDS-Database
      DBName: !Ref DatabaseName
      DBSubnetGroupName: !Ref RDSDBSubnetGroup
      DeleteAutomatedBackups: false
      DeletionProtection: false
      Engine: mysql
      EngineVersion: !Ref EngineVersion
      MasterUsername: !Ref DatabaseMasterName
      MasterUserPassword: !Ref DatabaseMasterPassword
      MaxAllocatedStorage: 1000
      MultiAZ: false
      PubliclyAccessible: false
      StorageEncrypted: false
      StorageType: gp2
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-RDS-Database
      VPCSecurityGroups:
        - !Ref RDSSG

スタック作成

  1. 上記のテンプレートをテキストエディタなどでYAML形式で保存してください。

  2. AWSコンソールからCloudFormation(東京リージョン)に移動し「スタックの作成」→「新しいリソースを使用 (標準)」をクリックしてください。

  3. 下記の画面で「既存のテンプレートを選択」と「テンプレートファイルのアップロード」を選択し、「ファイルの選択」をクリックしYAMLファイルをアップロードしてください。
  4. 「次へ」をクリックし、下記の画面で任意の「スタック名」と「DatabaseMasterPassword」を入力し、「KeyName」でキーペアを選択してください。 ※「EC2AMIID」と「EngineVersion」はデフォルトで記載されているものが使用できない場合があるので、EC2とRDSの作成画面でそれぞれAmazon Linux 2のAMI IDとMySQLのエンジンバージョンを確認して入力してください。

  5. 次のページのスタックオプションの設定はそのままで問題ありません。

  6. 最後に「確認して作成」画面が表示されます。ここで設定内容を確認し、下記の「AWS CloudFormation によって IAM リソースがカスタム名で作成される場合があることを承認します。」のチェックボックスにチェックを入れてください。

  7. 送信ボタンを押せばスタックの作成が開始されます。

  8. 問題がなければ下記のように「CREATE_COMPLETE」になるはずです。

動作確認

スタックの作成が完了したらブラウザからアクセスしてみましょう。

DNS名はロードバランサの詳細から確認できます。

下記の画面が表示されればOKです。

続いてSession ManagerでEC2インスタンスに接続してみましょう。

作成したEC2インスタンスを選択し、「インスタンスの状態」の左にある「接続」をクリックしてください。

下記の画面が表示されれば接続可能です。

※SSHで接続したい場合はEC2のセキュリティグループでSSHのインバウンド通信を許可する必要があります。

スタック削除

CloudFormationではスタック単位でのリソースの一括削除が可能です。

CloudFormationに移動し、「削除」をクリックしてください。

確認画面が表示されるので、再び「削除」をクリックしてください。

まとめ

ITインフラの効率的な運用にIaC(Infrastructure as Code)はもはや必須であり、CloudFormationはAWS環境の運用において強力な武器となります。

また、個人で検証環境を作成する際にも非常に便利です。

本ブログのテンプレートは初歩的なものですので、これを色々いじったりリソースを追加したりすることでよりAWSとCloudFormationの理解が深まると思います。

この機会にCloudFormationの沼に飛び込んでみてはいかがでしょうか?

お知らせ

APCはAWS Advanced Tier Services(アドバンストティアサービスパートナー)認定を受けております。

その中で私達クラウド事業部はAWSなどのクラウド技術を活用したSI/SESのご支援をしております。
www.ap-com.co.jp

https://www.ap-com.co.jp/service/utilize-aws/

また、一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。

www.ap-com.co.jp