ドメインを使用した静的ホスティング

アーキテクチャ

ドメインを使用した静的ホスティングは、シンプルな静的ウェブサイト作成者であり、ウェブサイトを提供するためのストレージ バケットを使用するシンプルなサイトを作成します。

  • ストレージ - ファイル ストレージ - Cloud Storage
  • ネットワーキング - ロード バランシング - Cloud ロードバランサ
  • ネットワーキング - DNS - Cloud DNS
  • ドメイン管理 - 登録事業者 - Cloud Domains

スタートガイド

Cloud Shell でソースコードのコピーへの次のリンクをクリックします。その後、1 つのコマンドでプロジェクト内のアプリケーションの作業コピーがスピンアップされます。

Cloud Shell で開く

GitHub でソースコードを見る


ドメインを使用した静的ホスティングのコンポーネント

ドメインと静的ホスティングのアーキテクチャでは、いくつかのプロダクトを使用しています。 以下に、関連動画、プロダクト ドキュメント、インタラクティブ チュートリアルへのリンクを含めた、コンポーネントの詳細を示します。
動画 ドキュメント チュートリアル
Cloud Storage Cloud Storage は、http(s) を介してファイル ストレージと画像の一般公開を提供します。
Cloud DNS Cloud DNS では DNS 管理が可能です。Google の既存の DNS インフラストラクチャを使用して独自の DNS インフラストラクチャを実行できます。
Cloud Load Balancing Google Cloud のロードバランサを使用すると、ストレージ バケットの前にロードバランサを配置して、SSL 証明書、Logging、Monitoring を使用できるようになります。
Cloud Domains Cloud Domains では、ドメイン登録事業者のようにドメインを購入できますが、購入すると、Google Cloud アカウントでドメインの管理と請求の処理を行うことができます。

スクリプト

インストール スクリプトでは、go と Terraform CLI ツールで記述された実行ファイルを使用して、空のプロジェクトを作成し、そこにアプリケーションをインストールします。出力は、機能するアプリケーションとロード バランシング IP アドレスの URL になります。

./main.tf

サービスを有効化する

Google Cloud サービスは、デフォルトではプロジェクトで無効になっています。ここに記載されているいずれかのソリューションを使用するには、以下の対象を有効にする必要があります。

  • Cloud Domains - ドメイン登録
  • Cloud Storage - 静的ファイルをホストする
  • Cloud Compute - ロードバランサへのアクセスを提供する
  • Cloud DNS - DNS 管理を提供する
variable "gcp_service_list" {
    description = "The list of apis necessary for the project"
    type        = list(string)
    default = [
        "domains.googleapis.com",
        "storage.googleapis.com",
        "compute.googleapis.com",
        "dns.googleapis.com",
        "appengine.googleapis.com",
    ]
}

resource "google_project_service" "all" {
    for_each                   = toset(var.gcp_service_list)
    project                    = var.project_number
    service                    = each.key
    disable_dependent_services = false
    disable_on_destroy         = false
}

DNS ゾーンを作成する

レコードを管理するための DNS ゾーンを作成します。レコードをロードバランサで参照し、ドメイン レジストラとやり取りして、他の DNS サーバーに対して DNS リクエストを処理します。

resource "google_dns_managed_zone" "dnszone" {
    project     = var.project_id
    name        = local.clouddnszone
    dns_name    = "${var.domain}."
    description = "A DNS Zone for managing ${var.domain}"
}

SSL 証明書を作成する

https プロトコル リクエストには SSL 証明書が必要です。次のコマンドでは、ロードバランサに接続される SSL 証明書が生成されます。

resource "google_compute_managed_ssl_certificate" "cert" {
    name        = "${local.basename}-cert"
    description = "Cert for ${local.basename}-microsite"
    project     = var.project_id

    managed {
        domains = [var.domain]
    }

    lifecycle {
        create_before_destroy = true
    }
}

外部 IP アドレスを作成する

ホストをドメイン名にバインドし、一般的にインターネット上で通信する必要があります。

resource "google_compute_global_address" "ip" {
    project    = var.project_id
    name       = "${local.basename}-ip"
    ip_version = "IPV4"
}

ストレージ バケットを作成する

これにより、ドメインの名前が付けられたバケットが作成され、ウェブサーバーとして機能するようにバケットが適切に構成されます。最後に、サイトのシンプルなテンプレートをコピーします。

resource "google_storage_bucket" "http_bucket" {
    name     = local.bucket
    project  = var.project_id
    location = var.location
    
    website {
        main_page_suffix = "index.html"
        not_found_page   = "404.html"
    }
}

resource "google_storage_bucket_iam_binding" "policy" {
    bucket = google_storage_bucket.http_bucket.name
    role   = "roles/storage.objectViewer"
    members = [
      "allUsers",
    ]
    depends_on = [google_storage_bucket.http_bucket]
  }

resource "google_storage_bucket_object" "archive" {
    name   = "index.html"
    bucket = google_storage_bucket.http_bucket.name
    source = "code/${var.yesorno}/index.html"
    depends_on = [
        google_project_service.all,
        google_storage_bucket.http_bucket,
    ]
}

ロードバランサの起動

以下により、ロードバランサが作成され、ロードバランサがアクセス権を提供するコンテンツのソースとしてストレージ バケットが設定されます。

resource "google_compute_backend_bucket" "be" {
    project     = var.project_id
    name        = "${local.basename}-be"
    bucket_name = google_storage_bucket.http_bucket.name
    depends_on = [google_storage_bucket.http_bucket]
}

resource "google_compute_url_map" "lb" {
    project         = var.project_id
    name            = "${local.basename}-lb"
    default_service = google_compute_backend_bucket.be.id
    depends_on = [google_compute_backend_bucket.be]
}

HTTP を有効にする

ロードバランサのポート 80 が実行中のサービスを指すように、必要なネットワーク ルールを作成します。

resource "google_compute_target_http_proxy" "lb-proxy" {
    project = var.project_id
    name    = "${local.basename}-lb-proxy"
    url_map = google_compute_url_map.lb.id
    depends_on = [google_compute_url_map.lb]
}

resource "google_compute_forwarding_rule" "http-lb-forwarding-rule" {
    project               = var.project_id
    name                  = "${local.basename}-http-lb-forwarding-rule"
    provider              = google-beta
    region                = "none"
    load_balancing_scheme = "EXTERNAL"
    port_range            = "80"
    target                = google_compute_target_http_proxy.lb-proxy.id
    ip_address            = google_compute_global_address.ip.id
    depends_on = [google_compute_target_http_proxy.lb-proxy]
}

HTTPS を有効にする

ロードバランサのポート 443 が実行中のサービスを指すように、必要なネットワーク ルールを作成します。また、証明書を適切に関連付けます。

resource "google_compute_target_https_proxy" "ssl-lb-proxy" {
    project          = var.project_id
    name             = "${local.basename}-ssl-lb-proxy"
    url_map          = google_compute_url_map.lb.id
    ssl_certificates = [google_compute_managed_ssl_certificate.cert.id]
    depends_on = [google_compute_url_map.lb,google_compute_managed_ssl_certificate.cert ]
}

resource "google_compute_forwarding_rule" "https-lb-forwarding-rule" {
    project               = var.project_id
    name                  = "${local.basename}-https-lb-forwarding-rule"
    provider              = google-beta
    region                = "none"
    load_balancing_scheme = "EXTERNAL"
    port_range            = "443"
    target                = google_compute_target_https_proxy.ssl-lb-proxy.id
    ip_address            = google_compute_global_address.ip.id
    depends_on = [google_compute_target_https_proxy.ssl-lb-proxy]
}

レコードを設定する

ドメイン名が作成した IP アドレスに解決されるように、Cloud DNS に適切なルールを作成します。

resource "google_dns_record_set" "a" {
    project      = var.project_id
    name         = "${var.domain}."
    managed_zone = google_dns_managed_zone.dnszone.name
    type         = "A"
    ttl          = 60


    rrdatas = [google_compute_global_address.ip.address]
    depends_on = [google_compute_global_address.ip]
}

./deploystack.go

ドメインの可用性をクエリする

リクエストされたドメインの可用性に関する情報を取得します。

func domainsSearch(project, domain string) ([]*domainspb.RegisterParameters, error) {
    ctx := context.Background()

    c, err := domains.NewClient(ctx)
    if err != nil {
        return nil, err
    }
    defer c.Close()

    req := &domainspb.SearchDomainsRequest{
        Query:    domain,
        Location: fmt.Sprintf("projects/%s/locations/global", project),
    }
    resp, err := c.SearchDomains(ctx, req)
    if err != nil {
        return nil, err
    }

    return resp.RegisterParameters, nil
}

func domainIsAvailable(project, domain string) (*domainspb.RegisterParameters, error) {
    list, err := domainsSearch(project, domain)
    if err != nil {
        return nil, err
    }
    for _, v := range list {
        if v.DomainName == domain {
            return v, err
        }
    }

    return nil, err
}

ドメイン所有権のクエリを実行する

現在のユーザーがリクエストされたドメインを所有しているかどうかの情報を取得します。

func domainsIsVerified(project, domain string) (bool, error) {
    ctx := context.Background()

    c, err := domains.NewClient(ctx)
    if err != nil {
        return false, err
    }
    defer c.Close()

    req := &domainspb.ListRegistrationsRequest{
        Filter: fmt.Sprintf("domainName=\"%s\"", domain),
        Parent: fmt.Sprintf("projects/%s/locations/global", project),
    }
    it := c.ListRegistrations(ctx, req)
    for {
        resp, err := it.Next()
        if err == iterator.Done {
            break
        }
        if err != nil {
            return false, err
        }

        if resp.DomainName == domain {
            return true, nil
        }
    }

    return false, nil
}

Cloud Domains でドメインを登録する

登録事業者からドメインを購入するオペレーションを実際に実行します。

func domainRegister(project string, domaininfo *domainspb.RegisterParameters, contact ContactData) error {
    ctx := context.Background()

    parent := fmt.Sprintf("projects/%s/locations/global", project)

    c, err := domains.NewClient(ctx)
    if err != nil {
        return err
    }
    defer c.Close()

    dnscontact, err := contact.DomainContact()
    if err != nil {
        return err
    }

    req := &domainspb.RegisterDomainRequest{
        Registration: &domainspb.Registration{
            Name:       fmt.Sprintf("%s/registrations/%s", parent, domaininfo.DomainName),
            DomainName: domaininfo.DomainName,
            DnsSettings: &domainspb.DnsSettings{
                DnsProvider: &domainspb.DnsSettings_CustomDns_{
                    CustomDns: &domainspb.DnsSettings_CustomDns{
                        NameServers: []string{
                            "ns-cloud-e1.googledomains.com",
                            "ns-cloud-e2.googledomains.com",
                            "ns-cloud-e3.googledomains.com",
                            "ns-cloud-e4.googledomains.com",
                        },
                    },
                },
            },
            ContactSettings: &dnscontact,
        },
        Parent:      parent,
        YearlyPrice: domaininfo.YearlyPrice,
    }

    if _, err := c.RegisterDomain(ctx, req); err != nil {
        return err
    }

    return nil
}


まとめ

ドメインと安全な静的サイトを配置した小規模なウェブサイトの設定が完了しました。さらに、環境に合わせてソリューションを変更または拡張するためのコードもすべて用意されています。