Skip to content

Latest commit

 

History

History
298 lines (237 loc) · 11.8 KB

DETAILS.md

File metadata and controls

298 lines (237 loc) · 11.8 KB

CFDeployer - Usage Details

Commands:
  cf_deploy config [ENVIRONMENT]                     # Show parsed config
  cf_deploy deploy [ENVIRONMENT] [COMPONENT]         # Deploy the specified components
  cf_deploy destroy [ENVIRONMENT] [COMPONENT]        # Destroy the specified environment/component
  cf_deploy help [COMMAND]                           # Describe available commands or one specific command
  cf_deploy json [ENVIRONMENT] [COMPONENT]           # Show parsed CloudFormation JSON for the target component
  cf_deploy kill_inactive [ENVIRONMENT] [COMPONENT]  # Destroy the inactive stack for a given component/environment
  cf_deploy status [ENVIRONMENT] [COMPONENT]         # Show the status of the specified Cloud Formation components specified in your yml
  cf_deploy switch [ENVIRONMENT] [COMPONENT]         # Switch active and inactive stacks

Options:
  -f, [--config-file=CONFIG-FILE]  # cf_deployer config file
                                   # Default: config/cf_deployer.yml
  -l, [--log-level=LOG-LEVEL]      # logging level
                                   # Default: info
                                   # Possible values: info, debug, aws-debug
  -d, [--dry-run]                  # Say what we would do but don't actually make changes to anything
  -s, [--settings=key:value]       # key:value pair to overwrite setting in config
  -i, [--inputs=key:value]         # key:value pair to overwrite in template inputs
  -r, [--region=REGION]            # Amazon region
                                   # Default: us-east-1

================

Defaults

By default cf_deployer looks at the config/cf_deployer.yml

  • This can be overridden by using the -f flag
cf_deployer -f samples/my_config.yml

By default cf_deployer will try and deploy all components listed in your config file

When using Cname Swap it looks for the ELB Name as an output referenced by the key 'ELBName' When using Auto Scaling Group Swap it looks for the Auto Scaling Group name as an output referenced by the key 'AutoScalingGroupName'

Default user hook timeout is 10 minutes, this can be overridden as shown in the examples below

For Blue/Green deployments keep-previous-stack is defaulted to true. This means that after a deployment, the previous deployed stack will be kept. For non-production environment, you may set it to false to delete the previous stack after a deployment for saving cost.

If the CloudFormation template has the output named 'AutoScalingGroupName' or the cf_deployer.yml has the setting 'auto-scaling-group-name-output', CfDeployer knows that auto-scaling groups need to deploy and will warm up the auto-scaling groups by checking associated ELB instance status if applicable.

===================

Deployment Strategies

There are 3 strategies for deploying with cf_deployer:

  • create_or_update
    • Non-Blue-Green - Updates the CloudFormation directly
  • cname_swap
    • Blue-Green - Deploy to the inactive stack, then swap the CNAME entry to point to the ElbName output from your CloudFormation template.
  • auto_scaling_group_swap
    • Blue-Green - Deploy to the inactive stack, then warm up the new auto scaling group to the same size as old. Then cool down (set instances to 0) the old auto scaling group.

==================

Settings vs Inputs

Settings:

Used by the gem for blue/green deployments and naming conventions

  • For all deploys - These are the settings you can use
    • dns-driver (defaults to CfDeployer::Driver::Route53)
    • keep-previous-stack (True/False: for Cname-Swap and Auto Scaling Group Swap, previous stack will be kept after new stack is created by default. Set it to false to delete the previous stack)
    • raise-error-for-unused-inputs (True/False: it is false by default. If it is set to true, errors will be thrown if there are any inputs which are not used as parameters of CloudFormation json templates. If it is set to false or the setting does not exist, warnings will be printed in the console if there are un-used inputs.)
    • auto-scaling-group-name-output
    • create-stack-policy: The name of the stack policy to be used during the creation of the component stack. The location of the policy json file is assumed to be "config/{create-stack-policy}.json". The intended use of this setting is to make it persistent by placing it in your cf_deployer.yml file.
    • override-stack-policy: The name of the override stack policy to be used during an update of the component stack. The location of the policy json file is assumed to be "config/{override-stack-policy}.json". Since the override policy is only used occasionally to override the create-stack-policy, the intended use of this setting is for it to be called via the --settings cli flag, v.s. being persistently set in the config file.
  • For Components Using the Cname-Swap Deployment Strategy
    • dns-fqdn (DNS record set name, for example, myApp.api.abc.com)
    • dns-zone (DNS hosted zone, for example, api.abc.com)
    • elb-name-output
Inputs:

Used by CloudFormation in the JSON template (Note: Settings can be used as inputs, but inputs are not used as settings). These can be almost anything. They are defined in the Parameters block in the CF template.

=================

Tagging Your Stacks

You can use tags option to tag your application. The root level tags will be applied to all the components. The component level tags will be only applied to that component. When a component with tags is deploying, the co-responding cloud-formation stack and auto-scaling groups and ec2 instances within the stack will be tagged with the tags of the component.

tags:
  project: my-project
  environment: <%= environment %>
  ownerEmail: [email protected]
components:
  web:
    capabilities:
      - CAPABILITY_IAM
    tags:
      component: web

=================

Get notification of events of cloud-formation stacks

You can create ASW SNS topics and set the notify option to the ARNs of the topics to get notification of the events of cloud-formation stacks. The notify option can be set to a string or an array of strings.

notify: arn:foo:boo:mytopic
components:
  web:
    notify: arn:web:topic
environments:
  prod:
    notify: arn:only-prod:topic

==================

How Termination Works

Blue/Green deployments keep at most 2 different stacks up (Blue and Green). If both are deployed and Green is active, Blue will be deleted before being deployed with the newest version.

Components may be deleted several different ways:

  • By default with Blue/Green deployments, after deploying a color (Green) stack, the opposite color (Blue) will be kept.
    • This can be overridden with the keep-previous-stack setting set as false.
  • You can use the destroy command and it will delete any versions (blue/green) of the specified component that exist (BE CAREFUL!!)
  • You can use the kill_inactive command to delete the 'inactive stack'
    • For cname-swap this is the stack that DNS is not pointing to
    • For asg_swap this is the stack that has an autoscaling group with 0 instances

==============

Hooks

There are 3 places currently where a user-provided script can be run before continuing with the deploy process. (Specified in the cf_deployer.yml) Information about how to add new hooks is at the bottom of this document.

  • before-destroy
    • This runs before a stack is spun down (This happens on delete, as well as when spinning down the stack so a new version may be deployed)
  • after-create
    • CnameSwap: Before the cname is switched to the new version (Smoke Test)
    • AutoScalingGroupSwap: Before warming up the new stack and cooling down the old
  • after-swap
    • CnameSwap: After the cname is switched (Wait Script to ensure DNS Switch happened)
    • AutoScalingGroupSwap: After the warming up the new stack, and cooling down the old stack

Examples:

Ruby Code:
components:
  web:
    after-create: raise 'Server is down' unless system('curl -f http://mydomain.com/status')
Ruby Code with Timeout:
components:
  web:
    after-create:
      code: raise 'Server is down' unless system('curl -f http://mydomain.com/status')
      timeout: 1500
Ruby Code in a Separate File:
components:
  web:
    after-create:
      file: smoke_test.rb

================

ERB Like a Pro

CFDeployer runs its YAML config file and all CF JSON templates through ERB to allow sophisticated templating.

ERB in cf_deployer.yml

ERB Templating in your cf_deployer.yml:
<% app_name = 'my_app' %>

application: <%= app_name %>

settings:
  inputs:
    S3BucketName: <%= app_name %>-<%= environment %>-artifacts
  • Note that 'environment' is exposed to the template. Other settings and inputs are not available yet because the parsing hasn't happened yet.
This also allows the use of helper classes to abstract organization-specific boilerplate settings:
<%
require 'my_cfd_helper'
helper = MyCfdHelper::SettingsHelper.new
%>

settings:
  inputs:
    InstanceSubnets: <%= helper.instance_subnets %>
    SecurityGrouops: <%= helper.default_security_groups %>
    ElasticIP:       <%= helper.next_available_elastic_ip %>

ERB in component_cf.json

Similarly to the cf_deployer.yml example above, ERB can be used with a helper class to abstract boilerplate JSON. This example might be used stand up a basic, ElasticBeanstalk-like web stack:
<%
require 'my_cfd_helper'
helper = MyCfdHelper::TemplateHelper.new config
component_name  = config[:settings][:component].capitalize
%>
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "<%= config[:settings][:application] %> Web Stack",
  "Parameters": {
     <%= template_helper.standard_params %>
    ,<%= template_helper.asg_params %>
    ,<%= template_helper.elb_params %>
  },
  "Resources": {
     <%= template_helper.component_role :component_name => component_name %>
    ,<%= template_helper.web_elb :component_name => component_name %>
    ,<%= template_helper.asg :component_name => component_name,
                             :elb_name => "#{component_name}ServerELB",
     %>
  },
  "Outputs": {
     <%= template_helper.asg_outputs :component_name => component_name %>
    ,<%= template_helper.elb_outputs :component_name => component_name %>
  }
}

  • Note that the component-specific settings and inputs parsed from the cf_deployer.yml are made available via the 'config' Hash. DNS Providers are pluggable based on the dns-driver setting. To supply a new driver, you'll need to implement the find_alias_target and set_alias_target methods. See lib/cf_deployer/dns/ for examples.

================

Extending CFDeployer

Adding New Hooks

Hooks should be relatively simple to extend. The Hook class should be initialized with the hook information pulled from the config file, not hard-coded ruby. Convention is the YAML contains a key with the name of the hook as the key. The value can be:

  • Ruby Code
  • A hash containing the :file key (Takes a file relative to the config file directory)
  • A hash containing the :code key

The instantiated object can then be #run This takes any one parameter (generally a hash, currently a hash with all your settings/inputs/JSON Template information) which will then be available in the user defined script/code (Currently the hash may be referenced by accessing the 'context' variable that is available due to passing the binding to eval in CfDeployer::Hook#execute)

Adding New DNS Providers

DNS Providers are pluggable based on the dns-driver setting. To supply a new driver, you'll need to implement the find_alias_target and set_alias_target methods. See lib/cf_deployer/driver/ for examples.