A split-level junction photographed in the night

Keep order and save time - Infrastructure as Code

At Droptica, a very important role is played by the servers, where we host websites and applications developed by us. By implementing Infrastructure as Code in our company, we have managed to increase the stability and availability of services and optimise the time necessary to implement changes in the infrastructure. Automatic server management has greatly simplified several of our processes.

Infrastructure as Code - what is it all about?

Until recently, most companies operating in the IT sector and providing services related to the Web had to have their own servers, which required a lot of preparation – setting them up in data centres, configuring the operating system, network, setting up security mechanisms and preparing for the provision of services.

However, this trend has been changing for some years now. We are all currently witnessing the Cloud revolution – solutions, where powerful players in the data processing market offer their unused resources, most often in the form of virtual machines that can be provisioned in just a few clicks. In such case, you don’t have to deal with the whole process of preparing a hardware-based environment, which in itself is a great way to reduce expenses. However, the biggest advantage of this solution is quick and easy provisioning, which can be additionally automated by means of APIs provided by the suppliers.

This is where the concept of Infrastructure as Code appears – a paradigm, the aim of which is to define the entire environment in the form of code, which, if subjected to an appropriate process, will prepare the described infrastructure. We use such a solution on our Drupal hosting platforms. This helps us focus on Drupal development services rather than on managing servers.

And now, to answer the question “why we decided to implement IaC at Droptica?” Well, there are many reasons, but the most important among them are:

  • storing the server status in the repository – what is in the code reflects the actual state and in addition is clear and understandable, not only to administrators;
  • possibility to return to any moment in history – if the code is stored in the repository, then we know the entire history of changes and we can easily switch between them;
  • such representation is easily validated and tested, and the technologies we use provide us with such mechanisms;
  • deployment automation, which further accelerates the preparation and provision of servers in the production environment.

How do I get down to it?

Implementation of Infrastructure as Code in our case has been reduced to two steps – finding the software that would meet our assumptions and prepare the code that reflects the environment.

In the case of the first step we defined two basic conditions:

  • support for multiple Cloud platforms – at Droptica, we are not limited to one specific solution. We constantly test new products on the market to offer our clients more and more interesting and stable solutions (currently we use services offered by AWS, Digital Ocean and Linode, among others).
  • Immutable infrastructure – our goal is to store the actual state of the environment in a repository so that it can be retrieved and restored at any time. We cannot, therefore, have a situation where there were changes made to the state of the infrastructure, which has not previously been included in the code – which is a practical definition of the immutable infrastructure paradigm. 

Tool selection

There are many tools available on the market that allow you to implement Infrastructure as Code. In our deliberations, we have taken into account a number of them, including Ansible, Cloud Formation and Terraform, which we finally decided to use.

Although the main task of Ansible is not environment orchestration, but managing configurations, the tool has modules that allow it to work with cloud computing services. This solution seems to be very practical. Both the code responsible for the desired shape of the infrastructure and its subsequent configuration can be stored and maintained in a single repository. However, unlike the other two, Ansible uses a procedural code style where we specify the tasks to be performed step by step. In some cases, this may lead to non-intuitive changes in the code, as shown in the example below.
The simplified code in Ansible, which will create 10 virtual machines, looks like this:

- ec2:
    count: 10
    image: ami-v1    
    instance_type: t2.micro

And the same in Terraform:

resource "aws_instance" "example" {
  count = 10
  ami = "ami-v1"
  instance_type = "t2.micro"
}


After executing both snippets, we will achieve the same result – 10 virtual servers will be provisioned in AWS. However, what happens if you decide to increase the number to 15?

In the case of Ansible, the current status is not stored anywhere, so if you increase the number of servers in the code to 15, you will provide exactly that many. In the end, you will have 25 of them, which is why the number of new machines included in the code should be 5:

- ec2:
    count: 5
    image: ami-v1    
    instance_type: t2.micro


Both CloudFormation and Terraform store information about the current state of the infrastructure, so the change is cosmetic and much more intuitive:

resource "aws_instance" "example" {
  count = 15
  ami = "ami-v1"
  instance_type = "t2.micro"
}


For this reason, we have decided against using Ansible. Cloud Formation has been dropped for a much simpler reason – it's a tool dedicated to AWS, so it doesn't meet our first condition – it doesn’t work with many suppliers. 


How does Terraform work?

In this part of the article, we will present a short practical guide, which will allow you to take your first steps in the field of automation and implementation of IaC. We are going to focus on Ubuntu because we use this environment at Droptica on a daily basis.

Installation

Terraform can now be found in the official Ubuntu / Debian repositories, but the developer provides ready-made packages that can be simply unpacked and installed.

wget https://releases.hashicorp.com/terraform/0.11.2/terraform_0.11.2_linux_amd64.zip
unzip terraform_0.11.2_linux_amd64.zip
cp terraform /usr/local/bin

Plug-in installation

Not all cloud providers are supported natively, so you might need to install plug-ins for some of them, for example, Linode and OVH. In most cases, these procedures are well described in the repositories, like in the following two cases:

The compiled plug-ins should be copied into the directory  ~/.terraform.d/plugins

Describing the first server

Using Terraform is really easy! The code shown above is enough to create the first virtual machine in AWS, but let's deal with a slightly more complicated issue.

resource "aws_instance" "server-01" {
  ami               = "ami-0d77397e"
  availability_zone = "eu-west-1a"
  instance_type     = "t2.large"

  root_block_device {
    volume_type = "gp2"
    volume_size = "120"
    delete_on_termination = "true"
  }

  tags {
    Name = "server-01"
    Org  = "droptica"
  }
}


The above example will also lead to the creation of a new virtual server named server-01 in AWS – this is specified by the aws_instance resource type. Then, you have to fill in the necessary parameters (defined in the AWS documentation):

  • ami – image identifier (in our case Ubuntu Server 16.04);
  • availability_zone – region and zone in which the server will be provisioned;
  • Instance_type – the type and size of the instance.

In the root_block_device section, you can specify the parameters of the disk that will be associated with this instance; in this case, we are going for the basic gp2 type with the size of 120 GB.

Running the code

After the necessary preparations, the code is ready to run. However, before implementing the changes in the target environment, it is worth reading the list of steps that will be made. This will allow you to detect possible errors in the code. In order to do it, simply run the command:

$ terraform plan


If everything is right, all you need to do now is to run the code and apply the changes:

> terraform plan
aws_instance.example: Refreshing state... (ID: i-6a7c545b)
(...)
~ aws_instance.example
   tags.%:    "0" => "2"
   tags.Name: "" => "server-01"
   tags.Org: "" => "droptica"
Plan: 1 to add, 0 to change, 0 to destroy.

If everything is right, all you need to do now is to run the code and apply the changes:

terraform apply

What's next?

Machine description itself is only the beginning since cloud computing services are now so extensive that they allow creating a much more complex architecture. At Droptica, we defined, among others, the network (both public and private), servers that host our websites, databases and load balancers – in short, we created a reliable hosting platform based on AWS.


For those who want to get to know all the possibilities of Terraform, I recommend reading the documentation, and if you want to try it out, the official guide will be the ideal starting point for you.

3. Best practices for software development teams