DEV Community

reerii code
reerii code

Posted on

Deploy API เเบบ VPS โดยใช้ AWS EC2(free) Express, Prisma, Google login ,Superbase

หลังจากที่เราได้เข้าใจการ deploy คร่าว ๆ แล้ว จาก deploy คืออะไร?

เรามาลองทำจริงกันเลยดีกว่า ก่อนอื่นเราจะใช้เจ้าไหนดี อันนี้แล้วแต่ชอบเลย มีทั้ง Oracle Cloud, Google Cloud, AWS

แน่นอนผมต้องใช้ของฟรีอยู่แล้ว เลือกมาหนึ่งเจ้า ผมเลือก AWS ละกัน ต้องบอกก่อนว่าเป้าหมายของเราคือ ต้องการเครื่อง server หรือคอมพิวเตอร์ 1 เครื่องที่อยู่บน cloud ซึ่งจะได้เลข IP Public มา นั่นคือที่อยู่ของ server เรา และนั่นแหละคือสิ่งที่เราต้องการ

ไปที่เว็บ AWS

ก่อนอื่นต้องสมัครก่อนเว้ย

สิ่งที่เขาให้เราคือเครดิต 100 ดอลลาร์ตอนสมัคร และได้เพิ่มอีกสูงสุด 100 ดอลลาร์เมื่อลองใช้บริการพื้นฐานของ AWS (ไปอ่านเพิ่มเติมได้นะ) หลังจากนั้นก็ยืนยัน Gmail ให้เรียบร้อย แล้วก็ใส่เลขบัตรให้เรียบร้อยด้วย ไม่ต้องกลัวโดนหักเงินนะจ๊ะ

Free Plan: ไม่มีค่าใช้จ่ายจนกว่าจะเปลี่ยนเป็น Paid Plan แต่ใช้ได้ไม่ครบทุกบริการ ถ้าลืมปิดเครื่อง ค่าใช้จ่ายจะหักจากเครดิต ไม่ใช่บัตรเครดิต ข้อควรระวังคือ AWS จะปิดบัญชีอัตโนมัติเมื่อครบ 6 เดือน หรือเมื่อเครดิตหมด แล้วแต่อย่างไหนถึงก่อน และขยายเกิน 6 เดือนไม่ได้ เว้นแต่จะอัปเกรดเป็น Paid Plan (หลังบัญชีปิด ยังมีเวลา 90 วันให้อัปเกรดเพื่อเปิดบัญชีกลับมาได้)

เลือก Free แล้วกด Complete sign up ได้เลย
หลังจากนั้นจะมาที่หน้านี้


แน่นอนว่าต้องไปที่ Console


เปิด sidebar


ไปที่ EC2

EC2 คืออะไร?

EC2 (Elastic Compute Cloud) คือบริการเช่าเครื่องเซิร์ฟเวอร์เสมือน (virtual machine) บน AWS เทียบได้กับ VPS ทั่วไป คือได้เครื่อง Linux มา 1 เครื่อง
ไปที่ Instances (instance = เครื่องเซิร์ฟเวอร์หนึ่งเครื่อง)

แน่นอนว่าเราเลือกระบบปฏิบัติการที่ถนัดได้เลย
ปกติก็จะเลือก Ubuntu
คิดว่าผมจะเลือก Ubuntu ล่ะสิ ไม่! ผมจะเลือก Amazon Linux ทำไมน่ะหรอ ก็เพราะไม่เคยลองไง แค่อยากลองเฉย ๆ hahaha!

เออ! ตามนี้ละกัน ที่จริงไม่ต้องตั้งค่าอะไรเลย ใช้ค่าเริ่มต้นได้ แต่ที่เราต้องสนใจคือ Key pair

Key pair คืออะไร?

Key pair คือ "กุญแจ" สำหรับเข้าเครื่อง instance ผ่าน SSH ใช้แทนรหัสผ่าน มี 2 ชิ้นที่ทำงานคู่กัน

  • Public key (กุญแจสาธารณะ): AWS ฝังไว้ในเครื่อง instance ให้ เปรียบเหมือนแม่กุญแจที่ติดไว้หน้าประตู
  • Private key (กุญแจส่วนตัว): ไฟล์ที่เราดาวน์โหลดเก็บไว้ เปรียบเหมือนลูกกุญแจ ใครมีไฟล์นี้ก็เข้าเครื่องได้

หยุด ๆ ๆ ไม่ต้องอธิบายยาวขนาดนั้นก็ได้ ที่จริงมันคือไฟล์ที่เอาไว้ login แค่มีไฟล์นี้ก็เข้าไปใช้งานเครื่อง server ได้แล้ว (เพราะงั้นห้ามทำหาย และห้ามส่งให้ใครเด็ดขาด)


ตั้งชื่อไฟล์ แล้วเลือก type จะเป็น RSA หรือ ED25519 ก็ได้ ED25519 ใหม่กว่าและสั้นกว่า แต่ RSA รองรับโปรแกรมได้กว้างกว่า ผมเลือกตามนี้ ส่วนนามสกุลไฟล์แล้วแต่เลย (ใช้ CMD/Terminal ให้เลือก .pem ถ้าใช้ PuTTY ให้เลือก .ppk) พอกด Create key pair มันก็จะดาวน์โหลดไฟล์ให้ทันที ง่ายยยยย

ส่วนอันนี้คือ

  • Allow SSH traffic from (พอร์ต 22): ช่องสำหรับเข้าไปสั่งงานเครื่องผ่าน terminal ต้องติ๊กไว้ ไม่อย่างนั้นเข้าเครื่องไม่ได้นะเว้ย ส่วนช่องข้าง ๆ แนะนำให้เลือก My IP (เฉพาะ IP เราเข้าได้) ถ้าเลือก Anywhere บอตทั่วโลกจะมาสุ่มเจาะพอร์ตนี้ทั้งวัน
  • Allow HTTPS traffic from the internet (พอร์ต 443): สำหรับเว็บแบบ https:// ใช้ตอนเรามี domain แล้ว
  • Allow HTTP traffic from the internet (พอร์ต 80): สำหรับเว็บแบบ http://

ที่จริงอันนี้ต้องอธิบายอีกทีในหัวข้อ Nginx ว่าทำไมต้องเปิดพอร์ตเหล่านี้ด้วย ตอนนี้ติ๊กแค่ข้อแรกก่อน
ที่เหลือใช้ค่าเริ่มต้นไปเลย ไว้มาแก้ทีหลังได้


กดสร้างเครื่องที่ Launch instance แค่นี้เราก็ได้เครื่อง server มาแล้ว ง่ายยยยยยยใช่ไหมล่ะ

EZ

หลังจากนั้นกด View all instances


และนี่คือเครื่องที่เราสร้างเมื่อกี้ มีสถานะเป็น Running คือกำลังทำงานอยู่
กดเข้าไปก็จะเห็นรายละเอียดต่าง ๆ ที่เรากำหนดไว้

และแน่นอนว่าสิ่งที่เราอยากได้ก็คือเลข IP Public นั่นเอง

⚠️ ระวัง: ถ้าเรากด Stop แล้ว Start เครื่องใหม่ เลข IP Public จะเปลี่ยน (แค่ Reboot ไม่เปลี่ยน) ถ้าอยากได้ IP ถาวรให้ไปขอ Elastic IP มาผูกกับเครื่อง (มีค่าใช้จ่าย หักจากเครดิต)

หลังจากนั้นเลื่อนลงมา

กดที่ Security เพื่อดูว่าเราเปิดพอร์ตอะไรไว้บ้าง

พอร์ต 22 คือที่เราจะ login เข้าไปแบบ SSH ส่วน 443 กับ 80 เป็นเรื่องของ Nginx นะจ๊ะ hahaha

การ login ด้วยไฟล์ .pem

เปิด CMD ในโฟลเดอร์ที่มีไฟล์ .pem (ของผมอยู่ที่ Downloads) แล้วใช้คำสั่ง

ssh -i ชื่อไฟล์.pem ec2-user@IP-Public
Enter fullscreen mode Exit fullscreen mode

ใครใช้ Mac หรือ Linux ต้องสั่ง chmod 400 ชื่อไฟล์.pem ก่อน ไม่งั้น ssh จะไม่ยอมให้เข้า พร้อมขึ้นข้อความ UNPROTECTED PRIVATE KEY FILE

เข้าเครื่องได้แล้ว ต่อไปเราต้องการ clone project ของเราลงมา ซึ่งต้องใช้คำสั่ง Git ใช่ไหมล่ะ ก็ต้องติดตั้ง git ก่อน แล้วก็ Node ด้วย เพราะเราจะรัน API Express ของเรา

sudo dnf install -y git
Enter fullscreen mode Exit fullscreen mode


curl -fsSL https://rpm.nodesource.com/setup_22.x | sudo bash -
sudo dnf install -y nodejs
Enter fullscreen mode Exit fullscreen mode

เลือกเวอร์ชันให้ตรงกับที่ API เราใช้ (ตัวอย่างนี้คือ 22)


node -v && npm -v
Enter fullscreen mode Exit fullscreen mode

เช็กเวอร์ชันของ Node กับ npm
แค่นี้ก็เรียบร้อย ไปขั้นตอนถัดไปกัน

โหลด Project ลงมา

git clone url
Enter fullscreen mode Exit fullscreen mode

ถ้า project เป็น Public ก็ clone ได้เลย แต่ถ้าเป็น Private ล่ะ เราต้องยืนยันว่า project นี้เป็นของเราจริง ๆ ทำยังไง? ก็เอา Token จาก GitHub มายืนยัน ง่ายมาก ไปที่

Settings → Developer settings → Personal access tokens → Tokens (classic)

กด Generate new token (classic) เพื่อสร้าง token


ตั้งชื่อ แล้วติ๊กสิทธิ์ที่ต้องใช้ ผมติ๊กแค่ repo แล้วกดสร้าง

ทางเลือกที่ปลอดภัยกว่า: ใช้ Fine-grained token แล้วให้สิทธิ์แค่ repo นี้ repo เดียว แบบ Contents: Read-only พอ ถ้า token หลุดก็เสียหายน้อยกว่า

อย่าลืมนะฮะ ต้อง copy เก็บไว้ให้ดี เพราะพอปิดหน้านั้นไป token จะไม่โชว์อีกแล้ว ฟิ้ววว
ได้มาแล้วก็กลับไปที่ server

git clone url
Enter fullscreen mode Exit fullscreen mode

มันจะให้ใส่ Username ของ GitHub ส่วนช่อง Password ให้วาง Token ที่เพิ่งได้มา (ไม่ใช่รหัสผ่าน GitHub นะ)


แค่นี้ก็เรียบร้อยแล้ววววว

แต่ปัญหาคือ Project ของผม login ด้วย Google ซึ่ง Redirect URI ต้องเป็น Domain แบบ https เท่านั้น แต่ของเราเป็นเลข IP แบบ http ซึ่งไม่รองรับ

(กรณีใช้ Google Login) แก้ปัญหาโดยยืม subdomain ของฝั่ง Frontend (Vercel)

เว็บของเราจะ deploy ที่ Vercel ซึ่งจะได้ subdomain มาเป็น https://ชื่อ.vercel.app
Deploy Web on Vercel
เราต้องไปแก้ไฟล์ vercel.json ฝั่ง frontend ให้ชี้ https://ชื่อ.vercel.app/api ไปเรียก http://เลขIP:เลขport ซึ่งก็คือ server API ที่เรากำลังทำอยู่ตอนนี้

แค่นี้ก็เรียบร้อย ส่วนใน Google Console ก็ตั้งแบบนี้

จะเห็นว่าเราใช้ domain ของ Vercel ตามด้วย /api เพื่อเรียก API แทน

หมายเหตุ: วิธีนี้เป็นทางลัดให้ใช้งานได้ก่อน ช่วงระหว่าง Vercel กับ EC2 ยังเป็น http ที่ไม่เข้ารหัส และใครรู้ IP ก็ยิงตรงเข้าพอร์ตได้ ถ้าจะใช้งานจริงจัง ควรทำ domain + Nginx + HTTPS (เดี๋ยวมีบทต่อ)

การตั้งค่า .env

เข้าไปในโฟลเดอร์ project แล้วสร้างไฟล์ .env

ls
Enter fullscreen mode Exit fullscreen mode
cd ชื่อProject
Enter fullscreen mode Exit fullscreen mode
nano .env
Enter fullscreen mode Exit fullscreen mode

วางค่า env ของ Production ลงไป เสร็จแล้วกด Ctrl + X เพื่อออก มันจะถามว่า Save modified buffer? ให้กด Y แล้ว Enter

(กรณีใช้ Supabase) เปลี่ยนพอร์ตเป็น 5432

sed -i 's/supabase.com:6543/supabase.com:5432/' .env
Enter fullscreen mode Exit fullscreen mode

คำสั่งนี้แก้พอร์ตของ Supabase pooler ใน .env จาก 6543 เป็น 5432 จะเข้าไปแก้เองใน nano ก็ได้นะ

  • พอร์ต 6543 คือ Transaction mode: เหมาะกับตอนแอปรันจริง แต่ prisma migrate มักค้างหรือ error เพราะ migrate ต้องใช้ advisory lock และคำสั่งบางอย่างที่ transaction mode ไม่รองรับ
  • พอร์ต 5432 คือ Session mode: รัน migration ได้ปกติ

พอใช้ 5432 แล้ว จะลบ ?pgbouncer=true ท้าย URL ออกก็ได้ เพราะค่านี้ใช้กับ 6543 เท่านั้น

วิธีที่ดีกว่าในระยะยาว: แยกเป็น 2 ตัวแปร ให้แอปใช้ 6543 ตอนรันจริง ส่วน migrate ใช้ 5432 จะได้ไม่ต้องสลับพอร์ตไปมา

DATABASE_URL="...pooler.supabase.com:6543/postgres?pgbouncer=true"
DIRECT_URL="...pooler.supabase.com:5432/postgres"
Enter fullscreen mode Exit fullscreen mode

แล้วเพิ่ม directUrl = env("DIRECT_URL") ใน datasource db ของ schema.prisma

ติดตั้ง package, migrate และ build

ก่อนอื่นต้องติดตั้ง package ก่อน (เครื่องใหม่ยังไม่มี node_modules)

npm ci
Enter fullscreen mode Exit fullscreen mode

แล้วค่อยรันต่อ

npm run prisma:deploy && npm run db:seed && npm run build
Enter fullscreen mode Exit fullscreen mode
  • prisma migrate deploy สร้างหรืออัปเดตตารางใน Supabase ตาม migration ที่มีอยู่
  • db:seed ใส่ข้อมูลตั้งต้น
  • build คอมไพล์โค้ด API


ขึ้นแบบนี้ก็แสดงว่าถูกต้องเรียบร้อย
ลองเช็กดู

ls
Enter fullscreen mode Exit fullscreen mode

ถ้าเห็นโฟลเดอร์ชื่อ dist คือไฟล์ที่ build แล้ว ที่เหลือแค่สั่ง run

รันด้วย PM2

sudo npm i -g pm2
Enter fullscreen mode Exit fullscreen mode

ติดตั้ง PM2 แบบ global (ใช้ได้ทั้งเครื่อง) PM2 เป็นตัวจัดการโปรเซสของ Node.js ทำหน้าที่

  • รันแอปเป็นเบื้องหลัง ปิด terminal แล้วแอปยังทำงานต่อ
  • ถ้าแอป crash จะรีสตาร์ตให้เอง
  • เก็บ log ไว้ดูย้อนหลัง


pm2 start dist/server.js --name upro-api
Enter fullscreen mode Exit fullscreen mode

สั่งรันไฟล์ dist/server.js ที่ได้มาจาก npm run build เมื่อกี้ และตั้งชื่อโปรเซสว่า upro-api ไว้เรียกใช้ทีหลัง

pm2 save
Enter fullscreen mode Exit fullscreen mode

บันทึกรายการโปรเซสที่รันอยู่ตอนนี้

แต่ pm2 save อย่างเดียวยังไม่พอนะ ถ้าเครื่องรีบูต API จะไม่เปิดขึ้นมาเอง ต้องสั่งอีกคำสั่ง

pm2 startup
Enter fullscreen mode Exit fullscreen mode

มันจะพิมพ์คำสั่งที่ขึ้นต้นด้วย sudo env PATH=... ออกมา ให้ copy คำสั่งนั้นไปรัน แล้วตามด้วย pm2 save อีกรอบ แค่นี้รีบูตกี่ทีแอปก็กลับมาเอง

คำสั่ง PM2 ที่ใช้บ่อย

  • pm2 list ดูสถานะ
  • pm2 logs upro-api ดู log ตอนหา error
  • pm2 restart upro-api รีสตาร์ตหลัง build ใหม่

และแน่นอน เช็กว่า API ทำงานได้ไหม

sleep 6 && curl -i http://localhost:5000/health
Enter fullscreen mode Exit fullscreen mode

ถ้าได้ HTTP/1.1 200 OK ก็เสร็จแล้ววววว เย้!

แต่ ๆ ๆ ลองไปเช็กที่ Browser ดูสิ

ลองเปิด http://IP:5000/health ในเบราว์เซอร์

มันไม่ได้ใช่ไหมล่ะ

ทำไมใน server ได้ แต่นอก server ไม่ได้?

ก็เพราะเรายังไม่ได้เปิด firewall หรือพูดง่าย ๆ คือยังไม่ได้เปิดประตูนั่นเอง ซึ่งเปิดง่ายมาก


ไปที่ Security Group → Edit inbound rules เพื่อไปเปิดพอร์ต

กด Add rule

ตั้งค่าประมาณนี้ คือ Type: Custom TCP, Port: 5000 (พอร์ต API ของเรา), Source: 0.0.0.0/0 (Vercel ต้องเรียกเข้ามาได้) แต่แนะนำให้ทำ Nginx นะจ๊ะ อันนี้แค่ deploy ให้ใช้งานได้ก่อนเท่านั้น


ลองเปิด http://IP:5000/health ในเบราว์เซอร์อีกที ขึ้น OK ก็เรียบร้อยแล้ววว แค่นี้ API ของเราก็ใช้งานได้แล้ว

เเต่นี้คือเราเปิดประตูหลัง อาจจะโดนซอยตูดได้ เพราะงันต้องทำ Nginx น่ะจ่ะ ทำไงติดตามน่ะจ่ะ

Top comments (0)