14.3 Deploy เวอร์ชันใหม่
บทนี้อธิบายขั้นตอนการ deploy สำหรับทั้ง 5 services รวมถึง rollback
ภาพรวมกระบวนการ deploy
| Service | pipeline auto? | มี image ใน GCR | วิธี deploy จริง |
|---|---|---|---|
| vtrc-api | ✅ push image | ✅ | manual docker pull + docker-compose up -d |
| vtrc-web | ✅ push image | ✅ | manual docker pull + docker-compose up -d |
| vtrc-rc-backoffice | ❌ | ❌ | build image ใน host + docker-compose up -d |
| centralize-api | ❌ | ❌ | build image ใน host + docker-compose up -d prod |
| cu-central-api | ❌ | ✅ (มีใน compose) | manual docker pull + docker-compose up -d |
สรุป: pipeline push image จบ — การ deploy ไป server ทำด้วยมือทุกครั้ง
ขั้นตอนก่อน deploy
1. สำรองข้อมูล (ถ้าเกี่ยวข้อง)
# backup database (รายละเอียดบท 13.5)
mysqldump -h <db-host> -u root -p vtrc > /backup/vtrc-$(date +%Y%m%d-%H%M).sql2. แจ้งผู้ใช้ล่วงหน้า
- ระบุเวลาที่จะ deploy และ downtime ที่คาดไว้
- แจ้งช่องทางที่จะแจ้งสถานะ (อีเมล, LINE, etc.)
3. ตรวจสอบ image tag ที่จะ deploy
# ดู tag ล่าสุดใน GCR (ต้องลงชื่อเข้า gcloud)
gcloud container images list-tags gcr.io/vtrc-nonprod/vtrc-api--prodจด tag ปัจจุบันไว้สำหรับ rollback
Deploy vtrc-api
ขั้นตอน
cd /home/dev/vtrc-api/
# 1. ดึง image ใหม่
docker pull gcr.io/vtrc-nonprod/vtrc-api--prod:latest
# 2. ตรวจสอบว่า image มาถึง
docker images | grep vtrc-api
# 3. deploy ทีละ replica เพื่อรักษา uptime
# replica 1
docker-compose up -d --no-deps vtrc-api
sleep 15
docker logs --tail 30 vtrc-api-nodejs
# รอจนกว่าจะเห็น "Server ready"
# ทดสอบ
curl -I https://vtrcapi.redcross.or.th/api/graphql
# replica 2
docker-compose up -d --no-deps vtrc-api-2
sleep 15
docker logs --tail 30 vtrc-api-nodejs-2
# replica 3
docker-compose up -d --no-deps vtrc-api-3
sleep 15
docker logs --tail 30 vtrc-api-nodejs-3
# replica 5
docker-compose up -d --no-deps vtrc-api-5
sleep 15
docker logs --tail 30 vtrc-api-nodejs-5Rollback vtrc-api
# 1. หา tag ก่อนหน้า
gcloud container images list-tags gcr.io/vtrc-nonprod/vtrc-api--prod
# 2. แก้ docker-compose.yml เพื่อระบุ tag ก่อนหน้า
# image: gcr.io/vtrc-nonprod/vtrc-api--prod:<previous-tag>
# 3. pull และ restart ทีละ replica
docker pull gcr.io/vtrc-nonprod/vtrc-api--prod:<previous-tag>
docker-compose up -d --no-deps vtrc-api
# ... ทำซ้ำกับทุก replicaDeploy vtrc-web
vtrc-web เป็น static file ที่ nginx serve จาก volume ./build
ขั้นตอน
cd /home/dev/vtrc-web/
# 1. pull image ใหม่
docker pull gcr.io/vtrc-nonprod/vtrc-web--prod:latest
# 2. สร้าง container ชั่วคราวเพื่อคัดลอก build
docker create --name vtrc-web-tmp gcr.io/vtrc-nonprod/vtrc-web--prod:latest
docker cp vtrc-web-tmp:/usr/share/nginx/html/build ./build-new
# 3. สำรอง build เดิม
mv /home/dev/vtrc-web/build /home/dev/vtrc-web/build-backup-$(date +%Y%m%d-%H%M)
# 4. ย้าย build ใหม่เข้าที่
mv ./build-new /home/dev/vtrc-web/build
# 5. ลบ container ชั่วคราว
docker rm vtrc-web-tmp
# 6. ทดสอบ
curl -I https://vtrc.redcross.or.th/หมายเหตุ: วิธีนี้อาจไม่ตรงกับที่ทีมทำจริง — บางทีอาจ build ใน host โดยตรง ต้องสอบถามทีม
Rollback vtrc-web
# คืน build สำรอง
rm -rf /home/dev/vtrc-web/build
mv /home/dev/vtrc-web/build-backup-<timestamp> /home/dev/vtrc-web/buildDeploy vtrc-rc-backoffice
ขั้นตอน
cd /home/dev/vtrc-rc-backoffice/
# 1. pull source ใหม่ (หรือ git pull)
git pull origin prod
# 2. build (ต้องใช้ host ที่มี Node 16 และ npm)
CI=false GENERATE_SOURCEMAP=false NODE_OPTIONS=--max-old-space-size=4096 npm run build
# 3. สำรอง build เดิม
mv build build-backup-$(date +%Y%m%d-%H%M)
# 4. ย้าย build ใหม่
# build ถูกสร้างใน ./build/ โดย react-scripts
# 5. restart nginx container
docker-compose restart web-server
# 6. ทดสอบ
curl -I https://vtrcbackoffice.redcross.or.th/Rollback
rm -rf build
mv build-backup-<timestamp> build
docker-compose restart web-serverDeploy centralize-api
ขั้นตอน
cd /home/dev/centralize-api/
# 1. pull source ใหม่
git pull origin main
# 2. build image ในเครื่อง (ไม่มี CI สำหรับ service นี้)
docker-compose build prod
# 3. deploy
docker-compose up -d prod
# 4. ตรวจสอบ log
docker-compose logs --tail 50 prod
# 5. ทดสอบ — Swagger UI ควรเข้าได้
curl -I http://localhost:3000/apiRollback
# กลับไป commit ก่อนหน้า
git checkout <previous-commit>
docker-compose build prod
docker-compose up -d prodDeploy cu-central-api
ขั้นตอน
cd /home/dev/cu-central-api/
# 1. ดึง image ใหม่
docker pull gcr.io/vtrc-nonprod/cu-centralize-api:latest
# 2. deploy ทีละ replica
docker-compose up -d --no-deps cu-cen-api
sleep 15
docker logs --tail 30 cu-central-api
docker-compose up -d --no-deps cu-cen-api_2
sleep 15
docker logs --tail 30 cu-central-api_2
docker-compose up -d --no-deps cu-cen-api_3
sleep 15
docker logs --tail 30 cu-central-api_3Rollback
เช่นเดียวกับ vtrc-api — ระบุ tag ก่อนหน้าใน docker-compose.yml
สิ่งที่ต้องทอดสอบหลัง deploy
แม้จะไม่มี automated test, ควรทดสอบด้วยมือตาม checklist:
vtrc-api
- [ ] GraphQL playground เข้าได้ที่
/api/graphql - [ ] login ผ่านด้วยบัญชีทดสอบ
- [ ] ดึงข้อมูล profile สำเร็จ
- [ ] สร้าง leave request สำเร็จ (จะไม่ submit จริง แค่ draft)
- [ ] download payslip PDF สำเร็จ
vtrc-web
- [ ] หน้า login โหลด
- [ ] login สำเร็จ
- [ ] นำทางไปหน้าหลักสำเร็จ
- [ ] ตรวจสอบ browser console ไม่มี error
vtrc-rc-backoffice
- [ ] หน้า login โหลด
- [ ] login ด้วยบัญชี admin สำเร็จ
- [ ] ดู report สำเร็จ
centralize-api
- [ ] Swagger UI ที่
/docsโหลด - [ ] เรียก endpoint
/api/...สำเร็จ
cu-central-api
- [ ] เรียก GraphQL endpoint ผ่าน vtrc-api สำเร็จ (login ผ่าน vtrc-web แล้ว profile กลับมา)
ปัญหาที่พบบ่อยตอน deploy
1. image pull ไม่สำเร็จ
Error response from daemon: pull access deniedสาเหตุ: ไม่ได้ลงชื่อเข้า gcloud Docker registry
แก้:
gcloud auth configure-docker
docker pull ...2. database schema เปลี่ยนระหว่าง deploy
อาการ: หลัง deploy service รัน แต่ query บางอย่าง fail
สาเหตุ: syncModelToTable() รันตอน Node start และอัปเดต schema ตาม code ใหม่
แก้: ดู log ของ vtrc-api ว่ามีการ alter table หรือไม่ ถ้า rollback ต้อง rollback schema ด้วย
3. cron job รันซ้อน
ถ้า deploy ทั้งหมดในเวลาใกล้เคียงกัน และ APP_NO=1 กลับมา online พร้อมกับ replica อื่น ๆ อาจมีกรณี cron รันซ้อน (edge case)
แก้: deploy APP_NO=1 เป็นตัวสุดท้าย และรอให้ replica อื่น stable ก่อน
4. nginx ส่งไป replica ที่ยัง boot อยู่
อาการ: ผู้ใช้ได้รับ 502 หรือ connection refused หลัง deploy
สาเหตุ: nginx upstream ส่ง traffic ไป port ที่ Node ยังไม่ฟัง
แก้: รอ docker logs แสดง "Server ready" ก่อน restart replica ถัดไป
แนวทางที่ดีขึ้น
การ deploy ด้วยมือเป็นแหล่งของความผิดพลาด — แนะนำให้:
- เขียน deploy script ที่ automate ขั้นตอนข้างต้น
- ใช้ Watchtower สำหรับ auto-update container เมื่อ image ใหม่มาถึง
- สร้าง pipeline สำหรับทุก repo ไม่ใช่แค่ 2 แรก
- เพิ่ม health check และใช้
depends_on: condition: service_healthyใน compose - ใช้ blue-green deploy แทน rolling ถ้าเป็นไปได้
รายละเอียดเพิ่มเติมอยู่ใน Volume 15 (Modernization Plan)