Statisches Hosting mit Domain

Architektur

Static Hosting with Domain ist ein einfacher Erstellungstool für statische Websites, der eine einfache Website mithilfe eines Storage-Buckets erstellt:

  • Storage – Dateispeicher – Cloud Storage
  • Netzwerk – Load-Balancing – Cloud Load Balancer
  • Netzwerk – DNS – Cloud DNS
  • Domainverwaltung – Registrar – Cloud Domains

Jetzt starten

Klicken Sie auf den folgenden Link, um den Quellcode in Cloud Shell zu kopieren. Dort wird mit einem einzigen Befehl eine funktionierende Kopie der Anwendung in Ihrem Projekt erstellt.

In Cloud Shell öffnen

Quellcode auf GitHub ansehen


Statisches Hosting mit Domainkomponenten

Die Architektur „Static Hosting with Domain“ nutzt mehrere Produkte. Im Folgenden finden Sie eine Liste der Komponenten sowie weitere Informationen zu den Komponenten, einschließlich Links zu ähnlichen Videos, Produktdokumentationen und interaktiven Schritt-für-Schritt-Anleitungen.
Video Docs Schritt-für-Schritt-Anleitungen
Cloud Storage Cloud Storage bietet Dateispeicher und öffentliche Bereitstellung von Bildern über HTTP(s).
Cloud DNS Cloud DNS bietet DNS-Verwaltung, sodass Sie mit der bestehenden DNS-Infrastruktur von Google Ihre eigene DNS-Infrastruktur ausführen können.
Cloud Load Balancing Mit dem Google Cloud-Load-Balancer können Sie einen Load-Balancer vor dem Storage-Bucket platzieren, um SSL-Zertifikate, Logging und Monitoring zu verwenden.
Cloud Domains Mit Cloud Domains können Sie Domains wie ein Domain-Registrar kaufen. Danach können Sie sie jedoch über Ihr Google Cloud-Konto verwalten und die Abrechnung verwalten.

Skripts

Das Installationsskript verwendet eine ausführbare Datei, die in go und Terraform-Befehlszeilentools geschrieben ist, um ein leeres Projekt zu erstellen und die Anwendung darin zu installieren. Die Ausgabe sollte eine funktionierende Anwendung und eine URL für die Load-Balancing-IP-Adresse sein.

./main.tf

Dienste aktivieren

Google Cloud-Dienste sind in einem Projekt standardmäßig deaktiviert. Damit Sie eine der Lösungen verwenden können, müssen Sie Folgendes aktivieren:

  • Cloud Domains – Domainregistrierung
  • Cloud Storage – Hosting der statischen Dateien
  • Cloud Compute – Zugriff auf Load-Balancer gewähren.
  • Cloud DNS: bietet DNS-Verwaltung
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-Zone erstellen

Erstellt eine DNS-Zone zum Verwalten von Einträgen, verweist sie auf Load-Balancer, interagiert mit Domain-Registraren und stellt DNS-Anfragen an andere DNS-Server bereit.

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-Zertifikat erstellen

Für HTTPS-Protokollanfragen sind SSL-Zertifikate erforderlich. Dadurch wird ein Schlüssel generiert, der in einem späteren Befehl an den Load-Balancer angehängt wird.

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

Externe IP-Adresse erstellen

Muss einen Host an den Domainnamen binden und allgemein im Internet kommunizieren.

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

Storage-Bucket erstellen

Dadurch wird ein Bucket erstellt, der nach der Domain benannt ist. Anschließend wird der Bucket so konfiguriert, dass er als Webserver fungiert. Und schließlich wird die einfache Vorlage für die Website kopiert.

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

Stand-up-Load-Balancer

Dadurch wird der Load-Balancer erstellt und der Storage-Bucket als Quelle der Inhalte eingerichtet, auf die der Load-Balancer Zugriff gewährt.

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 aktivieren

Erstellt die erforderlichen Netzwerkregeln, um Port 80 auf dem Load-Balancer auf den ausgeführten Dienst zu verweisen.

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 aktivieren

Erstellt die erforderlichen Netzwerkregeln, um Port 443 am Load-Balancer auf den ausgeführten Dienst zu verweisen. Außerdem wird das Zertifikat ordnungsgemäß verknüpft.

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-Datensatz einstellen

Die entsprechende Regel in Cloud DNS wird erstellt, damit der Domainname in die von uns erstellte IP-Adresse aufgelöst wird.

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

Domainverfügbarkeit abfragen

Informationen zur Verfügbarkeit der angeforderten Domain abrufen

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
}

Domaininhaberschaft abfragen

Ruft Informationen dazu ab, ob der aktuelle Nutzer Inhaber der angeforderten Domain ist oder nicht.

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
}

Domain bei Cloud Domains registrieren

Die Domain wird vom Registrar erworben.

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
}


Fazit

Sie haben nun eine kleine Website mit einer Domain und einer sicheren, statischen Website eingerichtet. Außerdem sollten Sie über den gesamten Code zum Ändern oder Erweitern dieser Lösung an Ihre Umgebung verfügen.