name: gcp-cloud-run description: 在Google Cloud Run上部署容器化应用程序,实现自动扩展、流量管理和服务网格集成。适用于基于容器的无服务器计算。
GCP Cloud Run
概览
Google Cloud Run允许在不管理基础设施的情况下大规模部署容器化应用程序。运行无状态HTTP容器,自动扩展从零到数千个实例,仅支付消耗的计算时间。
何时使用
- 微服务和API
- Web应用程序和后端
- 批处理作业
- 长运行后台工作
- CI/CD流水线集成
- 数据处理流水线
- WebSocket应用程序
- 多语言服务
实施示例
1. 使用gcloud CLI进行Cloud Run部署
# 构建容器镜像
gcloud builds submit --tag gcr.io/MY_PROJECT_ID/my-app:latest
# 部署到Cloud Run
gcloud run deploy my-app \
--image gcr.io/MY_PROJECT_ID/my-app:latest \
--platform managed \
--region us-central1 \
--memory 512Mi \
--cpu 1 \
--timeout 3600 \
--max-instances 100 \
--min-instances 1 \
--no-allow-unauthenticated \
--set-env-vars NODE_ENV=production,DATABASE_URL=postgresql://...
# 允许公开访问
gcloud run services add-iam-policy-binding my-app \
--platform managed \
--region us-central1 \
--member=allUsers \
--role=roles/run.invoker
# 获取服务URL
gcloud run services describe my-app \
--platform managed \
--region us-central1 \
--format 'value(status.url)'
# 查看日志
gcloud run services logs read my-app --limit 50
# 使用新镜像更新服务
gcloud run deploy my-app \
--image gcr.io/MY_PROJECT_ID/my-app:v2 \
--platform managed \
--region us-central1 \
--update-env-vars VERSION=2
2. 容器化应用程序(Node.js)
# Dockerfile
FROM node:18-alpine
WORKDIR /app
# 复制包文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制应用程序代码
COPY . .
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node healthcheck.js
# 暴露端口(Cloud Run默认使用8080)
EXPOSE 8080
# 运行应用程序
CMD ["node", "server.js"]
// server.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 8080;
app.use(express.json());
// 健康检查端点
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// 存活探针
app.get('/live', (req, res) => {
res.status(200).send('alive');
});
// 就绪探针
app.get('/ready', (req, res) => {
res.status(200).send('ready');
});
// API端点
app.get('/api/data', async (req, res) => {
try {
const data = await fetchData();
res.json(data);
} catch (error) {
console.error('Error fetching data:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// 优雅关闭
let isShuttingDown = false;
process.on('SIGTERM', () => {
console.log('SIGTERM信号接收:关闭HTTP服务器');
isShuttingDown = true;
server.close(() => {
console.log('HTTP服务器已关闭');
process.exit(0);
});
// 强制关闭后30秒
setTimeout(() => {
console.error('由于超时强制关闭');
process.exit(1);
}, 30000);
});
const server = app.listen(PORT, () => {
console.log(`服务器正在监听端口${PORT}`);
});
async function fetchData() {
return { items: [] };
}
3. Terraform Cloud Run配置
# cloud-run.tf
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
provider "google" {
project = var.project_id
region = var.region
}
variable "project_id" {
description = "GCP项目ID"
}
variable "region" {
default = "us-central1"
}
variable "image" {
description = "容器镜像URI"
}
# Cloud Run的服务账户
resource "google_service_account" "cloud_run_sa" {
account_id = "cloud-run-sa"
display_name = "Cloud Run服务账户"
}
# 授予Cloud Logging角色
resource "google_project_iam_member" "cloud_run_logs" {
project = var.project_id
role = "roles/logging.logWriter"
member = "serviceAccount:${google_service_account.cloud_run_sa.email}"
}
# Cloud SQL客户端角色(如果使用Cloud SQL)
resource "google_project_iam_member" "cloud_sql_client" {
project = var.project_id
role = "roles/cloudsql.client"
member = "serviceAccount:${google_service_account.cloud_run_sa.email}"
}
# Cloud Run服务
resource "google_cloud_run_service" "app" {
name = "my-app"
location = var.region
template {
spec {
service_account_name = google_service_account.cloud_run_sa.email
containers {
image = var.image
resources {
limits = {
cpu = "1"
memory = "512Mi"
}
}
env {
name = "NODE_ENV"
value = "production"
}
env {
name = "PORT"
value = "8080"
}
ports {
container_port = 8080
}
# 启动探针
startup_probe {
http_get {
path = "/ready"
port = 8080
}
failure_threshold = 3
period_seconds = 10
}
# 存活探针
liveness_probe {
http_get {
path = "/live"
port = 8080
}
failure_threshold = 3
period_seconds = 10
initial_delay_seconds = 10
}
}
timeout_seconds = 3600
service_account_name = google_service_account.cloud_run_sa.email
}
metadata {
annotations = {
"autoscaling.knative.dev/maxScale" = "100"
"autoscaling.knative.dev/minScale" = "1"
}
}
}
traffic {
percent = 100
latest_revision = true
}
depends_on = [google_project_iam_member.cloud_run_logs]
}
# 允许公开访问
resource "google_cloud_run_service_iam_binding" "public" {
service = google_cloud_run_service.app.name
location = google_cloud_run_service.app.location
role = "roles/run.invoker"
members = [
"allUsers"
]
}
# Cloud Load Balancer用于全球访问
resource "google_compute_backend_service" "app" {
name = "my-app-backend"
protocol = "HTTPS"
security_policy = google_compute_security_policy.app.id
backend {
group = google_compute_network_endpoint_group.app.id
}
health_checks = [google_compute_health_check.app.id]
log_config {
enable = true
sample_rate = 1.0
}
}
# 网络端点组用于Cloud Run
resource "google_compute_network_endpoint_group" "app" {
name = "my-app-neg"
network_endpoint_type = "SERVERLESS"
cloud_run_config {
service = google_cloud_run_service.app.name
}
location = var.region
}
# 健康检查
resource "google_compute_health_check" "app" {
name = "my-app-health-check"
https_health_check {
port = "8080"
request_path = "/health"
}
}
# Cloud Armor安全策略
resource "google_compute_security_policy" "app" {
name = "my-app-policy"
rules {
action = "deny(403)"
priority = "100"
match {
versioned_expr = "CEL_V1"
expression = "origin.country_code in ['CN', 'RU']"
}
}
rules {
action = "rate_based_ban"
priority = "200"
match {
versioned_expr = "CEL_V1"
expression = "true"
}
rate_limit_options {
conform_action = "allow"
exceed_action = "deny(429)"
enforce_on_key = "IP"
ban_duration_sec = 600
rate_limit_threshold {
count = 100
interval_sec = 60
}
ban_threshold_rule {
count = 1000
interval_sec = 60
}
}
}
rules {
action = "allow"
priority = "65535"
match {
versioned_expr = "CEL_V1"
expression = "true"
}
}
}
# 全球地址
resource "google_compute_global_address" "app" {
name = "my-app-address"
}
# HTTPS重定向
resource "google_compute_url_map" "https_redirect" {
name = "my-app-https-redirect"
default_url_redirect {
https_redirect = true
redirect_response_code = "301"
strip_query = false
}
}
# HTTPS目标代理
resource "google_compute_target_https_proxy" "app" {
name = "my-app-proxy"
url_map = google_compute_url_map.app.id
ssl_certificates = [google_compute_managed_ssl_certificate.app.id]
}
# 管理SSL证书
resource "google_compute_managed_ssl_certificate" "app" {
name = "my-app-cert"
managed {
domains = ["example.com"]
}
}
# URL映射
resource "google_compute_url_map" "app" {
name = "my-app-url-map"
default_service = google_compute_backend_service.app.id
}
# 转发规则
resource "google_compute_global_forwarding_rule" "app" {
name = "my-app-forwarding-rule"
ip_protocol = "TCP"
load_balancing_scheme = "EXTERNAL"
port_range = "443"
target = google_compute_target_https_proxy.app.id
address = google_compute_global_address.app.address
}
# 监控警报
resource "google_monitoring_alert_policy" "cloud_run_errors" {
display_name = "Cloud Run高错误率"
combiner = "OR"
conditions {
display_name = "错误率阈值"
condition_threshold {
filter = "metric.type=\"run.googleapis.com/request_count\" AND resource.label.service_name=\"my-app\" AND metric.label.response_code_class=\"5xx\""
duration = "60s"
comparison = "COMPARISON_GT"
threshold_value = 10
aggregations {
alignment_period = "60s"
per_series_aligner = "ALIGN_RATE"
}
}
}
notification_channels = []
}
# Cloud Run作业批量处理
resource "google_cloud_run_v2_job" "batch" {
name = "batch-processor"
location = var.region
template {
containers {
image = var.image
env {
name = "JOB_TYPE"
value = "batch"
}
}
timeout = "3600s"
service_account = google_service_account.cloud_run_sa.email
}
}
# Cloud Scheduler触发作业
resource "google_cloud_scheduler_job" "batch_trigger" {
name = "batch-processor-trigger"
schedule = "0 2 * * *"
time_zone = "UTC"
attempt_deadline = "320s"
region = var.region
http_target {
http_method = "POST"
uri = "https://${var.region}-run.googleapis.com/apis/run.googleapis.com/v1/projects/${var.project_id}/locations/${var.region}/jobs/batch-processor:run"
headers = {
"Content-Type" = "application/json"
}
oidc_token {
service_account_email = google_service_account.cloud_run_sa.email
}
}
}
output "cloud_run_url" {
value = google_cloud_run_service.app.status[0].url
}
output "load_balancer_ip" {
value = google_compute_global_address.app.address
}
4. Docker构建和推送
# 本地构建镜像
docker build -t my-app:latest .
# 标记为容器注册表
docker tag my-app:latest gcr.io/MY_PROJECT_ID/my-app:latest
# 推送到容器注册表
docker push gcr.io/MY_PROJECT_ID/my-app:latest
# 或使用Cloud Build
gcloud builds submit \
--tag gcr.io/MY_PROJECT_ID/my-app:latest \
--source-dir . \
--no-cache
最佳实践
✅ DO
- 使用容器健康检查
- 设置适当的CPU和内存
- 实施优雅关闭
- 使用最小权限的服务账户
- 使用Cloud Logging进行监控
- 启用Cloud Armor进行保护
- 使用版本管理进行蓝绿部署
- 实施启动和存活探针
❌ DON’T
- 在代码中存储秘密
- 使用默认服务账户
- 创建有状态应用程序
- 忽略健康检查
- 未经测试即部署
- 使用过多的资源限制
- 在容器文件系统中存储文件
监控
- Cloud Logging用于应用程序日志
- Cloud Monitoring用于指标
- Error Reporting用于错误跟踪
- Cloud Trace用于分布式跟踪
- 版本指标和分析