Skip to main content

Cours

DEVOPS - Du commit au déploiement.pdf

Bloc 1

Culture : né en 2009 par Patrick Debois
Accélérer les délais de livraison, le time-to-market et la fiabilité des déploiements. (un livre  : The phoenix project)

Les 3 piliers du DevOps

La culture : 

You build it, you run it

Werner Vogels CTO d'Amazon

  • Collaboration
  • Responsabilité partagée
  • Amélioration continue

L'automatisation : 

Pulumi, Terraform, Ansible (OpenSource, agentless)

  • CI/CD Pipeline
  • Infra as code
  • Tests automatisés

La mesure :

Prometheus + Grafana

  • Monitoring
  • Feedback loop
  • Métriques DORA

CI/CD : définition et différences

Continuous integration Continuous delivery/deployment
Intégration fréquent Delivery : déploiement en 1 clic
Test auto a chaque commit Deployment automatique
Détection rapide des régressions Environnement staging -> production
Build automatique de l'artefact Rollback auto

3 environnements : Dev/Sandbox -> Staging release -> Production

Les bénéfices du CI/CD
  • Reduction du time-to-market de 50 à 80%
  • Détection des bugs en minutes et pas en jours
  • Déploiement plus fréquents et plus sûr
  • Meilleur collaboration Dev/Ops
  • Traçabilité complète du code à la production
  • Capacité de rollback instantané
Flux CI/CD

CODE -> COMMIT -> TEST -> BUILD -> PUSH -> DEPLOY
Développeur -> git push -> npm test -> Docker build -> Docker hub -> Netlify
CI CD

Tour des outils :

Github action, GitLab CI, Jenkins (Orienté CD), Docker, Netlify (fronted), N8N

Nous on va utiliser GitHub action, qui se base sur des fichiers YAML

Les GitHub Actions

Workflow > Event > Job > Step > Runner

Bloc 2

No-code low code Full code
Zapier, make n8n, Retool Scripts Python
Interface visuelle Nodes + expressions

Terraform, Ansible

Aucun code requis Code optionnel

Contrôle total

n8n, concepts clés

Webhook, trigger -> IF, Condition -> HTTP Request -> Message Discord et/ou notification email

Cas d'usage métier 

CI/CI notif Monitoring Onboarding Sécurité Data Sync Reporting
Pipeline -> Slack/Discord Alertes infra -> ticket Nouvel employé -> comptes CVE détectée -> Scan CRm -> ERP sync KPI quotidien - PDF
Alerte échec build Jira/ServiceNow automatique AD, Email, Slack, VPN Rapport - équipe sécu Transformation + routing Email auto au manages

Bloc 3

TP dans le support de cours.

N8N

CyberWatch-TP-Guide.pdf

Workflow fait pour le TP (jusqu'à la partie metabase)

n8n

image.png

Ce workflow fait une requête au site NVD pour récupérer les 30 derniers résultats. Ensuite il envoie dans la BDD Supabase

 JSON copiable dans n8n

{
  "name": "Cyberwatch - MSA",
  "nodes": [
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        384,
        -168
      ],
      "id": "453c733d-675e-4ebe-9a53-4fd65e000333",
      "name": "When clicking ‘Execute workflow’"
    },
    {
      "parameters": {
        "jsCode": "const row = $input.first().json;\nreturn [{ json: { body: JSON.stringify(row) } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1280,
        -240
      ],
      "id": "cd7e689f-ea56-4fda-bd2a-823419a914c5",
      "name": "Prepare Body"
    },
    {
      "parameters": {
        "url": "https://services.nvd.nist.gov/rest/json/cves/2.0",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "pubStartDate",
              "value": "2025-01-01T00:00:00.000"
            },
            {
              "name": "pubEndDate",
              "value": "2025-04-16T23:59:59.999"
            },
            {
              "name": "cvssV3Severity",
              "value": "CRITICAL"
            },
            {
              "name": "resultsPerPage",
              "value": "10"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        608,
        -168
      ],
      "id": "e6087f2e-7eba-4e17-a0b8-a598496a3c0d",
      "name": "HTTP Request6"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst parsed = [];\nfor (const item of items) {\n  const vulnerabilities = item.json.vulnerabilities || [];\n  for (const vuln of vulnerabilities) {\n    const cve = vuln.cve;\n    if (!cve) continue;\n    const desc = (cve.descriptions || []).find(d => d.lang === 'en');\n    const cvssV3 = (cve.metrics?.cvssMetricV31 ||\n      cve.metrics?.cvssMetricV30 || [])[0]?.cvssData || {};\n    let vendor = null, product = null;\n    outer: for (const config of (cve.configurations || [])) {\n      for (const node of (config.nodes || [])) {\n        for (const cpe of (node.cpeMatch || [])) {\n          const parts = (cpe.criteria || '').split(':');\n          if (parts.length >= 5) {\n            vendor  = parts[3] !== '*' ? parts[3] : null;\n            product = parts[4] !== '*' ? parts[4] : null;\n            break outer;\n          }\n        }\n      }\n    }\n    parsed.push({ json: {\n      cve_id:              cve.id,\n      published_at:        cve.published,\n      last_modified:       cve.lastModified,\n      status:              cve.vulnStatus,\n      cvss_v3_score:       cvssV3.baseScore ?? null,\n      cvss_v3_severity:    cvssV3.baseSeverity ?? null,\n      attack_vector:       cvssV3.attackVector ?? null,\n      attack_complexity:   cvssV3.attackComplexity ?? null,\n      privileges_required: cvssV3.privilegesRequired ?? null,\n      user_interaction:    cvssV3.userInteraction ?? null,\n      confidentiality:     cvssV3.confidentialityImpact ?? null,\n      integrity:           cvssV3.integrityImpact ?? null,\n      availability:        cvssV3.availabilityImpact ?? null,\n      affected_vendor:     vendor,\n      affected_product:    product,\n      description:         desc?.value ?? null,\n      collected_at:        new Date().toISOString()\n    }});\n  }\n}\nreturn parsed;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        832,
        -168
      ],
      "id": "90b50001-d634-4574-be03-8b0e1d50cace",
      "name": "Code in JavaScript3"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        1056,
        -168
      ],
      "id": "2d70e2f4-80ef-43e6-80fa-74123f211673",
      "name": "Split In Batches2"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $json.Supabase_URL }}/rest/v1/cve",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "on_conflict",
              "value": "cve_id"
            }
          ]
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "={{ $json.Supabase_secret }}"
            },
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.Supabase_secret }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "resolution=merge-duplicates"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ $json.body }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 250
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1728,
        -168
      ],
      "id": "91d6986d-3422-439a-9fce-deedd6aca126",
      "name": "HTTP Request7"
    },
    {
      "parameters": {
        "mode": "raw",
        "jsonOutput": "{\n  \"Supabase_secret\": \"sb_secret_ici\",\n  \"Supabase_URL\":\"Supabase_URL_ici\"\n}\n",
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1504,
        -240
      ],
      "id": "a8a2ba10-992b-4e4c-8d11-6e7ebb9de4a7",
      "name": "Secret Supabase"
    }
  ],
  "pinData": {},
  "connections": {
    "When clicking ‘Execute workflow’": {
      "main": [
        [
          {
            "node": "HTTP Request6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Body": {
      "main": [
        [
          {
            "node": "Secret Supabase",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request6": {
      "main": [
        [
          {
            "node": "Code in JavaScript3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript3": {
      "main": [
        [
          {
            "node": "Split In Batches2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split In Batches2": {
      "main": [
        [],
        [
          {
            "node": "Prepare Body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request7": {
      "main": [
        [
          {
            "node": "Split In Batches2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Secret Supabase": {
      "main": [
        [
          {
            "node": "HTTP Request7",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "versionId": "ffd8ee42-0806-4696-b09a-17ede870e426",
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "3bf30934ba51ff8b01281907dd297e33866b7d2872975c43b1d7af51d9aa78aa"
  },
  "id": "ai9avvidlbvQkEKs",
  "tags": []
}
Supabase

image.png

Le workflow peuple la BDD dans Supabase en ligne

Metabase

Une connexion à la BDD de Supabase vers Metabase, permet un tableau de bord dynamique (~20 sec de délais de MàJ).

image.png

 

docker system prune -f