RousselTMLEARNING

DevOps

Infrastructure as Code and GitOps: Terraform, Ansible and Argo CD

Manage infrastructure as code: declarative or imperative, Terraform (providers, state, plan, modules, drift), Ansible for configuration, immutable infrastructure, IaC testing and GitOps with Argo CD and Flux.

1. Why IaC #

Reproducible servers.

Principles #

  • Infrastructure is described in versioned files, reviewed and tested like code.
  • An environment can be recreated identically in minutes (disaster recovery, new test environment).
  • Changes go through pull requests: traceability and review.
  • No more « snowflake » servers configured by hand and impossible to reproduce.
ApproachPrincipleTools
Declarativeyou describe the desired state, the tool computes the actionsTerraform/OpenTofu, Kubernetes, Pulumi, CloudFormation
Imperative / proceduralyou describe the steps to runscripts, Ansible (idempotent modules)
ToolMain use
Terraform / OpenTofuprovisioning: networks, machines, managed databases, DNS, cloud accounts
Ansibleconfiguring: packages, files, services on existing machines
Packerbuilding immutable machine images
Helm / Kustomizedescribing Kubernetes applications

2. Terraform #

Declarative provisioning.

The cycle #

Terraform cycle
terraform plan shows exactly what is going to change.
terraform {
  required_providers { docker = { source = "kreuzwerker/docker", version = "~> 3.0" } }
  backend "s3" { bucket = "tfstate-shop" key = "staging/terraform.tfstate" region = "eu-west-3" }
}
variable "replicas" { type = number, default = 2 }
resource "docker_image" "nginx" { name = "nginx:1.27" }
resource "docker_container" "web" {
  count = var.replicas
  name  = "web-${count.index}"
  image = docker_image.nginx.image_id
  ports { internal = 80, external = 8080 + count.index }
}
output "urls" { value = [for c in docker_container.web : "http://localhost:${c.ports[0].external}"] }
terraform fmt -check && terraform validate
terraform plan -out=plan.tfplan
terraform apply plan.tfplan
terraform state list
terraform plan -detailed-exitcode   # 2 = dérive détectée (utile en tâche planifiée)

State #

Terraform records what it manages in a state file. In a team, it is stored in a remote backend (S3, GCS, Terraform Cloud, GitLab) with locking, to avoid two simultaneous apply runs.

Common mistake: State often contains secrets in plain text: encrypt the backend, restrict access to it and never commit it to Git.

Modules and environments #

  • Factor into versioned modules (network, cluster, database).
  • One directory or workspace per environment, with different variables and the same code.
  • Run plan in the pull request and apply after merging, from CI.
  • Check compliance with tflint, checkov or OPA policies.

3. Configuration and immutability #

Ansible and immutable images.

Ansible #

- name: Serveurs web
  hosts: web
  become: true
  tasks:
    - name: Installer nginx
      ansible.builtin.apt: { name: nginx, state: present, update_cache: true }
    - name: Déployer la configuration
      ansible.builtin.template: { src: site.conf.j2, dest: /etc/nginx/conf.d/site.conf }
      notify: Recharger nginx
  handlers:
    - name: Recharger nginx
      ansible.builtin.service: { name: nginx, state: reloaded }

Ansible modules are idempotent: rerunning the playbook changes nothing if the state is already compliant. --check --diff shows the changes without applying them.

Immutable infrastructure #

Rather than modifying servers in place (inevitable drift), you build a new image (Packer, container image) and replace the instances. It is the natural model of containers and Kubernetes.

4. GitOps #

Git as the source of truth for deployment.

Principles #

GitOps loop
Any manual change to the cluster is detected and reverted.
  1. The desired state of the system is declarative.
  2. It is versioned in Git, immutable and historized.
  3. It is pulled automatically by an agent.
  4. It is continuously reconciled: any drift is corrected.

Argo CD #

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata: { name: shop-staging, namespace: argocd }
spec:
  project: default
  source:
    repoURL: https://github.com/mon-org/shop-deploy.git
    targetRevision: main
    path: overlays/staging
  destination: { server: https://kubernetes.default.svc, namespace: shop }
  syncPolicy:
    automated: { prune: true, selfHeal: true }
    syncOptions: [CreateNamespace=true]
Good practice: Separate the application repository (code, Dockerfile) from the deployment repository (manifests per environment). CI updates the image tag in the deployment repository; a promotion to production is a pull request.

Related articles

Organizing DevOps: value streams, teams and a blameless culture

Set up a DevOps organization: value chain and value stream mapping, Team Topologies, « you build it, you run …

Git for DevOps: branches, code reviews and versions

Organize work with Git in a DevOps approach: trunk-based development or GitFlow, short-lived branches, pull r…

End-to-end CI/CD (1/5): principles and anatomy of a pipeline

Understand end-to-end CI/CD: continuous integration, continuous delivery and continuous deployment, pipeline …

Keep learning

Support us

♥ SUPPORT US

Support RousselTM Learning

Courses, articles, labs and certificates stay free with no intrusive ads. A voluntary contribution, of any amount you choose, pays for hosting, lab environments and new content.

Contribute with PayPal Any amount · card or PayPal accountLearn more
QR code PayPal
Or scan this QR code with your phone

Thank you, every contribution counts!