• Home
  • Blog
  • Configuration as Code: Everything Developers Need to Know

Configuration as Code: Everything Developers Need to Know

Configuration as code (CaC), a practice that involves setting up operating systems and software through configuration files, has quickly become an essential concept for software developers and DevOps teams. The key reason for this is that CaC integrates seamlessly with CI/CD and version control pipelines, a game-changing benefit discussed in this article. What’s more, this article will also delve into practical examples demonstrating its application in Kubernetes and offer you a hands-on understanding of CaC.

Overview of Configuration as Code

Primarily, CaC promotes consistency across environments since every team member will use the same definition file stored in a dedicated Git repository. This helps eliminate manual errors and enhances reliability. Moreover, using CI/CD pipelines fosters automation, thus accelerating OS and app configuration. It also enhances transparency and accountability by versioning every change, allowing teams to track progress and identify issues promptly.

CaC relies on a practice known as idempotency to ensure that the configuration won’t change beyond the desired state. This attribute enhances reliability, as operations can be repeated without adverse effects.

Last but not least, it’s necessary to talk about two key concepts used by CaC: the declarative and imperative models.

The declarative model defines the desired state of the system, leaving the tool to figure out how to achieve it. In contrast, the imperative model provides explicit instructions on what actions to take. Both models have their merits, and the choice often depends on the specific use case.

Tools for Configuration as Code

Finding a CaC tool that will work for your organization’s unique needs can be challenging since there are so many options available. This article focuses on three of the most popular choices—AnsibleTerraform, and Puppet—which cover most use cases.

Ansible

Ansible is a robust command line tool written in Python that stands out for its simplicity and ease of use.

Ansible is ideal for tasks like installing and configuring apps, updating and configuring OS packages, and advanced workflows that involve installing the necessary dependencies to set up certain packages or programs.

  • Pros: Ansible is fast, simple to use, and works on major Linux distributions, macOS, FreeBSD, and Microsoft Windows.
  • Cons: Ansible may not scale as effectively as other tools (like Terraform) in larger, more complex environments. This is mainly because Ansible uses the imperative model, where you must give explicit instructions on how to configure the application or OS.

To be fair, Ansible’s imperative approach can also be an advantage. Sometimes, using a procedural programming approach makes it easier to understand where the configuration problems are when compared to the declarative model used by Terraform, where the tool is in charge of finding the optimal solution.

Terraform

Terraform is a tool designed from the ground up to provision and manage infrastructure safely and efficiently. In other words, Terraform is better known as an infrastructure as code (IaC) tool. This is not to say that you cannot use Terraform to deploy and monitor applications.

Terraform is ideal for managing large infrastructures using a declarative model. Specifically, from a CaC perspective, Terraform is an excellent choice if your team needs to use an immutable configuration approach, where to make a configuration change or update to an app or package, it must first be destroyed and then reinstalled from scratch.

  • Pros: As discussed, Terraform uses a declarative model, meaning that your team just needs to define what the infrastructure or configuration should look like, and the tool does the rest. This reduces the risk of human error.
  • Cons: Terraform is a tool that shines in IaC, but when it comes to CaC, it presents some limitations that ironically have to do with its declarative model. For instance, when configuring complex applications with many dependencies that require version-specific packages, it’s best to have the flexibility of Ansible, which lets your team decide how to get to the desired result.

Puppet

Lastly, Puppet is a pioneer in the CaC space, offering a declarative-driven approach to managing configuration.

Like Terraform, Puppet excels at enforcing desired states, making it ideal for use cases where developers need to spend less time creating detailed instructions on what actions to take to configure an application.

  • Pros: According to its developers, Puppet is “built for complexity, scale, and long-term deployment.” Put another way, Puppet is excellent for maintaining the desired state of a complex infrastructure made up of different operating systems, each with different package managers and configuration features.
  • Cons: Puppet’s agent-server design might be less flexible compared to modular tools like Terraform or Ansible.

All in all, each tool offers distinct benefits and challenges. However, it would be a mistake to see these tools as adversaries when the reality is that each tool complements the others. So, you can use Terraform with Ansible or Puppet to take advantage of each of their strengths.

Configuration as Code in Kubernetes

Before delving into how to use Ansible, Terraform, and Puppet on Kubernetes, it’s worth taking a step back and reviewing some code examples that show why adding CaC to your Kubernetes toolbox makes so much sense.

Kubernetes Declarative Configuration

The fundamental appeal of Kubernetes lies in its support for declarative configuration. Instead of scripting the steps to achieve a desired state, one simply declares the desired end state, and Kubernetes takes care of the rest. This approach aligns perfectly with the CaC paradigm, where configuration files can be used to describe the desired state of the system.

Consider this simple example using a YAML file to define a deployment in Kubernetes:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.0
        ports:
        - containerPort: 8080

The above code snippet declares a deployment named my-nginx-deployment. This deployment ensures that two instances of the nginx pod are running. If a pod goes down, the deployment will ensure it’s replaced. This is an illustration of the declarative model at work. Let’s review another use case, this time for ConfigMaps and secrets.

ConfigMaps and Secrets

As with deployments, the ConfigMaps and secrets are defined declaratively, which means that you could also use CaC tools to configure sensitive information in Kubernetes.

ConfigMaps allow you to decouple configuration specifics from pod specs, thus making your applications more portable. Here’s an example of a ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: my-game
data:
  game.properties: |
    enemies=humans
    lives=5

The ConfigMap named my-game contains two properties: enemies and lives. Pods can consume ConfigMaps as environment variables or as configuration files through a volume.

Secrets, on the other hand, provide a more secure way to store and manage sensitive information, such as passwords, OAuth tokens, and SSH keys. Here’s an example of a secret:

apiVersion: v1
kind: Secret
metadata:
  name: my-secret-password
type: Opaque
data:
  password: U3VwZXJTZWN1cmVQYXNzd29yZA== # Base64 equivalent of "SuperSecurePassword"

The secret named my-secret-password contains one data item (password). The value is Base64-encoded, which is the requirement for all secrets.

As you can see, each of these Kubernetes resources is managed in YAML configuration files, which makes it very easy to use CaC tools.

CaC Tools in Kubernetes: Ansible, Terraform, and Puppet

Implementing CaC in Kubernetes can be achieved using a variety of tools. However, for the purposes of this guide, let’s focus on the three discussed: Ansible, Terraform, and Puppet.

Since Ansible is primarily a configuration management tool, it’s an excellent choice for handling Kubernetes objects. It allows for an imperative style of handling configurations while maintaining idempotency. In this sense, Ansible’s Kubernetes module enables users to manage Kubernetes resources just like any other Ansible task.

For its part, Terraform supports a wide range of service providers, including Kubernetes. With its declarative coding style, Terraform scripts define what the Kubernetes object should look like, thereby aligning perfectly with the Kubernetes philosophy.

As mentioned, Puppet is excellent at enforcing the desired state of your configuration. Puppet’s Kubernetes module allows you to manage Kubernetes resources using Puppet’s declarative language and report any drift from the desired state.

As such, choosing among Ansible, Terraform, and Puppet depends significantly on your specific use case, environment, and the degree of control you wish to retain over your configurations.

Ansible

You can use a basic Ansible playbook to create a Kubernetes deployment using Ansible’s Kubernetes module.

This section demonstrates how to do this with an example that assumes you have a local Kubernetes cluster up and running and have installed and configured Ansible on your local machine. In the example, the Kubernetes config file is named config and located in the ~/.kube/ directory:

---
...
- hosts: localhost
  gather_facts: no
  tasks:
  - name: Create a namespace
    k8s:
      kubeconfig: ~/.kube/config
      state: present
      definition:
        apiVersion: v1
        kind: Namespace
        metadata:
          name: nginx-namespace
 
  - name: Create a deployment
    k8s:
      kubeconfig: ~/.kube/config
      state: present
      definition:
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: my-nginx-deployment
          namespace: nginx-namespace
          labels:
            app: nginx
        spec:
          replicas: 2
          selector:
            matchLabels:
              app: nginx
          template:
            metadata:
              labels:
                app: nginx
            spec:
              containers:
              - name: nginx
                image: nginx:1.14.0
                ports:
                - containerPort: 8080
...

This playbook consists of two tasks. The first task creates a namespace called nginx-namespace in your Kubernetes cluster. The second task creates a deployment called my-nginx-deployment in the nginx-namespace namespace. The deployment runs two replicas of the nginx:1.14.0 image. Once you have this playbook, you can run it with the command ansible-playbook my-playbook.yml.

Terraform

Just like with Ansible, you can use Terraform to create and manage Kubernetes resources, such as deployments, services, ConfigMaps, and secrets.

To begin, you need to install Terraform on your local system and have a Kubernetes cluster up and running where you can apply your configurations.

Below is a simple Terraform configuration that creates an Nginx implementation similar to the one above:

provider "kubernetes" {
  config_path = "~/.kube/config"
}

resource "kubernetes_deployment" "my-nginx-deployment" {
  metadata {
    name = "my-nginx-deployment"
    labels = {
      App = "nginx"
    }
  }

  spec {
    replicas = 2

    selector {
      match_labels = {
        App = "nginx"
      }
    }

    template {
      metadata {
        labels = {
          App = "nginx"
        }
      }

      spec {
        container {
          image = "nginx:1.14.0"
          name  = "nginx"

          port {
            container_port = 8080
          }
        }
      }
    }
  }
}

In this Terraform script:

  • You first declare the Kubernetes provider and specify the location of the kubeconfig file.
  • Then, you define a resource of type kubernetes_deployment named my-nginx-deployment. This resource represents a Kubernetes deployment.
  • The metadata block specifies the name of the deployment and its labels.
  • The spec block defines the specifications of the deployment, such as the number of replicas, the selector, and the template for the pods.
  • The container block specifies the container image to use, the name of the container, and the port it exposes.

To apply this configuration, save the file with a .tf extension and run the following commands in the terminal:

terraform init
terraform apply

As you can see, the procedure is very similar to the one used in Ansible.

Puppet

The steps to follow in Puppet are very similar to those explained in the previous examples.

You first need to install the required Kubernetes module on Puppet’s master node. Optionally, you can install Puppet’s Helm module if you intend to configure your applications and services using Helm charts.

Next, create a manifest file named ngix_deployment.pp to describe the Kubernetes deployment. Below is an extract of it:

...
kubernetes_pod { 'my-nginx-deployment':
  ensure   => present,
  metadata => {
    namespace => 'nginx-namespace',
  },
  spec     => {
    containers => [{
      name  => 'nginx',
      image => 'nginx:1.14.0',
    }]
  },
}
...

This Puppet code applies the my-nginx-deployment Nginx deployment in the nginx-namespace as easily as with Ansible and Terraform.

You can use a similar procedure to handle various resources, applications, and services in your Kubernetes cluster using Ansible, Terraform, or Puppet. Moreover, since these tools are not mutually exclusive, you can combine them to perform different tasks. For instance, your organization could leverage the principles of IaC and CaC by using Terraform to deploy your infrastructure and Terraform’s Ansible provider to provision and configure a Kubernetes cluster.

Conclusion

This article explored how the CaC approach brings scalability, repeatability, and reliability to the software development process. It enables teams to manage their infrastructure in an automated and efficient manner, similar to how they handle application code. In this sense, Kubernetes’s declarative approach beautifully complements the CaC paradigm, providing a robust and efficient environment for application deployment and management.

Best practices for implementing CaC include maintaining a version-controlled repository of configuration files, automating configurations, and ensuring seamless integration with the existing DevOps pipeline. These best practices ensure a consistent and predictable Kubernetes environment, enabling developers to focus more on application development than on configuration management.

Overall, the power of CaC lies in its ability to simplify complex tasks, promote collaboration, and increase operational efficiency. It is the cornerstone of modern, agile, and robust environments. Therefore, understanding and employing it correctly within your Kubernetes ecosystem is a necessity and a business advantage. If you need help during this process, feel free to join the Komodor Kommunity on Slack, where you will get resources and support every step of the way.

How useful was this post?

Click on a star to rate it!

Average rating 5 / 5. Vote count: 1

No votes so far! Be the first to rate this post.