Module 7 : CI/CD avec Cloud Build & Artifact Registry
Durée estimée : 30 minutes
Objectifs du Module
À la fin de ce module, vous serez capable de :
- Configurer Cloud Build pour l'intégration continue
- Stocker et gérer des images Docker avec Artifact Registry
- Créer des pipelines multi-étapes avec
cloudbuild.yaml - Déclencher des builds automatiques (triggers)
- Déployer automatiquement sur GKE et Cloud Run
1. Vue d'ensemble CI/CD sur GCP
Architecture CI/CD native GCP
graph LR
subgraph "Source"
GH[GitHub]
GL[GitLab]
CSR[Cloud Source Repos]
end
subgraph "Build"
CB[Cloud Build]
end
subgraph "Store"
AR[Artifact Registry]
end
subgraph "Deploy"
GKE[GKE]
CR[Cloud Run]
GCE[Compute Engine]
GCF[Cloud Functions]
end
GH --> CB
GL --> CB
CSR --> CB
CB --> AR
AR --> GKE
AR --> CR
CB --> GCE
CB --> GCF
style CB fill:#4285F4,color:#fff
style AR fill:#34A853,color:#fff
Services CI/CD GCP
| Service | Rôle | Équivalent |
|---|---|---|
| Cloud Build | CI/CD serverless | Jenkins, GitLab CI |
| Artifact Registry | Registre d'artefacts | Docker Hub, Nexus |
| Cloud Source Repositories | Git hosting | GitHub, GitLab |
| Cloud Deploy | CD GitOps | ArgoCD, Flux |
2. Artifact Registry
Créer un repository
# Repository Docker
gcloud artifacts repositories create docker-repo \
--repository-format=docker \
--location=europe-west1 \
--description="Production Docker images"
# Repository npm
gcloud artifacts repositories create npm-repo \
--repository-format=npm \
--location=europe-west1
# Repository Python (PyPI)
gcloud artifacts repositories create python-repo \
--repository-format=python \
--location=europe-west1
# Repository Maven
gcloud artifacts repositories create maven-repo \
--repository-format=maven \
--location=europe-west1
# Lister les repositories
gcloud artifacts repositories list --location=europe-west1
Configurer l'authentification
# Docker
gcloud auth configure-docker europe-west1-docker.pkg.dev
# npm
gcloud artifacts print-settings npm \
--repository=npm-repo \
--location=europe-west1
# Python (pip)
gcloud artifacts print-settings python \
--repository=python-repo \
--location=europe-west1
Push/Pull d'images Docker
# Format de l'URL
# LOCATION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE:TAG
PROJECT_ID=$(gcloud config get-value project)
REGION="europe-west1"
# Tagger une image locale
docker tag myapp:latest ${REGION}-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp:v1.0.0
docker tag myapp:latest ${REGION}-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp:latest
# Push
docker push ${REGION}-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp:v1.0.0
docker push ${REGION}-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp:latest
# Pull
docker pull ${REGION}-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp:v1.0.0
# Lister les images
gcloud artifacts docker images list \
${REGION}-docker.pkg.dev/${PROJECT_ID}/docker-repo
# Lister les tags d'une image
gcloud artifacts docker tags list \
${REGION}-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp
Politiques de nettoyage
# Supprimer les images non taggées de plus de 7 jours
gcloud artifacts docker images delete \
${REGION}-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp \
--delete-tags \
--filter="createTime<-P7D AND tags=''"
# Cleanup policy automatique (via Console ou Terraform)
# Artifacts Registry > Repository > Settings > Cleanup policies
Scan de vulnérabilités
# Activer le scan automatique
gcloud services enable containeranalysis.googleapis.com
gcloud services enable containerscanning.googleapis.com
# Les images sont scannées automatiquement au push
# Voir les vulnérabilités
gcloud artifacts docker images list \
${REGION}-docker.pkg.dev/${PROJECT_ID}/docker-repo \
--show-occurrences \
--format="table(package,version,location,vulnerability.severity)"
3. Cloud Build Basics
Premier build manuel
# Build une image depuis un Dockerfile
gcloud builds submit --tag ${REGION}-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp:v1
# Build avec un Dockerfile spécifique
gcloud builds submit --tag ${REGION}-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp:v1 \
--dockerfile=Dockerfile.prod
# Build depuis une URL Git
gcloud builds submit \
--tag ${REGION}-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp:v1 \
https://github.com/user/repo.git#branch
Configuration cloudbuild.yaml
# cloudbuild.yaml - Pipeline basique
steps:
# Étape 1 : Build l'image Docker
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA', '.']
# Étape 2 : Push vers Artifact Registry
- name: 'gcr.io/cloud-builders/docker'
args: ['push', '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA']
# Étape 3 : Tag "latest"
- name: 'gcr.io/cloud-builders/docker'
args: ['tag',
'${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA',
'${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:latest']
- name: 'gcr.io/cloud-builders/docker'
args: ['push', '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:latest']
# Images à stocker
images:
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA'
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:latest'
# Variables de substitution
substitutions:
_REGION: europe-west1
_REPO: docker-repo
# Options
options:
logging: CLOUD_LOGGING_ONLY
machineType: 'E2_HIGHCPU_8'
Exécuter un build avec cloudbuild.yaml
# Build local
gcloud builds submit --config=cloudbuild.yaml .
# Avec substitutions custom
gcloud builds submit --config=cloudbuild.yaml \
--substitutions=_REGION=us-central1,_REPO=my-repo .
# Voir les logs
gcloud builds log BUILD_ID
# Lister les builds
gcloud builds list --limit=10
4. Pipelines Avancés
Pipeline multi-étapes avec tests
# cloudbuild.yaml - Pipeline complet
steps:
# === STAGE: Test ===
- id: 'unit-tests'
name: 'node:18'
entrypoint: 'npm'
args: ['test']
dir: 'app'
- id: 'lint'
name: 'node:18'
entrypoint: 'npm'
args: ['run', 'lint']
dir: 'app'
waitFor: ['-'] # Parallèle avec unit-tests
# === STAGE: Build ===
- id: 'build-image'
name: 'gcr.io/cloud-builders/docker'
args:
- 'build'
- '-t'
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA'
- '-t'
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$BRANCH_NAME'
- '--cache-from'
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:latest'
- '.'
waitFor: ['unit-tests', 'lint']
# === STAGE: Security Scan ===
- id: 'trivy-scan'
name: 'aquasec/trivy'
args:
- 'image'
- '--exit-code'
- '1'
- '--severity'
- 'HIGH,CRITICAL'
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA'
waitFor: ['build-image']
# === STAGE: Push ===
- id: 'push-image'
name: 'gcr.io/cloud-builders/docker'
args: ['push', '--all-tags', '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp']
waitFor: ['trivy-scan']
# === STAGE: Deploy to Staging ===
- id: 'deploy-staging'
name: 'gcr.io/cloud-builders/gke-deploy'
args:
- 'run'
- '--filename=k8s/'
- '--image=${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA'
- '--cluster=staging-cluster'
- '--location=europe-west1'
waitFor: ['push-image']
images:
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA'
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$BRANCH_NAME'
substitutions:
_REGION: europe-west1
_REPO: docker-repo
options:
machineType: 'E2_HIGHCPU_8'
logging: CLOUD_LOGGING_ONLY
timeout: '1200s'
Visualisation du pipeline
graph LR
subgraph "Test Stage"
A[unit-tests]
B[lint]
end
subgraph "Build Stage"
C[build-image]
end
subgraph "Security Stage"
D[trivy-scan]
end
subgraph "Push Stage"
E[push-image]
end
subgraph "Deploy Stage"
F[deploy-staging]
end
A --> C
B --> C
C --> D
D --> E
E --> F
style A fill:#4285F4,color:#fff
style B fill:#4285F4,color:#fff
style C fill:#34A853,color:#fff
style D fill:#EA4335,color:#fff
style E fill:#FBBC04,color:#000
style F fill:#34A853,color:#fff
Pipeline avec secrets
steps:
- id: 'deploy'
name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-c'
- |
# Le secret est monté comme variable d'environnement
echo "Using API key: $${API_KEY:0:4}..."
# Deploy avec le secret
gcloud run deploy myapp \
--image=${_IMAGE} \
--set-env-vars="API_KEY=$$API_KEY"
secretEnv: ['API_KEY']
availableSecrets:
secretManager:
- versionName: projects/$PROJECT_ID/secrets/api-key/versions/latest
env: 'API_KEY'
5. Build Triggers
Types de triggers
| Type | Déclencheur | Usage |
|---|---|---|
| Push to branch | Push sur une branche | CI sur develop/feature |
| Push to tag | Tag créé | Release builds |
| Pull Request | PR ouverte/mise à jour | Validation PR |
| Manual | Invocation manuelle | Deploys manuels |
| Pub/Sub | Message Pub/Sub | Événements externes |
| Webhook | HTTP POST | Intégrations custom |
Créer un trigger
# Trigger sur push vers main
gcloud builds triggers create github \
--name="deploy-prod" \
--repo-owner="myorg" \
--repo-name="myapp" \
--branch-pattern="^main$" \
--build-config="cloudbuild.yaml" \
--substitutions="_ENV=prod"
# Trigger sur tag (release)
gcloud builds triggers create github \
--name="release-build" \
--repo-owner="myorg" \
--repo-name="myapp" \
--tag-pattern="^v[0-9]+\.[0-9]+\.[0-9]+$" \
--build-config="cloudbuild-release.yaml"
# Trigger sur PR
gcloud builds triggers create github \
--name="pr-validation" \
--repo-owner="myorg" \
--repo-name="myapp" \
--pull-request-pattern="^main$" \
--build-config="cloudbuild-pr.yaml" \
--comment-control="COMMENTS_ENABLED"
# Lister les triggers
gcloud builds triggers list
# Exécuter manuellement
gcloud builds triggers run deploy-prod --branch=main
Fichier de configuration par environnement
# cloudbuild-prod.yaml
steps:
- id: 'build'
name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', '$_IMAGE:$SHORT_SHA', '.']
- id: 'push'
name: 'gcr.io/cloud-builders/docker'
args: ['push', '$_IMAGE:$SHORT_SHA']
# Approval Gate (via Cloud Deploy ou manuel)
- id: 'deploy-prod'
name: 'gcr.io/cloud-builders/gke-deploy'
args:
- 'run'
- '--filename=k8s/prod/'
- '--image=$_IMAGE:$SHORT_SHA'
- '--cluster=prod-cluster'
- '--location=europe-west1'
substitutions:
_IMAGE: europe-west1-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp
options:
logging: CLOUD_LOGGING_ONLY
6. Déploiement sur GKE
Pipeline GKE complet
# cloudbuild-gke.yaml
steps:
# Build
- id: 'build'
name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', '$_IMAGE:$SHORT_SHA', '.']
# Push
- id: 'push'
name: 'gcr.io/cloud-builders/docker'
args: ['push', '$_IMAGE:$SHORT_SHA']
# Update Kubernetes manifests
- id: 'update-manifests'
name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-c'
- |
sed -i "s|IMAGE_TAG|$SHORT_SHA|g" k8s/deployment.yaml
cat k8s/deployment.yaml
# Get GKE credentials
- id: 'get-credentials'
name: 'gcr.io/cloud-builders/gcloud'
args:
- 'container'
- 'clusters'
- 'get-credentials'
- '$_CLUSTER'
- '--region=$_REGION'
# Apply Kubernetes manifests
- id: 'deploy'
name: 'gcr.io/cloud-builders/kubectl'
args: ['apply', '-f', 'k8s/']
env:
- 'CLOUDSDK_COMPUTE_REGION=$_REGION'
- 'CLOUDSDK_CONTAINER_CLUSTER=$_CLUSTER'
# Wait for rollout
- id: 'wait-rollout'
name: 'gcr.io/cloud-builders/kubectl'
args: ['rollout', 'status', 'deployment/myapp', '-n', 'default', '--timeout=300s']
env:
- 'CLOUDSDK_COMPUTE_REGION=$_REGION'
- 'CLOUDSDK_CONTAINER_CLUSTER=$_CLUSTER'
substitutions:
_IMAGE: europe-west1-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp
_CLUSTER: prod-cluster
_REGION: europe-west1
images:
- '$_IMAGE:$SHORT_SHA'
options:
logging: CLOUD_LOGGING_ONLY
Manifests Kubernetes pour CI/CD
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: europe-west1-docker.pkg.dev/PROJECT_ID/docker-repo/myapp:IMAGE_TAG
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
7. Déploiement sur Cloud Run
Pipeline Cloud Run
# cloudbuild-cloudrun.yaml
steps:
# Build
- id: 'build'
name: 'gcr.io/cloud-builders/docker'
args:
- 'build'
- '-t'
- '$_IMAGE:$SHORT_SHA'
- '-t'
- '$_IMAGE:latest'
- '.'
# Push
- id: 'push'
name: 'gcr.io/cloud-builders/docker'
args: ['push', '--all-tags', '$_IMAGE']
# Deploy to Cloud Run
- id: 'deploy'
name: 'gcr.io/cloud-builders/gcloud'
args:
- 'run'
- 'deploy'
- 'myapp'
- '--image=$_IMAGE:$SHORT_SHA'
- '--region=$_REGION'
- '--platform=managed'
- '--allow-unauthenticated'
- '--cpu=1'
- '--memory=512Mi'
- '--min-instances=0'
- '--max-instances=10'
- '--set-env-vars=ENV=production'
substitutions:
_IMAGE: europe-west1-docker.pkg.dev/${PROJECT_ID}/docker-repo/myapp
_REGION: europe-west1
images:
- '$_IMAGE:$SHORT_SHA'
- '$_IMAGE:latest'
Traffic splitting pour canary deployments
steps:
# Deploy nouvelle revision sans traffic
- id: 'deploy-canary'
name: 'gcr.io/cloud-builders/gcloud'
args:
- 'run'
- 'deploy'
- 'myapp'
- '--image=$_IMAGE:$SHORT_SHA'
- '--region=$_REGION'
- '--no-traffic'
- '--tag=canary'
# Envoyer 10% du traffic vers canary
- id: 'traffic-split'
name: 'gcr.io/cloud-builders/gcloud'
args:
- 'run'
- 'services'
- 'update-traffic'
- 'myapp'
- '--region=$_REGION'
- '--to-tags=canary=10'
8. Exercices Pratiques
Exercice 1 : Pipeline basique
Exercice
- Créez un repository Artifact Registry
training-repo - Créez une application Node.js simple avec Dockerfile
- Écrivez un
cloudbuild.yamlqui build et push l'image - Exécutez le build manuellement
Solution
# Créer le repository
gcloud artifacts repositories create training-repo \
--repository-format=docker \
--location=europe-west1
# Créer l'application
mkdir myapp && cd myapp
cat > package.json << 'EOF'
{
"name": "myapp",
"version": "1.0.0",
"scripts": {
"start": "node server.js",
"test": "echo 'Tests passed'"
}
}
EOF
cat > server.js << 'EOF'
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello from Cloud Build!');
});
server.listen(8080, () => console.log('Server running on port 8080'));
EOF
cat > Dockerfile << 'EOF'
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 8080
CMD ["npm", "start"]
EOF
cat > cloudbuild.yaml << 'EOF'
steps:
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA', '.']
- name: 'gcr.io/cloud-builders/docker'
args: ['push', '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA']
images:
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA'
substitutions:
_REGION: europe-west1
_REPO: training-repo
EOF
# Exécuter le build
gcloud builds submit --config=cloudbuild.yaml .
# Vérifier l'image
gcloud artifacts docker images list \
europe-west1-docker.pkg.dev/$(gcloud config get-value project)/training-repo
Exercice 2 : Pipeline avec tests et déploiement Cloud Run
Exercice
- Ajoutez des tests à l'application
- Modifiez le
cloudbuild.yamlpour :- Exécuter les tests
- Builder l'image
- Déployer sur Cloud Run
- Créez un trigger sur la branche
main
Solution
# cloudbuild.yaml amélioré
cat > cloudbuild.yaml << 'EOF'
steps:
# Tests
- id: 'test'
name: 'node:18'
entrypoint: 'npm'
args: ['test']
# Build
- id: 'build'
name: 'gcr.io/cloud-builders/docker'
args:
- 'build'
- '-t'
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA'
- '.'
waitFor: ['test']
# Push
- id: 'push'
name: 'gcr.io/cloud-builders/docker'
args: ['push', '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA']
# Deploy Cloud Run
- id: 'deploy'
name: 'gcr.io/cloud-builders/gcloud'
args:
- 'run'
- 'deploy'
- 'myapp'
- '--image=${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA'
- '--region=${_REGION}'
- '--platform=managed'
- '--allow-unauthenticated'
substitutions:
_REGION: europe-west1
_REPO: training-repo
images:
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA'
EOF
# Exécuter
gcloud builds submit --config=cloudbuild.yaml .
# Obtenir l'URL Cloud Run
gcloud run services describe myapp --region=europe-west1 --format="get(status.url)"
Exercice 3 : Canary deployment
Exercice
Implémentez un canary deployment sur Cloud Run :
- Déployez une v1 avec 100% du traffic
- Déployez une v2 avec tag "canary" sans traffic
- Routez 20% du traffic vers canary
- Observez puis faites un rollout complet ou rollback
Solution
# V1 avec 100% traffic
gcloud run deploy myapp \
--image=europe-west1-docker.pkg.dev/$PROJECT_ID/training-repo/myapp:v1 \
--region=europe-west1 \
--platform=managed
# V2 sans traffic, tag canary
gcloud run deploy myapp \
--image=europe-west1-docker.pkg.dev/$PROJECT_ID/training-repo/myapp:v2 \
--region=europe-west1 \
--no-traffic \
--tag=canary
# 20% vers canary
gcloud run services update-traffic myapp \
--region=europe-west1 \
--to-tags=canary=20
# Observer (générer du traffic et voir les logs)
URL=$(gcloud run services describe myapp --region=europe-west1 --format="get(status.url)")
for i in {1..100}; do curl -s $URL; done
# Rollout complet vers canary
gcloud run services update-traffic myapp \
--region=europe-west1 \
--to-latest
# OU Rollback (100% vers revision précédente)
gcloud run services update-traffic myapp \
--region=europe-west1 \
--to-revisions=myapp-00001-xxx=100
Exercice : À Vous de Jouer
Mise en Pratique
Objectif : Mettre en place un pipeline CI/CD complet avec Cloud Build, Artifact Registry et déploiement automatique sur Cloud Run
Contexte : Vous devez automatiser le déploiement d'une application web conteneurisée. À chaque push sur la branche main, l'application doit être buildée, testée, son image stockée dans Artifact Registry, et déployée automatiquement sur Cloud Run en production.
Tâches à réaliser :
- Créer un repository Artifact Registry
cicd-repopour Docker - Créer une application web simple (Node.js ou Python) avec un Dockerfile
- Builder l'image manuellement et la pousser dans Artifact Registry
- Déployer manuellement sur Cloud Run pour valider l'image
- Créer un fichier
cloudbuild.yamlavec 4 étapes :- Build de l'image Docker
- Push vers Artifact Registry
- Tests (basiques)
- Déploiement sur Cloud Run
- Créer un Build Trigger lié à un repository GitHub/Cloud Source
- Effectuer un push et vérifier le déploiement automatique
- Configurer une deuxième révision avec traffic splitting (50/50)
Critères de validation :
- [ ] Artifact Registry repository créé
- [ ] Image Docker buildée et poussée
- [ ] Application déployée sur Cloud Run
- [ ] Fichier
cloudbuild.yamlfonctionnel avec toutes les étapes - [ ] Build trigger configuré
- [ ] Pipeline déclenché automatiquement au push
- [ ] Traffic splitting configuré entre deux révisions
- [ ] Logs du build accessibles et sans erreur
Solution
# 1. Créer Artifact Registry
REGION="europe-west1"
PROJECT_ID=$(gcloud config get-value project)
gcloud artifacts repositories create cicd-repo \
--repository-format=docker \
--location=$REGION \
--description="CI/CD Pipeline Repository"
gcloud auth configure-docker ${REGION}-docker.pkg.dev
# 2. Créer l'application
mkdir my-cicd-app && cd my-cicd-app
cat > app.js << 'EOF'
const express = require('express');
const app = express();
const PORT = process.env.PORT || 8080;
app.get('/', (req, res) => {
res.json({
message: 'Hello from CI/CD Pipeline!',
version: process.env.VERSION || '1.0',
timestamp: new Date().toISOString()
});
});
app.get('/health', (req, res) => res.json({ status: 'healthy' }));
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
EOF
cat > package.json << 'EOF'
{
"name": "cicd-app",
"version": "1.0.0",
"main": "app.js",
"scripts": {
"start": "node app.js",
"test": "echo 'Tests passed!'"
},
"dependencies": {
"express": "^4.18.0"
}
}
EOF
cat > Dockerfile << 'EOF'
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 8080
CMD ["npm", "start"]
EOF
# 3. Build et push manuel
IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/cicd-repo/myapp:v1"
gcloud builds submit --tag=$IMAGE
# 4. Déployer sur Cloud Run
gcloud run deploy myapp \
--image=$IMAGE \
--region=$REGION \
--platform=managed \
--allow-unauthenticated \
--port=8080
# Tester
URL=$(gcloud run services describe myapp --region=$REGION --format="get(status.url)")
curl $URL
# 5. Créer cloudbuild.yaml
cat > cloudbuild.yaml << 'EOF'
steps:
# Step 1: Build
- id: 'build'
name: 'gcr.io/cloud-builders/docker'
args: [
'build',
'-t', '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA',
'-t', '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:latest',
'.'
]
# Step 2: Push
- id: 'push'
name: 'gcr.io/cloud-builders/docker'
args: [
'push',
'--all-tags',
'${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp'
]
# Step 3: Test
- id: 'test'
name: 'node:18-alpine'
entrypoint: 'sh'
args:
- '-c'
- |
npm install
npm test
# Step 4: Deploy
- id: 'deploy'
name: 'gcr.io/cloud-builders/gcloud'
args: [
'run', 'deploy', 'myapp',
'--image', '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA',
'--region', '${_REGION}',
'--platform', 'managed',
'--allow-unauthenticated'
]
substitutions:
_REGION: europe-west1
_REPO: cicd-repo
images:
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:$SHORT_SHA'
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPO}/myapp:latest'
options:
logging: CLOUD_LOGGING_ONLY
EOF
# 6. Créer un trigger (si repository Git disponible)
# gcloud builds triggers create github \
# --repo-name=my-repo \
# --repo-owner=my-github \
# --branch-pattern="^main$" \
# --build-config=cloudbuild.yaml
# Alternative: Build manuel avec cloudbuild.yaml
gcloud builds submit --config=cloudbuild.yaml
# 7. Traffic splitting (v1 vs v2)
# Modifier app.js avec version 2.0
sed -i "s/version: '1.0'/version: '2.0'/g" app.js
# Build v2
IMAGE_V2="${REGION}-docker.pkg.dev/${PROJECT_ID}/cicd-repo/myapp:v2"
gcloud builds submit --tag=$IMAGE_V2
# Déployer v2 sans traffic
gcloud run deploy myapp \
--image=$IMAGE_V2 \
--region=$REGION \
--no-traffic \
--tag=v2
# Split traffic 50/50
gcloud run services update-traffic myapp \
--region=$REGION \
--to-revisions=LATEST=50,v2=50
# Validation
echo "=== VALIDATION ==="
gcloud run services describe myapp --region=$REGION
gcloud builds list --limit=5
echo ""
echo "✅ Pipeline CI/CD configuré!"
echo "URL: $URL"
9. Nettoyage
# Cloud Run
gcloud run services delete myapp --region=europe-west1 --quiet
# Artifact Registry (attention : supprime toutes les images)
gcloud artifacts repositories delete training-repo \
--location=europe-west1 --quiet
# Build triggers
gcloud builds triggers list --format="value(id)" | \
xargs -I {} gcloud builds triggers delete {} --quiet
Résumé du Module
| Concept | Points clés |
|---|---|
| Artifact Registry | Registre multi-format (Docker, npm, Maven, Python) |
| Cloud Build | CI/CD serverless, fichier cloudbuild.yaml |
| Triggers | Push, tag, PR, webhook, Pub/Sub |
| Secrets | Intégration Secret Manager |
| GKE Deploy | kubectl, gke-deploy builders |
| Cloud Run | Déploiement serverless, canary avec traffic splitting |
← Retour au Module 6 | Continuer vers le Module 8 : Serverless →
Retour au : Programme de la Formation | Catalogue des Formations
Navigation
| ← Module 6 : TP Final - Infrastructure ... | Module 8 : Serverless - Cloud Functio... → |