Getting started with Cloudify Blueprint

Cloudify Blueprint is a YAML file with definitions of resources and connections between them, the syntax is based on the TOSCA  specification (topology orchestration specification for cloud applications).  It is best suited to  enable multi-domain service orchestration based on Ansible, Terraform, AWS Cloud Formation, Azure ARM, Kubernetes Helm and so on.

In this guide you will learn the basics on how to write service automation using the Cloudify blueprint.  The guide is broken down into the following steps.

  • Step 1 – Blueprint syntax introduction, define structure, inputs and outputs.
  • Step 2 – Manage a resource, use the topology section to define a web server resource node.
  • Step 3 – Integrate between resources in a blueprint, use relationships to make the resources work together.
  • Step 4 – Use blueprints together by using service composition and create a service from several independent blueprints

NOTE: To simplify this exercise, the examples in this guide runs on the local Cloudify Manager instance and therefore wouldn’t require any cloud credentials. 

If you’re  interested to learn how to use the Cloudify blueprint to run Ansible, Terraform, Kubernetes based services across a Multi Cloud infrastructure, you can refer to the relevant section in the Cloudify Getting Started guide

Basic Blueprint Structure 

The input and output blueprint example walks illustrates the most basic blueprint structure.

tosca_definitions_version: cloudify_dsl_1_3

description: >
  Input and outputs - describing the most basic blueprint structure

imports:
  - https://cloudify.co/spec/cloudify/6.3.0/types.yaml

inputs:
  hello:
    description: Say Hello to
    default: World

node_templates:
  MyResource:
    type:  cloudify.nodes.ApplicationModule

capabilities:
  hello:
    value:  { get_input: hello }

Blueprint Description

The description section:

description: >
  Input and outputs - describing the most basic blueprint structure

The description section as its name suggests is used to provide a free text field to describe the purpose and usage of this specific blueprint.

The import section

imports:
  - https://cloudify.co/spec/cloudify/6.3.0/types.yaml

The import section imports the relevant resource providers libraries (a.k.a plugins) that will be used in this blueprint. 

Cloudify comes with a list of built in resource provider libraries known as plugins which include cloud infrastructure resources such as AWS, Azure and GCP as well as infrastructure resources such as Ansible, Terraform , AWS Cloud Formation and Azure ARM as well as Kubernetes resources. You can find the full list here.

Note that you can also create custom resource libraries and add them to the blueprint. You can find more information on how to add custom plugins here.

The input section

inputs:
   hello:
    description: Say Hello to
    default: World

The input section as its name suggests defines the arguments that a user can pass and are needed to run the blueprint. An input can have a default value which will be used in case that the user didn’t provide input on this input field. An input can also be constrained to ensure that the value provided is valid.

The capabilities section

The capabilities section exposes the relevant resources to the outside world. Usually that will be API endpoint, website URL needed to get access to this service by other users or services.
In this specific example we print the input value using the get_input intrinsic functions

capabilities:
   hello:
    value:  { get_input: hello }

Adding Node Types 

The hello world example runs a simple web server application.

It illustrates how you could write a simple service lifecycle operation and execute it through Cloudify.  (Note that in this example the web server comes empty, in the next example we will demonstrate how you can add a web application to the server.)

The node template section

The node template section is the main part of the blueprint. It defines the resources in our  environment and the lifecycle operation of each resource (node) and the relationship between the nodes.

 http_web_server:
    type: cloudify.nodes.WebServer
    properties:
      port: { get_input: webserver_port }
    interfaces:
      cloudify.interfaces.lifecycle:
        start:
          implementation: webserver/start.sh
          executor: central_deployment_agent
        stop:
          implementation: webserver/stop.sh
          executor: central_deployment_agent

Node definition:

The TOSCA based node definition is based on an object oriented approach. As with other object oriented languages it includes interfaces, properties , and it also supports inheritance.

http_web_server:
    type: cloudify.nodes.WebServer

In this specific case we chose node name – httpe_web_server which is of type cloudify.nodes.WebServer – the type definition is derived from the import section. In this case we chose one of the built in types cloudify.nodes.WebServer which is defined in the types.yaml. (A simplified way to browse through the definition of each available node type in each plugin would be to use the IDE code completion feature which is supported through Cloudify IDE Integration

Most of the node types are derived from cloudify.nodes. Root which as you can see below define a few interfaces , lifecycle, validation and monitoring. You can also see that operations can be specified for many different parts of a node’s lifecycle. These operations offer flexibility when modeling environments. For example, a web server might be installed during the “create” operation, and the server software may be stopped via the “stop” operation.

cloudify.nodes.Root:
    interfaces:
      cloudify.interfaces.lifecycle:
        precreate: {}
        create: {}
        configure: {}
        start: {}
        poststart: {}
        prestop: {}
        stop: {}
        delete: {}
        postdelete: {}
        pull: {}
      cloudify.interfaces.validation:
        create: {}
        delete: {}
        # Deprecated - should not be implemented.
        creation: {}
        deletion: {}
      cloudify.interfaces.monitoring:
        start: {}
        stop: {}

In our specific example we chose to implement only the lifecycle interface and only the create and delete operation.

The create and delete operation points to install.py and uninstall.py respectively which are in this case local python script located at the same folder where the blueprint.yaml is located (the path to those scripts is relative to the location of the blueprint location).

Relationship Example

In the previous Hello world example we saw how we can model a single node type and execute its lifecycle operation through a simple blueprint definition.

In this example we will add a NodeJS calculator application as another node type which will be contained in the http web server from the previous example.We will fetch this application from a git repo by calling app_scripts/create.sh as part of the create: lifecycle event.

  web_app:
    type:  cloudify.nodes.ApplicationModule
    relationships:
      - type: cloudify.relationships.contained_in
        target: http_web_server
    interfaces:
      cloudify.interfaces.lifecycle:
        create:
          implementation: app_scripts/create.sh
          executor: central_deployment_agent
          inputs:
            app_path: {get_attribute: [http_web_server, path] }
            app_git: { get_input: app_git }
        delete:
          implementation: app_scripts/delete.sh
          executor: central_deployment_agent
          inputs:
            app_path: {get_attribute: [http_web_server, path] }

For this purpose we will add a relationship section to our node definition as described below:

  relationships:
      - type: cloudify.relationships.contained_in
        target: http_web_server

The cloudify.relationships.contained_in will tell Cloudify to run the lifecycle operation of this node type on the same host that was created by the http_web_server node above

This also means that this web_app node will run only after the http_web_server has been deployed successfully

Note: The relationship is an object of its own and represents a lifecycle operation that is triggered when the relationship is established. For example: a  relationship can be expressed between a server and a load balancer. When this relationship is established, a REST call might be executed to add a server to the load balancer.

There are few built in relationships such as depends/_on connected_to and contained_in. Each plugin can also define a custom relationship implementation.

Nested Blueprints

The nested blueprint example illustrates how you can create a multi-tier or distributed service where each service will have an independent blueprint and lifecycle operation similar to the way micro-services works. We will illustrate how we can create a dependency and relationship between those services , pass inputs/output parameters etc.

To do so we are using a feature that is referred to as  Service Composition. The Service Composition node type allows us to wrap an external service and expose it as a local node type. This allows us to leverage the relationship and dependency management and the rest of the  blueprint feature just as one would with a regular blueprint.

The following diagram shows the parent blueprint topology.

The Certificate and WebService are ServiceComponent node type that each point to a child blueprint respectively..

The difference between a regular node type and a service component node type is the lego-icon on the top right. The lego icon indicates that this specific node points to another blueprint. At the bottom left we can see an arrow and a cross that allows usl zoom in and out to see the underlying blueprint behind each component.

Blueprint description

A service component is a special node type that can point to an external blueprint and expose it as local node type to the parent blueprint.  

WebServiceComponent:
    type: cloudify.nodes.ServiceComponent
    properties:
      resource_config:
        blueprint:
          external_resource: False
          id: ex3-relationship-blueprint
          blueprint_archive: { get_input:  blueprint_archive }
          main_file_name: ex3-relationship-blueprint.yaml
        deployment:
          id: ex3-relationship-blueprint

he resource configuration provides information that will allow Cloudify to create a deployment resource on demand.

There are basically two modes in which this can be done:

  • Creating a deployment from an already uploaded blueprint as described below.
     resource_config:
        blueprint:
          external_resource: True
          id: ex3-relationship-blueprint
        deployment:
          id: ex3-relationship-blueprint
  • Upload and install a blueprint on demand

If the blueprint wasn’t preloaded to the manager, we can load it on demand.To do this we need to include blueprint_archive which points to the package that contains this service and main_file_name which points to the blueprint file in the package – as described below:

 resource_config:
        blueprint:
          external_resource: False
          id: ex3-relationship-blueprint
          blueprint_archive: { get_input:  blueprint_archive }
          main_file_name: ex3-relationship-blueprint.yaml
        deployment:
          id: ex3-relationship-blueprint

For a more advanced use case of service composition see the multi cloud NodeJS example which illustrates how you can use this capability to run the same NodeJS application across different cloud and infrastructure orchestration tools. In this case we use the service components to decouple the application service from the infrastructure and thus allow the user to choose the infrastructure that best suits his needs. The EaaS example illustrates how you can use service composition to optimize the development and production environment stack.

Running the examples

To run the example you can use the following command:

Example 1 – basic blueprint

> cfy install -b ex1 -n ex1-input-output-blueprint.yaml getting-started-blueprint-examples.tar 

Example 2 – adding node type 

> cfy install -b ex2 -n ex2-node-type-blueprint.yaml getting-started-blueprint-examples.tar 

Example 3 – adding relationship

> cfy install -b ex3 -n ex3-relationship-blueprint.yaml getting-started-blueprint-examples.tar 

Example 4 – nested blueprint

> cfy install -b ex4 -n ex4-nested-blueprint.yaml getting-started-blueprint-examples.tar 

You can also use the web console interface in the following way:

Summary and next steps

In this set of examples we learned some of the core concepts behind the Cloudify blueprint.

It is important to note that the most common use case to use Cloudify blueprint today is as an orchestrator of orchestrators.  In this case Cloudify orchestrates resources based on Terraform, Kubernetes , Ansible etc across a  Multi Cloud  environment.  To learn more on how this is done refer to the relevant section in the Cloudify Getting Started Guide.

References:

comments

    Leave a Reply

    Your email address will not be published.

    Back to top