ce-deploy

ASG Management

This role should be called in a separate playbook, e.g. a separate command in CI, to the rest of the build with the host set as localhost and not the target ASG group. The available hosts in the ASG group may change after it has run, so Ansible needs to interogate the ASG host group after this play has run and before building. This cannot be done unless Ansible stops and starts again.

In order to manipulate an AWS Autoscaling Group (ASG) your deploy user must have an AWS CLI profile for a user with the following IAM permissions:

Set the asg_management.name to the machine name of your ASG in order to automatically suspend and resume autoscaling on build.

To use this role the recommended approach is two different playbooks and separate Ansible commands. Don’t forget to add the asg_management variables to your variables file as well. Below you will find a GitLab CI example and suggested variables.

.gitlab-ci.yml

---
stages:
  - deploy

deploy_dev:
  stage: deploy
  script:
    - /bin/sh /home/deploy/ce-deploy/scripts/deploy.sh --workspace "$CI_PROJECT_DIR" --playbook deploy/asg.yml --ansible-extra-vars "{\"build_type\":\"dev\"}" --build-number ${CI_PIPELINE_IID} --build-id acme-dev --boto-profile acme
    - /bin/sh /home/deploy/ce-deploy/scripts/build.sh --workspace "$CI_PROJECT_DIR" --playbook deploy/deploy-dev.yml --build-number ${CI_PIPELINE_IID} --build-id acme-dev --boto-profile acme
    - /bin/sh /home/deploy/ce-deploy/scripts/cleanup.sh --workspace "$CI_PROJECT_DIR" --playbook deploy/asg.yml --ansible-extra-vars "{\"build_type\":\"dev\"}" --build-number ${CI_PIPELINE_IID} --build-id acme-dev --boto-profile acme
  rules:
    - if: '$CI_PIPELINE_SOURCE != "web" && $CI_COMMIT_BRANCH == "dev"'
    - if: '$CI_PIPELINE_SOURCE == "web" && $SYNC == "no" && $BUILD_TYPE == "dev"'

asg.yml

---
- hosts: localhost
  vars_files:
    - vars/common.yml
    - "vars/{{ build_type }}.yml"
  roles:
    - asg_management

deploy-dev.yml

---
- hosts: _ce_www_acme_codeenigma_net # ASG group name from EC2 discovery
  vars_files:
    - vars/common.yml
    - vars/dev.yml
  roles:
    - _meta/deploy-drupal8

Process explained

The example is a single development environment Drupal build. Your CI will call asg.yml with the deploy.sh script which will only run the deploy operation, therefore it will try to suspend ASG processes and wait until the ASG has settled down before continuing. After that we call a normal Drupal build, deploy-dev.yml, same as you would if it were a standalone server or a static cluster. Finally, we call asg.yml again but this time with the cleanup.sh script which will only run the cleanup operation, therefore it will re-enable the suspended ASG processes.

Default variables

---
# AWS ASG variables to allow for the suspension of autoscaling during a code deployment.
asg_management:
  name: "" # if the deploy is on an ASG put the name here
  #target_group_name: "example" # matches the ASG name by default, specify if your TargetGroup name is different (for example due to the 32-char name length limit in AWS)
  refresh_asg_instances: true # runs only if squashFS image unmount failed and this set to true.
  profile: "{{ lookup('env','AWS_PROFILE') | trim | default(omit) }}" # the boto profile name to use if not the system default (if doesn't exist, then EC2 instance IAM profile will be used)
  region: "eu-west-1"
  suspend_processes: "Launch Terminate HealthCheck" # space separated string, see https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-suspend-resume-processes.html
  pause: 10 # localhost ping count, to wait before polling the AWS API again for instance statuses (instead of ansible 'pause' module which seems to be buggy and hangs sometimes)