IDCFクラウドからAWSへWEBサーバーの移管を行った際経験になった色々(備忘録)
他クラウドサービスからAWSへWEBサーバーを移管
直近、Webサーバーを他クラウドからAWSへ移管する機会がありましたので、そこで体験した諸々について書き残す事にいたしました。
もっと「こうしたい」「こう出来ばよかった」という所は私の中でも多くあるのと合わせて、今回は備忘録的な意味合いが強く箇条書きに近いですが、眺める程度にお付き合いいただければ幸いです。
事の起こり・状況
・リスティング広告をアウトソーシングしている会社管理だったWebサーバーを諸々の事情で自社管理・運用する事となった
・冗長構成や負荷増時のアクセスを見込んでクラウド且つ、現状最も理解のあるAWSを選択
・2-3週間で移管を済ませたいとの注文
・作業者は私と移管前の環境を管理いただいていたインフラ担当様の計2人
私のスキルレベル
・事業会社の情シス。AWSサービスについての座学的知識は有
・ミドルウェアやDNS周りについてはまだまだ勉強不足
・実務でAWSを触る機会は今までなかった
・IaCが好き
ちなみに一緒に作業くださった担当様はAWSはAssociate程度のサービス理解は既にお持ちで、技術記事を読めば理解し作成が可能、ミドルウェアやDNS周りなどの広範にご経験のおありな方でした。
→私が主にインスタンスの外側、インスタンス内の設定等はお願いする形としました。
移管前のサーバー周りの構成をヒアリング
・IDCFクラウドを利用
・インスタンスタイプはhighcpu.L8 仮想CPU:4CPU、メモリ容量:8GB、ストレージ:115GB
・3ドメインのコンテンツが同じインスタンス内にある。
・内2ドメインについてはSSLの更新期限が切れている状況。
・ルートドメインにアクセスがあった場合wwwサブドメインにリダイレクトするプログラムがあるとの事。
・postfix、mysqlserver、postgresql serverが動いている。
・AutoScalingのような拡張性は無し、冗長構成はあるがコールドスタンバイ。
・パートナー企業の運用監視プランを契約中。
今回私が経験した事まとめ
メイン
・運用監視会社の選定・契約締結
・会社のクレカでAWSアカウントを新規作成
・クラスメソッド社メンバーズへの加入手続きと上記アカウント転入作業
・AWS SA様 技術者様への技術相談
・ドメインレジストラの変更
・新サーバー環境構築
・旧サーバーで進行中作業に新サーバーの差分発生となる部分の洗い出し
・コスパ面から削減可能な部分の判断
その他
・広告のレポーティングや電話番号出し分けに使う各ツールの機能と継続するかの判断に関わる情報整理
・GA、GTM、広告アカウントの作成、権限付与に関わる作業諸々
構成図とテンプレート
構成図
テンプレート
CloudFormationテンプレート
AWSTemplateFormatVersion: "2010-09-09"
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------#
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: Stack(CloudFormation)
Parameters:
- PJPrefix
- Environment
-
Label:
default: VPC
Parameters:
- VPCCidrBlock
-
Label:
default: PublicSubnet
Parameters:
- PublicSubnet1CidrBlock
- PublicSubnet2CidrBlock
-
Label:
default: ProtectedSubnet(forWebServer)
Parameters:
- ProtectedSubnet1CidrBlock
- ProtectedSubnet2CidrBlock
-
Label:
default: Instance
Parameters:
- InstanceType
- ImageId
- DesiredCapacity
- MinSize
- MaxSize
- TargetValue
-
Label:
default: Route53
Parameters:
- DomainNameforexample1
- DomainNameforexample2
- DomainNameforexample3
- MXResourceRecord
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
PJPrefix:
Type: String
Environment:
Type: String
Default: prd
VPCCidrBlock:
Type: String
Default: 10.1.0.0/16
PublicSubnet1CidrBlock:
Type: String
Default: 10.1.0.0/24
PublicSubnet2CidrBlock:
Type: String
Default: 10.1.1.0/24
ProtectedSubnet1CidrBlock:
Type: String
Default: 10.1.2.0/24
ProtectedSubnet2CidrBlock:
Type: String
Default: 10.1.3.0/24
InstanceType:
Type: String
Default: m5.large
ImageId:
Type: String
Default: ami-xxxxxxxxxxxxxxxxx
AllowedValues:
DesiredCapacity:
Type: String
Default: 1
MinSize:
Type: String
Default: 1
MaxSize:
Type: String
Default: 2
TargetValue:
Type: String
Default: 90
DomainNameforexample1:
Type: String
Default: example1.com
AllowedValues:
- example1.com
DomainNameforexample2:
Type: String
Default: example2.com
AllowedValues:
- example2.com
DomainNameforexample3:
Type: String
Default: example.com
AllowedValues:
- example.com
MXResourceRecord:
Type: String
# ------------------------------------------------------------#
# Resources
# ------------------------------------------------------------#
Resources:
# ------------------------------------------------------------#
# Role
# ------------------------------------------------------------#
# Role
EC2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- ec2.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
- arn:aws:iam::aws:policy/AmazonS3FullAccess
- arn:aws:iam::aws:policy/AmazonSESFullAccess
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-role-for-ec2
# InstanceProfile
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles:
- !Ref EC2Role
# ------------------------------------------------------------#
# HostedZone
# ------------------------------------------------------------#
HostedZoneforexample1:
Type: AWS::Route53::HostedZone
Properties:
Name: !Ref DomainNameforexample1
HostedZoneforexample2:
Type: AWS::Route53::HostedZone
Properties:
Name: !Ref DomainNameforexample2
HostedZoneforexample3:
Type: AWS::Route53::HostedZone
Properties:
Name: !Ref DomainNameforexample3
# ------------------------------------------------------------#
# RecordSetGroup
# ------------------------------------------------------------#
AliasRecordexample1:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: !Ref HostedZoneforexample1
RecordSets:
- Name: !Ref DomainNameforexample1
AliasTarget:
DNSName: !GetAtt LoadBalancer.DNSName
HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID
Type: A
- Name: !Sub www.${DomainNameforexample1}
AliasTarget:
DNSName: !GetAtt LoadBalancer.DNSName
HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID
Type: A
- Name: !Ref DomainNameforexample1
TTL: 300
ResourceRecords:
- !Ref MXResourceRecord
Type: MX
- Name: !GetAtt EmailIdentityforexample1.DkimDNSTokenName1
TTL: 300
ResourceRecords:
- !GetAtt EmailIdentityforexample1.DkimDNSTokenValue1
Type: CNAME
- Name: !GetAtt EmailIdentityforexample1.DkimDNSTokenName2
TTL: 300
ResourceRecords:
- !GetAtt EmailIdentityforexample1.DkimDNSTokenValue2
Type: CNAME
- Name: !GetAtt EmailIdentityforexample1.DkimDNSTokenName3
TTL: 300
ResourceRecords:
- !GetAtt EmailIdentityforexample1.DkimDNSTokenValue3
Type: CNAME
- Name: !Ref DomainNameforexample1
TTL: 300
ResourceRecords:
- '"v=spf1 include:amazonses.com ~all"'
Type: TXT
AliasRecordexample2:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: !Ref HostedZoneforexample2
RecordSets:
- Name: !Ref DomainNameforexample2
AliasTarget:
DNSName: !GetAtt LoadBalancer.DNSName
HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID
Type: A
- Name: !Sub www.${DomainNameforexample2}
AliasTarget:
DNSName: !GetAtt LoadBalancer.DNSName
HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID
Type: A
- Name: !Ref DomainNameforexample2
TTL: 300
ResourceRecords:
- !Ref MXResourceRecord
Type: MX
- Name: !GetAtt EmailIdentityforexample2.DkimDNSTokenName1
TTL: 300
ResourceRecords:
- !GetAtt EmailIdentityforexample2.DkimDNSTokenValue1
Type: CNAME
- Name: !GetAtt EmailIdentityforexample2.DkimDNSTokenName2
TTL: 300
ResourceRecords:
- !GetAtt EmailIdentityforexample2.DkimDNSTokenValue2
Type: CNAME
- Name: !GetAtt EmailIdentityforexample2.DkimDNSTokenName3
TTL: 300
ResourceRecords:
- !GetAtt EmailIdentityforexample2.DkimDNSTokenValue3
Type: CNAME
- Name: !Ref DomainNameforexample2
TTL: 300
ResourceRecords:
- '"v=spf1 include:amazonses.com ~all"'
Type: TXT
AliasRecordexample3:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: !Ref HostedZoneforexample3
RecordSets:
- Name: !Ref DomainNameforexample3
AliasTarget:
DNSName: !GetAtt LoadBalancer.DNSName
HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID
Type: A
- Name: !Sub www.${DomainNameforexample3}
AliasTarget:
DNSName: !GetAtt LoadBalancer.DNSName
HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID
Type: A
- Name: !Ref DomainNameforexample3
TTL: 300
ResourceRecords:
- !Ref MXResourceRecord
Type: MX
- Name: !GetAtt EmailIdentityforexample3.DkimDNSTokenName1
TTL: 300
ResourceRecords:
- !GetAtt EmailIdentityforexample3.DkimDNSTokenValue1
Type: CNAME
- Name: !GetAtt EmailIdentityforexample3.DkimDNSTokenName2
TTL: 300
ResourceRecords:
- !GetAtt EmailIdentityforexample3.DkimDNSTokenValue2
Type: CNAME
- Name: !GetAtt EmailIdentityforexample3.DkimDNSTokenName3
TTL: 300
ResourceRecords:
- !GetAtt EmailIdentityforexample3.DkimDNSTokenValue3
Type: CNAME
- Name: !Ref DomainNameforexample3
TTL: 300
ResourceRecords:
- '"v=spf1 include:amazonses.com ~all"'
Type: TXT
# ------------------------------------------------------------#
# WebACL
# ------------------------------------------------------------#
WebACL:
Type: AWS::WAFv2::WebACL
Properties:
Scope: REGIONAL
Name: !Sub ${PJPrefix}-${Environment}-webacl
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub ${PJPrefix}-${Environment}-webaclmetric
SampledRequestsEnabled: true
DefaultAction:
Allow: {}
Rules:
- Name: !Sub ${PJPrefix}-${Environment}-AWSManagedRulesBotControlRuleSet
Priority: 0
OverrideAction:
None: {}
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesBotControlRuleSet
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: AWSManagedRulesCommonRuleSetMetric
SampledRequestsEnabled: true
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-webacl
# ------------------------------------------------------------#
# WebACLAssociation
# ------------------------------------------------------------#
WebACLAssociation:
Type: AWS::WAFv2::WebACLAssociation
Properties:
ResourceArn: !Ref LoadBalancer
WebACLArn: !GetAtt WebACL.Arn
# ------------------------------------------------------------#
# Bucket
# ------------------------------------------------------------#
Bucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Properties:
BucketName: !Sub ${PJPrefix}-${Environment}-bucket-${AWS::AccountId}
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-bucket-${AWS::AccountId}
# ------------------------------------------------------------#
# EmailIdentity
# ------------------------------------------------------------#
EmailIdentityforexample1:
DependsOn: HostedZoneforexample1
Type: AWS::SES::EmailIdentity
Properties:
EmailIdentity: !Ref DomainNameforexample1
EmailIdentityforexample2:
DependsOn: HostedZoneforexample2
Type: AWS::SES::EmailIdentity
Properties:
EmailIdentity: !Ref DomainNameforexample2
EmailIdentityforexample3:
DependsOn: HostedZoneforexample3
Type: AWS::SES::EmailIdentity
Properties:
EmailIdentity: !Ref DomainNameforexample3
# ------------------------------------------------------------#
# Certificate
# ------------------------------------------------------------#
Certificateforexample1:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: !Ref DomainNameforexample1
DomainValidationOptions:
- DomainName: !Ref DomainNameforexample1
HostedZoneId: !Ref HostedZoneforexample1
SubjectAlternativeNames:
- !Sub "*.${DomainNameforexample1}"
ValidationMethod: DNS
Certificateforexample2:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: !Ref DomainNameforexample2
DomainValidationOptions:
- DomainName: !Ref DomainNameforexample2
HostedZoneId: !Ref HostedZoneforexample2
SubjectAlternativeNames:
- !Sub "*.${DomainNameforexample2}"
ValidationMethod: DNS
Certificateforexample3:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: !Ref DomainNameforexample3
DomainValidationOptions:
- DomainName: !Ref DomainNameforexample3
HostedZoneId: !Ref HostedZoneforexample3
SubjectAlternativeNames:
- !Sub "*.${DomainNameforexample3}"
ValidationMethod: DNS
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------#
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCidrBlock
InstanceTenancy: default
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-VPC
# ------------------------------------------------------------#
# InternetGateway
# ------------------------------------------------------------#
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-InternetGateway
# ------------------------------------------------------------#
# VPCGatewayAttachment
# ------------------------------------------------------------#
# アタッチメント
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------#
# Public
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: !Ref PublicSubnet1CidrBlock
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs ]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-PublicSubnet1
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: !Ref PublicSubnet2CidrBlock
VpcId: !Ref VPC
AvailabilityZone: !Select [ 1, !GetAZs ]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-PublicSubnet2
# Protected
ProtectedSubnet1:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: !Ref ProtectedSubnet1CidrBlock
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs ]
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-ProtectedSubnet1
ProtectedSubnet2:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: !Ref ProtectedSubnet2CidrBlock
VpcId: !Ref VPC
AvailabilityZone: !Select [ 1, !GetAZs ]
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-ProtectedSubnet2
# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------#
# Public
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-PublicRouteTable
# Protected
ProtectedRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-ProtectedRouteTable1
ProtectedRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-ProtectedRouteTable2
# ------------------------------------------------------------#
# Route
# ------------------------------------------------------------#
# Public
PublicRoute:
DependsOn:
- InternetGateway
- VPCGatewayAttachment
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
# Protected
ProtectedRoute1:
DependsOn:
- InternetGateway
- VPCGatewayAttachment
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref ProtectedRouteTable1
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NATGateway1
ProtectedRoute2:
DependsOn:
- InternetGateway
- VPCGatewayAttachment
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref ProtectedRouteTable2
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NATGateway2
# ------------------------------------------------------------#
# SubnetRouteTableAssociation
# ------------------------------------------------------------#
# Public
PublicSubnet1Assoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
PublicSubnet2Assoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
# Protected
ProtectedSubnet1Assoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref ProtectedSubnet1
RouteTableId: !Ref ProtectedRouteTable1
ProtectedSubnet2Assoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref ProtectedSubnet2
RouteTableId: !Ref ProtectedRouteTable2
# ------------------------------------------------------------#
# NatGateway
# ------------------------------------------------------------#
NATGateway1:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIPforNATGAteway1.AllocationId
SubnetId: !Ref PublicSubnet1
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-NATGateway1
NATGateway2:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIPforNATGateway2.AllocationId
SubnetId: !Ref PublicSubnet2
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-NATGateway2
# ------------------------------------------------------------#
# VPCEndpoint
# ------------------------------------------------------------#
VPCESSM:
Type: AWS::EC2::VPCEndpoint
Properties:
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref VPCESecurityGroup
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm
SubnetIds:
- !Ref ProtectedSubnet1
- !Ref ProtectedSubnet2
VpcEndpointType: Interface
VpcId: !Ref VPC
VPCESSMMessages:
Type: AWS::EC2::VPCEndpoint
Properties:
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref VPCESecurityGroup
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages
SubnetIds:
- !Ref ProtectedSubnet1
- !Ref ProtectedSubnet2
VpcEndpointType: Interface
VpcId: !Ref VPC
VPCEEC2Messages:
Type: AWS::EC2::VPCEndpoint
Properties:
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref VPCESecurityGroup
ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages
SubnetIds:
- !Ref ProtectedSubnet1
- !Ref ProtectedSubnet2
VpcEndpointType: Interface
VpcId: !Ref VPC
VPCES3:
Type: AWS::EC2::VPCEndpoint
Properties:
RouteTableIds:
- !Ref PublicRouteTable
- !Ref ProtectedRouteTable1
- !Ref ProtectedRouteTable2
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
VpcEndpointType: Gateway
VpcId: !Ref VPC
# ------------------------------------------------------------#
# EIP
# ------------------------------------------------------------#
EIPforNATGAteway1:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-EIPforNATGAteway1
EIPforNATGateway2:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-EIPforNATGateway2
# ------------------------------------------------------------#
# LoadBalancer
# ------------------------------------------------------------#
LoadBalancer:
DependsOn:
- InternetGateway
- VPCGatewayAttachment
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub ${PJPrefix}-${Environment}-LoadBalancer
Scheme: internet-facing
Type: application
LoadBalancerAttributes:
- Key: deletion_protection.enabled
Value: false
- Key: idle_timeout.timeout_seconds
Value: 60
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-LoadBalancer
# ------------------------------------------------------------#
# Listener
# ------------------------------------------------------------#
ListenerHTTP:
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 LoadBalancer
ListenerHTTPSforexample1:
DependsOn: Certificateforexample1
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
Port: 443
Protocol: HTTPS
Certificates:
- CertificateArn: !Ref Certificateforexample1
DefaultActions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
LoadBalancerArn: !Ref LoadBalancer
# ------------------------------------------------------------#
# ListenerCertificate
# ------------------------------------------------------------#
ListenerCertificatesforexample2:
DependsOn: Certificateforexample2
Type: AWS::ElasticLoadBalancingV2::ListenerCertificate
Properties:
Certificates:
- CertificateArn: !Ref Certificateforexample2
ListenerArn: !Ref ListenerHTTPSforexample1
ListenerCertificatesforexample3:
DependsOn: Certificateforexample3
Type: AWS::ElasticLoadBalancingV2::ListenerCertificate
Properties:
Certificates:
- CertificateArn: !Ref Certificateforexample3
ListenerArn: !Ref ListenerHTTPSforexample1
# ------------------------------------------------------------#
# TargetGroup
# ------------------------------------------------------------#
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId: !Ref VPC
Name: !Sub ${PJPrefix}-${Environment}-TargetGroup
Protocol: HTTPS
ProtocolVersion: HTTP1
Port: 443
HealthCheckEnabled: true
HealthCheckProtocol: HTTPS
HealthCheckPath: /
HealthCheckPort: traffic-port
HealthyThresholdCount: 5
UnhealthyThresholdCount: 2
HealthCheckTimeoutSeconds: 5
HealthCheckIntervalSeconds: 30
IpAddressType: ipv4
Matcher:
HttpCode: 200
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-TargetGroup
# ------------------------------------------------------------#
# AutoScalingGroup
# ------------------------------------------------------------#
AutoScalingGroupforWebServer:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
AutoScalingGroupName: !Sub ${PJPrefix}-${Environment}-AutoScalingGroupforWebServer
VPCZoneIdentifier:
- !Ref ProtectedSubnet1
- !Ref ProtectedSubnet2
LaunchTemplate:
LaunchTemplateId: !Ref LaunchTemplateforWebServer
Version: !GetAtt LaunchTemplateforWebServer.LatestVersionNumber
TargetGroupARNs:
- !Ref TargetGroup
DesiredCapacity: !Ref DesiredCapacity
MinSize: !Ref MinSize
MaxSize: !Ref MaxSize
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-EC2
PropagateAtLaunch: true
# ------------------------------------------------------------#
# LaunchTemplate
# ------------------------------------------------------------#
LaunchTemplateforWebServer:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateName: !Sub ${PJPrefix}-${Environment}-LaunchTemplateforWebServer
LaunchTemplateData:
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeType: gp3
VolumeSize: 30
DeleteOnTermination: true
TagSpecifications:
- ResourceType: instance
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-Instance
KeyName: !Ref KeyPair
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
IamInstanceProfile:
Arn: !GetAtt EC2InstanceProfile.Arn
NetworkInterfaces:
- AssociatePublicIpAddress: false
DeviceIndex: 0
Groups:
- !Ref EC2SecurityGroup
# ------------------------------------------------------------#
# ScalingPolicy
# ------------------------------------------------------------#
ScalingPolicy:
Type: AWS::AutoScaling::ScalingPolicy
Properties:
AutoScalingGroupName: !Ref AutoScalingGroupforWebServer
EstimatedInstanceWarmup: 120
PolicyType: TargetTrackingScaling
TargetTrackingConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ASGAverageCPUUtilization
TargetValue: !Ref TargetValue
# ------------------------------------------------------------#
# KeyPair
# ------------------------------------------------------------#
KeyPair:
Type: AWS::EC2::KeyPair
Properties:
KeyFormat: pem
KeyName: !Sub ${PJPrefix}-${Environment}-KeyPair
KeyType: rsa
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-KeyPair
# ------------------------------------------------------------#
# SecurityGroup
# ------------------------------------------------------------#
VPCESecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref VPC
GroupName: !Sub ${PJPrefix}-${Environment}-sg-vpcendpoint
GroupDescription: for VPCE for Session Manager and More.
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-SecurityGroup-VPCE
# Ingress
VPCESecurityGroupIngressEC2SecurityGroup443:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Allow port 443 access from EC2.
FromPort: 443
ToPort: 443
IpProtocol: tcp
SourceSecurityGroupId: !Ref EC2SecurityGroup
GroupId: !GetAtt VPCESecurityGroup.GroupId
VPCESecurityGroupIngressfromEC2SecurityGroup587:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Allow port 587 access from EC2.
FromPort: 587
ToPort: 587
IpProtocol: tcp
SourceSecurityGroupId: !Ref EC2SecurityGroup
GroupId: !GetAtt VPCESecurityGroup.GroupId
#≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref VPC
GroupName: !Sub ${PJPrefix}-${Environment}-SecurityGroup-LoadBalancer
GroupDescription: for LoadBalancer
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-SecurityGroup-LoadBalancer
# Ingress
LoadBalancerSecurityGroupIngressfromInternet80: # これがないと80->443へリダイレクトするリスナーが上手くいかなかった為
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Allow port 80 access from Any.
FromPort: 80
ToPort: 80
IpProtocol: tcp
CidrIp: 0.0.0.0/0
GroupId: !GetAtt LoadBalancerSecurityGroup.GroupId
LoadBalancerSecurityGroupIngressfromInternet443:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Allow port 443 access from Any.
FromPort: 443
ToPort: 443
IpProtocol: tcp
CidrIp: 0.0.0.0/0
GroupId: !GetAtt LoadBalancerSecurityGroup.GroupId
#≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref VPC
GroupName: !Sub ${PJPrefix}-${Environment}-SecurityGroup-EC2
GroupDescription: for EC2
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-SecurityGroup-EC2
EC2SecurityGroupIngressfromLoadBalancerSecurityGroup443:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Allow port 443 access from LoadBalancer.
FromPort: 443
ToPort: 443
IpProtocol: tcp
SourceSecurityGroupId: !GetAtt LoadBalancerSecurityGroup.GroupId
GroupId: !GetAtt EC2SecurityGroup.GroupId
方針と補足
とにかく時間がない為、一旦「問題なく動く」を目指し、その後ベストプラクティスに近づけていく事にする。
・本来は最低限CloudFrontは作成したかったが相談の結果、期待する動作にならない場合に試行錯誤する時間がないので一旦後回しにする事に。
・ALB→EC2間の通信は80でもよかったのが、SSLの切れていたサイトについてオレオレ証明書を発行し443で通信。
・EC2権限に一旦フルを与えている点・バケットポリシーなどの制限は今後追加予定。
・Autoscalingにした兼ね合い、インスタンス内にメールやDBがあると、スケール後と差分が出てしまう為、顧客とのやりとりに利用するメールはGmailに変更。RDSPostgresqlは検討したが、格納しているデータは二股でSalesforceとメールでも飛ぶようになっている為、運用の調整後廃止。
・メールはSESを利用して送信。VPCEのIngressルールには25ではなく587を許可。(※参考OP25B)
・インスタンス内にルートドメインアクセスをwwwでリダイレクトさせるプログラムが存在するが、削除している時間はない為、ありきでACMを設定。80でのアクセスは443にリスナーでリダイレクト。
・S3は作業用で作成しているが、作業者がS3→EC2の二段階作業が面倒になる事等を想定し結局キーペアも作成。
・EC2はサイズ検討の結果、m5.largeでスタート。AmiはPHP7.4のサポートの兼ね合いでAlmaLinux X86_64をサブスクライブし、ミドルウェアやコンテンツを設定したものを事前作成し利用。
課題と対処
EC2はスケールさせるか
スケールさせるのであれば、インスタンス内のメールとDBはインスタンス間の差分になり、期待する動作にならない。外出しか廃止が必要。
DB→運用を調整し廃止
メールGmailに変更。(メール送信機能はSESと絡めて残す。)
最初はコスト面を考え、ALBも無しで現在のように障害発生時・CPU負荷があがった際などアラームを設定したり運用監視会社に再起動のみ依頼して後は手動、或いは以下の記事ようにする事も考えましたが、結果やめました。
PHPのVerが5である事が発覚、これを機に7.4に変更する事に
それに対応して、ベースイメージはPHP7のサポートが2029年まであるらしいAlmaLinux8系が良いのではとの話になり、最初にarmをサブスクリプションするもphpのインストールで以下エラーが発生。
Error: Failed to download metadata for repo 'remi-modular': Cannot prepare internal mirrorlist: Status code: 403 for http://cdn.remirepo.net/enterprise/8/modular/aarch64/mirror
調べてみるとremiは現在armに対応していない
とのことでX86-64をサブスクライブし直し事なきを得た。SES周りで経験した事
MXレコードと、TXTでSPFレコードを作成
CustomMailFromDomainは利用せず
SESで検証済みIDを登録するがEasyDKIMの認証が最大72時間かかるとの記述あり。
いつ終わるかわからないのは日中広告を打っている兼ね合い許容出来ないと思っていたが、結果6時間程度で3ドメイン全てが検証済みになった。
ここを短時間で検証完了にさせるには何か工夫で解決出来る方法などあるかが気になった。
作業者アカウントの作成・インスタンスへの接続方法
直前に発表されたEC2ConnectEndPointを利用したいと思ったが、複数サブネットに対応していない為大人しくSSMセッションマネージャーでVPCE×3を生やす事に。
User・Groupの作成、
・AmazonSSMFullAccess
・AmazonEC2FullAccess
の付与し、ローカルにAWS CLI v2とSession Manager pluginをインストールしてもらった上、
aws ssm start-session --target [インスタンスID]
で接続してもらった。
当初はEC2のAMIにもCLIを入れてS3経由でオブジェクトをcpして作業していただいていたが、結果ゆくゆくを考えてキーペア作成もする事に。
キーペアはCloudformationで作成した為、シークレットキーはSSMパラメータストアに格納された。
値をコピーして自分でテキストエディタを使い.pemファイルを作成という流れ。お名前.comへドメインを移管
Authcode というものを初めて知る。
話の流れで既に別のレジストラからお名前.comにドメインを移管してしまったが、確かRoute53だけでもいけたのではとAWSの技術者様から教えていただいた。
しかし、お名前.comからAmazon Route 53へドメインを移管するには
移行対象のドメインを現在のドメインレジストラに登録してから、少なくとも60日経っていること
との事。色々見ましたが、優先順位が低い為一旦見送りとしました。
ちなみに「指定期間内にWhois情報の登録をしてください」というメールが別の部の人間に届いていたのに気づかず、お名前.comでいうIcannHoldがかかり、1時間程度サイトに繋がらなくなるという経験もしました。(修正後すぐに伝播してよかったですが一瞬嫌な汗をかきました。)
国外IPを全てdenyしてしまうとGoogleやYahooの広告のボットに影響するかも
WAFのBotControleはよしなにやってくれるもののようだが、国外IPを遮断まですると問題があるか。
[参考] BotControl
[参考] 海外IP遮断
一旦BotControlのみを設定したが、読み込んで問題がなさそうとわかれば追加していく事にした。
運用保守会社を比較・AWSのSAさんやパートナー企業に相談
どの単位で費用が発生するか等説明を受け、社内稟議を通し契約まで行いました。
詳細は割愛しますがこれらの流れや仕組みも勉強になりました。
同時にAWSのSAさんと技術者の方と三者でアーキテクチャについて相談。
リフト戦略、クラウドフロントを追加するメリットのおさらい、新しい世代のM系のインスタンスの検討について、WAFとRoute53の地理的なアクセス制御の細かい違い、CICDにしていく事、どこまでやるか、VPCEを普段停止しておくような節約は一般的か など質問し、アドバイスいただけた。
終わりに
備忘録+箇条書きで全く完成された内容ではありませんでしたが、ここからどうベストプラクティスなものに近づけていけるかという出発地点として書きました。
一旦、今のところは期間内に移動出来、正常動作している事に安堵していますが、「これってこうじゃない?」的なコメントをいただければとても嬉しいです。
お付き合いいただき有難うございました。
その他勉強になった技術記事
Discussion