部署網路負載平衡的 Logbook 應用程式

在本教學課程中,您將部署一個前端使用 Node.js、後端使用 MySQL 的 Logbook 範例應用程式。教學課程結束時,您的部署作業會具有下列資源:

網路負載平衡的部署資源 (按一下可放大)

如果您是第一次使用 Deployment Manager,請參閱快速入門導覽課程逐步指南教學課程。

事前準備

建立資源範本

本範例會啟動包含各種 Google Cloud資源的部署作業。首先,您會建立範本,分別定義這些資源。稍後,您會在最終設定中呼叫這些範本。您的部署作業包含下列資源:

  • Compute Engine 執行個體,用於託管應用程式的 MySQL 資料庫。
  • 前端執行個體的執行個體範本,使用 Node.js 應用程式的 Docker 映像檔。
  • 代管執行個體群組,會使用執行個體範本建立兩個前端執行個體。
  • 自動配置器,會根據連入流量啟動或停止其他前端執行個體。
  • 健康狀態檢查,會檢查前端執行個體是否能順利執行工作。
  • 具有轉送規則的網路負載平衡器。
  • 代管執行個體群組的目標集區。

為 MySQL 後端建立範本

本應用程式的後端是執行 MySQL Docker 容器的單一 Compute Engine 執行個體。container_vm.py 範本會定義可執行 Docker 容器的 Compute Engine 執行個體。為確保範本符合正確的結構,並且包含所有必要屬性,您還需要一個結構定義檔。

請複製下方的範本,或從 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

下載範本的結構定義檔

範本中有一些未定義的屬性 (例如 containerImage),將會在後續的範本中定義。

在 Compute Engine 執行個體上使用容器映像檔時,您還需要提供資訊清單檔案,以說明要使用的容器映像檔。建立名為 container_helper.py 的輔助方法,以動態方式定義容器資訊清單:

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

為 Node.js 前端建立範本

應用程式前端會執行 Node.js,並允許使用者在網頁中張貼訊息。前端會在虛擬機器執行個體群組上執行,並由自動調度器和負載平衡器提供支援。如要建立前端範本,請按照下列指示操作。

  1. 建立「執行個體範本」資源。

    您需要使用執行個體範本來建立代管執行個體群組,這是一組可做為單一實體控管的相同虛擬機器 (VM) 執行個體。

    建立名為 container_instance_template.py 的檔案,並下載範本的結構定義:

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

    下載範本的結構定義檔

  2. 建立自動配置器、代管執行個體群組和負載平衡器。

    接著,建立另一個使用 container_instance_template.py 範本的範本和其餘前端資源,包含自動配置器、負載平衡器和代管執行個體群組。

這個範本包含下列資源:

  1. 使用 container_instance_template.py 範本的「執行個體範本」

  2. 使用執行個體範本的「代管執行個體群組」,以及參照代管執行個體群組的「自動調度器」。使用參照可確保 Deployment Manager 會以特定順序建立資源。在本例中,代管執行個體群組是在自動配置器之前建立。

  3. 「網路負載平衡器」包含下列資源:

    • 具有單一外部 IP 位址且向網際網路公開的轉送規則。
    • 包含您先前建立的代管執行個體群組的目標集區。
    • 要附加到目標集區的健康狀態檢查。

建立名為 frontend.py 的檔案,並下載範本的結構定義:

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

下載範本的結構定義檔

建立整合範本

最後,建立能整合前後端範本的範本。建立名為 nodejs.py 的檔案,並在當中加入下列內容:

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

下載範本的結構定義檔

請注意,應用程式的前端名為 env["deployment"]-frontend,後端的名稱也類似。部署應用程式時,Deployment Manager 會自動將 env["deployment"] 替換為部署名稱。

建立設定

所有範本準備就緒後,您可以建立用於部署資源的設定。建立名為 nodejs.yaml 的設定檔,並在當中加入下列內容:

# 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

ZONE_TO_RUN 替換為所需的資源所在區域,例如 us-central1-a

部署資源

現在可以部署您的資源。使用 Google Cloud CLI 執行下列指令:

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

部署完成後,Deployment Manager 會顯示已建立資源的摘要,您將看到類似以下的內容:

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

測試應用程式

如要測試應用程式,請先查詢轉送規則,以取得提供流量的外部 IP 位址:

$ 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

在本範例中,外部 IP 為 104.154.81.44

接著,在瀏覽器中前往通訊埠為 8080 的外部 IP 位址。舉例來說,如果您的外部 IP 位址為 104.154.81.44,則網址為:

http://104.154.81.44:8080

您應該會看到空白頁面,這是正常的。接下來,請在網頁中張貼訊息。請前往下列網址:

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

系統會顯示確認訊息,說明已新增訊息。返回主要網址,該頁面現在應該會顯示以下訊息:

hellothere!

您現在已部署應用程式,可以記錄傳送給該應用程式的訊息。

後續步驟

完成本範例後,您可以: