Logbook-Anwendung mit Load-Balancing bereitstellen

In dieser Anleitung stellen Sie eine Beispiel-Logbook-Anwendung bereit, die Node.js für das Frontend und MySQL als Backend verwendet. Am Ende der Anleitung umfasst Ihre Bereitstellung folgende Ressourcen:

Deployment-Ressourcen mit Netzwerk-Load-Balancer (zum Vergrößern klicken)

Wenn Sie Deployment Manager noch nicht kennen, finden Sie entsprechende Informationen in der Kurzanleitung oder in den detaillierten Anleitungen.

Hinweis

Ressourcenvorlagen erstellen

In diesem Beispiel wird eine Bereitstellung begonnen, die unterschiedliche Google Cloud-Ressourcen enthält. Zuerst erstellen Sie Vorlagen, in denen diese Ressourcen getrennt definiert werden. Später rufen Sie diese Vorlagen in Ihrer endgültigen Konfiguration auf. Ihre Bereitstellung enthält folgende Ressourcen:

  • Eine Compute Engine-Instanz, die eine MySQL-Datenbank für die Anwendung hostet
  • Eine Instanzvorlage für Front-End-Instanzen, die ein Docker-Image für den Node.js-Anwendung
  • Eine verwaltete Instanzgruppe, in der mit der Instanzvorlage zwei Frontend-Instanzen erstellt werden
  • Ein Autoscaling, das auf der Basis des eingehenden Traffics zusätzliche Frontend-Instanzen startet oder beendet
  • Eine Systemdiagnose, die prüft, ob die Frontend-Instanzen verfügbar und funktionsfähig sind
  • Ein Netzwerk-Load-Balancer mit einer Weiterleitungsregel
  • Ein Zielpool für die verwaltete Instanzgruppe

Vorlage für das MySQL-Back-End erstellen

Das Back-End dieser Anwendung ist eine einzelne Compute Engine-Instanz, auf der ein MySQL Docker-Container ausgeführt wird. Die Vorlage container_vm.py definiert eine Compute Engine-Instanz, die Docker-Container ausführen kann. Um zu gewährleisten, dass die Vorlage die korrekte Struktur aufweist und alle erforderlichen Attribute enthält, benötigen Sie außerdem eine Schemadatei.

Kopieren Sie die Vorlage unten oder laden Sie sie aus dem GitHub-Repository herunter:

# 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

Schemadatei für die Vorlage herunterladen

Die Vorlage enthält einige nicht definierte Attribute wie containerImage. Diese sind in späteren Vorlagen definiert.

Wenn Sie Container-Images auf Compute Engine-Instanzen verwenden, müssen Sie außerdem eine Manifestdatei zur Verfügung stellen. In dieser wird angegeben, welches Container-Image zu verwenden ist. Erstellen Sie eine Hilfsmethode mit dem Namen container_helper.py, um das Container-Manifest dynamisch zu definieren:

# 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)

Vorlage für das Node.js-Front-End erstellen

Das Front-End der Anwendung führt Node.js aus und ermöglicht Nutzern, Nachrichten auf der Webseite zu posten. Die Ausführung des Front-Ends erfolgt auf einer Gruppe von virtuellen Maschinen, die durch Autoscaling und einen Load-Balancer unterstützt werden. Folgen Sie der Anleitung unten, um Front-End-Vorlagen zu erstellen.

  1. Erstellen Sie eine Instanzvorlagenressource.

    Sie benötigen eine Instanzvorlage, um eine verwaltete Instanzgruppe zu erstellen. Hierbei handelt es sich um eine Gruppe identischer VM-Instanzen, die Sie als eine einzelne Entität steuern.

    Erstellen Sie eine Datei mit dem Namen container_instance_template.py und laden Sie das Schema für die Vorlage herunter:

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

    Schemadatei für die Vorlage herunterladen

  2. Erstellen Sie Autoscaling, eine verwaltete Instanzgruppe und einen Load-Balancer.

    Erstellen Sie als Nächstes eine andere Vorlage, die die Vorlage container_instance_template.py verwendet und die restlichen Front-End-Ressourcen, einschließlich Autoscaling, Load Balancer und verwalteter Instanzgruppe, erstellt.

Diese Vorlage enthält folgende Ressourcen:

  1. Eine Instanzvorlage mit der Vorlage container_instance_template.py.

  2. Eine verwaltete Instanzgruppe, die die Instanzvorlage verwendet, und ein Autoscaling, das die verwaltete Instanzgruppe referenziert. Mithilfe von Verweisen kann festgelegt werden, dass Deployment Manager die Ressourcen in einer bestimmten Reihenfolge erstellt. In diesem Fall wird die verwaltete Instanzgruppe vor dem Autoscaling angelegt.

  3. Ein Netzwerk-Load-Balancer, der folgende Ressourcen enthält:

    • Eine Weiterleitungsregel mit einer einzelnen externen IP-Adresse, die für das Internet freigegeben ist
    • Einen Zielpool, der die verwaltete Instanzgruppe enthält, die Sie erstellt haben
    • Eine Systemdiagnose zur Verbindung mit dem Zielpool

Erstellen Sie eine Datei mit dem Namen frontend.py und laden Sie das Schema für die Vorlage herunter:

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

Schemadatei für die Vorlage herunterladen

Einheitliche Vorlage erstellen

Erstellen Sie schließlich eine Vorlage zur Kombination der Backend- und Frontend-Vorlagen. Erstellen Sie eine Datei mit dem Namen nodejs.py und folgendem Inhalt:

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

Schemadatei für die Vorlage herunterladen

Beachten Sie, dass das Frontend der Anwendung den Namen env["deployment"]-frontend hat. Das Backend hat einen ähnlichen Namen. Wenn Sie die Anwendung bereitstellen, wird env["deployment"] von Deployment Manager automatisch durch den Bereitstellungsnamen ersetzt.

Konfiguration erstellen

Nachdem Sie mit allen Vorlagen fertig sind, können Sie eine Konfiguration erstellen, um Ihre Ressourcen bereitzustellen. Erstellen Sie eine Konfigurationsdatei mit dem Namen nodejs.yaml und folgendem Inhalt:

# 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

Ersetzen Sie ZONE_TO_RUN durch die Zone, in der Sie die Ressourcen verwenden möchten, z. B. us-central1-a.

Ressourcen bereitstellen

Stellen Sie jetzt Ihre Ressourcen bereit. Führen Sie über die Google Cloud CLI folgenden Befehl aus:

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

Wenn das Deployment abgeschlossen ist, zeigt der Deployment Manager eine Zusammenfassung der erstellten Ressourcen an, die etwa folgendermaßen aussieht:

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

Anwendung testen

Um Ihre Anwendung zu testen, ermitteln Sie zuerst die externe IP-Adresse, die den Traffic weiterleitet. Fragen Sie dazu folgende Weiterleitungsregel ab:

$ 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 diesem Fall lautet die externe IP 104.154.81.44.

Rufen Sie dann in einem Browser die externe IP-Adresse mit Port 8080 auf. Wenn beispielsweise Ihre externe IP 104.154.81.44 ist, lautet die URL:

http://104.154.81.44:8080

Es sollte eine leere Seite angezeigt werden. Posten Sie dann eine Nachricht auf der Seite. Wechseln Sie zur folgenden URL:

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

Es wird bestätigt, dass Ihre Nachricht hinzugefügt wurde. Gehen Sie zurück zur Haupt-URL. Die Seite sollte jetzt folgende Meldung enthalten:

hellothere!

Sie haben jetzt eine bereitgestellte Anwendung, die an sie gesendete Nachrichten protokollieren kann.

Tipp

Wenn Sie mit diesem Beispiel fertig sind, haben Sie folgende Möglichkeiten: