web-dev-qa-db-ja.com

systemdサービスがサービスファイルのUser =で失敗する

RHEL 7/8で、いくつかの古いSystem Vタイプのサービスを「実際の」systemdサービスに移行する作業をしています。 Redhat 7、SLES 12、SLES 15で問題なく動いています。しかし、RHEL 8でサービスを実行しようとすると、システムがアプリケーションのpidfileを/runディレクトリに配置する必要があることを発見しました...または、少なくともそこに配置する必要があります。 (私たちのアプリケーションは通常、アプリケーションのインストールディレクトリにpidfileを書き込んでいました。)

アプリケーションの起動スクリプトを変更して/runディレクトリに書き込むことで、これを実現できることがわかりました。だから成功!しかし、今は乗り越えられそうにない問題があります。実行中のサービスが、rootとしてではなく、特定のユーザー(ログインID)のコンテキストで実行されている必要があります。私のすべての調査は、これを達成するためにUser=ファイルで.serviceを指定するだけでよいと私に言っています。しかし、その1行をファイルに追加すると、サービスの開始が失敗します。 pidfileが/runディレクトリに書き込まれた時点で失敗しているようです。 Pidfileに書き込めません。プロセスは終了します。

私のサービスファイル:

Description={removed}
After=remote-fs.target
After=network-online.target
Wants=remote-fs.target
Wants=network-online.target

[Service]
Type=forking
Restart=no
User=myid
TimeoutSec=5min
IgnoreSIGPIPE=no
KillMode=none
GuessMainPID=no
RemainAfterExit=no
SuccessExitStatus=5 6 255
PIDFile=/run/adidmn.pid
ExecStart=<fullPathToScript> start
ExecStop=<fullPathToScript> stop

[Install]
WantedBy=multi-user.target

私の開始スクリプトは、さまざまなプラットフォームでデーモンを実行する方法を定義し、いくつかの環境変数を設定して、実際のプロセスを開始するシェルスクリプトを呼び出します。呼び出されたデーモンスクリプトは、Sudo権限で呼び出されたときに直接実行できます。私のユーザーIDはsudoersリストにありますが、もちろん、実行中のプロセスの所有者はrootです。

.serviceファイルに "User="プロパティがなければ、サービスの開始に問題はありません。私はいつもsystemctlコマンドを自分のユーザーIDでSudoとして実行しています。私の最終目標は、実際の実行中のプロセスをrootではなく、ユーザーIDで実行しているものとして表示することです。 User=<myid>ファイルに.serviceを追加することでこれを達成できると思っていましたが、その1行でサービスの開始が失敗しました。

ユーザーが存在し(自分のユーザーID)、sudoersリストにも含まれている。/runはrootが所有し、pidファイルがSudoを使用してそこに書き込まれる場合、pidファイルの所有者はrootです。開始スクリプトのコマンドにsu <userid>を使用してみましたが、デーモンがまったく起動しませんでした。

基本的に、私はSuSEとRedhatで異なる動作をします。私のデーモンは(何年もの間)プログラムのインストールパスにpidファイルを考え出しました。プロセスは、ユーザー "a"のコンテキストでプロセスを開始するsu <userid> -cコマンドを使用して開始されます。 pidファイルはアプリケーションのインストールパスに配置され、ユーザー「a」が所有します。 SuSE Linux:問題ありません。ただし、Redhatでは、プロセスの実行中、systemdは次のように不満を示します。

新しいメインPID 26979はサービスに属しておらず、PIDファイルはルートによって所有されていません。拒否。

なぜ違いますか?私が「実行可能」を達成しようとしているのは何ですか?それともrootがすべてのsystemdサービスプロセスを所有している必要がありますか?

3
BobP

複雑なのは、デーモンを開始した基礎となるスクリプトの「su -c」コマンドでした。システムはそれを好まなかった。しかし、スクリプトは他のプラットフォームでも動作する必要があるため、Linux用のcaseステートメントで変更しました。とにかくsystemctlはSudoの下で実行されるため、問題ありません。これで、pidファイルをどこにでも書き込むことができ、システムは満足しているようです。

2
BobP

あなたがぶつかる問題は、System V initスクリプトがrootとして実行されることを期待していることです。これは、それらがどのように機能するかについての「仕様」の一部であり、多くの場合、rootが完了する必要のあるステップがあります。

あなたの場合、su <userid> -c ...を実行して実際に非rootユーザーとして実行を開始することについてでしたが、そのユーザーの下でalreadyを実行している場合、その部分は実際には失敗します。 System V initスクリプトは、しばしばsurunasなどのツールを使用して非rootユーザーに切り替えますが、これらのツールはその目的(suはもともとインタラクティブシェルから実行することを意図しており、ここではあまり意味のないPAMと統合されます。

さらに悪いことに、一部のSystem V initスクリプトはユーザーの変更に対応せず、ルートとしてデーモンを不必要に実行することになります。これは、System V initスクリプトでより「自然」に感じるためです。私の意見では、これはSystem V initスクリプトの最悪の問題の1つであり、ここで間違ったことが簡単に行われ、正しいことを行うのが難しくなっています。

System V initスクリプトとの互換性を維持したい場合は、could systemdからrootとして実行するだけです。これは、そのようなスクリプトを呼び出すときの「プロトコル」だからです。実際、互換性を維持したい場合は、systemdが systemd-sysv-generator を使用してそれ自体を生成できるため、systemdユニットを出荷する必要さえありません。生成されたユニットは、代わりにrootによって実行されることを除いて、提示したものとよく似ています。

doでsystemdサービスユニットを発送したい場合(そうすることをお勧めします)、forkingではなくType=simpleを使用するユニットの発送を真剣に検討する必要があります。

そのための唯一の前提条件は、デーモンをフォアグラウンドで起動できることです。これは、多くのデーモンが追加のコマンドラインフラグを渡すか、いくつかの構成を通じて実行できることです。 (実際にはかなりのlessの労力がかかるため、デーモンが現在それをサポートしておらず、ソースを制御している場合は、その機能を追加または要求することを検討してください。)

その時点で必要なのは、ExecStart=ディレクティブからフォアグラウンドでデーモンを呼び出すことだけです。デーモンがシグナルを受信して​​強制終了した場合、デーモンを強制終了する限り、ExecStop=は必要ありません。

pidfileはもう必要ありません! systemdはデーモンプロセスをフォアグラウンドで起動しているので、知っているデーモンプロセスのメインPIDは何ですか。 pidfileはしばしば/通常は正しく実装されていないので(デーモンがサービスを提供する準備ができて初めて作成されることになっている)、そのため、この要件を取り除くことは非常に重要です。

メインプロセスを開始する前に特定の環境変数をエクスポートする必要がある場合は、systemdのEnvironment=またはEnvironmentFile=を使用してこれらの変数を設定できます。 (変数が動的に生成された値ではなく固定値に設定されている限り、これは正常に機能します。)デーモンが開始する前にステップを実行する必要がある場合は、ExecStartPre=を使用できます。

さらに柔軟性が必要な場合(たとえば、条件付きで変数を設定したり、コマンドを実行したり、変数を動的な値に設定したりするなど)、起動をシェルスクリプト(またはPython、Perlなど)でラップし、そのスクリプトをExecStart=。スクリプトは、メインデーモンを実行する前に、必要な変数を設定およびエクスポートし、実行する必要があるコマンドを実行します。

シェルスクリプトを使用してデーモンを起動する場合の重要な部分は、execコマンドを使用して、シェルをデーモンプログラム。つまり、シェルはもう存在せず、デーモンはシェルで使用されていたのと同じPIDで実行されるため、systemdはデーモンのメインPIDを確実に認識します。もちろん、シェルスクリプトによってexec 'dされたデーモンは、フォアグラウンドで実行する必要があります。

Type=simpleサービスを使用すると、systemdユニット自体でUser=を設定できます。さらに、通常はsystemd構成を通じてより多くのセキュリティ対策を適用できます。これにより、System V initスクリプトが作動し、その使用が妨げられる可能性があります。また、simpleではなくforkingを使用すると、この設定の信頼性が大幅に向上し、システムでの効率も向上します。

パッケージにType=simpleand System V initスクリプトを使用して、両方のsystemdサービスユニットを簡単に出荷できます。それらが同じ名前である限り、systemdはネイティブサービスユニットを優先します(そのため、systemd-sysv-generatorのレガシーコードは、その場合のinitスクリプトに対してトリガーされません)。これにより、Linux以外との互換性を維持できます。 systemd以外のセットアップだけでなく、systemdを使用して最新のLinuxシステムを最大限に活用することもできます。

3
filbranden