Disponible · DevOps Engineer

Fred Riche NGATCHOU

$ deploy --env production --scale auto

Ingénieur DevOps basé à Bordeaux. Je conçois et industrialise des pipelines CI/CD, orchestre des infrastructures Kubernetes, et mets en place des stacks d'observabilité complètes pour accélérer la livraison logicielle.

fred@devops:~
kubectl get nodes
NAME         STATUS  ROLES
master-01   Ready   control-plane
worker-01   Ready   worker
worker-02   Ready   worker

terraform apply
Plan: 12 to add, 0 to change
Apply complete! Resources: 12 added

ansible-playbook deploy.yml
PLAY RECAP ****
prod-server : ok=14 changed=3 failed=0

01 // Compétences

Stack Technique

⚙️

CI/CD & Qualité

Jenkins GitLab CI SonarQube
🐳

Conteneurisation

Docker Docker Compose Kubernetes
🤖

Automatisation & IaC

Terraform Ansible Bash Python Vagrant
📊

Observabilité

Prometheus Grafana Loki OpenTelemetry Alloy
☁️

Cloud & Versioning

Azure AWS GitHub GitLab Traefik
🔥

Testing & Performance

JMeter Tests de charge Rapports XML
02 // Infrastructure live

Projets déployés

📊
LIVE

Grafana

↗ grafana.mydevprojects.fr

Dashboards de monitoring en temps réel. Visualisation des métriques système, réseau et applicatives avec alerting configuré.

🦊
LIVE

GitLab

↗ gitlab.mydevprojects.fr

Instance GitLab auto-hébergée avec runners CI/CD configurés, gestion des dépôts et pipelines d'intégration continue.

🔍
LIVE

SonarQube

↗ sonarqube.mydevprojects.fr

Analyse statique de code, détection de vulnérabilités et génération de rapports de couverture de tests au format XML.

🔥
MONITORING

Prometheus

↗ prometheus.mydevprojects.fr

Collecte et stockage de métriques time-series. Alertmanager configuré et scraping automatique des services exposés.

📋
MONITORING

Loki

↗ loki.mydevprojects.fr

Agrégation et indexation de logs distribués. Stack PLG (Promtail, Loki, Grafana) pour la centralisation des journaux.

LIVE

JMeter

↗ jmeter.mydevprojects.fr

Tests de performance et de charge automatisés. Analyse de la scalabilité et génération de rapports de montée en charge.

🔭
MONITORING

OpenTelemetry

↗ opentelemetry.mydevprojects.fr

Collecteur OTEL pour l'observabilité unifiée : traces distribuées, métriques et logs en un seul pipeline d'ingestion.

🌐
LIVE

Traefik

↗ Reverse proxy + SSL

Reverse proxy Traefik v2 pour le routage automatique des services avec gestion automatique des certificats SSL Let's Encrypt.

📦
IaC

Vagrant + Ansible

↗ Infrastructure as Code

Provisioning de 4 VM isolées (BDD, Front, Back, Admin) avec Vagrant, puis déploiement automatisé des applications via playbooks Ansible.

03 // Infrastructure as Code

Extraits de Playbooks

Provisioning VM
Installation Apps
Docker / K8s
Conteneurisation
Observabilité
CI/CD
Cloud & Versioning
Vagrantfile — 4 VM (BDD, Front, Back, Admin)
# -*- mode: ruby -*- # Vagrantfile — Provisioning 4 VM isolées Vagrant.configure("2") do |config| ## VM Base commune config.vm.box = "ubuntu/jammy64" ## VM BDD — PostgreSQL config.vm.define "db" do |db| db.vm.hostname = "vm-db" db.vm.network "private_network", ip: "192.168.56.10" db.vm.provider "virtualbox" do |v| v.memory = 1024 v.cpus = 1 end db.vm.provision "ansible", playbook: "ansible/db.yml" end ## VM Front — Nginx + React config.vm.define "front" do |front| front.vm.hostname = "vm-front" front.vm.network "private_network", ip: "192.168.56.11" front.vm.provider "virtualbox" do |v| v.memory = 1024 v.cpus = 1 end front.vm.provision "ansible", playbook: "ansible/front.yml" end ## VM Back — Node.js / Spring Boot config.vm.define "back" do |back| back.vm.hostname = "vm-back" back.vm.network "private_network", ip: "192.168.56.12" back.vm.provider "virtualbox" do |v| v.memory = 1024 v.cpus = 1 end back.vm.provision "ansible", playbook: "ansible/back.yml" end ## VM Admin — Outils d'administration config.vm.define "admin" do |admin| admin.vm.hostname = "vm-admin" admin.vm.network "private_network", ip: "192.168.56.13" admin.vm.provider "virtualbox" do |v| v.memory = 2048 v.cpus = 2 end admin.vm.provision "ansible", playbook: "ansible/admin.yml" end end
ansible/site.yml — Installation des applications
--- # Playbook principal — Installation des applications sur chaque VM - name: Provisionner la VM Base de données hosts: db become: true roles: - common - postgresql vars: pg_version: "15" pg_db_name: "appdb" pg_user: "appuser" - name: Provisionner la VM Front-end hosts: front become: true roles: - common - nodejs - nginx vars: node_version: "20" app_port: 3000 - name: Provisionner la VM Back-end hosts: back become: true roles: - common - java - spring-boot vars: java_version: "17" app_jar: "app.jar" app_port: 8080 - name: Provisionner la VM Admin hosts: admin become: true roles: - common - docker - monitoring tasks: - name: Installer les outils d'administration ansible.builtin.package: name: ["htop", "curl", "vim", "net-tools"] state: present
ansible/roles/docker/tasks/main.yml — Déploiement Docker & K8s
--- # Role Ansible — Installation Docker + déploiement des services - name: Installer les dépendances Docker ansible.builtin.apt: name: - ca-certificates - curl - gnupg state: present update_cache: true - name: Ajouter la clé GPG Docker ansible.builtin.apt_key: url: https://download.docker.com/linux/ubuntu/gpg state: present - name: Installer Docker Engine ansible.builtin.apt: name: - docker-ce - docker-ce-cli - containerd.io - docker-compose-plugin state: present - name: Démarrer et activer Docker ansible.builtin.service: name: docker state: started enabled: true - name: Ajouter l'utilisateur au groupe docker ansible.builtin.user: name: "{{ ansible_user }}" groups: docker append: true - name: Copier le fichier docker-compose ansible.builtin.template: src: docker-compose.yml.j2 dest: /opt/app/docker-compose.yml mode: "0644" - name: Déployer les services avec Docker Compose community.docker.docker_compose_v2: project_src: /opt/app state: present pull: true --- # Déploiement sur cluster Kubernetes via kubectl - name: Appliquer les manifests Kubernetes kubernetes.core.k8s: state: present src: "{{ item }}" kubeconfig: /etc/kubernetes/admin.conf loop: - manifests/namespace.yml - manifests/deployment.yml - manifests/service.yml - manifests/ingress.yml
docker-compose.yml — Stack multi-services
# docker-compose.yml — Orchestration multi-services avec Traefik services: traefik: image: traefik:v2.11 command: - "--api.dashboard=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - "--certificatesresolvers.myresolver.acme.tlschallenge=true" - "--certificatesresolvers.myresolver.acme.email=ngatchoufredriche@gmail.com" - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - letsencrypt:/letsencrypt networks: - traefik-net app: image: mon-app:latest restart: unless-stopped environment: DATABASE_URL: postgres://user:pass@db:5432/appdb NODE_ENV: production labels: - "traefik.enable=true" - "traefik.http.routers.app.rule=Host(`app.mydevprojects.fr`)" - "traefik.http.routers.app.entrypoints=websecure" - "traefik.http.routers.app.tls.certresolver=myresolver" depends_on: - db networks: - traefik-net - backend db: image: postgres:15-alpine restart: unless-stopped environment: POSTGRES_DB: appdb POSTGRES_USER: user POSTGRES_PASSWORD: pass volumes: - pgdata:/var/lib/postgresql/data networks: - backend volumes: letsencrypt: pgdata: networks: traefik-net: external: true backend:
docker-compose.observability.yml — PLG Stack + Tempo + OpenTelemetry
# Stack d'observabilité complète : Prometheus · Loki · Grafana · Tempo · Alloy · OTEL services: prometheus: image: prom/prometheus:latest volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml command: - "--config.file=/etc/prometheus/prometheus.yml" - "--storage.tsdb.retention.time=15d" labels: - "traefik.enable=true" - "traefik.http.routers.prometheus.rule=Host(`prometheus.mydevprojects.fr`)" - "traefik.http.routers.prometheus.tls.certresolver=myresolver" loki: image: grafana/loki:latest command: -config.file=/etc/loki/config.yml volumes: - ./loki-config.yml:/etc/loki/config.yml - loki-data:/loki tempo: image: grafana/tempo:latest command: -config.file=/etc/tempo/config.yml volumes: - ./tempo-config.yml:/etc/tempo/config.yml - tempo-data:/var/tempo ports: - "4317:4317" # OTLP gRPC - "4318:4318" # OTLP HTTP alloy: image: grafana/alloy:latest volumes: - ./alloy-config.river:/etc/alloy/config.river - /var/log:/var/log:ro command: run /etc/alloy/config.river labels: - "traefik.enable=true" - "traefik.http.routers.alloy.rule=Host(`alloy.mydevprojects.fr`)" - "traefik.http.routers.alloy.tls.certresolver=myresolver" otel-collector: image: otel/opentelemetry-collector-contrib:latest command: ["--config=/etc/otel/config.yml"] volumes: - ./otel-config.yml:/etc/otel/config.yml ports: - "4319:4317" # OTLP gRPC entrant grafana: image: grafana/grafana:latest environment: GF_AUTH_ANONYMOUS_ENABLED: "false" GF_FEATURE_TOGGLES_ENABLE: traceqlEditor tempoSearch volumes: - ./grafana/provisioning:/etc/grafana/provisioning - grafana-data:/var/lib/grafana labels: - "traefik.enable=true" - "traefik.http.routers.grafana.rule=Host(`grafana.mydevprojects.fr`)" - "traefik.http.routers.grafana.tls.certresolver=myresolver" volumes: loki-data: tempo-data: grafana-data:
.gitlab-ci.yml — Pipeline CI/CD complet
# .gitlab-ci.yml — Build · Test · Quality · Deploy stages: - build - test - quality - deploy variables: IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA DOCKER_TLS_CERTDIR: "" # ── BUILD ────────────────────────────────────────── build-image: stage: build image: docker:24 services: - docker:24-dind script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker build -t $IMAGE_TAG . - docker push $IMAGE_TAG # ── TEST ─────────────────────────────────────────── unit-tests: stage: test image: maven:3.9-eclipse-temurin-17 script: - mvn test -B --no-transfer-progress - mvn jacoco:report artifacts: reports: junit: target/surefire-reports/*.xml paths: - target/site/jacoco/ perf-tests: stage: test image: justb4/jmeter:5.5 script: - jmeter -n -t tests/load-test.jmx -l results.jtl -e -o report/ artifacts: paths: - report/ # ── QUALITY ──────────────────────────────────────── sonarqube-scan: stage: quality image: sonarsource/sonar-scanner-cli:latest script: - sonar-scanner -Dsonar.projectKey=$CI_PROJECT_NAME -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_TOKEN -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml allow_failure: false # ── DEPLOY ───────────────────────────────────────── deploy-prod: stage: deploy image: alpine:latest before_script: - apk add --no-cache openssh-client - eval $(ssh-agent -s) - echo "$SSH_PRIVATE_KEY" | ssh-add - script: - ssh $DEPLOY_USER@$DEPLOY_HOST " docker pull $IMAGE_TAG && docker compose -f /opt/app/docker-compose.yml up -d --no-deps app" only: - main
main.tf — Infrastructure Terraform + .gitconfig
# main.tf — Provisioning infra cloud avec Terraform terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } # State stocké à distance (S3 + DynamoDB lock) backend "s3" { bucket = "my-terraform-state" key = "prod/terraform.tfstate" region = "eu-west-3" dynamodb_table = "terraform-lock" } } provider "aws" { region = "eu-west-3" } # VPC + Subnets resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { Name = "prod-vpc" } } resource "aws_subnet" "public" { vpc_id = aws_vpc.main.id cidr_block = "10.0.1.0/24" availability_zone = "eu-west-3a" } # Instance EC2 resource "aws_instance" "app_server" { ami = "ami-0c55b159cbfafe1f0" instance_type = "t3.medium" subnet_id = aws_subnet.public.id key_name = var.ssh_key_name user_data = <<-EOF #!/bin/bash apt-get update -y apt-get install -y docker.io ansible EOF tags = { Name = "app-server-prod" Environment = "production" ManagedBy = "terraform" } } ## ── Git workflow — branches et conventions ────────── ## .gitconfig — Aliases pratiques # [alias] # st = status # co = checkout # lg = log --oneline --graph --decorate --all # pf = push --force-with-lease ## Stratégie de branches : ## main → production (protégée, merge request obligatoire) ## develop → intégration continue ## feature/* → nouvelles fonctionnalités ## hotfix/* → correctifs urgents en production
04 // Parcours

Expériences

DevOps Engineer (CDI)
Kan'sult — Bordeaux
  • Mise en place et industrialisation du cycle de livraison des applications
  • Rédaction de fichiers compose.yml complexes pour orchestrer GitLab, SonarQube et applications
  • Industrialisation des déploiements via Ansible (provisioning), Vagrant et Kubernetes
  • Monitoring et observabilité avec Prometheus, Loki et Grafana
  • Configuration de Traefik v2 pour SSL automatique et routage des services
  • Rédaction de pipelines CI/CD pour l'automatisation complète des déploiements
Oct 2023 — Sept 2025
DevOps (Freelance)
LOC'HALD — Mérignac, France
  • Configuration Traefik v2 et gestion automatique des certificats SSL
  • Tests de performance et scalabilité avec JMeter
  • Provisioning serveur et automatisation via Ansible
  • Orchestration Kubernetes pour gérer la scalabilité lors des pics de trafic
Fév 2022 — Juin 2023
Formateur Java (Freelance)
Webforce 3 — Bordeaux
  • Encadrement de 15 apprenants sur Java, Spring Boot et bonnes pratiques
  • Enseignement de la sécurité applicative et des pratiques CI/CD
Déc 2022 — Jan 2023
05 // Formations

Parcours académique

2025–2026

Bootcamp DevOps

Chillo Tech
SEPT 2025 – JAN 2026
2024

Master en Informatique et Systèmes d'Information

EPSI — Bordeaux
RNCP NIVEAU 7 · MBA
2022

Bachelor en Génie Logiciel

Institut universitaire de l'estuaire — Cameroun
BACHELOR
06 // Accès démo

Explorer mon GitLab

🦊

Instance GitLab auto-hébergée

Accède à mes dépôts en lecture seule avec les identifiants ci-contre. Tu pourras consulter mes pipelines CI/CD, playbooks Ansible, configurations Kubernetes et tous mes projets DevOps.

Pipelines CI/CD Playbooks Ansible Manifests K8s Dockerfiles Terraform
// Identifiants de démonstration
Login visiteur
Password Portfolio2026
Rôle Reporter · lecture seule
🦊 Accéder au GitLab

Travaillons ensemble

Disponible pour de nouvelles missions — Bordeaux & Remote