Uncategorized Cloud Automation Cloud Management Platform Cross-Cloud Services

Customizing Cloud Assembly Deployments with Cloud-Init

Automation is great. We all love the idea of hitting a request button, and a few minutes having a resource (Virtual Machine in most cases) drop in our lap. It’s like magic! That being said, the magic wears off quickly when the machine isn’t actually useful. How do we make machines useful? Through varying forms of customization! Often times, these customizations include things like OS level configurations, user account management, or application installation and subsequent configuration of those applications.

We’ve talked many times about the fact that Cloud Assembly was designed from the ground up with the concept of “multi-cloud” in mind. In public cloud, we don’t have access to the vSphere Customization Specification concept to perform some of the levels of customization. The most common path in Public Cloud today is to leverage Cloud-Init

Logo

Cloud-Init is a bootstrapping utility for virtual machines used to apply customizations. A couple of examples of these customizations could be…

  • Injecting specific User data (username, password, SSH key, specific permissions)
  • Cloning down source control (Github, Gitlab, etc…) repositories and executing content from within
  • Changing individual configuration files for installed software
  • Installing packages from Yum/Apt
  • Executing open source Puppet/Chef/Ansible content

Cloud-init is just one of many ways that we provide users in Cloud Automation Services to build useful platforms that solve real problems for customers. In this blog post I’m going to share a couple of examples of how mixing Cloud-with your Cloud Assembly builds can get you useful machines, quickly!

Obtaining Cloud-Init

At the end of the day, Cloud-Init is simply a light weight agent installed on a Virtual Machine template. Most AWS AMI’s already include Cloud-Init, along with many Azure images. For on-premises builds, you can install Cloud-Init very easily, and it requires minimal configuration (if any…) in most cases.

Today, Cloud-Init only runs on Linux based builds. A similar agent, called cloudbase-init exists for Windows, but is not the focus of this article.

For Red Hat (and CentOS) based images, you can simply run a….

yum install -y cloud-init

Similarly, on Debian based systems (like Ubuntu) you can run a…

apt-get -y update 
apt-get -y install cloud-init

and the package will install easily. No further configuration is needed!

Note: Cloud-Init cannot be combined with vSphere Customization Specifications. Choose only one!

Blueprinting With CloudConfig

Note: All blueprint examples that we will talk about in this post (and more…) are committed to a Github repository. Fork it, issue Pull Requests to add more examples, open issues if you find any. Let’s grow the samples!

In Cloud Assembly, we leverage the Cloud-Init configuration as via a property in the blueprint YAML called “cloudConfig”. See below for an example of a cloudConfig.

cloudConfig: |
  #cloud-config
  hostname: ${input.hostname}
  users:
    - name: ${input.user}
      sudo: ['ALL=(ALL) NOPASSWD:ALL']
      groups: [wheel, sudo, admin]
      shell: '/bin/bash'

These cloudConfig’s can be as simple or as complex as we need them to be. Sometimes we might only need to update a hostname, other times we might want to push a hostname as well as username/password configuration data like we are doing above. Some examples show full application installations leveraging cloud-init. While this is certainly a path that can be taken, but there are some better ways to consume cloud-init than that.

Before we jump into breaking down some examples; let’s discuss some prescriptive recommendations when it comes to cloud-config utilization.

Best Practices for Cloud-Init

  1. Parameterize When Possible
    • In the above example, you’ll notice several fields that leverage the notation ${input.value}. We do this to avoid static values in our scripts, exposing the ability to take “inputs”. These can also be leveraged in Service Broker as part of custom forms
  2. Consider Cloud-Init as a Staging Tool
    • It’s easy to fall into the trap of piping all platform configurations into cloud-init. We make it pretty easy to in the platform! Focus on pulling in values that help stage your server for larger interactions. Examples of this include things like pushing user account data (using the above guidance around parameterizing values), or the following suggestion…leveraging GIT!
  3. Avoid Long RUNCMD Blocks – Use GIT When Possible! 
    • Did you know most standard Linux builds come prepackaged with GIT? And those that don’t we can have cloud-init install GIT for us. Consider loading your installation scripts into a GIT repository, and leveraging cloud-init to clone those scripts down and execute them. This gives you a lot more freedom in the way things are executed. For example, leveraging other languages like Python (my personal favorite =))

With that out of the way, lets jump into the fun stuff – Examples!

Simple Hostname Customization

Blueprint YAML Git Link 

CloudConfig Example:

cloudConfig: |
  #cloud-config
  hostname: ${input.hostname}

In the above example, we create a simple hostname input and feed it into our cloud-config to determine the hostname being applied. This flow isn’t very intelligent, as it doesn’t have a way to check and see if that hostname is already in use. It also doesn’t perform any sort of Linux based “directory join” use cases – but it does give the requesting user the capability of setting their hostname to a customized version.

User/Account Management

Blueprint YAML Git Link

CloudConfig Example:

cloudConfig: |
  #cloud-config
  hostname: ${input.hostname}
  users:
    - name: ${input.user}
      ssh_authorized_keys:
        - ${input.sshkey}
      sudo: ['ALL=(ALL) NOPASSWD:ALL']
      groups: [wheel, sudo, admin]
      shell: '/bin/bash'

In this example, we push user data into our machine; specifically username and sshkey as well as adding appropriate permissions to the account we’re pushing in.

Note: Cloud Assembly has a built in capability to generate SSH credentials leveraging the remoteAccess properties, however, this usecase enables a developer who already has an SSH key they are using to simply enter their username, as well as the local SSH key they want to leverage into the request and immediately have access to the provisioned system.

Web Application Installation and Configuration

Blueprint YAML Git Link

CloudConfig Example:

cloudConfig: |
  #cloud-config
  package:
    - wget
    - git
  runcmd:
    - wget https://apt.puppet.com/puppet6-release-xenial.deb
    - dpkg -i puppet6-release-xenial.deb
    - apt-get update
    - apt-get install puppet -y
    - git clone -b puppet https://github.com/codyde/puppet-demoapp /puppet-demoapp
    - puppet apply --parser future /puppet-demoapp/plans/bootcampapp.pp --debug
    - systemctl restart nginx

For our final example we are ramping up the level of usefulness of our blueprint. We are…

  • Update/Install the wget and git packages
  • Install the Puppet repository
  • Update our packages in apt
  • Install Puppet open source
  • Clone down our git repository onto our build
  • Applying the Puppet manifest
  • NGINX restarted after installation completes

We’ve talked in other blog posts about how Cloud Assembly has the capability to leverage Puppet Enterprise. Puppet Enterprise has a ton of great features that make platform deployments much easier. In this case we demonstrate leveraging open-source software. We’ll talk more about Puppet Enterprise in a future blog post.

The application that deploys here has a lot of complexities to it and many steps to complete it’s configuration. We could’ve done all of that in runcmd blocks but it would’ve been extremely long – and hard to track failure around. Leveraging a Puppet manifest (an external artifact) gives us a lot more capabilities around error finding and troubleshooting. Furthermore, storing these scripts in a source control repository gives us the ability to iterate and extend our deployment object (script, manifest, code).

In Closing…

We spend a lot of time talking about how Cloud Assembly deploys platforms and services, we’re just starting our journey around discussing how to deploy USEFUL platforms. Cloud-init is just one of many paths to making our platforms complete. These examples are fairly basic, and hopefully they get your mind flowing on the possibilities and capabilities.

For further research about topics on this post here are a few bits of light reading: