Cache Distributionパターン(CloudFront+S3)を一撃で設定する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

静的コンテンツで構成され、ファイルサイズが大きく、かつアクセス数が多いWebサイトの場合、S3にWebコンテンツを配置し、その前にCloudFrontを置くのが一般的な構成です。クラウドデザインパターンで言うところのCache Distributionパターンですね。

cdp_cd

しかしこの構成、Webサイト数が多くなると設定の手間が膨大になります。流れとしては

  1. S3にバケットを作成する。
  2. バケットポリシーを設定する。
  3. バケットにStatic Website Hostingの設定をする。
  4. CloudFrontでDistributionを作成する。
  5. DNSのWebサイトのレコードを変更し、CloudFrontへ向ける。

をサイト数分繰り返します。面倒臭い。何とか一撃で出来ないものか。ということでAWS SDK for Rubyでやりました。

ソース

※IAM Role for EC2でPowerUsers権限を与えたEC2から実行することを想定しているので、ACCESS_KEY等の設定はしていません。

#!/usr/bin/ruby

require 'rubygems'
require 'aws-sdk'

# Create Bucket
s3 = AWS::S3.new
chkbckt1 = s3.buckets[ARGV[0]]

if chkbckt1.exists? == true then 
  puts "Failed: S3 bucket is already exists."
  exit(1) 
end

bucket = s3.buckets.create(ARGV[0])

# Configure Bucket Policy
policy = AWS::S3::Policy.new
policy.allow(
  :actions => [:get_object],
  :resources => "arn:aws:s3:::#{ARGV[0]}/*",
  :principals => :any)

bucket.policy = policy

# Configure Static Website Hosting
bucket.configure_website do |cfg|
  cfg.index_document_suffix = 'index.html'
  cfg.error_document_key = 'error.html'
end

chkbckt2 = s3.buckets[ARGV[0]]
if chkbckt2.exists? == false then
  puts "Failed: S3 bucket not created."
  exit(1)
end

# Create CloudFront distributions
cf = AWS::CloudFront.new

cf_config = {
  :distribution_config => {
    :caller_reference => Time.now.nsec.to_s,
    :aliases => {
      :quantity => 1,
      :items => [
      	"#{ARGV[0]}"
      ]
    },
    :origins => {
      :quantity => 1,
      :items => [
        :id => "S3-#{ARGV[0]}.s3.amazonaws.com",
        :domain_name => "#{ARGV[0]}.s3.amazonaws.com",
        :custom_origin_config => {
	  :http_port => 80,
          :https_port => 443,
          :origin_protocol_policy => "http-only"
        }
      ]
    },
    :default_cache_behavior => {
      :target_origin_id => "S3-#{ARGV[0]}.s3.amazonaws.com",
      :forwarded_values => {
        :query_string => false,
        :cookies => {
          :forward => "none"
        }
      },
      :trusted_signers => {
        :enabled => false,
        :quantity => 0
      },
      :viewer_protocol_policy => "allow-all",
      :min_ttl => 0
    },
    :cache_behaviors => {
      :quantity => 0
    },
    :price_class => "PriceClass_All",
    :default_root_object => "index.html",
    :logging => {
      :enabled => false,
      :bucket => "",
      :prefix => "",
      :include_cookies => false
    },
    :comment => "",
    :enabled => true
  }
}

begin
	cf.client.create_distribution(cf_config)
	puts "Create Distribution Success!"
rescue
	puts "Failed: Distribution not created."
	bucket.delete
end

実行

こんな感じでスクリプトファイルの後にドメイン名を引数として付けて実行します。

./cdonepunch.rb www.smokeymonkey.me

S3

バケットポリシーはこうなります。

{
	"Version": "2008-10-17",
	"Id": "1234567890",
	"Statement": [
		{
			"Sid": "1234567890",
			"Effect": "Allow",
			"Principal": {
				"AWS": "*"
			},
			"Action": "s3:GetObject",
			"Resource": "arn:aws:s3:::www.smokeymonkey.me/*"
		}
	]
}

そしてStatic Website Hostingはこんな感じ。

res1

CloudFront

各設定はこんな感じになります。

General

res2

Origins

s3

Behaviors

res4

まとめ

ここではログ保存とかキャッシュ期間とかは考慮していないのでその辺は要件に併せて調整が必要です。が、もちろんそれもこのスクリプトを改造するだけで出来ます。

AWS SDK for Rubyはだいたいなんでも出来て優秀だなぁと思います。この辺の業務改善スクリプトは今後も作っていきたいし、作ったものはDevelopers.IOでアウトプットしていきます!