Trong bài viết này, tôi sẽ chia sẻ cách tôi sử dụng Pulumi để quản lý hạ tầng API AI một cách có hệ thống, thay vì thủ công qua giao diện cloud console. Sau hơn 2 năm vận hành nền tảng AI production với hàng triệu request mỗi ngày, tôi nhận ra rằng Infrastructure as Code (IaC) không chỉ là best practice — mà là yêu cầu bắt buộc khi bạn cần scale nhanh, rollback an toàn, và đảm bảo tính nhất quán giữa môi trường dev, staging, và production.
Tại sao chọn Pulumi thay vì Terraform?
Trước khi đi vào code, tôi muốn giải thích ngắn gọn lý do tôi chọn Pulumi. Sau khi dùng thử cả Terraform và Pulumi cho dự án riêng, đây là những gì tôi rút ra:
- Ngôn ngữ lập trình thực tế: Pulumi hỗ trợ TypeScript, Python, Go, C#, Java — thay vì phải học HCL (HashiCorp Configuration Language) của Terraform. Đội ngũ của tôi quen thuộc hơn với TypeScript nên việc debug và refactor dễ dàng hơn rất nhiều.
- Type safety và IDE support: Với TypeScript, tôi có autocomplete đầy đủ, kiểm tra lỗi tại thời điểm viết code thay vì lúc apply. Điều này giúp tôi phát hiện sai sót cấu hình sớm, trước khi tạo ra tài nguyên cloud thực sự.
- Kế thừa logic lập trình: Vòng lặp, điều kiện, hàm tái sử dụng — tất cả đều hoạt động như code thông thường. Tôi đã tạo module chia sẻ giữa các dự án, giảm 60% code trùng lặp so với cách viết Terraform module.
- State management đơn giản: Pulumi tự động quản lý state, có thể lưu trữ trên cloud storage (S3, GCS, Azure Blob) hoặc dùng Pulumi Service miễn phí cho team nhỏ.
Kiến trúc Hạ tầng AI API
Trước khi viết code, hãy xác định rõ kiến trúc mà chúng ta sẽ triển khai. Với use case phổ biến — một ứng dụng web/giao diện cần gọi đến API AI — tôi thường thiết kế theo mô hình sau:
- API Gateway / Load Balancer: Phân phối request, rate limiting, SSL termination
- Backend Service: Xử lý logic nghiệp vụ, gọi AI API, caching
- Database: PostgreSQL cho dữ liệu persistent, Redis cho caching và session
- CDN / Edge: CloudFront/CloudFlare để cache response và giảm latency
- AI API Provider: HolySheep AI với chi phí thấp hơn 85% so với OpenAI, hỗ trợ thanh toán qua WeChat/Alipay — rất thuận tiện cho developers Trung Quốc và quốc tế
Cài đặt Pulumi và Thiết lập Dự án
Đầu tiên, tôi cài đặt Pulumi CLI và thiết lập dự án TypeScript. Toàn bộ quá trình mất khoảng 5 phút.
# Cài đặt Pulumi CLI trên macOS/Linux
curl -fsSL https://get.pulumi.com | sh
Hoặc cài qua Homebrew (macOS)
brew install pulumi
Kiểm tra phiên bản
pulumi version
Output: v3.115.0
Đăng nhập Pulumi (dùng local state hoặc Pulumi Service)
pulumi login
Tạo dự án mới
mkdir ai-api-infrastructure && cd ai-api-infrastructure
pulumi new typescript
Các câu hỏi khi tạo project:
- Project name: ai-api-infra
- Stack name: dev
- AWS region: us-east-1
- Description: AI API Infrastructure with Pulumi
Cài đặt các provider cần thiết
npm install @pulumi/pulumi @pulumi/aws @pulumi/awsx
Sau khi cài đặt, tôi kiểm tra cấu trúc thư mục và xác nhận mọi thứ hoạt động đúng:
# Kiểm tra cấu trúc dự án
ls -la ai-api-infrastructure/
Kết quả:
Pulumi.yaml
Pulumi.dev.yaml
tsconfig.json
package.json
index.ts
Chạy thử để đảm bảo Pulumi hoạt động
pulumi preview
Nếu thành công sẽ hiển thị "Previewing" với danh sách resources sẽ tạo
Triển khai Hạ tầng với Code Chi tiết
2.1. File Cấu hình Chính (index.ts)
Đây là file chính nơi tôi định nghĩa toàn bộ hạ tầng. Tôi đã tổ chức code theo module pattern để dễ bảo trì và mở rộng.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";
// ============================================
// CẤU HÌNH CHÍNH
// ============================================
const config = new pulumi.Config();
const environment = pulumi.getStack(); // dev | staging | production
const isProduction = environment === "production";
// HolySheep AI Configuration - Đây là AI API provider chính của tôi
// Chi phí chỉ bằng 15% so với OpenAI, hỗ trợ WeChat/Alipay
const holySheepConfig = {
baseUrl: "https://api.holysheep.ai/v1",
apiKey: config.requireSecret("holySheepApiKey"),
models: {
gpt41: "gpt-4.1",
claudeSonnet: "claude-sonnet-4.5",
geminiFlash: "gemini-2.5-flash",
deepseekV3: "deepseek-v3.2",
},
};
// Tags chuẩn cho toàn bộ tài nguyên
const commonTags = {
Project: "ai-api-platform",
Environment: environment,
ManagedBy: "Pulumi",
CostCenter: "engineering",
};
// ============================================
// VPC & NETWORKING
// ============================================
const vpc = new aws.ec2.Vpc("ai-vpc", {
cidrBlock: "10.0.0.0/16",
enableDnsHostnames: true,
enableDnsSupport: true,
tags: { ...commonTags, Name: ai-vpc-${environment} },
});
const publicSubnet = new aws.ec2.Subnet("ai-public-subnet", {
vpcId: vpc.id,
cidrBlock: "10.0.1.0/24",
availabilityZone: "us-east-1a",
mapPublicIpOnLaunch: true,
tags: { ...commonTags, Name: ai-public-subnet-${environment} },
});
const privateSubnet = new aws.ec2.Subnet("ai-private-subnet", {
vpcId: vpc.id,
cidrBlock: "10.0.2.0/24",
availabilityZone: "us-east-1a",
tags: { ...commonTags, Name: ai-private-subnet-${environment} },
});
const internetGateway = new aws.ec2.InternetGateway("ai-igw", {
vpcId: vpc.id,
tags: { ...commonTags, Name: ai-igw-${environment} },
});
const eip = new aws.ec2.Eip("nat-eip", {
domain: "vpc",
tags: { ...commonTags, Name: nat-eip-${environment} },
});
const natGateway = new aws.ec2.NatGateway("ai-nat", {
subnetId: publicSubnet.id,
allocationId: eip.id,
tags: { ...commonTags, Name: ai-nat-${environment} },
});
const privateRouteTable = new aws.ec2.RouteTable("ai-private-rt", {
vpcId: vpc.id,
routes: [
{
cidrBlock: "0.0.0.0/0",
natGatewayId: natGateway.id,
},
],
tags: { ...commonTags, Name: ai-private-rt-${environment} },
});
new aws.ec2.RouteTableAssociation("ai-private-rta", {
subnetId: privateSubnet.id,
routeTableId: privateRouteTable.id,
});
// ============================================
// SECURITY GROUPS
// ============================================
const albSecurityGroup = new aws.ec2.SecurityGroup("ai-alb-sg", {
vpcId: vpc.id,
description: "Security group for Application Load Balancer",
ingress: [
{
protocol: "tcp",
fromPort: 443,
toPort: 443,
cidrBlocks: ["0.0.0.0/0"],
description: "HTTPS from anywhere",
},
{
protocol: "tcp",
fromPort: 80,
toPort: 80,
cidrBlocks: ["0.0.0.0/0"],
description: "HTTP redirect to HTTPS",
},
],
egress: [
{
protocol: "-1",
fromPort: 0,
toPort: 0,
cidrBlocks: ["0.0.0.0/0"],
},
],
tags: { ...commonTags, Name: ai-alb-sg-${environment} },
});
const ecsSecurityGroup = new aws.ec2.SecurityGroup("ai-ecs-sg", {
vpcId: vpc.id,
description: "Security group for ECS Fargate tasks",
ingress: [
{
protocol: "tcp",
fromPort: 8080,
toPort: 8080,
securityGroups: [albSecurityGroup.id],
description: "Traffic from ALB",
},
],
egress: [
{
protocol: "-1",
fromPort: 0,
toPort: 0,
cidrBlocks: ["0.0.0.0/0"],
},
],
tags: { ...commonTags, Name: ai-ecs-sg-${environment} },
});
// ============================================
// ECS CLUSTER & SERVICES
// ============================================
const ecsCluster = new aws.ecs.Cluster("ai-cluster", {
name: ai-api-cluster-${environment},
settings: [
{
name: "containerInsights",
value: "enabled",
},
],
tags: { ...commonTags, Name: ai-cluster-${environment} },
});
const taskExecutionRole = new aws.iam.Role("ai-task-execution-role", {
name: ai-task-execution-${environment},
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Principal: { Service: ["ecs-tasks.amazonaws.com"] },
Action: "sts:AssumeRole",
},
],
}),
});
new aws.iam.RolePolicyAttachment("ai-task-execution-policy", {
role: taskExecutionRole.name,
policyArn: "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy",
});
// Task Role cho phép gọi HolySheep AI API
const taskRole = new aws.iam.Role("ai-task-role", {
name: ai-task-role-${environment},
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Principal: { Service: ["ecs-tasks.amazonaws.com"] },
Action: "sts:AssumeRole",
},
],
}),
});
new aws.iam.RolePolicy("ai-task-policy", {
name: ai-task-policy-${environment},
role: taskRole.name,
policy: pulumi.output(holySheepConfig.apiKey).apply(() => JSON.stringify({
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Action: ["execute-api:Invoke"],
Resource: ["arn:aws:execute-api:*:*:*"],
},
{
Effect: "Allow",
Action: ["secretsmanager:GetSecretValue"],
Resource: [arn:aws:secretsmanager:*:*:secret:holySheepApiKey-*],
},
],
})),
});
// ============================================
// APPLICATION LOAD BALANCER
// ============================================
const alb = new awsx.lb.ApplicationLoadBalancer("ai-alb", {
name: ai-alb-${environment},
securityGroups: [albSecurityGroup.id],
subnets: [publicSubnet.id],
defaultTargetGroup: {
port: 8080,
protocol: "HTTP",
healthCheck: {
enabled: true,
path: "/health",
interval: 30,
timeout: 5,
healthyThreshold: 2,
unhealthyThreshold: 3,
},
},
});
// ============================================
// DATABASE LAYER
// ============================================
const dbSubnetGroup = new aws.rds.SubnetGroup("ai-db-subnet-group", {
name: ai-db-subnet-${environment},
subnetIds: [privateSubnet.id],
tags: { ...commonTags, Name: ai-db-subnet-${environment} },
});
const dbParameterGroup = new aws.rds.ParameterGroup("ai-db-params", {
name: ai-db-params-${environment},
family: "postgres16",
parameters: {
"max_connections": "200",
"shared_buffers": (isProduction ? "512" : "128") + "MB",
"effective_cache_size": (isProduction ? "1536" : "384") + "MB",
},
tags: { ...commonTags, Name: ai-db-params-${environment} },
});
const dbInstance = new aws.rds.Instance("ai-postgres", {
identifier: ai-postgres-${environment},
engine: "postgres",
engineVersion: "16.2",
instanceClass: isProduction ? "db.r7g.large" : "db.t4g.medium",
allocatedStorage: isProduction ? 100 : 20,
maxAllocatedStorage: isProduction ? 500 : 100,
dbName: "aiplatform",
username: config.require("dbUsername"),
password: config.requireSecret("dbPassword"),
dbSubnetGroupName: dbSubnetGroup.name,
vpcSecurityGroupIds: [ecsSecurityGroup.id],
parameterGroupName: dbParameterGroup.name,
backupRetentionPeriod: isProduction ? 14 : 1,
skipFinalSnapshot: !isProduction,
deletionProtection: isProduction,
publiclyAccessible: false,
tags: { ...commonTags, Name: ai-postgres-${environment} },
});
// ============================================
// REDIS CACHE
// ============================================
const elasticacheSubnetGroup = new aws.elasticache.SubnetGroup("ai-redis-subnet", {
name: ai-redis-subnet-${environment},
subnetIds: [privateSubnet.id],
});
const redisCluster = new aws.elasticache.Cluster("ai-redis", {
clusterId: ai-redis-${environment},
engine: "redis",
engineVersion: "7.1",
nodeType: isProduction ? "cache.r7g.large" : "cache.t4g.micro",
numCacheNodes: isProduction ? 2 : 1,
parameterGroupName: "default.redis7",
port: 6379,
subnetGroupName: elasticacheSubnetGroup.name,
securityGroupIds: [ecsSecurityGroup.id],
tags: { ...commonTags, Name: ai-redis-${environment} },
});
// ============================================
// SECRETS MANAGER
// ============================================
const holySheepSecret = new aws.secretsmanager.Secret("holy-sheep-api-key", {
name: holySheepApiKey-${environment},
description: "HolySheep AI API Key for AI API Platform",
recoveryWindowInDays: isProduction ? 30 : 7,
tags: { ...commonTags, Name: holySheep-api-key-${environment} },
});
new aws.secretsmanager.SecretVersion("holy-sheep-api-key-value", {
secretId: holySheepSecret.id,
secretString: config.requireSecret("holySheepApiKeyRaw"),
});
// ============================================
// ECS TASK DEFINITION & SERVICE
// ============================================
const aiServiceTaskDefinition = new aws.ecs.TaskDefinition("ai-service", {
family: ai-service-${environment},
cpu: isProduction ? "1024" : "512",
memory: isProduction ? "2048" : "1024",
networkMode: "awsvpc",
requiresCompatibilities: ["FARGATE"],
executionRoleArn: taskExecutionRole.arn,
taskRoleArn: taskRole.arn,
containerDefinitions: pulumi.jsonStringify([
{
name: "ai-api-backend",
image: "ghcr.io/myorg/ai-api-backend:latest",
essential: true,
portMappings: [{ containerPort: 8080, protocol: "tcp" }],
environment: [
{ name: "NODE_ENV", value: environment },
{ name: "HOLYSHEEP_BASE_URL", value: holySheepConfig.baseUrl },
{ name: "DB_HOST", value: dbInstance.address },
{ name: "REDIS_HOST", value: redisCluster.cacheNodes[0].address },
{ name: "AWS_REGION", value: aws.getRegion().then(r => r.name) },
],
secrets: [
{
name: "HOLYSHEEP_API_KEY",
valueFrom: pulumi.interpolate${holySheepSecret.arn}:api_key::,
},
{
name: "DB_PASSWORD",
valueFrom: arn:aws:secretsmanager:*:*:secret:dbPassword-${environment}::,
},
],
logConfiguration: {
logDriver: "awslogs",
options: {
"awslogs-group": /ecs/ai-service-${environment},
"awslogs-region": aws.getRegion().then(r => r.name),
"awslogs-stream-prefix": "ecs",
},
},
healthCheck: {
command: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"],
interval: 30,
timeout: 5,
retries: 3,
startPeriod: 60,
},
},
]),
tags: { ...commonTags, Name: ai-service-task-${environment} },
});
// CloudWatch Log Group
const ecsLogGroup = new aws.cloudwatch.LogGroup("ai-ecs-logs", {
name: /ecs/ai-service-${environment},
retentionInDays: isProduction ? 30 : 7,
tags: { ...commonTags, Name: ai-ecs-logs-${environment} },
});
const aiService = new aws.ecs.Service("ai-service", {
cluster: ecsCluster.id,
name: ai-service-${environment},
taskDefinition: aiServiceTaskDefinition.arn,
desiredCount: isProduction ? 3 : 1,
launchType: "FARGATE",
healthCheckGracePeriodSeconds: 60,
loadBalancers: [
{
targetGroupArn