คู่มือ CI/CD ด้วย GitHub Actions ไป DigitalOcean 2026
A practical guide to building GitHub Actions workflows that deploy code to DigitalOcean Droplets via SSH and to App Platform via doctl, covering secrets management and rollback strategy.
การผูก GitHub Actions เข้ากับ DigitalOcean ช่วยให้ทุกครั้งที่ push โค้ดเข้าสาขา main ระบบ build, test และ deploy ให้อัตโนมัติโดยไม่ต้อง SSH เข้าเครื่องด้วยมือ บทความนี้พาไล่เรียงตั้งแต่แนวคิด CI/CD ไปจนถึงการเขียน workflow จริงสำหรับ deploy ไป Droplet ผ่าน SSH และไป App Platform ผ่าน doctl พร้อมวิธีเก็บ secret และวางแผน rollback เมื่อ deploy ล้มเหลว
สารบัญ
CI/CD คืออะไร ทำไมสำคัญ
CI/CD ย่อมาจาก Continuous Integration และ Continuous Deployment (บางทีมใช้คำว่า Continuous Delivery แทนตัวหลัง) เป็นแนวทางที่ให้ทุกครั้งที่มีการเปลี่ยนแปลงโค้ดถูก build, test และนำขึ้นใช้งานจริงโดยอัตโนมัติผ่านขั้นตอนที่กำหนดไว้ล่วงหน้า แทนที่นักพัฒนาจะต้อง SSH เข้าเซิร์ฟเวอร์ pull โค้ด restart service ด้วยมือทุกครั้งที่มีการแก้ไข ซึ่งนอกจากจะใช้เวลานานแล้วยังเสี่ยงต่อความผิดพลาดจากมนุษย์ เช่น ลืมขั้นตอนใดขั้นตอนหนึ่ง หรือรันคำสั่งผิดสภาพแวดล้อมโดยไม่ตั้งใจ
GitHub Actions เป็นระบบ CI/CD ที่ผูกติดกับ GitHub โดยตรง ทำงานผ่านไฟล์ YAML ที่วางไว้ในโฟลเดอร์ .github/workflows/ ของ repository เช่น .github/workflows/deploy.yml ระบบจะรัน workflow อัตโนมัติเมื่อเกิดเหตุการณ์ที่กำหนดไว้ เช่น on: push: branches: [main] หมายความว่าทุกครั้งที่มีการ push หรือ merge เข้าสาขา main จะทริกเกอร์ workflow ทันที ข้อดีของการใช้ GitHub Actions คือไม่ต้องติดตั้งเซิร์ฟเวอร์ CI แยกต่างหาก ไม่มีค่าใช้จ่ายเพิ่มสำหรับ public repository และ private repository ก็มีโควตาชั่วโมงรันฟรีต่อเดือนตามแผนบัญชี GitHub
สำหรับผู้ใช้ DigitalOcean การเชื่อม GitHub Actions เข้ากับโครงสร้างพื้นฐานทำได้สองแนวทางหลัก คือ deploy ไป Droplet ที่บริหารจัดการเองผ่าน SSH ซึ่งให้ความยืดหยุ่นสูงสุดเพราะควบคุมทุกขั้นตอนได้เอง กับ deploy ไป App Platform ซึ่งเป็นบริการ PaaS ที่ DigitalOcean จัดการ infrastructure ให้ทั้งหมด เหลือแค่ต้องส่ง spec หรือโค้ดเข้าไป โดยทั้งสองแนวทางสามารถทำผ่าน workflow เดียวกันได้ เพียงแค่เปลี่ยนขั้นตอนสุดท้ายของ pipeline
จุดสำคัญที่ทำให้ CI/CD คุ้มค่าไม่ใช่แค่ความเร็ว แต่คือความสม่ำเสมอ (consistency) ทุกครั้งที่ deploy จะผ่านขั้นตอนเดียวกันทุกครั้งไม่ว่าใครเป็นคน push โค้ด ลดปัญหาที่พบบ่อยในทีมขนาดเล็กที่แต่ละคน deploy ด้วยวิธีต่างกัน เช่น บางคนลืมรัน migration ฐานข้อมูลก่อน restart service ทำให้เกิด error ที่ตามหาสาเหตุยาก
CI/CD stands for Continuous Integration and Continuous Deployment (some teams use Continuous Delivery for the latter term instead). It's an approach where code changes are automatically built, tested, and deployed to production through predefined steps, rather than requiring developers to manually SSH into a server, pull code, and restart services each time — which is time-consuming and error-prone. Mistakes like forgetting a step or running commands in the wrong environment are common human errors.
GitHub Actions is a CI/CD system built directly into GitHub, operating through YAML files placed in the .github/workflows/ folder of your repository, such as .github/workflows/deploy.yml. The system automatically runs workflows when specified events occur, for example on: push: branches: [main] means the workflow triggers every time code is pushed or merged into the main branch. The advantage of GitHub Actions is that you don't need a separate CI server, there's no extra cost for public repositories, and private repositories include free runtime minutes per month based on your GitHub plan.
For DigitalOcean users, connecting GitHub Actions to your infrastructure involves two main approaches: deploying to a Droplet that you manage yourself via SSH (which offers maximum flexibility since you control every step), or deploying to App Platform, which is a PaaS service where DigitalOcean handles the infrastructure. Both approaches can work with the same workflow — you just swap the final deployment step. The real value of CI/CD isn't just speed, but consistency: every deployment follows the same process regardless of who pushed the code, reducing issues that plague small teams where everyone deploys differently, like forgetting to run database migrations before restarting services.
- CI (Continuous Integration) = build/test อัตโนมัติทุกครั้งที่มีการเปลี่ยนโค้ด, CD (Continuous Deployment/Delivery) = นำขึ้นใช้งานจริงอัตโนมัติต่อจากนั้น
- GitHub Actions ทำงานผ่านไฟล์ YAML ใน
.github/workflows/ทริกเกอร์ด้วย event เช่นon: push: branches: [main] - ใช้ได้ทั้งสอง path บน DigitalOcean: Droplet ผ่าน SSH (ควบคุมเต็มที่) หรือ App Platform ผ่าน doctl (จัดการ infra ให้)
สร้าง Workflow deploy ไป Droplet ผ่าน SSH
จากการรีวิวหลายรอบ การ deploy ไป Droplet ผ่าน GitHub Actions ใช้หลักการเดียวกับที่นักพัฒนา SSH เข้าเครื่องด้วยมือ เพียงแต่ให้ workflow เป็นคนรันคำสั่งแทน วิธีที่นิยมที่สุดคือใช้ action สำเร็จรูปอย่าง appleboy/ssh-action ซึ่งรับ host, username และ private key แล้วรันคำสั่งที่กำหนดไว้บนเครื่องปลายทางให้ ตัวอย่าง step ในไฟล์ workflow เช่น
- uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DROPLET_HOST }}
username: deploy
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/myapp
git pull origin main
docker compose up -d --build
ขั้นตอนก่อนหน้านั้นในไฟล์เดียวกันมักมี job สำหรับ build และ test ก่อน เช่น checkout โค้ดด้วย actions/checkout@v4 ติดตั้ง dependency รันเทสต์ แล้วค่อยส่งต่อไปยัง step deploy เฉพาะเมื่อเทสต์ผ่านทั้งหมด โดยใช้ needs: ผูก job deploy ให้รอ job test ก่อนเสมอ ป้องกันไม่ให้โค้ดที่เทสต์ไม่ผ่านหลุดขึ้น production
สำหรับ Droplet ที่รันแอปแบบ container ควรพิจารณาใช้ pattern แบบ pull image สำเร็จรูปแทนการ build โค้ดบนเครื่อง production โดยตรง เช่น workflow build image แล้ว push เข้า DigitalOcean Container Registry ก่อน จากนั้น step SSH เข้า Droplet แค่รัน docker pull และ docker compose up -d ดึง image ที่ build เสร็จแล้วมาใช้ วิธีนี้ทำให้เครื่อง production ไม่ต้องมี toolchain สำหรับ build ติดตั้งอยู่ และลดเวลา downtime ระหว่าง deploy เพราะแค่สลับ image ไม่ต้อง compile ใหม่บนเครื่องจริง
ขนาด Droplet ที่เหมาะสำหรับรันแอปเว็บทั่วไปเริ่มต้นที่ระดับ 1 GiB RAM / 1 vCPU ราคา $6 ต่อเดือน ซึ่งเพียงพอสำหรับโปรเจกต์ขนาดเล็กถึงกลาง ส่วน region ที่แนะนำสำหรับผู้ใช้ในไทยคือ sgp1 (Singapore) เพราะ latency ต่ำที่สุดเมื่อเทียบกับ region อื่นของ DigitalOcean หากแอปมีผู้ใช้งานส่วนใหญ่อยู่ในภูมิภาคเอเชียตะวันออกเฉียงใต้
- ใช้ action สำเร็จรูปอย่าง
appleboy/ssh-actionรับ host/username/private key แล้วรันคำสั่งบน Droplet ให้อัตโนมัติ - ผูก job deploy ให้รอ job test ผ่านก่อนด้วย
needs:ป้องกันโค้ดที่เทสต์ไม่ผ่านหลุดขึ้น production - แนวทางที่นิยม: build+push image เข้า Container Registry ก่อน แล้วให้ Droplet แค่
docker pull+docker compose up -d - ลด downtime เพราะเครื่อง production แค่สลับ image ไม่ต้อง compile ใหม่ทุกครั้ง
สร้าง Workflow deploy ไป App Platform
App Platform รองรับการเชื่อมกับ GitHub แบบ native อยู่แล้วผ่านหน้า control panel ซึ่งจะ auto-deploy ทุกครั้งที่ push เข้าสาขาที่ตั้งค่าไว้โดยไม่ต้องเขียน workflow เอง แต่หลายทีมยังเลือกควบคุม deploy ผ่าน GitHub Actions แทน เพราะต้องการรัน test/lint/build step ที่ซับซ้อนกว่าก่อน trigger deployment จริง หรือต้องการ deploy จาก monorepo เฉพาะบางโฟลเดอร์ที่มีการเปลี่ยนแปลง
เครื่องมือหลักที่ใช้คือ doctl ผ่าน GitHub Action ทางการ digitalocean/action-doctl@v2 ซึ่งติดตั้ง doctl ให้ในเครื่อง runner แล้ว authenticate ด้วย Personal Access Token ที่เก็บไว้ใน secret ตัวอย่าง step
- uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- run: doctl apps create-deployment ${{ secrets.DO_APP_ID }} --wait
คำสั่ง doctl apps create-deployment <app-id> --wait สั่งให้ App Platform เริ่ม deployment รอบใหม่จาก branch ที่ผูกไว้กับแอปนั้น และ flag --wait ทำให้ workflow บล็อกรอจนกว่า deployment จะเสร็จสมบูรณ์หรือ fail จริง แทนที่จะจบ job ทันทีทั้งที่ deploy ยังไม่เสร็จ ซึ่งสำคัญมากถ้า workflow มี step ทดสอบหลัง deploy ต่อท้าย
สำหรับการสร้างแอปใหม่หรือเปลี่ยนโครงสร้างของแอป (เช่น เพิ่ม environment variable, เปลี่ยน instance size) ใช้ไฟล์ spec แบบ YAML ที่เก็บไว้ใน repository เช่น app.yaml แล้วรัน doctl apps create --spec app.yaml สำหรับแอปใหม่ หรือ doctl apps update <app-id> --spec app.yaml สำหรับแอปที่มีอยู่แล้ว วิธีนี้ทำให้โครงสร้างแอปทั้งหมดถูกเก็บเป็นโค้ดใน git และตรวจสอบการเปลี่ยนแปลงผ่าน pull request ได้เหมือนโค้ดส่วนอื่น
ด้านค่าใช้จ่าย App Platform มีแผนแบบ container instance ให้เลือกตามขนาด เช่น ระดับเริ่มต้น shared 1 vCPU / 512 MiB RAM / 50 GiB transfer ราคา $5 ต่อเดือน ไปจนถึงระดับ shared 2 vCPU / 4 GiB RAM / 250 GiB transfer ที่ $50 ต่อเดือน ส่วนแผนฟรีรองรับเฉพาะ static site สูงสุด 3 แอป และ 1 GiB transfer ต่อแอป เหมาะสำหรับทดสอบ workflow ก่อนใช้งานจริง
- App Platform เชื่อม GitHub แบบ native อยู่แล้ว (auto-deploy) — ใช้ GitHub Actions เพิ่มเมื่อต้องการ custom build/test step ก่อน deploy
digitalocean/action-doctl@v2ติดตั้ง doctl ใน runner แล้ว authenticate ด้วยsecrets.DIGITALOCEAN_ACCESS_TOKENdoctl apps create-deployment <app-id> --waitเริ่ม deployment ใหม่และรอจนเสร็จก่อนไป step ถัดไปdoctl apps create --spec app.yaml/doctl apps update <app-id> --spec app.yamlเก็บโครงสร้างแอปเป็นโค้ดตรวจสอบผ่าน PR ได้
เก็บ Secret (SSH Key/API Token) อย่างปลอดภัย
ไม่ว่าจะ deploy ไป Droplet หรือ App Platform ข้อมูลอ่อนไหวอย่าง SSH private key และ DigitalOcean Personal Access Token ต้องไม่ถูกเขียนตรงในไฟล์ workflow เด็ดขาด GitHub มีระบบ encrypted secrets ในตัวอยู่แล้ว เข้าถึงได้ที่หน้า repository settings ภายใต้ Settings > Secrets and variables > Actions โดยค่าที่บันทึกไว้จะถูกเข้ารหัสและไม่แสดงในหน้า log ของ workflow แม้แต่เจ้าของ repository เองก็ดูค่าเดิมย้อนหลังไม่ได้ ทำได้แค่สร้างใหม่ทับ
สำหรับ SSH key ควรสร้างคู่ key แยกเฉพาะสำหรับใช้ deploy จาก CI/CD เท่านั้น ไม่ใช้ key เดียวกับที่นักพัฒนาใช้ SSH เข้าเครื่องส่วนตัว ด้วยคำสั่ง ssh-keygen -t ed25519 -C "github-actions-deploy" แล้วนำ public key ไปใส่ใน ~/.ssh/authorized_keys ของ user ที่ใช้ deploy บน Droplet (แนะนำสร้าง user แยกชื่อ deploy ที่มีสิทธิ์จำกัดเฉพาะโฟลเดอร์แอป ไม่ใช้ root โดยตรง) ส่วน private key นำไปวางในค่า secret ชื่อ SSH_PRIVATE_KEY โดยคัดลอกทั้งไฟล์รวม header/footer แบบ -----BEGIN OPENSSH PRIVATE KEY----- ให้ครบ
สำหรับ Personal Access Token ของ DigitalOcean สร้างที่หน้า cloud.digitalocean.com/account/api/tokens และควรสร้างแยกเฉพาะสำหรับ CI/CD ไม่ใช้ token เดียวกับที่ใช้บนเครื่องส่วนตัว เพื่อให้ revoke ได้ทันทีหากมีปัญหาโดยไม่กระทบการทำงานอื่น เก็บไว้ใน secret ชื่อ DIGITALOCEAN_ACCESS_TOKEN และเลือกสิทธิ์เฉพาะที่จำเป็น เช่น ถ้า workflow แค่ trigger deployment ของแอปที่มีอยู่แล้วไม่จำเป็นต้องให้สิทธิ์สร้าง/ลบทรัพยากรอื่นทั้งหมด
สำหรับทีมที่มีหลายสภาพแวดล้อม เช่น staging และ production ควรใช้ฟีเจอร์ GitHub Environments แยก secret ของแต่ละสภาพแวดล้อมออกจากกัน เช่น secret ชื่อเดียวกันแต่ค่าต่างกันระหว่าง environment staging กับ production พร้อมตั้ง required reviewers ให้ต้องมีคนอนุมัติก่อน job ที่ deploy เข้า production จะรันจริง เป็นการป้องกันชั้นเพิ่มเติมนอกเหนือจากการเก็บ secret ให้ปลอดภัย
- ใช้ GitHub encrypted secrets เสมอ (Settings > Secrets and variables > Actions) ห้ามเขียน key/token ตรงในไฟล์ YAML
- สร้าง SSH key คู่ใหม่เฉพาะสำหรับ CI/CD ด้วย
ssh-keygen -t ed25519แยกจาก key ส่วนตัว และใช้ user จำกัดสิทธิ์ ไม่ใช้ root - สร้าง DigitalOcean Personal Access Token แยกเฉพาะ CI/CD ที่ cloud.digitalocean.com/account/api/tokens เพื่อ revoke ได้โดยไม่กระทบส่วนอื่น
- ใช้ GitHub Environments แยก secret ระหว่าง staging/production พร้อมตั้ง required reviewers ก่อน deploy เข้า production
- ให้สิทธิ์ token เท่าที่จำเป็น (least privilege) และหมั่น rotate token/key เป็นระยะ
Rollback เมื่อ Deploy ล้มเหลว
การวางแผน rollback ควรทำไว้ตั้งแต่ตอนออกแบบ workflow ไม่ใช่ค่อยคิดตอนเกิดปัญหาจริง สำหรับ Droplet ที่ deploy ผ่าน SSH วิธีที่นิยมคือ pattern แบบเก็บหลาย release ไว้ในโฟลเดอร์แยกกัน เช่น /var/www/myapp/releases/<timestamp> แล้วใช้ symlink ชื่อ current ชี้ไปยัง release ที่ใช้งานอยู่จริง เมื่อ deploy รอบใหม่ล้มเหลวหรือพบปัญหาหลัง deploy สามารถสั่งย้าย symlink กลับไปชี้ release เดิมได้ทันทีด้วยคำสั่งเดียว โดยไม่ต้อง build หรือ pull โค้ดใหม่
ถ้าใช้ container บน Droplet การ rollback ทำได้ง่ายกว่าด้วยการ tag image ทุกครั้งที่ build ด้วย commit SHA แทนการใช้ tag latest อย่างเดียว เช่น push image เป็น myapp:${{ github.sha }} เมื่อต้อง rollback แค่รัน docker compose up -d โดยเปลี่ยนค่า image tag ในไฟล์ compose กลับไปเป็น SHA ของ commit ก่อนหน้าที่รู้ว่าใช้งานได้ปกติ แล้ว restart service เดียวก็กลับสู่สถานะเดิม
สำหรับ App Platform มีระบบเก็บประวัติ deployment ให้อยู่แล้วในตัว ตรวจสอบรายการ deployment ที่ผ่านมาด้วย doctl apps list-deployments <app-id> ซึ่งแสดง deployment ID พร้อมสถานะและเวลาของแต่ละรอบ หากรอบล่าสุด fail หรือมีปัญหาหลัง deploy สามารถสั่ง deploy ซ้ำจาก commit ก่อนหน้าได้โดยตรง หรือในบางกรณี App Platform จะ auto-rollback ให้เองถ้า deployment ใหม่ fail ระหว่าง build/health check โดยยังให้เวอร์ชันเดิมทำงานต่อจนกว่า deployment ใหม่จะสำเร็จ
เพื่อลดความเสี่ยงตั้งแต่ต้น ควรเพิ่ม step smoke test ต่อท้าย workflow หลัง deploy เสร็จ เช่น curl endpoint health check ของแอปแล้วตรวจ HTTP status code หรือเนื้อหาตอบกลับที่คาดหวัง หาก step นี้ fail ให้ workflow ตีกลับเป็นสถานะ failed ทันทีเพื่อแจ้งเตือนทีมเร็วที่สุด แทนที่จะปล่อยให้ deployment ที่มีปัญหาทำงานอยู่โดยไม่มีใครรู้จนกว่าผู้ใช้จะแจ้งปัญหาเข้ามาเอง
- Droplet: ใช้ pattern releases/<timestamp> + symlink current ชี้ release ที่ใช้งานจริง ย้าย symlink กลับได้ทันทีเมื่อต้อง rollback
- Container: tag image ด้วย commit SHA แทน latest อย่างเดียว เพื่อ rollback ได้แม่นยำโดยเปลี่ยนแค่ tag แล้ว restart
- App Platform:
doctl apps list-deployments <app-id>ดูประวัติ deployment แล้ว deploy ซ้ำจาก commit ก่อนหน้าได้
เมื่อไหร่ควรใช้ฟีเจอร์นี้ (Use Case จริง)
การลงทุนเขียน GitHub Actions workflow สำหรับ deploy คุ้มค่าที่สุดกับทีมหรือโปรเจกต์ที่ push โค้ดบ่อย เช่น หลายครั้งต่อวันหรือต่อสัปดาห์ เพราะทุกครั้งที่ประหยัดเวลาจากการไม่ต้อง SSH เข้าเครื่องเองจะยิ่งคุ้มค่าตามความถี่ที่ deploy โปรเจกต์ที่มีทีมมากกว่าหนึ่งคนก็ได้ประโยชน์ชัดเจน เพราะทุกคนใช้ขั้นตอน deploy เดียวกันแทนแต่ละคนมีวิธีของตัวเอง ลดปัญหา "deploy ได้บนเครื่องฉัน แต่พังตอน deploy จริง" สำหรับโปรเจกต์ที่มี environment แยกชัดเจน เช่น staging สำหรับทดสอบก่อนแล้วค่อย promote ขึ้น production, workflow อัตโนมัติช่วยให้ deploy ขึ้น staging ทุกครั้งที่ merge เข้า branch develop ได้ทันทีโดยไม่ต้องรอคน แล้วเก็บขั้นตอน deploy เข้า production ไว้ที่ merge เข้า main พร้อม required reviewer เป็นด่านสุดท้าย ทำให้ทีมทดสอบ feature ใหม่บน staging ได้เร็วโดยไม่กระทบผู้ใช้จริง ในทางกลับกัน โปรเจกต์ทดลองส่วนตัวขนาดเล็กที่ deploy นานๆ ครั้ง หรือมีนักพัฒนาคนเดียวที่คุ้นเคยกับขั้นตอน deploy ด้วยมืออยู่แล้ว อาจไม่คุ้มกับเวลาที่ใช้เซ็ตอัพ workflow ตั้งแต่แรก ในกรณีนี้การ SSH เข้าเครื่องแล้วรันสคริปต์ deploy ธรรมดา หรือใช้ระบบ auto-deploy แบบ native ของ App Platform ที่ผูกกับ GitHub โดยตรงโดยไม่ต้องเขียน workflow เองเลยอาจตอบโจทย์ได้เร็วกว่า อีกกรณีที่ควรใช้ GitHub Actions แทนการพึ่ง auto-deploy แบบ native ล้วนๆ คือเมื่อ pipeline ต้องมีขั้นตอนที่ซับซ้อนกว่าการ build ธรรมดา เช่น รัน database migration ก่อน restart service, รัน integration test ที่ต้องต่อกับฐานข้อมูลจริงก่อน deploy, แจ้งเตือนทีมผ่าน Slack เมื่อ deploy สำเร็จหรือล้มเหลว หรือ deploy จาก monorepo ที่ต้องเลือก build เฉพาะ service ที่มีการเปลี่ยนแปลงจริงเท่านั้น ซึ่งเป็นสิ่งที่ทำได้ยากถ้าพึ่งแค่ auto-deploy พื้นฐานของแพลตฟอร์มอย่างเดียว
- คุ้มค่าที่สุดกับทีม/โปรเจกต์ที่ push โค้ดบ่อย (หลายครั้งต่อวัน-สัปดาห์) และมีมากกว่าหนึ่งคนในทีม
- เหมาะกับโปรเจกต์ที่มี staging/production แยกกัน ให้ deploy staging อัตโนมัติแต่ต้องมีคนอนุมัติก่อนเข้า production
- โปรเจกต์ทดลองส่วนตัว deploy นานๆ ครั้ง อาจไม่คุ้มกับเวลาเซ็ตอัพ — ใช้ SSH ธรรมดาหรือ auto-deploy native ของ App Platform แทนได้
- ใช้เมื่อต้องมีขั้นตอนซับซ้อนกว่า build ปกติ เช่น migration ฐานข้อมูล, integration test, แจ้งเตือน Slack, deploy เฉพาะ service ใน monorepo
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
จุดที่ผู้ใช้มักถามมาคือ error ที่พบบ่อยที่สุดในขั้นตอน SSH deploy คือ "Permission denied (publickey)" ซึ่งมักเกิดจากสาเหตุใดสาเหตุหนึ่งในสามอย่าง คือ public key ยังไม่ได้ใส่ใน ~/.ssh/authorized_keys ของ user ปลายทางให้ถูกต้อง, private key ที่วางใน secret มีการตัดบรรทัดหรือ format เพี้ยนระหว่างคัดลอก (ควรคัดลอกทั้งไฟล์รวม header/footer ให้ครบ ไม่ตัดบรรทัดว่างท้ายไฟล์ออก) หรือ user/permission ของโฟลเดอร์ .ssh บนเครื่องปลายทางตั้งค่าผิด เช่น permission กว้างเกินไปทำให้ SSH daemon ปฏิเสธ key นั้นด้วยเหตุผลด้านความปลอดภัย
อีก error ที่พบบ่อยเมื่อใช้ doctl ใน workflow คือ 401 Unauthorized ทั้งที่ตั้งค่า secret แล้ว มักเกิดจากชื่อ secret ในไฟล์ workflow ไม่ตรงกับชื่อที่สร้างไว้จริงในหน้า repository settings (เช่นพิมพ์ผิดตัวพิมพ์เล็ก-ใหญ่) หรือ token ที่ใช้หมดอายุ/ถูก revoke ไปแล้ว วิธีตรวจสอบคือเพิ่ม step ทดสอบง่ายๆ อย่าง doctl account get ไว้ต้นๆ workflow เพื่อยืนยันว่า authenticate ผ่านจริงก่อนไปขั้นตอน deploy จริง
กรณี workflow รันผ่านสำเร็จทุก step แต่แอปที่รันอยู่จริงไม่มีการเปลี่ยนแปลง มักมาจากสาเหตุที่ลืมเติม step restart service หลัง pull โค้ดใหม่ เช่น pull โค้ดเสร็จแต่ process เดิมยังรันโค้ดเวอร์ชันก่อนหน้าอยู่ในหน่วยความจำ ต้องมี step restart เช่น systemctl restart myapp หรือ docker compose up -d --build ต่อท้ายเสมอ อีกสาเหตุที่พบบ่อยคือเงื่อนไข trigger ของ workflow ผิด เช่น ตั้งเป็น branches: [master] ทั้งที่ repository เปลี่ยนไปใช้ main แล้ว ทำให้ workflow ไม่ทำงานเลยแม้จะ push จริง
สำหรับ App Platform error ที่พบบ่อยคือ "unable to update app: spec is invalid" ซึ่งเกิดจากไฟล์ app.yaml มี field ที่พิมพ์ผิดหรือใช้ syntax ไม่ตรงตามที่ App Platform รองรับ วิธีแก้คือทดสอบ validate spec ก่อน apply จริงด้วยคำสั่ง doctl apps spec validate app.yaml ก่อนจะรัน create/update จริงในขั้นตอนถัดไปของ workflow เพื่อจับข้อผิดพลาดตั้งแต่ก่อน deploy
- "Permission denied (publickey)": ตรวจ public key ใน authorized_keys, format ของ private key ใน secret ให้ครบ header/footer, permission ของโฟลเดอร์ .ssh
- 401 Unauthorized จาก doctl: เช็คชื่อ secret ให้ตรงตัวพิมพ์เล็ก-ใหญ่ และเพิ่ม step
doctl account getทดสอบ auth ก่อน deploy จริง - Workflow ผ่านแต่แอปไม่เปลี่ยน: ลืม step restart เช่น
systemctl restart myappหรือdocker compose up -d --buildหลัง pull โค้ด - Workflow ไม่ทริกเกอร์เลย: ตรวจ
branches:ในเงื่อนไข on.push ให้ตรงกับชื่อสาขาจริง (main vs master)
แนวทางปฏิบัติที่ดีที่สุด (Best Practices)
การเขียน workflow ให้ปลอดภัยและดูแลรักษาง่ายในระยะยาวมีหลายแนวทางที่ควรทำตั้งแต่ต้น เริ่มจากการ pin เวอร์ชันของ action ที่ใช้ให้ชัดเจน เช่น actions/checkout@v4 แทนการใช้ @main หรือ tag ที่เปลี่ยนแปลงได้ เพื่อป้องกันไม่ให้ action เวอร์ชันใหม่ที่ผู้พัฒนาปล่อยออกมาโดยไม่ได้ตั้งใจมาทำลาย workflow ที่เคยทำงานได้ปกติ สำหรับทีมที่ต้องการความปลอดภัยสูงสุดอาจ pin ไปถึงระดับ commit SHA แทน tag เพราะ tag สามารถถูกย้ายไปชี้ commit อื่นได้ในภายหลัง
ควรแยก job build/test ออกจาก job deploy อย่างชัดเจนด้วย needs: เสมอ ให้ deploy รันได้เฉพาะเมื่อ test ผ่านทั้งหมดแล้วเท่านั้น และควรเพิ่ม concurrency: group ในระดับ workflow เพื่อป้องกันไม่ให้มี deployment สองรอบทำงานซ้อนกันพร้อมกัน เช่น กรณี push สองครั้งติดกันเร็วๆ ซึ่งอาจทำให้เกิดสถานะไม่แน่นอนบนเครื่อง production ถ้าปล่อยให้ deploy สองรอบแข่งกันเขียนไฟล์เดียวกัน
ด้าน environment ควรใช้ GitHub Environments แยก staging กับ production ให้ชัดเจน พร้อมตั้ง required reviewers สำหรับ production เสมอ แม้แต่ทีมขนาดเล็กก็ควรมีขั้นตอนอนุมัติก่อน deploy เข้า production อย่างน้อยหนึ่งชั้น เพื่อลดโอกาส deploy โดยไม่ตั้งใจจาก merge ที่พลาด และควรเก็บ log การ deploy ไว้ตรวจสอบย้อนหลังได้ เช่นใช้ doctl apps list-deployments สำหรับ App Platform หรือเขียน log ลงไฟล์บน Droplet ทุกครั้งที่ deploy สำเร็จ
สุดท้าย ควรทดสอบ workflow ทั้งเส้นทางบน environment staging ก่อนนำไปใช้กับ production จริงเสมอ รวมถึงทดสอบ path การ rollback ด้วยไม่ใช่แค่ path การ deploy สำเร็จ เพราะ workflow ที่ไม่เคยถูกทดสอบตอน fail มักมีปัญหาซ่อนอยู่ที่จะเจอตอนต้องการใช้งานจริงที่สุด ซึ่งมักเป็นช่วงเวลาที่กดดันที่สุดเช่นกัน
- Pin เวอร์ชัน action ให้ชัดเจน เช่น
@v4หรือระดับ commit SHA แทน@mainป้องกัน action เปลี่ยนแปลงโดยไม่ตั้งใจ - แยก job test/build ออกจาก deploy ด้วย
needs:ให้ deploy รันเฉพาะเมื่อ test ผ่านแล้วเท่านั้น - เพิ่ม
concurrency:group ป้องกัน deployment สองรอบทำงานซ้อนกันจาก push ถี่ๆ