Managing services with update-rc.d

Bộ dự án hiện tại mình đang tham gia thì khách hàng có mong muốn là sau khi restart server, ứng dụng phải được chạy cùng. Ứng dụng của mình sử dụng Unicorn và Sidekiq. Mình đã đi theo hướng viết 02 service và cho vào thư mục /etc/init.d cho tiện việc sử dụng sudo service <service name> [start, stop, restart, status, reload] và thêm nó vào update-rc.d (trên Ubuntu) hoặc chkconfig (trên CentOS). Vấn đề chẳng có gì khi mà trên server Ubuntu thì service Sidekiq lại được chạy trước thằng Redis server dẫn đến Sidekiq không thể khởi động được do không kết nối được vào Redis. Nên mình đã Google để giải quyết vấn đề priority khi khởi động một service. Thấy hay nên mình sẽ dịch lại để chia sẻ với mọi người nhé. Hy vọng sẽ có lúc giúp ích cho mọi người.

$ ll /etc/rc2.d/ | grep -E "unicorn|sidekiq|redis"
lrwxrwxrwx  1 root root   28 Jan 16 13:31 S20unicorn -> ../init.d/unicorn*
lrwxrwxrwx  1 root root   28 Jan 16 13:39 S20sidekiq -> ../init.d/sidekiq*
lrwxrwxrwx  1 root root   20 Oct 28  2016 S20redis_6379 -> ../init.d/redis_6379*

Trong bài viết này mình không đi vào vấn đề viết service script như thế nào. Có lẽ mình sẽ viết ở bài viết sau, giới thiệu và chia sẻ với mọi người cách viết một service script đơn giản. Và trong bài viết này chúng ta chỉ quan tâm tới update-rc.d của Ubuntu vì chkconfig trên CentOS nó có hỗ trợ đọc priority ngay trong script rồi.

Chúng ta bắt đầu nhé.

How-To: Managing services with update-rc.d

Các service trên nền Linux có thể được start, stop và restart (reload) bằng cách đặt các service script trong thư mục /etc/init.d. Và runlevel (cấp độ chạy) của script bạn có thể tìm thấy trong các thư mục /etc/rcX.d (trong đó, X là runlevel của script - chúng ta sẽ đi tìm hiểu sau). Bài này sẽ giải thích cho chúng ta làm sao để activate, deactivate hay modify một service khi khởi động nhé.

Trên Debian, khi cài đặt một service mới, nó mặc định được bật để có thể chạy khi khởi động. Ví dụ, sau khi bạn cài apache2 xong, apache service sẽ tự động chạy khi bạn khởi động máy trong những lần kế tiếp. Nếu bạn không muốn nó tự chạy khi khởi động, mà chỉ muốn chạy thủ công khi nào cần bằng lệnh

sudo service apache2 start
# or
sudo /etc/init.d/apache2 start

Bạn có thể vô hiệu hóa chúng bằng cách xóa symbolic links (hay còn gọi là shortcut trên Windows) trong /etc/rcX.d/SYYapache2 (YY là priority - chúng ta sẽ tìm hiểu sau) hoặc sử dụng update.rc-d. Lợi ích của việc sử dụng update-rc.d là nó sẽ tự động thực hiện việc thêm/gỡ bỏ các symbolic links cần thiết trong /etc/init.d. Chúng ta lấy luôn ví dụ với thằng Apache nhé.

$ ls -l /etc/rc*.d/ | grep apache2
lrwxrwxrwx 1 root root  17 Mar  3  2015 K09apache2 -> ../init.d/apache2
lrwxrwxrwx 1 root root  17 Mar  3  2015 K09apache2 -> ../init.d/apache2
lrwxrwxrwx 1 root root  17 Mar  3  2015 S91apache2 -> ../init.d/apache2
lrwxrwxrwx 1 root root  17 Mar  3  2015 S91apache2 -> ../init.d/apache2
lrwxrwxrwx 1 root root  17 Mar  3  2015 S91apache2 -> ../init.d/apache2
lrwxrwxrwx 1 root root  17 Mar  3  2015 S91apache2 -> ../init.d/apache2
lrwxrwxrwx 1 root root  17 Mar  3  2015 K09apache2 -> ../init.d/apache2

Như bạn đã thấy, các runlevel 0, 1 và 6 được bắt đầu bởi chữ K, các runlevel 2, 3, 4 và 5 được bắt đầu bởi chữ S. Hai ký tự đó tượng trưng cho Kill và Start. Giờ chúng ta sẽ đi tìm hiểu các runlevel trên Debia (Ubuntu).

  • Các runlevel 2, 3, 4 và 5 là cấp độ chạy của chế độ đa nhiệm (multi-users)
  • Runlevel 0 là halt (tạm dừng)
  • Runlevel 1 là single user mode (chế độ đơn nhiệm)
  • Runlevel 6 là reboot (khởi động lại)

Xóa một service

Nếu bạn thực sự muốn vô hiệu hóa Apache service khi khởi động, bạn có thể thực hiện bằng tay bằng cách xóa hết symbolic links liên quan đến apache2 trong thư mục /etc/rcX.d/ hoặc đơn giản hơn là bạn sử dụng update-rc.d với câu lệnh:

sudo update-rc.d -f apache2 remove

Với tham số -f là yêu cầu update-rc.d xóa hết symbolic links của Apache trong /etc/rcX.d/, kể cả file gốc /etc/init.d/apache2 vẫn đang tồn tại.

Chú ý: Câu lệnh trên sẽ chỉ vô hiệu hóa không cho Apache service chạy khi khởi động cho đến khi service này được nâng cấp. Nếu bạn muốn tắt hoàn toàn, kể cả khi service được nâng cấp, bạn có thể sử dụng lệnh sau:

sudo update-rc.d apache2 stop 80 0 1 2 3 4 5 6

Thêm một service

Độ ưu tiên mặc định

Bạn muốn một service được chạy khi khởi động máy, bạn có thể thực hiện câu lệnh sau:

sudo update-rc.d <service name> defaults

Kết quả bạn sẽ nhận được như sau:

$ sudo update-rc.d <service name> defaults
 Adding system startup for /etc/init.d/<service name> ...
   /etc/rc0.d/K20<service name> -> ../init.d/<service name>
   /etc/rc1.d/K20<service name> -> ../init.d/<service name>
   /etc/rc6.d/K20<service name> -> ../init.d/<service name>
   /etc/rc2.d/S20<service name> -> ../init.d/<service name>
   /etc/rc3.d/S20<service name> -> ../init.d/<service name>
   /etc/rc4.d/S20<service name> -> ../init.d/<service name>
   /etc/rc5.d/S20<service name> -> ../init.d/<service name>

Tùy chọn độ ưu tiên

Với độ ưu tiên mặc định, mình đã gặp phải vấn đề là service Sidekiq của mình được chạy trước service của Redis dẫn đến lỗi. Để đảm bảo không bị lỗi và service có thể chạy một cách bình thường, chúng ta cần phải đổi lại thứ tự ưu tiên cho service đó. Làm sao để nó được chạy (và kết thúc) sau là được. Để thay đổi thứ tự, chúng ta chỉ cần chạy lệnh:

$ update-rc.d <service name> defaults 91

Bạn sẽ nhận được kết quả sau:

 Adding system startup for /etc/init.d/<service name> ...
   /etc/rc0.d/K91<service name> -> ../init.d/<service name>
   /etc/rc1.d/K91<service name> -> ../init.d/<service name>
   /etc/rc6.d/K91<service name> -> ../init.d/<service name>
   /etc/rc2.d/S91<service name> -> ../init.d/<service name>
   /etc/rc3.d/S91<service name> -> ../init.d/<service name>
   /etc/rc4.d/S91<service name> -> ../init.d/<service name>
   /etc/rc5.d/S91<service name> -> ../init.d/<service name>

Và trong trường hợp thực tế của mình:

$ ll /etc/rc*.d/ | grep -E "sidekiq|redis"
lrwxrwxrwx  1 root root   20 Oct 28  2016 K20redis_6379 -> ../init.d/redis_6379*
lrwxrwxrwx  1 root root   28 Jan 16 13:39 K70sidekiq -> ../init.d/sidekiq*
lrwxrwxrwx  1 root root   20 Oct 28  2016 K20redis_6379 -> ../init.d/redis_6379*
lrwxrwxrwx  1 root root   28 Jan 16 13:39 K70sidekiq -> ../init.d/sidekiq*
lrwxrwxrwx  1 root root   20 Oct 28  2016 S20redis_6379 -> ../init.d/redis_6379*
lrwxrwxrwx  1 root root   28 Jan 16 13:39 S95sidekiq -> ../init.d/sidekiq*
lrwxrwxrwx  1 root root   20 Oct 28  2016 S20redis_6379 -> ../init.d/redis_6379*
lrwxrwxrwx  1 root root   28 Jan 16 13:39 S95sidekiq -> ../init.d/sidekiq*
lrwxrwxrwx  1 root root   20 Oct 28  2016 S20redis_6379 -> ../init.d/redis_6379*
lrwxrwxrwx  1 root root   28 Jan 16 13:39 S95sidekiq -> ../init.d/sidekiq*
lrwxrwxrwx  1 root root   20 Oct 28  2016 S20redis_6379 -> ../init.d/redis_6379*
lrwxrwxrwx  1 root root   28 Jan 16 13:39 S95sidekiq -> ../init.d/sidekiq*
lrwxrwxrwx  1 root root   20 Oct 28  2016 K20redis_6379 -> ../init.d/redis_6379*
lrwxrwxrwx  1 root root   28 Jan 16 13:39 K70sidekiq -> ../init.d/sidekiq*

Thế là đã đảm bảo rằng Sidekiq sẽ được chạy sau Redis.

Tùy chọn độ ưu tiên cho Start và Kill

Nếu bạn muốn độ ưu tiên của Kill và Start khác nhau (thay vì K91 và S91), bạn có thể thay đổi thành một số bạn muốn. Ví dụ:

sudo update-rc.d <service name> defaults 20 90

Bạn sẽ nhận được kết quả như sau:

 Adding system startup for /etc/init.d/<service name> ...
   /etc/rc0.d/K90<service name> -> ../init.d/<service name>
   /etc/rc1.d/K90<service name> -> ../init.d/<service name>
   /etc/rc6.d/K90<service name> -> ../init.d/<service name>
   /etc/rc2.d/S20<service name> -> ../init.d/<service name>
   /etc/rc3.d/S20<service name> -> ../init.d/<service name>
   /etc/rc4.d/S20<service name> -> ../init.d/<service name>
   /etc/rc5.d/S20<service name> -> ../init.d/<service name>

Tùy chọn runlevels

Nếu bạn muốn Start và Kill ở runlevel nào, bạn có thể chỉ rõ trong câu lệnh update-rc.d. Ví dụ, chúng ta muốn Apache start với độ ưu tiên là 20 ở runlevel 2, 3, 4 và 5, kill với độ ưu tiên là 80 ở runlevel 0, 1 và 6:

$ sudo update-rc.d apache2 start 20 2 3 4 5 . stop 80 0 1 6 .
 Adding system startup for /etc/init.d/apache2 ...
   /etc/rc0.d/K80apache2 -> ../init.d/apache2
   /etc/rc1.d/K80apache2 -> ../init.d/apache2
   /etc/rc6.d/K80apache2 -> ../init.d/apache2
   /etc/rc2.d/S20apache2 -> ../init.d/apache2
   /etc/rc3.d/S20apache2 -> ../init.d/apache2
   /etc/rc4.d/S20apache2 -> ../init.d/apache2
   /etc/rc5.d/S20apache2 -> ../init.d/apache2

Hoặc, start với độ ưu tiên 20 ở runlevel 2, 3, 4. Độ ưu tiên 30 cho runlevel 5 và kill với độ ưu tiên 80 ở runlevel 0, 1, 6:

$ sudo update-rc.d apache2 start 20 2 3 4 . start 30 5 . stop 80 0 1 6 . 
update-rc.d: warning: /etc/init.d/apache2 missing LSB information
update-rc.d: see <http://wiki.debian.org/LSBInitScripts>
 Adding system startup for /etc/init.d/apache2 ...
   /etc/rc0.d/K80apache2 -> ../init.d/apache2
   /etc/rc1.d/K80apache2 -> ../init.d/apache2
   /etc/rc6.d/K80apache2 -> ../init.d/apache2
   /etc/rc2.d/S20apache2 -> ../init.d/apache2
   /etc/rc3.d/S20apache2 -> ../init.d/apache2
   /etc/rc4.d/S20apache2 -> ../init.d/apache2
   /etc/rc5.d/S30apache2 -> ../init.d/apache2

Lời kết

Đến đây là bài dịch của mình đã kết thúc. Hy vọng nó sẽ có ích cho mọi người trong tương lai. Chắc bài sau mình sẽ hướng dẫn mọi người viết một service script đơn giản nhé. Hẹn gặp lại 😄!

Bài viết gốc: How-To: Managing services with update-rc.d