带域名的静态托管

+

架构

“带网域的静态网站托管”是一款简单的静态网站制作工具,可使用存储分区制作简单的网站:

  • 存储 - 文件存储 - Cloud Storage
  • 网络 - 负载均衡 - Cloud Load Balancing
  • 网络 - DNS - Cloud DNS
  • 域名管理 - 注册商 - Cloud Domains

开始使用

点击以下链接,在 Cloud Shell 中查看源代码的副本。进入该环境后,只需一个命令即可在项目中启动应用的工作副本。

在 Cloud Shell 中打开

在 GitHub 上查看源代码


使用网域组件的静态托管

借助“带网域的静态托管”架构,您可以使用多种产品。 以下列出了这些组件,以及有关这些组件的更多信息,包括指向相关视频、产品文档和互动式演示文稿的链接。
视频 文档 演示
Cloud Storage Cloud Storage 通过 http(s) 提供文件存储和图片公开传送服务。
Cloud DNS Cloud DNS 提供 DNS 管理功能,可让您使用 Google 现有的 DNS 基础架构运行自己的 DNS 基础架构。
Cloud Load Balancing 借助 Google Cloud 负载平衡器,您可以在存储分区前面放置负载平衡器,从而使用 SSL 证书、日志记录和监控功能。
Cloud 网域 借助 Cloud Domains,您可以购买域名,就像域名注册商一样。不过,购买域名后,您可以通过 Google Cloud 账号管理域名并处理结算。

脚本

安装脚本使用使用 go 和 Terraform CLI 工具编写的可执行文件,获取一个空项目并在其中安装应用。输出应为一个正常运行的应用和负载均衡 IP 地址的网址。

./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 证书。这会生成一个证书,稍后的命令将会将其附加到负载平衡器。

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"
}

创建存储分区

这会创建一个以网域命名的存储分区,然后使用适当的配置将该存储分区设置为 Web 服务器。最后,它会为网站复制简单模板。

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]
}

设置 A 记录

在 Cloud DNS 中创建适当的规则,以便将域名解析为我们创建的 IP 地址。

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
}


总结

现在,您已经设置了一个小型网站,其中包含域名和安全的静态网站。 此外,您应该拥有修改或扩展此解决方案以适应您环境的所有代码。