頑張るときはいつも今

自称エンジニアのゴリラです。日々精進。

【AWS】TerraformでS3バケットを構築してみる

新年あけましておめでとうございます

スキルゼロの監視オペレータ(今年からWebエンジニアにジョブチェンジします)です。

今年は厳しい1年になるかもですので、糞ほど勉強してスキル向上に努めて、より良い1年にしたいと思います。

この記事で何をするか

AWSの基本的なサービスや操作を理解するためにAWSアソシエイトを勉強しています。

ただUdemyの動画や本を読んでいてもつまらないなと思い、インフラ構成管理ツールのTerraformと平行で勉強している最中です

前回は、VPC構築〜EC2構築までをやっていましたが今回はS3を作成したいと思います。

大まかな記事の流れは下記です。

  1. S3の基礎知識
    • よくある本とかWebで入手できる内容を自分用にまとめたものです
  2. Terraformでバケット(パブリックアクセス無し)作成
    • AWS CLIを利用してファイルのアップロードから内容確認、ダウンロード(Pull)までを行います
  3. Terraformでバケット(パブリックアクセス有り)作成
  4. HTMLファイルをホスティングしてアクセスできる事を確認します。

この記事で得られるもの

  • S3って何か、基本的なサービスについて理解できる
  • Terraformを使って、S3バケットを構築することができる

S3の基礎知識

そもそもS3って?

AWSが、マネージド型で提供しているストレージサービスです。

S3以外のストレージサービスにはEBS,インスタンスストアEFSなどがありますが、今回のS3はオブジェクト型のストレージとなります。

各ストレージの特徴を簡単にまとめました。

ストレージタイプ AWSサービスの例 特徴 プロトコル
ブロック型 EBS、インスタンスストア 保存されるファイルやオブジェクトのデータを複数のブロックに分散する。頻繁かつ高速なアクセスが必要な場合の用途 SATA, SCSI
オブジェクト型 S3 ファイルに任意のメタデータを追加してオブジェクトとして管理する。作成済みデータに対するCRD(Create, Read, Delete)のみが可能 HTTP(S)
ファイル型 EFS ブロックストレージ上にファイルシステムを構築。複数クライアントからNW経由でファイルアクセスするデータ共有など。ファイルサーバ的なやつ NFS

何が特徴的なのか?

  • 高い耐久性(イレブンナイン)と可用性(99.99%)
    • ストレージクラスがSTANDARDの場合
    • S3作成時にリージョンを選択するが、S3は3箇所以上のAZに同期される
  • HTTP(S)を利用して、AWS CLIやWEB APIから操作が可能
  • 結果整合性モデルを採用している
    • 更新・削除の結果が反映されるまで時間がかかる(Eventual Consistency Read)
    • 新規登録は即時にデータが反映される(Consistency Read)

何が保存されるのか?

データ(オブジェクト)は指定したリージョンのバケット(データの入れ物)に保存されます。

  • バケット
    • オブジェクトを保存する領域。
    • バケット名はAWS内で一意にする必要がある。
  • オブジェクト

    • S3に格納されるデータ本体。
    • 各オブジェクトにはキー(オブジェクト名)が付与され、「バケット名+キー名+バージョンID」で必ず一意なURLが作成される
  • メタデータ

    • オブジェクトを管理するための情報
    • オブジェクトの作成日時やサイズなどのメタデータやアプリケーションが必要な情報(ユーザ定義)が保持できる
  • バージョンID

f:id:wa_football_1120:20200104102927j:plain

S3の機能

ここからはバージョン管理やライフサイクルなどのS3で使える機能群を簡単にまとめていきます。

アクセス管理

S3のバケット(もしくはオブジェクト)に対するアクセス管理には、3種類が存在します

ポリシーに関してはこちらの記事がとても参考になりました

  • バケットポリシー
    • バケット単位でアクセスを制御する
    • バケットに保存するオブジェクト全てに適用されるため、バケット全体的なアクセス制御をするときに有効
    • JSON形式でポリシーは記述する
 {
  "Version":"2012-10-17",
  "Statement":[
    {
      "Sid":"PublicRead",
      "Effect":"Allow",
      "Principal": "*",
      "Action":["s3:GetObject"],
      "Resource":["arn:aws:s3:::examplebucket/*"]
    }
  ]
}
  • IAMポリシー

    • バケットポリシーと同じようにJSON形式で制御をかける
    • 特定ユーザへのアクセス権を設定する場合にはこちらを使った方が良さそう
  • ACL

    • バケット/オブジェクトごとに付与された対象のアクセスを制御できる

暗号化

S3に保存するデータを暗号化して保存することができる

  • サーバサイド暗号化
    • S3のサーバリソースを利用して格納データを暗号化
  • クライアントサイド暗号化
    • 暗号化のプロセスをクライアント(利用者)側で用意する
    • AWS KMSやクライアントが保持するマスターキーで暗号化する
      • オブジェクトのメタデータを元にどのキーで復号化するのかS3側で判別する

レプリケーション

バケット内のデータを異なる(もしくは同一の別AZ)のバケットレプリケーション(同期)することが可能

バックアップやBCP用途に使うことができる

バージョン管理

バケット単位で有効もしくは無効を管理する

ライフサイクル管理

バケットに保存されたオブジェクトの利用頻度に基づいてライフサイクルを管理できる。

ライフサイクル管理によって、

  • 古いデータはより安く保存できるストレージサービス(Glacier)に保存する
  • それよりも古いデータは削除する

といったことができるようになる。

  • 移行アクション

    • データの利用頻度に応じて、ストレージクラスを変更するアクション
      • 一定期間がすぎると利用頻度が低くなるオブジェクトをアーカイブとして保存するなど
  • 有効期限アクション

  • 指定された期限を超えたオブジェクトをS3から削除する
  • S3は保存容量に応じて課金されるため、不要なデータを削除することでコスト削減が見込める

f:id:wa_football_1120:20200104112009j:plain

ホスティング

静的コンテンツ(HTML, 画像や動画, Javascript)をバケット内に保管して、Webサイトとしてホスティングさせることができる。

独自ドメインホスティングする場合にはバケット名とドメイン名を一致させる必要がある

Terraformでバケットを作成する

本記事で扱っていない機能だとかオプションは公式を参照してください。

https://www.terraform.io/docs/providers/aws/r/s3_bucket.html

下準備

  • バケットポリシーも設定しいきたいので、IAMユーザを作成しておきます。
  • ポリシーは何もアタッチしていない状態。
aws iam list-users \
> --query 'Users[].UserName'
[
    Your User Name,
    "unauthorize-user"
]
aws iam list-attached-user-policies \
> --user-name 'unauthorize-user'
{
    "AttachedPolicies": []
}

パブリックアクセス無しのバケットを作成

作成するバケットの要件は下記とします。

  • ログ保管用とデータ保管用のバケット2つを作成する
  • バケットは共に外部(インターネット)に公開しない
  • バケットにCRDできるのは、作成してユーザ(上で言うところのYour User Name)のみ
    • unauthorize-userからはアクセスできないことを確認する

実装コード

ログ保管用バケット

  • リソースはaws_s3_bucketで作成ができる
  • bucketバケット名を指定する
    • 前述だが、グローバルで一意な名前でなければならない
  • aclバケットへのACLを設定
    • log-delivery-writeでログの書き込みができるはず
  • lifecycle_ruleでは、有効期限アクションを設定
##############################################
# Create Log  Backet for Private Bucket      #
##############################################
resource "aws_s3_bucket" "logging_for_sapmle_private_bucket" {
    bucket       = "logging-for-private-turedure-study-bucket"
    acl          = "log-delivery-write"

    lifecycle_rule {
        enabled  = true

        expiration {
            days = "180"
        }
    }
}

データ保管用のバケット

重複している部分の説明は割愛します。

  • versioningでバージョニングを有効化
  • server_side_encryption_configurationでサーバサイド暗号化を有効化
  • loggingで上で作成したバケットにログを記録するように設定
  • 別リソースaws_s3_bucket_public_access_blockでパブリックへオブジェクトが公開されることを防止
##############################################
# Create Private Bucket                      #
##############################################

resource "aws_s3_bucket" "sample_private_bucket" {
    bucket      = "private-turedure-study-bucket"

    versioning {
        enabled = true
    }

    server_side_encryption_configuration {
        rule {
            apply_server_side_encryption_by_default {
                sse_algorithm = "AES256"
            }
        }
    }
    
    logging {
        target_bucket = "${aws_s3_bucket.logging_for_sapmle_private_bucket.id}"
        target_prefix = "log/"
    }
}

resource "aws_s3_bucket_public_access_block" "for_sample_private_bucket" {
    bucket                  = "${aws_s3_bucket.sample_private_bucket.id}"
    block_public_acls       = true
    block_public_policy     = true
    ignore_public_acls      = true
    restrict_public_buckets = true
}

結果確認

  • terraform plan ~ terraform applyを実行後にバケットの一覧を取得してみると作成ができている
aws s3 ls
2020-01-04 16:20:39 logging-for-private-turedure-study-bucket
2020-01-04 16:20:56 private-turedure-study-bucket
  • 続いてファイルをアップロードしてみる
aws s3 cp ./sample.txt s3://private-turedure-study-bucket
upload: ./sample.txt to s3://private-turedure-study-bucket/sample.txt
aws s3 ls s3://private-turedure-study-bucket
2020-01-04 16:24:37         20 sample.txt
  • 下準備で作成したユーザ(unauthorize-user)がバケット内を参照しようとすると拒否される
aws s3 ls s3://private-turedure-study-bucket --profile terraform

An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied

試しに作成したバケットの設定は上手くいってるみたいです。

下準備で作成したユーザがアクセスできるようにバケットポリシーを作成していこうと思います。

バケットポリシーを作成

新しくリソースaws_s3_bucket_policyを作成して、バケットポリシーをアタッチをしていきます。

  • bucketでアタッチするバケットを指定
  • policyには、ヒアドキュメントでJSONをインラインで記述
    • 作成したユーザ(unauthorize-user)がバケット内を参照できるように設定
resource "aws_s3_bucket_policy" "private_bucket_policy" {
    bucket                  = "${aws_s3_bucket.sample_private_bucket.id}"
    policy                  =<<POLICY
{
    "Version": "2012-10-17",
    "Id": "PrivateBucketPolicy",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::{作成したユーザのARN}"
            },
            "Action": "S3:ListBucket",
            "Resource": "${aws_s3_bucket.sample_private_bucket.arn}"
        }
    ]
}
POLICY
}
  • terraform planからterraform applyをもう一度実行して、S3のオブジェクトを取得ができている
aws s3 ls s3://private-turedure-study-bucket --profile terraform
2020-01-04 16:24:37         20 sample.txt

プライベートバケットの作成ハンズオンはここで終わり!

バケット(パブリックアクセス有り)作成

HTMLをホスティングしてインターネットから参照できるバケットを作成します。

先ほどとほぼ同じではあるが、作成するバケットの要件は下記とします。

ホスティング用のバケット

ログ保管用のバケットは先ほどとほぼ同じなので割愛します

  • websiteホスティングサービスを有効化
    • index_documentでデフォルトルートのオブジェクトを指定
    • error_documentで400番系のエラーが発生した際に表示するオブジェクトを指定する
  • バケットポリシーは、AWSの公式ドキュメントをそのまま設定
##############################################
# Create Public Bucket                       #
##############################################
resource "aws_s3_bucket" "hosting_bucket" {
    bucket = "hosting-turedure-study-bucket"

    versioning {
        enabled = true
    }

    acl    = "public-read"

    website {
        index_document = "index.html"
        error_document = "error.html"
    }
}

resource "aws_s3_bucket_policy" "hosting_bucket_policy" {
    bucket = "${aws_s3_bucket.hosting_bucket.id}"
    policy = <<POLICY
{
    "Version": "2012-10-17",
    "Id": "HostingPolicy",
    "Statement": [
        {
            "Sid": "PubcliReadForGetBucketObjects",
            "Effect": "Allow",
            "Principal": "*",
            "Action": ["s3:GetObject"],
            "Resource": "${aws_s3_bucket.hosting_bucket.arn}/*"
        }
    ]
}
POLICY
}
  • terraform apply後にindex.htmlとerror.htmlをバケットに配置
aws s3 cp index.html s3://hosting-turedure-study-bucket
upload: ./index.html to s3://hosting-turedure-study-bucket/index.html
aws s3 cp error.html s3://hosting-turedure-study-bucket
upload: ./error.html to s3://hosting-turedure-study-bucket/error.html
  • curlindex.htmlerror.html表示できることが確認できる
curl http://hosting-turedure-study-bucket.s3-website-ap-northeast-1.amazonaws.com/
<!doctype html>
<html lang="ja">
    <head>
        <title>Sample Hosting</title>
    </head>
    <body>
        <h1>This is Sample Hosting Page </h1>
    </body>
</html>
curl http://hosting-turedure-study-bucket.s3-website-ap-northeast-1.amazonaws.com/error
<!doctype html>
<html lang="ja">
    <head>
        <title>Sample Hosting</title>
    </head>
    <body>
        <h1>Sorry, error is occured</h1>
    </body>
</html>

まとめ

  • S3はAWSが提供しているマネージド型のオブジェクトストレージ
  • 操作にはHTTP(S)プロトコルとして提供している
  • ホスティングやログ保管、BCP対策を実現機能群が提供されている

新年一発目の投稿がこれで終わりです。

(実用的な記事を投稿するにはまだまだ文章力や知識も足りていなくて自己嫌悪ですね。。)