8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

概要

静的サイトをGCPで簡単に構築する方法を共有します!
今回はTerraformを使用し、下記のような構成でサイトを構築しようと思います。

  • GCS
    • GLBからのリクエストを受付、HTML, CSS, JSを表示する
  • GLB
    • http→httpsリダイレクトを行う
    • GCSにリクエストを転送
  • 証明書
    • GCPのマネージド証明書を利用

構成図.png

GCSではhtaccessなどが動作しないので、GLBでhttpsリダイレクトの役割を持たせることにします。

リダイレクトの主な仕組みについては、公式ドキュメントがわかりやすく解説してくれてますので、興味があればご覧ください!
また、証明書の更新もGCPのマネージド証明書を利用すると自動でやってくれるので、マネージド証明書を利用します。

Terraformによる構築

共通設定

provider.tf

provider "google" {
  credentials = file(var.credentials)
  project     = var.project
}

provider "google-beta" {
  credentials = file(var.credentials)
  project     = var.project
}

GCSバケット

下記の内容でバケットを構築します。

  • バージョニングを有効化
  • 特殊ページを設定
    • index.html
    • 404.html
  • アクセス権を均一化

main.tf

resource "google_storage_bucket" "bucket" {
  name          = var.advent_calendar_bucket.bucket_name
  location      = var.advent_calendar_bucket.region
  storage_class = var.advent_calendar_bucket.storage_class
  force_destroy = true
  labels = {
    project = "advent-calendar"
  }
  # バージョニングの有効化
  versioning {
    enabled = true
  }
  # 特殊ページの割り当て
  website {
    main_page_suffix = "index.html"
    not_found_page   = "404.html"
  }
  # アクセスレベルを均一化
  uniform_bucket_level_access = true
}

# 誰でも見れるように一般公開(allUsers)にする
resource "google_storage_bucket_iam_member" "advent_calendar_bucket_iam_member_object_viewer" {
  bucket = google_storage_bucket.bucket.name
  role   = "roles/storage.legacyObjectReader"
  member = "allUsers"
}

variables.tf

variable "advent_calendar_bucket" {
  default = {
    bucket_name   = "バケット名"
    region        = "asia"
    storage_class = "MULTI_REGIONAL"
  }
}

GCSのアクセス制御はGCPの推奨アーキテクチャ通り、アクセスレベルを均一にしています。公開するバケットにてオブジェクト単位でアクセス制御を行っていると、オブジェクトを新しく作成するたびにアクセス権を設定しないといけません。こういった作業は事故の元なので、アクセスレベルは均一にしています。

また、指定されたページがない時やhttps://ドメイン名/にアクセスされた時にデフォルトのページを表示したいので特殊ページを設定しています。

そのほかの特徴として、google_storage_bucket_iam_memberを使用してバケットに紐づくIAMを設定しています。今回はバケットを公開するため、allUsersroles/storage.legacyObjectReaderを付与しています。
バケット公開をするにあたってroles/storage.objectViewerでも良いですが、こちらの権限はバケットのオブジェクト一覧を表示する権限があります。権限を最小化するため今回はroles/storage.legacyObjectReaderを付与しています。
参考公式リンク

GLB

main.tf(GCSのリソースと同じファイルに書く想定で書いてます。)

resource "google_compute_managed_ssl_certificate" "cert" {
  provider = google-beta
  name     = var.certificate_name

  managed {
    domains = [var.certificate_domain]
  }
}

resource "google_compute_global_address" "lb_ip_address" {
  name = var.global_address_name
}

resource "google_compute_backend_bucket" "lb_backend_bucket" {
  name        = var.backend_name
  bucket_name = google_storage_bucket.bucket.name
  enable_cdn  = var.enable_cdn
}

# http→httpsリダイレクトを行うためにLBを二つに分ける
resource "google_compute_url_map" "lb_http_url_map" {
  name = var.http_lb_name
  # default_url_redirectを指定する場合は、default_servicは指定してはいけない
  default_url_redirect {
    strip_query    = false
    https_redirect = true
  }
}

resource "google_compute_url_map" "lb_https_url_map" {
  name            = var.https_lb_name
  default_service = google_compute_backend_bucket.lb_backend_bucket.self_link
}

resource "google_compute_target_http_proxy" "lb_http_proxy" {
  name    = var.http_proxy_name
  url_map = google_compute_url_map.lb_http_url_map.self_link
}

resource "google_compute_target_https_proxy" "lb_https_proxy" {
  name             = var.https_proxy_name
  url_map          = google_compute_url_map.lb_https_url_map.self_link
  ssl_certificates = [google_compute_managed_ssl_certificate.cert.id]
}

resource "google_compute_global_forwarding_rule" "lb_http_forwarding_rule" {
  name       = var.http_forwarding_rule_name
  target     = google_compute_target_http_proxy.lb_http_proxy.self_link
  ip_address = google_compute_global_address.lb_ip_address.address
  port_range = var.http_forwarding_rule_port
}

resource "google_compute_global_forwarding_rule" "lb_https_forwarding_rule" {
  name       = var.https_forwarding_rule_name
  target     = google_compute_target_https_proxy.lb_https_proxy.self_link
  ip_address = google_compute_global_address.lb_ip_address.address
  port_range = var.https_forwarding_rule_port
}

variables.tf

variable "certificate_name" {
  default = "advent-calendar2020"
}
variable "certificate_domain" {
  default = "example.com"
}
variable "backend_name" {
  default = "advent-calendaer2020-backend"
}
variable "http_lb_name" {
  default = "advent-calendar2020-http-lb"
}
variable "https_lb_name" {
  default = "advent-calendar2020-https-lb"
}
variable "http_proxy_name" {
  default = "advent-calendar2020-http-proxy"
}
variable "https_proxy_name" {
  default = "advent-calendar2020-https-proxy"
}
variable "http_forwarding_rule_name" {
  default = "advent-calendar2020-http-forwarding-rule"
}
variable "https_forwarding_rule_name" {
  default = "advent-calendar2020-https-forwarding-rule"
}
variable "http_forwarding_rule_port" {
  default = "80"
}
variable "https_forwarding_rule_port" {
  default = "443"
}
variable "enable_cdn" {
  default = true
}
variable "global_address_name" {
  default = "advent-calendar2020-address"
}

Googleのロードバランサーは複数のリソースから成り立っています。
そのため、必要なリソースをterraformで定義しています。特徴としては、今回HTTPSリダイレクトを行っているため、 default_url_redirecturl_mapで定義しています。
この時、クエリストリングを削ることもできるようですが、特に削る必要がないので削らないように設定をしています。

各リソースがどう関係しているかは公式の下記画像を見ていただくとわかりやすいと思います。構築したサイトと異なる点としてはCompute HTTPS load balancerBackend Serviceの部分がBackend Bucketになっているだけですね。

image.png
https://cloud.google.com/load-balancing/docs/https/setting-up-http-https-redirect

特殊ページのアップロード

下記のような簡単なファイルをGCSバケットにアップロードします。

index.html

<h1>Hello world.</h1>

404.html

<h1>Not Found.</h1>

構築したページ

TOP

スクリーンショット 2020-12-23 12.07.05.png

指定したパスがない時

スクリーンショット 2020-12-23 12.06.47.png

ちょっと困ったところ

GCSを用いたGLBでは、IPアドレス制御などによるアクセス制限が行えない

一般的にGLBでアクセス制御を行うには下記の方法があります。

しかし、両方とも対応しているのはバックエンドサービスのみという事実。。。バックエンドバケットは対応していませんでした。。。

すでに機能追加リクエストがGCPにあがっているものの、まだ対応されていません。

こちら、ステージング環境を社内のみ・関連会社のみに共有したいという場合などにとても困ります。
また、Cloud Armorを設定したとしてもGCSのオブジェクト自体は公開されているので、公開した際に発行されるGCS専用URLがわかると誰でもアクセスできてしまいます。

対応方法

バケットの公開範囲を絞る方法としてVPC Service Controlがあります。

こちらを用いることで、BigQueryやGCSといった非VPCリソースに対してIP制限を行うことが可能だと聞いています。
ただ、こちらの設定はGCPの組織に紐づく設定のようで、十分に検討・検証を行わないと既存のトラフィックにも影響が出てしまう可能性があります。

良かったところ

  • HTTPSリダイレクトをGLBに持たせることができる
  • Cloud CDNで簡単にキャッシュできる
  • マネージド証明書による証明書管理
    • コストなし・自動更新

証明書の更新作業やリダイレクト処理をGCPが賄ってくれるのが非常にありがたいと感じました!
また、terraformのコードさえ書いて仕舞えば、同じ構成のリソースを簡単に作れるので一時的なサイトなどを構築する際は非常に便利だと感じています。

まとめ

GCS・GLBを利用することで簡単に静的サイトを構築することができました。
しかし、IPアドレスの制限などを行いたい場合はひと工夫ふた工夫必要になってきます🤥

皆さんにおすすめしたいこととしては、「バックエンドサービス(GCE)でできるのだから、バックエンドバケット(GCS)でもできるでしょ〜🤪」などといった思い込みはもたずに、案件が来たらちゃんと実現できるか公式ドキュメントを漁りましょう!(こんな思い込みをしてて痛い目にあいました。)

それでは、良いお年を!

8
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
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?