【AWS】CloudFormation을 통한 웹 서비스 구축 ②(EC2편)

1. 시작
본 투고는 아래 보도의 속편이다.
【AWS】CloudFormation을 통한 웹 서비스 구축①(NW편)
이 투고를 처음 보신 분은 이쪽 기사부터 읽어주세요.
2.EC2 개요
이번'AWS'클라우드 포메이션은 웹 서비스 ②(EC2편)를 구축해 웹 환경의 핵심이 되는 서버 군과 주변 서비스의 모델로 제작한다.
구체적인 서비스는 다음과 같다.
2.1 "서버.yml"에 등장하는 서비스
  • IAM 롤러
  • MySQL과의 합작으로 SSM을 사용하기 때문에 SSM에 대한 권한을 EC2
  • 에 부여한다
  • SSM
    EC2 연결용

  • EC2(Web/AP 서버)
  • HTTP 서버는 Apache
  • 를 사용합니다.
  • Route53
  • 도메인 이름을 미리 준비하십시오.
    (Route 53에서 가져온 도메인을 사용하는 기고자)
  • Elastic Load Balancer(ALB)
  • 부하 분산 방식이지만 웹 환경을 구축하기 위해 가장 친화적인 ALB
  • 를 사용합니다.
  • ACM
  • 인증서가 ACM에서 새로 발행됨
  • 2.2 EC 2편 구성도
    EC2편으로 작성된 템플릿 환경의 구성도는 다음 그림과 같습니다.

    3. EC2 템플릿 제작
    다음은 EC2 섹션의 템플릿입니다.
    명명 규칙 등 필요하면 자신의 환경에 따라 수정하세요.
    수정이 완료되면 작업 디렉토리에 "server.yml"파일 이름으로 저장합니다.
    server.yml
    AWSTemplateFormatVersion: "2010-09-09"
    Description: Web2tier-Server-Template
    
    
    # ------------------------------------------------------------
    # Input Parameters
    # ------------------------------------------------------------
    Parameters:
    ### Project Prefix ###
      PJPrefix:
        Type: String
    ### Key pair ###
      KeyName:
        Description: input EC2 Keyname
        Type: 'AWS::EC2::KeyPair::KeyName'
    ### Hosted Zone ###
      HostedZoneName:
        Type: String
        Description: DNS Name to create
        Default: '<<ドメイン(example.com)>>'
        AllowedPattern: (?!-)[a-zA-Z0-9-.]{1,63}(?<!-)
        ConstraintDescription: must be a valid DNS zone name
      SubDomain:
        Description: FQDN of the certificate
        Type: String
        Default: 'www.<<ドメイン(example.com)>>'
    
    
    ### Resources ###
    Resources: 
    # ------------------------------------------------------------
    # IAM Role
    # ------------------------------------------------------------
      SSMRole:
        Type: AWS::IAM::Role
        Properties:
          Path: /
          RoleName: !Sub "${PJPrefix}-SSMRole"
          AssumeRolePolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Principal:
                  Service:
                    - ec2.amazonaws.com
                Action:
                  - sts:AssumeRole
          MaxSessionDuration: 3600
          ManagedPolicyArns: 
            - arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM
    
      SSMRoleProfile:
        Type: AWS::IAM::InstanceProfile
        Properties:
          Path: /
          Roles:
            - !Ref SSMRole
    
    
    # ------------------------------------------------------------
    # EC2
    # ------------------------------------------------------------
    ### AvailabilityZone-A ###
      EC2WebA: 
        Type: AWS::EC2::Instance
        Properties: 
          ImageId: ami-032d6db78f84e8bf5
          InstanceType: t2.micro
          IamInstanceProfile: !Ref SSMRoleProfile
          KeyName: !Ref KeyName
          NetworkInterfaces: 
            - AssociatePublicIpAddress: "true"
              DeviceIndex: "0"
              SubnetId: { "Fn::ImportValue": !Sub "${PJPrefix}-private-subnet-a" }
              GroupSet:
                - { "Fn::ImportValue": !Sub "${PJPrefix}-web-sg" }
          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 AZ-a" > /var/www/html/index.html
              sudo systemctl start amazon-ssm-agent.service
              sudo systemctl enable amazon-ssm-agent.service
          Tags:
              - Key: Name
                Value: !Sub "${PJPrefix}-web-server-1a"
    
    ### AvailabilityZone-C ###
      EC2WebC: 
        Type: AWS::EC2::Instance
        Properties: 
          ImageId: ami-032d6db78f84e8bf5
          InstanceType: t2.micro
          IamInstanceProfile: !Ref SSMRoleProfile
          KeyName: !Ref KeyName
          NetworkInterfaces: 
            - AssociatePublicIpAddress: "true"
              DeviceIndex: "0"
              SubnetId: { "Fn::ImportValue": !Sub "${PJPrefix}-private-subnet-c" }
              GroupSet:
                - { "Fn::ImportValue": !Sub "${PJPrefix}-web-sg" }
          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 AZ-c" > /var/www/html/index.html
              sudo systemctl start amazon-ssm-agent.service
              sudo systemctl enable amazon-ssm-agent.service
          Tags:
              - Key: Name
                Value: !Sub "${PJPrefix}-web-server-1c"
    
    
    # ------------------------------------------------------------
    # Target Group
    # ------------------------------------------------------------
      TargetGroup:
        Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
        Properties: 
          VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" } 
          Name: !Sub "${PJPrefix}-tg"
          Protocol: HTTP
          Port: 80
          HealthCheckProtocol: HTTP
          HealthCheckPath: "/"
          HealthCheckPort: "traffic-port"
          HealthyThresholdCount: 2
          UnhealthyThresholdCount: 2
          HealthCheckTimeoutSeconds: 5
          HealthCheckIntervalSeconds: 10
          Matcher: 
            HttpCode: 200
          Tags: 
            - Key: Name
              Value: !Sub "${PJPrefix}-${ALB}-tg"
          TargetGroupAttributes: 
            - Key: "deregistration_delay.timeout_seconds"
              Value: 300
            - Key: "stickiness.enabled"
              Value: false
            - Key: "stickiness.type"
              Value: lb_cookie
            - Key: "stickiness.lb_cookie.duration_seconds"
              Value: 86400
          Targets: 
            - Id: !Ref EC2WebA
            - Id: !Ref EC2WebC
              Port: 80
    
    
    # ------------------------------------------------------------
    # Application Load balancer
    # ------------------------------------------------------------
    ### ALB ###
      ALB: 
        Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
        Properties: 
          Name: !Sub "${PJPrefix}-alb"
          Scheme: "internet-facing"
          LoadBalancerAttributes: 
            - Key: "deletion_protection.enabled"
              Value: false
            - Key: "idle_timeout.timeout_seconds"
              Value: 4000
          SecurityGroups:
            - { "Fn::ImportValue": !Sub "${PJPrefix}-alb-sg" } 
          Subnets: 
            - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a" } 
            - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c" }
    
    ### Listener HTTP ###
      ALBListenerHTTP: 
        Type: "AWS::ElasticLoadBalancingV2::Listener"
        Properties: 
          Port: 80
          Protocol: HTTP
          DefaultActions: 
            - Type: redirect
              RedirectConfig: 
                Host: '#{host}'
                Path: '/#{path}'
                Port: 443
                Protocol: HTTPS
                Query: '#{query}'
                StatusCode: HTTP_301
          LoadBalancerArn: !Ref ALB
    
    ### Listener HTTPS ###
      ALBListenerHTTPS:
        Type: AWS::ElasticLoadBalancingV2::Listener
        Properties:
          Port: 443
          Protocol: HTTPS
          Certificates:
            - CertificateArn: !Ref ACM
          DefaultActions:
            - TargetGroupArn: !Ref TargetGroup
              Type: forward
          LoadBalancerArn: !Ref ALB
    
    
    # ------------------------------------------------------------
    # Route53
    # ------------------------------------------------------------
      HostedZone:
        Type: AWS::Route53::HostedZone
        Properties:
          Name: !Ref HostedZoneName
          HostedZoneTags:
          - Key: Name
            Value: !Sub "${PJPrefix}-cfn-hostedzone"
    
      DNSRecord:
        Type: AWS::Route53::RecordSet
        Properties:
          HostedZoneId: !Sub '${HostedZone}'
          Comment: "alias to alb"
          Name: !Sub '${SubDomain}'
          Type: A
          AliasTarget:
            HostedZoneId: !GetAtt 'ALB.CanonicalHostedZoneID'
            DNSName: !GetAtt 'ALB.DNSName'
    
    
    # ------------------------------------------------------------
    # ACM
    # ------------------------------------------------------------
      ACM:
        Type: AWS::CertificateManager::Certificate
        Properties:
          DomainName: !Sub '${SubDomain}'
          DomainValidationOptions:
            - DomainName: !Sub '${SubDomain}'
              HostedZoneId: !Sub '${HostedZone}'
          ValidationMethod: DNS
    
    
    # ------------------------------------------------------------
    # Output Parameter
    # ------------------------------------------------------------
    Outputs:
    ### EC2 ###
      EC2WebA:
        Value: !Ref EC2WebA
        Export:
          Name: !Sub "${PJPrefix}-web-server-1a"
      EC2WebC:
        Value: !Ref EC2WebC
        Export:
          Name: !Sub "${PJPrefix}-web-server-1c"
    
    3.1 EC 2 템플릿 구축
    작성된 CFn 템플릿의 구성
    다음 명령을 실행하십시오.
    ※ 스택 이름, 접두사, 또는 추가로 지정하려는 매개 변수 등이 있으면 자신의 환경에 따라 수정하십시오.
    서버 템플릿 구성 명령
    $ aws cloudformation create-stack \
    --stack-name web2tier-Server-stack \
    --template-body file://./server.yml \
    --parameters ParameterKey=KeyName,ParameterValue=<<キーペア名>> \ 
    ParameterKey=PJPrefix,ParameterValue=web2tier
    
    ↓ 문법 오류가 없으면 다음 결과를 출력합니다.
    실행 결과
    {
        "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxx:stack/<スタックネーム>/<メタ番号>"
    }
    
    3.2 도메인 이름 프로세서에 NS 등록
    Route 53을 통해서만 호스트 영역을 생성하여 이름을 해결할 수 없습니다.
    릴리즈 도메인의 전문 심판 중에는 Route 53 릴리즈 NS 레코드에 로그인해야 합니다.
    이 절차에 관해서는 CFn이 실행할 수 없는지 수동으로 진행해야 한다.
    죄송하지만 이 기사는 IaC의 환경 구축에 초점을 맞췄기 때문에 전문 심판에게 NS음반을 등록하는 절차를 생략하도록 허락해 주십시오.
    그러나 Google에서 "Route53 < 도메인 프로세서 이름 > NS 레코드 로그인"을 검색하면 같은 절차가 발생합니다. 저쪽을 보십시오.
    3.3 ACM 인증서 DNS 검증
    CFn이 구축되면 NS 레코드가 DNS 공급업체에 추가됩니다.
    여기까지 아직 일을 끝내지 못했다.
    ACM에서 발행한 인증서의 DNS 검증이 끝나지 않으면 HTTPS 통신을 할 수 없으므로 검증이 끝날 때까지 기다려야 합니다.
    CFn을 구축할 때 ACM의 DNS 검증은 자동으로 시작되기 때문에 정말 기다리기만 한다(웃음)
    약 15~30분 정도면 검증이 완료되니 기다려주세요.
    4. 작업 확인 - 웹 서버의 테스트 페이지 탐색
    여기서 동작 확인을 한번 해보겠습니다.
    완성 요건 중 하나로서 다음과 같은 사항을 확인한다.
    · 웹 서버의 테스트 페이지 탐색
    구체적으로 호스트 영역의 도메인 입력 브라우저에 로그인하여 웹 서버가 설정한 테스트 페이지를 탐색합니다.
    또한 다시 로드하여 ALB에 하중 분산이 있는지 확인합니다.
    당장 확인해봐.
    다음 페이지를 표시하고 다시 불러오기를 반복합니다. "a"와 "c"의 표시는 변동이 발생할 수 있습니다. 만약 웹 페이지가 암호화되었는지 확인할 수 있다면 동작 확인은 삭제됩니다.
     
    ● AZ-a

    ● AZ-c

    5. 요약
    이번 보도에서 웹 환경에서 서버 그룹의 디버깅이 완료되었습니다.
    이런 작업만으로도 서버를 구축할 수 있기 때문에 IaC가 위대하다고 말할 수 밖에 없다.
    내 개인적인 견해는 장래에 인프라 시설 건설도 모두 코드에 의해 관리될 것이라고 한다.
    (CFn이 반드시 주류는 아니지만.)
    다음 글은 데이터베이스(RDS) 편입니다.
    기대하세요.
    - 기사 목록 -
    ①[AWS] 클라우드 포메이션을 통한 웹 서비스 구축 ①(NW편)
    ②[AWS] 클라우드 포메이션을 통한 웹 서비스 구축 ②(EC2편)
    ③[AWS] 클라우드 포메이션을 통한 웹 서비스 구축 ③(RDS편)

    좋은 웹페이지 즐겨찾기