다크 모드
Pulumi IaC
프로젝트 설정
| 항목 | 값 |
|---|---|
| 프로젝트명 | truloop-infra |
| 런타임 | Node.js (TypeScript) |
| 백엔드 | Pulumi Cloud (butbeautiful 조직) |
| 스택 | dev, prod |
| AWS 리전 | ap-northeast-2 |
의존성
json
{
"dependencies": {
"@pulumi/aws": "^7.16.0",
"@pulumi/awsx": "^3.1.0",
"@pulumi/pulumi": "^3.215.0"
},
"devDependencies": {
"@biomejs/biome": "^2.3.11",
"@types/node": "^22",
"typescript": "^5.9.3"
}
}프로젝트 구조
truloop-infra/
├── index.ts # 메인 엔트리 포인트
├── Pulumi.yaml # 프로젝트 설정 (공통)
├── Pulumi.dev.yaml # Dev 환경 설정
├── Pulumi.prod.yaml # Prod 환경 설정
├── src/
│ ├── constants.ts # 리소스 네이밍 상수 (RESOURCE_NAMES, PORTS)
│ ├── components/ # ComponentResource 클래스
│ │ ├── networking/
│ │ │ ├── vpc.ts # VPC, Subnet, ALB SG, NAT, Service Discovery
│ │ │ ├── alb.ts # ALB, Listener, ACM Certificate, Grafana TG
│ │ │ ├── vpc-endpoints.ts # VPC Endpoints (SSM)
│ │ │ └── ssm-bastion.ts # SSM Bastion (EC2 t4g.nano)
│ │ ├── compute/
│ │ │ └── ecs-cluster.ts # ECS Cluster (Service Connect)
│ │ ├── storage/
│ │ │ └── buckets.ts # S3 Buckets (BucketV2)
│ │ ├── cdn/
│ │ │ └── assets-cdn.ts # CloudFront (OAC + ACM us-east-1)
│ │ ├── dns/
│ │ │ ├── constants.ts # DNS 레코드/도메인 상수 (환경별)
│ │ │ └── records.ts # Route53 Hosted Zones + Records
│ │ └── security/
│ │ ├── ecs-roles.ts # ECS Task/Execution Role (기존 Role 참조)
│ │ └── oidc-roles.ts # Pulumi ESC OIDC Role
│ ├── transforms/
│ │ └── auto-tagging.ts # 자동 태깅 Transform
│ └── utils/
│ ├── config.ts # Config 로더 + 헬퍼 (stackName, isProduction)
│ └── aliases.ts # Resource Alias 관리 (ComponentResource 마이그레이션)
├── biome.json # Biome 설정 (포매터/린터)
├── tsconfig.json # TypeScript 설정
├── renovate.json # Renovate 자동 업데이트 설정
└── package.jsonComponentResource 패턴
각 인프라 구성 요소는 Pulumi의 ComponentResource를 상속받아 구현됩니다. 이를 통해 관련 리소스를 논리적으로 그룹화하고, 입출력을 명확히 정의합니다.
typescript
export class Vpc extends pulumi.ComponentResource {
public readonly vpcId: pulumi.Output<string>;
public readonly publicSubnetIds: pulumi.Output<string>[];
// ...
constructor(name: string, args: VpcArgs, opts?: pulumi.ComponentResourceOptions) {
super("infra:networking:Vpc", name, args, opts);
// 리소스 생성...
this.registerOutputs({ vpcId: this.vpcId });
}
}컴포넌트 의존성 그래프
자동 태깅
registerAutoTagging Transform을 사용하여 모든 AWS 리소스에 표준 태그를 자동 적용합니다.
typescript
// src/transforms/auto-tagging.ts
registerAutoTagging(stackName, "truloop");적용되는 태그:
| 태그 | 값 |
|---|---|
Environment | dev / prod |
ManagedBy | Pulumi |
Project | truloop |
Stack | dev / prod |
정보
태그를 지원하지 않는 AWS 리소스는 자동으로 제외됩니다: RolePolicyAttachment, Route53 Record, SecurityGroupRule, OriginAccessControl, ListenerRule, Service (ServiceDiscovery), OpenIdConnectProvider, BucketPolicy, CertificateValidation
리소스 네이밍 규칙
src/constants.ts에서 일관된 리소스 이름을 정의합니다.
typescript
export const RESOURCE_NAMES = {
vpc: `truloop-${stackName}-vpc`,
igw: `truloop-${stackName}-igw`,
natGateway: `truloop-${stackName}-nat-gateway`,
albSecurityGroup: `truloop-alb-${stackName}`,
alb: `truloop-${stackName}-alb`,
ecsCluster: `truloop-${stackName}`,
serviceDiscoveryNamespace: `truloop-${stackName}.local`,
} as const;
export const PORTS = {
http: 80,
https: 443,
} as const;Stack 구성
Config YAML
환경별 차이는 Pulumi.{env}.yaml에서 관리합니다. 코드에는 환경 공통 로직만 작성합니다.
yaml
config:
aws:region: ap-northeast-2
truloop-infra:project:
name: truloop
truloop-infra:vpc:
cidr: 10.0.0.0/16
truloop-infra:storage:
s3:
main: truloop-dev-bucket
configs: truloop-configs-dev
images: truloop-images-dev
assets: truloop-assets-dev
truloop-infra:grouping:
apiBaseUrl: https://dev-api.truloop.app
truloop-infra:groupingInternalApiKey:
secure: <encrypted>
environment:
- truloop/infra-dev정보
Dev 환경에는 configs, images 버킷이 추가로 존재합니다. Prod에서는 필요한 최소 버킷만 관리합니다.
ESC 환경 연결
각 스택의 environment 필드에서 Pulumi ESC 환경을 참조합니다. ESC 환경이 OIDC를 통해 AWS 자격증명을 자동 제공합니다.
Stack Reference (Outputs)
index.ts에서 다른 서비스가 참조할 수 있는 outputs를 내보냅니다.
Primary Outputs
| Output | 타입 | 설명 |
|---|---|---|
vpcId | string | VPC ID |
vpcCidr | string | VPC CIDR Block |
publicSubnetIds | string[] | Public Subnet IDs |
privateSubnetIds | string[] | Private Subnet IDs |
albSecurityGroupId | string | ALB Security Group ID |
ecsClusterArn | string | ECS Cluster ARN |
ecsClusterName | string | ECS Cluster 이름 |
albArn | string | ALB ARN |
albDnsName | string | ALB DNS 이름 |
albZoneId | string | ALB Hosted Zone ID |
httpListenerArn | string | HTTP Listener ARN |
httpsListenerArn | string | HTTPS Listener ARN |
albCertificateArn | string | ACM Certificate ARN |
taskRoleArn | string | ECS Task Role ARN |
executionRoleArn | string | ECS Execution Role ARN |
serviceDiscoveryNamespaceId | string | Service Discovery Namespace ID |
serviceDiscoveryNamespaceArn | string | Service Discovery Namespace ARN |
s3Buckets | Record<string, string> | S3 Bucket IDs |
hostedZoneIds | Record<string, string> | Route53 Zone IDs |
vpcEndpointIds | Record<string, string> | VPC Endpoint IDs (SSM) |
vpcEndpointsSecurityGroupId | string | VPC Endpoints Security Group ID |
ssmBastionSecurityGroupId | string | SSM Bastion Security Group ID |
ssmBastionInstanceId | string | SSM Bastion EC2 Instance ID |
Legacy Outputs
하위 호환을 위해 유지되는 그룹화된 outputs입니다.
| Output | 설명 |
|---|---|
compute | ecsClusterName, ecsClusterArn |
networking | albDnsName, albZoneId, targetGroups, securityGroups, listeners |
dns | hostedZones, records |
iam | roles.ecsTask, roles.ecsExecution, pulumiEscRoleArn |
assetsCdn | cloudFrontId, cloudFrontDomainName, cloudFrontArn, cloudFrontUrl |
ssmJumpSecurityGroupId | ssmBastionSecurityGroupId의 alias (하위 호환) |
Import 워크플로우
기존 AWS 리소스를 Pulumi State에 추가하는 절차입니다.
리소스 식별
AWS CLI로 기존 리소스의 ID/ARN을 확인합니다.
코드 작성
new 키워드로 리소스를 정의하고, 속성을 AWS 실제 값과 일치시킵니다.
Import 실행
bash
pulumi import aws:ec2/vpc:Vpc vpc vpc-0ddd834af5d7924a5검증
bash
pulumi preview # 0 changes 확인주요 명령어
bash
# Pulumi Cloud 로그인
pulumi login
# 스택 선택
pulumi stack select dev # 또는 prod
# 변경사항 미리보기
pulumi preview
# 변경사항 적용
pulumi up
# 상태 새로고침 (AWS와 동기화)
pulumi refresh
# 코드 품질 (Biome)
npx biome check --write변경 이력
| 날짜 | 변경 내용 |
|---|---|
| 2026-03-12 | Dev config에 이미지 그룹핑 설정 추가 (grouping apiBaseUrl, groupingInternalApiKey) |
| 2026-03-10 | 소스 코드 검증: Stack Reference outputs 테이블 대폭 보강 (Primary + Legacy 구분), 프로젝트 구조 상세 주석 추가, devDependencies 추가, constants.ts에 PORTS 추가, auto-tagging 제외 리소스 목록 갱신 |