네트워크 부하 분산 로그북 앱 배포

이 가이드에서는 프런트엔드에 Node.js를 사용하고 백엔드에 MySQL을 사용하는 예시 로그북 앱을 배포합니다. 이 가이드를 완료하면 다음 리소스가 배포에 포함됩니다.

네트워크 부하 분산된 배포 리소스(확대하려면 클릭)

Deployment Manager를 처음 사용하는 경우에는 빠른 시작 또는 단계별 안내를 참조하세요.

시작하기 전에

리소스 템플릿 만들기

이 예시에서는 다양한 Google Cloud 리소스가 포함된 배포를 시작합니다. 시작하려면 이러한 리소스를 개별적으로 정의하는 템플릿을 만듭니다. 나중에 이러한 리소스를 최종 구성에서 호출합니다. 배포에는 다음 리소스가 포함됩니다.

  • 앱에 대해 MySQL 데이터베이스를 호스팅하는 Compute Engine 인스턴스
  • Node.js 앱에 대해 Docker 이미지를 사용하는 프런트엔드 인스턴스를 위한 인스턴스 템플릿
  • 인스턴스 템플릿을 사용하여 2개의 프런트엔드 인스턴스를 만드는 관리형 인스턴스 그룹
  • 들어오는 트래픽을 기준으로 추가 프런트엔드 인스턴스를 시작하거나 중지하는 자동 확장 처리
  • 프런트엔드 인스턴스를 사용해서 작업을 수행할 수 있는지 여부를 확인하는 상태 확인
  • 전달 규칙이 포함된 네트워크 부하 분산기
  • 관리형 인스턴스 그룹의 대상 풀

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이면 URL은 다음과 같습니다.

http://104.154.81.44:8080

예상대로 빈 페이지가 표시되면 메시지를 게시합니다. 다음 URL로 이동합니다.

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

메시지가 추가되었음을 알리는 확인이 표시됩니다. 기본 URL로 돌아가면 페이지에 다음 메시지가 표시됩니다.

hellothere!

이제 전송된 메시지를 로깅할 수 있는 앱이 배포되었습니다.

다음 단계

이 샘플을 완료한 후에는 다음을 수행할 수 있습니다.