Deployment di un'app di log con bilanciamento del carico di rete

In questo tutorial eseguirai il deployment di un'app di log di esempio che utilizza Node.js per e MySQL per il suo backend. Al termine del tutorial, il deployment conterrà le seguenti risorse:

Risorse di deployment con bilanciamento del carico di rete (fai clic per ingrandire)

Se non hai mai utilizzato Deployment Manager, consulta i tutorial della guida rapida o della guida passo passo.

Prima di iniziare

Creare i modelli di risorse

Questo esempio avvia un deployment che contiene una serie di Google Cloud. Per iniziare, crea modelli che definiscono queste risorse distintamente. In seguito, questi modelli verranno richiamati nella configurazione finale. Il tuo il deployment contiene le seguenti risorse:

  • Un'istanza Compute Engine che ospita un database MySQL per l'app.
  • Un modello di istanza per le istanze di frontend che utilizza un'immagine Docker per l'app Node.js.
  • Un gruppo di istanze gestite che utilizza il modello di istanza per creare due istanze frontend.
  • Un gestore della scalabilità automatica, che avvia o arresta altre istanze di frontend in base per il traffico in entrata.
  • Un controllo di integrità, che verifica se le istanze di frontend sono disponibili lavorare.
  • Un bilanciatore del carico di rete con una regola di inoltro.
  • Un pool di destinazione per il gruppo di istanze gestite.

Creazione del modello per il backend MySQL

Il backend di questa app è una singola istanza Compute Engine che esegue un container Docker MySQL. Il modello container_vm.py definisce un'istanza Compute Engine che può eseguire container Docker. Per assicurarti che il modello segua la struttura corretta e contenga tutte le proprietà obbligatorie, hai bisogno anche di un file schema.

Copia il modello di seguito o scaricalo dal repository GitHub:

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Creates a Container VM with the provided Container manifest."""

from container_helper import GenerateManifest


COMPUTE_URL_BASE = 'https://www.googleapis.com/compute/v1/'


def GlobalComputeUrl(project, collection, name):
  return ''.join([COMPUTE_URL_BASE, 'projects/', project,
                  '/global/', collection, '/', name])


def ZonalComputeUrl(project, zone, collection, name):
  return ''.join([COMPUTE_URL_BASE, 'projects/', project,
                  '/zones/', zone, '/', collection, '/', name])


def GenerateConfig(context):
  """Generate configuration."""

  base_name = context.env['name']

  # Properties for the container-based instance.
  instance = {
      'zone': context.properties['zone'],
      'machineType': ZonalComputeUrl(context.env['project'],
                                     context.properties['zone'],
                                     'machineTypes',
                                     'f1-micro'),
      'metadata': {
          'items': [{
              'key': 'gce-container-declaration',
              'value': GenerateManifest(context)
              }]
          },
      'disks': [{
          'deviceName': 'boot',
          'type': 'PERSISTENT',
          'autoDelete': True,
          'boot': True,
          'initializeParams': {
              'diskName': base_name + '-disk',
              'sourceImage': GlobalComputeUrl('cos-cloud',
                                              'images',
                                              context.properties[
                                                  'containerImage'])
              },
          }],
      'networkInterfaces': [{
          'accessConfigs': [{
              'name': 'external-nat',
              'type': 'ONE_TO_ONE_NAT'
              }],
          'network': GlobalComputeUrl(context.env['project'],
                                      'networks',
                                      'default')
          }],
        'serviceAccounts': [{
            'email': 'default',
            'scopes': ['https://www.googleapis.com/auth/logging.write']
            }]
      }

  # Resources to return.
  resources = {
      'resources': [{
          'name': base_name,
          'type': 'compute.v1.instance',
          'properties': instance
          }]
      }

  return resources

Scarica il file dello schema per il modello.

Il modello contiene alcune proprietà non definite, come containerImage, che sono definite nei modelli successivi.

Quando utilizzi immagini container su istanze di Compute Engine, devi anche fornire un file manifest che descriva l'immagine container da utilizzare. Crea un metodo di supporto denominato container_helper.py per definire dinamicamente il manifest del contenitore:

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Helper methods for working with containers in config."""

import six
import yaml


def GenerateManifest(context):
  """Generates a Container Manifest given a Template context.

  Args:
    context: Template context, which must contain dockerImage and port
        properties, and an optional dockerEnv property.

  Returns:
    A Container Manifest as a YAML string.
  """
  env_list = []
  if 'dockerEnv' in context.properties:
    for key, value in six.iteritems(context.properties['dockerEnv']):
      env_list.append({'name': key, 'value': str(value)})

  manifest = {
      'apiVersion': 'v1',
      'kind': 'Pod',
      'metadata': {
          'name': str(context.env['name'])
          },
      'spec': {
          'containers': [{
              'name': str(context.env['name']),
              'image': context.properties['dockerImage'],
              'ports': [{
                  'hostPort': context.properties['port'],
                  'containerPort': context.properties['port']
                  }],
              }]
          }
      }

  if env_list:
    manifest['spec']['containers'][0]['env'] = env_list

  return yaml.dump(manifest, default_flow_style=False)

Creazione del modello per il frontend Node.js

Il frontend dell'app esegue Node.js e consente agli utenti di pubblicare messaggi sul web . Il frontend viene eseguito su un gruppo di istanze di macchine virtuali, supportate da un gestore della scalabilità automatica e un bilanciatore del carico. Per creare modelli frontend, segui le istruzioni riportate di seguito.

  1. Crea una risorsa modello di istanza.

    Per creare un gruppo di istanze gestite, ovvero un gruppo di istanze di macchine virtuali (VM) identiche che puoi controllare come singola entità, è necessario un modello di istanza.

    Crea un file denominato container_instance_template.py e scarica l'app schema per il modello:

    # Copyright 2016 Google Inc. All rights reserved.
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    """Creates a Container VM with the provided Container manifest."""
    
    from container_helper import GenerateManifest
    
    
    def GenerateConfig(context):
      """Generates configuration."""
    
      image = ''.join(['https://www.googleapis.com/compute/v1/',
                       'projects/cos-cloud/global/images/',
                       context.properties['containerImage']])
      default_network = ''.join(['https://www.googleapis.com/compute/v1/projects/',
                                 context.env['project'],
                                 '/global/networks/default'])
    
      instance_template = {
          'name': context.env['name'] + '-it',
          'type': 'compute.v1.instanceTemplate',
          'properties': {
              'properties': {
                  'metadata': {
                      'items': [{
                          'key': 'gce-container-declaration',
                          'value': GenerateManifest(context)
                          }]
                      },
                  'machineType': 'f1-micro',
                  'disks': [{
                      'deviceName': 'boot',
                      'boot': True,
                      'autoDelete': True,
                      'mode': 'READ_WRITE',
                      'type': 'PERSISTENT',
                      'initializeParams': {'sourceImage': image}
                      }],
                  'networkInterfaces': [{
                      'accessConfigs': [{
                          'name': 'external-nat',
                          'type': 'ONE_TO_ONE_NAT'
                          }],
                      'network': default_network
                      }],
                    'serviceAccounts': [{
                        'email': 'default',
                        'scopes': ['https://www.googleapis.com/auth/logging.write']
                        }]
                  }
              }
          }
    
      outputs = [{'name': 'instanceTemplateSelfLink',
                  'value': '$(ref.' + instance_template['name'] + '.selfLink)'}]
    
      return {'resources': [instance_template], 'outputs': outputs}
    

    Scarica il file di schema per il modello.

  2. Crea un regolatore automatico, un gruppo di istanze gestite e un bilanciatore del carico.

    Poi, crea un altro modello che utilizzi container_instance_template.py e crea le altre risorse di frontend, un gestore della scalabilità automatica, un bilanciatore del carico e un gruppo di istanze gestite.

Questo modello include le seguenti risorse:

  1. Un modello di istanza che utilizza container_instance_template.py modello.

  2. Un gruppo di istanze gestite che utilizza il modello di istanza e un gestore della scalabilità automatica che fa riferimento al gruppo di istanze gestite. L'utilizzo dei riferimenti garantisce che Deployment Manager crei le risorse in un ordine specifico. In questo caso, il gruppo di istanze gestite viene creato prima del gestore della scalabilità automatica.

  3. Un bilanciatore del carico di rete contenente le seguenti risorse:

    • Una regola di forwarding con un singolo indirizzo IP esterno esposto al Internet.
    • Un pool di destinazione contenente il gruppo di istanze gestite creato in precedenza.
    • Un controllo di integrità da collegare al pool di destinazione.

Crea un file denominato frontend.py e scarica lo schema per il modello:

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Creates autoscaled, network LB IGM running specified docker image."""


def GenerateConfig(context):
  """Generate YAML resource configuration."""

  # Pull the region out of the zone
  region = context.properties['zone'][:context.properties['zone'].rfind('-')]
  name = context.env['name']

  resources = [{
      'name': name,
      'type': 'container_instance_template.py',
      'properties': {
          'port': context.properties['port'],
          'dockerEnv': context.properties['dockerEnv'],
          'dockerImage': context.properties['dockerImage'],
          'containerImage': context.properties['containerImage']
      }
  }, {
      'name': name + '-igm',
      'type': 'compute.v1.instanceGroupManager',
      'properties': {
          'zone': context.properties['zone'],
          'targetSize': context.properties['size'],
          'targetPools': ['$(ref.' + name + '-tp.selfLink)'],
          'baseInstanceName': name + '-instance',
          'instanceTemplate': '$(ref.' + name + '-it.selfLink)'
      }
  }, {
      'name': name + '-as',
      'type': 'compute.v1.autoscaler',
      'properties': {
          'zone': context.properties['zone'],
          'target': '$(ref.' + name + '-igm.selfLink)',
          'autoscalingPolicy': {
              'maxNumReplicas': context.properties['maxSize']
          }
      }
  }, {
      'name': name + '-hc',
      'type': 'compute.v1.httpHealthCheck',
      'properties': {
          'port': context.properties['port'],
          'requestPath': '/_ah/health'
      }
  }, {
      'name': name + '-tp',
      'type': 'compute.v1.targetPool',
      'properties': {
          'region': region,
          'healthChecks': ['$(ref.' + name + '-hc.selfLink)']
      }
  }, {
      'name': name + '-lb',
      'type': 'compute.v1.forwardingRule',
      'properties': {
          'region': region,
          'portRange': context.properties['port'],
          'target': '$(ref.' + name + '-tp.selfLink)'
      }
  }]
  return {'resources': resources}

Scarica il file di schema per il modello.

Creazione di un modello unificante

Infine, crea un modello che raccolga le risorse di backend e modelli di frontend. Crea un file denominato nodejs.py con i seguenti contenuti:

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Create nodejs template with the back-end and front-end templates."""


def GenerateConfig(context):
  """Generate configuration."""

  backend = context.env['deployment'] + '-backend'
  frontend = context.env['deployment'] + '-frontend'
  firewall = context.env['deployment'] + '-application-fw'
  application_port = 8080
  mysql_port = 3306
  resources = [{
      'name': backend,
      'type': 'container_vm.py',
      'properties': {
          'zone': context.properties['zone'],
          'dockerImage': 'gcr.io/qwiklabs-resources/mysql',
          'containerImage': 'family/cos-stable',
          'port': mysql_port,
          'dockerEnv': {
              'MYSQL_ROOT_PASSWORD': 'mypassword'
          }
      }
  }, {
      'name': frontend,
      'type': 'frontend.py',
      'properties': {
          'zone': context.properties['zone'],
          'dockerImage': 'gcr.io/qwiklabs-resources/nodejsservice',
          'port': application_port,
          # Define the variables that are exposed to container as env variables.
          'dockerEnv': {
              'SEVEN_SERVICE_MYSQL_PORT': mysql_port,
              'SEVEN_SERVICE_PROXY_HOST': '$(ref.' + backend
                                          + '.networkInterfaces[0].networkIP)'
          },
          # If left out will default to 1
          'size': 2,
          # If left out will default to 1
          'maxSize': 20
      }
  }, {
      'name': firewall,
      'type': 'compute.v1.firewall',
      'properties': {
          'allowed': [{
              'IPProtocol': 'TCP',
              'ports': [application_port]
          }],
          'sourceRanges': ['0.0.0.0/0']
      }
  }]
  return {'resources': resources}

Scarica il file dello schema per il modello.

Tieni presente che il frontend dell'app si chiama env["deployment"]-frontend e anche il backend ha lo stesso nome. Quando esegui il deployment dell'app, Deployment Manager sostituirà automaticamente env["deployment"] con il nome del deployment.

Creazione della configurazione

Quando tutti i modelli sono pronti, puoi creare una configurazione per il deployment delle risorse. Crea un file di configurazione denominato nodejs.yaml con i seguenti contenuti:

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Launches an autoscaled, load-balanced frontend running nodejs for serving
# traffic. Also launches a single MySQL container instance, wires the two
# together using references, and passes them as env variables to the underlying
# frontend Docker containers.
imports:
- path: nodejs.py

resources:
- name: nodejs
  type: nodejs.py
  properties:
    zone: ZONE_TO_RUN

Sostituisci ZONE_TO_RUN con la zona in cui vuoi le risorse, ad esempio us-central1-a.

Deployment delle risorse

Ora esegui il deployment delle risorse. Utilizzando Google Cloud CLI, esegui:

gcloud deployment-manager deployments create advanced-configuration --config nodejs.yaml

Al termine del deployment, Deployment Manager mostra un riepilogo delle risorse create, simile al seguente:

Waiting for create operation-1468522101491-5379cf2344539-5961abe8-a500190c...done.
Create operation operation-1468522101491-5379cf2344539-5961abe8-a500190c completed successfully.
NAME                                   TYPE                             STATE      ERRORS
advanced-configuration-application-fw  compute.v1.firewall              COMPLETED  []
advanced-configuration-backend         compute.v1.instance              COMPLETED  []
advanced-configuration-frontend-as     compute.v1.autoscaler            COMPLETED  []
advanced-configuration-frontend-hc     compute.v1.httpHealthCheck       COMPLETED  []
advanced-configuration-frontend-igm    compute.v1.instanceGroupManager  COMPLETED  []
advanced-configuration-frontend-it     compute.v1.instanceTemplate      COMPLETED  []
advanced-configuration-frontend-lb     compute.v1.forwardingRule        COMPLETED  []
advanced-configuration-frontend-tp     compute.v1.targetPool            COMPLETED  []

Test dell'app

Per testare l'app, devi prima ottenere l'indirizzo IP esterno che gestisce il traffico, eseguire una query sulla regola di forwarding:

$ gcloud compute forwarding-rules describe advanced-configuration-frontend-lb --region us-central1
IPAddress: 104.154.81.44
IPProtocol: TCP
creationTimestamp: '2016-07-14T11:48:37.228-07:00'
description: ''
id: '9033201246750269546'
kind: compute#forwardingRule
name: advanced-configuration-frontend-lb
portRange: 8080-8080
region: https://www.googleapis.com/compute/v1/projects/myproject/regions/us-central1
selfLink: https://www.googleapis.com/compute/v1/projects/myproject/regions/us-central1/forwardingRules/advanced-configuration-frontend-lb
target: https://www.googleapis.com/compute/v1/projects/myproject/regions/us-central1/targetPools/advanced-configuration-frontend-tp

In questo caso, l'IP esterno è 104.154.81.44.

Successivamente, in un browser, visita l'indirizzo IP esterno con la porta 8080. Ad esempio, se il tuo indirizzo IP esterno è 104.154.81.44, l'URL sarà:

http://104.154.81.44:8080

Dovresti visualizzare una pagina vuota, come previsto. Quindi, pubblica un messaggio nella . Vai al seguente URL:

http://104.154.81.44:8080?msg=hellothere!

Verrà visualizzata la conferma dell'aggiunta del messaggio. Torna alla URL principale e ora la pagina dovrebbe contenere il messaggio:

hellothere!

Ora hai un'app di cui è stato eseguito il deployment che può registrare i messaggi inviati.

Passaggi successivi

Dopo aver completato questo esempio, puoi: