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 ย้ายตำแหน่งโฟลเดอร์ปัจจุบัน cd /home/pui/workshop/s1
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

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

ให้นักเรียนโคลน Repository มายังโฟลเดอร์ส่วนตัว โดยแยกโฟลเดอร์ตาม Student ID:

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

# โคลนโปรเจกต์จาก Gitea
git clone ssh://git@code.loolootech.com/loolootech/fertilizer-erp.git
cd fertilizer-erp

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

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

# คัดลอกและสร้างไฟล์คอนฟิกูเรชัน
cd backend
cp .env.dist .env
nano .env

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

# 1. แยกชื่อโครงการในระบบ Docker Compose (สำคัญมากเพื่อป้องกันคอนเทนเนอร์ชนกัน)
COMPOSE_PROJECT_NAME=parich-s1

# 2. ผูกพอร์ตตามตารางข้อ 1 (Frontend 3001, Backend 8001)
HOST_BACKEND_PORT=8001

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

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

8. Build & Deploy (การสตาร์ทระบบผ่าน Docker Compose)

เมื่อตั้งค่าเสร็จแล้ว ให้นักเรียนบิลด์อิมเมจและสตาร์ทตู้ขึ้นมา:

# 1. รัน Deploy Script ของผู้สอนหรือรันคำสั่ง Docker Compose ด้วยตนเอง
docker compose build
docker compose up -d

# 2. ทำการ Migrate ฐานข้อมูลและสร้างข้อมูล Static Assets ของหน้าบ้าน
docker compose exec -T backend python manage.py migrate
docker compose exec -T backend python manage.py collectstatic --no-input

ตรวจสอบสถานะคอนเทนเนอร์ของคุณด้วยคำสั่ง: docker compose ps ซึ่งจะต้องเห็นคอนเทนเนอร์แยกเฉพาะชื่อ เช่น parich-s1-backend และ parich-s1-celery ทำงานตามปกติ

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/fertilizer-erp/backend/static/;
        expires 30d;
        add_header Cache-Control "public, no-transform";
    }

    location /media/ {
        alias /home/pui/workshop/s1/fertilizer-erp/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/fertilizer-erp/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/fertilizer-erp
chmod 755 /home/pui/workshop/s1/fertilizer-erp/backend