Centos Systemd puma service

Witajcie
Mam server z Centos 7 i nginx i chciałem, aby istniał w nim service w pliku /etc/systemd/system/puma_test.service, niestety nie wiem jak to zrobić. Jestem noobem, wiem, i wielu rzeczy nie wiem. Udało mi się to zrobić wcześniej z Django za pomocą gunicorn.
Mój plik to:

[Unit]
Description=Puma application server
After=network.target

[Service]
WorkingDirectory=/var/www/test_app
Environment=RAILS_ENV=development

PIDFile=/var/www/shared/pids/puma.pid
ExecStart=/usr/bin/env ruby /usr/local/rvm/gems/ruby-2.2.1/gems/bundler-1.9.4/bin/bundle exec puma -e development -b unix:///var/www/shared/pids/puma.sock --pidfile /var/www/shared/pids/puma.pid

[Install]
WantedBy=multi-user.target

niestety to nie działa i zwraca błąd:

kwi 19 06:45:22 x.ovh.net systemd[1]: Starting Puma application server...
kwi 19 06:45:22 x.ovh.net systemd[1]: Started Puma application server.
kwi 19 06:45:22 x.ovh.net env[13535]: /usr/bin/env: ruby: No such file or directory
kwi 19 06:45:22 x.ovh.net systemd[1]: puma_test.service: main process exited, code=exited, status=127/n/a
kwi 19 06:45:22 x.ovh.net systemd[1]: Unit puma_test.service entered failed state.

Nie wiem jak się za to zabrać. Dodam, że:

/usr/bin/env ruby /usr/local/rvm/gems/ruby-2.2.1/gems/bundler-1.9.4/bin/bundle exec puma -e development -b unix:///var/www/shared/pids/puma.sock --pidfile /var/www/shared/pids/puma.pid

uruchomione w odpowiednim katalogu działa.
Czy moglibyście mi pomóc i powiedzieć jak to naprawić? Zainstalowałem rubiego za pomocą rvm na koncie root co chyba nie jest wskazane.

Systemd nie wie nic o zmiennych środowiskowych utworzonych przez rvm. Poczytaj o różnicach w “bash login shell” oraz “bash interactive shell”.

Dirty hack:

ExecStart=/bin/bash -lc "/usr/bin/env ruby /usr/local/rvm/gems/ruby-2.2.1/gems/bundler-1.9.4/bin/bundle exec puma -e development -b unix:///var/www/shared/pids/puma.sock --pidfile /var/www/shared/pids/puma.pid"
1 Like

Dzięki za odpowiedź.

Czy ten dirty hack jest legalny, a jeżeli nie to jak go zastąpić?
Da się jakoś to napisać, by wyrzucić wersję rubiego i bundlera i uzależnić to od tego co jest w Gemfile?

Jest legalny, ale mało elegancki.

Z czystej ciekawości, znasz może eleganckie? Albo gdzie go szukać?

To troszkę skomplikowane, a wynika to ze sposobu działania RVM oraz innych tego typu narzędzi. Mianowicie chodzi o ustawienie całego szeregu zmiennych środowiskowych, aby odpalić ruby. Fajnie to działa w trybie interaktywnym, gdy użytkownik pisze sobie rvm use lub korzysta z dotfile typu .ruby-version i wszystko dzieje się automagicznie.

Schody zaczynają się gdy wychodzimy z ciepłego środowiska developerskiego, gdzie wszystko robimy z palca, a przechodzimy na poziom aplikacji typu systemd/upstart czy chociażby zadań crona lub deployu przez capistrano. Mimo, że wszystko ma się ładnie ładować to zawsze są problemy, że coś komuś nie działa - a powodem w 99% są zawsze ustawienia zmiennych środowiskowych, a raczej ich brak.

Teraz pytanie jak to zrobić ładnie:

  1. Pierwsze pytanie to czemu chcesz korzystać z systemd do uruchomienia thin-a, jeśli to jest Twoje (jak podejrzewam) lokalne środowisko developerskie. Jeśli robisz sztukę dla sztuki (bo się chcesz nauczyć systemd) to ok. W innym przypadku IMHO nie ma to sensu (strzelanie do muchy z armaty)

  2. Jeśli nie chcesz za dużo zmieniać w projekcie i/lub zmieniać przyzwyczajeń developerów/administratorów możesz stosować ten prosty trick z /bin/bash -lc "my command". On po prostu działa i zapewnia, że wszystkie zmienne środowiskowe zostaną ustawione (odczyta .bash_profile, .bashrc itp. itpd.). Problem w tym, że trzeba o tym pamiętać i nie zawsze jest to wygodne i czytelne.

  3. Jeśli to jest serwer, na którym działa tylko jedna aplikacja (np. produkcja) to nie warto bawić się w RVM tylko posiadać jedną wersję rubego, która razem ze wszystkimi narzędziami typu rake, bundle itp. będzie dostępna w PATH (np. w /usr/bin, /usr/local/bin)

  4. Jeśli już musisz mieć takiego RVM to warto stworzyć skrypt, przy pomocy którego przygotujesz swoje całe środowisko do pracy tj. zainicjujesz sobie zmienne środowiskowe lub po prostu załadujesz to co jest Ci potrzebne z .bash_profile / .bashrc. Dalej, taki skrypt wykonujesz przed wywołaniem każdego commandline-nowego toola:

$ app-run bundle exec puma ...

1 Like

Dzięki za odpowiedź.
Jestem studentem i dopiero uczę się. Robię to jako sztuka dla sztuki, aby się nauczyć. Tak, wiem, że w przypadku środowiska developerskiego nie ma to sensu zbytnio.

Rozumiem, że głównym problemem dla systemd są zmienne środowiskowe i sam muszę się o nie zatroszczyć?

Tak, zmienne środowiskowe. Nie jest to tylko problem systemd, ale każdej innej aplikacji (cron, capistrano, ansible itp.). Jest taka niepisana zasada, że uruchamiana aplikacja nie powinna polegać na zewnętrznych, niestandardowych zależnościach takich jak:

  • customowe zmienne środowiskowe
  • niestandardowe ścieżki do bibliotek (LD_LIBRARY_PATH) oraz binarek (PATH)

Jeśli Twoja aplikacja wymaga ich do działania to powinieneś zadbać o ich dostarczenie w swojej aplikacji lub skrypcie, który tę aplikację będzie uruchamiać.

czyli jak. Napisać skrypt w bashu, który by definiował np. zmienne środowiskowe itp. jako
ExecStartPre=
?