- เป้าหมายของ Workshop
- 1. Twelve-Factor App คืออะไร?
- Workshop: Twelve-Factor REST API
- 2. เตรียม Project
- 3. Factor I --- Codebase
- 4. Factor II --- Dependencies
- 5. Factor III --- Config
- 6. Factor IV --- Backing Services
- 7. Factor V --- Build, Release, Run
- 8. Factor VI --- Processes
- 9. Factor VII --- Port Binding
- 10. Factor VIII --- Concurrency
- 11. Factor IX --- Disposability
- 12. Factor X --- Dev/Prod Parity
- 13. Factor XI --- Logs
- 14. Factor XII --- Admin Processes
- 15. ทดสอบระบบ
- 16. Twelve-Factor กับ Kubernetes
- 17. แบบฝึกหัด
- 18. Checklist สำหรับตรวจ Application
- 19. สรุป
Workshop สำหรับเรียนรู้หลักการ The Twelve-Factor App ผ่านการออกแบบและปรับปรุง REST API ให้เหมาะกับการ Deploy บน Cloud และ Container
#เป้าหมายของ Workshop
หลังจบ Workshop นี้ ผู้เรียนจะสามารถ:
- อธิบายหลักการ Twelve-Factor App ทั้ง 12 ข้อได้
- แยก Configuration และ Secret ออกจาก Source Code
- จัดการ Dependencies อย่างชัดเจน
- ออกแบบ Application ให้เป็น Stateless
- เชื่อมต่อ Database ในฐานะ Backing Service
- แยกขั้นตอน Build, Release และ Run
- ส่ง Logs ผ่าน
stdout/stderr - Containerize Application ด้วย Docker
- เชื่อมโยง Twelve-Factor กับ Kubernetes และ CI/CD ได้
#1. Twelve-Factor App คืออะไร?
The Twelve-Factor App คือ methodology สำหรับออกแบบ Web Application และ Software-as-a-Service (SaaS) ให้สามารถ Deploy, Scale และดูแลรักษาได้ง่าย โดยเฉพาะระบบที่ทำงานบน Cloud
หลักการประกอบด้วย 12 Factors:
# Factor แนวคิด
1 Codebase หนึ่ง Codebase อยู่ใน Version Control 2 Dependencies ประกาศ Dependencies อย่างชัดเจน 3 Config เก็บ Configuration นอก Source Code 4 Backing Services มอง DB/Cache/Queue เป็น Resource 5 Build, Release, Run แยก lifecycle ออกจากกัน 6 Processes Process ต้อง Stateless 7 Port Binding Application ให้บริการผ่าน Port 8 Concurrency Scale ด้วย Process/Instance 9 Disposability Start เร็วและ Shutdown ได้อย่างเหมาะสม 10 Dev/Prod Parity Environment ควรใกล้เคียงกัน 11 Logs มอง Log เป็น Event Stream 12 Admin Processes งาน Admin เป็น One-off Process
#Workshop: Twelve-Factor REST API
ใน Workshop นี้จะสร้าง REST API ตัวอย่างด้วย Node.js + Express + PostgreSQL + Docker Compose
โครงสร้างระบบ:
Client
|
v
REST API :3000
|
v
PostgreSQL :5432
#2. เตรียม Project
mkdir twelve-factor-workshop
cd twelve-factor-workshop
npm init -y
npm install express pg
สร้างโครงสร้าง:
twelve-factor-workshop/
├── src/
│ └── app.js
├── package.json
├── Dockerfile
├── compose.yaml
└── .gitignore
เพิ่ม .gitignore
node_modules/
.env
#3. Factor I --- Codebase
หลักการ:
One codebase tracked in revision control, many deploys.
นำ Project เข้า Git:
git init
git add .
git commit -m "Initial Twelve-Factor application"
Codebase เดียวสามารถนำไปใช้ได้หลาย Environment:
Git Repository
|
+---- Development
+---- Staging
+---- Production
ไม่ควรสร้าง Source Code แยกชุด เช่น app-dev, app-test, app-production
#4. Factor II --- Dependencies
Application ต้องประกาศ Dependency อย่างชัดเจน
สำหรับ Node.js ใช้:
package.json
package-lock.json
ตรวจสอบ:
npm list --depth=0
ตัวอย่าง:
{
"dependencies": {
"express": "...",
"pg": "..."
}
}
เมื่อ Clone Project ไปยังเครื่องใหม่:
npm ci
Application จึงไม่ควรพึ่ง Library ที่ติดตั้งแบบ global โดยไม่ประกาศไว้ใน Project
#5. Factor III --- Config
ไม่ควร Hard-code Configuration:
const password = "123456";
ให้ใช้ Environment Variables:
const dbConfig = {
host: process.env.DB_HOST,
port: process.env.DB_PORT || 5432,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD
};
แนวคิดสำคัญคือ:
Source Code != Configuration
Environment ต่างกันจึงใช้ Code เดียวกันได้:
Development -> DB_HOST=localhost
Staging -> DB_HOST=staging-db
Production -> DB_HOST=production-db
.envเหมาะสำหรับ local development แต่ Secret จริงใน Production ควรจัดการผ่าน secret-management mechanism ของ platform
#6. Factor IV --- Backing Services
Database ควรถูกมองเป็น Resource ที่เชื่อมเข้ามาผ่าน Configuration
สร้าง src/app.js
const express = require("express");
const { Pool } = require("pg");
const app = express();
app.use(express.json());
const pool = new Pool({
host: process.env.DB_HOST,
port: Number(process.env.DB_PORT || 5432),
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD
});
app.get("/", (req, res) => {
res.json({
message: "Twelve-Factor App Workshop"
});
});
app.get("/health", async (req, res) => {
try {
await pool.query("SELECT 1");
res.json({ status: "UP" });
} catch (error) {
console.error(error);
res.status(503).json({ status: "DOWN" });
}
});
const port = Number(process.env.PORT || 3000);
const server = app.listen(port, "0.0.0.0", () => {
console.log(`Application listening on port ${port}`);
});
Application ไม่จำเป็นต้องรู้ว่า PostgreSQL ทำงานบนเครื่องเดียวกัน, Container อื่น หรือ Managed Database
#7. Factor V --- Build, Release, Run
Lifecycle ควรถูกแยกเป็น:
Source
|
v
BUILD
|
v
Artifact / Docker Image
|
+ Configuration
|
v
RELEASE
|
v
RUN
สร้าง Dockerfile
FROM node:24-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY src ./src
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "src/app.js"]
Build:
docker build -t twelve-factor-api:1.0 .
Run:
docker run --rm \
-p 3000:3000 \
-e PORT=3000 \
twelve-factor-api:1.0
ใน CI/CD ควร Build artifact ครั้งเดียว แล้ว promote artifact เดิมไปยัง environment ต่าง ๆ พร้อม configuration ที่เหมาะสม
#8. Factor VI --- Processes
Application Process ควรเป็น Stateless
ไม่ควรเก็บข้อมูลสำคัญไว้ใน:
RAM ของ Instance
Local filesystem
Local session
ควรเก็บ State ไว้ใน Backing Services:
API Instance 1 ---+
API Instance 2 ---+---> PostgreSQL / Redis / Object Storage
API Instance 3 ---+
ผลคือ Instance ใดหยุดทำงาน ระบบสามารถสร้าง Instance ใหม่ได้โดยไม่สูญเสีย Application State ที่จำเป็น
#9. Factor VII --- Port Binding
Application ให้บริการผ่าน Port ที่กำหนดจาก Configuration:
const port = Number(process.env.PORT || 3000);
จากนั้น expose ผ่าน:
http://localhost:3000
ทดสอบ:
curl http://localhost:3000/
Expected response:
{
"message": "Twelve-Factor App Workshop"
}
#10. Factor VIII --- Concurrency
แทนที่จะผูกระบบกับ Process เดียว ควรออกแบบให้สามารถเพิ่ม Instance ได้:
+--> API #1
Load Balancer +--> API #2
+--> API #3
บน Kubernetes ตัวอย่างเช่น:
spec:
replicas: 3
เมื่อใช้ Horizontal Pod Autoscaler จำนวน Pod สามารถเพิ่ม/ลดตาม workload ได้
#11. Factor IX --- Disposability
Application ต้องเริ่มได้เร็วและรองรับ Graceful Shutdown
เพิ่มใน src/app.js:
async function shutdown(signal) {
console.log(`${signal} received`);
server.close(async () => {
try {
await pool.end();
console.log("Application stopped");
process.exit(0);
} catch (error) {
console.error(error);
process.exit(1);
}
});
}
process.on("SIGTERM", () => shutdown("SIGTERM"));
process.on("SIGINT", () => shutdown("SIGINT"));
สิ่งนี้สำคัญกับ:
- Rolling Deployment
- Auto Scaling
- Container restart
- Kubernetes Pod termination
#12. Factor X --- Dev/Prod Parity
Development และ Production ควรใกล้เคียงกัน
แทนที่จะใช้:
Development -> SQLite
Production -> PostgreSQL
Workshop นี้ใช้ PostgreSQL ผ่าน Container เพื่อให้ Development ใกล้ Production มากขึ้น
สร้าง compose.yaml
services:
api:
build: .
ports:
- "3000:3000"
environment:
PORT: 3000
DB_HOST: db
DB_PORT: 5432
DB_NAME: workshop
DB_USER: workshop
DB_PASSWORD: workshop_password
depends_on:
db:
condition: service_healthy
db:
image: postgres:18-alpine
environment:
POSTGRES_DB: workshop
POSTGRES_USER: workshop
POSTGRES_PASSWORD: workshop_password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U workshop -d workshop"]
interval: 5s
timeout: 5s
retries: 10
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
ค่า credential ในตัวอย่างมีไว้สำหรับ Workshop เท่านั้น ไม่ควรใช้ค่าแบบนี้ใน Production
Run:
docker compose up --build
ทดสอบ:
curl http://localhost:3000/health
#13. Factor XI --- Logs
Application ไม่ควรผูกตัวเองกับไฟล์ Log เฉพาะ เช่น:
/var/log/myapp.log
ให้ส่ง Log ออก stdout / stderr:
console.log("Application started");
console.error("Database connection failed");
ดู Log ของ Container:
docker compose logs -f api
ใน Production สามารถส่งต่อ Event Stream ไปยังระบบ เช่น:
Application
|
stdout / stderr
|
Container Runtime
|
+--> Loki
+--> Elasticsearch
+--> Cloud Logging
#14. Factor XII --- Admin Processes
งาน Administration ควรทำเป็น One-off Process โดยใช้ code/configuration ชุดเดียวกับ Application
ตัวอย่างเช่น:
docker compose exec api node scripts/migrate.js
หรือใน Framework อื่น:
php artisan migrate
python manage.py migrate
บน Kubernetes สามารถใช้ Job สำหรับ Database Migration หรือ administrative
task ที่ต้องทำครั้งเดียว
#15. ทดสอบระบบ
เริ่มระบบ:
docker compose up --build -d
ดู Container:
docker compose ps
ทดสอบ API:
curl http://localhost:3000/
curl http://localhost:3000/health
ดู Logs:
docker compose logs -f api
หยุดระบบ:
docker compose down
หากต้องการลบ Volume ด้วย:
docker compose down -v
#16. Twelve-Factor กับ Kubernetes
สามารถ Mapping หลักการกับ Kubernetes ได้โดยประมาณ:
Twelve-Factor Kubernetes / Cloud-Native
Codebase Git Repository Dependencies Container Image Config ConfigMap / Secret Backing Services Service / Managed DB Build, Release, Run CI/CD + Registry + Deployment Processes Pods Port Binding Container Port / Service Concurrency Replica / HPA Disposability Pod lifecycle / Graceful shutdown Dev/Prod Parity Containers / IaC Logs stdout / stderr Admin Processes Job
Architecture:
Developer
|
v
Git Repository
|
v
CI Pipeline
|
v
Container Image
|
v
Container Registry
|
v
Kubernetes
|
+------> Pod #1 ----+
+------> Pod #2 ----+----> PostgreSQL
+------> Pod #3 ----+
|
v
stdout/stderr
|
v
Logging Platform
#17. แบบฝึกหัด
ให้ผู้เรียนปรับปรุง Workshop ต่อไปนี้:
- เพิ่ม
GET /users - สร้างตาราง
usersใน PostgreSQL - เพิ่ม
POST /users - ห้าม Hard-code Database Configuration ใน Source Code
- เพิ่ม Health Check
- เพิ่ม Graceful Shutdown
- เพิ่ม Docker Compose
- ทดลอง Scale API หลาย Instance
- ตรวจสอบ Logs จาก Container
- อธิบายว่าแต่ละส่วนสอดคล้องกับ Factor ใด
#18. Checklist สำหรับตรวจ Application
ใช้ Checklist นี้กับ Project จริง:
[ ] Source Code อยู่ใน Version Control
[ ] Dependencies ถูกประกาศอย่างชัดเจน
[ ] ไม่มี Secret ฝังอยู่ใน Source Code
[ ] Config เปลี่ยนตาม Environment ได้
[ ] Database/Cache/Queue ถูกมองเป็น Backing Service
[ ] Build, Release และ Run แยกจากกัน
[ ] Application Process เป็น Stateless
[ ] Service เปิดใช้งานผ่าน Port
[ ] สามารถเพิ่ม Instance เพื่อ Scale ได้
[ ] Application รองรับ Graceful Shutdown
[ ] Development และ Production ใกล้เคียงกัน
[ ] Logs ส่งออกเป็น Event Stream
[ ] Admin Task สามารถ Run เป็น One-off Process
#19. สรุป
หัวใจของ Twelve-Factor App ไม่ใช่การบังคับให้ใช้ Docker หรือ Kubernetes แต่คือการออกแบบ Application ให้ portable, configurable, stateless, observable และ deployable
เมื่อนำหลักการมาใช้ร่วมกับเทคโนโลยีสมัยใหม่จะเห็นภาพดังนี้:
Twelve-Factor
|
+--> Git
+--> Environment Configuration
+--> Docker
+--> CI/CD
+--> Kubernetes
+--> Managed Services
+--> Centralized Logging
|
v
Cloud-Native Application
การเข้าใจ Twelve-Factor จึงเป็นพื้นฐานที่ดีสำหรับการศึกษาต่อในหัวข้อ DevOps, Microservices, Docker, Kubernetes, GitOps, CI/CD, SRE และ Platform Engineering
#เอกสารอ้างอิง
- The Twelve-Factor App: https://12factor.net/
- Docker Documentation: https://docs.docker.com/
- Kubernetes Documentation: https://kubernetes.io/docs/