Declarative Deployments— Giving Your Containers More Insight With Cycle

Cycle has been live for two months, and we’ve seen a huge push from companies wanting to take advantage of having their own private cloud without any of the management headaches that come from that. As we’ve been bringing on larger and larger clients, we’ve begun hosting workshops where we walk the client through containerizing a piece of their application and deploying it to Cycle.

For one of our client workshops, I was preparing a stack file that would deploy two containers: Magento and MariaDB. Cycle could then import that file and automatically configure and deploy the entire app without any further modification. That all went smoothly, until I realized there was a major issue with the default Magento image… an issue that led us to implement an entirely new (and powerful) feature into the Cycle.yml spec.

The Container-Infrastructure Disconnect

Containers are meant to be infrastructure agnostic — one of the many reasons adoption by developers has grown so quickly. Cycle is able to take the same container you used for local dev and run it on performance hardware without any modification.

While that is the ideal way it should work, often you’ll come across a container that, while technically agnostic, needs to have intricate knowledge of how your infrastructure is configured in order to function properly. This usually comes in the form of environment variables that need to be configured in order to provide the container with critical runtime information, such as it’s IP, domain name information, and other server-dependent variables. In cases where you need to replicate the deployment hundreds of times, it can become frustrating and tedious to update/redeploy any time one of these changes.

In the case of this Magento container, it needed to have the public IP set as an environment variable before the container started the first time. Since Cycle manages the network/IP assignments automatically, the environment variable was not populated when I deployed.

Thinking I was clever, I started the container and then copied the IP address Cycle generated for it into the environment variable, then restarted. It had…zero effect. The Magento instance started, but every link pointed to (the default)!

Despite the obvious design issues with this image, it’s not that uncommon of a pattern —especially for many of the earlier CMS software packages that were built during the shared hosting days. For infrastructure you’re managing yourself, or where you only need to deploy one time to a server you know the IP of, it’s not that big of a deal to set it with the docker-compose command. If you’re replicating that and deploying to infrastructure where you don’t care about the IPs, this becomes a show stopper.

The solution was obvious. Cycle needed to be able to set those environment variables before starting the container, and we had the perfect opportunity to handle that.

Giving The Container What It Wants

As mentioned in the beginning of the article, we introduced a new feature into the Cycle.yml spec.

 MAGENTO_HOST: "{{ instance.ips.public }}"

That’s right. Cycle now supports injecting dynamic infrastructure-related information into your containers when they start. This is all well and good, but what about dynamically generating passwords or other goodies?

MAGENTO_DATABASE_PASSWORD: "{ string.random(n) }"

By creating certain template functions with parameters, it opens up a ton of possibilities. Those of you with sharp eyes will have noticed on one it’s `{ }` and on the other it’s `{{ }}` . The former is created only on first start, such as my database password. The latter is generated and injected every single time the container restarts.

// Generated on first start  
MAGENTO_DATABASE_PASSWORD: "{ string.random(n) }" // AbC123

// Generated every time container restarts  
MAGENTO_DATABASE_PASSWORD: "{{ string.random(n) }}" // AbC123, ZyX098 etc...

Though only a few basics are supported at this time, we’ll continuously expand the functionality. Some functions/variables you can utilize in your stacks today include:

// Generate a random string of n length  

// Public IPs  
{ instance.ips.public } OR { instance.ips.public.v4 }  
{ instance.ips.public.v6 }

// Private IPs  
{ instance.ips.private } OR { instance.ips.private.v4 }  
{ instance.ips.private.v6 }

// Inject domain assigned to container  
{ container.domain }

// TLS variables  
{ }  
{ }

// Hostname  
{ }

// Cycle IDs  
{ }  
{ }

Of course, we’re open to suggestions. If you have a great use case for injected variables in your stack file, head over to our public Slack channel and shoot us a message. We’d be happy to hear from you!

With the updates to the stack file, the default Magento image was easily deployed onto Cycle without any modification to the image itself. Go ahead and give it a try!

Magento Cycle.yml file