LoginSignup
5
3

More than 3 years have passed since last update.

circleciとterraformで静的サイトのデプロイ

Last updated at Posted at 2019-12-01

はじめに

circleciとterraformでreactなどで作られた静的サイトをデプロイするまでの例です。

terraformでs3とcloudfrontの構築を行い、その後circleciでbuildした静的ファイルのs3へのアップロードを行います。

route53で独自ドメインからcloudfrontへのaliasレコード作るのはここではやってないです。cloudfrontのDistributionsを作ったらデフォルトで作ってくれる *.cloudfront.net のドメインで静的サイトが閲覧できるようにするまでがゴールです。

terraform

awsの設定

regionの指定、awsのproviderのverの固定、利用するcredentials(~/.aws/credentials)の指定をします。

aws.tf
provider "aws" {
  region  = "ap-northeast-1"
  profile = "private"
  version = "2.40.0"
}

terraformの設定

利用するverの固定します。

terraform.tf
terraform {
  required_version = "0.12.6"
}

s3の設定

バケットの作成です。

s3.tf
resource "aws_s3_bucket" "connpass-map-front" {
  bucket = "connpass-map-front"
  acl    = "private"
  region = "ap-northeast-1"
}

cloudfrontとiamの設定

一旦全体のtfファイルを載せます。

cloudfront.tf
resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
  comment = "origin access identity for s3"
}

resource "aws_cloudfront_distribution" "s3_distribution" {
  origin {
    domain_name = "${aws_s3_bucket.connpass-map-front.bucket_regional_domain_name}"
    origin_id   = "${aws_s3_bucket.connpass-map-front.id}"

    s3_origin_config {
      origin_access_identity = "${aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path}"
    }
  }

  enabled             = true
  is_ipv6_enabled     = false
  comment             = "connpass-map-front"
  default_root_object = "index.html"

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "${aws_s3_bucket.connpass-map-front.id}"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

iam.tf
data "aws_iam_policy_document" "cf_to_s3_policy" {
  statement {
    actions = ["s3:GetObject"]

    resources = [
      "${aws_s3_bucket.connpass-map-front.arn}",
      "${aws_s3_bucket.connpass-map-front.arn}/*",
    ]

    principals {
      type        = "AWS"
      identifiers = ["${aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn}"]
    }
  }
}

resource "aws_s3_bucket_policy" "cf-to-s3" {
  bucket = "${aws_s3_bucket.connpass-map-front.id}"
  policy = "${data.aws_iam_policy_document.cf_to_s3_policy.json}"
}

Origin Access Identityの設定

cloudfrontからのみ、s3にアクセスできるようにするため、Origin Access Identityの設定をしています。

まず、cloudfrontでorigin_access_identityのリソースを作成し、

cloudfront.tf
resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {

そのリソースが紐づいた形のポリシーを作成。

iam.tf
data "aws_iam_policy_document" "cf_to_s3_policy" {

作成したポリシーを作成したs3のバケットに紐付け。これでcloudfront経由でのみs3にアクセスできる状態になります。

iam.tf
resource "aws_s3_bucket_policy" "cf-to-s3" {

cloudfrontのdistributionの作成

cloudfront.tf
resource "aws_cloudfront_distribution" "s3_distribution" {

distributionのリソースで、特筆するところとしては
s3のバケットを指定する箇所(originとか)は作成したバケットを指定するようにすること、

cloudfront.tf
  origin {
    domain_name = "${aws_s3_bucket.connpass-map-front.bucket_regional_domain_name}"
    origin_id   = "${aws_s3_bucket.connpass-map-front.id}"

default_root_object にindex.htmlを指定すること、(buildしたファイル群にindex.htmlがある前提)

cloudfront.tf
  default_root_object = "index.html"

viewer_certificate は独自ドメインは利用しないのでデフォルトのままにすること

cloudfront.tf
  viewer_certificate {
    cloudfront_default_certificate = true
  }

これで terraform apply すれば、s3とcloudfrontが作成されます。

tfstateの管理

terraform apply するとローカルにtfstateが作成されますが、tfstateはs3かterraform cloudで管理するのが定石になってます。(チームで開発するのであれば)

ここではterraform cloudを使ってtfstateの管理をやってみます。
流れとしては以下のような感じです。

1) [ブラウザ] terraform cloudでアカウント作成
2) [ブラウザ] organizationの作成
3) [ブラウザ] terraform cloudにアクセスするためのトークンの作成
4) [ローカル] 3で作成したトークンを $HOME/.terraformrc に書く
5) [ローカル] tfstateをterraform cloudに保存するように設定
6) [ブラウザ] workspaceの設定でawsの操作はローカルから行うように変更
7) [ローカル] terraform init

1 〜 3

1 〜 3までの流れは以下の記事に画像付きで載ってます。
https://dev.classmethod.jp/cloud/aws/manage-tfstate-terraform-cloud/

4) [ローカル] 3で作成したトークンを $HOME/terraformrc に書く

$HOME/.terraformrc
credentials "app.terraform.io" {
  token = "作成したトークンをここに"
}

5) [ローカル] tfstateをterraform cloudに保存するように設定

terraform.tfに設定を追加。

terraform.tf
terraform {
  required_version = "0.12.6"

  backend "remote" {
    organization = "connpass-map-front"

    workspaces {
      name = "connpass-map-front-workspace"
    }
  }
}

6) [ブラウザ] workspaceの設定でawsの操作はローカルから行うように変更

ローカルでterraformを動かしている場合、awsのcredentilasはローカルの ~/.aws/credentials をみに行きます。terraform cloudを使う場合、そのawsの操作をterraform cloudから行うか、ローカルから行うか設定できます。(workspaceの設定)

スクリーンショット 2019-12-01 13.14.48.png

ここがremote(デフォルト値)となっていると、 terraform applyterraform plan を実行した際、ローカルの ~/.aws/credentials を見にいかなくなるので以下のエラーが発生します。

Error: No valid credential sources found for AWS Provider.
        Please see https://terraform.io/docs/providers/aws/index.html for more information on
        providing credentials for the AWS Provider

Localに設定すればawsの操作はローカルから実行し、tfstateの管理のみをterraform cloudが行うようになるので上記のエラーは出なくなります。

7) [ローカル] terraform init

terraform cloudにアクセスできたらこんな感じでinitが成功します。
これで terraform apply すればtfstateがterraform cloudにあがっていく状態になりました。

$ terraform init

Initializing the backend...

Successfully configured the backend "remote"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "aws" (hashicorp/aws) 2.40.0...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

ここまでで、環境は整いましたが、肝心のs3のバケットには何も入っていないので、バンドルされた静的ファイルをs3にアップロードする流れをcircleciで設定します。

circle ci

npm run build でビルドしたファイルを/buildに配置するようになっている前提です。

.circleci/config.yml
version: 2.1
orbs:
  aws-s3: circleci/aws-s3@1.0.11
executors:
  default:
    docker:
      - image: circleci/node:12
    working_directory: ~/repo
commands:
  npm_install:
    steps:
      - restore_cache:
          keys:
            - v2-dependencies-{{ checksum "package.json" }}
            - v2-dependencies-
      - run: npm install
      - save_cache:
          paths:
            - node_modules
          key: v2-dependencies-{{ checksum "package.json" }}
jobs:
  build:
    executor: default
    steps:
      - checkout
      - npm_install
      - run: npm run build
      - persist_to_workspace:
          root: .
          paths:
            - "*"
            - ".*"
  deploy:
    executor: default
    steps:
      - attach_workspace:
          at: ~/repo
      - aws-s3/sync:
          from: build
          to: s3://connpass-map-front
          overwrite: true
workflows:
  version: 2
  build-deploy:
    jobs:
      - build
      - deploy:
          requires:
            - build
          filters:
            branches:
              only:
                - /feature.*/
                - master

aws-s3のorbsを使っているので、これでconnpass-map-frontのバケットにbuild配下のファイルがsyncされます。

.circleci/config.yml
      - aws-s3/sync:
          from: build
          to: s3://connpass-map-front
          overwrite: true

また、このs3のorbsを使うためには、事前にcircleciの環境変数にAWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_REGIONを設定しておく必要があります。

このcircleciの設定で、masterにブランチがマージされる度にs3へ最新のバンドルファイルがアップロードされます。

アップロードが終わったあとで、作成したcloudfrontのDistributionsのドメイン(*.cloudfront.net)にアクセスすればアップロードした静的なサイトが閲覧できます。

参考

https://qiita.com/natsumisawa/items/404217208ab1c96d8719
https://dev.classmethod.jp/cloud/aws/manage-tfstate-terraform-cloud/

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3