eadl:bloc4:fm2:td3

TD3 : Stack applicative 3 tiers avec Terraform et Ansible

  • Provisionner une stack front / back / db avec Terraform
  • Configurer chaque service avec Ansible
  • Utiliser les groupes, variables et handlers Ansible
  • Identifier les limites d'idempotence d'un playbook

Vous travaillez dans une equipe DevOps pour une startup.

L'equipe doit deployer un environnement de developpement compose de :

  • un frontend NGINX (page statique)
  • un backend Python Flask
  • une base de donnees PostgreSQL

Probleme actuel :

  • les developpeurs configurent les services a la main
  • les environnements different d'une machine a l'autre
  • le backend demarre avant la base de donnees et plante

Objectif :

Automatiser le deploiement et la configuration de la stack complete.

mkdir td3-stack
cd td3-stack
mkdir terraform ansible ansible/group_vars

Structure attendue :

td3-stack/
  terraform/
    main.tf
    variables.tf
    outputs.tf
  ansible/
    inventory.ini
    playbook.yml
    group_vars/
      frontend.yml
      backend.yml
      database.yml

Fichier : terraform/main.tf

terraform {
  required_providers {
    docker = {
      source = "kreuzwerker/docker"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx" {
  name = "nginx:latest"
}

resource "docker_image" "flask" {
  name = "python:3.11-slim"
}

resource "docker_network" "stack" {
  name = "td3-network"
}

resource "docker_container" "frontend" {
  name  = "frontend"
  image = docker_image.nginx.name

  networks_advanced {
    name = docker_network.stack.name
  }

  ports {
    internal = 80
    external = 8080
  }
}

resource "docker_container" "backend" {
  name  = "backend"
  image = docker_image.flask.name

  networks_advanced {
    name = docker_network.stack.name
  }

  ports {
    internal = 5000
    external = 5000
  }

  command = ["sleep", "infinity"]
}

# A COMPLETER : image et conteneur PostgreSQL
# port interne : 5432, port externe : 5432
# variable d'environnement POSTGRES_PASSWORD = "secret"
# connecte a td3-network

Fichier : terraform/outputs.tf

output "frontend_url" {
  value = "http://localhost:8080"
}

output "backend_url" {
  value = "http://localhost:5000"
}

output "database_host" {
  value = "database"
}

cd terraform
terraform init
terraform apply

Verifier que les trois conteneurs sont en cours d'execution :

docker ps
docker network inspect td3-network

Fichier : ansible/inventory.ini

[frontend]
localhost ansible_connection=local

[backend]
localhost ansible_connection=local

[database]
localhost ansible_connection=local

Fichier : ansible/group_vars/frontend.yml

container_name: frontend
html_content: "<h1>Frontend - TD3</h1>"

Fichier : ansible/group_vars/backend.yml

container_name: backend
flask_app: |
  from flask import Flask
  app = Flask(__name__)

  @app.route('/')
  def index():
      return 'Backend OK'

  if __name__ == '__main__':
      app.run(host='0.0.0.0', port=5000)
packages:
  - flask

Fichier : ansible/group_vars/database.yml

container_name: database
db_user: postgres
db_name: appdb
db_password: secret

Fichier : ansible/playbook.yml

---
- name: Configuration frontend
  hosts: frontend
  gather_facts: false

  tasks:
    - name: Copier la page HTML dans le conteneur
      community.docker.docker_container_exec:
        container: "{{ container_name }}"
        command: >
          bash -c "echo '{{ html_content }}' > /usr/share/nginx/html/index.html"

    - name: Recharger nginx
      community.docker.docker_container_exec:
        container: "{{ container_name }}"
        command: nginx -s reload

- name: Configuration backend
  hosts: backend
  gather_facts: false

  tasks:
    - name: Installer les dependances Python
      community.docker.docker_container_exec:
        container: "{{ container_name }}"
        command: pip install {{ packages | join(' ') }}

    - name: Copier le code Flask
      community.docker.docker_container_exec:
        container: "{{ container_name }}"
        command: >
          bash -c "cat > /app.py << 'EOF'
          {{ flask_app }}
          EOF"

    - name: Demarrer Flask
      community.docker.docker_container_exec:
        container: "{{ container_name }}"
        command: bash -c "nohup python /app.py &"

- name: Configuration base de donnees
  hosts: database
  gather_facts: false

  tasks:
    - name: Attendre que PostgreSQL soit pret
      community.docker.docker_container_exec:
        container: "{{ container_name }}"
        command: pg_isready -U {{ db_user }}
      register: pg_ready
      retries: 10
      delay: 3
      until: pg_ready.rc == 0

    - name: Creer la base de donnees applicative
      community.docker.docker_container_exec:
        container: "{{ container_name }}"
        command: psql -U {{ db_user }} -c "CREATE DATABASE {{ db_name }};"

cd ansible
ansible-playbook -i inventory.ini playbook.yml

Verifier que les services repondent :

curl http://localhost:8080
curl http://localhost:5000

Relancer le playbook sans modifier quoi que ce soit :

ansible-playbook -i inventory.ini playbook.yml

Observer attentivement les erreurs.

Questions :

  • Quelles taches echouent ? Pour quelle raison precise ?
  • Parmi les taches qui reussissent, lesquelles produisent quand meme un effet indesirable ?
  • Qu'est-ce que cela implique si ce playbook est execute automatiquement dans une pipeline CI/CD ?
  • Comment corrigeriez-vous chaque tache concernee ?

Ajouter un reverse proxy devant la stack.

Specifications :

  • image : nginx:latest
  • nom du conteneur : proxy
  • port externe : 80
  • connecte a td3-network
  • rediriger / vers le frontend et /api vers le backend

Contrainte : le playbook Ansible de configuration du proxy doit etre idempotent.

Generer l'inventaire Ansible automatiquement depuis les outputs Terraform.

cd terraform
terraform output -json > ../ansible/tf_outputs.json

Ecrire un script qui lit tf_outputs.json et produit un fichier inventory.ini valide.

Le resultat doit etre identique a l'inventaire ecrit manuellement en section 3.

Question :

  • Dans quel cas cet inventaire genere devient-il indispensable par rapport a un inventaire ecrit a la main ?
  • eadl/bloc4/fm2/td3.txt
  • Dernière modification : il y a 14 heures
  • de jcheron