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
Workflow fait pour le TP (jusqu'à la partie metabase)
{
"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": []
}
docker system prune -f