Automatiser l'infrastructure cloud : du setup manuel au déploiement piloté par le code
Si vous ne pouvez pas recréer votre infrastructure from scratch en moins de 30 minutes, vous avez un point de défaillance unique. Ansible vous permet de décrire exactement à quoi doit ressembler un serveur et de l'appliquer de manière répétée, identique, dans chaque environnement.
Déployer des applications manuellement est source d'erreurs et ne passe pas à l'échelle. L'Infrastructure as Code (IaC) résout ce problème en traitant l'infrastructure de la même manière que le code applicatif — versionné, testé et reproductible. Ce guide présente Cloud-1, un projet IaC complet qui déploie un stack WordPress sur DigitalOcean grâce à l'automatisation Ansible et des conteneurs Docker.
Prérequis
Avant de commencer, assurez-vous d'avoir :
- Un compte DigitalOcean avec un token API (générez-le dans API → Tokens)
- Une clé SSH ajoutée à votre compte DigitalOcean
- Python 3.8+ installé localement
- Un nom de domaine avec les serveurs de noms pointant vers DigitalOcean
- Un certificat SSL pour votre domaine (ou utilisez Let's Encrypt)
Installez la collection Ansible requise :
# Créer un environnement virtuel et installer les dépendances
python3 -m venv venv
source venv/bin/activate
pip install ansible
# Installer la collection DigitalOcean
ansible-galaxy collection install community.digitalocean
Qu'est-ce que l'Infrastructure as Code ?
L'Infrastructure as Code (IaC) est la pratique de gérer et de provisionner l'infrastructure via des fichiers de configuration lisibles par machine plutôt que par des processus manuels. Au lieu de cliquer dans les consoles cloud, vous définissez votre infrastructure en code.
| Processus Manuel | Infrastructure as Code |
|---|---|
| Naviguer dans l'interface console cloud | Définir les ressources dans des fichiers YAML |
| Aucun enregistrement des modifications | Versionné dans Git |
| Difficile de reproduire les environnements | Environnements identiques à chaque fois |
| Sujet aux erreurs humaines | Automatisé et cohérent |
Fondamentaux d'Ansible
Ansible est un outil d'automatisation sans agent qui utilise SSH pour se connecter aux machines distantes et exécuter des tâches. Contrairement à Chef ou Puppet, aucun daemon ne tourne sur les serveurs cibles — Ansible pousse les configurations depuis un nœud de contrôle.
Comment Fonctionne Ansible
MODÈLE D'EXÉCUTION ANSIBLE ┌─────────────────┐ ┌─────────────────┐ │ NŒUD CONTRÔLE │ │ NŒUD GÉRÉ │ │ (Votre Machine)│ │ (Serveur Distant)│ │ │ │ │ │ ┌───────────┐ │ SSH + SFTP │ ┌───────────┐ │ │ │ Playbook │──┼───────────────────▶│ │ Python │ │ │ │ (YAML) │ │ │ │ Modules │ │ │ └───────────┘ │ │ └───────────┘ │ │ │ │ │ │ │ │ ▼ │ │ ▼ │ │ ┌───────────┐ │ Exécuter Tâches │ ┌───────────┐ │ │ │ Inventory │ │◀──────────────────▶│ │ État │ │ │ │ (Hôtes) │ │ Retourner Résult │ │ Système │ │ │ └───────────┘ │ │ └───────────┘ │ └─────────────────┘ └─────────────────┘
Concepts Clés
Inventory : Une liste de nœuds gérés (serveurs). Peut être statique (fichier INI/YAML) ou dynamique (généré par API).
Playbook : Fichiers YAML qui définissent un ensemble de tâches à exécuter sur les hôtes. Les playbooks sont lisibles par l'humain et décrivent l'état souhaité.
Roles : Unités réutilisables d'organisation. Un rôle encapsule des tâches, des handlers, des fichiers, des templates et des variables pour un but spécifique (ex. : "installer nginx").
Idempotence : Exécuter le même playbook plusieurs fois produit le même résultat. Si nginx est déjà installé, Ansible ne le réinstallera pas — il vérifie d'abord l'état actuel.
# Exemple de tâche idempotente
- name: Install nginx
apt:
name: nginx
state: present # Installe uniquement si absent
# Non-idempotent (évitez ce schéma)
- name: Install nginx
command: apt-get install nginx # S'exécute à chaque fois
Flux d'Exécution des Tâches
- Analyser : Ansible lit le playbook et l'inventory
- Connecter : Établit une connexion SSH avec chaque hôte
- Transférer : Copie les modules Python vers /tmp distant
- Exécuter : Lance les modules sur l'hôte distant
- Nettoyer : Supprime les fichiers temporaires, retourne les résultats
Fondamentaux SSL/TLS
HTTPS chiffre le trafic entre les clients et les serveurs en utilisant TLS (Transport Layer Security). Un certificat prouve l'identité du serveur, et le chiffrement empêche l'espionnage.
Comment Fonctionne la Poignée de Main TLS
POIGNÉE DE MAIN TLS (Simplifiée)
CLIENT SERVEUR
│ │
│──── 1. ClientHello ─────────────────▶│
│ (chiffrements supportés, version TLS) │
│ │
│◀─── 2. ServerHello ──────────────────│
│ (chiffrement choisi, certificat) │
│ │
│──── 3. Échange de Clés ─────────────▶│
│ (client génère une clé de session)│
│ │
│◀─── 4. Terminé ──────────────────────│
│ (la communication chiffrée commence) │
│ │
│◀═══ 5. Données Chiffrées ═══════════▶│
│ │
Chaîne de Certificats
Les certificats SSL forment une chaîne de confiance :
- CA Racine : Pré-installée dans les navigateurs/OS (ex. : DigiCert, Let's Encrypt)
- CA Intermédiaire : Signée par la CA Racine, signe votre certificat
- Certificat Serveur : Le certificat de votre domaine, signé par la CA Intermédiaire
Schéma de Proxy Inverse
Nginx agit comme un proxy inverse : il termine TLS (gère le chiffrement) et transfère les requêtes non chiffrées aux services backend. Cela centralise la gestion des certificats et décharge la cryptographie des serveurs d'application.
PROXY INVERSE AVEC TERMINAISON TLS
Client Nginx WordPress
│ │ │
│── HTTPS (443) ─────▶│ │
│ (chiffré) │ │
│ │── HTTP (9000) ─────▶│
│ │ (non chiffré) │
│ │ │
│ │◀── Réponse ─────────│
│◀── HTTPS ───────────│ │
│ │ │
└─────── TLS ─────────┘└─── Interne ────────┘
(chiffré) (de confiance)
Architecture de Cloud-1
Cloud-1 déploie un stack WordPress complet en utilisant Ansible pour provisionner l'infrastructure DigitalOcean et Docker Compose pour orchestrer les services conteneurisés.
ARCHITECTURE DE DÉPLOIEMENT CLOUD-1
┌────────────────────────────────────────────────────────────────┐
│ NŒUD DE CONTRÔLE │
│ (Machine Locale) │
│ │
│ ┌───────────┐ ┌───────────┐ ┌────────────────────────┐ │
│ │ Makefile │──▶│ Ansible │──▶│ API DigitalOcean │ │
│ │ (setup) │ │ Playbook │ │ (Droplet + DNS) │ │
│ └───────────┘ └───────────┘ └────────────────────────┘ │
└────────────────────────────────────────────────────────────────┘
│
│ SSH
▼
┌────────────────────────────────────────────────────────────────┐
│ DROPLET DIGITALOCEAN │
│ Ubuntu 20.04 | 1vCPU | 1 Go RAM │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ DOCKER COMPOSE │ │
│ │ │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
│ │ │ nginx │──▶│ wordpress │──▶│ mariadb │ │ │
│ │ │ :443/80 │ │ :9000 │ │ :3306 │ │ │
│ │ │ (SSL/TLS) │ │ (PHP-FPM) │ │ (MySQL) │ │ │
│ │ └───────────┘ └───────────┘ └───────────┘ │ │
│ │ │ │ │ │ │
│ │ └───────────────┼───────────────┘ │ │
│ │ │ │ │
│ │ ┌───────▼───────┐ │ │
│ │ │ my_network │ │ │
│ │ │ (bridge) │ │ │
│ │ └───────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────┘
Structure du Projet
Cloud-1/
├── Makefile # Automatisation de la configuration
├── inventory.ini # Inventory Ansible
├── playbook.yaml # Playbook Ansible principal
└── roles/
├── droplet/ # Provisionnement DigitalOcean
│ └── tasks/main.yml
├── setup/ # Installation Docker
│ └── tasks/main.yml
├── database/ # Conteneur MariaDB
│ └── tasks/main.yml
├── wordpress/ # Conteneur WordPress
│ └── tasks/main.yml
├── webServer/ # Conteneur Nginx
│ └── tasks/main.yml
└── Inception/ # Configuration Docker Compose
└── srcs/
├── docker-compose.yml
├── .env
└── requirements/
├── nginx/
├── wordpress/
└── mariadb/
Playbook Ansible
Le playbook orchestre le déploiement en deux phases : d'abord le provisionnement de l'infrastructure cloud, puis la configuration du serveur avec des services conteneurisés.
# playbook.yaml
- hosts: server
become: true
roles:
- droplet # Créer le droplet DigitalOcean et le DNS
- hosts: created_droplets
become: true
roles:
- setup # Installer Docker et les dépendances
- database # Démarrer le conteneur MariaDB
- wordpress # Démarrer le conteneur WordPress
- webserver # Démarrer le conteneur Nginx
Inventory
# inventory.ini
[server]
localhost ansible_connection=local
Rôle Droplet — Provisionnement Cloud
Le rôle droplet crée une VM DigitalOcean et configure les enregistrements DNS pointant vers l'adresse IP du nouveau serveur.
# roles/droplet/tasks/main.yml
- name: Create a new Droplet
community.digitalocean.digital_ocean_droplet:
state: present
oauth_token: "{{ lookup('ansible.builtin.env', 'DO_API_TOKEN') }}"
name: mydroplet1
unique_name: true
size: s-1vcpu-1gb
region: sfo3
image: ubuntu-20-04-x64
wait_timeout: 500
ssh_keys:
- '29:75:f5:6a:07:49:94:28:e3:ed:a7:60:61:0c:9f:ea'
register: my_droplet
- name: Show Droplet info
ansible.builtin.debug:
msg: |
Droplet ID is {{ my_droplet.data.droplet.id }}
Public IPv4 is {{ (my_droplet.data.droplet.networks.v4 | selectattr('type', 'equalto', 'public')).0.ip_address }}
- name: Add Droplet to Ansible inventory
add_host:
name: "{{ (my_droplet.data.droplet.networks.v4 | selectattr('type', 'equalto', 'public')).0.ip_address }}"
groups: created_droplets
ansible_user: root
ansible_ssh_private_key_file: "~/.ssh/id_rsa"
- name: Add domain to DigitalOcean
community.digitalocean.digital_ocean_domain:
state: present
name: yourdomain.com
oauth_token: "{{ lookup('ansible.builtin.env', 'DO_API_TOKEN') }}"
- name: Add A record for root domain
community.digitalocean.digital_ocean_domain_record:
state: present
domain: yourdomain.com
type: A
name: "@"
data: "{{ (my_droplet.data.droplet.networks.v4 | selectattr('type', 'equalto', 'public')).0.ip_address }}"
oauth_token: "{{ lookup('ansible.builtin.env', 'DO_API_TOKEN') }}"
- name: Add A record for www subdomain
community.digitalocean.digital_ocean_domain_record:
state: present
domain: yourdomain.com
type: A
name: "www"
data: "{{ (my_droplet.data.droplet.networks.v4 | selectattr('type', 'equalto', 'public')).0.ip_address }}"
oauth_token: "{{ lookup('ansible.builtin.env', 'DO_API_TOKEN') }}"
Rôle Setup — Installation Docker
Le rôle setup installe Docker, Docker Compose et copie la configuration des conteneurs sur le serveur distant.
# roles/setup/tasks/main.yml
- name: Wait for apt locks
shell: while fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 1; done
changed_when: false
- name: Install required system packages
apt:
pkg:
- apt-transport-https
- ca-certificates
- curl
- software-properties-common
- python3-pip
- virtualenv
- python3-setuptools
state: latest
update_cache: true
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker repository
apt_repository:
repo: deb https://download.docker.com/linux/ubuntu focal stable
state: present
- name: Install Docker-ce
apt:
name: docker-ce
state: latest
update_cache: yes
- name: Install Docker Module for Python
apt:
name: python3-docker
state: present
- name: Install Docker Compose plugin
apt:
name: docker-compose-plugin
state: present
- name: Copy directory to remote host
copy:
src: ../Inception
dest: /
- name: Create Volume Directories
file:
path: /var/data/{{ item }}
state: directory
mode: '0755'
loop:
- "database_volume"
- "wordpress_volume"
Docker Compose — Orchestration des Conteneurs
Docker Compose définit trois services qui travaillent ensemble : nginx (proxy inverse), wordpress (application PHP) et mariadb (base de données).
# docker-compose.yml
version: '3.7'
networks:
my_network:
driver: bridge
volumes:
wordpress_volume:
driver: local
driver_opts:
type: none
device: /var/data/wordpress_volume
o: bind
database_volume:
driver: local
driver_opts:
type: none
device: /var/data/database_volume
o: bind
services:
nginx:
build: ./requirements/nginx
image: nginx
container_name: nginx
restart: always
depends_on:
- wordpress
env_file:
- .env
networks:
- my_network
ports:
- "443:443"
- "80:80"
volumes:
- wordpress_volume:/var/www/html
mariadb:
build: ./requirements/mariadb
image: mariadb
container_name: mariadb
restart: on-failure
env_file:
- .env
networks:
- my_network
ports:
- "3306:3306"
volumes:
- database_volume:/var/lib/mysql
wordpress:
build: ./requirements/wordpress
image: wordpress
container_name: wordpress
restart: on-failure
depends_on:
- mariadb
env_file:
- .env
networks:
- my_network
ports:
- "9000:9000"
volumes:
- wordpress_volume:/var/www/html
Dockerfiles des Conteneurs
Nginx — Proxy Inverse avec SSL
# requirements/nginx/Dockerfile
FROM debian:buster
RUN apt-get -y update && apt-get -y install nginx dumb-init; \
apt-get install -y certbot python3-certbot-nginx;
COPY ./conf/default ./etc/nginx/sites-available/
COPY ./cert/www_yourdomain_com.crt ./etc/ssl/certs/
COPY ./cert/yourdomain.com.key ./etc/ssl/certs/
EXPOSE 443
ENTRYPOINT ["/usr/bin/dumb-init", "/usr/sbin/nginx"]
CMD ["-g", "daemon off;"]
Configuration Nginx
# requirements/nginx/conf/default
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
root /var/www/html/;
index index.php index.html index.htm;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/ssl/certs/www_yourdomain_com.crt;
ssl_certificate_key /etc/ssl/certs/yourdomain.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_pass wordpress:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
}
}
WordPress — Application PHP-FPM
# requirements/wordpress/Dockerfile
FROM debian:buster
RUN apt-get -y update && apt-get -y install curl dumb-init; \
apt-get install -y php7.3-fpm php7.3-mysql; \
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar; \
chmod +x wp-cli.phar; \
mv wp-cli.phar /usr/local/bin/wp;
EXPOSE 9000
COPY ./tools/script.sh ./
COPY ./conf/www.conf ./etc/php/7.3/fpm/pool.d/
RUN chmod +x script.sh
ENTRYPOINT ["/usr/bin/dumb-init"]
CMD ["./script.sh"]
Script de Configuration WordPress
# requirements/wordpress/tools/script.sh
#!/bin/bash
cd /var/www/html
# Télécharger les fichiers WordPress
wp core download --path=/var/www/html --allow-root
# Créer wp-config.php
cat > wp-config.php << EOF
<?php
define('DB_NAME', '$DB_NAME');
define('DB_USER', '$DB_USER_NAME');
define('DB_PASSWORD', '$DB_USER_PASS');
define('DB_HOST', '$HOST');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
\$table_prefix = 'wp_';
define('WP_DEBUG', false);
require_once ABSPATH . 'wp-settings.php';
EOF
chown www-data:www-data wp-config.php
chmod 644 wp-config.php
service php7.3-fpm start
# Installer WordPress
wp core install --url=$DOMAIN_NAME \
--title="$WP_TITLE" \
--admin_name=$WP_ADMIN_USER \
--admin_password=$WP_ADMIN_PASS \
--admin_email=$WP_ADMIN_EMAIL \
--path=/var/www/html/ \
--allow-root
# Créer un utilisateur supplémentaire (optionnel)
wp user create $WP_USER $WP_USER_EMAIL \
--role=author --user_pass=$WP_USER_PASS --allow-root --path=/var/www/html/
service php7.3-fpm stop
chown -R www-data:www-data /var/www/html
# Démarrer PHP-FPM en avant-plan
php-fpm7.3 -F
MariaDB — Serveur de Base de Données
# requirements/mariadb/Dockerfile
FROM debian:buster
RUN apt-get -y update && apt-get -y install mariadb-server dumb-init
EXPOSE 3306
COPY ./conf/50-server.cnf ./etc/mysql/mariadb.conf.d/50-server.cnf
COPY ./tools /
RUN chmod +x /script.sh
ENTRYPOINT ["/usr/bin/dumb-init"]
CMD ["./script.sh"]
Script de Configuration MariaDB
# requirements/mariadb/tools/script.sh
#!/bin/bash
service mysql start
sleep 1
mysql -u root --password="$ROOT_PASSWORD" << EOF
CREATE DATABASE IF NOT EXISTS $DB_NAME;
CREATE USER '$DB_USER_NAME'@'%' IDENTIFIED BY '$DB_USER_PASS';
GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER_NAME'@'%';
ALTER USER 'root'@'localhost' IDENTIFIED BY '$ROOT_PASSWORD';
FLUSH PRIVILEGES;
EOF
kill `cat /var/run/mysqld/mysqld.pid`
sleep 1
# Démarrer MySQL en avant-plan
mysqld
Makefile — Automatisation du Déploiement
Le Makefile automatise l'ensemble du processus de déploiement : copie des certificats, configuration d'un environnement Python et exécution d'Ansible.
# Makefile
HOME_DIR = $(HOME)
CERT_DIR = roles/Inception/srcs/requirements/nginx/cert
KEY_NAME = yourdomain.com.key
CERT_NAME = www_yourdomain_com.crt
ENV_FILE = env
all: setup
setup: copy-files run-commands
copy-files:
mkdir -p $(CERT_DIR)
cp $(HOME_DIR)/certs/$(KEY_NAME) $(CERT_DIR)/$(KEY_NAME)
cp $(HOME_DIR)/certs/$(CERT_NAME) $(CERT_DIR)/$(CERT_NAME)
cp $(HOME_DIR)/certs/$(ENV_FILE) roles/Inception/srcs/.$(ENV_FILE)
DO_API_TOKEN := $(shell grep DO_API_TOKEN roles/Inception/srcs/.env | cut -d '=' -f2)
run-commands:
python3 -m venv venv && \
. venv/bin/activate && \
pip install ansible && \
export DO_API_TOKEN=$(DO_API_TOKEN) && \
ansible-playbook playbook.yaml -i inventory.ini
Rôles de Démarrage des Services
Après la configuration, Ansible démarre chaque conteneur dans l'ordre des dépendances en utilisant Docker Compose.
# roles/database/tasks/main.yml
- name: Start MariaDB using Docker Compose
command: docker-compose -f ../Inception/srcs/docker-compose.yml up -d mariadb
# roles/wordpress/tasks/main.yml
- name: Start WordPress using Docker Compose
command: docker-compose -f ../Inception/srcs/docker-compose.yml up -d wordpress
# roles/webServer/tasks/main.yml
- name: Start Nginx using Docker Compose
command: docker-compose -f ../Inception/srcs/docker-compose.yml up -d nginx
Flux de Déploiement
SÉQUENCE DE DÉPLOIEMENT
make
│
├──▶ copy-files
│ • Copier les certificats SSL
│ • Copier le fichier .env
│
└──▶ run-commands
│
├──▶ Créer un venv Python
├──▶ Installer Ansible
└──▶ ansible-playbook
│
├──▶ rôle droplet (localhost)
│ • Créer le droplet DigitalOcean
│ • Configurer les enregistrements DNS
│ • Ajouter à l'inventory dynamique
│
└──▶ rôles de configuration (distant)
│
├──▶ setup : Installer Docker
├──▶ database : Démarrer MariaDB
├──▶ wordpress : Démarrer WordPress
└──▶ webserver : Démarrer Nginx
Variables d'Environnement
Le fichier .env contient la configuration de tous les services :
# .env
DO_API_TOKEN=your_digitalocean_api_token
DB_NAME=wordpress
DB_USER_NAME=wp_user
DB_USER_PASS=secure_password
ROOT_PASSWORD=root_secure_password
HOST=mariadb
DOMAIN_NAME=yourdomain.com
WP_TITLE=My WordPress Site
WP_ADMIN_USER=admin
WP_ADMIN_PASS=your_admin_password
WP_ADMIN_EMAIL=admin@yourdomain.com
WP_USER=editor
WP_USER_EMAIL=editor@yourdomain.com
WP_USER_PASS=your_user_password
Considérations de Sécurité
Les déploiements en production nécessitent un renforcement supplémentaire :
Pourquoi dumb-init ?
Les conteneurs utilisent dumb-init comme PID 1 pour gérer correctement les signaux et récupérer les processus zombies. Sans lui, votre application ne recevra pas correctement SIGTERM lors de l'arrêt du conteneur.
Configuration du Pare-feu
# Activer UFW et autoriser uniquement les ports nécessaires
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp # SSH
ufw allow 80/tcp # HTTP (redirection vers HTTPS)
ufw allow 443/tcp # HTTPS
ufw enable
Recommandations Supplémentaires
- Renforcement SSH : Désactiver l'authentification par mot de passe, utiliser uniquement l'authentification par clé
- Fail2ban : Protéger contre les attaques par force brute
- Mises à jour régulières : Maintenir les images de base et les paquets à jour
- Gestion des secrets : Envisager d'utiliser Ansible Vault pour les données sensibles
- Accès à la base de données : Le port MariaDB 3306 ne doit pas être exposé publiquement
Dépannage
| Problème | Solution |
|---|---|
| Connexion SSH refusée | Attendre 1-2 minutes que le droplet s'initialise complètement |
| Erreurs de verrou apt | Le rôle setup attend automatiquement les verrous |
| WordPress ne peut pas se connecter à la BD | Vérifier que le conteneur MariaDB est en cours d'exécution : docker logs mariadb |
| Erreurs de certificat SSL | Vérifier les chemins et permissions des certificats dans le conteneur nginx |
| 502 Bad Gateway | Vérifier si PHP-FPM est en cours d'exécution : docker logs wordpress |
Conclusion
Cloud-1 démontre un flux de travail Infrastructure as Code complet : du provisionnement des ressources cloud au déploiement des applications conteneurisées. La combinaison d'Ansible pour l'orchestration et de Docker Compose pour la gestion des conteneurs crée un pipeline de déploiement reproductible et versionné.
Points clés à retenir :
- Déploiement en deux phases : Provisionner l'infrastructure d'abord, puis la configurer
- Séparation des rôles : Chaque rôle Ansible gère une préoccupation (droplet, setup, database, etc.)
- Orchestration des conteneurs : Docker Compose gère les dépendances et le réseau entre services
- SSL/TLS : Nginx termine HTTPS et proxie vers PHP-FPM
- Isolation de l'environnement : Le réseau bridge maintient les conteneurs sécurisés
- Automatisation : Le Makefile fournit un déploiement en une seule commande
Explorer le Code
Consultez le dépôt Cloud-1 sur GitHub pour voir l'implémentation complète.
Écrit par

Tech Lead et Ingénieur Full Stack pilotant une équipe de 5 ingénieurs chez Fygurs (Paris, Remote) sur un SaaS cloud-native Azure. Diplômé de 1337 Coding School (42 Network / UM6P). Écrit sur l'architecture, l'infrastructure cloud et le leadership technique.