Déployer sur GCP Cloud Run avec Claude Code : guide pratique
Déployez une API Node.js sécurisée sur Cloud Run avec Docker, IAM, secrets, logs et rollback.
Cloud Run est un bon choix quand vous voulez publier un service HTTP conteneurisé sans exploiter Kubernetes, ECS ou une flotte de VM. Contrairement à Cloud Functions ou Lambda, vous n’avez pas besoin de découper toute l’application en petites fonctions. Vous pouvez prendre une API Express classique, l’empaqueter en image Docker et laisser Google Cloud gérer HTTPS, révisions, scaling et logs.
Ce guide montre comment utiliser Claude Code pour amener une API TypeScript/Express de votre poste local à Cloud Run. Il couvre le code exécutable, le Dockerfile, le test local, Artifact Registry, gcloud run deploy, le compte de service, IAM, Secret Manager, la concurrence, les instances minimales, Cloud Logging, le rollback de révision, les pièges de coût et une prompt de revue avant déploiement.
En clair : Cloud Run est une plateforme serverless pour exécuter des conteneurs qui répondent à des requêtes HTTP. Artifact Registry est le dépôt d’images Docker. Secret Manager est le coffre pour mots de passe et clés API. IAM est le système de permissions qui détermine quelle identité peut accéder à quelle ressource.
Quand Cloud Run bat Functions ou Lambda
Cloud Run n’est pas toujours supérieur à Cloud Functions ou Lambda. Pour un minuscule handler d’événement, une fonction peut être plus simple. Cloud Run devient plus intéressant quand le service est déjà une application HTTP ou quand le runtime doit être plus contrôlé.
| Cas d’usage | Pourquoi Cloud Run convient |
|---|---|
| Récepteur de webhooks | Stripe, GitHub ou LINE se modélisent naturellement en routes Express |
| Petit BFF ou serveur API | Auth, middleware, routage et validation restent dans une application Node.js normale |
| Endpoint HTTP de traitement planifié | Cloud Scheduler peut l’appeler sans créer un autre framework de jobs |
| API légère IA ou données | Dépendances natives et binaires personnalisés restent dans le conteneur |
Pour un worker longuement actif, vérifiez CPU et coût. Cloud Run est excellent pour les services déclenchés par requêtes, moins évident pour des boucles de fond permanentes.
Service Express exécutable
Cloud Run fournit le port via la variable PORT. Le service doit la lire, exposer /health et s’arrêter proprement quand Cloud Run envoie SIGTERM.
{
"name": "cloud-run-claude-code-api",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "tsx src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "node --test"
},
"dependencies": {
"express": "^4.19.2"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^22.10.0",
"tsx": "^4.19.2",
"typescript": "^5.7.2"
}
}
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts"]
}
import express from "express";
const app = express();
app.use(express.json());
const requiredEnv = ["DATABASE_URL", "JWT_SECRET"];
for (const key of requiredEnv) {
if (!process.env[key]) {
console.error(`Missing required environment variable: ${key}`);
process.exit(1);
}
}
app.get("/health", (_req, res) => {
res.status(200).json({ ok: true, service: "myapp-api" });
});
app.post("/webhooks/example", (req, res) => {
console.log("webhook_received", {
eventType: req.body?.type ?? "unknown",
receivedAt: new Date().toISOString()
});
res.status(202).json({ accepted: true });
});
app.get("/config-check", (_req, res) => {
res.json({
nodeEnv: process.env.NODE_ENV ?? "development",
hasDatabaseUrl: Boolean(process.env.DATABASE_URL),
hasJwtSecret: Boolean(process.env.JWT_SECRET)
});
});
const port = Number(process.env.PORT ?? 8080);
const server = app.listen(port, () => {
console.log(`listening on ${port}`);
});
process.on("SIGTERM", () => {
console.log("SIGTERM received, closing HTTP server");
server.close(() => process.exit(0));
setTimeout(() => process.exit(1), 30000).unref();
});
Testez d’abord en local :
npm install
DATABASE_URL="postgresql://local" JWT_SECRET="local-secret" npm run dev
curl http://localhost:8080/health
curl -X POST http://localhost:8080/webhooks/example \
-H "Content-Type: application/json" \
-d '{"type":"demo.created"}'
Dockerfile et revue Claude Code
Demandez à Claude Code une revue de production, pas seulement une génération de fichier. Les points clés sont : image runtime compacte, dépendances de production uniquement, utilisateur non root, .dockerignore, prise en charge de PORT, risques sécurité et coût.
claude -p "
Review and improve this Cloud Run Docker setup.
Requirements:
- Node.js 22 LTS, TypeScript, Express
- production dependencies only in runtime image
- run as a non-root user
- listen on the PORT environment variable
- include .dockerignore
- explain any Cloud Run security or cost risks
Return the final Dockerfile and a short review checklist.
"
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src ./src
RUN npm run build
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=8080
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=builder /app/dist ./dist
USER appuser
EXPOSE 8080
CMD ["node", "--max-old-space-size=384", "dist/index.js"]
node_modules
dist
.env
.env.*
*.log
.git
.gitignore
Dockerfile
README.md
docker build -t myapp-api:local .
docker run --rm -p 8080:8080 \
-e PORT=8080 \
-e DATABASE_URL="postgresql://local" \
-e JWT_SECRET="local-secret" \
myapp-api:local
curl http://localhost:8080/health
Artifact Registry et premier déploiement
Artifact Registry stocke l’image Docker. Configurez l’authentification Docker du host régional, créez le dépôt, puis poussez l’image.
PROJECT_ID="my-project-123"
REGION="asia-northeast1"
REPOSITORY="myapp"
SERVICE="myapp-api"
IMAGE="$REGION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/api:v1.0.0"
gcloud config set project "$PROJECT_ID"
gcloud services enable run.googleapis.com artifactregistry.googleapis.com secretmanager.googleapis.com logging.googleapis.com
gcloud artifacts repositories create "$REPOSITORY" \
--repository-format=docker \
--location="$REGION" \
--description="Docker images for myapp"
gcloud auth configure-docker "$REGION-docker.pkg.dev"
docker build -t "$IMAGE" .
docker push "$IMAGE"
Créez un compte de service runtime dédié avant le déploiement.
gcloud iam service-accounts create myapp-run \
--display-name="Cloud Run runtime for myapp"
SERVICE_ACCOUNT="myapp-run@$PROJECT_ID.iam.gserviceaccount.com"
gcloud run deploy "$SERVICE" \
--image "$IMAGE" \
--region "$REGION" \
--platform managed \
--service-account "$SERVICE_ACCOUNT" \
--memory 512Mi \
--cpu 1 \
--concurrency 80 \
--min-instances 0 \
--max-instances 20 \
--allow-unauthenticated \
--set-env-vars NODE_ENV=production \
--port 8080
Pour une API interne, retirez --allow-unauthenticated et donnez Cloud Run Invoker uniquement aux appelants autorisés.
Secret Manager, IAM et sécurité
N’écrivez pas les secrets dans --set-env-vars. Stockez-les dans Secret Manager et injectez-les dans Cloud Run.
echo -n "postgresql://user:password@host:5432/app" | \
gcloud secrets create DATABASE_URL --data-file=-
echo -n "replace-with-long-random-value" | \
gcloud secrets create JWT_SECRET --data-file=-
gcloud secrets add-iam-policy-binding DATABASE_URL \
--member="serviceAccount:$SERVICE_ACCOUNT" \
--role="roles/secretmanager.secretAccessor"
gcloud secrets add-iam-policy-binding JWT_SECRET \
--member="serviceAccount:$SERVICE_ACCOUNT" \
--role="roles/secretmanager.secretAccessor"
gcloud run services update "$SERVICE" \
--region "$REGION" \
--set-secrets "DATABASE_URL=DATABASE_URL:latest,JWT_SECRET=JWT_SECRET:latest"
Préférez des permissions au niveau du secret plutôt qu’un rôle large sur le projet.
Concurrence, instances minimales et coût
La concurrence est le nombre maximum de requêtes simultanées traitées par une instance. Une valeur élevée réduit les instances, mais peut saturer la base ou une API externe.
gcloud run services update "$SERVICE" \
--region "$REGION" \
--concurrency 40 \
--min-instances 1 \
--max-instances 20 \
--cpu-throttling
Gardez min-instances 0 en développement ou pour un service très peu utilisé. Passez à 1 ou plus si une API ou un webhook de production ne tolère pas le cold start. Démarrez les webhooks vers 20-40, les BFF/API vers 40-80, puis ajustez avec la latence p95, les connexions DB et le taux d’erreur.
Logs et rollback
Cloud Run envoie les logs de requête, conteneur et système à Cloud Logging. Pour Node.js, stdout/stderr suffit souvent.
gcloud run services logs read "$SERVICE" \
--region "$REGION" \
--limit 20
gcloud logging read \
"resource.type=cloud_run_revision AND resource.labels.service_name=$SERVICE" \
--limit 20 \
--format=json
Chaque déploiement ou changement de configuration crée une révision immuable. Pour revenir en arrière, déplacez le trafic vers une révision stable.
gcloud run revisions list \
--service "$SERVICE" \
--region "$REGION"
gcloud run services update-traffic "$SERVICE" \
--region "$REGION" \
--to-revisions myapp-api-00012-abc=100
Prompt de revue avant production :
claude -p "
Act as a Cloud Run deployment reviewer.
Review package.json, Dockerfile, src/index.ts, and the gcloud commands below.
Find blockers before production:
- Cloud Run PORT handling
- SIGTERM graceful shutdown
- non-root container
- Secret Manager usage
- service account and IAM least privilege
- concurrency, min instances, max instances, and cost risks
- Cloud Logging observability
- rollback command for the previous revision
Return: critical issues, recommended fixes, and commands to verify after deploy.
"
Pièges courants
Les grosses images ralentissent build, push et cold start. Les secrets en variables ordinaires peuvent finir dans l’historique ou les logs CI. min-instances 1 réduit la latence, mais facture même sans trafic. Une concurrence trop haute peut épuiser la base ou les APIs tierces. Le rollback doit être testé avant l’incident.
Références et suite
- Documentation Cloud Run
- Déployer des images de conteneur sur Cloud Run
- Authentification Docker Artifact Registry
- Configurer les secrets Cloud Run
- Logs Cloud Run
- Rollback et migration de trafic Cloud Run
- Intégration Docker avec Claude Code
- Guide Claude Code AWS ECS/Fargate
- Bonnes pratiques de sécurité Claude Code
Cloud Run offre un compromis pratique : plus flexible qu’une plateforme de fonctions, plus léger qu’ECS ou Kubernetes. Pour mettre en place des prompts, revues et garde-fous de déploiement en équipe, consultez la formation et le conseil Claude Code.
Note de vérification terrain
L’exemple a été vérifié avec npm run dev, l’accès Docker à /health, la gestion de PORT et la validation des variables obligatoires. Avant production, répétez avec les permissions Artifact Registry réelles, l’IAM Secret Manager, les requêtes Cloud Logging et un exercice de rollback via update-traffic.
PDF gratuit: cheatsheet Claude Code
Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.
Nous protégeons vos données et n'envoyons pas de spam.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Workflow Obsidian vers CLAUDE.md avec Claude Code
Transformer des notes Obsidian en notes CLAUDE.md concises pour reprendre les sessions sans réexpliquer.
Claude Code Revenue CTA Routing : relier articles, PDF, Gumroad et consultation
Un workflow Claude Code pour orienter les lecteurs vers PDF gratuit, Gumroad ou consultation selon l'intention.
Règles de handoff Claude Code en équipe: preuves, permissions, rollback et revenus
Un format concret pour transmettre un travail Claude Code avec preuves, permissions, rollback, PDF gratuit, Gumroad et consultation.