はじめに
こんにちは。Webサービス開発グループの2019年度新入社員の渡邊です。
現在、弊社ではWebサービスのインフラを以前まで利用していたニフクラからAWSへの移行を行っています。
なぜ、AWS移行をするかについての詳細はこちらのページを参考にしてください。
一部サービスはAWS移行が既に完了しており、最適化を行っていく段階にあります。その一環としてAmazon CloudFrontの導入業務を担いました。
行ったこと
結論からいいますと現在のAWS環境はユーザーがALBを経由してEC2にアクセスする仕組みになっています。
この構造にユーザーとALBの間にCloudFrontを挟んで接続を行うようにしました。
Amazon CloudFrontとは
CloudFront は、ユーザーへの静的および動的ウェブコンテンツ (.html、.css、.js、イメージファイルなど) の配信を高速化するウェブサービスであり、 CloudFront ではエッジロケーションと呼ばれるデータセンターの世界規模のネットワークを通じてコンテンツが配信されます。
CloudFront を使用して提供されているコンテンツをユーザーがリクエストすると、そのユーザーはエッジロケーションにルーティングされます。エッジロケーションでは最も低いレイテンシー (遅延時間) が提供されるので、コンテンツは可能な最高のパフォーマンスで配信されます。
簡単に言うとAWSが提供するCDN(コンテンツデリバリーネットワーク)サービスです。
ユーザーに提供するコンテンツをサーバーから直接配信せず、CDNを介して配信します。
メリット
- 通信の安定・高速化
- コスト削減
- CloudFrontには12ヶ月間以上利用することをAmazon Web Services社に対し、約束をすると割引が実施されます。
- 弊社は複数のサービスを開発・運用しているのでこのサービスの恩恵を受けるには、AWS移行が完了したサービスは随時対応していく必要があります。
- 詳しくはこちらを参照してください。
AWS CloudFormationとは
EC2やALBといったAWSリソースの環境構築を設定ファイル(テンプレート)を元に自動化できるサービスです。
メリット
- テンプレートなので同じインフラ構成をすぐに再現できます。
- インフラ構成をバージョン管理、可視化できます。
詳しくは公式ドキュメントを参照してください。
作成したCloudFormationの説明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
AWSTemplateFormatVersion: 2010-09-09 Description: Template for creating CloudFront linked with S3 Bucket and ALB. Parameters: S3BucketName: Description: Type of this cloudfront log bucketname like "xxx.nifty.com" Type: String AlbDnsName: Description: Type of this AlB domain name like "alb-xxxx-xxx.ap-northeast-1.elb.amazonaws.com" Type: String DefaultTTL: Description: The default cache time in seconds. Type: String Default: "1" MaxTTL: Description: The maximum cache time in seconds. Type: String Default: "1" MinTTL: Description: The minimum amount of cache time. Type: String Default: "1" ErrorCacheTTL: Description: The error cache time in seconds. Type: String Default: "1" MinimumProtocolVersion: Description: The minimum version of the SSL protocol that you want CloudFront to use for HTTPS connections. Type: String Default: "TLSv1" AllowedValues: - TLSv1.2 - TLSv1.1 - TLSv1 - SSLv3 Metadata: "AWS::CloudFormation::Interface": ParameterGroups: - Label: default: "S3" Parameters: - S3BucketName - Label: default: "CloudFront" Parameters: - AlbDnsName - MinimumProtocolVersion - DefaultTTL - MaxTTL - MinTTL - ErrorCacheTTL Resources: CloudFrontLogBucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub ${S3BucketName}-cloudfront-log-bucket AccessControl: LogDeliveryWrite # CloudFront CloudFrontDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Origins: - DomainName: !Ref AlbDnsName Id: ALBOrigin #CloudFront Origins and Origin Groups CustomOriginConfig: OriginSSLProtocols: - !Ref MinimumProtocolVersion OriginProtocolPolicy: http-only HTTPPort: '80' HTTPSPort: '443' Enabled: true HttpVersion: http2 DefaultRootObject: index.html #CloudFront Behaviors DefaultCacheBehavior: TargetOriginId: ALBOrigin ViewerProtocolPolicy: redirect-to-https AllowedMethods: - GET - HEAD - DELETE - OPTIONS - PATCH - POST - PUT CachedMethods: - GET - HEAD - OPTIONS DefaultTTL: !Ref DefaultTTL MaxTTL: !Ref MaxTTL MinTTL: !Ref MinTTL ForwardedValues: Cookies: Forward: none QueryString: true #CloudFront Log Logging: Bucket: !GetAtt CloudFrontLogBucket.DomainName Prefix: Logs/prod #CloudFront Error Pages CustomErrorResponses: - ErrorCode: 400 ErrorCachingMinTTL: !Ref ErrorCacheTTL - ErrorCode: 403 ErrorCachingMinTTL: !Ref ErrorCacheTTL - ErrorCode: 404 ErrorCachingMinTTL: !Ref ErrorCacheTTL - ErrorCode: 500 ErrorCachingMinTTL: !Ref ErrorCacheTTL - ErrorCode: 502 ErrorCachingMinTTL: !Ref ErrorCacheTTL - ErrorCode: 503 ErrorCachingMinTTL: !Ref ErrorCacheTTL - ErrorCode: 504 ErrorCachingMinTTL: !Ref ErrorCacheTTL Outputs: URL: Value: !Join [ "", [ "https://", !GetAtt [ CloudFrontDistribution, DomainName ]]] S3BucketName: Value: !Ref CloudFrontLogBucket DistributionID: Value: !Ref CloudFrontDistribution |
このテンプレートでは、Amazon CloudFrontのログを集計するためのS3とALB連携したCloudFrontを作成します。
Parametersの詳細は以下になります。
- S3BucketName
- CloudFrontのログを集計するためのバケット名
- AlbDnsName
- CloudFrontのオリジンを作成する時に必要なALBのドメイン名
- MinimumProtocolVersion
- TLSのバージョン
- DefaultTTL
- キャッシュが削除される時間のデフォルト値
- MaxTTL
- キャッシュが削除される時間の最大値
- MinTTL
- キャッシュが削除される時間の最小値
- ErrorCacheTTL
- エラー画面のキャッシュが削除される時間
実際の入力画面はこのようになります。パラメータは最小、最大値なども設定することが可能です。詳しくはこちら。
実行手順
1.事前準備
ALBのドメイン名を調べる
CloudFront作成時にALBのドメイン名が必要になるので事前に調べます。
※今回は既存のAWS環境にCloudFrontを導入するのでALBは作成済みです。
EC2→ロードバランサーと選択していき、対象のALBを選択します。DNS名横にボタンがあるので押して、コピーします。
2.CloudFomationの実行
AWSのCloudFomationを選択してスタックの作成を選択します。
この画面では作成したテンプレートファイルをアップロードしたり、デザイナー画面でリソースを可視化しながらコードを書くことができます。詳しくはこちら。
次へを押し、パラメータの値を入力したら、この後の設定は特にはないので次へを押していき、リソースの作成を選択します。
リソースの作成が始まり、20分程度で完成します。
CloudFrontとS3が作成されているのを確認できたら導入は完了です。
接続の確認方法
① ブラウザでデベロッパーツールのNetworkタブを確認する。
② X-Cacheの値を確認する。
③ CloudFrontからレスポンスが返ってくるとX-Cacheの値が、以下のうちのどれかになる。
- Hit from cloudfront
- RefreshHit from cloudfront
- Miss from cloudfront
下記の画像ではX-Cacheの値がMiss from cloudfrontなのでCloudFrontからレスポンスが返却されていることがわかります。
この後、CloudFrontでヘッダーや証明書などの細かい設定はありますが、ここでは割愛します。
大変だった点
今回、CloudFormationを使って環境構築に挑戦してみましたが、CloudFormationは一部の設定が対応していない問題があるので、100%自動化するということは現状不可能でした。ですから、今回は再現できない部分は手入力してもらう形を取りました。
さらに、テンプレートのデザイナー画面ではエラーが出ていなくても実行時にエラーが発生することが多いです。AWS側が書き方のルールを定めているので公式ドキュメントをしっかりと読み込んでおかないとなかなかエラーから抜け出すことができませんでした。
最後に
CloudFrontは設定項目が多く、かつログ集計用のS3を作る必要もあります。
これをすべてのサービスに導入するには工数がかかってしまいます。加えてAWSの知識がない状態ですと手作業でのリソース作成は、ミスが発生しやすく余計に時間がかかります。
そこで、このような問題を解決し、業務の効率化を図るためにCloudFormationで環境構築の自動化をしました。
今回の業務を通して、各AWSリソースの結びつきと汎用性の高さを知ることができるようになり、ますますAWSへの関心が高まりました。
手作業している時間を別の時間に回せるので、効率化を図りたい方はぜひCloudFormationを試してみてください。
おまけ
CloudFrontのオリジンにはALBだけでなく、S3にも設定することができます。
テンプレートはこちら
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
AWSTemplateFormatVersion: 2010-09-09 Description: Static contents distribution using S3 and CloudFront. Parameters: S3BucketName: Description: Type of this BacketName. Type: String Resources: # S3 bucket S3Bucket: Type: AWS::S3::Bucket DeletionPolicy: Retain Properties: BucketName: !Sub ${S3BucketName} AccessControl: PublicRead WebsiteConfiguration: IndexDocument: index.html CloudFrontLogBucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub ${S3BucketName}-cloudfront-log-bucket AccessControl: LogDeliveryWrite # S3 bucket policy AssetsBucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref S3Bucket PolicyDocument: Statement: - Action: s3:GetObject Effect: Allow Resource: !Sub arn:aws:s3:::${S3Bucket}/* Principal: AWS: !Sub arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${CloudFrontOriginAccessIdentity} # CloudFront CloudFrontDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Origins: - Id: S3Origin DomainName: !GetAtt S3Bucket.DomainName S3OriginConfig: OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity} Enabled: true DefaultRootObject: index.html Comment: !Sub ${AWS::StackName} distribution DefaultCacheBehavior: TargetOriginId: S3Origin ForwardedValues: QueryString: false ViewerProtocolPolicy: redirect-to-https Logging: Bucket: !GetAtt CloudFrontLogBucket.DomainName Prefix: Logs/prod/ HttpVersion: http2 CloudFrontOriginAccessIdentity: Type: AWS::CloudFront::CloudFrontOriginAccessIdentity Properties: CloudFrontOriginAccessIdentityConfig: Comment: !Ref AWS::StackName Outputs: URL: Value: !Join [ "", [ "https://", !GetAtt [ CloudFrontDistribution, DomainName ]]] S3BucketName: Value: !Ref S3Bucket DistributionID: Value: !Ref CloudFrontDistribution |