Skip to content

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.json

ComponentResource 패턴

각 인프라 구성 요소는 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");

적용되는 태그:

태그
Environmentdev / prod
ManagedByPulumi
Projecttruloop
Stackdev / 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타입설명
vpcIdstringVPC ID
vpcCidrstringVPC CIDR Block
publicSubnetIdsstring[]Public Subnet IDs
privateSubnetIdsstring[]Private Subnet IDs
albSecurityGroupIdstringALB Security Group ID
ecsClusterArnstringECS Cluster ARN
ecsClusterNamestringECS Cluster 이름
albArnstringALB ARN
albDnsNamestringALB DNS 이름
albZoneIdstringALB Hosted Zone ID
httpListenerArnstringHTTP Listener ARN
httpsListenerArnstringHTTPS Listener ARN
albCertificateArnstringACM Certificate ARN
taskRoleArnstringECS Task Role ARN
executionRoleArnstringECS Execution Role ARN
serviceDiscoveryNamespaceIdstringService Discovery Namespace ID
serviceDiscoveryNamespaceArnstringService Discovery Namespace ARN
s3BucketsRecord<string, string>S3 Bucket IDs
hostedZoneIdsRecord<string, string>Route53 Zone IDs
vpcEndpointIdsRecord<string, string>VPC Endpoint IDs (SSM)
vpcEndpointsSecurityGroupIdstringVPC Endpoints Security Group ID
ssmBastionSecurityGroupIdstringSSM Bastion Security Group ID
ssmBastionInstanceIdstringSSM Bastion EC2 Instance ID

Legacy Outputs

하위 호환을 위해 유지되는 그룹화된 outputs입니다.

Output설명
computeecsClusterName, ecsClusterArn
networkingalbDnsName, albZoneId, targetGroups, securityGroups, listeners
dnshostedZones, records
iamroles.ecsTask, roles.ecsExecution, pulumiEscRoleArn
assetsCdncloudFrontId, cloudFrontDomainName, cloudFrontArn, cloudFrontUrl
ssmJumpSecurityGroupIdssmBastionSecurityGroupId의 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-12Dev config에 이미지 그룹핑 설정 추가 (grouping apiBaseUrl, groupingInternalApiKey)
2026-03-10소스 코드 검증: Stack Reference outputs 테이블 대폭 보강 (Primary + Legacy 구분), 프로젝트 구조 상세 주석 추가, devDependencies 추가, constants.ts에 PORTS 추가, auto-tagging 제외 리소스 목록 갱신