Hosting estático con dominio

+

Arquitectura

El alojamiento estático con dominio es un creador de sitios web estáticos simple que crea un sitio simple con un bucket de almacenamiento para entregar el sitio web:

  • Almacenamiento: Almacenamiento de archivos: Cloud Storage
  • Redes: Balanceo de cargas: Cloud Load Balancer
  • Redes - DNS - Cloud DNS
  • Administración de dominios: Registrador: Cloud Domains

Comenzar

Haz clic en el siguiente vínculo para obtener una copia del código fuente en Cloud Shell. Una vez allí, un solo comando iniciará una copia de trabajo de la aplicación en tu proyecto.

Abrir en Cloud Shell

Ver el código fuente en GitHub


Hosting estático con componentes de dominio

La arquitectura de hosting estático con dominio usa varios productos. A continuación, se enumeran los componentes, junto con más información sobre ellos, incluidos vínculos a videos relacionados, documentación del producto y explicaciones interactivas.
Video Documentos Explicaciones
Cloud Storage Cloud Storage proporciona almacenamiento de archivos y entrega pública de imágenes a través de HTTP(s).
Cloud DNS Cloud DNS proporciona administración de DNS, lo que te permite ejecutar tu propia infraestructura de DNS con la infraestructura de DNS existente de Google.
Cloud Load Balancing El balanceador de cargas de Google Cloud te permite colocar un balanceador de cargas frente al bucket de almacenamiento, lo que te permite usar certificados SSL, registro y supervisión.
Dominios en la nube Cloud Domains te permite comprar dominios y actuar como registrador de dominios. Una vez que lo hagas, podrás administrarlos y controlar la facturación a través de tu cuenta de Google Cloud.

Secuencias de comandos

La secuencia de comandos de instalación usa un ejecutable escrito en go y herramientas de la CLI de Terraform para tomar un proyecto vacío e instalar la aplicación en él. El resultado debe ser una aplicación que funcione y una URL para la dirección IP del balanceador de cargas.

./main.tf

Habilitar servicios

Los servicios de Google Cloud están inhabilitados en un proyecto de forma predeterminada. Para usar cualquiera de las soluciones que se indican a continuación, debemos activar lo siguiente:

  • Cloud Domains: Registro de dominios
  • Cloud Storage: aloja los archivos estáticos
  • Cloud Compute: Proporciona acceso a los balanceadores de cargas.
  • Cloud DNS: Proporciona administración de 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
}

Crea una zona de DNS

Crea una zona DNS para administrar registros, apuntarlos a balanceadores de cargas, interactuar con registradores de dominios y entregar solicitudes de DNS a otros servidores 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}"
}

Cómo crear un certificado SSL

Los certificados SSL son obligatorios para las solicitudes de protocolo HTTPS. Esto genera uno que se adjuntará al balanceador de cargas en un comando posterior.

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

Crea una dirección IP externa

Se necesita para vincular un host al nombre de dominio y comunicarse en general en Internet.

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

Cree un bucket de almacenamiento

Esto crea un bucket con el nombre del dominio y, luego, lo configura con la configuración adecuada para que funcione como servidor web. Por último, copia la plantilla simple para el sitio.

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

Cómo configurar un balanceador de cargas

Esto crea el balanceador de cargas y configura el bucket de almacenamiento como la fuente del contenido al que el balanceador de cargas proporcionará acceso.

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

Habilita HTTP

Crea las reglas de red necesarias para apuntar el puerto 80 del balanceador de cargas al servicio que ejecutamos.

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

Habilita HTTPS

Crea las reglas de red necesarias para que el puerto 443 del balanceador de cargas apunte al servicio que ejecutamos. También asocia el certificado correctamente.

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

Cómo configurar un registro A

Crea la regla adecuada en Cloud DNS que hará que el nombre de dominio se resuelva en la dirección IP que creamos.

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

Consulta la disponibilidad del dominio

Obtiene información sobre la disponibilidad del dominio que se solicita.

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
}

Consulta la propiedad del dominio

Obtiene información sobre si el usuario actual es propietario del dominio solicitado o no.

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
}

Registra un dominio con Cloud Domains

Realiza la operación para comprar el dominio al registrador.

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
}


Conclusión

Ahora tienes un pequeño sitio web configurado con un dominio y un sitio estático y seguro. Además, debes tener todo el código para modificar o extender esta solución para que se adapte a tu entorno.