在 Azure Kubernetes 上实现成本高效的微服务平台
Source: Dev.to
Scope and Assumptions
This post assumes:
- Familiarity with Kubernetes fundamentals
- Comfort reading Terraform and Helm
- Interest in operating systems, not just deploying them
The platform runs on Azure Kubernetes Service, provisioned with Terraform, and deployed using Helm.
1. AKS 基准:小规模起步,按需扩展
最常见的 AKS 成本错误是为峰值负载预配资源。
我们改为:
- 从最小基线容量开始
- 启用集群自动伸缩器
- 让需求决定节点数量
带自动伸缩的 AKS 集群
resource "azurerm_kubernetes_cluster" "aks" {
name = var.cluster_name
location = var.location
resource_group_name = var.resource_group_name
dns_prefix = var.cluster_name
default_node_pool {
name = "default"
vm_size = "Standard_D2s_v5"
auto_scaling_enabled = true
min_count = 1
max_count = 10
}
identity {
type = "SystemAssigned"
}
}
为什么这样有效
- 空闲成本保持低
- 只有在 pod 等待时才会添加节点
- 容量匹配实际需求,而非估算
2. 可预测行为的水平 Pod 自动伸缩
自动伸缩的默认设置过于激进且常常不稳定。
我们显式地调优伸缩行为,以降低抖动和延迟峰值。
带稳定性的 HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-service
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300
关键结果
- 防止在短暂流量下降期间快速缩容
- 改善尾部延迟
- 减少不必要的 Pod 重启
3. Spot节点池用于容错工作负载
Spot容量是最高杠杆的成本优化手段之一——前提是正确隔离。
Terraform:Spot节点池
resource "azurerm_kubernetes_cluster_node_pool" "spot" {
name = "spot"
kubernetes_cluster_id = azurerm_kubernetes_cluster.aks.id
vm_size = "Standard_D2as_v5"
priority = "Spot"
eviction_policy = "Delete"
spot_max_price = -1
auto_scaling_enabled = true
min_count = 0
max_count = 10
node_taints = [
"kubernetes.azure.com/scalesetpriority=spot:NoSchedule"
]
}
在 Spot 节点上调度工作者
tolerations:
- key: "kubernetes.azure.com/scalesetpriority"
operator: "Equal"
value: "spot"
effect: "NoSchedule"
nodeSelector:
kubernetes.azure.com/scalesetpriority: spot
我们遵循的规则
- API 永不在 Spot 上运行
- 工作者能够干净地处理
SIGTERM - 所有状态都存放在工作者之外
以这种方式使用时,Spot 容量在不影响用户体验的前提下实现了显著的成本节约。
4. 使用私有网络的托管 PostgreSQL
Databases are not where cost experiments belong.
PostgreSQL runs as a managed service with:
- 子网委派
- 私有 DNS
- 无公共访问
PostgreSQL 的委派子网
resource "azurerm_subnet" "postgres" {
name = "postgres-subnet"
virtual_network_name = azurerm_virtual_network.main.name
resource_group_name = var.resource_group_name
address_prefixes = ["10.0.2.0/24"]
delegation {
name = "postgres"
service_delegation {
name = "Microsoft.DBforPostgreSQL/flexibleServers"
}
}
}
这为何重要
- 数据库无法从公共互联网访问
- 访问在网络层受到限制
- 运营风险显著降低
5. 使用 Helm 默认进行安全部署
Helm charts 在编写时采用了 安全默认 的假设。
Pod 安全上下文
securityContext:
runAsUser: 1000
runAsGroup: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
这会立即:
- 减少攻击面
- 防止运行时变更
- 及早暴露不安全的镜像
关键健康探针
livenessProbe:
httpGet:
path: /health/db-cache
port: 8080
initialDelaySeconds: 60
我们有意检查 依赖关系,而不仅仅是进程健康。
6. 工作负载身份:在 Kubernetes 中不使用 Secrets
在 Kubernetes Secrets 中存储云凭证是没有必要的。
我们使用 Workload Identity 实现 pod 与 Azure 的身份验证。
联合身份凭证
resource "azurerm_federated_identity_credential" "api" {
name = "api-federated"
parent_id = azurerm_user_assigned_identity.api.id
issuer = azurerm_kubernetes_cluster.aks.oidc_issuer_url
subject = "system:serviceaccount:default:api"
audiences = ["api://AzureADTokenExchange"]
}
7. 观察性无需基于摄取的定价
而不是使用托管日志摄取,我们使用:
- Prometheus 用于指标
- Loki 用于日志
- 对象存储 用于保留
Loki 存储配置
storage_config:
azure:
container_name: logs
account_name: ${ACCOUNT_NAME}
access_tier: Cool
为什么这样可行
- 日志查询频率低
- 存储成本低廉
- 摄取成本在托管观察性定价中占主导
8. 基础设施访问模式(安全且实用)
集群访问
Azure AD‑支持的 kubectl
- 可审计
- 无共享凭证
在实践中,cluster‑admin 访问仅限于一个小的引导组;大多数团队使用命名空间范围的角色。
数据库访问(偶尔的管理员任务)
kubectl run psql \
--image=postgres:16 \
--rm -it -- \
psql -h <private‑endpoint> -U admin
数据库保持私有;访问经过身份验证且可审计。
可观测性仪表盘
Grafana 通过 Azure AD 使用 OAuth 进行保护。
OAuth2‑Proxy 运行多个副本并轮换 cookie 密钥,以避免成为单点故障。
- 无需 VPN
- 无需堡垒主机
- 无需额外的托管服务
Operational Lessons (The Real Ones)
-
Defaults Are Rarely Production‑Safe
自动伸缩、探针和安全上下文需要显式调优。 -
Spot Capacity Requires Discipline
它的效果非常好——但仅在隔离的情况下。 -
Identity Scales Better Than Secrets
它降低了运维负载和安全风险。 -
Kubernetes Is a Tool, Not a Destination
在它能提供杠杆作用的地方使用它——不要把它当作垃圾场。
结束思考
此实现并非关于巧妙的技巧——而是关于 有意的权衡。
通过:
- 仅在需要时进行扩展
- 负责任地使用 Spot 容量
- 保持关键状态的管理
- 避免基于摄取的可观测性成本
- 将安全视为默认
我们最终得到一个系统,它是:
- 可预测的运行
- 空闲时成本高效
- 在负载下具备弹性
- 易于演进
架构指明方向。
实现决定它是否能经受现实的考验。
