Files
2nd/10_Wiki/Topics/Coding/DevOps_Pulumi_IaC.md
T
2026-05-09 21:08:02 +09:00

7.2 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
devops-pulumi-iac Pulumi — 코드로 IaC (TS / Python / Go) Coding draft B conceptual 2026-05-09 2026-05-09
devops
pulumi
iac
vibe-coding
language applicable_to
TS / Python / Go
DevOps
Pulumi
IaC
CDK
AWS CDK
infrastructure as code
programmatic IaC

Pulumi

Terraform 의 코드 버전. TS / Python / Go / .NET 으로 IaC. AWS CDK 비슷 + multi-cloud. Loop / function / abstraction 자유.

📖 핵심 개념

  • 코드 = infra 정의.
  • State: Pulumi Cloud / S3 / 자체.
  • Stack: 환경별 (dev / prod).
  • Component: 재사용 unit.

💻 코드 패턴

시작

brew install pulumi
pulumi new typescript
# 또는 aws-typescript / azure-typescript / kubernetes-typescript

TS 예 — S3 + RDS

import * as aws from '@pulumi/aws';
import * as random from '@pulumi/random';

// S3
const bucket = new aws.s3.BucketV2('my-bucket', {
  bucket: 'my-bucket',
  tags: { Env: 'prod' },
});

new aws.s3.BucketServerSideEncryptionConfigurationV2('encrypt', {
  bucket: bucket.id,
  rules: [{ applyServerSideEncryptionByDefault: { sseAlgorithm: 'AES256' } }],
});

// RDS
const password = new random.RandomPassword('db-password', { length: 32 });

const db = new aws.rds.Instance('app-db', {
  engine: 'postgres',
  engineVersion: '16',
  instanceClass: 'db.t4g.micro',
  allocatedStorage: 20,
  storageEncrypted: true,
  username: 'app',
  password: password.result,
  skipFinalSnapshot: false,
  finalSnapshotIdentifier: 'app-db-final',
  deletionProtection: true,
});

export const dbEndpoint = db.endpoint;
export const bucketName = bucket.id;

명령

pulumi up         # plan + apply
pulumi up --yes   # 확인 없이
pulumi preview    # plan only
pulumi destroy
pulumi stack ls
pulumi stack output dbEndpoint

Stack (환경별)

pulumi stack init dev
pulumi stack init staging
pulumi stack init prod

pulumi stack select prod
pulumi up
# Pulumi.prod.yaml
config:
  aws:region: us-east-1
  myapp:dbInstanceClass: db.r6g.large
  myapp:replicaCount: 5
// Code
import * as pulumi from '@pulumi/pulumi';
const cfg = new pulumi.Config('myapp');
const instanceClass = cfg.require('dbInstanceClass');
const replicaCount = cfg.requireNumber('replicaCount');

Component (재사용)

class AppDatabase extends pulumi.ComponentResource {
  public readonly endpoint: pulumi.Output<string>;
  
  constructor(name: string, args: AppDatabaseArgs, opts?: pulumi.ComponentResourceOptions) {
    super('myco:db:AppDatabase', name, args, opts);
    
    const password = new random.RandomPassword(`${name}-pw`, { length: 32 }, { parent: this });
    
    const db = new aws.rds.Instance(`${name}-rds`, {
      engine: 'postgres',
      instanceClass: args.instanceClass,
      // ...
    }, { parent: this });
    
    new aws.secretsmanager.Secret(`${name}-secret`, { ... }, { parent: this });
    
    this.endpoint = db.endpoint;
    this.registerOutputs({ endpoint: this.endpoint });
  }
}

interface AppDatabaseArgs {
  instanceClass: string;
}

// 사용
const ordersDb = new AppDatabase('orders', { instanceClass: 'db.r6g.large' });
const inventoryDb = new AppDatabase('inventory', { instanceClass: 'db.t4g.medium' });

→ 재사용 + grouping.

Multi-cloud

import * as aws from '@pulumi/aws';
import * as gcp from '@pulumi/gcp';
import * as k8s from '@pulumi/kubernetes';

// AWS
const bucket = new aws.s3.BucketV2('logs');

// GCP
const gcsBucket = new gcp.storage.Bucket('analytics', { location: 'US' });

// K8s
const ns = new k8s.core.v1.Namespace('app', { metadata: { name: 'app' } });

→ 한 program 안 multi-cloud.

Output (async value)

const url = pulumi.interpolate`https://${db.endpoint}:5432/${dbName}`;

// 또는
const connection = pulumi.all([db.endpoint, password.result]).apply(([endpoint, pw]) => 
  `postgresql://app:${pw}@${endpoint}:5432/app`
);

→ Pulumi 의 lazy / async value handling.

Secret

const apiKey = cfg.requireSecret('apiKey');  // encrypted in state
pulumi config set --secret myapp:apiKey sk-abc123

Import (existing)

pulumi import aws:s3/bucket:Bucket existing my-existing-bucket

→ 기존 cloud 자원 → Pulumi 안 가져오기.

Drift detection

pulumi refresh    # cloud → state sync
pulumi up          # state → cloud sync

CI

- uses: pulumi/actions@v5
  with:
    command: up
    stack-name: prod
    cloud-url: s3://my-state-bucket

vs Terraform

Pulumi:
+ Real programming language (TS / Python)
+ Loop / function / abstraction
+ Test (Jest)
+ Type-safe (TS)
- Smaller community
- Newer

Terraform:
+ HCL (declarative, simpler 작은 case)
+ Largest community
+ Module marketplace
- HCL 의 한계 (loop, complex logic)

→ 큰 / 복잡 = Pulumi. 단순 / 표준 = Terraform.

Test

import { describe, it } from '@jest/globals';
import * as pulumi from '@pulumi/pulumi';

pulumi.runtime.setMocks({
  newResource: (args) => ({
    id: `${args.name}-id`,
    state: { ...args.inputs, id: `${args.name}-id` },
  }),
  call: () => ({}),
});

describe('infrastructure', () => {
  it('creates encrypted bucket', async () => {
    const infra = await import('./index');
    const bucket = infra.bucket;
    const sseConfig = await new Promise((resolve) => bucket.serverSideEncryptionConfiguration.apply(resolve));
    expect(sseConfig).toBeDefined();
  });
});

→ 일반 unit test 처럼.

Component packages (sharing)

# Component 를 npm package 로
yarn publish
// 다른 곳
import { AppDatabase } from '@myco/pulumi-components';
const db = new AppDatabase('orders', {...});

Crossplane vs Pulumi

Crossplane: K8s 안 cloud manage.
Pulumi:     Code 로 cloud manage.

→ K8s native = Crossplane. Code-first = Pulumi.

AWS CDK

// CDK = AWS only Pulumi-like
import { Stack } from 'aws-cdk-lib';
import { Bucket } from 'aws-cdk-lib/aws-s3';

class MyStack extends Stack {
  constructor(...) {
    super(...);
    new Bucket(this, 'MyBucket');
  }
}

→ AWS only. AWS deeply 통합.

Best practices

1. State = remote (Pulumi Cloud / S3).
2. Stack 별 환경 분리.
3. Component 로 재사용.
4. Test (mock).
5. CI 자동.
6. Secret 명시 (encrypted state).
7. Import existing 가능.
8. Drift 정기 detect.

🤔 의사결정 기준

상황 추천
Code-first IaC Pulumi
AWS only Pulumi 또는 CDK
일반 / 표준 Terraform / OpenTofu
K8s 중심 Crossplane
Multi-cloud Pulumi
TS team Pulumi (자연)
Module marketplace Terraform

안티패턴

  • State local file: 잃으면 disaster. Remote.
  • Stack mix (dev / prod 한 곳): 분리.
  • Secret plain config: requireSecret.
  • Drift 무 detect: 콘솔 변경 → 다음 up 가 덮음.
  • Component 없이 copy-paste: 코드 폭발.
  • Test 없이 prod: 위험.
  • Outputs share 안 함: 다른 stack 가 reference 못 함.

🤖 LLM 활용 힌트

  • TS = type-safe IaC.
  • Component 적극.
  • Stack per env.
  • Pulumi Cloud free tier.

🔗 관련 문서