Membuat aplikasi logbook load balancer HTTP

Contoh lanjutan ini menunjukkan cara mem-build aplikasi logbook yang menggunakan node.js untuk frontend-nya dan MySQL untuk backend-nya. Template ini juga membuat dan menghubungkan load balancer HTTP yang melakukan load balancing di dua zona, dan autoscaler untuk menskalakan aplikasi secara otomatis.

Resource deployment load balancing HTTP
Resource deployment load balancing HTTP (klik untuk memperbesar)

Contoh ini mengasumsikan bahwa Anda sudah memahami penampung Docker, serta resource Compute Engine, terutama load balancing HTTP, penskalaan otomatis, grup instance terkelola, dan template instance.

Untuk tutorial pengantar lainnya, lihat Panduan memulai atau Panduan langkah demi langkah.

Sebelum memulai

Membuat template

Contoh ini meluncurkan deployment dengan beberapa jenis resource. Untuk memulai, Anda akan membuat template yang dapat digunakan kembali yang menentukan resource ini secara terpisah. Nanti, Anda akan menggunakan template ini dalam konfigurasi akhir.

Di akhir contoh ini, Anda akan memiliki deployment yang berisi resource berikut:

  • Satu instance Compute Engine untuk virtual machine MySQL backend.
  • Template instance yang menggunakan image Docker.
  • Dua grup instance terkelola dengan penskalaan otomatis di dua zona yang berbeda, yang menjalankan layanan node.js frontend.
  • Dua grup instance terkelola lainnya yang diskalakan secara otomatis dan menayangkan data statis.
  • Health check dan load balancer HTTP untuk mendistribusikan traffic di seluruh grup instance terkelola.

Membuat template backend

Backend aplikasi ini adalah satu instance Compute Engine yang menjalankan penampung Docker MySQL. Buat template yang menentukan instance Compute Engine yang menggunakan image yang dioptimalkan untuk container. Beri nama file container_vm.[py|jinja]:

Jinja



{% from 'container_helper.jinja' import GenerateManifest %}
{% set COMPUTE_URL_BASE = 'https://www.googleapis.com/compute/v1/' %}

resources:
- name: {{ env['name'] }}
  type: compute.v1.instance
  properties:
    zone: {{ properties['zone'] }}
    machineType: {{ COMPUTE_URL_BASE }}projects/{{ env['project'] }}/zones/{{ properties['zone'] }}/machineTypes/f1-micro
    metadata:
      items:
      - key: gce-container-declaration
        value: |
          {{ GenerateManifest(env['name'], properties['port'], properties['dockerImage'], properties['dockerEnv'])|indent(10) }}
    disks:
    - deviceName: boot
      type: PERSISTENT
      autoDelete: true
      boot: true
      initializeParams:
        diskName: {{ env['name'] }}-disk
        sourceImage: {{ COMPUTE_URL_BASE }}projects/cos-cloud/global/images/{{ properties['containerImage'] }}
    networkInterfaces:
    - accessConfigs:
      - name: external-nat
        type: ONE_TO_ONE_NAT
      network: {{ COMPUTE_URL_BASE }}projects/{{ env['project'] }}/global/networks/default
    serviceAccounts:
      - email: default
        scopes:
        - https://www.googleapis.com/auth/logging.write
        - https://www.googleapis.com/auth/monitoring.write

Python

# 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

Template menentukan sejumlah variabel, seperti containerImage dan manifest, yang akan diisi saat Anda menentukan konfigurasi. Template ini saja hanya membuat satu instance virtual machine (VM).

Saat menggunakan image container di instance Compute Engine, Anda juga perlu menyediakan file manifes (berbeda dengan manifes Deployment Manager) untuk menjelaskan kepada Compute Engine image container mana yang akan digunakan. Buat metode bantuan yang disebut container_helper.[py|jinja] untuk menentukan manifes penampung secara dinamis:

Jinja



{% macro GenerateManifest(name, port, dockerImage, dockerEnv) -%}

apiVersion: v1
kind: Pod
metadata:
  name: {{ name }}
spec:
  containers:
  - name: {{ name }}
    image: {{ dockerImage }}
    ports:
    - hostPort: {{ port }}
      containerPort: {{ port }}
    {% if dockerEnv -%}
    env:
    {% for key, value in dockerEnv.items() -%}
    - name: {{ key }}
      value: '{{ value }}'
    {% endfor -%}
    {% endif -%}
{%- endmacro -%}

Python

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

Membuat template frontend

Frontend aplikasi ini menjalankan Node.js dan memungkinkan pengguna memposting pesan ke halaman web. Akan ada dua grup instance terkelola yang masing-masing berisi dua instance: grup instance terkelola utama, dan grup instance terkelola sekunder untuk load balancing.

Untuk membuat template frontend ini, gunakan petunjuk berikut.

  1. Membuat template instance.

    Anda memerlukan resource Template Instance untuk membuat grup instance terkelola, yang merupakan grup instance VM identik yang dikelola secara terpusat. Contoh ini membuat grup instance terkelola untuk instance node.js frontend, tetapi pertama-tama, Anda harus membuat Template Instance.

    Tentukan file bernama container_instance_template.[py|jinja]:

    Jinja

    
    
    {% from 'container_helper.jinja' import GenerateManifest %}
    {% set IT_NAME = env['name'] + '-it' %}
    
    resources:
    - name: {{ IT_NAME }}
      type: compute.v1.instanceTemplate
      properties:
        properties:
          metadata:
            items:
            - key: gce-container-declaration
              value: |
                {{ GenerateManifest(env['name'], properties['port'],properties['dockerImage'], properties['dockerEnv'])|indent(12) }}
          machineType: f1-micro
          disks:
          - deviceName: boot
            boot: true
            autoDelete: true
            mode: READ_WRITE
            type: PERSISTENT
            initializeParams:
              sourceImage: https://www.googleapis.com/compute/v1/projects/cos-cloud/global/images/{{ properties['containerImage'] }}
          networkInterfaces:
          - accessConfigs:
            - name: external-nat
              type: ONE_TO_ONE_NAT
            network: https://www.googleapis.com/compute/v1/projects/{{ env['project'] }}/global/networks/default
          serviceAccounts:
            - email: default
              scopes:
              - https://www.googleapis.com/auth/logging.write
              - https://www.googleapis.com/auth/monitoring.write
    outputs:
    - name: instanceTemplateSelfLink
      value: $(ref.{{ IT_NAME }}.selfLink)
    

    Python

    # 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. Membuat grup instance terkelola dengan penskalaan otomatis.

    Setelah memiliki template instance, Anda dapat menentukan template yang menggunakan template instance untuk membuat grup instance terkelola dengan penskalaan otomatis. Buat file baru bernama autoscaled_group.[py|jinja] dengan konten berikut:

    Jinja

    
    
    resources:
      - name: {{ env["name"] }}-igm
        type: compute.v1.instanceGroupManager
        properties:
          zone: {{ properties["zone"] }}
          targetSize: {{ properties["size"] }}
          baseInstanceName: {{ env["name"] }}-instance
          instanceTemplate: {{ properties["instanceTemplate"] }}
    
      - name: {{ env["name"] }}-as
        type: compute.v1.autoscaler
        properties:
          zone: {{ properties["zone"] }}
          target: $(ref.{{ env["name"] }}-igm.selfLink)
          autoscalingPolicy:
            maxNumReplicas: {{ properties["maxSize"] }}
    

    Python

    # 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."""
    
      # NOTE: Once we can specify the port/service during creation of IGM,
      # we will wire it up here.
      name = context.env['name']
      resources = [{
          'name': name + '-igm',
          'type': 'compute.v1.instanceGroupManager',
          'properties': {
              'zone': context.properties['zone'],
              'targetSize': context.properties['size'],
              'baseInstanceName': name + '-instance',
              'instanceTemplate': context.properties['instanceTemplate']
          }
      }, {
          'name': name + '-as',
          'type': 'compute.v1.autoscaler',
          'properties': {
              'zone': context.properties['zone'],
              'target': '$(ref.' + name + '-igm.selfLink)',
              'autoscalingPolicy': {
                  'maxNumReplicas': context.properties['maxSize']
    
              }
          }
      }]
      return {'resources': resources}
    

    Buat file skema yang sesuai:

    Jinja

    # 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.
    
    info:
      title: Autoscaled, network LB IGM template
      author: Google
      description: Creates an autoscaled Instance Group Manager running the specified Docker image
      version: 1.0
    
    required:
    - zone
    - instanceTemplate
    
    properties:
      zone:
        type: string
        description: Zone in which this VM will run
    
      instanceTemplate:
        type: string
        description: URL for the instance template to use for IGM
    
      size:
        type: integer
        default: 1
        description: Initial size of the Managed Instance Group
    
      maxSize:
        type: integer
        default: 1
        description: Maximum size the Managed Instance Group will be autoscaled to
    

    Python

    # 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.
    
    info:
      title: Autoscaled, network LB IGM template
      author: Google
      description: Creates an autoscaled Instance Group Manager running the specified Docker image
      version: 1.0
    
    required:
    - zone
    - instanceTemplate
    
    properties:
      zone:
        type: string
        description: Zone in which this VM will run
    
      instanceTemplate:
        type: string
        description: URL for the instance template to use for IGM
    
      size:
        type: integer
        default: 1
        description: Initial size of the Managed Instance Group
    
      maxSize:
        type: integer
        default: 1
        description: Maximum size the Managed Instance Group will be autoscaled to
    

  3. Buat resource menggunakan template ini.

    Hingga saat ini, Anda telah menentukan template dasar yang menentukan properti resource Anda. Dengan template ini, tentukan penyiapan frontend Anda. Buat file baru bernama service.[py|jinja] dengan konten berikut:

    Jinja

    
    
    resources:
      - name: {{ env["name"] }}
        type: container_instance_template.jinja
        properties:
          port: {{ properties["port"] }}
          dockerEnv: {{ properties["dockerEnv"] }}
          dockerImage: {{ properties["dockerImage"] }}
          containerImage: {{ properties["containerImage"] }}
    
      - name: {{ env["name"] }}-pri
        type: autoscaled_group.jinja
        properties:
          zone: {{ properties["primaryZone"] }}
          size: {{ properties["primarySize"] }}
          maxSize: {{ properties["maxSize"] }}
          port: {{ properties["port"] }}
          service: {{ properties["service"] }}
          baseInstanceName: {{ env["name"] }}-instance
          instanceTemplate: $(ref.{{ env["name"] }}-it.selfLink)
    
      - name: {{ env["name"] }}-sec
        type: autoscaled_group.jinja
        properties:
          zone: {{ properties["secondaryZone"] }}
          size: {{ properties["secondarySize"] }}
          maxSize: {{ properties["maxSize"] }}
          port: {{ properties["port"] }}
          service: {{ properties["service"] }}
          baseInstanceName: {{ env["name"] }}-instance
          instanceTemplate: $(ref.{{ env["name"] }}-it.selfLink)
    
      - name: {{ env["name"] }}-hc
        type: compute.v1.httpHealthCheck
        properties:
          port: {{ properties["port"] }}
          requestPath: /_ah/health
    
      - name: {{ env["name"] }}-bes
        type: compute.v1.backendService
        properties:
          port: {{ properties["port"] }}
          portName: {{ properties["service"] }}
          backends:
            - name: {{ env["name"] }}-primary
              group: $(ref.{{ env["name"] }}-pri-igm.instanceGroup)
            - name: {{ env["name"] }}-secondary
              group: $(ref.{{ env["name"] }}-sec-igm.instanceGroup)
          healthChecks: [ $(ref.{{ env["name"] }}-hc.selfLink) ]
    

    Python

    # 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 primary/secondary zone autoscaled IGM running specified container."""
    
    
    def GenerateConfig(context):
      """Generate YAML resource configuration."""
    
      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 + '-pri',
          'type': 'autoscaled_group.py',
          'properties': {
              'zone': context.properties['primaryZone'],
              'size': context.properties['primarySize'],
              'maxSize': context.properties['maxSize'],
              'port': context.properties['port'],
              'service': context.properties['service'],
              'baseInstanceName': name + '-instance',
              'instanceTemplate': '$(ref.' + name + '-it.selfLink)'
          }
      }, {
          'name': name + '-sec',
          'type': 'autoscaled_group.py',
          'properties': {
              'zone': context.properties['secondaryZone'],
              'size': context.properties['secondarySize'],
              'maxSize': context.properties['maxSize'],
              'port': context.properties['port'],
              'service': context.properties['service'],
              'baseInstanceName': name + '-instance',
              'instanceTemplate': '$(ref.' + name + '-it.selfLink)'
          }
      }, {
          'name': name + '-hc',
          'type': 'compute.v1.httpHealthCheck',
          'properties': {
              'port': context.properties['port'],
              'requestPath': '/_ah/health'
          }
      }, {
          'name': name + '-bes',
          'type': 'compute.v1.backendService',
          'properties': {
              'port': context.properties['port'],
              'portName': context.properties['service'],
              'backends': [{
                  'name': name + '-primary',
                  'group': '$(ref.' + name + '-pri-igm.instanceGroup)'
              }, {
                  'name': name + '-secondary',
                  'group': '$(ref.' + name + '-sec-igm.instanceGroup)'
              }],
              'healthChecks': ['$(ref.' + name + '-hc.selfLink)']
          }
      }]
      return {'resources': resources}
    

    Buat file skema yang sesuai:

    Jinja

    # 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.
    
    info:
      title: Autoscaled IGM
      author: Google
      description: Creates primary/secondary zone autoscaled IGM running specified container.
      version: 1.0
    
    imports:
    - path: autoscaled_group.jinja
    - path: ../../common/jinja/container_instance_template.jinja
      name: container_instance_template.jinja
    
    required:
    - port
    - service
    - primaryZone
    - secondaryZone
    - dockerImage
    
    properties:
      primarySize:
        type: integer
        default: 1
        description: The size of the primary autoscaled IGM
    
      secondarySize:
        type: integer
        default: 0
        description: The size of the secondary autoscaled IGM
    
      maxSize:
        type: integer
        default: 1
        description: The maximum size of the IGM
    
      containerImage:
        type: string
        default: family/cos-stable
        description: The container image to be used
    
      dockerEnv:
        type: object
        default: {}
        description: The container environment variables
    
      dockerImage:
        type: string
        description: the docker image to be used
    
      port:
        type: integer
        description: Port to expose on the container as well as on the load balancer (e.g., 8080)
    
      service:
        type: string
        description: Name of the service the port exposes for loadbalancing (backendService) purposes
    
      primaryZone:
        type: string
        description: Primary Zone in which to run the service
    
      secondaryZone:
        type: string
        description: Secondary Zone in which to run the service
    

    Python

    # 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.
    
    info:
      title: Autoscaled IGM
      author: Google
      description: Creates primary/secondary zone autoscaled IGM running specified container.
      version: 1.0
    
    imports:
    - path: autoscaled_group.py
    - path: ../../common/python/container_instance_template.py
      name: container_instance_template.py
    
    required:
    - port
    - service
    - primaryZone
    - secondaryZone
    - dockerImage
    
    properties:
      primarySize:
        type: integer
        default: 1
        description: The size of the primary autoscaled IGM
    
      secondarySize:
        type: integer
        default: 0
        description: The size of the secondary autoscaled IGM
    
      maxSize:
        type: integer
        default: 1
        description: The maximum size of the IGM
    
      containerImage:
        type: string
        default: family/cos-stable
        description: The container image to be used
    
      dockerEnv:
        type: object
        default: {}
        description: The container environment variables
    
      dockerImage:
        type: string
        description: the docker image to be used
    
      port:
        type: integer
        description: Port to expose on the container as well as on the load balancer (e.g., 8080)
    
      service:
        type: string
        description: Name of the service the port exposes for loadbalancing (backendService) purposes
    
      primaryZone:
        type: string
        description: Primary Zone in which to run the service
    
      secondaryZone:
        type: string
        description: Secondary Zone in which to run the service
    

    Mari kita uraikan apa yang dibuat template ini:

    1. Dua grup instance terkelola, satu primer dan satu sekunder.

      Template ini menggunakan template autoscaled_group.[py|jinja] untuk membuat grup instance terkelola primer dan sekunder dengan penskalaan otomatis.

    2. Selanjutnya, template akan membuat layanan backend dan health checker. Layanan backend diperlukan untuk load balancing HTTP, dan menentukan kapasitas penayangan grup instance dalam layanan backend tersebut. Dalam hal ini, grup instance terkelola utama dan sekunder adalah bagian dari backend ini, dan properti default layanan backend berlaku.

      Secara default, layanan backend melakukan load balancing berdasarkan penggunaan CPU dari grup instance terkait, tetapi Anda juga dapat melakukan load balancing berdasarkan permintaan per detik (RPS).

      Catatan: Health check selalu diperlukan saat membuat layanan backend.

Membuat template pemersatu

Terakhir, buat template pemersatu yang menggabungkan template backend dan frontend. Buat file baru bernama application.[py|jinja]:

Jinja



{% set BACKEND = env["deployment"] + "-backend" %}
{% set FRONTEND = env["deployment"] + "-frontend" %}
{% set STATIC_SERVICE = env["deployment"] + "-static-service" %}
{% set APPLICATION = env["deployment"] + "-application" %}

{% set APPLICATION_PORT = 8080 %}
{% set LB_PORT = 8080 %}
{% set MYSQL_PORT = 8080 %}

{% set CONTAINER_IMAGE = "family/cos-stable" %}

resources:
- name: {{ BACKEND }}
  type: container_vm.jinja
  properties:
    zone: {{ properties["primaryZone"] }}
    dockerImage: {{ properties["backendImage"] }}
    containerImage: {{ CONTAINER_IMAGE }}
    port: {{ MYSQL_PORT }}

- name: {{ FRONTEND }}
  type: service.jinja
  properties:
    primaryZone: {{ properties["primaryZone"] }}
    primarySize: 2
    secondaryZone: {{ properties["secondaryZone"] }}
    secondarySize: 0
    dockerImage: {{ properties["frontendImage"] }}
    containerImage: {{ CONTAINER_IMAGE }}
    port: {{ APPLICATION_PORT }}
    service: http
    # If left out will default to 1
    maxSize: 20

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

- name: {{ STATIC_SERVICE }}
  type: service.jinja
  properties:
    primaryZone: {{ properties["primaryZone"] }}
    primarySize: 2
    secondaryZone: {{ properties["secondaryZone"] }}
    secondarySize: 0
    dockerImage: {{ properties["staticImage"] }}
    containerImage: {{ CONTAINER_IMAGE }}
    port: {{ APPLICATION_PORT }}
    service: httpstatic
    # If left out will default to 1
    maxSize: 20

- name: {{ APPLICATION }}-urlmap
  type: compute.v1.urlMap
  properties:
    defaultService: $(ref.{{ FRONTEND }}-bes.selfLink)
    hostRules:
      - hosts: ["*"]
        pathMatcher: pathmap
    pathMatchers:
      - name: pathmap
        defaultService: $(ref.{{ FRONTEND }}-bes.selfLink)
        pathRules:
          - paths: ["/static", "/static/*"]
            service: $(ref.{{ STATIC_SERVICE }}-bes.selfLink)
- name: {{ APPLICATION }}-targetproxy
  type: compute.v1.targetHttpProxy
  properties:
    urlMap: $(ref.{{ APPLICATION }}-urlmap.selfLink)
- name: {{ APPLICATION }}-l7lb
  type: compute.v1.globalForwardingRule
  properties:
    IPProtocol: TCP
    portRange: {{ LB_PORT }}
    target: $(ref.{{ APPLICATION }}-targetproxy.selfLink)
- name: {{ APPLICATION }}-fw
  type: compute.v1.firewall
  properties:
    allowed:
      - IPProtocol: TCP
        ports: [ {{ LB_PORT }} ]
    sourceRanges: [ 0.0.0.0/0 ]

Python


# 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 appplication template with back-end and front-end templates."""


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

  backend = context.env['deployment'] + '-backend'
  frontend = context.env['deployment'] + '-frontend'
  static_service = context.env['deployment'] + '-static-service'
  application = context.env['deployment'] + '-application'

  container_image = 'family/cos-stable'

  application_port = 8080
  lb_port = 8080
  mysql_port = 8080

  resources = [{
      'name': backend,
      'type': 'container_vm.py',
      'properties': {
          'zone': context.properties['primaryZone'],
          'dockerImage': context.properties['backendImage'],
          'containerImage': container_image,
          'port': mysql_port
      }
  }, {
      'name': frontend,
      'type': 'service.py',
      'properties': {
          'primaryZone': context.properties['primaryZone'],
          'primarySize': 2,
          'secondaryZone': context.properties['secondaryZone'],
          'secondarySize': 0,
          'dockerImage': context.properties['frontendImage'],
          'containerImage': container_image,
          'port': application_port,
          'service': 'http',
          # If left out will default to 1
          'maxSize': 20,
          # 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)'
          }
      }
  }, {
      'name': static_service,
      'type': 'service.py',
      'properties': {
          'primaryZone': context.properties['primaryZone'],
          'primarySize': 2,
          'secondaryZone': context.properties['secondaryZone'],
          'secondarySize': 0,
          'dockerImage': context.properties['staticImage'],
          'containerImage': container_image,
          'port': application_port,
          'service': 'httpstatic',
          # If left out will default to 1
          'maxSize': 20
      }
  }, {
      'name': application + '-urlmap',
      'type': 'compute.v1.urlMap',
      'properties': {
          'defaultService': '$(ref.' + frontend + '-bes.selfLink)',
          'hostRules': [{
              'hosts': ['*'],
              'pathMatcher': 'pathmap'
          }],
          'pathMatchers': [{
              'name': 'pathmap',
              'defaultService': '$(ref.' + frontend + '-bes.selfLink)',
              'pathRules': [{
                  'paths': ['/static', '/static/*'],
                  'service': '$(ref.' + static_service + '-bes.selfLink)'
              }]
          }]
      }
  }, {
      'name': application + '-targetproxy',
      'type': 'compute.v1.targetHttpProxy',
      'properties': {
          'urlMap': '$(ref.' + application + '-urlmap.selfLink)'
      }
  }, {
      'name': application + '-l7lb',
      'type': 'compute.v1.globalForwardingRule',
      'properties': {
          'IPProtocol': 'TCP',
          'portRange': lb_port,
          'target': '$(ref.' + application + '-targetproxy.selfLink)'
      }
  }, {
      'name': application + '-fw',
      'type': 'compute.v1.firewall',
      'properties': {
          'allowed': [{
              'IPProtocol': 'TCP',
              'ports': [lb_port]
          }],
          'sourceRanges': ['0.0.0.0/0']
      }
  }]
  return {'resources': resources}

Buat file skema yang sesuai:

Jinja

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

info:
  title: Application Template
  author: Google
  description: Create application template with back-end and front-end templates
  version: 1.0

imports:
- path: service.jinja
- path: ../../common/jinja/container_vm.jinja
  name: container_vm.jinja

required:
- primaryZone
- secondaryZone
- backendImage
- frontendImage
- staticImage

properties:
  primaryZone:
    type: string
    description: Primary Zone in which to run the service

  secondaryZone:
    type: string
    description: Secondary Zone in which to run the service

  backendImage:
    type: string
    description: Docker image to use in the backend

  frontendImage:
    type: string
    description: Docker image to use in the frontend service

  staticImage:
    type: string
    description: Docker image to use in the static service

Python

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

info:
  title: Application Template
  author: Google
  description: Create application template with back-end and front-end templates
  version: 1.0

imports:
- path: service.py
- path: ../../common/python/container_vm.py
  name: container_vm.py

required:
- primaryZone
- secondaryZone
- backendImage
- frontendImage
- staticImage

properties:
  primaryZone:
    type: string
    description: Primary Zone in which to run the service

  secondaryZone:
    type: string
    description: Secondary Zone in which to run the service

  backendImage:
    type: string
    description: Docker image to use in the backend

  frontendImage:
    type: string
    description: Docker image to use in the frontend service

  staticImage:
    type: string
    description: Docker image to use in the static service

Selain frontend dan backend, template juga menentukan beberapa resource tambahan:

  1. Layanan statis dengan grup instance terkelola primer dan sekunder. Layanan statis ini menayangkan halaman web yang terletak di jalur /static di aplikasi Anda.

  2. Resource Peta URL. Load balancing HTTP memerlukan peta URL untuk memetakan berbagai URL ke jalur yang benar. Dalam hal ini, jalur default, yang ditunjukkan oleh properti defaultService, adalah layanan backend yang Anda buat sebelumnya. Jika pengguna membuka /static, peta URL akan memetakan jalur tersebut ke layanan statis, seperti yang ditentukan di bagian pathMatchers.

  3. Aturan penerusan global dan proxy HTTP target. Karena aplikasi di-load balance di dua zona terpisah, Anda memerlukan aturan penerusan global yang menayangkan satu alamat IP eksternal. Selain itu, proxy HTTP target diperlukan untuk penyiapan load balancing HTTP.

  4. Aturan firewall yang memungkinkan traffic melalui port 8080.

Membuat konfigurasi

Setelah template dan skema terkait siap, Anda dapat membuat konfigurasi yang men-deploy resource ini. Buat file konfigurasi bernama application.yaml dengan konten berikut, dan ganti ZONE_TO_RUN dan SECONDARY_ZONE_TO_RUN dengan zona utama dan sekunder pilihan Anda.

Jinja

# 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 in two zones running nodejs
# for serving traffic using L7 loadbalancing. 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.
#
# NOTE: Due to the fact that IGM does not allow specifying service/port to
# created IG, you must run the following commands after creation of the
# template:
#
# export DEPLOYMENT=<DEPLOYMENT NAME>
# export PRIMARY_ZONE=<PRIMARY ZONE>
# export SECONDARY_ZONE=<SECONDARY ZONE>
#
# gcloud compute instance-groups unmanaged set-named-ports ${DEPLOYMENT}-frontend-pri-igm \
#  --named-ports http:8080,httpstatic:8080 \
#  --zone ${PRIMARY_ZONE}
#
# gcloud compute instance-groups unmanaged set-named-ports ${DEPLOYMENT}-frontend-sec-igm \
#  --named-ports http:8080,httpstatic:8080 \
#  --zone ${SECONDARY_ZONE}
#
# Then to see the IP that exposes the application, you can do:
# gcloud compute forwarding-rules list | grep application-${DEPLOYMENT}-l7lb

imports:
- path: application.jinja

resources:
- name: nodejs
  type: application.jinja
  properties:
    primaryZone: ZONE_TO_RUN
    secondaryZone: SECOND_ZONE_TO_RUN
    backendImage: gcr.io/deployment-manager-examples/mysql
    frontendImage: gcr.io/deployment-manager-examples/nodejsservice
    staticImage: gcr.io/deployment-manager-examples/nodejsservicestatic

Python

# 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 in two zones running nodejs
# for serving traffic using L7 loadbalancing. 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.
#
# NOTE: Due to the fact that IGM does not allow specifying service/port to
# created IG, you must run the following commands after creation of the
# template:
#
# export DEPLOYMENT=<DEPLOYMENT NAME>
# export PRIMARY_ZONE=<PRIMARY ZONE>
# export SECONDARY_ZONE=<SECONDARY ZONE>
#
# gcloud compute instance-groups unmanaged set-named-ports ${DEPLOYMENT}-frontend-pri-igm \
#  --named-ports http:8080,httpstatic:8080 \
#  --zone ${PRIMARY_ZONE}
#
# gcloud compute instance-groups unmanaged set-named-ports ${DEPLOYMENT}-frontend-sec-igm \
#  --named-ports http:8080,httpstatic:8080 \
#  --zone ${SECONDARY_ZONE}
#
# Then to see the IP that exposes the application, you can do:
# gcloud compute forwarding-rules list | grep application-${DEPLOYMENT}-l7lb

imports:
- path: application.py

resources:
- name: nodejs
  type: application.py
  properties:
    primaryZone: ZONE_TO_RUN
    secondaryZone: SECOND_ZONE_TO_RUN
    backendImage: gcr.io/deployment-manager-examples/mysql
    frontendImage: gcr.io/deployment-manager-examples/nodejsservice
    staticImage: gcr.io/deployment-manager-examples/nodejsservicestatic

Men-deploy konfigurasi

Sekarang, mari kita deploy resource Anda. Dengan menggunakan Google Cloud CLI, jalankan perintah berikut, dengan memilih untuk mengganti advanced-configuration-l7 dengan nama deployment pilihan Anda. Perhatikan bahwa nama deployment Anda akan otomatis digunakan untuk memberi nama resource.

Dalam contoh ini, nama deployment adalah advanced-configuration-l7. Jika Anda memilih untuk mengubah nama deployment, pastikan untuk menggunakan nama deployment tersebut di semua contoh berikut.

gcloud deployment-manager deployments create advanced-configuration-l7 --config application.yaml

Responsnya akan terlihat seperti resource berikut:

Waiting for create operation-1469468950934-5387966d431f0-49b11bc4-1421b2f0...done.
Create operation operation-1469468950934-5387966d431f0-49b11bc4-1421b2f0 completed successfully.
NAME                                               TYPE                             STATE      ERRORS
advanced-configuration-l7-application-fw           compute.v1.firewall              COMPLETED  []
advanced-configuration-l7-application-l7lb         compute.v1.globalForwardingRule  COMPLETED  []
advanced-configuration-l7-application-targetproxy  compute.v1.targetHttpProxy       COMPLETED  []
advanced-configuration-l7-application-urlmap       compute.v1.urlMap                COMPLETED  []
advanced-configuration-l7-backend                  compute.v1.instance              COMPLETED  []
advanced-configuration-l7-frontend-bes             compute.v1.backendService        COMPLETED  []
advanced-configuration-l7-frontend-hc              compute.v1.httpHealthCheck       COMPLETED  []
advanced-configuration-l7-frontend-it              compute.v1.instanceTemplate      COMPLETED  []
advanced-configuration-l7-frontend-pri-as          compute.v1.autoscaler            COMPLETED  []
advanced-configuration-l7-frontend-pri-igm         compute.v1.instanceGroupManager  COMPLETED  []
advanced-configuration-l7-frontend-sec-as          compute.v1.autoscaler            COMPLETED  []
advanced-configuration-l7-frontend-sec-igm         compute.v1.instanceGroupManager  COMPLETED  []
advanced-configuration-l7-static-service-bes       compute.v1.backendService        COMPLETED  []
advanced-configuration-l7-static-service-hc        compute.v1.httpHealthCheck       COMPLETED  []
advanced-configuration-l7-static-service-it        compute.v1.instanceTemplate      COMPLETED  []
advanced-configuration-l7-static-service-pri-as    compute.v1.autoscaler            COMPLETED  []
advanced-configuration-l7-static-service-pri-igm   compute.v1.instanceGroupManager  COMPLETED  []
advanced-configuration-l7-static-service-sec-as    compute.v1.autoscaler            COMPLETED  []
advanced-configuration-l7-static-service-sec-igm   compute.v1.instanceGroupManager  COMPLETED  []

Menambahkan label layanan

Selanjutnya, tentukan label layanan yang sesuai untuk grup instance terkelola Anda. Label layanan adalah metadata yang digunakan oleh layanan load balancer untuk mengelompokkan resource.

Untuk menambahkan label layanan, jalankan perintah berikut, yang mencocokkan zona utama dan sekunder dengan zona yang Anda pilih dalam file konfigurasi deployment:

gcloud compute instance-groups unmanaged set-named-ports advanced-configuration-l7-frontend-pri-igm \
  --named-ports http:8080,httpstatic:8080 \
  --zone [PRIMARY_ZONE]

gcloud compute instance-groups unmanaged set-named-ports advanced-configuration-l7-frontend-sec-igm \
  --named-ports http:8080,httpstatic:8080 \
  --zone [SECONDARY_ZONE]

Menguji konfigurasi Anda

Untuk menguji konfigurasi Anda, dapatkan alamat IP eksternal yang menyalurkan traffic dengan membuat kueri aturan penerusan:

gcloud compute forwarding-rules list | grep advanced-configuration-l7-l7lb
advanced-configuration-l7-l7lb             107.178.249.126 TCP         advanced-configuration-l7-targetproxy

Dalam hal ini, IP eksternal adalah 107.178.249.126.

Di browser, buka alamat IP eksternal di port 8080. Misalnya, jika IP eksternal Anda adalah 107.178.249.126, URL-nya adalah:

http://107.178.249.126:8080

Anda akan melihat halaman kosong, yang diharapkan. Selanjutnya, posting pesan ke halaman. Buka URL berikut:

http://107.178.249.126:8080?msg=hello_world!

Anda akan melihat konfirmasi bahwa pesan Anda telah ditambahkan. Kembali ke URL utama dan sekarang halaman akan menampilkan pesan:

hello_world!

Anda juga dapat membuka halaman statis yang Anda buat, atau memeriksa kondisi aplikasi, dengan membuka URL berikut:

# Static web page
http://107.178.249.126:8080/static

# Health check
http://107.178.249.126:8080/_ah/health

Selamat, Anda berhasil men-deploy konfigurasi.

(Opsional) Membuat image Docker

Docker memungkinkan Anda mengotomatiskan dan menjalankan software di dalam container. Container memungkinkan Anda mengisolasi berbagai layanan dalam container yang semuanya dapat dijalankan di satu instance Linux.

Contoh ini menggunakan beberapa image Docker yang ada, tetapi Anda juga dapat membuat versi image Docker ini sendiri. Anda dapat menemukan petunjuk untuk membuat image backend MySQL dan image frontend Node.js di bagian Membuat template resource.

Untuk membuat image Docker yang menayangkan halaman web statis:

  1. Buat instance VM baru dengan image yang dioptimalkan untuk container:

    gcloud compute instances create docker-playground \
      --image-family container-vm \
      --image-project google-containers \
      --zone us-central1-a \
      --machine-type f1-micro
    
  2. Hubungkan ke instance:

    gcloud compute ssh --zone us-central1-a docker-playground
    
  3. Buat file bernama Dockerfile dengan konten berikut:

    FROM node:latest
    
    RUN mkdir /var/www/
    ADD service.js /var/www/service.js
    WORKDIR /var/www/
    RUN npm install mysql
    
    CMD ["node", "service.js"]
    
  4. Buat file bernama service.js dengan konten berikut:

    var http = require('http');
    var url = require('url');
    
    console.log('Started static node server')
    
    http.createServer(function (req, res) {
      reqUrl = url.parse(req.url, true);
    
      res.useChunkedEncodingByDefault = false;
      res.writeHead(200, {'Content-Type': 'text/html'});
    
      if (reqUrl.pathname == '/_ah/health') {
        res.end('ok');
      } else if (reqUrl.pathname == '/exit') {
        process.exit(-1)
      } else {
          res.end('static server');
      }
    }).listen(8080, '0.0.0.0');
    
    console.log('Static server running at http://127.0.0.1:8080/');
    
  5. Build image Docker, dengan mengganti username dengan nama pengguna Docker Hub Anda. Jika Anda tidak memiliki nama pengguna Docker Hub, buat terlebih dahulu sebelum mem-build image Docker.

    sudo docker build --no-cache -t username/nodejsservicestatic .
    
  6. Kirim image ke repositori Docker:

    sudo docker push username/nodejsservicestatic
    

Sekarang Anda memiliki image Docker untuk menjalankan Node.js dan MySQL. Anda sebenarnya dapat melihat gambar ini di repositori dengan menelusuri nama gambar. Untuk mencoba gambar, Anda dapat mengganti semua instance gcr.io/deployment-manager-examples/mysql dan gcr.io/deployment-manager-examples/nodejsservice dengan gambar masing-masing.

Langkah berikutnya

Setelah menyelesaikan contoh ini, Anda dapat:

  • Lanjutkan untuk membuat contoh ini dengan membuat halaman web yang lebih andal, atau menambahkan lebih banyak layanan ke server web.
  • Baca selengkapnya tentang konfigurasi atau deployment.
  • Coba buat konfigurasi Anda sendiri.