- บทนำ
- 1. สถาปัตยกรรม
- 2. สิ่งที่ต้องเตรียม
- 3. สร้างโปรเจกต์ Pulumi
- 4. เขียน Infrastructure ใน index.ts
- 5. Deploy
- 6. ตรวจสอบผ่าน AWS CLI
- 7. แก้ไขและอัปเดต
- 8. ลบทรัพยากรเพื่อหยุดค่าใช้จ่าย
- 9. แนวทางสำหรับ Production
- 10. สรุป
- เอกสารอ้างอิง
#บทนำ
Pulumi เป็นเครื่องมือ Infrastructure as Code (IaC) ที่ช่วยสร้างและจัดการ Cloud Infrastructure ด้วยภาษาโปรแกรม เช่น TypeScript, Python, Go และ C# แทนการกำหนดโครงสร้างพื้นฐานด้วยไฟล์คอนฟิกเพียงอย่างเดียว บทความนี้ใช้ TypeScript เพื่อสร้าง AWS EC2 พร้อม Security Group และติดตั้ง Nginx ผ่าน User Data
ข้อควรระวัง: AWS EC2, EBS และทรัพยากรเครือข่ายบางชนิดอาจมีค่าใช้จ่าย แม้จะทดลองช่วงสั้น ๆ ควรตรวจสอบราคาและสิทธิ Free Tier ของบัญชีก่อนเริ่ม และลบทรัพยากรเมื่อเสร็จสิ้น
#1. สถาปัตยกรรม
flowchart LR
Dev[Developer] --> CLI[Pulumi CLI]
CLI --> State[Pulumi State Backend]
CLI --> AWS[AWS Provider]
AWS --> SG[Security Group: TCP 80]
AWS --> EC2[EC2 Instance]
SG --> EC2
EC2 --> Nginx[Nginx Web Server]
Browser[Web Browser] -->|HTTP 80| Nginx
ตัวอย่างนี้ใช้ Default VPC ของ Region ที่กำหนด และเปิดเฉพาะ HTTP TCP/80 สู่สาธารณะ ไม่เปิด SSH เพื่อจำกัดพื้นผิวการโจมตี เหมาะกับการสาธิตเท่านั้น; ระบบจริงควรใช้ HTTPS, Load Balancer และการจัดการเครือข่ายที่เหมาะสม
#2. สิ่งที่ต้องเตรียม
- บัญชี AWS และ IAM credentials ที่มีสิทธิสร้าง/ลบ EC2, Security Group และอ่านข้อมูล VPC/AMI
- AWS CLI และ Pulumi CLI
- Node.js LTS และ npm
- AWS Region ที่มี Default VPC เช่น
ap-southeast-1(ตรวจสอบในบัญชีจริง)
ตั้งค่า credentials ผ่าน AWS CLI:
aws configure --profile pulumi-lab
export AWS_PROFILE=pulumi-lab
aws sts get-caller-identity
บน Windows PowerShell ใช้ $env:AWS_PROFILE="pulumi-lab" แทน export
#3. สร้างโปรเจกต์ Pulumi
mkdir pulumi-aws-ec2 && cd pulumi-aws-ec2
pulumi new aws-typescript
npm install
pulumi config set aws:region ap-southeast-1
เมื่อระบบถามให้เลือก Stack เช่น dev และเลือก Pulumi Cloud หรือ State Backend ที่องค์กรกำหนด หลีกเลี่ยงการเก็บไฟล์ State ที่มีข้อมูลอ่อนไหวใน Repository สาธารณะ
#4. เขียน Infrastructure ใน index.ts
import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";
const config = new pulumi.Config();
const instanceType = config.get("instanceType") || "t3.micro";
const defaultVpc = aws.ec2.getVpc({ default: true });
const defaultSubnets = defaultVpc.then(vpc =>
aws.ec2.getSubnets({
filters: [{ name: "vpc-id", values: [vpc.id] }],
})
);
const webSecurityGroup = new aws.ec2.SecurityGroup("web-sg", {
vpcId: defaultVpc.then(vpc => vpc.id),
description: "Allow HTTP for Pulumi workshop",
ingress: [{
protocol: "tcp",
fromPort: 80,
toPort: 80,
cidrBlocks: ["0.0.0.0/0"],
description: "Public HTTP demo",
}],
egress: [{
protocol: "-1",
fromPort: 0,
toPort: 0,
cidrBlocks: ["0.0.0.0/0"],
}],
});
const ami = aws.ec2.getAmi({
mostRecent: true,
owners: ["amazon"],
filters: [
{ name: "name", values: ["al2023-ami-2023.*-x86_64"] },
{ name: "architecture", values: ["x86_64"] },
{ name: "virtualization-type", values: ["hvm"] },
],
});
const web = new aws.ec2.Instance("web-server", {
ami: ami.then(result => result.id),
instanceType,
subnetId: defaultSubnets.then(s => s.ids[0]),
vpcSecurityGroupIds: [webSecurityGroup.id],
associatePublicIpAddress: true,
rootBlockDevice: { volumeSize: 8, volumeType: "gp3" },
userData: `#!/bin/bash
set -euxo pipefail
dnf install -y nginx
systemctl enable --now nginx
cat > /usr/share/nginx/html/index.html <<'HTML'
<!doctype html>
<html lang="en"><meta charset="utf-8"><title>Pulumi EC2</title>
<body><h1>Hello from Pulumi + AWS EC2!</h1></body></html>
HTML
`,
tags: { Name: "pulumi-workshop-web", Environment: "dev" },
});
export const instanceId = web.id;
export const publicIp = web.publicIp;
export const websiteUrl = pulumi.interpolate`http://${web.publicIp}`;
หมายเหตุ: AMI ของ Amazon Linux 2023 และชนิดอินสแตนซ์ต้องรองรับ Region ที่เลือก การเลือก AMI ล่าสุดอาจทำให้ Pulumi เสนอเปลี่ยนอินสแตนซ์ในอนาคต ควรตรึง AMI ID สำหรับระบบ Production; ตัวอย่างนี้ใช้ Default Subnet แรกเพื่อความง่าย ไม่รับประกันความพร้อมใช้งานทุกบัญชี
#5. Deploy
pulumi config set instanceType t3.micro
pulumi preview
pulumi up
ตรวจสอบรายการทรัพยากรและค่าใช้จ่ายก่อนยืนยัน หลัง Deploy เสร็จ:
pulumi stack output websiteUrl
curl "$(pulumi stack output websiteUrl)"
Nginx อาจใช้เวลา 1–3 นาทีในการติดตั้งหลัง EC2 เริ่มทำงาน
#6. ตรวจสอบผ่าน AWS CLI
INSTANCE_ID=$(pulumi stack output instanceId)
aws ec2 describe-instances \
--instance-ids "$INSTANCE_ID" \
--query 'Reservations[].Instances[].[InstanceId,State.Name,PublicIpAddress]' \
--output table
#7. แก้ไขและอัปเดต
ลองเปลี่ยนข้อความ HTML ใน userData แล้วเรียก:
pulumi preview
pulumi up
ข้อควรทราบ: การแก้ไข User Data ไม่ได้รับประกันว่าจะรันสคริปต์อีกครั้งบนอินสแตนซ์เดิม การตั้งค่า User Data อาจทำให้ EC2 หยุด/เริ่มใหม่หรือถูกแทนที่ตาม Provider และการเปลี่ยนแปลงที่เกิดขึ้น สำหรับ Production ควรใช้ Image Baking, Configuration Management หรือกลไก Deployment ของแอปพลิเคชัน
#8. ลบทรัพยากรเพื่อหยุดค่าใช้จ่าย
pulumi destroy
pulumi stack rm
ตรวจสอบใน AWS Console ว่า EC2, EBS Volume และ Security Group ของ Workshop ถูกลบแล้ว และตรวจสอบ Billing ด้วย
#9. แนวทางสำหรับ Production
- ใช้ Private Subnet และ ALB พร้อม HTTPS/ACM แทนการเปิด HTTP ตรงสู่ EC2
- ใช้ IAM Role สำหรับ EC2 แทนการฝังคีย์ใน Source Code
- ใช้ SSM Session Manager แทนการเปิด SSH สู่ Internet
- กำหนด Tags, Budget Alerts, Logging และ Monitoring ด้วย CloudWatch
- ใช้ Stack แยก
dev,staging,prodและ CI/CD ตรวจpulumi previewก่อน Deploy - ตรวจสอบ Policy, Secrets และการควบคุมสิทธิของ Pulumi State Backend
#10. สรุป
Pulumi ช่วยจัดการ AWS Infrastructure แบบประกาศสถานะที่ต้องการ (Declarative) ผ่าน TypeScript โดย Workshop นี้สาธิตการสร้าง EC2, Security Group, การติดตั้ง Nginx, การส่งออก URL และการลบทรัพยากรอย่างเป็นระบบ เหมาะสำหรับต่อยอดสู่การ Deploy แอปพลิเคชันและ CI/CD
#เอกสารอ้างอิง
- Pulumi AWS: https://www.pulumi.com/registry/packages/aws/
- Pulumi AWS TypeScript Tutorial: https://www.pulumi.com/docs/iac/get-started/aws/
- AWS EC2 Documentation: https://docs.aws.amazon.com/ec2/
- AWS Pricing: https://aws.amazon.com/ec2/pricing/