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

324 lines
7.2 KiB
Markdown

---
id: devops-pulumi-iac
title: Pulumi — 코드로 IaC (TS / Python / Go)
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [devops, pulumi, iac, vibe-coding]
tech_stack: { language: "TS / Python / Go", applicable_to: ["DevOps"] }
applied_in: []
aliases: [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.
## 💻 코드 패턴
### 시작
```bash
brew install pulumi
pulumi new typescript
# 또는 aws-typescript / azure-typescript / kubernetes-typescript
```
### TS 예 — S3 + RDS
```ts
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;
```
### 명령
```bash
pulumi up # plan + apply
pulumi up --yes # 확인 없이
pulumi preview # plan only
pulumi destroy
pulumi stack ls
pulumi stack output dbEndpoint
```
### Stack (환경별)
```bash
pulumi stack init dev
pulumi stack init staging
pulumi stack init prod
pulumi stack select prod
pulumi up
```
```yaml
# Pulumi.prod.yaml
config:
aws:region: us-east-1
myapp:dbInstanceClass: db.r6g.large
myapp:replicaCount: 5
```
```ts
// Code
import * as pulumi from '@pulumi/pulumi';
const cfg = new pulumi.Config('myapp');
const instanceClass = cfg.require('dbInstanceClass');
const replicaCount = cfg.requireNumber('replicaCount');
```
### Component (재사용)
```ts
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
```ts
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)
```ts
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
```ts
const apiKey = cfg.requireSecret('apiKey'); // encrypted in state
```
```bash
pulumi config set --secret myapp:apiKey sk-abc123
```
### Import (existing)
```bash
pulumi import aws:s3/bucket:Bucket existing my-existing-bucket
```
→ 기존 cloud 자원 → Pulumi 안 가져오기.
### Drift detection
```bash
pulumi refresh # cloud → state sync
pulumi up # state → cloud sync
```
### CI
```yaml
- 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
```ts
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)
```bash
# Component 를 npm package 로
yarn publish
```
```ts
// 다른 곳
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
```ts
// 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.
## 🔗 관련 문서
- [[DevOps_Terraform_Patterns]]
- [[DevOps_Crossplane_Tekton]]
- [[DevOps_ArgoCD_GitOps]]