Friday, August 7, 2015

Auto restart node daemon: รีสตาร์ทเมื่อแก้ไขซอร์สโค้ด#2

Update: 25/09/2018
-----------------------
หลังจากใช้ pm2 อยู่นาน ไม่ได้เข้าไปวุ่นวายอะไรมากระบบก็ใช้ได้อยู่อย่างนั้นแหละ แต่เมื่อติดตั้งระบบใหม่ Ubuntu เวอร์ชันใหม่ Node.js ก็ใหม่ขึ้น NPM และ PM2 ก็อัพเดทตาม แต่วิธีการเก่ากลับใช้ไม่ได้ผล

ข้อความที่เขียนไว้ด้านล่างนี้ตอนนี้ใช้ไม่ได้ เมื่อเรียกผ่าน json ไฟล์แล้ว เวลาเราเข้าไปดูรายละเอียด pm2 จะหยุดรัน process นั้นๆ ทำให้เว็บเข้าไม่ได้ พยายามแก้ไขอยู่นาน หาอ่านหลายๆ เว็บก็ไม่เจอทางออก

สุดท้ายลองใน DigitalOcean บน Node.js Droplets แล้ว ถ้าเรียกผ่าน command line จะใช้ได้ เช่น

pm2 start app.js --watch
เอาเป็นว่าตอนนี้ใช้แบบนี้ไปก่อนก็แล้วกัน แล้วค่อยตามอัพเดทกันต่อไป

-----------------------
เมื่อวานเขียนเกี่ยวกับ nodemon เพื่อรีสตาร์ท node.js application เมื่อมีการแก้ไขซอร์สโค้ด ก็ถือว่าใช้งานได้ดีระดับหนึ่ง แต่พอลองใช้จริงๆ แล้วกลับเจอปัญหา ด้วยคู่มือและตัวอย่างต่างๆ ก็ยังไม่ละเอียด จึงมองหา Node Monitor ตัวอื่นๆ ก็พบโปรแกรมชื่อ PM2 หรือ Process Monitor สำหรับ node.js โดยลองหาข้อมูลเทียบๆ ดูแล้วหลายคนบอกว่าใช้ได้ดี เหมาะสำหรับติดตั้งระบบจริง

ปัญหาของ nodemon ที่พบ คือ ต้องรันคำสั่ง nodemon <appname>.js ที่ working directory เท่านั้น ถ้ารันผ่าน path จะไม่อัพเดท เช่น nodemon /var/xxx/yyy/<appname>.js เมื่อแก้ไขแล้วจะไม่มีการรีสตาร์ทโพรเซส ปัญหาที่สอง ถึงแม้จะรัน nodemon แบบทำงานเบื้องหลังแล้ว เมื่อทิ้งไว้นาน ลองเข้าไปเช็กด้วยคำสั่ง ps -A | grep node กลับไม่มีโพรเซสของ node.js

แต่ใช่ว่า pm2 จะจบเลย พอสั่งรัน app แล้ว แต่ไม่อัพเดทการแก้ไข เมื่อดูจากคำสั่ง pm2 list แล้วพบว่า watching เป็น disabled ถ้าหน้าจอใครไม่เหมือนตัวอย่างด้านล่าง แสดงว่าเป็นเวอร์ชันเก่าให้ติดตั้งเวอร์ชั่นใหม่ล่าสุดด้วยคำสั่ง
npm install pm2@lastest -g
เมื่อติดตั้งเสร็จ watching ก็ยังไม่เริ่มต้นทำงาน ต้องเขียนคำสั่งเพิ่มเติมก่อนที่จะรันแอพพลิเคชันก่อน

root@tms:# pm2 list
┌──────────┬────┬──────┬─────┬─────────┬─────────┬────────┬────────┬──────────┐
│ App name │ id │ mode │ pid │ status  │ restart │ uptime │ memory │ watching │
├──────────┼────┼──────┼─────┼─────────┼─────────┼────────┼────────┼──────────┤
│ ticket   │ 0  │ fork │ 0   │ errored │ 28      │ 0      │ 0 B    │ disabled │
└──────────┴────┴──────┴─────┴─────────┴─────────┴────────┴────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app
root@tms:#


ถ้าต้องการดูรายละเอียดการของ pm2 ให้ใช้คำสั่ง pm2 show <app_name> ซึ่งจะมีรายละเอียดไฟล์ log และอื่นๆ ดังตัวอย่างด้านล่าง

root@tms:# pm2 show ticket
Describing process with id 0 - name ticket
┌───────────────────┬───────────────────────────────────────┐
│ status            │ errored                               │
│ name              │ ticket                                │
│ id                │ 0                                     │
│ path              │ /var/ast/public_html/nodejs/ticket.js │
│ args              │                                       │
│ exec cwd          │ /var/ast/public_html/nodejs           │
│ error log path    │ /root/.pm2/logs/ticket-error-0.log    │
│ out log path      │ /root/.pm2/logs/ticket-out-0.log      │
│ pid path          │ /root/.pm2/pids/ticket-0.pid          │
│ mode              │ fork_mode                             │
│ node v8 arguments │                                       │
│ watch & reload    │                                     │
│ interpreter       │ node                                  │
│ restarts          │ 28                                    │
│ unstable restarts │ 0                                     │
│ uptime            │ 0                                     │
│ created at        │ N/A                                   │
└───────────────────┴──────────── ──────────────────────────┘

root@tms:#

จากนั้นให้เขียนไฟล์ <app_name>.json เอาไว้ที่ใดที่หนึ่งที่ต้องการจะสั่งรัน เช่น หน้าหลักของ user หรือ /root ก็ได้ ตัวอย่างผู้เขียนไว้ที่ home ของ user เป็น inet999 และใช้ชื่อ ticket.json มีรายละเอียดภายในดังนี้
{  "apps": [{    "name": "ticket",    "script": "/var/www/ast/public_html/ticket.js",    "log_file": "/var/log/pm2/ticket.log",    "error_file": "/var/log/pm2/ticket-err.log",    "watch": true,    "ignore_watch": ["files"]  }]}
ที่สำคัญที่สุดคือ สองบรรทัดตัวอักษรสีแดง ที่จะสั่งให้ pm2 เฝ้าดูไฟล์สคริปส์ที่กำหนดไว้ข้างบน จากนั้นก็สั่งรันด้วยคำสั่ง

root@tms:/home/inet999# pm2 start ticket.json
เมื่อเรียบร้อยจะได้ข้อความแสดงรายละเอียดของ App และ pid ดังนี้

[PM2] restartProcessId process id 0
[PM2] restartProcessId process id 1
┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid   │ status │ restart │ uptime │ memory      │ watching │
├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
ticket   │ 0  │ fork │ 28359 │ online │ 14      │ 0s     │ 19.859 MB   │  enabled
└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app
root@tms:/home/inet99#


เมื่อเรียบร้อยแล้วลองแก้ไขไฟล์สคริปส์แล้ว และรันคำสั่ง pm2 list จะเห็นหมายเลข pid เปลี่ยนแปลงไปเรื่อยๆ จากนั้นลอง ก็ไปกำหนดให้รัน pm2 ทุกครั้งที่มีการรีสตาร์ทเครื่องเซิร์ฟเวอร์ด้วยคำสั่ง

root@tms:~/.pm2/logs# pm2 startup ubuntu
โดยระบบจะสร้างไฟล์เพื่อเปิดโปรแกรม pm2 หลังจากรีสตาร์ทเครื่องดังนี้

[PM2] Generating system init script in /etc/init.d/pm2-init.sh
[PM2] Making script booting at startup...
[PM2] -ubuntu- Using the command:
      su -c "chmod +x /etc/init.d/pm2-init.sh && update-rc.d pm2-init.sh defaults"
 Adding system startup for /etc/init.d/pm2-init.sh ...
   /etc/rc0.d/K20pm2-init.sh -> ../init.d/pm2-init.sh
   /etc/rc1.d/K20pm2-init.sh -> ../init.d/pm2-init.sh
   /etc/rc6.d/K20pm2-init.sh -> ../init.d/pm2-init.sh
   /etc/rc2.d/S20pm2-init.sh -> ../init.d/pm2-init.sh
   /etc/rc3.d/S20pm2-init.sh -> ../init.d/pm2-init.sh
   /etc/rc4.d/S20pm2-init.sh -> ../init.d/pm2-init.sh
   /etc/rc5.d/S20pm2-init.sh -> ../init.d/pm2-init.sh
[PM2] Done.
root@tms:~/.pm2/logs#


และหากต้องการยกเลิกโพรเซสของ pm2 เพื่อที่จะเริ่มต้นใหม่สามารถใช้คำสั่ง 
pm2 kill
สุดท้ายทดสอบด้วยการสั่ง restart เครื่องอีกครั้งหนึ่ง และเข้าไปดูโพรเซส ก็จะเห็นมี node ทำงานอยู่แล้ว และใช้คำสั่ง pm2 list ก็จะเห็นรายละเอียดการเฝ้าติดตามอยู่ (ต้องล็อกอินให้ถูกตามชื่อผู้ใช้งานที่สั่งรัน pm2 ด้วย)

จากไฟล์ ticket.json จะกำหนดไฟล์ log ไว้ใน /var/log/pm2/ticket-0.log ซึ่งเป็นไฟล์ที่เก็บข้อมูลที่ส่งออกมาจาก node.js เราสามารถดูรายละเอียดด้วยคำสั่ง cat หรือ tail -f ก็ได้

ลองดูนะครับ หากมีข้อคิดเห็น ข้อเสนอแนะ หรือมีสิ่งใดผิดพลาดรบกวนช่วยชี้แนะด้วยครับ...

Thursday, August 6, 2015

Auto restart node daemon: รีสตาร์ทเมื่อแก้ไขซอร์สโค้ด#1

Node.js เป็นเทคโนโลยีที่ยังใหม่อยู่ในขณะนี้ (6/8/2015) และยังเป็นเซิร์ฟเวอร์ที่ต้องรันเซอร์วิสของ node.js เมื่อมีการเปลี่ยนแปลงซอร์สโค้ดทุกครั้งต้องปิดเซอร์วิสแล้วค่อยเปิดใหม่

ปกติแล้วการรันในเซิร์ฟเวอร์หรือในเครื่องทั่วไปก็ไม่มีปัญหามากนัก เพียงแค่รีโมทเข้าไปปิด-เปิดใหม่ก็ใช้ได้แล้ว เพราะไม่ค่อยได้อัพเดทกันบ่อยๆ

แต่สำหรับคนที่ทำระบบโดยไม่เข้าถึง Shell โดยตรงแล้วจะยากสำหรับการเข้าไปปิด-เปิด หรือ รีสตาร์ทเซอร์วิส แต่อย่างไรก็ตามใช่ว่าจะไร้หนทางเสียทีเดียวเมื่อมีคนเขียนโปรแกรมไว้รองรับเรียกว่า nodemon การติดตั้งก็ง่ายแสนง่ายรันผ่าน npm ได้เลย

npm install -g nodemon 

และการใช้งานก็เรียกแทน node.js ได้เลย
nodemon <app.js> 

คุณสมบัติของ nodemon

  • รีสตาร์ทโปรแกรมแบบอัตโนมัติเมื่อเปลี่ยนแปลงซอร์สโค้ด
  • ตรวจสอบไฟล์นามสกุลที่มอนิเตอร์อัตโนมัติ
  • สนับสนุน node และ coffeescript 
  • เฝ้าดูไดเรคทอรี่ที่ระบุ
  • เป็น Opensorce ดาวน์โหลดฟรีที่ github

การแก้ไขปัญหา

สำหรับคนที่ใช้ ubuntu เมื่อรัน node แล้วจะไม่พบไฟล์ ระบบจะแนะนำให้ติดตั้งโปรแกรมอื่นๆ ที่คาดว่าจะมีโปรแกรม node แต่ถ้ารัน nodejs จะสามารถใช้งานได้เช่นกัน ซึ่งบางคนติดรูปแบบการเรียน node มากกว่า มีวิธีแก้ไขโดยสร้างลิงค์ไฟล์ใหม่ คือ
sudo ln -s /usr/bin/nodejs /usr/local/bin/node
เพียงแค่นี้ก็จะเรียกใช้คำสั่ง node ได้แล้ว

เมื่อมีการแก้ไขที่หน้าจอ Console (ถ้ายังไม่ยกเลิกออกไปเสียก่อน) จะมีข้อความ
7 Aug 00:34:10 - [nodemon] restarting due to changes...
7 Aug 00:34:10 - [nodemon] starting `node ticket.js`
server start app onready
และเมื่อดูจากโพรเซสจะเห็นตัวเลขเปลี่ยนไป
14890 pts/0    00:00:00 node
15075 pts/0    00:00:00 node
root@tms:# nano ticket.js
root@tms:# ps -A | grep node
14890 pts/0    00:00:00 node
15183 pts/0    00:00:00 node
root@tms:# nano ticket.js
root@tms:# ps -A | grep node
14890 pts/0    00:00:00 node
15227 pts/0    00:00:00 node
root@tms:# nano ticket.js
root@tms:# ps -A | grep node
14890 pts/0    00:00:00 node
15283 pts/0    00:00:00 node

เท่าที่ทดสอบแล้ว nodemon ถือว่าเป็นโปรแกรมที่ช่วยอำนวยความสะดวกให้กับนักพัฒนาสาย JavaScripts ได้เป็นอย่างดี ไม่ต้องรีโมทบ่อยครั้ง เพียงแค่อัพโหลดไฟล์ผ่าน ftp ไปยังเซิร์ฟเวอร์ที่เหลือเป็นหน้าที่ของ nodemon รับช่วงต่อไป แต่อย่างไรก็ตาม หากอัพโหลดแล้วหากระบบไม่ทำงาน ให้ลองตรวจสอบคำสั่งอีกครั้งหนึ่ง บางครั้งคำสั่งที่แก้ไขนั่นแหละไม่ทำงาน เช่น ใช้คำสั่ง alert ในฝั่งเซิร์ฟเวอร์ซึ่งไม่สามารถใช้งานได้ เพราะฟังค์ชัน alert ไม่มีในสภาพแวดล้อมของ node.js แต่สามารถสั่งรันในฝั่ง client ได้ 

ปกติหากแก้ไขครั้งหนึ่งสั่งรัน node daemon กันครั้งหนึ่งก็ไม่มีปัญหาเพราะหากมีความผิดพลาดอะไรก็จะแสดงออกมาให้เห็นทันที แต่กรณีใช้ nodemon เราก็มองไม่เห็นเพราะอยู่บนฝั่งเซิร์ฟเวอร์ ดังนั้นควรที่จะตรวจสอบและระมัดระวังสักนิด...