Deploy เว็บไซต์ด้วย Git บน Linux Server
การอัปโหลดไฟล์เว็บผ่าน FTP หรือ SFTP ทีละไฟล์ทำให้เกิดปัญหาบ่อย เช่น ลืมอัปโหลดบางไฟล์ ไม่รู้ว่าบนเซิร์ฟเวอร์เป็นเวอร์ชันไหน และย้อนกลับไม่ได้เมื่อมีปัญหา การใช้ Git ทำให้เซิร์ฟเวอร์มีโค้ดตรงกับ Commit ที่ระบุได้แน่นอน อัปเดตได้ด้วยคำสั่งเดียว และย้อนกลับไปเวอร์ชันก่อนหน้าได้ภายในไม่กี่วินาที
บทความนี้อธิบายสองวิธีที่ใช้บ่อยบน Linux Server ที่ไม่มี Control Panel คือ วิธี A ดึงโค้ดจาก GitHub หรือ GitLab ด้วย Deploy Key และ วิธี B สร้าง Bare Repository บนเซิร์ฟเวอร์ แล้ว git push จากเครื่องของคุณโดยตรงพร้อม Hook ที่ Deploy ให้อัตโนมัติ หากคุณใช้ Plesk ให้ใช้ฟีเจอร์ Git ในตัวตาม Deploy เว็บไซต์จาก Git Repository บน Plesk
สิ่งที่ต้องเตรียม
- เซิร์ฟเวอร์ Ubuntu 22.04/24.04 หรือ AlmaLinux/Rocky Linux 8-9 ที่ตั้งเว็บเซิร์ฟเวอร์ไว้แล้ว (เช่น Nginx Server Block หรือ Apache)
- ผู้ใช้สำหรับ Deploy โดยเฉพาะ ในตัวอย่างชื่อ
deployสร้างได้ตาม สร้างผู้ใช้ใหม่และให้สิทธิ์ sudo บน Linux (ผู้ใช้นี้ไม่จำเป็นต้องมีสิทธิ์ sudo) - โปรเจกต์เว็บที่อยู่ใน Git แล้ว และไม่มีไฟล์ความลับ เช่น
.envหรือรหัสผ่านฐานข้อมูลอยู่ใน Repository
ติดตั้ง Git บนเซิร์ฟเวอร์:
# Ubuntu
sudo apt update && sudo apt install -y git
# AlmaLinux / Rocky Linux
sudo dnf install -y git
เตรียมโฟลเดอร์เว็บและสิทธิ์
ให้ผู้ใช้ deploy เป็นเจ้าของไฟล์ และให้กลุ่มของเว็บเซิร์ฟเวอร์อ่านได้ (Ubuntu ใช้กลุ่ม www-data ส่วน AlmaLinux/Rocky ใช้ nginx หรือ apache ตามเว็บเซิร์ฟเวอร์):
sudo mkdir -p /var/www/example.com
sudo chown deploy:www-data /var/www/example.com
sudo chmod 2750 /var/www/example.com
เลข 2 หน้าสุดคือ setgid ทำให้ไฟล์ที่สร้างใหม่ในโฟลเดอร์นี้อยู่ในกลุ่ม www-data อัตโนมัติ โฟลเดอร์ที่เว็บต้องเขียนได้ เช่น uploads ให้กำหนดสิทธิ์แยกเฉพาะโฟลเดอร์นั้น (ดู ทำความเข้าใจสิทธิ์ไฟล์บน Linux ด้วย chmod และ chown)
คำสั่งต่อจากนี้ให้รันในฐานะผู้ใช้ deploy:
sudo -iu deploy
วิธี A: ดึงโค้ดจาก GitHub หรือ GitLab ด้วย Deploy Key
ขั้นตอนที่ A1: สร้าง Deploy Key
ssh-keygen -t ed25519 -f ~/.ssh/deploy_example -N "" -C "[email protected]"
cat ~/.ssh/deploy_example.pub
คัดลอก Public Key ที่แสดงไปเพิ่มใน Repository บน GitHub ที่ Settings > Deploy keys > Add deploy key โดยไม่ต้องติ๊ก Allow write access เพื่อให้กุญแจนี้อ่านได้อย่างเดียว บน GitLab อยู่ที่ Settings > Repository > Deploy keys Deploy Key ผูกกับ Repository เดียว ถ้าเซิร์ฟเวอร์ถูกเจาะ ผู้โจมตีจะเข้าถึงได้เฉพาะ Repository นั้นแบบอ่านอย่างเดียว ปลอดภัยกว่าการใช้ SSH Key ส่วนตัวของคุณ
ขั้นตอนที่ A2: ให้ SSH ใช้กุญแจนี้กับ GitHub
cat >> ~/.ssh/config <<'EOF'
Host github.com
IdentityFile ~/.ssh/deploy_example
IdentitiesOnly yes
EOF
chmod 600 ~/.ssh/config
ssh -T [email protected]
ครั้งแรกจะถามยืนยัน Host Key ให้ตรวจ Fingerprint กับที่ GitHub เผยแพร่ไว้ในเอกสารทางการก่อนตอบ yes ผลที่ถูกต้องคือข้อความ Hi ชื่อ/repo! You've successfully authenticated, but GitHub does not provide shell access. รายละเอียดเรื่องไฟล์ ~/.ssh/config ดูได้ที่ ใช้ไฟล์ ~/.ssh/config ให้ SSH เข้าเซิร์ฟเวอร์ได้สะดวกขึ้น
ขั้นตอนที่ A3: Clone และ Deploy ครั้งแรก
git clone [email protected]:your-org/example-site.git /var/www/example.com/app
แนะนำให้ตั้ง Document Root ของเว็บเซิร์ฟเวอร์ไปที่โฟลเดอร์ย่อยที่เป็นหน้าเว็บจริง เช่น /var/www/example.com/app/public ไม่ใช่รากของ Repository เพื่อไม่ให้โฟลเดอร์ .git และไฟล์อื่นเข้าถึงได้จากเว็บ
ขั้นตอนที่ A4: อัปเดตครั้งต่อไป
สร้างสคริปต์ Deploy เพื่อให้ทุกครั้งทำเหมือนกัน:
cat > ~/deploy-example.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
cd /var/www/example.com/app
git fetch origin
git merge --ff-only origin/main
git log -1 --oneline
EOF
chmod +x ~/deploy-example.sh
~/deploy-example.sh
--ff-only ทำให้การอัปเดตล้มเหลวอย่างปลอดภัยถ้ามีคนแก้ไฟล์บนเซิร์ฟเวอร์โดยตรง แทนที่จะสร้าง Merge Commit แปลก ๆ บนเครื่อง Production บรรทัดสุดท้ายแสดง Commit ที่ Deploy อยู่ ถ้าโปรเจกต์ต้องติดตั้ง Dependency (เช่น composer install --no-dev หรือ npm ci) ให้เพิ่มคำสั่งเหล่านั้นต่อท้ายสคริปต์
วิธี B: Push ตรงเข้าเซิร์ฟเวอร์ด้วย Bare Repository และ Hook
เหมาะเมื่อไม่ต้องการพึ่ง GitHub หรือต้องการ Deploy ด้วยคำสั่ง git push จากเครื่องของคุณ
ขั้นตอนที่ B1: สร้าง Bare Repository
mkdir -p ~/repos
git init --bare ~/repos/example.git
Bare Repository เก็บเฉพาะประวัติของ Git ไม่มีไฟล์ทำงาน (Working Tree) ไฟล์เว็บจะถูก Checkout ไปที่โฟลเดอร์เว็บโดย Hook
ขั้นตอนที่ B2: สร้าง Hook post-receive
cat > ~/repos/example.git/hooks/post-receive <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
TARGET=/var/www/example.com/app
GIT_DIR=/home/deploy/repos/example.git
BRANCH=main
while read -r oldrev newrev ref; do
if [ "$ref" = "refs/heads/$BRANCH" ]; then
mkdir -p "$TARGET"
git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f "$BRANCH"
echo "Deployed $BRANCH at $(git --git-dir="$GIT_DIR" rev-parse --short "$newrev")"
else
echo "Ignored $ref (only $BRANCH is deployed)"
fi
done
EOF
chmod +x ~/repos/example.git/hooks/post-receive
Hook นี้ทำงานทุกครั้งที่มีการ Push เข้ามา และ Checkout เฉพาะ Branch main ไปที่โฟลเดอร์เว็บ วิธีนี้ไม่มีโฟลเดอร์ .git อยู่ในโฟลเดอร์เว็บเลย แต่ไฟล์ที่ถูกลบออกจาก Repository จะยังค้างอยู่ในโฟลเดอร์เว็บ ถ้าต้องการลบด้วย ให้เพิ่มคำสั่ง git --work-tree="$TARGET" --git-dir="$GIT_DIR" clean -fd หลังบรรทัด checkout โดยต้องแน่ใจก่อนว่าโฟลเดอร์ uploads และไฟล์ .env ถูกระบุใน .gitignore แล้ว เพราะ clean -fd ไม่ลบไฟล์ที่อยู่ใน .gitignore แต่จะลบไฟล์อื่นที่ Git ไม่รู้จักทั้งหมด
ขั้นตอนที่ B3: Push จากเครื่องของคุณ
บนเครื่องคอมพิวเตอร์ของคุณ (ต้องใส่ SSH Key ของคุณใน /home/deploy/.ssh/authorized_keys บนเซิร์ฟเวอร์ก่อน ดู ตั้งค่า SSH Key และปิดการเข้าสู่ระบบด้วยรหัสผ่านบน Linux):
git remote add production [email protected]:repos/example.git
git push production main
ผลลัพธ์จะมีบรรทัดที่ขึ้นต้นด้วย remote: ซึ่งเป็นข้อความจาก Hook เช่น remote: Deployed main at 3f2a9c1 ถ้า SSH ใช้พอร์ตอื่น ให้เขียน URL แบบ ssh://[email protected]:2222/home/deploy/repos/example.git
ตรวจสอบผลลัพธ์
- วิธี A: รัน
git -C /var/www/example.com/app log -1 --onelineบนเซิร์ฟเวอร์ ต้องตรงกับ Commit ล่าสุดบน GitHub - วิธี B: รัน
git --git-dir=/home/deploy/repos/example.git log -1 --oneline mainต้องตรงกับที่ Push ไป - ตรวจว่าโฟลเดอร์
.gitเข้าจากเว็บไม่ได้:curl -I https://example.com/.git/configต้องได้403หรือ404ถ้าได้200แปลว่าประวัติโค้ดทั้งหมดรั่วได้ ให้แก้ Document Root หรือเพิ่มกฎบล็อก เช่นใน Nginx ใช้location ~ /\.git { deny all; }
ย้อนกลับเมื่อ Deploy แล้วมีปัญหา
วิธีที่ปลอดภัยที่สุดคือสร้าง Revert Commit บนเครื่องของคุณ แล้ว Deploy ใหม่ตามปกติ:
git revert HEAD
git push origin main # วิธี A แล้วรันสคริปต์ Deploy บนเซิร์ฟเวอร์
git push production main # วิธี B
กรณีฉุกเฉินกับวิธี A สามารถให้เซิร์ฟเวอร์กลับไปที่ Commit ก่อนหน้าทันทีด้วย git -C /var/www/example.com/app reset --hard HEAD~1 คำสั่งนี้จะทิ้งการแก้ไขใด ๆ ที่ทำบนเซิร์ฟเวอร์โดยตรง และการ Deploy ครั้งถัดไปด้วย --ff-only จะยังทำงานได้ตามปกติ ถ้ามีการเปลี่ยนโครงสร้างฐานข้อมูล (Migration) ร่วมด้วย การย้อนโค้ดอย่างเดียวอาจไม่พอ ควรสำรองฐานข้อมูลก่อน Deploy ทุกครั้ง (ดู สำรองและกู้คืนฐานข้อมูล MySQL/MariaDB ด้วย mysqldump)
ปัญหาที่พบบ่อย
fatal: detected dubious ownership in repository
Git ปฏิเสธเมื่อเจ้าของโฟลเดอร์ Repository ไม่ใช่ผู้ใช้ที่รันคำสั่ง มักเกิดเมื่อรัน Git ด้วย root หรือผู้ใช้อื่น ทางแก้ที่ถูกคือรันด้วยผู้ใช้ deploy ผู้เป็นเจ้าของ ถ้าจำเป็นต้องรันด้วยผู้ใช้อื่นจริง ให้เพิ่มข้อยกเว้นเฉพาะ Path นั้น: git config --global --add safe.directory /var/www/example.com/app
Permission denied (publickey) ตอน git fetch
ยังไม่ได้เพิ่ม Deploy Key ใน Repository, ~/.ssh/config ไม่ได้ชี้ไปที่กุญแจ หรือรันด้วยผู้ใช้คนละคนกับที่สร้างกุญแจ ตรวจด้วย ssh -vT [email protected] แล้วดูว่าได้ลองใช้ไฟล์กุญแจใด
Not possible to fast-forward, aborting
มีคนแก้ไฟล์หรือ Commit บนเซิร์ฟเวอร์โดยตรง ดูด้วย git status และ git log origin/main..HEAD เก็บการแก้ไขที่ต้องการกลับเข้า Repository จากเครื่องพัฒนา แล้วค่อยรีเซ็ตเซิร์ฟเวอร์ให้ตรงกับ origin/main หลังสำรองไฟล์แล้ว
Hook ไม่ทำงานหลัง Push
ไฟล์ post-receive ไม่มีสิทธิ์ Execute หรือบันทึกด้วย Line Ending แบบ Windows (CRLF) ตรวจด้วย ls -l และ file ~/repos/example.git/hooks/post-receive ถ้าพบ CRLF ให้แปลงด้วย sed -i 's/\r$//' ไฟล์
หากต้องการวางระบบ Deploy ที่ซับซ้อนขึ้น เช่น หลายเซิร์ฟเวอร์หรือ Zero-downtime ติดต่อทีมซัพพอร์ต THAI DATA CLOUD ได้ที่ https://thaidata.cloud/contact/
- Categories:
- Cloud
- Tags:
- Cloud
- Cloud Server
Related Posts
หมวดหมู่ที่น่าสนใจ
- Account Settings
- AD Server
- AI
- Alibaba Cloud
- Anti-Spam Gateway
- AWS Amazon Web Services
- Campaign
- CentOS/AlmaLinux
- Cloud
- Cloud Backup
- Cloud Communication
- Cloud Migration
- Cloud Security
- Cloud Server Management
- Cloud Solution
- Cloud Solution for Government
- Cloud Solutions by Industry
- Cloud Storage
- Cloud VPS App Plus +
- Cloud VPS DirectAdmin
- Cloud VPS Plesk
- CSR
- Cyber Security
- Cybersecurity
- Data Sovereignty
- Database Server
- DDoS
- Digital Tranformation
- Digital Transformation
- Direct Mail
- Directadmin
- Domainname
- Ecommerce
- ERP
- Generative AI
- Getting Started
- Google Cloud
- Google G Suite
- Huawei Cloud
- IT News
- Linux Server
- Managed Cloud Services
- Managed Service Provider
- Manual
- Microsoft
- Microsoft 365
- Microsoft Azure
- News
- On-premise
- Private Mail Server
- Promotion
- Recommend Solution (Enterprise)
- Server
- Sovereign Cloud
- THAI DATA CLOUD Platform
- Ubuntu
- Ubuntu
- Uncategorized
- VMware
- VPS Server
- Web Design
- Web Hosting
- Web Hosting (DirectAdmin)
- Web Hosting (Plesk)
- Web Technologies
- Windows Server
- Wordpress
- Zimbra
- เรื่องราวความประทับใจ
- โซลูชันสำหรับธุรกิจการผลิตและยานยนต์
- โซลูชันสำหรับธุรกิจการศึกษา
- โซลูชันสำหรับธุรกิจการเงิน
- โซลูชันสำหรับธุรกิจขนส่งและกระจายสินค้า
- โซลูชันสำหรับธุรกิจค้าปลีก
- โซลูชันสำหรับธุรกิจท่องเที่ยว
- โซลูชันสำหรับธุรกิจบริการสุขภาพและโรงพยาบาล
- โซลูชันสำหรับธุรกิจประกันภัย
- โซลูชันสำหรับธุรกิจพลังงานและสาธารณูปโภค
- โซลูชันสำหรับธุรกิจสื่อสารมวลชนและเอ็นเตอร์เทนเมนท์
- โซลูชันสำหรับธุรกิจอสังหาริมทรัพย์
- โซลูชันสำหรับธุรกิจเทคโนโลยี


