สวัสดีครับ วันนี้จะพาไป Setup Server สำหรับ Deploy Backend และ Frontend คือ Node.js Application และ React App (ที่เป็น Single Page Application) ไว้ในเครื่องเดียวกัน โดยใช้ NGINX เป็น reverse proxy นะครับ
โดยปกติส่วนใหญ่แล้วส่วนใหญ่ SPA เป็น static website ดังนั้นมันสามารถ hosting ไว้ที่ไหนก็ได้ ไม่ว่าจะเป็น Netlify, Firebase Hosting, Github Pages แล้วใช้วิธี host Backend API ไว้อีกเครื่องนึง แต่สำหรับบางคนที่มีข้อจำกัด หรืออยาก Deploy ไว้ที่เครื่องเดียวกันเลยละ? จริงๆก็ทำไม่ยากเลย มาเริ่มกันดีกว่าครับ
Table of Contents
Node.js และ React App
หรือ clone จากนี้ครับ
git clone https://github.com/Phonbopit/example-node-react.git
ก็เริ่มต้นมา สมมติผมมี Backend API เป็น Server ง่ายๆ ด้วย express 1 ไฟล์ครับ สร้างโปรเจ็คมาง่ายๆเลย
mkdir backend & cd backendnpm init -ynpm install express corsส่วนไฟล์ server.js ก็มีแค่ endpoint เดียวคือ /api เพื่อส่ง response กับไปที่ client
const express = require('express')const cors = require('cors')const app = express()
app.use(cors())
app.get('/api', (req, res) => { res.json({ message: 'Ahoy!', users: [ { id: 1, name: 'John Doe' }, { id: 2, name: 'Chuck Norris' } ] })})
app.listen(4000, () => console.log("It's work!"))ต่อมา frontend ผมเลือกสร้างจาก Create React App
npx create-react-app frontend-appnpm install axiosจากนั้นผมแก้ไฟล์ App.js ให้ทำการ fetch data จาก /api
import React, { useEffect, useState } from 'react'import axios from 'axios'import logo from './logo.svg'import './App.css'
function App() { const [data, setData] = useState({})
useEffect(() => { async function fetchData() { const response = await axios.get('http://localhost:3000/api') setData(response.data) }
fetchData() return () => {} }, [])
return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p>{data.message}</p> <ul> {data.users && data.users.map((user) => { return <li key={user.id}>{user.name}</li> })} </ul> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> )}
export default Appทดสอบรัน server ทั้ง backend และ frontend ครับ โดย
- Backend อยู่ port 4000
- Frontend อยู่ port 3000
ต่อมานับ App ขึ้น Server จะใช้ VPS หรืออะไรก็แล้วแต่ครับ สำหรับใครไม่มี Server หรือไม่รู้วิธี Setup อ่านบทความเกี่ยวกับ Digital Ocean เพิ่มเติมได้ที่ผมเขียนไว้ Digital Ocean คืออะไร ? + สอนวิธีการติดตั้งและสร้าง Droplet
รู้จักกับ PM2
สมมติตอนนี้เรามี App 2 ตัวแบ่งเป็น 2 folders คือ frontend และ backend สิ่งที่เราจะทำต่อมาคือ ติดตั้ง PM2 (ไม่เกี่ยวอะไรกับ PM 2.5 นะ)
PM2 ถ้าตามความหมายของมันเลยเขียนไว้ว่า PM2 is a Production Process Manager for Node.js applications เป็น Process Manager เอาไว้จัดการ Process ของ Node.js Application นั่นเอง สามารถสั่ง restart ดู stats ดู log มี Monitoring และ config อะไรได้อีกมากมาย
ซึ่งจริงๆเราก็รัน Node ได้ด้วยคำสั่งธรรมดาๆ เช่น node server.js นั่นแหละ เพียงแต่ว่าใช้ PM2 มันมีประโยชน์มากกว่าแค่การรัน Node
การใช้งาน PM2 เราต้องทำการติดตั้งแบบ global ซะก่อน ผ่าน NPM หรือ Yarn ก็แล้วแต่สะดวกเลยครับ
$ npm install pm2@latest -g# หรือ$ yarn global add pm2เมื่อติดตั้งเสร็จ เราสามารถใช้คำสั่ง เพื่อ start server ได้เลย
pm2 start backend/server.jsซึ่งมันมีค่าเท่ากับการสั่งรัน node ปกติแหละ
node backend/server.jsเราสามารถดู List process ทั้งหมดที่รันด้วย PM2 ได้ด้วยคำสั่ง
pm2 l
┌────────┬────┬──────┬────────┬───┬─────┬───────────┐│ Name │ id │ mode │ status │ ↺ │ cpu │ memory │├────────┼────┼──────┼────────┼───┼─────┼───────────┤│ server │ 0 │ fork │ online │ 0 │ 0% │ 21.3 MB │└────────┴────┴──────┴────────┴───┴─────┴───────────┘จะสังเกตเห็นลักษณะแบบด้านบน คือจะมีระบุว่า Name เป็นอะไร และ id เป็นอะไร ตัวอย่างนี้ เราสามารถดูรายละเอียดได้ด้วยคำสั่ง
pm2 show <ID># เช่นpm2 show 0หรือจะดู Monitoring ก็ทำได้ด้วย
pm2 monitและก็คำสั่งที่สำคัญของ PM2 อีกอย่างคือ การสั่งให้มัน start กรณีที่เครื่อง VPS ดับ หรือ restart (เพราะถ้าปกติเรารัน node server.js ถ้าเครื่องดับจบเลยใช่มั้ยละครับ)
pm2 startup
[PM2] Init System found: launchd[PM2] To setup the Startup Script, copy/paste the following command:sudo env PATH=$PATH:/usr/local/Cellar/node/12.7.0/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup launchd -u YOUR_USER_NAME --hp /Users/YOUR_USER_NAMEและก็ copy ไอ้ตรงส่วน sudo env .... นั้นอะไปรัน มันก็จะรัน script daemon ให้เราเวลาเปิดเครื่องก็จะรัน Process (ขึ้นอยู่กับ OS อะไร Linux อาจจะเป็น systemctl)
สุดท้าย Save process list ไว้ครับ
pm2 saveสรุปคำสั่ง PM2 ที่เราต้องใช้ไว้รัน Server คือ (สามารถกำหนด name ให้มันได้ด้วย option --name)
# start serverpm2 start server.js --name="Awesome Name"
# monitoringpm2 monit
# startup & savepm2 startuppm2 saveนอกเหนือจากนี้ PM2 ยังทำ deployment ได้ด้วยการกำหนด ecosystem.config.js เพื่อ deploy dev, staging, production อะไรพวกนี้ได้ครับ จากไฟล์ config นั่นเอง
รายละเอียดเพิ่มเติมของคำสั่ง PM2 สามารถอ่านเพิ่มเติมได้ที่นี่ PM2 Documentation
ทำ Reverse proxy ด้วย NGINX
ต่อมาเราจะทำ Reverse proxy ด้วย NGINX ก็ติดตั้ง NGINX เลยครับ (ผมสมมติว่ารันอยู่บน Ubuntu นะครับ)
sudo apt-get install nginxจากนั้น ทำการเพิ่ม server block ของ nginx ตัวอย่างเช่น example.com เป็นโดเมนของเรา โดยเราจะ copy ตัวอย่างจาก default นั่นเอง
cd /etc/nginx/sites-availablecp default api.example.comจากนั้นแก้ส่วน location / เป็นแบบนี้
server_name api.example.com;
location / { proxy_pass http://localhost:4000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade;}สามารถ เช็คว่า NGINX เรา setup ได้ถูกมั้ยด้วยคำสั่ง
sudo nginx -t
# จะได้ result ประมาณนี้nginx: the configuration file /etc/nginx/nginx.conf syntax is oknginx: configuration file /etc/nginx/nginx.conf test is successfulสุดท้าย Restart NGINX ซะ
sudo systemctl restart nginxตอนนี้ Backend ของเราถูก Deploy และรันด้วย PM2 จากนั้นใช้ NGINX เป็น reverse proxy จาก url ที่เราเข้า เช่น api.example.com จะแมพ port 4000 ให้เรา โดยที่ URL เราไม่ต้องใส่ api.example.com:4000 ครับ
ต่อมา Frontend เราจะ setup NGINX แบบนี้ครับ (Copy default เหมือนเดิม หรือจริงๆจะใช้ default ก็ได้ครับ)
cd /etc/nginx/sites-availablecp default example.comจากนั้นแก้ไข เป็น path ที่อยู่ของไฟล์ dist ของเราครับ (หากใครยังไม่ได้ build production ก็รันก่อนนะครับ) ละก็อย่าลืมเปลี่ยน API endpoint จาก localhost เป็น URL ที่ตั้งค่าไว้ใน NGINX ด้วยนะครับ เช่น api.example.com
npm run buildตัวอย่างเช่น path ที่อยู่หลังจาก build แล้วเป็น /var/www/frontend/build ที่ไฟล์ default หรือ example.com แก้ไขเป็น ตรง root เป็นแบบนี้
root /var/www/frontend/build;Restart NGINX อีกรอบ เป็นอันเรียบร้อย
sudo systemctl restart nginxทีนี้เราก็จะได้ Frontend มี URL เป็น example.com และ Backend มี URL เป็น api.example.com โดยอยู่ที่เครื่องเดียวกันนั่นเอง
Happy Coding ❤️
- Authors
-
Chai Phonbopit
เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust