Hosting estático con dominio

Arquitectura

El hosting estático con dominio es un creador de sitios web estático simple que usando un bucket de Storage para entregar el sitio web:

  • Almacenamiento: Almacenamiento de archivos: Cloud Storage
  • Herramientas de redes - Balanceo de cargas - Balanceador de cargas de Cloud
  • 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 ahí, un solo comando iniciará una copia de trabajo de la aplicación en tu proyecto...

Abrir en Cloud Shell

Consulta 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 el componentes, incluidos los vínculos a videos relacionados, la documentación del producto y con 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 ofrece administración de DNS, lo que te permite ejecutar tu propia infraestructura de DNS usando 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, lo que funciona como un registrador de dominios, pero, 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 y una URL para la dirección IP del balanceo 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 dominio
  • Cloud Storage: Alojamiento de 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}"
}

Crear certificado SSL

Se requieren certificados SSL para las solicitudes de protocolo HTTPS. Esto genera una 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
    }
}

Crear dirección IP externa

Es necesario para vincular un host al nombre de dominio y comunicarse en general en el a Internet.

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

Cree un bucket de almacenamiento

Se creará un bucket con el nombre del dominio y, luego, se configurará el bucket para que funcione como servidor web. Por último, copia el flujo plantilla 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 Storage como la fuente de el 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 en el balanceador de cargas al servicio que estamos ejecutando.

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

Habilitar HTTPS

Crea las reglas de red necesarias para apuntar al puerto 443 en el balanceador de cargas al servicio que estamos ejecutando. También asocia el certificado de manera correcta.

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

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

Consultar la propiedad del dominio

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

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

En realidad, realiza la operación para comprarle 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 sitio web pequeño configurado con un dominio y un sitio seguro y estático. Además, debes tener todo el código para modificar o extender que se adapte a tu entorno.