Xin chào các bạn, lâu lắm mới có thời gian quay lại viết bài. Chủ yếu là do vừa mới tìm ra được một cách quá hay (mình nghĩ vậy, rất cool! 😀 ) để tạo script cho webserver tự khởi động lại khi bị crash. Mình cũng ghi chú thêm cách “chạy nền” script để không cần phải giữ session ssh. Đọc hết nhé!
Mục lục
Thí nghiệm
Sau đây mình sẽ có một thí nghiệm nhỏ. Đó là tạo một app Nodejs xuất ra thông báo “tôi đang chạy” mỗi giây để mô phỏng việc app sẽ liên tục. Đơn giản thôi!
1 2 3 4 5 6 |
console.log('NODEJS App is started'); var clock = setInterval(function(){ var current = Date.now(); console.log("I'm running...", current); }, 1000); |
Thử chạy, bạn sẽ thấy hiển thị như dưới:
1 2 3 4 5 6 7 |
hoangtua@dinosaur:~/Scripts$ node myapp.js NodeJs App is started I'm running... 1513265485445 I'm running... 1513265486447 I'm running... 1513265487448 ^C hoangtua@dinosaur:~/Scripts$ |
Dĩ nhiên app sẽ chạy mãi không ngừng đâu, nên mình phải bấm Ctrl+C để thoát ra.
Bây giờ, chúng ta sẽ giả lập việc app bị crash sau khoảng 10 giây chạy vì nó quá… “mệt”. Mình thêm vào biến start
để đếm thời gian và khi quá 10 giây thì ném ra lỗi “Run out of time”
1 2 3 4 5 6 7 8 9 10 11 |
console.log('NODEJS App is started'); var start = Date.now(); var clock = setInterval(function(){ var current = Date.now(); console.log("I'm running...", current); if (current - start > 10000){ console.log("I have been running for 10k milliseconds! I have to take a nap right now."); clearInterval(clock); throw new Error('Run out of time'); } }, 1000); |
Chạy script trên với Nodejs, bạn sẽ thấy hiển thị như bên dưới.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
hoangtua@dinosaur:~/Scripts$ node myapp.js NodeJs App is started I'm running... 1513265674042 I'm running... 1513265675044 I'm running... 1513265676046 I'm running... 1513265677047 I'm running... 1513265678049 I'm running... 1513265679050 I'm running... 1513265680051 I'm running... 1513265681052 I'm running... 1513265682053 I'm running... 1513265683055 I have been running for 10k milliseconds! I have to take a nap right now. /home/hoangtua/Scripts/myapp.js:10 throw new Error('Run out of time'); ^ Error: Run out of time at Timeout.<anonymous> (/home/hoangtua/Scripts/myapp.js:10:9) at ontimeout (timers.js:469:11) at tryOnTimeout (timers.js:304:5) at Timer.listOnTimeout (timers.js:264:5) hoangtua@dinosaur:~/Scripts$ |
Yeah! Trong phần thí nghiệm này mình đã mô phỏng thành công việc chạy app Nodejs liên tục và việc app bị crash. Phần tiếp theo bên dưới, cùng xem cách Tự khởi động lại app khi bị crash như thế nào!
Tạo script tự khởi động lại webserver khi bị crash
Về bản chất thì một tiến trình không thể tự mình khởi động lại khi bị crash! Bởi vì đã để bị crash là coi như die. Chết rồi thì làm méo gì control được gì nữa, ngay cả việc có được chôn cất tử tế không đã là khó khăn thì nói gì đến tự mình hồi sinh. Do đó, ngay từ đầu ta cần dùng một tiến trình khác để khởi động webserver (hay là myapp.js như thí nghiệm ở trên) cũng như theo dõi và xử lý hậu quả sau đó khi bị crash.
Có rất nhiều ứng dụng cho phép làm được việc đó, nhưng mình giới thiệu ở đây một cách thuần tự nhiên nhất, không cần cài app nào cả, đó là dùng shell script:
1 2 3 4 5 6 7 8 9 10 |
#!/bin/bash myscript(){ node myapp.js } until myscript; do echo Script myapp.js has been crashed with exit code $?. Restart echo now! sleep 1 done |
Tạo script self_restart.sh
với nội dụng như trên. sau đó cho phép script có thể thực thi lệnh:
1 |
chmod +x ./self_restart.sh |
Từ bây giờ, khi chạy lệnh ./self_restart.sh
, con shell này sẽ chạy mãi cho đến khi script tự kết thúc với exit code 0 – nghĩa là không bị lỗi gì cả. Còn nếu xảy ra lỗi, myscript() sẽ return exit code >= 1 và vòng lặp lại được tiếp tục, do đó myscript() sẽ được gọi lần nữa. Tada!!! myapp.js đã được triệu hồi.
Giải thích một chút về con shell này nhé.
myscript()
là khai báo hàm trong bash shell. Trong hàm này, bạn có thể gọi bất cứ lệnh nào để start server, ứng dụng mà bạn cần nó tự khởi động khi crash.until ....; do
đây là một vòng lặp trong bash shell, cho phép thực hiện mãi cho đến khi nào đúng thì thôi. Có thể coi nó là một cách viết khác củawhile [ ! myscript]; do
vậy đó.- Tại sao lại
sleep 1
cái này là khoảng thời gian backup thôi! Sau khi app bị crash, dành ra 1 ít thời gian trống để hệ điều hành dọn dẹp lại bộ nhớ, I/O,… rồi thì hãy khởi động lại app.
Tự chạy khi khởi động server
Dùng crontab như sau:
1 2 |
$ crontab -e @reboot /home/hoangtua/Scripts/self_restart.sh |
@reboot
sẽ chạy khi hệ điều hành bị khởi động lại.
Chạy nền script
Để tránh phải giữ session ssh, không cần giữ cửa sổ command line
Bài này mình cũng nói thêm 1 vấn đề mà rất rất nhiều bạn hay gặp phải khi mới dùng Linux nói chung. Đó là khi các bạn chạy app trên dòng lệnh thông qua ssh, đến khi thoát ssh thì cũng làm tắt luôn tiếng trình đang chạy.
Sở dĩ bị chuyện này là do, mỗi tiến trình chạy trên bash shell (command-line) nào thì sẽ bind vào Input/Output của bash shell đó. SSH cũng là một dạng remote shell cho nên nó cũng bị binding vào input/output của tiến trình đang chạy. Nếu bạn tắt ssh đi, đồng nghĩa với việc ngắt I/O của tiến trình và tiến trình sẽ crash và dừng luôn!
Vậy làm sao để tắt terminal, tắt command line, tắt ssh mà app command line vẫn chạy? Đó là thay vì bind output vào cmd thì ta direct output qua một luồng khác. Thực hiện đơn giản như sau:
1 |
nohup ./self_restart.sh > self_restart.log & |
Trong ví dụ trên, mình đã redirect output vào file .log thay vì xuất lên terminal trên màn hình. Sau khi chạy lệnh trên mình có thể yên tâm đóng terminal, command line mà không sợ app bị tắt mất. Không tin? Chạy lệnh tail -fn 10 self_restart.log
bạn sẽ thấy nội dung file liên tục được cập nhật!
Bonus
Ờ, nó chạy ngầm giờ tui tắt không được đây này!
Ok ok, tìm xem Process ID nó bao nhiều rồi kill thôi!
1 2 3 4 |
$ ps aux | grep self_restart hoangtua 32000 0.0 0.0 14092 3232 pts/2 S 23:16 0:00 /bin/bash ./self_restart.sh hoangtua 32035 0.0 0.0 15756 964 pts/2 S+ 23:17 0:00 grep --color=auto self $ kill -9 32000 |
Kết luận
Với cách làm tạo self_restart.sh
như trên, bạn không chỉ áp dụng được cho Nodejs mà có thể áp dụng cho mọi thứ chạy trên dòng lệnh: python, php, js, go, …. miễn là bạn setup hàm myscript() hợp lý.
- Sử dụng
until ... ; do
để restart command - Dùng @reboot trong crontab để run khi restart
- dùng
nohup ... > ...log &
để chạy nền
Hehe, nếu bạn gặp khó khăn gì, comment bên dưới hoặc trên Facebook fan page, mình sẽ cố gắng giúp đỡ. Thân!