Je souhaite démarrer un processus ngrok au démarrage du serveur. Pour y parvenir, j'ai codé une lib ngrok.rb et je l'appelle dans un initialiseur

app/lib/ngrok.rb

require "singleton"
class Ngrok
    include Singleton

    attr_accessor :api_url, :front_url


    def start
        if is_running?
            return fetch_urls
        end

        authenticate
        started = system("ngrok start --all -log=stdout > #{ENV['APP_ROOT']}/log/ngrok.log &")
        system("sleep 1")
        if !started
            return { api: nil, front: nil }
        end

        urls = fetch_urls
        sync_urls(urls["api_url"], urls["front_url"])
        return urls
    end

    def sync_urls(api_url, front_url)
        NgrokSyncJob.perform_later(api_url, front_url)
    end

    def is_running?
        return system("ps aux | grep ngrok")
    end
    def restart
        stop
        return start
    end
    def stop
        return system("pkill ngrok")
    end

    def authenticate
        has_file = system("ls ~/.ngrok2/ngrok.yml")
        if has_file
            return true
        else
            file_created = system("ngrok authtoken #{ENV['NGROK_TOKEN']}")
            if file_created
                return system("cat " + ENV['APP_ROOT'] + '/essentials/ngrok/example.yml >> ~/.ngrok2/ngrok.yml')
            else
                return false
            end
        end
    end

    def fetch_urls
        logfile = ENV['APP_ROOT'] + '/log/ngrok.log'

        file = File.open logfile
        text = file.read

        api_url = nil
        front_url = nil

        text.split("\n").each do |line|
            next if !line.include?("url=") || !line.include?("https")

            if line.split("name=")[1].split(" addr=")[0] == "ncommerce-api"
                api_url = line.split("url=")[1]
            elsif line.split("name=")[1].split(" addr=")[0] == "ncommerce"
                front_url = line.split("url=")[1]
            end
        end

        file.close

        self.api_url = api_url
        self.front_url = front_url

        res = {}
        res["api_url"] = api_url
        res["front_url"] = front_url

        return res
    end
end

config/initializers/app-init.rb

module AppModule
    class Application < Rails::Application
        config.after_initialize do
            puts "XXXXXXXXXXXXXXXXXXXXXXX"
            Ngrok.instance.start
            puts "XXXXXXXXXXXXXXXXXXXXXXX"

        end
    end
end

Lorsque je tape rails serve, voici un exemple du résultat

enter image description here

Nous savons donc avec certitude que mon initialiseur est appelé, mais quand je regarde la console rails si elle est en cours d'exécution, ce n'est pas le cas !

enter image description here

Mais lorsque je tape Ngrok.instance.start dans la console rails, voici le résultat :

enter image description here

Et ça commence !

enter image description here

Donc, ma question est la suivante : POURQUOI SUR TERRE system("ngrok start --all -log=stdout > #{ENV['APP_ROOT']}/log/ngrok.log &") ne fonctionne-t-il PAS sur le service de rails, mais il est sur la console de rails ?

MISE À JOUR

Si j'utilise 'byebug' dans ngrok.rb et que j'utilise rails serve, lorsque je quitte byebug avec "continue", le processus ngrok est créé et fonctionne

1
Aleksandrus 7 oct. 2020 à 22:46

1 réponse

Meilleure réponse

Vous créez un processus orphelin de la même manière que vous utilisez system() pour démarrer le processus ngrok en arrière-plan :

system("ngrok start --all -log=stdout > #{ENV['APP_ROOT']}/log/ngrok.log &")

Notez le & à la fin de la ligne de commande.

J'aurais besoin de plus de détails sur votre environnement d'exécution pour dire précisément quelle stratégie système tue le processus ngrok orphelin juste après l'avoir démarré (quel système d'exploitation ? si Linux, est-il basé sur systemd ? comment démarrez-vous rails serveur, depuis un terminal ou en tant que service système ?).

Mais ce qui se passe c'est ça :

  • system() démarre une instance de /bin/sh pour interpréter la ligne de commande
  • /bin/sh démarre le processus ngrok en arrière-plan et se termine
  • ngrok est maintenant "orphelin", ce qui signifie que son processus parent /bin/sh est terminé, de sorte que le processus ngrok ne peut pas être wait(2)ed pour
  • selon l'environnement, la terminaison /bin/sh peut tuer ngrok avec un signal SIGHUP
  • ou le système d'exploitation re-parent ngrok, normalement au processus d'initialisation (mais cela dépend)

Lorsque vous utilisez la console rails ou byebug, dans les deux cas, vous entrez dans un environnement interactif, qui prépare des "groupes de processus", des "identifiants de session" et des "terminaux de contrôle" d'une manière adaptée à une exécution interactive. Ces propriétés sont héritées par les processus enfants, comme ngrok. Cela influence les politiques du système concernant la gestion du processus d'arrière-plan orphelin.

Lorsque ngrok est démarré à partir du serveur rails, ces propriétés seront différentes (selon la manière dont le serveur rails est démarré).

Voici un bel article sur certains des mécanismes du système d'exploitation qui pourraient être impliqués : https://www.jstorimer.com/blogs/workingwithcode/7766093-daemon-processes-in-ruby

Vous auriez probablement plus de succès en utilisant Process.spawn de Ruby pour démarrer le processus d'arrière-plan, en combinaison avec Process.detach dans votre cas. Cela éviterait de rendre orphelin le processus ngrok.

1
kisch 8 oct. 2020 à 15:10