systemdサービスが失敗し、User =がサービスファイルに表示されました。

systemdサービスが失敗し、User =がサービスファイルに表示されました。

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

私はアプリケーション起動スクリプトを変更してディレクトリに書き込むことで/runこれを行うことができることがわかりました。だから効果がありました!しかし、今は克服できないような問題が生じました。 root以外の特定のユーザー(私のログインID)のコンテキストで実行するには、実行中のサービスが必要です。私が調べたところ、これを行うにはUser=ファイルに割り当てるだけです。.serviceただし、この行をファイルに追加するたびにサービスの起動は失敗します。ディレクトリに書き込むと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=属性 ""がない場合、サービスの起動に問題はありません。私はいつも私のユーザーIDでsystemctlコマンドをsudoとして実行します。私の究極の目標は、ルートではなく私のユーザーIDで実行される実際の実行プロセスを見ることです。User=<myid>ファイルに追加すると操作が完了すると思いましたが、.serviceこの行はサービスを開始しません。

ユーザーが存在し(私のユーザーID)、sudoersリストにもあります。 /runはrootが所有し、ここでsudoを使用してpidファイルを作成すると、pidファイルの所有者はrootです。起動スクリプトでforコマンドを試しましたが、su <userid>これはデーモンをまったく起動しませんでした。

デフォルトでは、SuSEとRedhatの間で動作が異なります。私のデーモンは(長年)プログラムのインストールパスにpidファイルを提供しました。su <userid> -cユーザー「a」のコンテキストでプロセスを開始するコマンドを使用してプロセスを開始します。 pidファイルはアプリケーションのインストールパスにあり、ユーザー「a」が所有します。 SuSE Linux:まったく問題ありません。しかし、Redhatでは、プロセスが実行されているとき、systemdは次のように文句を言います。

新しいマスターPID 26979はサービスに属しておらず、PIDファイルはrootが所有していません。拒否する

なぜ違いがありますか?私が達成したいことが「実行可能」なのか?それとも、ルートはすべてのシステムサービスプロセスを所有する必要がありますか?

答え1

問題は、System V initスクリプトがrootとして実行されることを期待し、動作方法の「仕様」の一部であり、完了するにはrootが必要な手順があることが多いことです。

あなたの場合、su <userid> -c ...実際にはroot以外のユーザーとして実行を開始するのは実行に関するものですが、すでにこのユーザーとして実行します。 System V initスクリプトは、多くの場合、root以外のユーザーに切り替えるためにsu同様のツールを使用しますrunasが、これらのツールはしばしば目的には完全には適していません(su元のインタラクティブシェルで実行することを意図しており、PAMと統合されています)。意味がありません。)

悪いことに、一部のSystem V initスクリプトは、ユーザーの変更を処理することなく、最終的にデーモンをrootとして不必要に実行します。なぜなら、これはSystem V initスクリプトでより「自然」な気がするからです。私の考えでは、これはSystem V initスクリプトの最も深刻な問題の1つです。ここでは間違ったことをするのがとても簡単で、正しいことをするのは非常に難しいです。

System V init スクリプトとの互換性を維持するには、次のようにします。できるそのようなスクリプトを呼び出すとき、これは「プロトコル」なので、systemdでrootとして実行してください。実際に互換性を維持するには、systemdユニットを送信する必要さえありません。 systemdは、次のようにして独自に1つを生成できるためです。systemd-sysv-ジェネレータ。結果のユニットはルートとして実行されることを除いて、提供されたユニットと非常によく似ています。

もしあなたならするsystemdサービスユニットを公開する(推奨します)Type=simple代わりにforking

唯一の前提条件は、フォアグラウンドでデーモンを起動できることです。多くのデーモンは追加のコマンドラインフラグを渡したり、いくつかの設定でこれを行うことができます。 (実際にはかなり大きい。少ないこれに取り組んでください。したがって、デーモンが現在その機能をサポートせずにソースを制御できる場合は、その機能を追加または要求することをお勧めします。 )

この時点でやるべきことは、ExecStart=コマンドでフォアグラウンドのデーモンを呼び出すことです。ExecStop=デーモンを終了するように指示された後、デーモンが正しく終了するまでは必要ありません。

これ以上pidfileは必要ありません!systemdはフォアグラウンドでデーモンを起動するので、知るデーモンプロセスのデフォルトPIDは何ですか? pidfileはしばしば/一般的に誤って実装されているため(デーモンがサービスする準備ができたときにのみ作成する必要があります)、この要件を削除することは非常に大きな問題です。

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

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

シェルスクリプトを使用してデーモンを起動する際の重要な部分は、次のものを使用することです。execシェルをデーモンに置き換えるコマンドです。つまり、シェルは存在しなくなり、デーモンはシェルで使用されているのと同じPIDで実行されるため、systemdはまだデーモンのデフォルトPIDを確実に知ることができます。もちろん、execシェルスクリプトで生成されたデーモンはまだフォアグラウンドで実行する必要があります。

サービスを使用すると、systemd デバイス自体内でType=simpleサービスを構成できます。User=また、systemd構成を使用して追加のセキュリティ対策を適用することがよくあります。これにより、System V initスクリプトがトリガーされ、そのスクリプトの使用を防ぐことができます。また、simple代わりにを使用すると、forkingシステムでこの設定をより安定して効率的にすることができます。

システム化されたサービスユニットを簡単に出荷できます。Type=simple そしてパッケージの System V 初期化スクリプト。名前が同じである限り、systemdはデフォルトのサービスユニットを好みます(したがって、この場合、systemd-sysv-generatorのレガシーコードはinitスクリプトを起動しません)。これにより、Linux以外の製品との互換性を維持できます。その他のシステム互換性。非systemd設定、systemdを使用すると、最新のLinuxシステムを最大限に活用できます。

答え2

問題は、デーモンを起動する基本スクリプトの「su -c」コマンドです。システムはこれが好きではありません。しかし、スクリプトは他のプラットフォームでも実行する必要があるため、Linux用のケースステートメントを使用して修正しました。とにかくsystemctlはsudoで実行されるため、これは問題ではありません。これで、pidファイルは私が必要とするどこでも書くことができ、システムは満足のいくようです。

答え3

この答えはシステム>= 230(2016年5月出版)。これは、SELinuxが有効でsystemdで実行suまたは提供できないFedoraまたはRedHatおよびその他のシステムでsudo必要です。

"+"解決策は、特権ユーザーとして実行する必要がある行でsystemd.serviceの接頭辞を使用することです。

例:

[Service]
Type=forking
User=myid
ExecStartPre=+sh -c "if ! test -d /run/myid; then mkdir /run/myid; chown myid:myid /run/myid; fi"
ExecStart=<my script>                                                    
PIDFile=/run/myid/my_service.pid
...

その後、権限のないユーザーとして実行されるスクリプトを使用してPIDFileを生成できます。既定では、サービスはPIDFileのpidに終了信号を送信して終了するため、「ExecStop =」行を使用する必要はありません。

システムサービス:

ExecStartPre=、ExecStartPre=、ExecStartPost=
...
"+"実行可能ファイルパスの前に「+」が付いている場合、プロセスはフル権限で実行されます。このモードでは、User =、Group =、CapabilityBoundingSet =、またはさまざまなファイルシステム名前空間オプション(PrivateDevices =、PrivateTmp =など)を使用して設定された権限制限は、呼び出しコマンドラインには適用されません(ただし、他のExecStart =、ExecStopにはまだ影響を与えます)。 =、...行)。

関連情報