Workshop Runbook

คู่มือการทดลองและติดตั้งระบบในรูปแบบ Multi-Student

คู่มือ Step-by-Step สำหรับผู้เรียนในการติดตั้งและจัดการโปรเจกต์ Parich ERP แยกคอนเทนเนอร์เป็นของตัวเองขนานกันบนเซิร์ฟเวอร์หลัก 1 เครื่อง โดยเข้าผ่านชื่อโดเมนหลักและแยกพอร์ตส่วนตัว

1. Ubuntu Basic Commands (คำสั่ง Ubuntu ที่จำเป็นสำหรับการทำ Workshop)

ก่อนเริ่มต้นกิจกรรม นักเรียนจำเป็นต้องมีความคุ้นเคยกับคำสั่งจัดการเซิร์ฟเวอร์พื้นฐานในระบบ Linux/Ubuntu ดังต่อไปนี้:

1.1 การจัดการไฟล์ โฟลเดอร์ และสิทธิ์ (File System & Permissions)

คำสั่ง (Command) คำอธิบาย (Description) ตัวอย่างการใช้งาน (Example)
cd ย้ายตำแหน่งโฟลเดอร์ปัจจุบัน (Change Directory) cd /home/pui/workshop/s1
cd .. ย้อนกลับออกไปนอกโฟลเดอร์ปัจจุบัน 1 ชั้น (จุดสองจุด .. หมายถึงโฟลเดอร์แม่ชั้นบน) cd ..
ls -la แสดงรายชื่อไฟล์ทั้งหมด (รวมถึงไฟล์ซ่อน เช่น .env, .git) ls -la
cp คัดลอกไฟล์หรือโฟลเดอร์ (ใช้ -r สำหรับโฟลเดอร์) cp .env.dist .env
mv ย้ายไฟล์ หรือเปลี่ยนชื่อไฟล์ mv old_name.txt new_name.txt
chmod เปลี่ยนสิทธิ์การเข้าถึงไฟล์/โฟลเดอร์ chmod 755 script.sh (อนุญาตให้รันได้)
chown เปลี่ยนเจ้าของไฟล์/โฟลเดอร์ chown pui:pui server_setup_guide.html

1.2 การวิเคราะห์ระบบและดูทรัพยากร (System Analysis & Resources)

คำสั่ง (Command) คำอธิบาย (Description) ตัวอย่างการใช้งาน (Example)
df -h ตรวจสอบพื้นที่ว่างบนหน่วยจัดเก็บข้อมูล (Disk Space) df -h
free -m ตรวจสอบการใช้งาน RAM (หน่วยเป็น MB) free -m
ps aux | grep ตรวจสอบสถานะโปรเซสว่าโปรแกรมกำลังรันอยู่หรือไม่ ps aux | grep nginx
tail -f ดู Log หรือเนื้อหาท้ายไฟล์แบบ Real-time ตามจริง tail -f /var/log/nginx/error.log
systemctl ตรวจสอบ ควบคุม หรือรีสตาร์ท Service บนระบบ Host sudo systemctl reload nginx

1.3 คำสั่ง Docker Compose ควรรู้

คำสั่ง (Command) คำอธิบาย (Description) ตัวอย่างการใช้งาน (Example)
docker compose build สั่งบิลด์อิมเมจจาก Dockerfile ล่าสุดในโครงการ docker compose build
docker compose up -d เริ่มรัน Container ในพื้นหลัง (Background) docker compose up -d
docker compose ps ตรวจสอบสถานะคอนเทนเนอร์ปัจจุบันในโฟลเดอร์โครงการ docker compose ps
docker compose logs -f ดูประวัติการบันทึก (Logs) ของคอนเทนเนอร์แบบ Real-time docker compose logs -f backend
docker compose down สั่งลบและปิดบริการ Container ทั้งหมดในโครงการ docker compose down

2. Workshop & Port Matrix (การกำหนดสิทธิ์และพื้นที่ส่วนตัว)

เพื่อให้นักเรียนทุกคนสามารถรันโปรเจกต์ของตัวเองควบคู่กันได้บนระบบปฏิบัติการ Ubuntu เดียวกันโดยไม่ชนกัน เราจะใช้ระบบระบุตัวตน Student ID (เช่น s1, s2, ...) เพื่อแยกพอร์ต คอนเทนเนอร์ และฐานข้อมูล ดังนี้:

🌐 โดเมนหลักในคลาสเรียน (ใช้โดเมนเดียว แยกพอร์ตเพื่อเข้าสู่หน้าของตนเอง)

Single Domain: demo-parichportal.loolootest.com

การเข้าหน้าเว็บของนักเรียน (s1):

  • หน้าบ้าน (Frontend): https://demo-parichportal.loolootest.com:3001
  • หลังบ้าน (Backend API): https://demo-parichportal.loolootest.com:8001
Student ID (N) URL หน้าบ้าน (Frontend) URL หลังบ้าน (Backend) Database Name Redis DB Index
s1 https://demo-parichportal.loolootest.com:3001 https://demo-parichportal.loolootest.com:8001 parich_db_s1 1
s2 https://demo-parichportal.loolootest.com:3002 https://demo-parichportal.loolootest.com:8002 parich_db_s2 2
s3 https://demo-parichportal.loolootest.com:3003 https://demo-parichportal.loolootest.com:8003 parich_db_s3 3
sN https://demo-parichportal.loolootest.com:3000+N https://demo-parichportal.loolootest.com:8000+N parich_db_sN N
⚠️ กฎเหล็กของ Workshop

ห้ามใช้พอร์ตหรือชื่อฐานข้อมูลซ้ำกับเพื่อนร่วมชั้นเรียน และระวังอย่าพิมพ์ทับซ้อนข้อมูลคอนเทนเนอร์กันเด็ดขาด

3. Hybrid Architecture (สถาปัตยกรรมระบบไฮบริด)

ระบบของ Parich ERP ทำงานในรูปแบบ Hybrid Architecture เพื่อผลลัพธ์ที่ดีที่สุด:

Docker Container ของผู้เรียน

Nuxt Frontend (พอร์ต 3000+N)

Django Backend (พอร์ต 8000+N)

Host OS (Ubuntu 24.04)

Nginx (Reverse Proxy ประจำพอร์ต)

PostgreSQL 16 & Redis (ร่วมกัน)

จุดประสงค์ของการทำ Hybrid:

4. OS & Core Setup (การติดตั้งระบบพื้นฐาน)

🛠️ สำหรับผู้สอน/ผู้จัดการระบบ (Instructor Part)

ขั้นตอนนี้ปกติแล้วผู้สอนจะติดตั้งเตรียมไว้ให้เรียบร้อยแล้ว แต่น้องๆ ควรรู้คำสั่งเหล่านี้สำหรับการตั้งค่าเซิร์ฟเวอร์ Linux ใหม่:

# 1. ติดตั้ง PostgreSQL, Redis, Nginx และ Docker บน Host OS
sudo apt update && sudo apt install -y curl git build-essential libmagic1 postgresql-16 redis-server nginx docker-ce
sudo systemctl enable postgresql redis-server nginx docker
sudo systemctl start postgresql redis-server nginx docker

น้องๆ ทุกคนจะได้รับสิทธิ์เข้ากลุ่ม docker เพื่อไม่ต้องใช้ sudo ทุกครั้งในการสั่งรันคอนเทนเนอร์:

sudo usermod -aG docker $USER && newgrp docker

5. Database Isolation (การจองและแยกแยะฐานข้อมูล)

ให้นักเรียนสร้าง Database และกำหนดสิทธิ์การเข้าใช้งานเป็นของตัวเองบน PostgreSQL ของระบบเซิร์ฟเวอร์ร่วม:

# 1. ล็อกอินเข้าใช้งาน PostgreSQL shell
sudo -u postgres psql

# 2. สร้างฐานข้อมูลของตนเอง (เปลี่ยน sN เป็นรหัสของนักเรียน เช่น s1, s2, s3)
CREATE DATABASE parich_db_sN;

# 3. ให้สิทธิ์การเข้าถึงทั้งหมดแก่ผู้ใช้ django_ci (ซึ่งกำหนดไว้ใน .env)
GRANT ALL PRIVILEGES ON DATABASE parich_db_sN TO django_ci;
\q

5.2 การ Restore ข้อมูลทดสอบ (Database Restore)

เพื่อให้น้องๆ มีข้อมูลจำลองสำหรับทดสอบใช้งานและพัฒนาต่อได้ทันที ให้ทำการกู้คืน (Restore) ฐานข้อมูลจากไฟล์ dump ต้นแบบที่จัดเตรียมไว้ให้บนเซิร์ฟเวอร์:

# รันคำสั่ง pg_restore ผ่านสิทธิ์ผู้ดูแลฐานข้อมูล postgres เพื่อย้ายข้อมูลเข้า parich_db_sN ของตนเอง
# (เปลี่ยน sN ให้เป็นชื่อ DB ที่ตั้งไว้ในขั้นตอนแรก เช่น parich_db_s1)
sudo -u postgres pg_restore --no-owner --no-privileges -d parich_db_sN /tmp/puidb_test_backup.dump

6. Workspace & Git Workflow (การเตรียมโค้ด)

เนื่องจากทั้ง **Backend** และ **Frontend** อยู่ใน **Repository เดียวกันแต่คนละ Branch** ให้นักเรียนทำการโคลนโปรเจกต์เต็มรูปแบบลงมา 2 โฟลเดอร์แยกกัน แล้วใช้คำสั่ง checkout สลับไปแต่ละ Branch เพื่อเรียนรู้คำสั่ง Git:

💡 การสลับหน้างานผ่าน Git Checkout

เราจะแยกโฟลเดอร์สำหรับทำงานหลังบ้าน (backend-project) สลับไปที่สาขา backend/base และหน้าบ้าน (frontend-project) สลับไปที่สาขา nuxt-frontend/base

# ตัวอย่างสำหรับนักเรียน s1 (เปลี่ยนโฟลเดอร์ตาม Student ID ของตัวเอง)
mkdir -p /home/pui/workshop/s1
cd /home/pui/workshop/s1

# 1. โคลน Repository เพื่อพัฒนา Backend
# เมื่อโคลนเสร็จแล้ว ให้ย้ายเข้าโฟลเดอร์เพื่อ checkout สาขา backend/base 
# และใช้คำสั่ง cd .. เพื่อย้อนกลับมาที่โฟลเดอร์ระดับบนก่อนเริ่มขั้นถัดไป
git clone ssh://git@code.loolootech.com/loolootech/fertilizer-erp.git backend-project
cd backend-project
git checkout backend/base
cd ..

# 2. โคลน Repository เพื่อพัฒนา Frontend (Nuxt)
# เมื่อโคลนเสร็จแล้ว ให้ย้ายเข้าโฟลเดอร์เพื่อ checkout สาขา nuxt-frontend/base
git clone ssh://git@code.loolootech.com/loolootech/fertilizer-erp.git frontend-project
cd frontend-project
git checkout nuxt-frontend/base
cd ..

7. .env Configuration (การตั้งค่า Config ส่วนบุคคล)

เพื่อไม่ให้เกิดการชนกันของระบบ คอนฟิกูเรชันใน `.env` ของแต่ละคนจะต้องแตกต่างกันอย่างสิ้นเชิง ให้นักเรียนสร้างและตั้งค่าคอนฟิกดังนี้:

7.1 การตั้งค่าฝั่ง Backend (Django)

cd /home/pui/workshop/s1/backend-project/backend
cp .env.dist .env
nano .env

ตัวอย่างการเปลี่ยนค่าตัวแปรใน .env ของ Backend นักเรียน s1:

# แยกชื่อโครงการในระบบ Docker Compose
COMPOSE_PROJECT_NAME=parich-s1

# ผูกพอร์ต Backend ของตัวเองตามตารางข้อ 2 (s1 ใช้ 8001)
HOST_BACKEND_PORT=8001

# ชี้ฐานข้อมูลไปยังตัวที่เราสร้างไว้ในข้อ 5
DB_NAME=parich_db_s1
DB_USER=django_ci
DB_PASSWORD=django_ci_password
DB_HOST=host.docker.internal  # เข้าถึงฐานข้อมูลบน Host OS ผ่านเครือข่าย Docker

# แยกฐานข้อมูล Redis Index ของระบบ Cache
REDIS_HOST=host.docker.internal
REDIS_PORT=6379
REDIS_URL=redis://host.docker.internal:6379/1

7.2 การตั้งค่าฝั่ง Frontend (Nuxt)

cd /home/pui/workshop/s1/frontend-project/nuxt_frontend
cp .env.dist .env
nano .env

ตัวอย่างการเปลี่ยนค่าตัวแปร in .env ของ Frontend นักเรียน s1:

# แยกชื่อโครงการฝั่งหน้าบ้าน
COMPOSE_PROJECT_NAME=parich-s1-fe

# ผูกพอร์ต Frontend ของตัวเองตามตารางข้อ 2 (s1 ใช้ 3001)
HOST_FRONTEND_PORT=3001

# ชี้ API Base URL ไปยัง Backend พอร์ต 8001 ของตัวเอง (ต้องผ่านโดเมนและพอร์ตตรงๆ เพื่อความปลอดภัย)
API_BASE_URL=https://demo-parichportal.loolootest.com:8001/api

8. Build & Deploy (การรันคำสั่ง Docker Compose เพื่อเรียนรู้ระบบ)

เพื่อให้นักเรียนเข้าใจกระบวนการทำงานเบื้องหลังของตู้คอนเทนเนอร์ เราจะสั่งงานโปรเจกต์ตรงด้วยคำสั่ง **Docker Compose** ทีละขั้นตอน:

8.1 การรันระบบและจัดการฐานข้อมูลฝั่ง Backend (Django)

ย้ายเข้าโฟลเดอร์ Backend จากนั้นทำตามขั้นตอนต่อไปนี้:

cd /home/pui/workshop/s1/backend-project/backend

# 1. สั่งสร้าง (Build) Image จาก Dockerfile.prod ของระบบหลังบ้าน
docker compose build

# 2. สั่งรันบริการ Backend ขึ้นมาในพื้นหลัง (Background Mode)
docker compose up -d

# 3. ตรวจสอบสถานะการรันคอนเทนเนอร์ของตนเอง
docker compose ps

# 4. สั่ง Migrate ฐานข้อมูลเข้าไปในฐานข้อมูล PostgreSQL บน Host OS ของตนเอง
docker compose exec -T backend python manage.py migrate

# 5. สั่งรวบรวมไฟล์ Static Assets ทั้งหมด เพื่อไปเตรียมเสิร์ฟที่ Host OS
docker compose exec -T backend python manage.py collectstatic --no-input

8.2 การรันระบบฝั่ง Frontend (Nuxt)

ย้ายเข้าโฟลเดอร์ Frontend Nuxt จากนั้นทำตามขั้นตอนต่อไปนี้:

cd /home/pui/workshop/s1/frontend-project/nuxt_frontend

# 1. สั่งสร้าง (Build) Image ซึ่งจะทำการคอมไพล์โค้ด Nuxt ลงในคอนเทนเนอร์
docker compose build

# 2. สั่งรันบริการ Frontend ขึ้นมา
docker compose up -d

# 3. ตรวจสอบดูว่า Frontend รันพอร์ต 3001 ของเราเรียบร้อยดีหรือไม่
docker compose ps

9. Nginx Proxy & SSL (การตั้งค่าจัดเส้นทางพอร์ตนักเรียน)

เพื่อให้สามารถเข้าผ่านชื่อโดเมนหลักทาง HTTPS ได้อย่างปลอดภัย (แก้ปัญหา Not Secure) ผู้สอนหรือนักเรียนจะทำการตั้งค่าเพิ่มบล็อก Nginx บน Host OS เพื่อรับสัญญาณ HTTPS พอร์ตของตัวเองส่งเข้าหา Container ภายในของตนเอง:

💡 แนวทางการสร้าง Nginx Block สำหรับนักเรียนคนที่ N

ชี้จากโดเมนรวมเข้าหาพอร์ตภายในของน้องแต่ละคนด้วย Config นี้:

# ไฟล์คอนฟิกตัวอย่างสำหรับนักเรียน s1 (/etc/nginx/sites-available/parich-s1)
# --------------------------------------------------
# FRONTEND CONFIG (ชี้พอร์ต 3001 เข้า Nuxt Container)
# --------------------------------------------------
server {
    listen 3001 ssl;
    server_name demo-parichportal.loolootest.com;

    # ใช้ใบรับรอง SSL ร่วมกันของโดเมนหลัก
    ssl_certificate /etc/letsencrypt/live/demo-parichportal.loolootest.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/demo-parichportal.loolootest.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3001; # ส่งต่อไปที่พอร์ตภายในของ Frontend Container
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

# --------------------------------------------------
# BACKEND CONFIG (ชี้พอร์ต 8001 เข้า Django Container)
# --------------------------------------------------
server {
    listen 8001 ssl;
    server_name demo-parichportal.loolootest.com;

    # ใช้ใบรับรอง SSL ร่วมกันของโดเมนหลัก
    ssl_certificate /etc/letsencrypt/live/demo-parichportal.loolootest.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/demo-parichportal.loolootest.com/privkey.pem;

    client_max_body_size 75M;

    # ดึง Static files ตรงจากไดเรกทอรี Workspace ของผู้เรียน s1 บน Host OS
    location /static/ {
        alias /home/pui/workshop/s1/backend-project/backend/static/;
        expires 30d;
        add_header Cache-Control "public, no-transform";
    }

    location /media/ {
        alias /home/pui/workshop/s1/backend-project/backend/media/;
        expires 7d;
    }

    location / {
        proxy_pass http://127.0.0.1:8001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

หลังจากบันทึกไฟล์คอนฟิกแล้ว ให้ทำการเปิดใช้งานบล็อคนั้นและสั่ง Reload Nginx:

sudo ln -sf /etc/nginx/sites-available/parich-s1 /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

10. Cron Jobs Isolation (การแยกงานตั้งเวลาของแต่ละคน)

เนื่องจากตัว Cron Daemon ทำงานในระดับ Host OS เพื่อความเสถียร สคริปต์ `deploy.sh` ของน้องๆ จะถูกเขียนให้สร้างคอนฟิกสำหรับนักเรียนแยกกันตามชื่อของโปรเจกต์:

# คอนฟิกใน /etc/cron.d/parich-backend-parich-s1 (สร้างโดยอัตโนมัติ)
0 * * * * root /home/pui/workshop/s1/backend-project/backend/cron_host.sh calculate_order_target --days-back=3

ช่วยให้น้องมั่นใจได้ว่าคำสั่ง Cron จะยิงงานเข้าไปยังตู้ parich-s1-backend ของตัวเองเท่านั้น ไม่ไปรบกวนข้อมูลของคนอื่น

11. Troubleshooting (การแก้ปัญหาเมื่อพบความผิดพลาด)

11.1 ปัญหา HTTP 403 Forbidden เมื่อเปิดหน้า Django Admin (CSS ไม่โหลด)

ปัญหานี้มักจะเกิดจากการที่สิทธิ์ในการเข้าถึงโฟลเดอร์ static ใน path ของผู้เรียน (เช่น /home/pui/...) ไม่ได้รับสิทธิ์ให้ user www-data ของ Nginx เดินผ่านได้ ให้รันคำสั่งเปิดทางเข้า (Execute) ดังนี้:

# เปิดสิทธิ์การเปิดอ่านและผ่านของโฟลเดอร์ไล่ระดับ
chmod 755 /home/pui
chmod 755 /home/pui/workshop
chmod 755 /home/pui/workshop/s1
chmod 755 /home/pui/workshop/s1/backend-project
chmod 755 /home/pui/workshop/s1/backend-project/backend