วิธีการ Setup Server สำหรับ Node.js และ React ด้วย NGINX
สวัสดีครับ วันนี้จะพาไป 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 backend
npm init -y
npm 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-app
npm 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 server
pm2 start server.js --name="Awesome Name"
# monitoring
pm2 monit
# startup & save
pm2 startup
pm2 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-available
cp 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 ok
nginx: 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-available
cp 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
- Name
- Chai Phonbopit
- Website
- @Phonbopit